touch_editable_impl_aura_browsertest.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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/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
29using blink::WebInputEvent;
30
31namespace content {
32
33class TestTouchEditableImplAura : public TouchEditableImplAura {
34 public:
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
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;
46  }
47
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();
54  }
55
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();
62  }
63
64  virtual void WaitForSelectionChangeCallback() {
65    if (selection_changed_callback_arrived_)
66      return;
67    waiting_for_selection_changed_callback_ = true;
68    selection_changed_wait_run_loop_.reset(new base::RunLoop());
69    selection_changed_wait_run_loop_->Run();
70  }
71
72  virtual void WaitForGestureAck(WebInputEvent::Type gesture_event_type) {
73    if (last_gesture_ack_type_ == gesture_event_type)
74      return;
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();
78  }
79
80 protected:
81  virtual ~TestTouchEditableImplAura() {}
82
83 private:
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_;
90
91  DISALLOW_COPY_AND_ASSIGN(TestTouchEditableImplAura);
92};
93
94class TouchEditableImplAuraTest : public ContentBrowserTest {
95 public:
96  TouchEditableImplAuraTest() {}
97
98 protected:
99  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
100    command_line->AppendSwitch(switches::kEnableTouchEditing);
101  }
102
103  // Executes the javascript synchronously and makes sure the returned value is
104  // freed properly.
105  void ExecuteSyncJSFunction(RenderFrameHost* rfh, const std::string& jscript) {
106    scoped_ptr<base::Value> value =
107        content::ExecuteScriptAndGetValue(rfh, jscript);
108  }
109
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
112  // complete.
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));
119  }
120
121  RenderWidgetHostViewAura* GetRenderWidgetHostViewAura(
122      TouchEditableImplAura* touch_editable) {
123    return touch_editable->rwhva_;
124  }
125
126  ui::TouchSelectionController* GetTouchSelectionController(
127      TouchEditableImplAura* touch_editable) {
128    return touch_editable->touch_selection_controller_.get();
129  }
130
131  ui::TextInputType GetTextInputType(TouchEditableImplAura* touch_editable) {
132    return touch_editable->text_input_type_;
133  }
134
135 private:
136  DISALLOW_COPY_AND_ASSIGN(TouchEditableImplAuraTest);
137};
138
139IN_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();
154
155  touch_editable->Reset();
156  ExecuteSyncJSFunction(main_frame, "select_all_text()");
157  touch_editable->WaitForSelectionChangeCallback();
158
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);
162
163  scoped_ptr<base::Value> value =
164      content::ExecuteScriptAndGetValue(main_frame, "get_selection()");
165  std::string selection;
166  value->GetAsString(&selection);
167
168  // Check if selection handles are showing.
169  EXPECT_TRUE(GetTouchSelectionController(touch_editable));
170  EXPECT_STREQ("Some text we can select", selection.c_str());
171
172  // Lets move the handles a bit to modify the selection
173  touch_editable->Reset();
174  generator.GestureScrollSequence(
175      gfx::Point(10, 47),
176      gfx::Point(30, 47),
177      base::TimeDelta::FromMilliseconds(20),
178      5);
179  touch_editable->WaitForSelectionChangeCallback();
180
181  EXPECT_TRUE(GetTouchSelectionController(touch_editable));
182  value = content::ExecuteScriptAndGetValue(main_frame, "get_selection()");
183  value->GetAsString(&selection);
184
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());
188}
189
190IN_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);
203
204  // Long press to select word.
205  ui::GestureEvent long_press(
206      10,
207      10,
208      0,
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();
214
215  // Check if selection handles are showing.
216  EXPECT_TRUE(GetTouchSelectionController(touch_editable));
217
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());
223
224  // Start scrolling. Handles should get hidden.
225  ui::GestureEvent scroll_begin(
226      10,
227      10,
228      0,
229      ui::EventTimeForNow(),
230      ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, 0, 0));
231  rwhva->OnGestureEvent(&scroll_begin);
232  EXPECT_FALSE(GetTouchSelectionController(touch_editable));
233
234  // Handles should come back after scroll ends.
235  ui::GestureEvent scroll_end(
236      10,
237      10,
238      0,
239      ui::EventTimeForNow(),
240      ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END, 0, 0));
241  rwhva->OnGestureEvent(&scroll_end);
242  EXPECT_TRUE(GetTouchSelectionController(touch_editable));
243}
244
245IN_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);
258
259  // Long press to select word.
260  ui::GestureEvent long_press(
261      10,
262      10,
263      0,
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();
269
270  // Check if selection handles are showing.
271  EXPECT_TRUE(GetTouchSelectionController(touch_editable));
272
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());
278}
279
280IN_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);
293
294  // Double-tap to select word.
295  ui::GestureEvent double_tap(
296      10,
297      10,
298      0,
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();
304
305  // Make sure touch selection handles are not showing.
306  EXPECT_FALSE(GetTouchSelectionController(touch_editable));
307
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());
313}
314
315#if defined(OS_CHROMEOS)
316// http://crbug.com/396509
317#define MAYBE_TouchCursorInTextfieldTest DISABLED_TouchCursorInTextfieldTest
318#else
319#define MAYBE_TouchCursorInTextfieldTest TouchCursorInTextfieldTest
320#endif
321IN_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);
337
338  ExecuteSyncJSFunction(main_frame, "focus_textfield()");
339  touch_editable->WaitForSelectionChangeCallback();
340
341  // Tap textfield
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();
348
349  // Check if cursor handle is showing.
350  EXPECT_NE(ui::TEXT_INPUT_TYPE_NONE, GetTextInputType(touch_editable));
351  EXPECT_TRUE(GetTouchSelectionController(touch_editable));
352
353  scoped_ptr<base::Value> value =
354      content::ExecuteScriptAndGetValue(main_frame, "get_cursor_position()");
355  int cursor_pos = -1;
356  value->GetAsInteger(&cursor_pos);
357  EXPECT_NE(-1, cursor_pos);
358
359  // Move the cursor handle.
360  generator.GestureScrollSequence(
361      gfx::Point(50, 59),
362      gfx::Point(10, 59),
363      base::TimeDelta::FromMilliseconds(20),
364      1);
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);
374}
375
376}  // namespace content
377