touch_editable_impl_aura_browsertest.cc revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
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.
4
5#include "content/browser/web_contents/touch_editable_impl_aura.h"
6
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/browser/web_contents_view.h"
16#include "content/public/common/content_switches.h"
17#include "content/public/test/browser_test_utils.h"
18#include "content/public/test/content_browser_test.h"
19#include "content/public/test/content_browser_test_utils.h"
20#include "content/public/test/test_utils.h"
21#include "content/shell/browser/shell.h"
22#include "third_party/WebKit/public/web/WebInputEvent.h"
23#include "ui/aura/test/event_generator.h"
24#include "ui/aura/window.h"
25#include "ui/aura/window_tree_host.h"
26#include "ui/base/ui_base_switches.h"
27#include "ui/compositor/scoped_animation_duration_scale_mode.h"
28#include "ui/events/event_utils.h"
29
30using blink::WebInputEvent;
31
32namespace content {
33
34class TestTouchEditableImplAura : public TouchEditableImplAura {
35 public:
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
42  virtual void Reset() {
43    selection_changed_callback_arrived_ = false;
44    waiting_for_selection_changed_callback_ = false;
45    waiting_for_gesture_ack_type_ = WebInputEvent::Undefined;
46    last_gesture_ack_type_ = WebInputEvent::Undefined;
47  }
48
49  virtual void OnSelectionOrCursorChanged(const gfx::Rect& anchor,
50                                          const gfx::Rect& focus) OVERRIDE {
51    selection_changed_callback_arrived_ = true;
52    TouchEditableImplAura::OnSelectionOrCursorChanged(anchor, focus);
53    if (waiting_for_selection_changed_callback_)
54      selection_changed_wait_run_loop_->Quit();
55  }
56
57  virtual void GestureEventAck(int gesture_event_type) OVERRIDE {
58    last_gesture_ack_type_ =
59        static_cast<WebInputEvent::Type>(gesture_event_type);
60    TouchEditableImplAura::GestureEventAck(gesture_event_type);
61    if (waiting_for_gesture_ack_type_ == gesture_event_type)
62      gesture_ack_wait_run_loop_->Quit();
63  }
64
65  virtual void WaitForSelectionChangeCallback() {
66    if (selection_changed_callback_arrived_)
67      return;
68    waiting_for_selection_changed_callback_ = true;
69    selection_changed_wait_run_loop_.reset(new base::RunLoop());
70    selection_changed_wait_run_loop_->Run();
71  }
72
73  virtual void WaitForGestureAck(WebInputEvent::Type gesture_event_type) {
74    if (last_gesture_ack_type_ == gesture_event_type)
75      return;
76    waiting_for_gesture_ack_type_ = gesture_event_type;
77    gesture_ack_wait_run_loop_.reset(new base::RunLoop());
78    gesture_ack_wait_run_loop_->Run();
79  }
80
81 protected:
82  virtual ~TestTouchEditableImplAura() {}
83
84 private:
85  bool selection_changed_callback_arrived_;
86  bool waiting_for_selection_changed_callback_;
87  WebInputEvent::Type waiting_for_gesture_ack_type_;
88  WebInputEvent::Type last_gesture_ack_type_;
89  scoped_ptr<base::RunLoop> selection_changed_wait_run_loop_;
90  scoped_ptr<base::RunLoop> gesture_ack_wait_run_loop_;
91
92  DISALLOW_COPY_AND_ASSIGN(TestTouchEditableImplAura);
93};
94
95// This class ignores mouse-moved, mouse-entered and mouse-exited events
96// without passing them to TouchEditableImplAura. Normally, we should not
97// receive these events; but, sometimes we receive them which breaks the tests
98// and makes them flaky: crbug.com/276992.
99class TestTouchEditableImplAuraIgnoreMouseMovement
100    : public TestTouchEditableImplAura {
101 public:
102  TestTouchEditableImplAuraIgnoreMouseMovement() {}
103
104  virtual bool HandleInputEvent(const ui::Event* event) OVERRIDE {
105    if (event->type() == ui::ET_MOUSE_ENTERED ||
106        event->type() == ui::ET_MOUSE_MOVED ||
107        event->type() == ui::ET_MOUSE_EXITED) {
108      return false;
109    }
110    return TestTouchEditableImplAura::HandleInputEvent(event);
111  }
112
113 protected:
114  virtual ~TestTouchEditableImplAuraIgnoreMouseMovement() {}
115
116 private:
117  DISALLOW_COPY_AND_ASSIGN(TestTouchEditableImplAuraIgnoreMouseMovement);
118};
119
120class TouchEditableImplAuraTest : public ContentBrowserTest {
121 public:
122  TouchEditableImplAuraTest() {}
123
124 protected:
125  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
126    command_line->AppendSwitch(switches::kEnableTouchEditing);
127  }
128
129  // Executes the javascript synchronously and makes sure the returned value is
130  // freed properly.
131  void ExecuteSyncJSFunction(RenderFrameHost* rfh, const std::string& jscript) {
132    scoped_ptr<base::Value> value =
133        content::ExecuteScriptAndGetValue(rfh, jscript);
134  }
135
136  // Starts the test server and navigates to the given url. Sets a large enough
137  // size to the root window.  Returns after the navigation to the url is
138  // complete.
139  void StartTestWithPage(const std::string& url) {
140    ASSERT_TRUE(test_server()->Start());
141    GURL test_url(test_server()->GetURL(url));
142    NavigateToURL(shell(), test_url);
143    aura::Window* content =
144        shell()->web_contents()->GetView()->GetContentNativeView();
145    content->GetHost()->SetBounds(gfx::Rect(800, 600));
146  }
147
148  RenderWidgetHostViewAura* GetRenderWidgetHostViewAura(
149      TouchEditableImplAura* touch_editable) {
150    return touch_editable->rwhva_;
151  }
152
153  ui::TouchSelectionController* GetTouchSelectionController(
154      TouchEditableImplAura* touch_editable) {
155    return touch_editable->touch_selection_controller_.get();
156  }
157
158  ui::TextInputType GetTextInputType(TouchEditableImplAura* touch_editable) {
159    return touch_editable->text_input_type_;
160  }
161
162 private:
163  DISALLOW_COPY_AND_ASSIGN(TouchEditableImplAuraTest);
164};
165
166IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest,
167                       TouchSelectionOriginatingFromWebpageTest) {
168  ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
169  WebContentsImpl* web_contents =
170      static_cast<WebContentsImpl*>(shell()->web_contents());
171  RenderFrameHost* main_frame = web_contents->GetMainFrame();
172  WebContentsViewAura* view_aura = static_cast<WebContentsViewAura*>(
173      web_contents->GetView());
174  TestTouchEditableImplAura* touch_editable =
175      new TestTouchEditableImplAuraIgnoreMouseMovement;
176  view_aura->SetTouchEditableForTest(touch_editable);
177  RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>(
178      web_contents->GetRenderWidgetHostView());
179  aura::Window* content = web_contents->GetView()->GetContentNativeView();
180  aura::test::EventGenerator generator(content->GetRootWindow(), content);
181  gfx::Rect bounds = content->GetBoundsInRootWindow();
182
183  touch_editable->Reset();
184  ExecuteSyncJSFunction(main_frame, "select_all_text()");
185  touch_editable->WaitForSelectionChangeCallback();
186
187  // Tap inside selection to bring up selection handles.
188  generator.GestureTapAt(gfx::Point(bounds.x() + 10, bounds.y() + 10));
189  EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable), rwhva);
190
191  scoped_ptr<base::Value> value =
192      content::ExecuteScriptAndGetValue(main_frame, "get_selection()");
193  std::string selection;
194  value->GetAsString(&selection);
195
196  // Check if selection handles are showing.
197  EXPECT_TRUE(GetTouchSelectionController(touch_editable));
198  EXPECT_STREQ("Some text we can select", selection.c_str());
199
200  // Lets move the handles a bit to modify the selection
201  touch_editable->Reset();
202  generator.GestureScrollSequence(
203      gfx::Point(10, 47),
204      gfx::Point(30, 47),
205      base::TimeDelta::FromMilliseconds(20),
206      5);
207  touch_editable->WaitForSelectionChangeCallback();
208
209  EXPECT_TRUE(GetTouchSelectionController(touch_editable));
210  value = content::ExecuteScriptAndGetValue(main_frame, "get_selection()");
211  value->GetAsString(&selection);
212
213  // It is hard to tell what exactly the selection would be now. But it would
214  // definitely be less than whatever was selected before.
215  EXPECT_GT(std::strlen("Some text we can select"), selection.size());
216}
217
218IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest,
219                       TestTouchSelectionHiddenWhenScrolling) {
220  ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
221  WebContentsImpl* web_contents =
222      static_cast<WebContentsImpl*>(shell()->web_contents());
223  RenderFrameHost* main_frame = web_contents->GetMainFrame();
224  WebContentsViewAura* view_aura = static_cast<WebContentsViewAura*>(
225      web_contents->GetView());
226  TestTouchEditableImplAura* touch_editable = new TestTouchEditableImplAura;
227  view_aura->SetTouchEditableForTest(touch_editable);
228  RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>(
229      web_contents->GetRenderWidgetHostView());
230  EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable), rwhva);
231
232  // Long press to select word.
233  ui::GestureEvent long_press(ui::ET_GESTURE_LONG_PRESS,
234                              10,
235                              10,
236                              0,
237                              ui::EventTimeForNow(),
238                              ui::GestureEventDetails(
239                                  ui::ET_GESTURE_LONG_PRESS, 0, 0),
240                              1);
241  touch_editable->Reset();
242  rwhva->OnGestureEvent(&long_press);
243  touch_editable->WaitForSelectionChangeCallback();
244
245  // Check if selection handles are showing.
246  EXPECT_TRUE(GetTouchSelectionController(touch_editable));
247
248  scoped_ptr<base::Value> value =
249      content::ExecuteScriptAndGetValue(main_frame, "get_selection()");
250  std::string selection;
251  value->GetAsString(&selection);
252  EXPECT_STREQ("Some", selection.c_str());
253
254  // Start scrolling. Handles should get hidden.
255  ui::GestureEvent scroll_begin(ui::ET_GESTURE_SCROLL_BEGIN,
256                                10,
257                                10,
258                                0,
259                                ui::EventTimeForNow(),
260                                ui::GestureEventDetails(
261                                    ui::ET_GESTURE_SCROLL_BEGIN, 0, 0),
262                                1);
263  rwhva->OnGestureEvent(&scroll_begin);
264  EXPECT_FALSE(GetTouchSelectionController(touch_editable));
265
266  // Handles should come back after scroll ends.
267  ui::GestureEvent scroll_end(ui::ET_GESTURE_SCROLL_END,
268                              10,
269                              10,
270                              0,
271                              ui::EventTimeForNow(),
272                              ui::GestureEventDetails(
273                                  ui::ET_GESTURE_SCROLL_END, 0, 0),
274                              1);
275  rwhva->OnGestureEvent(&scroll_end);
276  EXPECT_TRUE(GetTouchSelectionController(touch_editable));
277}
278
279IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest,
280                       TouchSelectionOnLongPressTest) {
281  ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
282  WebContentsImpl* web_contents =
283      static_cast<WebContentsImpl*>(shell()->web_contents());
284  RenderFrameHost* main_frame = web_contents->GetMainFrame();
285  WebContentsViewAura* view_aura = static_cast<WebContentsViewAura*>(
286      web_contents->GetView());
287  TestTouchEditableImplAura* touch_editable = new TestTouchEditableImplAura;
288  view_aura->SetTouchEditableForTest(touch_editable);
289  RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>(
290      web_contents->GetRenderWidgetHostView());
291  EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable), rwhva);
292
293  // Long press to select word.
294  ui::GestureEvent long_press(ui::ET_GESTURE_LONG_PRESS,
295                              10,
296                              10,
297                              0,
298                              ui::EventTimeForNow(),
299                              ui::GestureEventDetails(
300                                  ui::ET_GESTURE_LONG_PRESS, 0, 0),
301                              1);
302  touch_editable->Reset();
303  rwhva->OnGestureEvent(&long_press);
304  touch_editable->WaitForSelectionChangeCallback();
305
306  // Check if selection handles are showing.
307  EXPECT_TRUE(GetTouchSelectionController(touch_editable));
308
309  scoped_ptr<base::Value> value =
310      content::ExecuteScriptAndGetValue(main_frame, "get_selection()");
311  std::string selection;
312  value->GetAsString(&selection);
313  EXPECT_STREQ("Some", selection.c_str());
314}
315
316IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest,
317                       TouchSelectionOnDoubleTapTest) {
318  ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
319  WebContentsImpl* web_contents =
320      static_cast<WebContentsImpl*>(shell()->web_contents());
321  RenderFrameHost* main_frame = web_contents->GetMainFrame();
322  WebContentsViewAura* view_aura =
323      static_cast<WebContentsViewAura*>(web_contents->GetView());
324  TestTouchEditableImplAura* touch_editable = new TestTouchEditableImplAura;
325  view_aura->SetTouchEditableForTest(touch_editable);
326  RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>(
327      web_contents->GetRenderWidgetHostView());
328  EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable), rwhva);
329
330  // Double-tap to select word.
331  ui::GestureEvent double_tap(ui::ET_GESTURE_TAP,
332                              10,
333                              10,
334                              0,
335                              ui::EventTimeForNow(),
336                              ui::GestureEventDetails(ui::ET_GESTURE_TAP, 2, 0),
337                              1);
338  touch_editable->Reset();
339  rwhva->OnGestureEvent(&double_tap);
340  touch_editable->WaitForSelectionChangeCallback();
341
342  // Check if selection handles are showing.
343  EXPECT_TRUE(GetTouchSelectionController(touch_editable));
344
345  scoped_ptr<base::Value> value =
346      content::ExecuteScriptAndGetValue(main_frame, "get_selection()");
347  std::string selection;
348  value->GetAsString(&selection);
349  EXPECT_STREQ("Some", selection.c_str());
350}
351
352IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest,
353                       TouchCursorInTextfieldTest) {
354  ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
355  WebContentsImpl* web_contents =
356      static_cast<WebContentsImpl*>(shell()->web_contents());
357  RenderFrameHost* main_frame = web_contents->GetMainFrame();
358  WebContentsViewAura* view_aura = static_cast<WebContentsViewAura*>(
359      web_contents->GetView());
360  TestTouchEditableImplAura* touch_editable =
361      new TestTouchEditableImplAuraIgnoreMouseMovement;
362  view_aura->SetTouchEditableForTest(touch_editable);
363  RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>(
364      web_contents->GetRenderWidgetHostView());
365  aura::Window* content = web_contents->GetView()->GetContentNativeView();
366  aura::test::EventGenerator generator(content->GetRootWindow(), content);
367  gfx::Rect bounds = content->GetBoundsInRootWindow();
368  EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable), rwhva);
369
370  ExecuteSyncJSFunction(main_frame, "focus_textfield()");
371  touch_editable->WaitForSelectionChangeCallback();
372
373  // Tap textfield
374  touch_editable->Reset();
375  generator.GestureTapAt(gfx::Point(bounds.x() + 50, bounds.y() + 40));
376  // Tap Down acks are sent synchronously, while Tap acks are asynchronous.
377  touch_editable->WaitForGestureAck(WebInputEvent::GestureTap);
378  touch_editable->WaitForSelectionChangeCallback();
379  touch_editable->Reset();
380
381  // Check if cursor handle is showing.
382  EXPECT_NE(ui::TEXT_INPUT_TYPE_NONE, GetTextInputType(touch_editable));
383  EXPECT_TRUE(GetTouchSelectionController(touch_editable));
384
385  scoped_ptr<base::Value> value =
386      content::ExecuteScriptAndGetValue(main_frame, "get_cursor_position()");
387  int cursor_pos = -1;
388  value->GetAsInteger(&cursor_pos);
389  EXPECT_NE(-1, cursor_pos);
390
391  // Move the cursor handle.
392  generator.GestureScrollSequence(
393      gfx::Point(50, 59),
394      gfx::Point(10, 59),
395      base::TimeDelta::FromMilliseconds(20),
396      1);
397  touch_editable->WaitForSelectionChangeCallback();
398  EXPECT_TRUE(GetTouchSelectionController(touch_editable));
399  value = content::ExecuteScriptAndGetValue(main_frame,
400                                            "get_cursor_position()");
401  int new_cursor_pos = -1;
402  value->GetAsInteger(&new_cursor_pos);
403  EXPECT_NE(-1, new_cursor_pos);
404  // Cursor should have moved.
405  EXPECT_NE(new_cursor_pos, cursor_pos);
406}
407
408}  // namespace content
409