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