OLD | NEW |
| (Empty) |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "base/test/scoped_feature_list.h" | |
6 #include "content/browser/renderer_host/render_widget_host_impl.h" | |
7 #include "content/browser/renderer_host/render_widget_host_input_event_router.h" | |
8 #include "content/browser/web_contents/web_contents_impl.h" | |
9 #include "content/common/input/synthetic_web_input_event_builders.h" | |
10 #include "content/public/common/content_features.h" | |
11 #include "content/public/test/browser_test_utils.h" | |
12 #include "content/public/test/content_browser_test.h" | |
13 #include "content/public/test/content_browser_test_utils.h" | |
14 #include "content/shell/browser/shell.h" | |
15 #include "ui/events/gesture_detection/gesture_configuration.h" | |
16 | |
17 #if defined(OS_ANDROID) | |
18 #include "content/browser/renderer_host/render_widget_host_view_android.h" | |
19 #endif | |
20 | |
21 using blink::WebMouseWheelEvent; | |
22 | |
23 namespace { | |
24 void GiveItSomeTime() { | |
25 base::RunLoop run_loop; | |
26 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | |
27 FROM_HERE, run_loop.QuitClosure(), base::TimeDelta::FromMilliseconds(10)); | |
28 run_loop.Run(); | |
29 } | |
30 | |
31 const char kWheelEventLatchingDataURL[] = | |
32 "data:text/html;charset=utf-8," | |
33 "<!DOCTYPE html>" | |
34 "<style>" | |
35 "body {" | |
36 " height: 10000px;" | |
37 "}" | |
38 "#scrollableDiv {" | |
39 " position: absolute;" | |
40 " left: 100px;" | |
41 " top: 200px;" | |
42 " width: 400px;" | |
43 " height: 800px;" | |
44 " overflow: scroll;" | |
45 " background: red;" | |
46 "}" | |
47 "#nestedDiv {" | |
48 " width: 400px;" | |
49 " height: 8000px;" | |
50 " opacity: 0;" | |
51 "}" | |
52 "</style>" | |
53 "<div id='scrollableDiv'>" | |
54 " <div id='nestedDiv'></div>" | |
55 "</div>" | |
56 "<script>" | |
57 " var scrollableDiv = document.getElementById('scrollableDiv');" | |
58 " var scrollableDivWheelEventCounter = 0;" | |
59 " var documentWheelEventCounter = 0;" | |
60 " scrollableDiv.addEventListener('wheel'," | |
61 " function(e) { scrollableDivWheelEventCounter++;" | |
62 " e.stopPropagation(); });" | |
63 " document.scrollingElement.addEventListener('wheel'," | |
64 " function(e) { documentWheelEventCounter++; });" | |
65 "</script>"; | |
66 } // namespace | |
67 | |
68 namespace content { | |
69 class WheelScrollLatchingBrowserTest : public ContentBrowserTest { | |
70 public: | |
71 WheelScrollLatchingBrowserTest(bool wheel_scroll_latching_enabled = true) | |
72 : wheel_scroll_latching_enabled_(wheel_scroll_latching_enabled) { | |
73 ui::GestureConfiguration::GetInstance()->set_scroll_debounce_interval_in_ms( | |
74 0); | |
75 | |
76 if (wheel_scroll_latching_enabled_) | |
77 EnableWheelScrollLatching(); | |
78 else | |
79 DisableWheelScrollLatching(); | |
80 } | |
81 ~WheelScrollLatchingBrowserTest() override {} | |
82 | |
83 protected: | |
84 RenderWidgetHostImpl* GetWidgetHost() { | |
85 return RenderWidgetHostImpl::From( | |
86 web_contents()->GetRenderViewHost()->GetWidget()); | |
87 } | |
88 | |
89 WebContentsImpl* web_contents() const { | |
90 return static_cast<WebContentsImpl*>(shell()->web_contents()); | |
91 } | |
92 | |
93 RenderWidgetHostInputEventRouter* GetRouter() { | |
94 return web_contents()->GetInputEventRouter(); | |
95 } | |
96 | |
97 RenderWidgetHostViewBase* GetRootView() { | |
98 return static_cast<RenderWidgetHostViewBase*>(web_contents() | |
99 ->GetFrameTree() | |
100 ->root() | |
101 ->current_frame_host() | |
102 ->GetView()); | |
103 } | |
104 | |
105 float GetPageScaleFactor() { | |
106 return GetWidgetHost()->last_frame_metadata().page_scale_factor; | |
107 } | |
108 | |
109 void LoadURL() { | |
110 const GURL data_url(kWheelEventLatchingDataURL); | |
111 NavigateToURL(shell(), data_url); | |
112 | |
113 RenderWidgetHostImpl* host = GetWidgetHost(); | |
114 host->GetView()->SetSize(gfx::Size(600, 600)); | |
115 | |
116 // The page is loaded in the renderer, wait for a new frame to arrive. | |
117 while (!host->ScheduleComposite()) | |
118 GiveItSomeTime(); | |
119 } | |
120 int ExecuteScriptAndExtractInt(const std::string& script) { | |
121 int value = 0; | |
122 EXPECT_TRUE(content::ExecuteScriptAndExtractInt( | |
123 shell(), "domAutomationController.send(" + script + ")", &value)); | |
124 return value; | |
125 } | |
126 void EnableWheelScrollLatching() { | |
127 feature_list_.InitFromCommandLine( | |
128 features::kTouchpadAndWheelScrollLatching.name, ""); | |
129 } | |
130 void DisableWheelScrollLatching() { | |
131 feature_list_.InitFromCommandLine( | |
132 "", features::kTouchpadAndWheelScrollLatching.name); | |
133 } | |
134 | |
135 void WheelEventTargetTest() { | |
136 LoadURL(); | |
137 EXPECT_EQ(0, ExecuteScriptAndExtractInt("documentWheelEventCounter")); | |
138 EXPECT_EQ(0, ExecuteScriptAndExtractInt("scrollableDivWheelEventCounter")); | |
139 | |
140 FrameWatcher frame_watcher(shell()->web_contents()); | |
141 scoped_refptr<InputMsgWatcher> input_msg_watcher(new InputMsgWatcher( | |
142 GetWidgetHost(), blink::WebInputEvent::kMouseWheel)); | |
143 float scale_factor = GetPageScaleFactor(); | |
144 | |
145 float scrollable_div_top = | |
146 ExecuteScriptAndExtractInt("scrollableDiv.getBoundingClientRect().top"); | |
147 float x = scale_factor * | |
148 (ExecuteScriptAndExtractInt( | |
149 "scrollableDiv.getBoundingClientRect().left") + | |
150 ExecuteScriptAndExtractInt( | |
151 "scrollableDiv.getBoundingClientRect().right")) / | |
152 2; | |
153 float y = scale_factor * 0.5 * scrollable_div_top; | |
154 float delta_x = 0; | |
155 float delta_y = -0.5 * scrollable_div_top; | |
156 blink::WebMouseWheelEvent wheel_event = | |
157 SyntheticWebMouseWheelEventBuilder::Build(x, y, x, y, delta_x, delta_y, | |
158 0, true); | |
159 | |
160 wheel_event.phase = blink::WebMouseWheelEvent::kPhaseBegan; | |
161 GetRouter()->RouteMouseWheelEvent(GetRootView(), &wheel_event, | |
162 ui::LatencyInfo()); | |
163 | |
164 // Runs until we get the InputMsgAck callback. | |
165 EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, | |
166 input_msg_watcher->WaitForAck()); | |
167 | |
168 while (ExecuteScriptAndExtractInt("document.scrollingElement.scrollTop") < | |
169 -delta_y) { | |
170 frame_watcher.WaitFrames(1); | |
171 } | |
172 | |
173 EXPECT_EQ(0, ExecuteScriptAndExtractInt("scrollableDiv.scrollTop")); | |
174 EXPECT_EQ(1, ExecuteScriptAndExtractInt("documentWheelEventCounter")); | |
175 EXPECT_EQ(0, ExecuteScriptAndExtractInt("scrollableDivWheelEventCounter")); | |
176 | |
177 wheel_event.phase = blink::WebMouseWheelEvent::kPhaseChanged; | |
178 GetRouter()->RouteMouseWheelEvent(GetRootView(), &wheel_event, | |
179 ui::LatencyInfo()); | |
180 | |
181 // Runs until we get the InputMsgAck callback. | |
182 EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, | |
183 input_msg_watcher->WaitForAck()); | |
184 | |
185 if (wheel_scroll_latching_enabled_) { | |
186 while (ExecuteScriptAndExtractInt("document.scrollingElement.scrollTop") < | |
187 -2 * delta_y) { | |
188 frame_watcher.WaitFrames(1); | |
189 } | |
190 | |
191 EXPECT_EQ(0, ExecuteScriptAndExtractInt("scrollableDiv.scrollTop")); | |
192 EXPECT_EQ(2, ExecuteScriptAndExtractInt("documentWheelEventCounter")); | |
193 EXPECT_EQ(0, | |
194 ExecuteScriptAndExtractInt("scrollableDivWheelEventCounter")); | |
195 } else { // !wheel_scroll_latching_enabled_ | |
196 while (ExecuteScriptAndExtractInt("scrollableDiv.scrollTop") < -delta_y) | |
197 frame_watcher.WaitFrames(1); | |
198 | |
199 EXPECT_EQ(1, ExecuteScriptAndExtractInt("documentWheelEventCounter")); | |
200 EXPECT_EQ(1, | |
201 ExecuteScriptAndExtractInt("scrollableDivWheelEventCounter")); | |
202 } | |
203 } | |
204 | |
205 private: | |
206 base::test::ScopedFeatureList feature_list_; | |
207 bool wheel_scroll_latching_enabled_; | |
208 }; | |
209 | |
210 class WheelScrollLatchingDisabledBrowserTest | |
211 : public WheelScrollLatchingBrowserTest { | |
212 public: | |
213 WheelScrollLatchingDisabledBrowserTest() | |
214 : WheelScrollLatchingBrowserTest(false) {} | |
215 ~WheelScrollLatchingDisabledBrowserTest() override {} | |
216 }; | |
217 | |
218 // Start scrolling by mouse wheel on the document: the wheel event will be sent | |
219 // to the document's scrolling element, the scrollable div will be under the | |
220 // cursor after applying the scrolling. Continue scrolling by mouse wheel, since | |
221 // wheel scroll latching is enabled the wheel event will be still sent to the | |
222 // document's scrolling element and the document's scrolling element will | |
223 // continue scrolling. | |
224 IN_PROC_BROWSER_TEST_F(WheelScrollLatchingBrowserTest, WheelEventTarget) { | |
225 WheelEventTargetTest(); | |
226 } | |
227 | |
228 // Start scrolling by mouse wheel on the document: the wheel event will be sent | |
229 // to the document's scrolling element, the scrollable div will be under the | |
230 // cursor after applying the scrolloffsets. Continue scrolling by mouse wheel, | |
231 // since wheel scroll latching is disabled the wheel event will be still sent to | |
232 // the scrollable div which is currently under the cursor. The div will start | |
233 // scrolling. | |
234 IN_PROC_BROWSER_TEST_F(WheelScrollLatchingDisabledBrowserTest, | |
235 WheelEventTarget) { | |
236 WheelEventTargetTest(); | |
237 } | |
238 | |
239 } // namespace content | |
OLD | NEW |