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
) {}
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 class TouchEditableImplAuraTest
: public ContentBrowserTest
{
96 TouchEditableImplAuraTest() {}
99 virtual void SetUpCommandLine(CommandLine
* command_line
) OVERRIDE
{
100 command_line
->AppendSwitch(switches::kEnableTouchEditing
);
103 // Executes the javascript synchronously and makes sure the returned value is
105 void ExecuteSyncJSFunction(RenderFrameHost
* rfh
, const std::string
& jscript
) {
106 scoped_ptr
<base::Value
> value
=
107 content::ExecuteScriptAndGetValue(rfh
, jscript
);
110 // Starts the test server and navigates to the given url. Sets a large enough
111 // size to the root window. Returns after the navigation to the url is
113 void StartTestWithPage(const std::string
& url
) {
114 ASSERT_TRUE(test_server()->Start());
115 GURL
test_url(test_server()->GetURL(url
));
116 NavigateToURL(shell(), test_url
);
117 aura::Window
* content
= shell()->web_contents()->GetContentNativeView();
118 content
->GetHost()->SetBounds(gfx::Rect(800, 600));
121 RenderWidgetHostViewAura
* GetRenderWidgetHostViewAura(
122 TouchEditableImplAura
* touch_editable
) {
123 return touch_editable
->rwhva_
;
126 ui::TouchSelectionController
* GetTouchSelectionController(
127 TouchEditableImplAura
* touch_editable
) {
128 return touch_editable
->touch_selection_controller_
.get();
131 ui::TextInputType
GetTextInputType(TouchEditableImplAura
* touch_editable
) {
132 return touch_editable
->text_input_type_
;
136 DISALLOW_COPY_AND_ASSIGN(TouchEditableImplAuraTest
);
139 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest
,
140 TouchSelectionOriginatingFromWebpageTest
) {
141 ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
142 WebContentsImpl
* web_contents
=
143 static_cast<WebContentsImpl
*>(shell()->web_contents());
144 RenderFrameHost
* main_frame
= web_contents
->GetMainFrame();
145 WebContentsViewAura
* view_aura
= static_cast<WebContentsViewAura
*>(
146 web_contents
->GetView());
147 TestTouchEditableImplAura
* touch_editable
= new TestTouchEditableImplAura
;
148 view_aura
->SetTouchEditableForTest(touch_editable
);
149 RenderWidgetHostViewAura
* rwhva
= static_cast<RenderWidgetHostViewAura
*>(
150 web_contents
->GetRenderWidgetHostView());
151 aura::Window
* content
= web_contents
->GetContentNativeView();
152 ui::test::EventGenerator
generator(content
->GetRootWindow(), content
);
153 gfx::Rect bounds
= content
->GetBoundsInRootWindow();
155 touch_editable
->Reset();
156 ExecuteSyncJSFunction(main_frame
, "select_all_text()");
157 touch_editable
->WaitForSelectionChangeCallback();
159 // Tap inside selection to bring up selection handles.
160 generator
.GestureTapAt(gfx::Point(bounds
.x() + 10, bounds
.y() + 10));
161 EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable
), rwhva
);
163 scoped_ptr
<base::Value
> value
=
164 content::ExecuteScriptAndGetValue(main_frame
, "get_selection()");
165 std::string selection
;
166 value
->GetAsString(&selection
);
168 // Check if selection handles are showing.
169 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
170 EXPECT_STREQ("Some text we can select", selection
.c_str());
172 // Lets move the handles a bit to modify the selection
173 touch_editable
->Reset();
174 generator
.GestureScrollSequence(
177 base::TimeDelta::FromMilliseconds(20),
179 touch_editable
->WaitForSelectionChangeCallback();
181 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
182 value
= content::ExecuteScriptAndGetValue(main_frame
, "get_selection()");
183 value
->GetAsString(&selection
);
185 // It is hard to tell what exactly the selection would be now. But it would
186 // definitely be less than whatever was selected before.
187 EXPECT_GT(std::strlen("Some text we can select"), selection
.size());
190 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest
,
191 TestTouchSelectionHiddenWhenScrolling
) {
192 ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
193 WebContentsImpl
* web_contents
=
194 static_cast<WebContentsImpl
*>(shell()->web_contents());
195 RenderFrameHost
* main_frame
= web_contents
->GetMainFrame();
196 WebContentsViewAura
* view_aura
= static_cast<WebContentsViewAura
*>(
197 web_contents
->GetView());
198 TestTouchEditableImplAura
* touch_editable
= new TestTouchEditableImplAura
;
199 view_aura
->SetTouchEditableForTest(touch_editable
);
200 RenderWidgetHostViewAura
* rwhva
= static_cast<RenderWidgetHostViewAura
*>(
201 web_contents
->GetRenderWidgetHostView());
202 EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable
), rwhva
);
204 // Long press to select word.
205 ui::GestureEvent
long_press(
209 ui::EventTimeForNow(),
210 ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS
, 0, 0));
211 touch_editable
->Reset();
212 rwhva
->OnGestureEvent(&long_press
);
213 touch_editable
->WaitForSelectionChangeCallback();
215 // Check if selection handles are showing.
216 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
218 scoped_ptr
<base::Value
> value
=
219 content::ExecuteScriptAndGetValue(main_frame
, "get_selection()");
220 std::string selection
;
221 value
->GetAsString(&selection
);
222 EXPECT_STREQ("Some", selection
.c_str());
224 // Start scrolling. Handles should get hidden.
225 ui::GestureEvent
scroll_begin(
229 ui::EventTimeForNow(),
230 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN
, 0, 0));
231 rwhva
->OnGestureEvent(&scroll_begin
);
232 EXPECT_FALSE(GetTouchSelectionController(touch_editable
));
234 // Handles should come back after scroll ends.
235 ui::GestureEvent
scroll_end(
239 ui::EventTimeForNow(),
240 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END
, 0, 0));
241 rwhva
->OnGestureEvent(&scroll_end
);
242 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
245 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest
,
246 TouchSelectionOnLongPressTest
) {
247 ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
248 WebContentsImpl
* web_contents
=
249 static_cast<WebContentsImpl
*>(shell()->web_contents());
250 RenderFrameHost
* main_frame
= web_contents
->GetMainFrame();
251 WebContentsViewAura
* view_aura
= static_cast<WebContentsViewAura
*>(
252 web_contents
->GetView());
253 TestTouchEditableImplAura
* touch_editable
= new TestTouchEditableImplAura
;
254 view_aura
->SetTouchEditableForTest(touch_editable
);
255 RenderWidgetHostViewAura
* rwhva
= static_cast<RenderWidgetHostViewAura
*>(
256 web_contents
->GetRenderWidgetHostView());
257 EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable
), rwhva
);
259 // Long press to select word.
260 ui::GestureEvent
long_press(
264 ui::EventTimeForNow(),
265 ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS
, 0, 0));
266 touch_editable
->Reset();
267 rwhva
->OnGestureEvent(&long_press
);
268 touch_editable
->WaitForSelectionChangeCallback();
270 // Check if selection handles are showing.
271 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
273 scoped_ptr
<base::Value
> value
=
274 content::ExecuteScriptAndGetValue(main_frame
, "get_selection()");
275 std::string selection
;
276 value
->GetAsString(&selection
);
277 EXPECT_STREQ("Some", selection
.c_str());
280 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest
,
281 NoTouchSelectionOnDoubleTapTest
) {
282 ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
283 WebContentsImpl
* web_contents
=
284 static_cast<WebContentsImpl
*>(shell()->web_contents());
285 RenderFrameHost
* main_frame
= web_contents
->GetMainFrame();
286 WebContentsViewAura
* view_aura
=
287 static_cast<WebContentsViewAura
*>(web_contents
->GetView());
288 TestTouchEditableImplAura
* touch_editable
= new TestTouchEditableImplAura
;
289 view_aura
->SetTouchEditableForTest(touch_editable
);
290 RenderWidgetHostViewAura
* rwhva
= static_cast<RenderWidgetHostViewAura
*>(
291 web_contents
->GetRenderWidgetHostView());
292 EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable
), rwhva
);
294 // Double-tap to select word.
295 ui::GestureEvent
double_tap(
299 ui::EventTimeForNow(),
300 ui::GestureEventDetails(ui::ET_GESTURE_TAP
, 2, 0));
301 touch_editable
->Reset();
302 rwhva
->OnGestureEvent(&double_tap
);
303 touch_editable
->WaitForSelectionChangeCallback();
305 // Make sure touch selection handles are not showing.
306 EXPECT_FALSE(GetTouchSelectionController(touch_editable
));
308 scoped_ptr
<base::Value
> value
=
309 content::ExecuteScriptAndGetValue(main_frame
, "get_selection()");
310 std::string selection
;
311 value
->GetAsString(&selection
);
312 EXPECT_STREQ("Some", selection
.c_str());
315 #if defined(OS_CHROMEOS)
316 // http://crbug.com/396509
317 #define MAYBE_TouchCursorInTextfieldTest DISABLED_TouchCursorInTextfieldTest
319 #define MAYBE_TouchCursorInTextfieldTest TouchCursorInTextfieldTest
321 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest
,
322 MAYBE_TouchCursorInTextfieldTest
) {
323 ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
324 WebContentsImpl
* web_contents
=
325 static_cast<WebContentsImpl
*>(shell()->web_contents());
326 RenderFrameHost
* main_frame
= web_contents
->GetMainFrame();
327 WebContentsViewAura
* view_aura
= static_cast<WebContentsViewAura
*>(
328 web_contents
->GetView());
329 TestTouchEditableImplAura
* touch_editable
= new TestTouchEditableImplAura
;
330 view_aura
->SetTouchEditableForTest(touch_editable
);
331 RenderWidgetHostViewAura
* rwhva
= static_cast<RenderWidgetHostViewAura
*>(
332 web_contents
->GetRenderWidgetHostView());
333 aura::Window
* content
= web_contents
->GetContentNativeView();
334 ui::test::EventGenerator
generator(content
->GetRootWindow(), content
);
335 gfx::Rect bounds
= content
->GetBoundsInRootWindow();
336 EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable
), rwhva
);
338 ExecuteSyncJSFunction(main_frame
, "focus_textfield()");
339 touch_editable
->WaitForSelectionChangeCallback();
342 touch_editable
->Reset();
343 generator
.GestureTapAt(gfx::Point(bounds
.x() + 50, bounds
.y() + 40));
344 // Tap Down acks are sent synchronously, while Tap acks are asynchronous.
345 touch_editable
->WaitForGestureAck(WebInputEvent::GestureTap
);
346 touch_editable
->WaitForSelectionChangeCallback();
347 touch_editable
->Reset();
349 // Check if cursor handle is showing.
350 EXPECT_NE(ui::TEXT_INPUT_TYPE_NONE
, GetTextInputType(touch_editable
));
351 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
353 scoped_ptr
<base::Value
> value
=
354 content::ExecuteScriptAndGetValue(main_frame
, "get_cursor_position()");
356 value
->GetAsInteger(&cursor_pos
);
357 EXPECT_NE(-1, cursor_pos
);
359 // Move the cursor handle.
360 generator
.GestureScrollSequence(
363 base::TimeDelta::FromMilliseconds(20),
365 touch_editable
->WaitForSelectionChangeCallback();
366 EXPECT_TRUE(GetTouchSelectionController(touch_editable
));
367 value
= content::ExecuteScriptAndGetValue(main_frame
,
368 "get_cursor_position()");
369 int new_cursor_pos
= -1;
370 value
->GetAsInteger(&new_cursor_pos
);
371 EXPECT_NE(-1, new_cursor_pos
);
372 // Cursor should have moved.
373 EXPECT_NE(new_cursor_pos
, cursor_pos
);
376 } // namespace content