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