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/frame/Frame.h" | |
47 #include "core/frame/FrameView.h" | |
48 #include "core/rendering/HitTestResult.h" | |
49 #include "core/rendering/RenderTreeAsText.h" | |
50 #include "public/platform/Platform.h" | |
51 #include "public/platform/WebUnitTestSupport.h" | |
52 #include "public/web/WebHitTestResult.h" | |
53 #include "public/web/WebWidgetClient.h" | |
54 | |
55 #include <gtest/gtest.h> | |
56 | |
57 using namespace blink; | |
58 using blink::FrameTestHelpers::runPendingTasks; | |
59 | |
60 namespace { | |
61 | |
62 class TouchActionTrackingWebViewClient : public WebViewClient { | |
63 public: | |
64 TouchActionTrackingWebViewClient() : | |
65 m_actionSetCount(0), | |
66 m_touchId(0), | |
67 m_actionDelayed(false), | |
68 m_action(TouchActionAuto) | |
69 { | |
70 } | |
71 | |
72 // WebWidgetClient methods | |
73 virtual void setTouchAction(int touchId, bool touchActionDelayed, TouchActio n touchAction) | |
74 { | |
75 m_actionSetCount++; | |
76 m_touchId = touchId; | |
77 m_actionDelayed = touchActionDelayed; | |
78 m_action = touchAction; | |
79 } | |
80 | |
81 // Local methods | |
82 void reset() | |
83 { | |
84 m_actionSetCount = 0; | |
85 m_touchId = 0; | |
86 m_actionDelayed = false; | |
87 m_action = TouchActionAuto; | |
88 } | |
89 | |
90 int touchActionSetCount() | |
91 { | |
92 return m_actionSetCount; | |
93 } | |
94 | |
95 int lastTouchActionTouchId() | |
96 { | |
97 return m_touchId; | |
98 } | |
99 | |
100 bool lastTouchActionDelayed() | |
101 { | |
102 return m_actionDelayed; | |
103 } | |
104 | |
105 TouchAction lastTouchAction() | |
106 { | |
107 return m_action; | |
108 } | |
109 | |
110 private: | |
111 bool m_actionSetCount; | |
112 int m_touchId; | |
113 bool m_actionDelayed; | |
114 TouchAction m_action; | |
115 }; | |
116 | |
117 const int kfakeTouchId = 7; | |
118 | |
119 class TouchActionTest : public testing::Test { | |
120 public: | |
121 TouchActionTest() | |
122 : m_baseURL("http://www.test.com/") | |
123 { | |
124 URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseU RL), "touch-action-tests.css"); | |
125 WebCore::RuntimeEnabledFeatures::setCSSTouchActionEnabled(true); | |
126 } | |
127 | |
128 virtual void TearDown() | |
129 { | |
130 Platform::current()->unitTestSupport()->unregisterAllMockedURLs(); | |
131 } | |
132 | |
133 protected: | |
134 void runTouchActionTest(std::string file) | |
abarth-chromium
2013/11/18 21:16:59
This looks pretty involved. Would it be better to
Rick Byers
2013/11/19 21:24:21
Yeah that was my first thought as well. I eventua
| |
135 { | |
136 TouchActionTrackingWebViewClient client; | |
137 URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseU RL), WebString::fromUTF8(file)); | |
138 WebView* webView = m_webViewHelper.initializeAndLoad(m_baseURL + file, f alse, 0, &client); | |
139 | |
140 // Set size to enable hit testing, and avoid line wrapping for consisten cy with browser. | |
141 webView->resize(WebSize(700, 1000)); | |
142 | |
143 // Get the document and bypass the no-touch-handler optimization. | |
144 RefPtr<WebCore::Document> document = static_cast<PassRefPtr<WebCore::Doc ument> >(webView->mainFrame()->document()); | |
145 document->didAddTouchEventHandler(document.get()); | |
146 | |
147 // Scroll to verify the code properly transforms windows to client co-or ds. | |
148 const int kScrollOffset = 100; | |
149 document->frame()->view()->setScrollOffset(WebCore::IntPoint(0, kScrollO ffset)); | |
150 | |
151 // Find all elements to test the touch-action of in the document. | |
152 WebCore::TrackExceptionState es; | |
153 RefPtr<WebCore::NodeList> nodes = document->querySelectorAll("[expected- action]", es); | |
154 ASSERT_FALSE(es.hadException()); | |
155 ASSERT_GE(nodes->length(), 1u); | |
156 | |
157 for (unsigned index = 0; index < nodes->length(); index++) { | |
158 WebCore::Element* element = toElement(nodes->item(index)); | |
159 element->scrollIntoViewIfNeeded(); | |
160 ASSERT_TRUE(nodes->item(index)->isElementNode()); | |
161 | |
162 std::string failureContext("Test case: "); | |
163 if (element->hasID()) { | |
164 failureContext.append(element->getIdAttribute().string().ascii() .data()); | |
165 } else if (element->firstChild()) { | |
166 failureContext.append("\""); | |
167 failureContext.append(element->firstChild()->textContent(false). stripWhiteSpace().ascii().data()); | |
168 failureContext.append("\""); | |
169 } else { | |
170 failureContext += "<missing ID>"; | |
171 } | |
172 | |
173 // Run each test three times at different positions in the element. | |
174 // Note that we don't want the bounding box because our tests someti mes have elements with | |
175 // multiple border boxes with other elements in between. Use the fir st border box (which | |
176 // we can easily visualize in a browser for debugging). | |
177 RefPtr<WebCore::ClientRectList> rects = element->getClientRects(); | |
178 ASSERT_GE(rects->length(), 0u) << failureContext; | |
179 RefPtr<WebCore::ClientRect> r = rects->item(0); | |
180 WebCore::FloatRect clientFloatRect = WebCore::FloatRect(r->left(), r ->top(), r->width(), r->height()); | |
181 WebCore::IntRect clientRect = enclosedIntRect(clientFloatRect); | |
182 for (int locIdx = 0; locIdx < 3; locIdx++) { | |
183 WebCore::IntPoint clientPoint; | |
184 std::stringstream contextStream; | |
185 contextStream << failureContext << " ("; | |
186 switch (locIdx) { | |
187 case 0: | |
188 clientPoint = clientRect.center(); | |
189 contextStream << "center"; | |
190 break; | |
191 case 1: | |
192 clientPoint = clientRect.location(); | |
193 contextStream << "top-left"; | |
194 break; | |
195 case 2: | |
196 clientPoint = clientRect.maxXMaxYCorner(); | |
197 clientPoint.move(-1, -1); | |
198 contextStream << "bottom-right"; | |
199 break; | |
200 default: | |
201 FAIL() << "Invalid location index."; | |
202 } | |
203 contextStream << "=" << clientPoint.x() << "," << clientPoint.y( ) << ")."; | |
204 std::string failureContextPos = contextStream.str(); | |
205 | |
206 WebCore::IntRect visibleRect = document->frame()->view()->window ClipRect(); | |
207 ASSERT_TRUE(visibleRect.contains(clientPoint)) << failureContext Pos | |
208 << " Test point not contained in visible area: " << visibleR ect.x() << "," << visibleRect.y() | |
209 << "-" << visibleRect.maxX() << "," << visibleRect.maxY(); | |
210 | |
211 // First validate that a hit test at this point will really hit the element | |
212 // we intended. This is the easiest way for a test to be broken, but has nothing really | |
213 // to do with touch action. | |
214 WebCore::HitTestResult result = static_cast<WebCore::HitTestResu lt>(webView->hitTestResultAt(clientPoint)); | |
215 ASSERT_EQ(element, result.innerElement()) << "Unexpected hit tes t result " << failureContextPos | |
216 << " Got element: \"" << result.innerElement()->outerHTML() .stripWhiteSpace().left(80).ascii().data() << "\"" | |
217 << std::endl << "Document render tree:" << std::endl << exte rnalRepresentation(document->frame()).utf8().data(); | |
218 | |
219 // Now send the touch event and check any touch action result. | |
220 sendTouchEvent(webView, WebInputEvent::TouchStart, clientPoint); | |
221 | |
222 AtomicString expectedAction = element->getAttribute("expected-ac tion"); | |
223 if (expectedAction == "auto") { | |
224 // Auto is the default - no action set. | |
225 EXPECT_EQ(0, client.touchActionSetCount()) << failureContext Pos; | |
226 EXPECT_EQ(WebWidgetClient::TouchActionAuto, client.lastTouch Action()) << failureContextPos; | |
227 } else { | |
228 // Should have received exactly one touch action. | |
229 EXPECT_EQ(1, client.touchActionSetCount()) << failureContext Pos; | |
230 if (client.touchActionSetCount()) { | |
231 EXPECT_TRUE(client.lastTouchActionDelayed()) << failureC ontextPos; | |
232 EXPECT_EQ(kfakeTouchId, client.lastTouchActionTouchId()) << failureContextPos; | |
233 if (expectedAction == "none") { | |
234 EXPECT_EQ(WebWidgetClient::TouchActionNone, client.l astTouchAction()) << failureContextPos; | |
235 } else { | |
236 FAIL() << "Unrecognized expected-action \"" << expec tedAction.string().ascii().data() | |
237 << "\" " << failureContextPos; | |
238 } | |
239 } | |
240 } | |
241 | |
242 // Reset webview touch state. | |
243 client.reset(); | |
244 sendTouchEvent(webView, WebInputEvent::TouchCancel, clientPoint) ; | |
245 EXPECT_EQ(0, client.touchActionSetCount()); | |
246 } | |
247 } | |
248 } | |
249 | |
250 void sendTouchEvent(WebView* webView, WebInputEvent::Type type, WebCore::Int Point clientPoint) | |
251 { | |
252 ASSERT_TRUE(type == WebInputEvent::TouchStart || type == WebInputEvent:: TouchCancel); | |
253 | |
254 WebTouchEvent webTouchEvent; | |
255 webTouchEvent.type = type; | |
256 webTouchEvent.touchesLength = 1; | |
257 webTouchEvent.touches[0].state = (type == WebInputEvent::TouchStart ? | |
258 WebTouchPoint::StatePressed : | |
259 WebTouchPoint::StateCancelled); | |
260 webTouchEvent.touches[0].id = kfakeTouchId; | |
261 webTouchEvent.touches[0].screenPosition.x = clientPoint.x(); | |
262 webTouchEvent.touches[0].screenPosition.y = clientPoint.y(); | |
263 webTouchEvent.touches[0].position.x = clientPoint.x(); | |
264 webTouchEvent.touches[0].position.y = clientPoint.y(); | |
265 webTouchEvent.touches[0].radiusX = 10; | |
266 webTouchEvent.touches[0].radiusY = 10; | |
267 | |
268 webView->handleInputEvent(webTouchEvent); | |
269 runPendingTasks(); | |
270 } | |
271 | |
272 std::string m_baseURL; | |
273 FrameTestHelpers::WebViewHelper m_webViewHelper; | |
274 }; | |
275 | |
276 TEST_F(TouchActionTest, Simple) | |
277 { | |
278 runTouchActionTest("touch-action-simple.html"); | |
279 } | |
280 | |
281 TEST_F(TouchActionTest, Overflow) | |
282 { | |
283 runTouchActionTest("touch-action-overflow.html"); | |
284 } | |
285 | |
286 } | |
OLD | NEW |