void TouchActionTest::runShadowDOMTest(std::string file) { TouchActionTrackingWebViewClient client; WebView* webView = setupTest(file, client); TrackExceptionState es; // Oilpan: see runTouchActionTest() comment why these are persistent // references. Persistent<Document> document = static_cast<Document*>(webView->mainFrame()->document()); Persistent<StaticElementList> hostNodes = document->querySelectorAll("[shadow-host]", es); ASSERT_FALSE(es.hadException()); ASSERT_GE(hostNodes->length(), 1u); for (unsigned index = 0; index < hostNodes->length(); index++) { ShadowRoot* shadowRoot = hostNodes->item(index)->openShadowRoot(); runTestOnTree(shadowRoot, webView, client); } // Projections show up in the main document. runTestOnTree(document.get(), webView, client); // Explicitly reset to break dependency on locally scoped client. m_webViewHelper.reset(); }
void TouchActionTest::runTestOnTree(ContainerNode* root, WebView* webView, TouchActionTrackingWebViewClient& client) { // Find all elements to test the touch-action of in the document. TrackExceptionState es; // Oilpan: see runTouchActionTest() comment why these are persistent // references. Persistent<StaticElementList> elements = root->querySelectorAll("[expected-action]", es); ASSERT_FALSE(es.hadException()); for (unsigned index = 0; index < elements->length(); index++) { Element* element = elements->item(index); element->scrollIntoViewIfNeeded(); std::string failureContext("Test case: "); if (element->hasID()) { failureContext.append(element->getIdAttribute().ascii().data()); } else if (element->firstChild()) { failureContext.append("\""); failureContext.append(element->firstChild() ->textContent(false) .stripWhiteSpace() .ascii() .data()); failureContext.append("\""); } else { failureContext += "<missing ID>"; } // Run each test three times at different positions in the element. // Note that we don't want the bounding box because our tests sometimes have // elements with multiple border boxes with other elements in between. Use // the first border box (which we can easily visualize in a browser for // debugging). Persistent<ClientRectList> rects = element->getClientRects(); ASSERT_GE(rects->length(), 0u) << failureContext; Persistent<ClientRect> r = rects->item(0); FloatRect clientFloatRect = FloatRect(r->left(), r->top(), r->width(), r->height()); IntRect clientRect = enclosedIntRect(clientFloatRect); for (int locIdx = 0; locIdx < 3; locIdx++) { IntPoint framePoint; std::stringstream contextStream; contextStream << failureContext << " ("; switch (locIdx) { case 0: framePoint = clientRect.center(); contextStream << "center"; break; case 1: framePoint = clientRect.location(); contextStream << "top-left"; break; case 2: framePoint = clientRect.maxXMaxYCorner(); framePoint.move(-1, -1); contextStream << "bottom-right"; break; default: FAIL() << "Invalid location index."; } IntPoint windowPoint = root->document().frame()->view()->convertToRootFrame(framePoint); contextStream << "=" << windowPoint.x() << "," << windowPoint.y() << ")."; std::string failureContextPos = contextStream.str(); LocalFrame* mainFrame = static_cast<LocalFrame*>(webView->mainFrame()->toImplBase()->frame()); FrameView* mainFrameView = mainFrame->view(); IntRect visibleRect = windowClipRect(*mainFrameView); ASSERT_TRUE(visibleRect.contains(windowPoint)) << failureContextPos << " Test point not contained in visible area: " << visibleRect.x() << "," << visibleRect.y() << "-" << visibleRect.maxX() << "," << visibleRect.maxY(); // First validate that a hit test at this point will really hit the // element we intended. This is the easiest way for a test to be broken, // but has nothing really to do with touch action. Note that we can't use // WebView's hit test API because it doesn't look into shadow DOM. IntPoint docPoint(mainFrameView->frameToContents(windowPoint)); HitTestResult result = mainFrame->eventHandler().hitTestResultAtPoint( docPoint, HitTestRequest::ReadOnly | HitTestRequest::Active); ASSERT_EQ(element, result.innerElement()) << "Unexpected hit test result " << failureContextPos << " Got element: \"" << result.innerElement() ->outerHTML() .stripWhiteSpace() .left(80) .ascii() .data() << "\"" << std::endl << "Document render tree:" << std::endl << externalRepresentation(root->document().frame()).utf8().data(); // Now send the touch event and check any touch action result. sendTouchEvent(webView, WebInputEvent::TouchStart, windowPoint); AtomicString expectedAction = element->getAttribute("expected-action"); if (expectedAction == "auto") { // Auto is the default - no action set. EXPECT_EQ(0, client.touchActionSetCount()) << failureContextPos; EXPECT_EQ(WebTouchActionAuto, client.lastTouchAction()) << failureContextPos; } else { // Should have received exactly one touch action. EXPECT_EQ(1, client.touchActionSetCount()) << failureContextPos; if (client.touchActionSetCount()) { if (expectedAction == "none") { EXPECT_EQ(WebTouchActionNone, client.lastTouchAction()) << failureContextPos; } else if (expectedAction == "pan-x") { EXPECT_EQ(WebTouchActionPanX, client.lastTouchAction()) << failureContextPos; } else if (expectedAction == "pan-y") { EXPECT_EQ(WebTouchActionPanY, client.lastTouchAction()) << failureContextPos; } else if (expectedAction == "pan-x-y") { EXPECT_EQ((WebTouchActionPan), client.lastTouchAction()) << failureContextPos; } else if (expectedAction == "manipulation") { EXPECT_EQ((WebTouchActionManipulation), client.lastTouchAction()) << failureContextPos; } else { FAIL() << "Unrecognized expected-action \"" << expectedAction.ascii().data() << "\" " << failureContextPos; } } } // Reset webview touch state. client.reset(); sendTouchEvent(webView, WebInputEvent::TouchCancel, windowPoint); EXPECT_EQ(0, client.touchActionSetCount()); } } }