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/window.h"
23 #include "ui/aura/window_tree_host.h"
24 #include "ui/base/ui_base_switches.h"
25 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
26 #include "ui/events/event_utils.h"
27 #include "ui/events/test/event_generator.h"
28 #include "ui/wm/core/default_screen_position_client.h"
30 using blink::WebInputEvent
;
34 class TestTouchEditableImplAura
: public TouchEditableImplAura
{
36 TestTouchEditableImplAura()
37 : selection_changed_callback_arrived_(false),
38 waiting_for_selection_changed_callback_(false),
39 waiting_for_gesture_ack_type_(WebInputEvent::Undefined
),
40 last_gesture_ack_type_(WebInputEvent::Undefined
),
41 fling_stop_callback_arrived_(false),
42 waiting_for_fling_stop_callback_(false) {}
44 virtual void Reset() {
45 selection_changed_callback_arrived_
= false;
46 waiting_for_selection_changed_callback_
= false;
47 waiting_for_gesture_ack_type_
= WebInputEvent::Undefined
;
48 last_gesture_ack_type_
= WebInputEvent::Undefined
;
49 fling_stop_callback_arrived_
= false;
50 waiting_for_fling_stop_callback_
= false;
53 void OnSelectionOrCursorChanged(const ui::SelectionBound
& anchor
,
54 const ui::SelectionBound
& focus
) override
{
55 selection_changed_callback_arrived_
= true;
56 TouchEditableImplAura::OnSelectionOrCursorChanged(anchor
, focus
);
57 if (waiting_for_selection_changed_callback_
)
58 selection_changed_wait_run_loop_
->Quit();
61 void GestureEventAck(int gesture_event_type
) override
{
62 last_gesture_ack_type_
=
63 static_cast<WebInputEvent::Type
>(gesture_event_type
);
64 TouchEditableImplAura::GestureEventAck(gesture_event_type
);
65 if (waiting_for_gesture_ack_type_
== gesture_event_type
)
66 gesture_ack_wait_run_loop_
->Quit();
69 void DidStopFlinging() override
{
70 fling_stop_callback_arrived_
= true;
71 TouchEditableImplAura::DidStopFlinging();
72 if (waiting_for_fling_stop_callback_
)
73 fling_stop_wait_run_loop_
->Quit();
76 virtual void WaitForSelectionChangeCallback() {
77 if (selection_changed_callback_arrived_
)
79 waiting_for_selection_changed_callback_
= true;
80 selection_changed_wait_run_loop_
.reset(new base::RunLoop());
81 selection_changed_wait_run_loop_
->Run();
84 virtual void WaitForGestureAck(WebInputEvent::Type gesture_event_type
) {
85 if (last_gesture_ack_type_
== gesture_event_type
)
87 waiting_for_gesture_ack_type_
= gesture_event_type
;
88 gesture_ack_wait_run_loop_
.reset(new base::RunLoop());
89 gesture_ack_wait_run_loop_
->Run();
92 virtual void WaitForFlingStopCallback() {
93 if (fling_stop_callback_arrived_
)
95 waiting_for_fling_stop_callback_
= true;
96 fling_stop_wait_run_loop_
.reset(new base::RunLoop());
97 fling_stop_wait_run_loop_
->Run();
101 virtual ~TestTouchEditableImplAura() {}
104 bool selection_changed_callback_arrived_
;
105 bool waiting_for_selection_changed_callback_
;
106 WebInputEvent::Type waiting_for_gesture_ack_type_
;
107 WebInputEvent::Type last_gesture_ack_type_
;
108 bool fling_stop_callback_arrived_
;
109 bool waiting_for_fling_stop_callback_
;
110 scoped_ptr
<base::RunLoop
> selection_changed_wait_run_loop_
;
111 scoped_ptr
<base::RunLoop
> gesture_ack_wait_run_loop_
;
112 scoped_ptr
<base::RunLoop
> fling_stop_wait_run_loop_
;
114 DISALLOW_COPY_AND_ASSIGN(TestTouchEditableImplAura
);
117 class TouchEditableImplAuraTest
: public ContentBrowserTest
{
119 TouchEditableImplAuraTest() {}
122 void SetUpOnMainThread() override
{
123 ContentBrowserTest::SetUpOnMainThread();
124 aura::client::SetScreenPositionClient(shell()->window()->GetRootWindow(),
125 &screen_position_client_
);
128 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
129 command_line
->AppendSwitch(switches::kEnableTouchEditing
);
132 // Executes the javascript synchronously and makes sure the returned value is
134 void ExecuteSyncJSFunction(RenderFrameHost
* rfh
, const std::string
& jscript
) {
135 scoped_ptr
<base::Value
> value
=
136 content::ExecuteScriptAndGetValue(rfh
, jscript
);
139 // Starts the test server and navigates to the given url. Sets a large enough
140 // size to the root window. Returns after the navigation to the url is
142 void StartTestWithPage(const std::string
& url
) {
143 ASSERT_TRUE(test_server()->Start());
144 GURL
test_url(test_server()->GetURL(url
));
145 NavigateToURL(shell(), test_url
);
146 aura::Window
* content
= shell()->web_contents()->GetContentNativeView();
147 content
->GetHost()->SetBounds(gfx::Rect(800, 600));
150 RenderWidgetHostViewAura
* GetRenderWidgetHostViewAura(
151 TouchEditableImplAura
* touch_editable
) {
152 return touch_editable
->rwhva_
;
155 ui::TouchEditingControllerDeprecated
* GetTouchSelectionController(
156 TouchEditableImplAura
* touch_editable
) {
157 return touch_editable
->touch_selection_controller_
.get();
160 ui::TextInputType
GetTextInputType(TouchEditableImplAura
* touch_editable
) {
161 return touch_editable
->text_input_type_
;
165 wm::DefaultScreenPositionClient screen_position_client_
;
167 DISALLOW_COPY_AND_ASSIGN(TouchEditableImplAuraTest
);
170 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest
,
171 TouchSelectionOriginatingFromWebpageTest
) {
172 ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
173 WebContentsImpl
* web_contents
=
174 static_cast<WebContentsImpl
*>(shell()->web_contents());
175 RenderFrameHost
* main_frame
= web_contents
->GetMainFrame();
176 WebContentsViewAura
* view_aura
= static_cast<WebContentsViewAura
*>(
177 web_contents
->GetView());
178 TestTouchEditableImplAura
* touch_editable
= new TestTouchEditableImplAura
;
179 view_aura
->SetTouchEditableForTest(touch_editable
);
180 RenderWidgetHostViewAura
* rwhva
= static_cast<RenderWidgetHostViewAura
*>(
181 web_contents
->GetRenderWidgetHostView());
182 aura::Window
* content
= web_contents
->GetContentNativeView();
183 ui::test::EventGenerator
generator(content
->GetRootWindow(), content
);
184 gfx::Rect bounds
= content
->GetBoundsInRootWindow();
186 touch_editable
->Reset();
187 ExecuteSyncJSFunction(main_frame
, "select_all_text()");
188 touch_editable
->WaitForSelectionChangeCallback();
190 // Tap inside selection to bring up selection handles.
191 generator
.GestureTapAt(gfx::Point(bounds
.x() + 10, bounds
.y() + 10));
192 EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable
), rwhva
);
194 scoped_ptr
<base::Value
> value
=
195 content::ExecuteScriptAndGetValue(main_frame
, "get_selection()");
196 std::string selection
;
197 value
->GetAsString(&selection
);
199 // Check if selection handles are showing.
200 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
201 EXPECT_STREQ("Some text we can select", selection
.c_str());
203 // Lets move the handles a bit to modify the selection
204 touch_editable
->Reset();
205 ui::SelectionBound anchor
, focus
;
206 touch_editable
->GetSelectionEndPoints(&anchor
, &focus
);
207 // The distance by which a handle image is offset from the bottom of the
208 // selection/text baseline.
209 const int kSelectionHandleVerticalVisualOffset
= 2;
210 int handle_grab_x
= bounds
.x() + anchor
.edge_bottom_rounded().x();
211 int handle_grab_y
= bounds
.y() + anchor
.edge_bottom_rounded().y() +
212 kSelectionHandleVerticalVisualOffset
+ 1;
213 generator
.GestureScrollSequence(
214 gfx::Point(handle_grab_x
, handle_grab_y
),
215 gfx::Point(handle_grab_x
+ 20, handle_grab_y
),
216 base::TimeDelta::FromMilliseconds(20),
218 touch_editable
->WaitForSelectionChangeCallback();
220 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
221 value
= content::ExecuteScriptAndGetValue(main_frame
, "get_selection()");
222 value
->GetAsString(&selection
);
224 // It is hard to tell what exactly the selection would be now. But it would
225 // definitely be less than whatever was selected before.
226 EXPECT_GT(std::strlen("Some text we can select"), selection
.size());
229 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest
,
230 TestTouchSelectionHiddenWhenScrolling
) {
231 ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
232 WebContentsImpl
* web_contents
=
233 static_cast<WebContentsImpl
*>(shell()->web_contents());
234 RenderFrameHost
* main_frame
= web_contents
->GetMainFrame();
235 WebContentsViewAura
* view_aura
= static_cast<WebContentsViewAura
*>(
236 web_contents
->GetView());
237 TestTouchEditableImplAura
* touch_editable
= new TestTouchEditableImplAura
;
238 view_aura
->SetTouchEditableForTest(touch_editable
);
239 RenderWidgetHostViewAura
* rwhva
= static_cast<RenderWidgetHostViewAura
*>(
240 web_contents
->GetRenderWidgetHostView());
241 EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable
), rwhva
);
243 // Long press to select word.
244 ui::GestureEvent
long_press(
248 ui::EventTimeForNow(),
249 ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS
));
250 touch_editable
->Reset();
251 rwhva
->OnGestureEvent(&long_press
);
252 touch_editable
->WaitForSelectionChangeCallback();
254 // Check if selection handles are showing.
255 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
257 scoped_ptr
<base::Value
> value
=
258 content::ExecuteScriptAndGetValue(main_frame
, "get_selection()");
259 std::string selection
;
260 value
->GetAsString(&selection
);
261 EXPECT_STREQ("Some", selection
.c_str());
263 // Start scrolling. Handles should get hidden.
264 ui::GestureEvent
scroll_begin(
268 ui::EventTimeForNow(),
269 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN
));
270 rwhva
->OnGestureEvent(&scroll_begin
);
271 EXPECT_FALSE(GetTouchSelectionController(touch_editable
));
273 // Handles should come back after scroll ends.
274 ui::GestureEvent
scroll_end(
278 ui::EventTimeForNow(),
279 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END
));
280 rwhva
->OnGestureEvent(&scroll_end
);
281 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
284 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest
,
285 TestTouchSelectionReshownAfterFling
) {
286 ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
287 WebContentsImpl
* web_contents
=
288 static_cast<WebContentsImpl
*>(shell()->web_contents());
289 RenderFrameHost
* main_frame
= web_contents
->GetMainFrame();
290 WebContentsViewAura
* view_aura
= static_cast<WebContentsViewAura
*>(
291 web_contents
->GetView());
292 TestTouchEditableImplAura
* touch_editable
= new TestTouchEditableImplAura
;
293 view_aura
->SetTouchEditableForTest(touch_editable
);
294 RenderWidgetHostViewAura
* rwhva
= static_cast<RenderWidgetHostViewAura
*>(
295 web_contents
->GetRenderWidgetHostView());
296 EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable
), rwhva
);
298 // Long press to select word.
299 ui::GestureEvent
long_press(
303 ui::EventTimeForNow(),
304 ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS
));
305 touch_editable
->Reset();
306 rwhva
->OnGestureEvent(&long_press
);
307 touch_editable
->WaitForSelectionChangeCallback();
309 // Check if selection handles are showing.
310 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
312 scoped_ptr
<base::Value
> value
=
313 content::ExecuteScriptAndGetValue(main_frame
, "get_selection()");
314 std::string selection
;
315 value
->GetAsString(&selection
);
316 EXPECT_STREQ("Some", selection
.c_str());
318 // Start scrolling. Handles should get hidden.
319 ui::GestureEvent
scroll_begin(
323 ui::EventTimeForNow(),
324 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN
, 0, 0));
325 rwhva
->OnGestureEvent(&scroll_begin
);
326 EXPECT_FALSE(GetTouchSelectionController(touch_editable
));
328 // Start a fling. Handles should come back after fling stops.
329 ui::GestureEvent
fling_start(
333 ui::EventTimeForNow(),
334 ui::GestureEventDetails(ui::ET_SCROLL_FLING_START
, 1, 0));
335 rwhva
->OnGestureEvent(&fling_start
);
336 touch_editable
->WaitForFlingStopCallback();
337 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
340 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest
,
341 TouchSelectionOnLongPressTest
) {
342 ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
343 WebContentsImpl
* web_contents
=
344 static_cast<WebContentsImpl
*>(shell()->web_contents());
345 RenderFrameHost
* main_frame
= web_contents
->GetMainFrame();
346 WebContentsViewAura
* view_aura
= static_cast<WebContentsViewAura
*>(
347 web_contents
->GetView());
348 TestTouchEditableImplAura
* touch_editable
= new TestTouchEditableImplAura
;
349 view_aura
->SetTouchEditableForTest(touch_editable
);
350 RenderWidgetHostViewAura
* rwhva
= static_cast<RenderWidgetHostViewAura
*>(
351 web_contents
->GetRenderWidgetHostView());
352 EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable
), rwhva
);
354 // Long press to select word.
355 ui::GestureEvent
long_press(
359 ui::EventTimeForNow(),
360 ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS
));
361 touch_editable
->Reset();
362 rwhva
->OnGestureEvent(&long_press
);
363 touch_editable
->WaitForSelectionChangeCallback();
365 // Check if selection handles are showing.
366 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
368 scoped_ptr
<base::Value
> value
=
369 content::ExecuteScriptAndGetValue(main_frame
, "get_selection()");
370 std::string selection
;
371 value
->GetAsString(&selection
);
372 EXPECT_STREQ("Some", selection
.c_str());
375 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest
,
376 NoTouchSelectionOnDoubleTapTest
) {
377 ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
378 WebContentsImpl
* web_contents
=
379 static_cast<WebContentsImpl
*>(shell()->web_contents());
380 RenderFrameHost
* main_frame
= web_contents
->GetMainFrame();
381 WebContentsViewAura
* view_aura
=
382 static_cast<WebContentsViewAura
*>(web_contents
->GetView());
383 TestTouchEditableImplAura
* touch_editable
= new TestTouchEditableImplAura
;
384 view_aura
->SetTouchEditableForTest(touch_editable
);
385 RenderWidgetHostViewAura
* rwhva
= static_cast<RenderWidgetHostViewAura
*>(
386 web_contents
->GetRenderWidgetHostView());
387 EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable
), rwhva
);
389 // Double-tap to select word.
390 ui::GestureEventDetails
details(ui::ET_GESTURE_TAP
);
391 details
.set_tap_count(2);
392 ui::GestureEvent
double_tap(10, 10, 0, ui::EventTimeForNow(), details
);
393 touch_editable
->Reset();
394 rwhva
->OnGestureEvent(&double_tap
);
395 touch_editable
->WaitForSelectionChangeCallback();
397 // Make sure touch selection handles are not showing.
398 EXPECT_FALSE(GetTouchSelectionController(touch_editable
));
400 scoped_ptr
<base::Value
> value
=
401 content::ExecuteScriptAndGetValue(main_frame
, "get_selection()");
402 std::string selection
;
403 value
->GetAsString(&selection
);
404 EXPECT_STREQ("Some", selection
.c_str());
407 #if defined(OS_CHROMEOS)
408 // http://crbug.com/396509
409 #define MAYBE_TouchCursorInTextfieldTest DISABLED_TouchCursorInTextfieldTest
411 #define MAYBE_TouchCursorInTextfieldTest TouchCursorInTextfieldTest
413 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest
,
414 MAYBE_TouchCursorInTextfieldTest
) {
415 ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
416 WebContentsImpl
* web_contents
=
417 static_cast<WebContentsImpl
*>(shell()->web_contents());
418 RenderFrameHost
* main_frame
= web_contents
->GetMainFrame();
419 WebContentsViewAura
* view_aura
= static_cast<WebContentsViewAura
*>(
420 web_contents
->GetView());
421 TestTouchEditableImplAura
* touch_editable
= new TestTouchEditableImplAura
;
422 view_aura
->SetTouchEditableForTest(touch_editable
);
423 RenderWidgetHostViewAura
* rwhva
= static_cast<RenderWidgetHostViewAura
*>(
424 web_contents
->GetRenderWidgetHostView());
425 aura::Window
* content
= web_contents
->GetContentNativeView();
426 ui::test::EventGenerator
generator(content
->GetRootWindow(), content
);
427 gfx::Rect bounds
= content
->GetBoundsInRootWindow();
428 EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable
), rwhva
);
430 ExecuteSyncJSFunction(main_frame
, "focus_textfield()");
431 touch_editable
->WaitForSelectionChangeCallback();
434 touch_editable
->Reset();
435 generator
.GestureTapAt(gfx::Point(bounds
.x() + 50, bounds
.y() + 40));
436 // Tap Down acks are sent synchronously, while Tap acks are asynchronous.
437 touch_editable
->WaitForGestureAck(WebInputEvent::GestureTap
);
438 touch_editable
->WaitForSelectionChangeCallback();
439 touch_editable
->Reset();
441 // Check if cursor handle is showing.
442 EXPECT_NE(ui::TEXT_INPUT_TYPE_NONE
, GetTextInputType(touch_editable
));
443 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
445 scoped_ptr
<base::Value
> value
=
446 content::ExecuteScriptAndGetValue(main_frame
, "get_cursor_position()");
448 value
->GetAsInteger(&cursor_pos
);
449 EXPECT_NE(-1, cursor_pos
);
451 // Move the cursor handle.
452 generator
.GestureScrollSequence(
455 base::TimeDelta::FromMilliseconds(20),
457 touch_editable
->WaitForSelectionChangeCallback();
458 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
459 value
= content::ExecuteScriptAndGetValue(main_frame
,
460 "get_cursor_position()");
461 int new_cursor_pos
= -1;
462 value
->GetAsInteger(&new_cursor_pos
);
463 EXPECT_NE(-1, new_cursor_pos
);
464 // Cursor should have moved.
465 EXPECT_NE(new_cursor_pos
, cursor_pos
);
468 } // namespace content