Home | History | Annotate | Download | only in web_contents
      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 
     29 using blink::WebInputEvent;
     30 
     31 namespace content {
     32 
     33 class 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         fling_stop_callback_arrived_(false),
     41         waiting_for_fling_stop_callback_(false) {}
     42 
     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;
     50   }
     51 
     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();
     58   }
     59 
     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();
     66   }
     67 
     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();
     73   }
     74 
     75   virtual void WaitForSelectionChangeCallback() {
     76     if (selection_changed_callback_arrived_)
     77       return;
     78     waiting_for_selection_changed_callback_ = true;
     79     selection_changed_wait_run_loop_.reset(new base::RunLoop());
     80     selection_changed_wait_run_loop_->Run();
     81   }
     82 
     83   virtual void WaitForGestureAck(WebInputEvent::Type gesture_event_type) {
     84     if (last_gesture_ack_type_ == gesture_event_type)
     85       return;
     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();
     89   }
     90 
     91   virtual void WaitForFlingStopCallback() {
     92     if (fling_stop_callback_arrived_)
     93       return;
     94     waiting_for_fling_stop_callback_ = true;
     95     fling_stop_wait_run_loop_.reset(new base::RunLoop());
     96     fling_stop_wait_run_loop_->Run();
     97   }
     98 
     99  protected:
    100   virtual ~TestTouchEditableImplAura() {}
    101 
    102  private:
    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_;
    112 
    113   DISALLOW_COPY_AND_ASSIGN(TestTouchEditableImplAura);
    114 };
    115 
    116 class TouchEditableImplAuraTest : public ContentBrowserTest {
    117  public:
    118   TouchEditableImplAuraTest() {}
    119 
    120  protected:
    121   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
    122     command_line->AppendSwitch(switches::kEnableTouchEditing);
    123   }
    124 
    125   // Executes the javascript synchronously and makes sure the returned value is
    126   // freed properly.
    127   void ExecuteSyncJSFunction(RenderFrameHost* rfh, const std::string& jscript) {
    128     scoped_ptr<base::Value> value =
    129         content::ExecuteScriptAndGetValue(rfh, jscript);
    130   }
    131 
    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
    134   // complete.
    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));
    141   }
    142 
    143   RenderWidgetHostViewAura* GetRenderWidgetHostViewAura(
    144       TouchEditableImplAura* touch_editable) {
    145     return touch_editable->rwhva_;
    146   }
    147 
    148   ui::TouchSelectionController* GetTouchSelectionController(
    149       TouchEditableImplAura* touch_editable) {
    150     return touch_editable->touch_selection_controller_.get();
    151   }
    152 
    153   ui::TextInputType GetTextInputType(TouchEditableImplAura* touch_editable) {
    154     return touch_editable->text_input_type_;
    155   }
    156 
    157  private:
    158   DISALLOW_COPY_AND_ASSIGN(TouchEditableImplAuraTest);
    159 };
    160 
    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();
    176 
    177   touch_editable->Reset();
    178   ExecuteSyncJSFunction(main_frame, "select_all_text()");
    179   touch_editable->WaitForSelectionChangeCallback();
    180 
    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);
    184 
    185   scoped_ptr<base::Value> value =
    186       content::ExecuteScriptAndGetValue(main_frame, "get_selection()");
    187   std::string selection;
    188   value->GetAsString(&selection);
    189 
    190   // Check if selection handles are showing.
    191   EXPECT_TRUE(GetTouchSelectionController(touch_editable));
    192   EXPECT_STREQ("Some text we can select", selection.c_str());
    193 
    194   // Lets move the handles a bit to modify the selection
    195   touch_editable->Reset();
    196   generator.GestureScrollSequence(
    197       gfx::Point(10, 47),
    198       gfx::Point(30, 47),
    199       base::TimeDelta::FromMilliseconds(20),
    200       5);
    201   touch_editable->WaitForSelectionChangeCallback();
    202 
    203   EXPECT_TRUE(GetTouchSelectionController(touch_editable));
    204   value = content::ExecuteScriptAndGetValue(main_frame, "get_selection()");
    205   value->GetAsString(&selection);
    206 
    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());
    210 }
    211 
    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);
    225 
    226   // Long press to select word.
    227   ui::GestureEvent long_press(
    228       10,
    229       10,
    230       0,
    231       ui::EventTimeForNow(),
    232       ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS));
    233   touch_editable->Reset();
    234   rwhva->OnGestureEvent(&long_press);
    235   touch_editable->WaitForSelectionChangeCallback();
    236 
    237   // Check if selection handles are showing.
    238   EXPECT_TRUE(GetTouchSelectionController(touch_editable));
    239 
    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());
    245 
    246   // Start scrolling. Handles should get hidden.
    247   ui::GestureEvent scroll_begin(
    248       10,
    249       10,
    250       0,
    251       ui::EventTimeForNow(),
    252       ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN));
    253   rwhva->OnGestureEvent(&scroll_begin);
    254   EXPECT_FALSE(GetTouchSelectionController(touch_editable));
    255 
    256   // Handles should come back after scroll ends.
    257   ui::GestureEvent scroll_end(
    258       10,
    259       10,
    260       0,
    261       ui::EventTimeForNow(),
    262       ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END));
    263   rwhva->OnGestureEvent(&scroll_end);
    264   EXPECT_TRUE(GetTouchSelectionController(touch_editable));
    265 }
    266 
    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);
    280 
    281   // Long press to select word.
    282   ui::GestureEvent long_press(
    283       10,
    284       10,
    285       0,
    286       ui::EventTimeForNow(),
    287       ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS));
    288   touch_editable->Reset();
    289   rwhva->OnGestureEvent(&long_press);
    290   touch_editable->WaitForSelectionChangeCallback();
    291 
    292   // Check if selection handles are showing.
    293   EXPECT_TRUE(GetTouchSelectionController(touch_editable));
    294 
    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());
    300 
    301   // Start scrolling. Handles should get hidden.
    302   ui::GestureEvent scroll_begin(
    303       10,
    304       10,
    305       0,
    306       ui::EventTimeForNow(),
    307       ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, 0, 0));
    308   rwhva->OnGestureEvent(&scroll_begin);
    309   EXPECT_FALSE(GetTouchSelectionController(touch_editable));
    310 
    311   // Start a fling. Handles should come back after fling stops.
    312   ui::GestureEvent fling_start(
    313       10,
    314       10,
    315       0,
    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));
    321 }
    322 
    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);
    336 
    337   // Long press to select word.
    338   ui::GestureEvent long_press(
    339       10,
    340       10,
    341       0,
    342       ui::EventTimeForNow(),
    343       ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS));
    344   touch_editable->Reset();
    345   rwhva->OnGestureEvent(&long_press);
    346   touch_editable->WaitForSelectionChangeCallback();
    347 
    348   // Check if selection handles are showing.
    349   EXPECT_TRUE(GetTouchSelectionController(touch_editable));
    350 
    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());
    356 }
    357 
    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);
    371 
    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();
    379 
    380   // Make sure touch selection handles are not showing.
    381   EXPECT_FALSE(GetTouchSelectionController(touch_editable));
    382 
    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());
    388 }
    389 
    390 #if defined(OS_CHROMEOS)
    391 // http://crbug.com/396509
    392 #define MAYBE_TouchCursorInTextfieldTest DISABLED_TouchCursorInTextfieldTest
    393 #else
    394 #define MAYBE_TouchCursorInTextfieldTest TouchCursorInTextfieldTest
    395 #endif
    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);
    412 
    413   ExecuteSyncJSFunction(main_frame, "focus_textfield()");
    414   touch_editable->WaitForSelectionChangeCallback();
    415 
    416   // Tap textfield
    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();
    423 
    424   // Check if cursor handle is showing.
    425   EXPECT_NE(ui::TEXT_INPUT_TYPE_NONE, GetTextInputType(touch_editable));
    426   EXPECT_TRUE(GetTouchSelectionController(touch_editable));
    427 
    428   scoped_ptr<base::Value> value =
    429       content::ExecuteScriptAndGetValue(main_frame, "get_cursor_position()");
    430   int cursor_pos = -1;
    431   value->GetAsInteger(&cursor_pos);
    432   EXPECT_NE(-1, cursor_pos);
    433 
    434   // Move the cursor handle.
    435   generator.GestureScrollSequence(
    436       gfx::Point(50, 59),
    437       gfx::Point(10, 59),
    438       base::TimeDelta::FromMilliseconds(20),
    439       1);
    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);
    449 }
    450 
    451 }  // namespace content
    452