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