OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
| 3 * |
| 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are |
| 6 * met: |
| 7 * |
| 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above |
| 11 * copyright notice, this list of conditions and the following disclaimer |
| 12 * in the documentation and/or other materials provided with the |
| 13 * distribution. |
| 14 * * Neither the name of Google Inc. nor the names of its |
| 15 * contributors may be used to endorse or promote products derived from |
| 16 * this software without specific prior written permission. |
| 17 * |
| 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 */ |
| 30 |
| 31 #include "config.h" |
| 32 |
| 33 #include "FrameTestHelpers.h" |
| 34 #include "RuntimeEnabledFeatures.h" |
| 35 #include "URLTestHelpers.h" |
| 36 #include "WebDocument.h" |
| 37 #include "WebFrame.h" |
| 38 #include "WebInputEvent.h" |
| 39 #include "WebView.h" |
| 40 #include "WebViewClient.h" |
| 41 #include "WebViewImpl.h" |
| 42 #include "core/dom/ClientRect.h" |
| 43 #include "core/dom/ClientRectList.h" |
| 44 #include "core/dom/Document.h" |
| 45 #include "core/dom/Element.h" |
| 46 #include "core/dom/shadow/ShadowRoot.h" |
| 47 #include "core/frame/Frame.h" |
| 48 #include "core/frame/FrameView.h" |
| 49 #include "core/page/EventHandler.h" |
| 50 #include "core/rendering/HitTestResult.h" |
| 51 #include "core/rendering/RenderTreeAsText.h" |
| 52 #include "public/platform/Platform.h" |
| 53 #include "public/platform/WebUnitTestSupport.h" |
| 54 #include "public/web/WebHitTestResult.h" |
| 55 #include "public/web/WebTouchAction.h" |
| 56 #include "public/web/WebWidgetClient.h" |
| 57 |
| 58 #include <gtest/gtest.h> |
| 59 |
| 60 using namespace blink; |
| 61 using blink::FrameTestHelpers::runPendingTasks; |
| 62 |
| 63 namespace { |
| 64 |
| 65 class TouchActionTrackingWebViewClient : public WebViewClient { |
| 66 public: |
| 67 TouchActionTrackingWebViewClient() : |
| 68 m_actionSetCount(0), |
| 69 m_action(WebTouchActionAuto) |
| 70 { |
| 71 } |
| 72 |
| 73 // WebWidgetClient methods |
| 74 virtual void setTouchAction(WebTouchAction touchAction) |
| 75 { |
| 76 m_actionSetCount++; |
| 77 m_action = touchAction; |
| 78 } |
| 79 |
| 80 // Local methods |
| 81 void reset() |
| 82 { |
| 83 m_actionSetCount = 0; |
| 84 m_action = WebTouchActionAuto; |
| 85 } |
| 86 |
| 87 int touchActionSetCount() |
| 88 { |
| 89 return m_actionSetCount; |
| 90 } |
| 91 |
| 92 WebTouchAction lastTouchAction() |
| 93 { |
| 94 return m_action; |
| 95 } |
| 96 |
| 97 private: |
| 98 int m_actionSetCount; |
| 99 WebTouchAction m_action; |
| 100 }; |
| 101 |
| 102 const int kfakeTouchId = 7; |
| 103 |
| 104 class TouchActionTest : public testing::Test { |
| 105 public: |
| 106 TouchActionTest() |
| 107 : m_baseURL("http://www.test.com/") |
| 108 { |
| 109 URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseU
RL), "touch-action-tests.css"); |
| 110 URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseU
RL), "touch-action-tests.js"); |
| 111 WebCore::RuntimeEnabledFeatures::setCSSTouchActionEnabled(true); |
| 112 } |
| 113 |
| 114 virtual void TearDown() |
| 115 { |
| 116 Platform::current()->unitTestSupport()->unregisterAllMockedURLs(); |
| 117 } |
| 118 |
| 119 protected: |
| 120 void runTouchActionTest(std::string file); |
| 121 void runShadowDOMTest(std::string file); |
| 122 void sendTouchEvent(WebView*, WebInputEvent::Type, WebCore::IntPoint clientP
oint); |
| 123 WebView* setupTest(std::string file, TouchActionTrackingWebViewClient&); |
| 124 void runTestOnTree(WebCore::Node* root, WebView*, TouchActionTrackingWebView
Client&); |
| 125 |
| 126 std::string m_baseURL; |
| 127 FrameTestHelpers::WebViewHelper m_webViewHelper; |
| 128 }; |
| 129 |
| 130 void TouchActionTest::runTouchActionTest(std::string file) |
| 131 { |
| 132 TouchActionTrackingWebViewClient client; |
| 133 |
| 134 WebView* webView = setupTest(file, client); |
| 135 |
| 136 RefPtr<WebCore::Document> document = static_cast<PassRefPtr<WebCore::Documen
t> >(webView->mainFrame()->document()); |
| 137 runTestOnTree(document.get(), webView, client); |
| 138 } |
| 139 |
| 140 void TouchActionTest::runShadowDOMTest(std::string file) |
| 141 { |
| 142 TouchActionTrackingWebViewClient client; |
| 143 |
| 144 WebView* webView = setupTest(file, client); |
| 145 |
| 146 WebCore::TrackExceptionState es; |
| 147 RefPtr<WebCore::Document> document = static_cast<PassRefPtr<WebCore::Documen
t> >(webView->mainFrame()->document()); |
| 148 RefPtr<WebCore::NodeList> hostNodes = document->querySelectorAll("[shadow-ho
st]", es); |
| 149 ASSERT_FALSE(es.hadException()); |
| 150 ASSERT_GE(hostNodes->length(), 1u); |
| 151 |
| 152 for (unsigned index = 0; index < hostNodes->length(); index++) { |
| 153 WebCore::ShadowRoot* shadowRoot = WebCore::toElement(hostNodes->item(ind
ex))->shadowRoot(); |
| 154 runTestOnTree(shadowRoot, webView, client); |
| 155 } |
| 156 |
| 157 // Projections show up in the main document. |
| 158 runTestOnTree(document.get(), webView, client); |
| 159 } |
| 160 |
| 161 WebView* TouchActionTest::setupTest(std::string file, TouchActionTrackingWebView
Client& client) |
| 162 { |
| 163 URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL),
WebString::fromUTF8(file)); |
| 164 // Note that JavaScript must be enabled for shadow DOM tests. |
| 165 WebView* webView = m_webViewHelper.initializeAndLoad(m_baseURL + file, true,
0, &client); |
| 166 |
| 167 // Set size to enable hit testing, and avoid line wrapping for consistency w
ith browser. |
| 168 webView->resize(WebSize(700, 1000)); |
| 169 |
| 170 // Scroll to verify the code properly transforms windows to client co-ords. |
| 171 const int kScrollOffset = 100; |
| 172 RefPtr<WebCore::Document> document = static_cast<PassRefPtr<WebCore::Documen
t> >(webView->mainFrame()->document()); |
| 173 document->frame()->view()->setScrollOffset(WebCore::IntPoint(0, kScrollOffse
t)); |
| 174 |
| 175 return webView; |
| 176 } |
| 177 |
| 178 void TouchActionTest::runTestOnTree(WebCore::Node* root, WebView* webView, Touch
ActionTrackingWebViewClient& client) |
| 179 { |
| 180 // Find all elements to test the touch-action of in the document. |
| 181 WebCore::TrackExceptionState es; |
| 182 RefPtr<WebCore::NodeList> nodes = root->querySelectorAll("[expected-action]"
, es); |
| 183 ASSERT_FALSE(es.hadException()); |
| 184 |
| 185 for (unsigned index = 0; index < nodes->length(); index++) { |
| 186 WebCore::Element* element = toElement(nodes->item(index)); |
| 187 element->scrollIntoViewIfNeeded(); |
| 188 ASSERT_TRUE(nodes->item(index)->isElementNode()); |
| 189 |
| 190 std::string failureContext("Test case: "); |
| 191 if (element->hasID()) { |
| 192 failureContext.append(element->getIdAttribute().string().ascii().dat
a()); |
| 193 } else if (element->firstChild()) { |
| 194 failureContext.append("\""); |
| 195 failureContext.append(element->firstChild()->textContent(false).stri
pWhiteSpace().ascii().data()); |
| 196 failureContext.append("\""); |
| 197 } else { |
| 198 failureContext += "<missing ID>"; |
| 199 } |
| 200 |
| 201 // Run each test three times at different positions in the element. |
| 202 // Note that we don't want the bounding box because our tests sometimes
have elements with |
| 203 // multiple border boxes with other elements in between. Use the first b
order box (which |
| 204 // we can easily visualize in a browser for debugging). |
| 205 RefPtr<WebCore::ClientRectList> rects = element->getClientRects(); |
| 206 ASSERT_GE(rects->length(), 0u) << failureContext; |
| 207 RefPtr<WebCore::ClientRect> r = rects->item(0); |
| 208 WebCore::FloatRect clientFloatRect = WebCore::FloatRect(r->left(), r->to
p(), r->width(), r->height()); |
| 209 WebCore::IntRect clientRect = enclosedIntRect(clientFloatRect); |
| 210 for (int locIdx = 0; locIdx < 3; locIdx++) { |
| 211 WebCore::IntPoint clientPoint; |
| 212 std::stringstream contextStream; |
| 213 contextStream << failureContext << " ("; |
| 214 switch (locIdx) { |
| 215 case 0: |
| 216 clientPoint = clientRect.center(); |
| 217 contextStream << "center"; |
| 218 break; |
| 219 case 1: |
| 220 clientPoint = clientRect.location(); |
| 221 contextStream << "top-left"; |
| 222 break; |
| 223 case 2: |
| 224 clientPoint = clientRect.maxXMaxYCorner(); |
| 225 clientPoint.move(-1, -1); |
| 226 contextStream << "bottom-right"; |
| 227 break; |
| 228 default: |
| 229 FAIL() << "Invalid location index."; |
| 230 } |
| 231 contextStream << "=" << clientPoint.x() << "," << clientPoint.y() <<
")."; |
| 232 std::string failureContextPos = contextStream.str(); |
| 233 |
| 234 WebCore::Frame* frame = root->document().frame(); |
| 235 WebCore::FrameView* frameView = frame->view(); |
| 236 WebCore::IntRect visibleRect = frameView->windowClipRect(); |
| 237 ASSERT_TRUE(visibleRect.contains(clientPoint)) << failureContextPos |
| 238 << " Test point not contained in visible area: " << visibleRect.
x() << "," << visibleRect.y() |
| 239 << "-" << visibleRect.maxX() << "," << visibleRect.maxY(); |
| 240 |
| 241 // First validate that a hit test at this point will really hit the
element |
| 242 // we intended. This is the easiest way for a test to be broken, but
has nothing really |
| 243 // to do with touch action. |
| 244 // Note that we can't use WebView's hit test API because it doesn't
look into shadow DOM. |
| 245 WebCore::IntPoint docPoint(frameView->windowToContents(clientPoint))
; |
| 246 WebCore::HitTestResult result = frame->eventHandler().hitTestResultA
tPoint(docPoint, WebCore::HitTestRequest::ReadOnly | WebCore::HitTestRequest::Ac
tive); |
| 247 ASSERT_EQ(element, result.innerElement()) << "Unexpected hit test re
sult " << failureContextPos |
| 248 << " Got element: \"" << result.innerElement()->outerHTML().str
ipWhiteSpace().left(80).ascii().data() << "\"" |
| 249 << std::endl << "Document render tree:" << std::endl << external
Representation(root->document().frame()).utf8().data(); |
| 250 |
| 251 // Now send the touch event and check any touch action result. |
| 252 sendTouchEvent(webView, WebInputEvent::TouchStart, clientPoint); |
| 253 |
| 254 AtomicString expectedAction = element->getAttribute("expected-action
"); |
| 255 if (expectedAction == "auto") { |
| 256 // Auto is the default - no action set. |
| 257 EXPECT_EQ(0, client.touchActionSetCount()) << failureContextPos; |
| 258 EXPECT_EQ(WebTouchActionAuto, client.lastTouchAction()) << failu
reContextPos; |
| 259 } else { |
| 260 // Should have received exactly one touch action. |
| 261 EXPECT_EQ(1, client.touchActionSetCount()) << failureContextPos; |
| 262 if (client.touchActionSetCount()) { |
| 263 if (expectedAction == "none") { |
| 264 EXPECT_EQ(WebTouchActionNone, client.lastTouchAction())
<< failureContextPos; |
| 265 } else { |
| 266 FAIL() << "Unrecognized expected-action \"" << expectedA
ction.string().ascii().data() |
| 267 << "\" " << failureContextPos; |
| 268 } |
| 269 } |
| 270 } |
| 271 |
| 272 // Reset webview touch state. |
| 273 client.reset(); |
| 274 sendTouchEvent(webView, WebInputEvent::TouchCancel, clientPoint); |
| 275 EXPECT_EQ(0, client.touchActionSetCount()); |
| 276 } |
| 277 } |
| 278 } |
| 279 void TouchActionTest::sendTouchEvent(WebView* webView, WebInputEvent::Type type,
WebCore::IntPoint clientPoint) |
| 280 { |
| 281 ASSERT_TRUE(type == WebInputEvent::TouchStart || type == WebInputEvent::Touc
hCancel); |
| 282 |
| 283 WebTouchEvent webTouchEvent; |
| 284 webTouchEvent.type = type; |
| 285 webTouchEvent.touchesLength = 1; |
| 286 webTouchEvent.touches[0].state = (type == WebInputEvent::TouchStart ? |
| 287 WebTouchPoint::StatePressed : |
| 288 WebTouchPoint::StateCancelled); |
| 289 webTouchEvent.touches[0].id = kfakeTouchId; |
| 290 webTouchEvent.touches[0].screenPosition.x = clientPoint.x(); |
| 291 webTouchEvent.touches[0].screenPosition.y = clientPoint.y(); |
| 292 webTouchEvent.touches[0].position.x = clientPoint.x(); |
| 293 webTouchEvent.touches[0].position.y = clientPoint.y(); |
| 294 webTouchEvent.touches[0].radiusX = 10; |
| 295 webTouchEvent.touches[0].radiusY = 10; |
| 296 |
| 297 webView->handleInputEvent(webTouchEvent); |
| 298 runPendingTasks(); |
| 299 } |
| 300 |
| 301 TEST_F(TouchActionTest, Simple) |
| 302 { |
| 303 runTouchActionTest("touch-action-simple.html"); |
| 304 } |
| 305 |
| 306 TEST_F(TouchActionTest, Overflow) |
| 307 { |
| 308 runTouchActionTest("touch-action-overflow.html"); |
| 309 } |
| 310 |
| 311 TEST_F(TouchActionTest, ShadowDOM) |
| 312 { |
| 313 runShadowDOMTest("touch-action-shadow-dom.html"); |
| 314 } |
| 315 |
| 316 } |
OLD | NEW |