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