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