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