1 // Copyright (c) 2012 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.
5 #include "content/browser/web_contents/touch_editable_impl_aura.h"
7 #include "base/command_line.h"
8 #include "base/run_loop.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/test/test_timeouts.h"
11 #include "base/values.h"
12 #include "content/browser/web_contents/web_contents_impl.h"
13 #include "content/browser/web_contents/web_contents_view_aura.h"
14 #include "content/public/browser/render_frame_host.h"
15 #include "content/public/common/content_switches.h"
16 #include "content/public/test/browser_test_utils.h"
17 #include "content/public/test/content_browser_test.h"
18 #include "content/public/test/content_browser_test_utils.h"
19 #include "content/public/test/test_utils.h"
20 #include "content/shell/browser/shell.h"
21 #include "third_party/WebKit/public/web/WebInputEvent.h"
22 #include "ui/aura/test/event_generator.h"
23 #include "ui/aura/window.h"
24 #include "ui/aura/window_tree_host.h"
25 #include "ui/base/ui_base_switches.h"
26 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
27 #include "ui/events/event_utils.h"
29 using blink::WebInputEvent
;
33 class TestTouchEditableImplAura
: public TouchEditableImplAura
{
35 TestTouchEditableImplAura()
36 : selection_changed_callback_arrived_(false),
37 waiting_for_selection_changed_callback_(false),
38 waiting_for_gesture_ack_type_(WebInputEvent::Undefined
),
39 last_gesture_ack_type_(WebInputEvent::Undefined
) {}
41 virtual void Reset() {
42 selection_changed_callback_arrived_
= false;
43 waiting_for_selection_changed_callback_
= false;
44 waiting_for_gesture_ack_type_
= WebInputEvent::Undefined
;
45 last_gesture_ack_type_
= WebInputEvent::Undefined
;
48 virtual void OnSelectionOrCursorChanged(const gfx::Rect
& anchor
,
49 const gfx::Rect
& focus
) OVERRIDE
{
50 selection_changed_callback_arrived_
= true;
51 TouchEditableImplAura::OnSelectionOrCursorChanged(anchor
, focus
);
52 if (waiting_for_selection_changed_callback_
)
53 selection_changed_wait_run_loop_
->Quit();
56 virtual void GestureEventAck(int gesture_event_type
) OVERRIDE
{
57 last_gesture_ack_type_
=
58 static_cast<WebInputEvent::Type
>(gesture_event_type
);
59 TouchEditableImplAura::GestureEventAck(gesture_event_type
);
60 if (waiting_for_gesture_ack_type_
== gesture_event_type
)
61 gesture_ack_wait_run_loop_
->Quit();
64 virtual void WaitForSelectionChangeCallback() {
65 if (selection_changed_callback_arrived_
)
67 waiting_for_selection_changed_callback_
= true;
68 selection_changed_wait_run_loop_
.reset(new base::RunLoop());
69 selection_changed_wait_run_loop_
->Run();
72 virtual void WaitForGestureAck(WebInputEvent::Type gesture_event_type
) {
73 if (last_gesture_ack_type_
== gesture_event_type
)
75 waiting_for_gesture_ack_type_
= gesture_event_type
;
76 gesture_ack_wait_run_loop_
.reset(new base::RunLoop());
77 gesture_ack_wait_run_loop_
->Run();
81 virtual ~TestTouchEditableImplAura() {}
84 bool selection_changed_callback_arrived_
;
85 bool waiting_for_selection_changed_callback_
;
86 WebInputEvent::Type waiting_for_gesture_ack_type_
;
87 WebInputEvent::Type last_gesture_ack_type_
;
88 scoped_ptr
<base::RunLoop
> selection_changed_wait_run_loop_
;
89 scoped_ptr
<base::RunLoop
> gesture_ack_wait_run_loop_
;
91 DISALLOW_COPY_AND_ASSIGN(TestTouchEditableImplAura
);
94 // This class ignores mouse-moved, mouse-entered and mouse-exited events
95 // without passing them to TouchEditableImplAura. Normally, we should not
96 // receive these events; but, sometimes we receive them which breaks the tests
97 // and makes them flaky: crbug.com/276992.
98 class TestTouchEditableImplAuraIgnoreMouseMovement
99 : public TestTouchEditableImplAura
{
101 TestTouchEditableImplAuraIgnoreMouseMovement() {}
103 virtual bool HandleInputEvent(const ui::Event
* event
) OVERRIDE
{
104 if (event
->type() == ui::ET_MOUSE_ENTERED
||
105 event
->type() == ui::ET_MOUSE_MOVED
||
106 event
->type() == ui::ET_MOUSE_EXITED
) {
109 return TestTouchEditableImplAura::HandleInputEvent(event
);
113 virtual ~TestTouchEditableImplAuraIgnoreMouseMovement() {}
116 DISALLOW_COPY_AND_ASSIGN(TestTouchEditableImplAuraIgnoreMouseMovement
);
119 class TouchEditableImplAuraTest
: public ContentBrowserTest
{
121 TouchEditableImplAuraTest() {}
124 virtual void SetUpCommandLine(CommandLine
* command_line
) OVERRIDE
{
125 command_line
->AppendSwitch(switches::kEnableTouchEditing
);
128 // Executes the javascript synchronously and makes sure the returned value is
130 void ExecuteSyncJSFunction(RenderFrameHost
* rfh
, const std::string
& jscript
) {
131 scoped_ptr
<base::Value
> value
=
132 content::ExecuteScriptAndGetValue(rfh
, jscript
);
135 // Starts the test server and navigates to the given url. Sets a large enough
136 // size to the root window. Returns after the navigation to the url is
138 void StartTestWithPage(const std::string
& url
) {
139 ASSERT_TRUE(test_server()->Start());
140 GURL
test_url(test_server()->GetURL(url
));
141 NavigateToURL(shell(), test_url
);
142 aura::Window
* content
= shell()->web_contents()->GetContentNativeView();
143 content
->GetHost()->SetBounds(gfx::Rect(800, 600));
146 RenderWidgetHostViewAura
* GetRenderWidgetHostViewAura(
147 TouchEditableImplAura
* touch_editable
) {
148 return touch_editable
->rwhva_
;
151 ui::TouchSelectionController
* GetTouchSelectionController(
152 TouchEditableImplAura
* touch_editable
) {
153 return touch_editable
->touch_selection_controller_
.get();
156 ui::TextInputType
GetTextInputType(TouchEditableImplAura
* touch_editable
) {
157 return touch_editable
->text_input_type_
;
161 DISALLOW_COPY_AND_ASSIGN(TouchEditableImplAuraTest
);
164 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest
,
165 TouchSelectionOriginatingFromWebpageTest
) {
166 ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
167 WebContentsImpl
* web_contents
=
168 static_cast<WebContentsImpl
*>(shell()->web_contents());
169 RenderFrameHost
* main_frame
= web_contents
->GetMainFrame();
170 WebContentsViewAura
* view_aura
= static_cast<WebContentsViewAura
*>(
171 web_contents
->GetView());
172 TestTouchEditableImplAura
* touch_editable
=
173 new TestTouchEditableImplAuraIgnoreMouseMovement
;
174 view_aura
->SetTouchEditableForTest(touch_editable
);
175 RenderWidgetHostViewAura
* rwhva
= static_cast<RenderWidgetHostViewAura
*>(
176 web_contents
->GetRenderWidgetHostView());
177 aura::Window
* content
= web_contents
->GetContentNativeView();
178 aura::test::EventGenerator
generator(content
->GetRootWindow(), content
);
179 gfx::Rect bounds
= content
->GetBoundsInRootWindow();
181 touch_editable
->Reset();
182 ExecuteSyncJSFunction(main_frame
, "select_all_text()");
183 touch_editable
->WaitForSelectionChangeCallback();
185 // Tap inside selection to bring up selection handles.
186 generator
.GestureTapAt(gfx::Point(bounds
.x() + 10, bounds
.y() + 10));
187 EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable
), rwhva
);
189 scoped_ptr
<base::Value
> value
=
190 content::ExecuteScriptAndGetValue(main_frame
, "get_selection()");
191 std::string selection
;
192 value
->GetAsString(&selection
);
194 // Check if selection handles are showing.
195 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
196 EXPECT_STREQ("Some text we can select", selection
.c_str());
198 // Lets move the handles a bit to modify the selection
199 touch_editable
->Reset();
200 generator
.GestureScrollSequence(
203 base::TimeDelta::FromMilliseconds(20),
205 touch_editable
->WaitForSelectionChangeCallback();
207 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
208 value
= content::ExecuteScriptAndGetValue(main_frame
, "get_selection()");
209 value
->GetAsString(&selection
);
211 // It is hard to tell what exactly the selection would be now. But it would
212 // definitely be less than whatever was selected before.
213 EXPECT_GT(std::strlen("Some text we can select"), selection
.size());
216 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest
,
217 TestTouchSelectionHiddenWhenScrolling
) {
218 ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
219 WebContentsImpl
* web_contents
=
220 static_cast<WebContentsImpl
*>(shell()->web_contents());
221 RenderFrameHost
* main_frame
= web_contents
->GetMainFrame();
222 WebContentsViewAura
* view_aura
= static_cast<WebContentsViewAura
*>(
223 web_contents
->GetView());
224 TestTouchEditableImplAura
* touch_editable
= new TestTouchEditableImplAura
;
225 view_aura
->SetTouchEditableForTest(touch_editable
);
226 RenderWidgetHostViewAura
* rwhva
= static_cast<RenderWidgetHostViewAura
*>(
227 web_contents
->GetRenderWidgetHostView());
228 EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable
), rwhva
);
230 // Long press to select word.
231 ui::GestureEvent
long_press(ui::ET_GESTURE_LONG_PRESS
,
235 ui::EventTimeForNow(),
236 ui::GestureEventDetails(
237 ui::ET_GESTURE_LONG_PRESS
, 0, 0),
239 touch_editable
->Reset();
240 rwhva
->OnGestureEvent(&long_press
);
241 touch_editable
->WaitForSelectionChangeCallback();
243 // Check if selection handles are showing.
244 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
246 scoped_ptr
<base::Value
> value
=
247 content::ExecuteScriptAndGetValue(main_frame
, "get_selection()");
248 std::string selection
;
249 value
->GetAsString(&selection
);
250 EXPECT_STREQ("Some", selection
.c_str());
252 // Start scrolling. Handles should get hidden.
253 ui::GestureEvent
scroll_begin(ui::ET_GESTURE_SCROLL_BEGIN
,
257 ui::EventTimeForNow(),
258 ui::GestureEventDetails(
259 ui::ET_GESTURE_SCROLL_BEGIN
, 0, 0),
261 rwhva
->OnGestureEvent(&scroll_begin
);
262 EXPECT_FALSE(GetTouchSelectionController(touch_editable
));
264 // Handles should come back after scroll ends.
265 ui::GestureEvent
scroll_end(ui::ET_GESTURE_SCROLL_END
,
269 ui::EventTimeForNow(),
270 ui::GestureEventDetails(
271 ui::ET_GESTURE_SCROLL_END
, 0, 0),
273 rwhva
->OnGestureEvent(&scroll_end
);
274 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
277 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest
,
278 TouchSelectionOnLongPressTest
) {
279 ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
280 WebContentsImpl
* web_contents
=
281 static_cast<WebContentsImpl
*>(shell()->web_contents());
282 RenderFrameHost
* main_frame
= web_contents
->GetMainFrame();
283 WebContentsViewAura
* view_aura
= static_cast<WebContentsViewAura
*>(
284 web_contents
->GetView());
285 TestTouchEditableImplAura
* touch_editable
= new TestTouchEditableImplAura
;
286 view_aura
->SetTouchEditableForTest(touch_editable
);
287 RenderWidgetHostViewAura
* rwhva
= static_cast<RenderWidgetHostViewAura
*>(
288 web_contents
->GetRenderWidgetHostView());
289 EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable
), rwhva
);
291 // Long press to select word.
292 ui::GestureEvent
long_press(ui::ET_GESTURE_LONG_PRESS
,
296 ui::EventTimeForNow(),
297 ui::GestureEventDetails(
298 ui::ET_GESTURE_LONG_PRESS
, 0, 0),
300 touch_editable
->Reset();
301 rwhva
->OnGestureEvent(&long_press
);
302 touch_editable
->WaitForSelectionChangeCallback();
304 // Check if selection handles are showing.
305 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
307 scoped_ptr
<base::Value
> value
=
308 content::ExecuteScriptAndGetValue(main_frame
, "get_selection()");
309 std::string selection
;
310 value
->GetAsString(&selection
);
311 EXPECT_STREQ("Some", selection
.c_str());
314 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest
,
315 TouchSelectionOnDoubleTapTest
) {
316 ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
317 WebContentsImpl
* web_contents
=
318 static_cast<WebContentsImpl
*>(shell()->web_contents());
319 RenderFrameHost
* main_frame
= web_contents
->GetMainFrame();
320 WebContentsViewAura
* view_aura
=
321 static_cast<WebContentsViewAura
*>(web_contents
->GetView());
322 TestTouchEditableImplAura
* touch_editable
= new TestTouchEditableImplAura
;
323 view_aura
->SetTouchEditableForTest(touch_editable
);
324 RenderWidgetHostViewAura
* rwhva
= static_cast<RenderWidgetHostViewAura
*>(
325 web_contents
->GetRenderWidgetHostView());
326 EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable
), rwhva
);
328 // Double-tap to select word.
329 ui::GestureEvent
double_tap(ui::ET_GESTURE_TAP
,
333 ui::EventTimeForNow(),
334 ui::GestureEventDetails(ui::ET_GESTURE_TAP
, 2, 0),
336 touch_editable
->Reset();
337 rwhva
->OnGestureEvent(&double_tap
);
338 touch_editable
->WaitForSelectionChangeCallback();
340 // Check if selection handles are showing.
341 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
343 scoped_ptr
<base::Value
> value
=
344 content::ExecuteScriptAndGetValue(main_frame
, "get_selection()");
345 std::string selection
;
346 value
->GetAsString(&selection
);
347 EXPECT_STREQ("Some", selection
.c_str());
350 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest
,
351 TouchCursorInTextfieldTest
) {
352 ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
353 WebContentsImpl
* web_contents
=
354 static_cast<WebContentsImpl
*>(shell()->web_contents());
355 RenderFrameHost
* main_frame
= web_contents
->GetMainFrame();
356 WebContentsViewAura
* view_aura
= static_cast<WebContentsViewAura
*>(
357 web_contents
->GetView());
358 TestTouchEditableImplAura
* touch_editable
=
359 new TestTouchEditableImplAuraIgnoreMouseMovement
;
360 view_aura
->SetTouchEditableForTest(touch_editable
);
361 RenderWidgetHostViewAura
* rwhva
= static_cast<RenderWidgetHostViewAura
*>(
362 web_contents
->GetRenderWidgetHostView());
363 aura::Window
* content
= web_contents
->GetContentNativeView();
364 aura::test::EventGenerator
generator(content
->GetRootWindow(), content
);
365 gfx::Rect bounds
= content
->GetBoundsInRootWindow();
366 EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable
), rwhva
);
368 ExecuteSyncJSFunction(main_frame
, "focus_textfield()");
369 touch_editable
->WaitForSelectionChangeCallback();
372 touch_editable
->Reset();
373 generator
.GestureTapAt(gfx::Point(bounds
.x() + 50, bounds
.y() + 40));
374 // Tap Down acks are sent synchronously, while Tap acks are asynchronous.
375 touch_editable
->WaitForGestureAck(WebInputEvent::GestureTap
);
376 touch_editable
->WaitForSelectionChangeCallback();
377 touch_editable
->Reset();
379 // Check if cursor handle is showing.
380 EXPECT_NE(ui::TEXT_INPUT_TYPE_NONE
, GetTextInputType(touch_editable
));
381 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
383 scoped_ptr
<base::Value
> value
=
384 content::ExecuteScriptAndGetValue(main_frame
, "get_cursor_position()");
386 value
->GetAsInteger(&cursor_pos
);
387 EXPECT_NE(-1, cursor_pos
);
389 // Move the cursor handle.
390 generator
.GestureScrollSequence(
393 base::TimeDelta::FromMilliseconds(20),
395 touch_editable
->WaitForSelectionChangeCallback();
396 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
397 value
= content::ExecuteScriptAndGetValue(main_frame
,
398 "get_cursor_position()");
399 int new_cursor_pos
= -1;
400 value
->GetAsInteger(&new_cursor_pos
);
401 EXPECT_NE(-1, new_cursor_pos
);
402 // Cursor should have moved.
403 EXPECT_NE(new_cursor_pos
, cursor_pos
);
406 } // namespace content