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"
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
),
40 fling_stop_callback_arrived_(false),
41 waiting_for_fling_stop_callback_(false) {}
43 virtual void Reset() {
44 selection_changed_callback_arrived_
= false;
45 waiting_for_selection_changed_callback_
= false;
46 waiting_for_gesture_ack_type_
= WebInputEvent::Undefined
;
47 last_gesture_ack_type_
= WebInputEvent::Undefined
;
48 fling_stop_callback_arrived_
= false;
49 waiting_for_fling_stop_callback_
= false;
52 virtual void OnSelectionOrCursorChanged(const gfx::Rect
& anchor
,
53 const gfx::Rect
& focus
) override
{
54 selection_changed_callback_arrived_
= true;
55 TouchEditableImplAura::OnSelectionOrCursorChanged(anchor
, focus
);
56 if (waiting_for_selection_changed_callback_
)
57 selection_changed_wait_run_loop_
->Quit();
60 virtual void GestureEventAck(int gesture_event_type
) override
{
61 last_gesture_ack_type_
=
62 static_cast<WebInputEvent::Type
>(gesture_event_type
);
63 TouchEditableImplAura::GestureEventAck(gesture_event_type
);
64 if (waiting_for_gesture_ack_type_
== gesture_event_type
)
65 gesture_ack_wait_run_loop_
->Quit();
68 virtual void DidStopFlinging() override
{
69 fling_stop_callback_arrived_
= true;
70 TouchEditableImplAura::DidStopFlinging();
71 if (waiting_for_fling_stop_callback_
)
72 fling_stop_wait_run_loop_
->Quit();
75 virtual void WaitForSelectionChangeCallback() {
76 if (selection_changed_callback_arrived_
)
78 waiting_for_selection_changed_callback_
= true;
79 selection_changed_wait_run_loop_
.reset(new base::RunLoop());
80 selection_changed_wait_run_loop_
->Run();
83 virtual void WaitForGestureAck(WebInputEvent::Type gesture_event_type
) {
84 if (last_gesture_ack_type_
== gesture_event_type
)
86 waiting_for_gesture_ack_type_
= gesture_event_type
;
87 gesture_ack_wait_run_loop_
.reset(new base::RunLoop());
88 gesture_ack_wait_run_loop_
->Run();
91 virtual void WaitForFlingStopCallback() {
92 if (fling_stop_callback_arrived_
)
94 waiting_for_fling_stop_callback_
= true;
95 fling_stop_wait_run_loop_
.reset(new base::RunLoop());
96 fling_stop_wait_run_loop_
->Run();
100 virtual ~TestTouchEditableImplAura() {}
103 bool selection_changed_callback_arrived_
;
104 bool waiting_for_selection_changed_callback_
;
105 WebInputEvent::Type waiting_for_gesture_ack_type_
;
106 WebInputEvent::Type last_gesture_ack_type_
;
107 bool fling_stop_callback_arrived_
;
108 bool waiting_for_fling_stop_callback_
;
109 scoped_ptr
<base::RunLoop
> selection_changed_wait_run_loop_
;
110 scoped_ptr
<base::RunLoop
> gesture_ack_wait_run_loop_
;
111 scoped_ptr
<base::RunLoop
> fling_stop_wait_run_loop_
;
113 DISALLOW_COPY_AND_ASSIGN(TestTouchEditableImplAura
);
116 class TouchEditableImplAuraTest
: public ContentBrowserTest
{
118 TouchEditableImplAuraTest() {}
121 virtual void SetUpCommandLine(CommandLine
* command_line
) override
{
122 command_line
->AppendSwitch(switches::kEnableTouchEditing
);
125 // Executes the javascript synchronously and makes sure the returned value is
127 void ExecuteSyncJSFunction(RenderFrameHost
* rfh
, const std::string
& jscript
) {
128 scoped_ptr
<base::Value
> value
=
129 content::ExecuteScriptAndGetValue(rfh
, jscript
);
132 // Starts the test server and navigates to the given url. Sets a large enough
133 // size to the root window. Returns after the navigation to the url is
135 void StartTestWithPage(const std::string
& url
) {
136 ASSERT_TRUE(test_server()->Start());
137 GURL
test_url(test_server()->GetURL(url
));
138 NavigateToURL(shell(), test_url
);
139 aura::Window
* content
= shell()->web_contents()->GetContentNativeView();
140 content
->GetHost()->SetBounds(gfx::Rect(800, 600));
143 RenderWidgetHostViewAura
* GetRenderWidgetHostViewAura(
144 TouchEditableImplAura
* touch_editable
) {
145 return touch_editable
->rwhva_
;
148 ui::TouchSelectionController
* GetTouchSelectionController(
149 TouchEditableImplAura
* touch_editable
) {
150 return touch_editable
->touch_selection_controller_
.get();
153 ui::TextInputType
GetTextInputType(TouchEditableImplAura
* touch_editable
) {
154 return touch_editable
->text_input_type_
;
158 DISALLOW_COPY_AND_ASSIGN(TouchEditableImplAuraTest
);
161 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest
,
162 TouchSelectionOriginatingFromWebpageTest
) {
163 ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
164 WebContentsImpl
* web_contents
=
165 static_cast<WebContentsImpl
*>(shell()->web_contents());
166 RenderFrameHost
* main_frame
= web_contents
->GetMainFrame();
167 WebContentsViewAura
* view_aura
= static_cast<WebContentsViewAura
*>(
168 web_contents
->GetView());
169 TestTouchEditableImplAura
* touch_editable
= new TestTouchEditableImplAura
;
170 view_aura
->SetTouchEditableForTest(touch_editable
);
171 RenderWidgetHostViewAura
* rwhva
= static_cast<RenderWidgetHostViewAura
*>(
172 web_contents
->GetRenderWidgetHostView());
173 aura::Window
* content
= web_contents
->GetContentNativeView();
174 ui::test::EventGenerator
generator(content
->GetRootWindow(), content
);
175 gfx::Rect bounds
= content
->GetBoundsInRootWindow();
177 touch_editable
->Reset();
178 ExecuteSyncJSFunction(main_frame
, "select_all_text()");
179 touch_editable
->WaitForSelectionChangeCallback();
181 // Tap inside selection to bring up selection handles.
182 generator
.GestureTapAt(gfx::Point(bounds
.x() + 10, bounds
.y() + 10));
183 EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable
), rwhva
);
185 scoped_ptr
<base::Value
> value
=
186 content::ExecuteScriptAndGetValue(main_frame
, "get_selection()");
187 std::string selection
;
188 value
->GetAsString(&selection
);
190 // Check if selection handles are showing.
191 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
192 EXPECT_STREQ("Some text we can select", selection
.c_str());
194 // Lets move the handles a bit to modify the selection
195 touch_editable
->Reset();
196 generator
.GestureScrollSequence(
199 base::TimeDelta::FromMilliseconds(20),
201 touch_editable
->WaitForSelectionChangeCallback();
203 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
204 value
= content::ExecuteScriptAndGetValue(main_frame
, "get_selection()");
205 value
->GetAsString(&selection
);
207 // It is hard to tell what exactly the selection would be now. But it would
208 // definitely be less than whatever was selected before.
209 EXPECT_GT(std::strlen("Some text we can select"), selection
.size());
212 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest
,
213 TestTouchSelectionHiddenWhenScrolling
) {
214 ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
215 WebContentsImpl
* web_contents
=
216 static_cast<WebContentsImpl
*>(shell()->web_contents());
217 RenderFrameHost
* main_frame
= web_contents
->GetMainFrame();
218 WebContentsViewAura
* view_aura
= static_cast<WebContentsViewAura
*>(
219 web_contents
->GetView());
220 TestTouchEditableImplAura
* touch_editable
= new TestTouchEditableImplAura
;
221 view_aura
->SetTouchEditableForTest(touch_editable
);
222 RenderWidgetHostViewAura
* rwhva
= static_cast<RenderWidgetHostViewAura
*>(
223 web_contents
->GetRenderWidgetHostView());
224 EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable
), rwhva
);
226 // Long press to select word.
227 ui::GestureEvent
long_press(
231 ui::EventTimeForNow(),
232 ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS
));
233 touch_editable
->Reset();
234 rwhva
->OnGestureEvent(&long_press
);
235 touch_editable
->WaitForSelectionChangeCallback();
237 // Check if selection handles are showing.
238 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
240 scoped_ptr
<base::Value
> value
=
241 content::ExecuteScriptAndGetValue(main_frame
, "get_selection()");
242 std::string selection
;
243 value
->GetAsString(&selection
);
244 EXPECT_STREQ("Some", selection
.c_str());
246 // Start scrolling. Handles should get hidden.
247 ui::GestureEvent
scroll_begin(
251 ui::EventTimeForNow(),
252 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN
));
253 rwhva
->OnGestureEvent(&scroll_begin
);
254 EXPECT_FALSE(GetTouchSelectionController(touch_editable
));
256 // Handles should come back after scroll ends.
257 ui::GestureEvent
scroll_end(
261 ui::EventTimeForNow(),
262 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END
));
263 rwhva
->OnGestureEvent(&scroll_end
);
264 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
267 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest
,
268 TestTouchSelectionReshownAfterFling
) {
269 ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
270 WebContentsImpl
* web_contents
=
271 static_cast<WebContentsImpl
*>(shell()->web_contents());
272 RenderFrameHost
* main_frame
= web_contents
->GetMainFrame();
273 WebContentsViewAura
* view_aura
= static_cast<WebContentsViewAura
*>(
274 web_contents
->GetView());
275 TestTouchEditableImplAura
* touch_editable
= new TestTouchEditableImplAura
;
276 view_aura
->SetTouchEditableForTest(touch_editable
);
277 RenderWidgetHostViewAura
* rwhva
= static_cast<RenderWidgetHostViewAura
*>(
278 web_contents
->GetRenderWidgetHostView());
279 EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable
), rwhva
);
281 // Long press to select word.
282 ui::GestureEvent
long_press(
286 ui::EventTimeForNow(),
287 ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS
));
288 touch_editable
->Reset();
289 rwhva
->OnGestureEvent(&long_press
);
290 touch_editable
->WaitForSelectionChangeCallback();
292 // Check if selection handles are showing.
293 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
295 scoped_ptr
<base::Value
> value
=
296 content::ExecuteScriptAndGetValue(main_frame
, "get_selection()");
297 std::string selection
;
298 value
->GetAsString(&selection
);
299 EXPECT_STREQ("Some", selection
.c_str());
301 // Start scrolling. Handles should get hidden.
302 ui::GestureEvent
scroll_begin(
306 ui::EventTimeForNow(),
307 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN
, 0, 0));
308 rwhva
->OnGestureEvent(&scroll_begin
);
309 EXPECT_FALSE(GetTouchSelectionController(touch_editable
));
311 // Start a fling. Handles should come back after fling stops.
312 ui::GestureEvent
fling_start(
316 ui::EventTimeForNow(),
317 ui::GestureEventDetails(ui::ET_SCROLL_FLING_START
, 1, 0));
318 rwhva
->OnGestureEvent(&fling_start
);
319 touch_editable
->WaitForFlingStopCallback();
320 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
323 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest
,
324 TouchSelectionOnLongPressTest
) {
325 ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
326 WebContentsImpl
* web_contents
=
327 static_cast<WebContentsImpl
*>(shell()->web_contents());
328 RenderFrameHost
* main_frame
= web_contents
->GetMainFrame();
329 WebContentsViewAura
* view_aura
= static_cast<WebContentsViewAura
*>(
330 web_contents
->GetView());
331 TestTouchEditableImplAura
* touch_editable
= new TestTouchEditableImplAura
;
332 view_aura
->SetTouchEditableForTest(touch_editable
);
333 RenderWidgetHostViewAura
* rwhva
= static_cast<RenderWidgetHostViewAura
*>(
334 web_contents
->GetRenderWidgetHostView());
335 EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable
), rwhva
);
337 // Long press to select word.
338 ui::GestureEvent
long_press(
342 ui::EventTimeForNow(),
343 ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS
));
344 touch_editable
->Reset();
345 rwhva
->OnGestureEvent(&long_press
);
346 touch_editable
->WaitForSelectionChangeCallback();
348 // Check if selection handles are showing.
349 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
351 scoped_ptr
<base::Value
> value
=
352 content::ExecuteScriptAndGetValue(main_frame
, "get_selection()");
353 std::string selection
;
354 value
->GetAsString(&selection
);
355 EXPECT_STREQ("Some", selection
.c_str());
358 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest
,
359 NoTouchSelectionOnDoubleTapTest
) {
360 ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
361 WebContentsImpl
* web_contents
=
362 static_cast<WebContentsImpl
*>(shell()->web_contents());
363 RenderFrameHost
* main_frame
= web_contents
->GetMainFrame();
364 WebContentsViewAura
* view_aura
=
365 static_cast<WebContentsViewAura
*>(web_contents
->GetView());
366 TestTouchEditableImplAura
* touch_editable
= new TestTouchEditableImplAura
;
367 view_aura
->SetTouchEditableForTest(touch_editable
);
368 RenderWidgetHostViewAura
* rwhva
= static_cast<RenderWidgetHostViewAura
*>(
369 web_contents
->GetRenderWidgetHostView());
370 EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable
), rwhva
);
372 // Double-tap to select word.
373 ui::GestureEventDetails
details(ui::ET_GESTURE_TAP
);
374 details
.set_tap_count(2);
375 ui::GestureEvent
double_tap(10, 10, 0, ui::EventTimeForNow(), details
);
376 touch_editable
->Reset();
377 rwhva
->OnGestureEvent(&double_tap
);
378 touch_editable
->WaitForSelectionChangeCallback();
380 // Make sure touch selection handles are not showing.
381 EXPECT_FALSE(GetTouchSelectionController(touch_editable
));
383 scoped_ptr
<base::Value
> value
=
384 content::ExecuteScriptAndGetValue(main_frame
, "get_selection()");
385 std::string selection
;
386 value
->GetAsString(&selection
);
387 EXPECT_STREQ("Some", selection
.c_str());
390 #if defined(OS_CHROMEOS)
391 // http://crbug.com/396509
392 #define MAYBE_TouchCursorInTextfieldTest DISABLED_TouchCursorInTextfieldTest
394 #define MAYBE_TouchCursorInTextfieldTest TouchCursorInTextfieldTest
396 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest
,
397 MAYBE_TouchCursorInTextfieldTest
) {
398 ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
399 WebContentsImpl
* web_contents
=
400 static_cast<WebContentsImpl
*>(shell()->web_contents());
401 RenderFrameHost
* main_frame
= web_contents
->GetMainFrame();
402 WebContentsViewAura
* view_aura
= static_cast<WebContentsViewAura
*>(
403 web_contents
->GetView());
404 TestTouchEditableImplAura
* touch_editable
= new TestTouchEditableImplAura
;
405 view_aura
->SetTouchEditableForTest(touch_editable
);
406 RenderWidgetHostViewAura
* rwhva
= static_cast<RenderWidgetHostViewAura
*>(
407 web_contents
->GetRenderWidgetHostView());
408 aura::Window
* content
= web_contents
->GetContentNativeView();
409 ui::test::EventGenerator
generator(content
->GetRootWindow(), content
);
410 gfx::Rect bounds
= content
->GetBoundsInRootWindow();
411 EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable
), rwhva
);
413 ExecuteSyncJSFunction(main_frame
, "focus_textfield()");
414 touch_editable
->WaitForSelectionChangeCallback();
417 touch_editable
->Reset();
418 generator
.GestureTapAt(gfx::Point(bounds
.x() + 50, bounds
.y() + 40));
419 // Tap Down acks are sent synchronously, while Tap acks are asynchronous.
420 touch_editable
->WaitForGestureAck(WebInputEvent::GestureTap
);
421 touch_editable
->WaitForSelectionChangeCallback();
422 touch_editable
->Reset();
424 // Check if cursor handle is showing.
425 EXPECT_NE(ui::TEXT_INPUT_TYPE_NONE
, GetTextInputType(touch_editable
));
426 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
428 scoped_ptr
<base::Value
> value
=
429 content::ExecuteScriptAndGetValue(main_frame
, "get_cursor_position()");
431 value
->GetAsInteger(&cursor_pos
);
432 EXPECT_NE(-1, cursor_pos
);
434 // Move the cursor handle.
435 generator
.GestureScrollSequence(
438 base::TimeDelta::FromMilliseconds(20),
440 touch_editable
->WaitForSelectionChangeCallback();
441 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
442 value
= content::ExecuteScriptAndGetValue(main_frame
,
443 "get_cursor_position()");
444 int new_cursor_pos
= -1;
445 value
->GetAsInteger(&new_cursor_pos
);
446 EXPECT_NE(-1, new_cursor_pos
);
447 // Cursor should have moved.
448 EXPECT_NE(new_cursor_pos
, cursor_pos
);
451 } // namespace content