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/mock_motion_event.h" | |
9 | |
10 using ui::test::MockMotionEvent; | |
11 | |
12 namespace content { | |
13 namespace { | |
14 | |
15 class MockTouchHandleDrawable : public TouchHandleDrawable { | |
16 public: | |
17 explicit MockTouchHandleDrawable(bool* contains_point) | |
18 : contains_point_(contains_point) {} | |
19 virtual ~MockTouchHandleDrawable() {} | |
20 virtual void SetOrientation(TouchHandleOrientation orientation) OVERRIDE {} | |
21 virtual void SetAlpha(float alpha) OVERRIDE {} | |
22 virtual void SetFocus(const gfx::PointF& position) OVERRIDE {} | |
23 virtual void SetVisible(bool visible) OVERRIDE {} | |
24 virtual bool ContainsPoint(const gfx::PointF& point) const OVERRIDE { | |
25 return *contains_point_; | |
26 } | |
27 | |
28 private: | |
29 bool* contains_point_; | |
30 }; | |
31 | |
32 } // namespace | |
33 | |
34 class TouchSelectionControllerTest : public testing::Test, | |
35 public TouchSelectionControllerClient { | |
36 public: | |
37 TouchSelectionControllerTest() | |
38 : last_event_(SELECTION_CLEARED), | |
39 caret_moved_(false), | |
40 selection_moved_(false), | |
41 needs_animate_(false), | |
42 dragging_enabled_(false) {} | |
43 | |
44 virtual ~TouchSelectionControllerTest() {} | |
45 | |
46 // testing::Test implementation. | |
47 virtual void SetUp() OVERRIDE { | |
48 controller_.reset(new TouchSelectionController(this)); | |
49 } | |
50 | |
51 virtual void TearDown() OVERRIDE { controller_.reset(); } | |
52 | |
53 // TouchSelectionControllerClient implementation. | |
54 virtual void SetNeedsAnimate() OVERRIDE { needs_animate_ = true; } | |
55 | |
56 virtual void MoveCaret(const gfx::PointF& position) OVERRIDE { | |
57 caret_moved_ = true; | |
58 caret_position_ = position; | |
59 } | |
60 | |
61 virtual void SelectBetweenCoordinates(const gfx::PointF& start, | |
62 const gfx::PointF& end) OVERRIDE { | |
63 selection_moved_ = true; | |
64 selection_start_ = start; | |
65 selection_end_ = end; | |
66 } | |
67 | |
68 virtual void OnSelectionEvent(SelectionEventType event, | |
69 const gfx::PointF& anchor_position) OVERRIDE { | |
70 last_event_ = event; | |
71 last_event_anchor_ = anchor_position; | |
72 } | |
73 | |
74 virtual scoped_ptr<TouchHandleDrawable> CreateDrawable() OVERRIDE { | |
75 return scoped_ptr<TouchHandleDrawable>( | |
76 new MockTouchHandleDrawable(&dragging_enabled_)); | |
77 } | |
78 | |
79 void SetDraggingEnabled(bool enabled) { dragging_enabled_ = enabled; } | |
80 | |
81 void ClearSelection() { | |
82 controller_->OnSelectionBoundsChanged(gfx::RectF(), | |
83 TOUCH_HANDLE_ORIENTATION_UNDEFINED, | |
84 false, | |
85 gfx::RectF(), | |
86 TOUCH_HANDLE_ORIENTATION_UNDEFINED, | |
87 false); | |
88 } | |
89 | |
90 void ClearInsertion() { | |
91 ClearSelection(); | |
92 } | |
93 | |
94 void ChangeInsertion(const gfx::RectF& rect, | |
95 TouchHandleOrientation orientation, | |
96 bool visible) { | |
97 controller_->OnSelectionBoundsChanged( | |
98 rect, orientation, visible, rect, orientation, visible); | |
99 } | |
100 | |
101 void ChangeInsertion(const gfx::RectF& rect, bool visible) { | |
102 ChangeInsertion(rect, TOUCH_HANDLE_CENTER, visible); | |
103 } | |
104 | |
105 void ChangeSelection(const gfx::RectF& anchor_rect, | |
106 TouchHandleOrientation anchor_orientation, | |
107 bool anchor_visible, | |
108 const gfx::RectF& focus_rect, | |
109 TouchHandleOrientation focus_orientation, | |
110 bool focus_visible) { | |
111 controller_->OnSelectionBoundsChanged(anchor_rect, | |
112 anchor_orientation, | |
113 anchor_visible, | |
114 focus_rect, | |
115 focus_orientation, | |
116 focus_visible); | |
117 } | |
118 | |
119 void ChangeSelection(const gfx::RectF& anchor_rect, | |
120 bool anchor_visible, | |
121 const gfx::RectF& focus_rect, | |
122 bool focus_visible) { | |
123 ChangeSelection(anchor_rect, | |
124 TOUCH_HANDLE_LEFT, | |
125 anchor_visible, | |
126 focus_rect, | |
127 TOUCH_HANDLE_RIGHT, | |
128 focus_visible); | |
129 } | |
130 | |
131 void Animate() { | |
132 base::TimeTicks now = base::TimeTicks::Now(); | |
133 while (needs_animate_) { | |
134 needs_animate_ = false; | |
135 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 const gfx::PointF& GetLastCaretPosition() const { return caret_position_; } | |
159 const gfx::PointF& GetLastSelectionStart() const { return selection_start_; } | |
160 const gfx::PointF& GetLastSelectionEnd() const { return selection_end_; } | |
161 SelectionEventType GetLastEventType() const { return last_event_; } | |
162 const gfx::PointF& GetLastEventAnchor() const { return last_event_anchor_; } | |
163 | |
164 TouchSelectionController& controller() { return *controller_; } | |
165 | |
166 private: | |
167 gfx::PointF last_event_anchor_; | |
168 gfx::PointF caret_position_; | |
169 gfx::PointF selection_start_; | |
170 gfx::PointF selection_end_; | |
171 SelectionEventType last_event_; | |
172 bool caret_moved_; | |
173 bool selection_moved_; | |
174 bool needs_animate_; | |
175 bool dragging_enabled_; | |
176 scoped_ptr<TouchSelectionController> controller_; | |
177 }; | |
178 | |
179 TEST_F(TouchSelectionControllerTest, InsertionBasic) { | |
180 gfx::RectF insertion_rect(5, 5, 0, 10); | |
181 bool visible = true; | |
182 | |
183 // Insertion events are ignored until automatic showing is enabled. | |
184 ChangeInsertion(insertion_rect, visible); | |
185 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor()); | |
186 controller().AllowAutomaticInsertionShowing(); | |
187 | |
188 // Insertion events are ignored until the selection region is marked editable. | |
189 ChangeInsertion(insertion_rect, visible); | |
190 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor()); | |
191 controller().OnSelectionEditable(true); | |
192 | |
193 ChangeInsertion(insertion_rect, visible); | |
194 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); | |
195 EXPECT_EQ(insertion_rect.bottom_left(), GetLastEventAnchor()); | |
196 | |
197 insertion_rect.Offset(1, 0); | |
198 ChangeInsertion(insertion_rect, visible); | |
199 EXPECT_EQ(INSERTION_MOVED, GetLastEventType()); | |
200 EXPECT_EQ(insertion_rect.bottom_left(), GetLastEventAnchor()); | |
201 | |
202 insertion_rect.Offset(0, 1); | |
203 ChangeInsertion(insertion_rect, visible); | |
204 EXPECT_EQ(INSERTION_MOVED, GetLastEventType()); | |
205 EXPECT_EQ(insertion_rect.bottom_left(), GetLastEventAnchor()); | |
206 | |
207 ClearInsertion(); | |
208 EXPECT_EQ(INSERTION_CLEARED, GetLastEventType()); | |
209 } | |
210 | |
211 TEST_F(TouchSelectionControllerTest, InsertionClearedWhenLongerEditable) { | |
cjhopman
2014/07/09 22:29:12
s/WhenLonger/WhenNoLonger
jdduke (slow)
2014/07/10 02:08:39
Done.
| |
212 gfx::RectF insertion_rect(5, 5, 0, 10); | |
213 bool visible = true; | |
214 controller().AllowAutomaticInsertionShowing(); | |
215 controller().OnSelectionEditable(true); | |
216 | |
217 ChangeInsertion(insertion_rect, visible); | |
218 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); | |
219 EXPECT_EQ(insertion_rect.bottom_left(), GetLastEventAnchor()); | |
220 | |
221 controller().OnSelectionEditable(false); | |
222 EXPECT_EQ(INSERTION_CLEARED, GetLastEventType()); | |
223 } | |
224 | |
225 TEST_F(TouchSelectionControllerTest, InsertionToSelectionTransition) { | |
226 controller().AllowAutomaticSelectionShowing(); | |
227 controller().AllowAutomaticInsertionShowing(); | |
228 controller().OnSelectionEditable(true); | |
229 | |
230 gfx::RectF anchor_rect(5, 5, 0, 10); | |
231 gfx::RectF focus_rect(50, 5, 0, 10); | |
232 bool visible = true; | |
233 | |
234 ChangeInsertion(anchor_rect, visible); | |
235 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); | |
236 EXPECT_EQ(anchor_rect.bottom_left(), GetLastEventAnchor()); | |
237 | |
238 ChangeSelection(anchor_rect, visible, focus_rect, visible); | |
239 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType()); | |
240 EXPECT_EQ(anchor_rect.bottom_left(), GetLastEventAnchor()); | |
241 | |
242 ChangeInsertion(focus_rect, visible); | |
243 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); | |
244 EXPECT_EQ(focus_rect.bottom_left(), GetLastEventAnchor()); | |
245 | |
246 controller().HideAndDisallowAutomaticShowing(); | |
247 EXPECT_EQ(INSERTION_CLEARED, GetLastEventType()); | |
248 | |
249 controller().AllowAutomaticInsertionShowing(); | |
250 ChangeInsertion(focus_rect, visible); | |
251 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); | |
252 EXPECT_EQ(focus_rect.bottom_left(), GetLastEventAnchor()); | |
253 } | |
254 | |
255 TEST_F(TouchSelectionControllerTest, InsertionDragged) { | |
256 base::TimeTicks event_time = base::TimeTicks::Now(); | |
257 controller().AllowAutomaticInsertionShowing(); | |
258 controller().OnSelectionEditable(true); | |
259 | |
260 // The touch sequence should not be handled if insertion is not active. | |
261 MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 0, 0); | |
262 EXPECT_FALSE(controller().WillHandleTouchEvent(event)); | |
263 | |
264 float line_height = 10.f; | |
265 gfx::RectF anchor_rect(10, 0, 0, line_height); | |
266 bool visible = true; | |
267 ChangeInsertion(anchor_rect, visible); | |
268 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); | |
269 EXPECT_EQ(anchor_rect.bottom_left(), GetLastEventAnchor()); | |
270 | |
271 // The touch sequence should be handled only if the drawable reports a hit. | |
272 EXPECT_FALSE(controller().WillHandleTouchEvent(event)); | |
273 SetDraggingEnabled(true); | |
274 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); | |
275 EXPECT_FALSE(GetAndResetCaretMoved()); | |
276 | |
277 // The MoveCaret() result should reflect the movement. | |
278 // The reported position is offset from the center of |anchor_rect|. | |
279 gfx::PointF anchor_offset = anchor_rect.CenterPoint(); | |
280 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 0, 5); | |
281 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); | |
282 EXPECT_TRUE(GetAndResetCaretMoved()); | |
283 EXPECT_EQ(anchor_offset + gfx::Vector2dF(0, 5), GetLastCaretPosition()); | |
284 | |
285 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 5, 5); | |
286 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); | |
287 EXPECT_TRUE(GetAndResetCaretMoved()); | |
288 EXPECT_EQ(anchor_offset + gfx::Vector2dF(5, 5), GetLastCaretPosition()); | |
289 | |
290 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 10, 10); | |
291 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); | |
292 EXPECT_TRUE(GetAndResetCaretMoved()); | |
293 EXPECT_EQ(anchor_offset + gfx::Vector2dF(10, 10), GetLastCaretPosition()); | |
294 | |
295 event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 10, 5); | |
296 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); | |
297 EXPECT_FALSE(GetAndResetCaretMoved()); | |
298 | |
299 // Once the drag is complete, no more touch events should be consumed until | |
300 // the next ACTION_DOWN. | |
301 EXPECT_FALSE(controller().WillHandleTouchEvent(event)); | |
302 } | |
303 | |
304 TEST_F(TouchSelectionControllerTest, InsertionTapped) { | |
305 base::TimeTicks event_time = base::TimeTicks::Now(); | |
306 controller().AllowAutomaticInsertionShowing(); | |
307 controller().OnSelectionEditable(true); | |
308 SetDraggingEnabled(true); | |
309 | |
310 gfx::RectF anchor_rect(10, 0, 0, 10); | |
311 bool visible = true; | |
312 ChangeInsertion(anchor_rect, visible); | |
313 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); | |
314 | |
315 MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 0, 0); | |
316 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); | |
317 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); | |
318 | |
319 event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 0, 0); | |
320 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); | |
321 EXPECT_EQ(INSERTION_TAPPED, GetLastEventType()); | |
322 | |
323 // Reset the insertion. | |
324 ClearInsertion(); | |
325 controller().AllowAutomaticInsertionShowing(); | |
326 ChangeInsertion(anchor_rect, visible); | |
327 ASSERT_EQ(INSERTION_SHOWN, GetLastEventType()); | |
328 | |
329 // No tap should be signalled if the time between DOWN and UP was too long. | |
330 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0); | |
331 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); | |
332 event = MockMotionEvent(MockMotionEvent::ACTION_UP, | |
333 event_time + base::TimeDelta::FromSeconds(1), | |
334 0, | |
335 0); | |
336 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); | |
337 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); | |
338 | |
339 // Reset the insertion. | |
340 ClearInsertion(); | |
341 controller().AllowAutomaticInsertionShowing(); | |
342 ChangeInsertion(anchor_rect, visible); | |
343 ASSERT_EQ(INSERTION_SHOWN, GetLastEventType()); | |
344 | |
345 // No tap should be signalled if the touch sequence is cancelled. | |
346 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0); | |
347 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); | |
348 event = MockMotionEvent(MockMotionEvent::ACTION_CANCEL, event_time, 0, 0); | |
349 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); | |
350 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); | |
351 } | |
352 | |
353 TEST_F(TouchSelectionControllerTest, SelectionBasic) { | |
354 gfx::RectF anchor_rect(5, 5, 0, 10); | |
355 gfx::RectF focus_rect(50, 5, 0, 10); | |
356 bool visible = true; | |
357 | |
358 // Selection events are ignored until automatic showing is enabled. | |
359 ChangeSelection(anchor_rect, visible, focus_rect, visible); | |
360 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor()); | |
361 | |
362 controller().AllowAutomaticSelectionShowing(); | |
363 ChangeSelection(anchor_rect, visible, focus_rect, visible); | |
364 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType()); | |
365 EXPECT_EQ(anchor_rect.bottom_left(), GetLastEventAnchor()); | |
366 | |
367 anchor_rect.Offset(1, 0); | |
368 ChangeSelection(anchor_rect, visible, focus_rect, visible); | |
369 // Selection movement does not currently trigger a separate event. | |
370 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType()); | |
371 | |
372 ClearSelection(); | |
373 EXPECT_EQ(SELECTION_CLEARED, GetLastEventType()); | |
374 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor()); | |
375 } | |
376 | |
377 TEST_F(TouchSelectionControllerTest, SelectionDragged) { | |
378 base::TimeTicks event_time = base::TimeTicks::Now(); | |
379 controller().AllowAutomaticSelectionShowing(); | |
380 | |
381 // The touch sequence should not be handled if selection is not active. | |
382 MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 0, 0); | |
383 EXPECT_FALSE(controller().WillHandleTouchEvent(event)); | |
384 | |
385 float line_height = 10.f; | |
386 gfx::RectF anchor_rect(0, 0, 0, line_height); | |
387 gfx::RectF focus_rect(50, 0, 0, line_height); | |
388 bool visible = true; | |
389 ChangeSelection(anchor_rect, visible, focus_rect, visible); | |
390 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType()); | |
391 EXPECT_EQ(anchor_rect.bottom_left(), GetLastEventAnchor()); | |
392 | |
393 // The touch sequence should be handled only if the drawable reports a hit. | |
394 EXPECT_FALSE(controller().WillHandleTouchEvent(event)); | |
395 SetDraggingEnabled(true); | |
396 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); | |
397 EXPECT_FALSE(GetAndResetSelectionMoved()); | |
398 | |
399 // The SelectBetweenCoordinates() result should reflect the movement. Note | |
400 // that the start coordinate will always reflect the "fixed" handle's | |
401 // position, in this case the position from |focus_rect|. | |
402 // Note that the reported position is offset from the center of the | |
403 // input rects (i.e., the middle of the corresponding text line). | |
404 gfx::PointF fixed_offset = focus_rect.CenterPoint(); | |
405 gfx::PointF anchor_offset = anchor_rect.CenterPoint(); | |
406 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 0, 5); | |
407 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); | |
408 EXPECT_TRUE(GetAndResetSelectionMoved()); | |
409 EXPECT_EQ(fixed_offset, GetLastSelectionStart()); | |
410 EXPECT_EQ(anchor_offset + gfx::Vector2dF(0, 5), GetLastSelectionEnd()); | |
411 | |
412 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 5, 5); | |
413 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); | |
414 EXPECT_TRUE(GetAndResetSelectionMoved()); | |
415 EXPECT_EQ(fixed_offset, GetLastSelectionStart()); | |
416 EXPECT_EQ(anchor_offset + gfx::Vector2dF(5, 5), GetLastSelectionEnd()); | |
417 | |
418 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 10, 5); | |
419 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); | |
420 EXPECT_TRUE(GetAndResetSelectionMoved()); | |
421 EXPECT_EQ(fixed_offset, GetLastSelectionStart()); | |
422 EXPECT_EQ(anchor_offset + gfx::Vector2dF(10, 5), GetLastSelectionEnd()); | |
423 | |
424 event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 10, 5); | |
425 EXPECT_TRUE(controller().WillHandleTouchEvent(event)); | |
426 EXPECT_FALSE(GetAndResetSelectionMoved()); | |
427 | |
428 // Once the drag is complete, no more touch events should be consumed until | |
429 // the next ACTION_DOWN. | |
430 EXPECT_FALSE(controller().WillHandleTouchEvent(event)); | |
431 } | |
432 | |
433 TEST_F(TouchSelectionControllerTest, Animation) { | |
434 controller().AllowAutomaticInsertionShowing(); | |
435 controller().OnSelectionEditable(true); | |
436 | |
437 gfx::RectF insertion_rect(5, 5, 0, 10); | |
438 | |
439 // Animation should only happen if visibility changes *after* the insertion or | |
440 // selection is already active. | |
441 bool visible = true; | |
442 ChangeInsertion(insertion_rect, visible); | |
443 EXPECT_FALSE(GetAndResetNeedsAnimate()); | |
444 | |
445 visible = false; | |
446 ChangeInsertion(insertion_rect, visible); | |
447 EXPECT_TRUE(GetAndResetNeedsAnimate()); | |
448 | |
449 visible = true; | |
450 ChangeInsertion(insertion_rect, visible); | |
451 EXPECT_TRUE(GetAndResetNeedsAnimate()); | |
452 | |
453 // If the handles are explicity hidden, no animation should be triggered. | |
454 controller().HideAndDisallowAutomaticShowing(); | |
455 EXPECT_FALSE(GetAndResetNeedsAnimate()); | |
456 } | |
457 | |
458 } // namespace content | |
OLD | NEW |