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/test/event_generator.h"
     23 #include "ui/aura/window.h"
     24 #include "ui/aura/window_tree_host.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 
     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 
     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 
     94 class 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 
    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   aura::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 
    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);
    203 
    204   // Long press to select word.
    205   ui::GestureEvent long_press(ui::ET_GESTURE_LONG_PRESS,
    206                               10,
    207                               10,
    208                               0,
    209                               ui::EventTimeForNow(),
    210                               ui::GestureEventDetails(
    211                                   ui::ET_GESTURE_LONG_PRESS, 0, 0),
    212                               1);
    213   touch_editable->Reset();
    214   rwhva->OnGestureEvent(&long_press);
    215   touch_editable->WaitForSelectionChangeCallback();
    216 
    217   // Check if selection handles are showing.
    218   EXPECT_TRUE(GetTouchSelectionController(touch_editable));
    219 
    220   scoped_ptr<base::Value> value =
    221       content::ExecuteScriptAndGetValue(main_frame, "get_selection()");
    222   std::string selection;
    223   value->GetAsString(&selection);
    224   EXPECT_STREQ("Some", selection.c_str());
    225 
    226   // Start scrolling. Handles should get hidden.
    227   ui::GestureEvent scroll_begin(ui::ET_GESTURE_SCROLL_BEGIN,
    228                                 10,
    229                                 10,
    230                                 0,
    231                                 ui::EventTimeForNow(),
    232                                 ui::GestureEventDetails(
    233                                     ui::ET_GESTURE_SCROLL_BEGIN, 0, 0),
    234                                 1);
    235   rwhva->OnGestureEvent(&scroll_begin);
    236   EXPECT_FALSE(GetTouchSelectionController(touch_editable));
    237 
    238   // Handles should come back after scroll ends.
    239   ui::GestureEvent scroll_end(ui::ET_GESTURE_SCROLL_END,
    240                               10,
    241                               10,
    242                               0,
    243                               ui::EventTimeForNow(),
    244                               ui::GestureEventDetails(
    245                                   ui::ET_GESTURE_SCROLL_END, 0, 0),
    246                               1);
    247   rwhva->OnGestureEvent(&scroll_end);
    248   EXPECT_TRUE(GetTouchSelectionController(touch_editable));
    249 }
    250 
    251 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest,
    252                        TouchSelectionOnLongPressTest) {
    253   ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
    254   WebContentsImpl* web_contents =
    255       static_cast<WebContentsImpl*>(shell()->web_contents());
    256   RenderFrameHost* main_frame = web_contents->GetMainFrame();
    257   WebContentsViewAura* view_aura = static_cast<WebContentsViewAura*>(
    258       web_contents->GetView());
    259   TestTouchEditableImplAura* touch_editable = new TestTouchEditableImplAura;
    260   view_aura->SetTouchEditableForTest(touch_editable);
    261   RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>(
    262       web_contents->GetRenderWidgetHostView());
    263   EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable), rwhva);
    264 
    265   // Long press to select word.
    266   ui::GestureEvent long_press(ui::ET_GESTURE_LONG_PRESS,
    267                               10,
    268                               10,
    269                               0,
    270                               ui::EventTimeForNow(),
    271                               ui::GestureEventDetails(
    272                                   ui::ET_GESTURE_LONG_PRESS, 0, 0),
    273                               1);
    274   touch_editable->Reset();
    275   rwhva->OnGestureEvent(&long_press);
    276   touch_editable->WaitForSelectionChangeCallback();
    277 
    278   // Check if selection handles are showing.
    279   EXPECT_TRUE(GetTouchSelectionController(touch_editable));
    280 
    281   scoped_ptr<base::Value> value =
    282       content::ExecuteScriptAndGetValue(main_frame, "get_selection()");
    283   std::string selection;
    284   value->GetAsString(&selection);
    285   EXPECT_STREQ("Some", selection.c_str());
    286 }
    287 
    288 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest,
    289                        NoTouchSelectionOnDoubleTapTest) {
    290   ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
    291   WebContentsImpl* web_contents =
    292       static_cast<WebContentsImpl*>(shell()->web_contents());
    293   RenderFrameHost* main_frame = web_contents->GetMainFrame();
    294   WebContentsViewAura* view_aura =
    295       static_cast<WebContentsViewAura*>(web_contents->GetView());
    296   TestTouchEditableImplAura* touch_editable = new TestTouchEditableImplAura;
    297   view_aura->SetTouchEditableForTest(touch_editable);
    298   RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>(
    299       web_contents->GetRenderWidgetHostView());
    300   EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable), rwhva);
    301 
    302   // Double-tap to select word.
    303   ui::GestureEvent double_tap(ui::ET_GESTURE_TAP,
    304                               10,
    305                               10,
    306                               0,
    307                               ui::EventTimeForNow(),
    308                               ui::GestureEventDetails(ui::ET_GESTURE_TAP, 2, 0),
    309                               1);
    310   touch_editable->Reset();
    311   rwhva->OnGestureEvent(&double_tap);
    312   touch_editable->WaitForSelectionChangeCallback();
    313 
    314   // Make sure touch selection handles are not showing.
    315   EXPECT_FALSE(GetTouchSelectionController(touch_editable));
    316 
    317   scoped_ptr<base::Value> value =
    318       content::ExecuteScriptAndGetValue(main_frame, "get_selection()");
    319   std::string selection;
    320   value->GetAsString(&selection);
    321   EXPECT_STREQ("Some", selection.c_str());
    322 }
    323 
    324 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest,
    325                        TouchCursorInTextfieldTest) {
    326   ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html"));
    327   WebContentsImpl* web_contents =
    328       static_cast<WebContentsImpl*>(shell()->web_contents());
    329   RenderFrameHost* main_frame = web_contents->GetMainFrame();
    330   WebContentsViewAura* view_aura = static_cast<WebContentsViewAura*>(
    331       web_contents->GetView());
    332   TestTouchEditableImplAura* touch_editable = new TestTouchEditableImplAura;
    333   view_aura->SetTouchEditableForTest(touch_editable);
    334   RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>(
    335       web_contents->GetRenderWidgetHostView());
    336   aura::Window* content = web_contents->GetContentNativeView();
    337   aura::test::EventGenerator generator(content->GetRootWindow(), content);
    338   gfx::Rect bounds = content->GetBoundsInRootWindow();
    339   EXPECT_EQ(GetRenderWidgetHostViewAura(touch_editable), rwhva);
    340 
    341   ExecuteSyncJSFunction(main_frame, "focus_textfield()");
    342   touch_editable->WaitForSelectionChangeCallback();
    343 
    344   // Tap textfield
    345   touch_editable->Reset();
    346   generator.GestureTapAt(gfx::Point(bounds.x() + 50, bounds.y() + 40));
    347   // Tap Down acks are sent synchronously, while Tap acks are asynchronous.
    348   touch_editable->WaitForGestureAck(WebInputEvent::GestureTap);
    349   touch_editable->WaitForSelectionChangeCallback();
    350   touch_editable->Reset();
    351 
    352   // Check if cursor handle is showing.
    353   EXPECT_NE(ui::TEXT_INPUT_TYPE_NONE, GetTextInputType(touch_editable));
    354   EXPECT_TRUE(GetTouchSelectionController(touch_editable));
    355 
    356   scoped_ptr<base::Value> value =
    357       content::ExecuteScriptAndGetValue(main_frame, "get_cursor_position()");
    358   int cursor_pos = -1;
    359   value->GetAsInteger(&cursor_pos);
    360   EXPECT_NE(-1, cursor_pos);
    361 
    362   // Move the cursor handle.
    363   generator.GestureScrollSequence(
    364       gfx::Point(50, 59),
    365       gfx::Point(10, 59),
    366       base::TimeDelta::FromMilliseconds(20),
    367       1);
    368   touch_editable->WaitForSelectionChangeCallback();
    369   EXPECT_TRUE(GetTouchSelectionController(touch_editable));
    370   value = content::ExecuteScriptAndGetValue(main_frame,
    371                                             "get_cursor_position()");
    372   int new_cursor_pos = -1;
    373   value->GetAsInteger(&new_cursor_pos);
    374   EXPECT_NE(-1, new_cursor_pos);
    375   // Cursor should have moved.
    376   EXPECT_NE(new_cursor_pos, cursor_pos);
    377 }
    378 
    379 }  // namespace content
    380