1// Copyright 2014 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/renderer_host/input/touch_selection_controller.h" 6 7#include "testing/gtest/include/gtest/gtest.h" 8#include "ui/events/test/mock_motion_event.h" 9 10using ui::test::MockMotionEvent; 11 12namespace content { 13namespace { 14 15const int kDefaultTapTimeoutMs = 200; 16const float kDefaulTapSlop = 10.f; 17 18class MockTouchHandleDrawable : public TouchHandleDrawable { 19 public: 20 explicit MockTouchHandleDrawable(bool* contains_point) 21 : intersects_rect_(contains_point) {} 22 virtual ~MockTouchHandleDrawable() {} 23 virtual void SetEnabled(bool enabled) OVERRIDE {} 24 virtual void SetOrientation(TouchHandleOrientation orientation) OVERRIDE {} 25 virtual void SetAlpha(float alpha) OVERRIDE {} 26 virtual void SetFocus(const gfx::PointF& position) OVERRIDE {} 27 virtual void SetVisible(bool visible) OVERRIDE {} 28 virtual bool IntersectsWith(const gfx::RectF& rect) const OVERRIDE { 29 return *intersects_rect_; 30 } 31 32 private: 33 bool* intersects_rect_; 34}; 35 36} // namespace 37 38class TouchSelectionControllerTest : public testing::Test, 39 public TouchSelectionControllerClient { 40 public: 41 TouchSelectionControllerTest() 42 : last_event_(SELECTION_CLEARED), 43 caret_moved_(false), 44 selection_moved_(false), 45 needs_animate_(false), 46 animation_enabled_(true), 47 dragging_enabled_(false) {} 48 49 virtual ~TouchSelectionControllerTest() {} 50 51 // testing::Test implementation. 52 virtual void SetUp() OVERRIDE { 53 controller_.reset(new TouchSelectionController( 54 this, 55 base::TimeDelta::FromMilliseconds(kDefaultTapTimeoutMs), 56 kDefaulTapSlop)); 57 } 58 59 virtual void TearDown() OVERRIDE { controller_.reset(); } 60 61 // TouchSelectionControllerClient implementation. 62 63 virtual bool SupportsAnimation() const OVERRIDE { return animation_enabled_; } 64 65 virtual void SetNeedsAnimate() OVERRIDE { needs_animate_ = true; } 66 67 virtual void MoveCaret(const gfx::PointF& position) OVERRIDE { 68 caret_moved_ = true; 69 caret_position_ = position; 70 } 71 72 virtual void SelectBetweenCoordinates(const gfx::PointF& start, 73 const gfx::PointF& end) OVERRIDE { 74 selection_moved_ = true; 75 selection_start_ = start; 76 selection_end_ = end; 77 } 78 79 virtual void OnSelectionEvent(SelectionEventType event, 80 const gfx::PointF& end_position) OVERRIDE { 81 last_event_ = event; 82 last_event_start_ = end_position; 83 } 84 85 virtual scoped_ptr<TouchHandleDrawable> CreateDrawable() OVERRIDE { 86 return scoped_ptr<TouchHandleDrawable>( 87 new MockTouchHandleDrawable(&dragging_enabled_)); 88 } 89 90 void SetAnimationEnabled(bool enabled) { animation_enabled_ = enabled; } 91 void SetDraggingEnabled(bool enabled) { dragging_enabled_ = enabled; } 92 93 void ClearSelection() { 94 controller_->OnSelectionBoundsChanged(cc::ViewportSelectionBound(), 95 cc::ViewportSelectionBound()); 96 } 97 98 void ClearInsertion() { ClearSelection(); } 99 100 void ChangeInsertion(const gfx::RectF& rect, bool visible) { 101 cc::ViewportSelectionBound bound; 102 bound.type = cc::SELECTION_BOUND_CENTER; 103 bound.edge_top = rect.origin(); 104 bound.edge_bottom = rect.bottom_left(); 105 bound.visible = visible; 106 controller_->OnSelectionBoundsChanged(bound, bound); 107 } 108 109 void ChangeSelection(const gfx::RectF& start_rect, 110 bool start_visible, 111 const gfx::RectF& end_rect, 112 bool end_visible) { 113 cc::ViewportSelectionBound start_bound, end_bound; 114 start_bound.type = cc::SELECTION_BOUND_LEFT; 115 end_bound.type = cc::SELECTION_BOUND_RIGHT; 116 start_bound.edge_top = start_rect.origin(); 117 start_bound.edge_bottom = start_rect.bottom_left(); 118 end_bound.edge_top = end_rect.origin(); 119 end_bound.edge_bottom = end_rect.bottom_left(); 120 start_bound.visible = start_visible; 121 end_bound.visible = end_visible; 122 controller_->OnSelectionBoundsChanged(start_bound, end_bound); 123 } 124 125 void Animate() { 126 base::TimeTicks now = base::TimeTicks::Now(); 127 while (needs_animate_) { 128 needs_animate_ = controller_->Animate(now); 129 now += base::TimeDelta::FromMilliseconds(16); 130 } 131 } 132 133 bool GetAndResetNeedsAnimate() { 134 bool needs_animate = needs_animate_; 135 Animate(); 136 return needs_animate; 137 } 138 139 bool GetAndResetCaretMoved() { 140 bool moved = caret_moved_; 141 caret_moved_ = false; 142 return moved; 143 } 144 145 bool GetAndResetSelectionMoved() { 146 bool moved = selection_moved_; 147 selection_moved_ = false; 148 return moved; 149 } 150 151 const gfx::PointF& GetLastCaretPosition() const { return caret_position_; } 152 const gfx::PointF& GetLastSelectionStart() const { return selection_start_; } 153 const gfx::PointF& GetLastSelectionEnd() const { return selection_end_; } 154 SelectionEventType GetLastEventType() const { return last_event_; } 155 const gfx::PointF& GetLastEventAnchor() const { return last_event_start_; } 156 157 TouchSelectionController& controller() { return *controller_; } 158 159 private: 160 gfx::PointF last_event_start_; 161 gfx::PointF caret_position_; 162 gfx::PointF selection_start_; 163 gfx::PointF selection_end_; 164 SelectionEventType last_event_; 165 bool caret_moved_; 166 bool selection_moved_; 167 bool needs_animate_; 168 bool animation_enabled_; 169 bool dragging_enabled_; 170 scoped_ptr<TouchSelectionController> controller_; 171}; 172 173TEST_F(TouchSelectionControllerTest, InsertionBasic) { 174 gfx::RectF insertion_rect(5, 5, 0, 10); 175 bool visible = true; 176 177 // Insertion events are ignored until automatic showing is enabled. 178 ChangeInsertion(insertion_rect, visible); 179 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor()); 180 controller().OnTapEvent(); 181 182 // Insertion events are ignored until the selection region is marked editable. 183 ChangeInsertion(insertion_rect, visible); 184 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor()); 185 186 controller().OnTapEvent(); 187 controller().OnSelectionEditable(true); 188 ChangeInsertion(insertion_rect, visible); 189 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); 190 EXPECT_EQ(insertion_rect.bottom_left(), GetLastEventAnchor()); 191 192 insertion_rect.Offset(1, 0); 193 ChangeInsertion(insertion_rect, visible); 194 EXPECT_EQ(INSERTION_MOVED, GetLastEventType()); 195 EXPECT_EQ(insertion_rect.bottom_left(), GetLastEventAnchor()); 196 197 insertion_rect.Offset(0, 1); 198 ChangeInsertion(insertion_rect, visible); 199 EXPECT_EQ(INSERTION_MOVED, GetLastEventType()); 200 EXPECT_EQ(insertion_rect.bottom_left(), GetLastEventAnchor()); 201 202 ClearInsertion(); 203 EXPECT_EQ(INSERTION_CLEARED, GetLastEventType()); 204} 205 206TEST_F(TouchSelectionControllerTest, InsertionClearedWhenNoLongerEditable) { 207 gfx::RectF insertion_rect(5, 5, 0, 10); 208 bool visible = true; 209 controller().OnTapEvent(); 210 controller().OnSelectionEditable(true); 211 212 ChangeInsertion(insertion_rect, visible); 213 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); 214 EXPECT_EQ(insertion_rect.bottom_left(), GetLastEventAnchor()); 215 216 controller().OnSelectionEditable(false); 217 EXPECT_EQ(INSERTION_CLEARED, GetLastEventType()); 218} 219 220TEST_F(TouchSelectionControllerTest, InsertionStaysHiddenIfEmptyRegionTapped) { 221 gfx::RectF insertion_rect(5, 5, 0, 10); 222 bool visible = true; 223 controller().OnSelectionEditable(true); 224 225 // Taps should be ignored if they're in an empty editable region. 226 controller().OnTapEvent(); 227 controller().OnSelectionEmpty(true); 228 ChangeInsertion(insertion_rect, visible); 229 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor()); 230 231 // Once the region becomes editable, taps should show the insertion handle. 232 controller().OnTapEvent(); 233 controller().OnSelectionEmpty(false); 234 ChangeInsertion(insertion_rect, visible); 235 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); 236 EXPECT_EQ(insertion_rect.bottom_left(), GetLastEventAnchor()); 237 238 // Reset the selection. 239 controller().HideAndDisallowShowingAutomatically(); 240 EXPECT_EQ(INSERTION_CLEARED, GetLastEventType()); 241 242 // Long-pressing should show the handle even if the editable region is empty. 243 insertion_rect.Offset(2, -2); 244 controller().OnLongPressEvent(); 245 controller().OnSelectionEmpty(true); 246 ChangeInsertion(insertion_rect, visible); 247 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); 248 EXPECT_EQ(insertion_rect.bottom_left(), GetLastEventAnchor()); 249 250 // Single Tap on an empty edit field should clear insertion handle. 251 controller().OnTapEvent(); 252 EXPECT_EQ(INSERTION_CLEARED, GetLastEventType()); 253} 254 255TEST_F(TouchSelectionControllerTest, InsertionAppearsAfterTapFollowingTyping) { 256 gfx::RectF insertion_rect(5, 5, 0, 10); 257 bool visible = true; 258 259 // Simulate the user tapping an empty text field. 260 controller().OnTapEvent(); 261 controller().OnSelectionEditable(true); 262 controller().OnSelectionEmpty(true); 263 ChangeInsertion(insertion_rect, visible); 264 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor()); 265 266 // Simulate the cursor moving while a user is typing. 267 insertion_rect.Offset(10, 0); 268 controller().OnSelectionEmpty(false); 269 ChangeInsertion(insertion_rect, visible); 270 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor()); 271 272 // If the user taps the *same* position as the cursor at the end of the text 273 // entry, the handle should appear. 274 controller().OnTapEvent(); 275 ChangeInsertion(insertion_rect, visible); 276 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); 277 EXPECT_EQ(insertion_rect.bottom_left(), GetLastEventAnchor()); 278} 279 280TEST_F(TouchSelectionControllerTest, InsertionToSelectionTransition) { 281 controller().OnLongPressEvent(); 282 controller().OnSelectionEditable(true); 283 284 gfx::RectF start_rect(5, 5, 0, 10); 285 gfx::RectF end_rect(50, 5, 0, 10); 286 bool visible = true; 287 288 ChangeInsertion(start_rect, visible); 289 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); 290 EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor()); 291 292 ChangeSelection(start_rect, visible, end_rect, visible); 293 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType()); 294 EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor()); 295 296 ChangeInsertion(end_rect, visible); 297 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); 298 EXPECT_EQ(end_rect.bottom_left(), GetLastEventAnchor()); 299 300 controller().HideAndDisallowShowingAutomatically(); 301 EXPECT_EQ(INSERTION_CLEARED, GetLastEventType()); 302 303 controller().OnTapEvent(); 304 ChangeInsertion(end_rect, visible); 305 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); 306 EXPECT_EQ(end_rect.bottom_left(), GetLastEventAnchor()); 307} 308 309TEST_F(TouchSelectionControllerTest, InsertionDragged) { 310 base::TimeTicks event_time = base::TimeTicks::Now(); 311 controller().OnTapEvent(); 312 controller().OnSelectionEditable(true); 313 314 // The touch sequence should not be handled if insertion is not active. 315 MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 0, 0); 316 EXPECT_FALSE(controller().WillHandleTouchEvent(event)); 317 318 float line_height = 10.f; 319 gfx::RectF start_rect(10, 0, 0, line_height); 320 bool visible = true; 321 ChangeInsertion(start_rect, visible); 322 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); 323 EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor()); 324 325 // The touch sequence should be handled only if the drawable reports a hit. 326 EXPECT_FALSE(controller().WillHandleTouchEvent(event)); 327 SetDraggingEnabled(true); 328 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 329 EXPECT_FALSE(GetAndResetCaretMoved()); 330 331 // The MoveCaret() result should reflect the movement. 332 // The reported position is offset from the center of |start_rect|. 333 gfx::PointF start_offset = start_rect.CenterPoint(); 334 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 0, 5); 335 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 336 EXPECT_TRUE(GetAndResetCaretMoved()); 337 EXPECT_EQ(start_offset + gfx::Vector2dF(0, 5), GetLastCaretPosition()); 338 339 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 5, 5); 340 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 341 EXPECT_TRUE(GetAndResetCaretMoved()); 342 EXPECT_EQ(start_offset + gfx::Vector2dF(5, 5), GetLastCaretPosition()); 343 344 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 10, 10); 345 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 346 EXPECT_TRUE(GetAndResetCaretMoved()); 347 EXPECT_EQ(start_offset + gfx::Vector2dF(10, 10), GetLastCaretPosition()); 348 349 event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 10, 5); 350 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 351 EXPECT_FALSE(GetAndResetCaretMoved()); 352 353 // Once the drag is complete, no more touch events should be consumed until 354 // the next ACTION_DOWN. 355 EXPECT_FALSE(controller().WillHandleTouchEvent(event)); 356} 357 358TEST_F(TouchSelectionControllerTest, InsertionTapped) { 359 base::TimeTicks event_time = base::TimeTicks::Now(); 360 controller().OnTapEvent(); 361 controller().OnSelectionEditable(true); 362 SetDraggingEnabled(true); 363 364 gfx::RectF start_rect(10, 0, 0, 10); 365 bool visible = true; 366 ChangeInsertion(start_rect, visible); 367 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); 368 369 MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 0, 0); 370 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 371 //TODO(AKV): this test case has to be modified once crbug.com/394093 is fixed. 372 EXPECT_EQ(INSERTION_DRAG_STARTED, GetLastEventType()); 373 374 event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 0, 0); 375 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 376 EXPECT_EQ(INSERTION_TAPPED, GetLastEventType()); 377 378 // Reset the insertion. 379 ClearInsertion(); 380 controller().OnTapEvent(); 381 ChangeInsertion(start_rect, visible); 382 ASSERT_EQ(INSERTION_SHOWN, GetLastEventType()); 383 384 // No tap should be signalled if the time between DOWN and UP was too long. 385 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0); 386 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 387 event = MockMotionEvent(MockMotionEvent::ACTION_UP, 388 event_time + base::TimeDelta::FromSeconds(1), 389 0, 390 0); 391 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 392 EXPECT_EQ(INSERTION_DRAG_STARTED, GetLastEventType()); 393 394 // Reset the insertion. 395 ClearInsertion(); 396 controller().OnTapEvent(); 397 ChangeInsertion(start_rect, visible); 398 ASSERT_EQ(INSERTION_SHOWN, GetLastEventType()); 399 400 // No tap should be signalled if the drag was too long. 401 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0); 402 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 403 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 100, 0); 404 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 405 event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 100, 0); 406 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 407 EXPECT_EQ(INSERTION_DRAG_STARTED, GetLastEventType()); 408 409 // Reset the insertion. 410 ClearInsertion(); 411 controller().OnTapEvent(); 412 ChangeInsertion(start_rect, visible); 413 ASSERT_EQ(INSERTION_SHOWN, GetLastEventType()); 414 415 // No tap should be signalled if the touch sequence is cancelled. 416 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0); 417 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 418 event = MockMotionEvent(MockMotionEvent::ACTION_CANCEL, event_time, 0, 0); 419 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 420 EXPECT_EQ(INSERTION_DRAG_STARTED, GetLastEventType()); 421} 422 423TEST_F(TouchSelectionControllerTest, InsertionNotResetByRepeatedTapOrPress) { 424 base::TimeTicks event_time = base::TimeTicks::Now(); 425 controller().OnTapEvent(); 426 controller().OnSelectionEditable(true); 427 SetDraggingEnabled(true); 428 429 gfx::RectF anchor_rect(10, 0, 0, 10); 430 bool visible = true; 431 ChangeInsertion(anchor_rect, visible); 432 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); 433 EXPECT_EQ(anchor_rect.bottom_left(), GetLastEventAnchor()); 434 435 // Tapping again shouldn't reset the active insertion point. 436 controller().OnTapEvent(); 437 MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 0, 0); 438 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 439 EXPECT_EQ(INSERTION_DRAG_STARTED, GetLastEventType()); 440 EXPECT_EQ(anchor_rect.bottom_left(), GetLastEventAnchor()); 441 442 event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 0, 0); 443 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 444 EXPECT_EQ(INSERTION_TAPPED, GetLastEventType()); 445 EXPECT_EQ(anchor_rect.bottom_left(), GetLastEventAnchor()); 446 447 anchor_rect.Offset(5, 15); 448 ChangeInsertion(anchor_rect, visible); 449 EXPECT_EQ(INSERTION_MOVED, GetLastEventType()); 450 EXPECT_EQ(anchor_rect.bottom_left(), GetLastEventAnchor()); 451 452 // Pressing shouldn't reset the active insertion point. 453 controller().OnLongPressEvent(); 454 controller().OnSelectionEmpty(true); 455 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0); 456 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 457 EXPECT_EQ(INSERTION_DRAG_STARTED, GetLastEventType()); 458 EXPECT_EQ(anchor_rect.bottom_left(), GetLastEventAnchor()); 459 460 event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 0, 0); 461 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 462 EXPECT_EQ(INSERTION_TAPPED, GetLastEventType()); 463 EXPECT_EQ(anchor_rect.bottom_left(), GetLastEventAnchor()); 464} 465 466TEST_F(TouchSelectionControllerTest, SelectionBasic) { 467 gfx::RectF start_rect(5, 5, 0, 10); 468 gfx::RectF end_rect(50, 5, 0, 10); 469 bool visible = true; 470 471 // Selection events are ignored until automatic showing is enabled. 472 ChangeSelection(start_rect, visible, end_rect, visible); 473 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor()); 474 475 controller().OnLongPressEvent(); 476 ChangeSelection(start_rect, visible, end_rect, visible); 477 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType()); 478 EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor()); 479 480 start_rect.Offset(1, 0); 481 ChangeSelection(start_rect, visible, end_rect, visible); 482 // Selection movement does not currently trigger a separate event. 483 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType()); 484 485 ClearSelection(); 486 EXPECT_EQ(SELECTION_CLEARED, GetLastEventType()); 487 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor()); 488} 489 490TEST_F(TouchSelectionControllerTest, SelectionRepeatedLongPress) { 491 gfx::RectF start_rect(5, 5, 0, 10); 492 gfx::RectF end_rect(50, 5, 0, 10); 493 bool visible = true; 494 495 controller().OnLongPressEvent(); 496 ChangeSelection(start_rect, visible, end_rect, visible); 497 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType()); 498 EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor()); 499 500 // A long press triggering a new selection should re-send the SELECTION_SHOWN 501 // event notification. 502 start_rect.Offset(10, 10); 503 controller().OnLongPressEvent(); 504 ChangeSelection(start_rect, visible, end_rect, visible); 505 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType()); 506 EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor()); 507} 508 509TEST_F(TouchSelectionControllerTest, SelectionDragged) { 510 base::TimeTicks event_time = base::TimeTicks::Now(); 511 controller().OnLongPressEvent(); 512 513 // The touch sequence should not be handled if selection is not active. 514 MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 0, 0); 515 EXPECT_FALSE(controller().WillHandleTouchEvent(event)); 516 517 float line_height = 10.f; 518 gfx::RectF start_rect(0, 0, 0, line_height); 519 gfx::RectF end_rect(50, 0, 0, line_height); 520 bool visible = true; 521 ChangeSelection(start_rect, visible, end_rect, visible); 522 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType()); 523 EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor()); 524 525 // The touch sequence should be handled only if the drawable reports a hit. 526 EXPECT_FALSE(controller().WillHandleTouchEvent(event)); 527 SetDraggingEnabled(true); 528 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 529 EXPECT_FALSE(GetAndResetSelectionMoved()); 530 531 // The SelectBetweenCoordinates() result should reflect the movement. Note 532 // that the start coordinate will always reflect the "fixed" handle's 533 // position, in this case the position from |end_rect|. 534 // Note that the reported position is offset from the center of the 535 // input rects (i.e., the middle of the corresponding text line). 536 gfx::PointF fixed_offset = end_rect.CenterPoint(); 537 gfx::PointF start_offset = start_rect.CenterPoint(); 538 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 0, 5); 539 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 540 EXPECT_EQ(SELECTION_DRAG_STARTED, GetLastEventType()); 541 EXPECT_TRUE(GetAndResetSelectionMoved()); 542 EXPECT_EQ(fixed_offset, GetLastSelectionStart()); 543 EXPECT_EQ(start_offset + gfx::Vector2dF(0, 5), GetLastSelectionEnd()); 544 545 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 5, 5); 546 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 547 EXPECT_TRUE(GetAndResetSelectionMoved()); 548 EXPECT_EQ(fixed_offset, GetLastSelectionStart()); 549 EXPECT_EQ(start_offset + gfx::Vector2dF(5, 5), GetLastSelectionEnd()); 550 551 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 10, 5); 552 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 553 EXPECT_TRUE(GetAndResetSelectionMoved()); 554 EXPECT_EQ(fixed_offset, GetLastSelectionStart()); 555 EXPECT_EQ(start_offset + gfx::Vector2dF(10, 5), GetLastSelectionEnd()); 556 557 event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 10, 5); 558 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 559 EXPECT_EQ(SELECTION_DRAG_STOPPED, GetLastEventType()); 560 EXPECT_FALSE(GetAndResetSelectionMoved()); 561 562 // Once the drag is complete, no more touch events should be consumed until 563 // the next ACTION_DOWN. 564 EXPECT_FALSE(controller().WillHandleTouchEvent(event)); 565} 566 567TEST_F(TouchSelectionControllerTest, SelectionDraggedWithOverlap) { 568 base::TimeTicks event_time = base::TimeTicks::Now(); 569 controller().OnLongPressEvent(); 570 571 float line_height = 10.f; 572 gfx::RectF start_rect(0, 0, 0, line_height); 573 gfx::RectF end_rect(50, 0, 0, line_height); 574 bool visible = true; 575 ChangeSelection(start_rect, visible, end_rect, visible); 576 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType()); 577 EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor()); 578 579 // The ACTION_DOWN should lock to the closest handle. 580 gfx::PointF end_offset = end_rect.CenterPoint(); 581 gfx::PointF fixed_offset = start_rect.CenterPoint(); 582 float touch_down_x = (end_offset.x() + fixed_offset.x()) / 2 + 1.f; 583 MockMotionEvent event( 584 MockMotionEvent::ACTION_DOWN, event_time, touch_down_x, 0); 585 SetDraggingEnabled(true); 586 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 587 EXPECT_EQ(SELECTION_DRAG_STARTED, GetLastEventType()); 588 EXPECT_FALSE(GetAndResetSelectionMoved()); 589 590 // Even though the ACTION_MOVE is over the start handle, it should continue 591 // targetting the end handle that consumed the ACTION_DOWN. 592 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 0, 0); 593 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 594 EXPECT_TRUE(GetAndResetSelectionMoved()); 595 EXPECT_EQ(fixed_offset, GetLastSelectionStart()); 596 EXPECT_EQ(end_offset - gfx::Vector2dF(touch_down_x, 0), 597 GetLastSelectionEnd()); 598 599 event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 0, 0); 600 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); 601 EXPECT_EQ(SELECTION_DRAG_STOPPED, GetLastEventType()); 602 EXPECT_FALSE(GetAndResetSelectionMoved()); 603} 604 605TEST_F(TouchSelectionControllerTest, Animation) { 606 controller().OnTapEvent(); 607 controller().OnSelectionEditable(true); 608 609 gfx::RectF insertion_rect(5, 5, 0, 10); 610 611 bool visible = true; 612 ChangeInsertion(insertion_rect, visible); 613 EXPECT_FALSE(GetAndResetNeedsAnimate()); 614 615 visible = false; 616 ChangeInsertion(insertion_rect, visible); 617 EXPECT_TRUE(GetAndResetNeedsAnimate()); 618 619 visible = true; 620 ChangeInsertion(insertion_rect, visible); 621 EXPECT_TRUE(GetAndResetNeedsAnimate()); 622 623 // If the handles are explicity hidden, no animation should be triggered. 624 controller().HideAndDisallowShowingAutomatically(); 625 EXPECT_FALSE(GetAndResetNeedsAnimate()); 626 627 // If the client doesn't support animation, no animation should be triggered. 628 SetAnimationEnabled(false); 629 controller().OnTapEvent(); 630 visible = true; 631 ChangeInsertion(insertion_rect, visible); 632 EXPECT_FALSE(GetAndResetNeedsAnimate()); 633} 634 635TEST_F(TouchSelectionControllerTest, TemporarilyHidden) { 636 controller().OnTapEvent(); 637 controller().OnSelectionEditable(true); 638 639 gfx::RectF insertion_rect(5, 5, 0, 10); 640 641 bool visible = true; 642 ChangeInsertion(insertion_rect, visible); 643 EXPECT_FALSE(GetAndResetNeedsAnimate()); 644 645 controller().SetTemporarilyHidden(true); 646 EXPECT_TRUE(GetAndResetNeedsAnimate()); 647 648 visible = false; 649 ChangeInsertion(insertion_rect, visible); 650 EXPECT_FALSE(GetAndResetNeedsAnimate()); 651 652 visible = true; 653 ChangeInsertion(insertion_rect, visible); 654 EXPECT_FALSE(GetAndResetNeedsAnimate()); 655 656 controller().SetTemporarilyHidden(false); 657 EXPECT_TRUE(GetAndResetNeedsAnimate()); 658} 659 660TEST_F(TouchSelectionControllerTest, SelectionClearOnTap) { 661 gfx::RectF start_rect(5, 5, 0, 10); 662 gfx::RectF end_rect(50, 5, 0, 10); 663 bool visible = true; 664 665 controller().OnLongPressEvent(); 666 ChangeSelection(start_rect, visible, end_rect, visible); 667 668 // Selection should not be cleared if the selection bounds have not changed. 669 controller().OnTapEvent(); 670 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType()); 671 EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor()); 672 673 controller().OnTapEvent(); 674 ClearSelection(); 675 EXPECT_EQ(SELECTION_CLEARED, GetLastEventType()); 676 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor()); 677} 678 679} // namespace content 680