Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(245)

Side by Side Diff: content/public/android/javatests/src/org/chromium/content/browser/input/SelectionHandleTest.java

Issue 335943002: [Android] Composited selection handle rendering (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@input_native_handles_final
Patch Set: Fix animation tests Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « content/public/android/javatests/src/org/chromium/content/browser/input/InsertionHandleTest.java ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2013 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 package org.chromium.content.browser.input;
6
7 import android.graphics.Point;
8 import android.graphics.Rect;
9 import android.os.SystemClock;
10 import android.test.FlakyTest;
11 import android.test.suitebuilder.annotation.MediumTest;
12 import android.text.Editable;
13 import android.text.Selection;
14 import android.view.MotionEvent;
15 import android.view.ViewGroup;
16
17 import org.chromium.base.ThreadUtils;
18 import org.chromium.base.test.util.Feature;
19 import org.chromium.base.test.util.UrlUtils;
20 import org.chromium.content.browser.RenderCoordinates;
21 import org.chromium.content.browser.test.util.Criteria;
22 import org.chromium.content.browser.test.util.CriteriaHelper;
23 import org.chromium.content.browser.test.util.DOMUtils;
24 import org.chromium.content.browser.test.util.TestInputMethodManagerWrapper;
25 import org.chromium.content.browser.test.util.TestTouchUtils;
26 import org.chromium.content.browser.test.util.TouchCommon;
27 import org.chromium.content_shell_apk.ContentShellTestBase;
28
29 import java.util.concurrent.Callable;
30
31 public class SelectionHandleTest extends ContentShellTestBase {
32 private static final String META_DISABLE_ZOOM =
33 "<meta name=\"viewport\" content=\"" +
34 "height=device-height," +
35 "width=device-width," +
36 "initial-scale=1.0," +
37 "minimum-scale=1.0," +
38 "maximum-scale=1.0," +
39 "\" />";
40
41 // For these we use a tiny font-size so that we can be more strict on the ex pected handle
42 // positions.
43 private static final String TEXTAREA_ID = "textarea";
44 private static final String TEXTAREA_DATA_URL = UrlUtils.encodeHtmlDataUri(
45 "<html><head>" + META_DISABLE_ZOOM + "</head><body>" +
46 "<textarea id=\"" + TEXTAREA_ID +
47 "\" cols=\"40\" rows=\"20\" style=\"font-size:6px\">" +
48 "L r m i s m d l r s t a e , c n e t t r a i i i i g e i , s d d e u m d t m o " +
49 "i c d d n u l b r e d l r m g a l q a U e i a m n m e i m q i n s r d " +
50 "e e c t t o u l m o a o i n s u a i u p x a o m d c n e u t D i a t " +
51 "i u e o o i r p e e d r t n o u t t v l t s e i l m o o e u u i t u l " +
52 "p r a u . x e t u s n o c e a c p d t t o p o d n , u t n u p q i " +
53 "o f c a e e u t o l t n m d s l b r m." +
54 "L r m i s m d l r s t a e , c n e t t r a i i i i g e i , s d d e u m d t m o " +
55 "i c d d n u l b r e d l r m g a l q a U e i a m n m e i m q i n s r d " +
56 "e e c t t o u l m o a o i n s u a i u p x a o m d c n e u t D i a t " +
57 "i u e o o i r p e e d r t n o u t t v l t s e i l m o o e u u i t u l " +
58 "p r a u . x e t u s n o c e a c p d t t o p o d n , u t n u p q i " +
59 "o f c a e e u t o l t n m d s l b r m." +
60 "</textarea>" +
61 "</body></html>");
62
63 private static final String NONEDITABLE_DIV_ID = "noneditable";
64 private static final String NONEDITABLE_DATA_URL = UrlUtils.encodeHtmlDataUr i(
65 "<html><head>" + META_DISABLE_ZOOM + "</head><body>" +
66 "<div id=\"" + NONEDITABLE_DIV_ID + "\" style=\"width:200; font-size :6px\">" +
67 "L r m i s m d l r s t a e , c n e t t r a i i i i g e i , s d d e u m d t m o " +
68 "i c d d n u l b r e d l r m g a l q a U e i a m n m e i m q i n s r d " +
69 "e e c t t o u l m o a o i n s u a i u p x a o m d c n e u t D i a t " +
70 "i u e o o i r p e e d r t n o u t t v l t s e i l m o o e u u i t u l " +
71 "p r a u . x e t u s n o c e a c p d t t o p o d n , u t n u p q i " +
72 "o f c a e e u t o l t n m d s l b r m." +
73 "L r m i s m d l r s t a e , c n e t t r a i i i i g e i , s d d e u m d t m o " +
74 "i c d d n u l b r e d l r m g a l q a U e i a m n m e i m q i n s r d " +
75 "e e c t t o u l m o a o i n s u a i u p x a o m d c n e u t D i a t " +
76 "i u e o o i r p e e d r t n o u t t v l t s e i l m o o e u u i t u l " +
77 "p r a u . x e t u s n o c e a c p d t t o p o d n , u t n u p q i " +
78 "o f c a e e u t o l t n m d s l b r m." +
79 "</div>" +
80 "</body></html>");
81
82 // TODO(cjhopman): These tolerances should be based on the actual width/heig ht of a
83 // character/line.
84 private static final int HANDLE_POSITION_X_TOLERANCE_PIX = 20;
85 private static final int HANDLE_POSITION_Y_TOLERANCE_PIX = 30;
86
87 private enum TestPageType {
88 EDITABLE(TEXTAREA_ID, TEXTAREA_DATA_URL, true),
89 NONEDITABLE(NONEDITABLE_DIV_ID, NONEDITABLE_DATA_URL, false);
90
91 final String nodeId;
92 final String dataUrl;
93 final boolean selectionShouldBeEditable;
94
95 TestPageType(String nodeId, String dataUrl, boolean selectionShouldBeEdi table) {
96 this.nodeId = nodeId;
97 this.dataUrl = dataUrl;
98 this.selectionShouldBeEditable = selectionShouldBeEditable;
99 }
100 }
101
102 private void launchWithUrl(String url) throws Throwable {
103 launchContentShellWithUrl(url);
104 assertTrue("Page failed to load", waitForActiveShellToBeDoneLoading());
105 assertWaitForPageScaleFactorMatch(1.0f);
106
107 // The TestInputMethodManagerWrapper intercepts showSoftInput so that a keyboard is never
108 // brought up.
109 getImeAdapter().setInputMethodManagerWrapper(
110 new TestInputMethodManagerWrapper(getContentViewCore()));
111 }
112
113 private void assertWaitForHasSelectionPosition()
114 throws Throwable {
115 assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
116 @Override
117 public boolean isSatisfied() {
118 int start = getSelectionStart();
119 int end = getSelectionEnd();
120 return start > 0 && start == end;
121 }
122 }));
123 }
124
125 /**
126 * Verifies that when a long-press is performed on static page text,
127 * selection handles appear and that handles can be dragged to extend the
128 * selection. Does not check exact handle position as this will depend on
129 * screen size; instead, position is expected to be correct within
130 * HANDLE_POSITION_TOLERANCE_PIX.
131 *
132 * Test is flaky: crbug.com/290375
133 * @MediumTest
134 * @Feature({ "TextSelection", "Main" })
135 */
136 @FlakyTest
137 public void testNoneditableSelectionHandles() throws Throwable {
138 doSelectionHandleTest(TestPageType.NONEDITABLE);
139 }
140
141 /**
142 * Test is flaky: crbug.com/290375
143 * @MediumTest
144 * @Feature({ "TextSelection", "Main" })
145 */
146 @FlakyTest
147 public void testUpdateContainerViewAndNoneditableSelectionHandles() throws T hrowable {
148 launchWithUrl(TestPageType.NONEDITABLE.dataUrl);
149 replaceContainerView();
150 doSelectionHandleTestUrlLaunched(TestPageType.NONEDITABLE);
151 }
152
153 /**
154 * Verifies that when a long-press is performed on editable text (within a
155 * textarea), selection handles appear and that handles can be dragged to
156 * extend the selection. Does not check exact handle position as this will
157 * depend on screen size; instead, position is expected to be correct within
158 * HANDLE_POSITION_TOLERANCE_PIX.
159 */
160 @MediumTest
161 @Feature({ "TextSelection" })
162 public void testEditableSelectionHandles() throws Throwable {
163 doSelectionHandleTest(TestPageType.EDITABLE);
164 }
165
166 @MediumTest
167 @Feature({ "TextSelection" })
168 public void testUpdateContainerViewAndEditableSelectionHandles() throws Thro wable {
169 launchWithUrl(TestPageType.EDITABLE.dataUrl);
170 replaceContainerView();
171 doSelectionHandleTestUrlLaunched(TestPageType.EDITABLE);
172 }
173
174 private void doSelectionHandleTest(TestPageType pageType) throws Throwable {
175 launchWithUrl(pageType.dataUrl);
176 doSelectionHandleTestUrlLaunched(pageType);
177 }
178
179 private void doSelectionHandleTestUrlLaunched(TestPageType pageType) throws Throwable {
180 clickNodeToShowSelectionHandles(pageType.nodeId);
181 assertWaitForSelectionEditableEquals(pageType.selectionShouldBeEditable) ;
182
183 HandleView startHandle = getStartHandle();
184 HandleView endHandle = getEndHandle();
185
186 Rect nodeWindowBounds = getNodeBoundsPix(pageType.nodeId);
187
188 int leftX = (nodeWindowBounds.left + nodeWindowBounds.centerX()) / 2;
189 int centerX = nodeWindowBounds.centerX();
190 int rightX = (nodeWindowBounds.right + nodeWindowBounds.centerX()) / 2;
191
192 int topY = (nodeWindowBounds.top + nodeWindowBounds.centerY()) / 2;
193 int centerY = nodeWindowBounds.centerY();
194 int bottomY = (nodeWindowBounds.bottom + nodeWindowBounds.centerY()) / 2 ;
195
196 // Drag start handle up and to the left. The selection start should decr ease.
197 dragHandleAndCheckSelectionChange(startHandle, leftX, topY, -1, 0);
198 // Drag end handle down and to the right. The selection end should incre ase.
199 dragHandleAndCheckSelectionChange(endHandle, rightX, bottomY, 0, 1);
200 // Drag start handle back to the middle. The selection start should incr ease.
201 dragHandleAndCheckSelectionChange(startHandle, centerX, centerY, 1, 0);
202 // Drag end handle up and to the left past the start handle. Both select ion start and end
203 // should decrease.
204 dragHandleAndCheckSelectionChange(endHandle, leftX, topY, -1, -1);
205 // Drag start handle down and to the right past the end handle. Both sel ection start and end
206 // should increase.
207 dragHandleAndCheckSelectionChange(startHandle, rightX, bottomY, 1, 1);
208
209 clickToDismissHandles();
210 }
211
212 private void dragHandleAndCheckSelectionChange(HandleView handle, int dragTo X, int dragToY,
213 final int expectedStartChange, final int expectedEndChange) throws T hrowable {
214 String initialText = getContentViewCore().getSelectedText();
215 final int initialSelectionEnd = getSelectionEnd();
216 final int initialSelectionStart = getSelectionStart();
217
218 dragHandleTo(handle, dragToX, dragToY, 10);
219 assertWaitForEitherHandleNear(dragToX, dragToY);
220
221 if (getContentViewCore().isSelectionEditable()) {
222 assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
223 @Override
224 public boolean isSatisfied() {
225 int startChange = getSelectionStart() - initialSelectionStar t;
226 // TODO(cjhopman): Due to http://crbug.com/244633 we can't r eally assert that
227 // there is no change when we expect to be able to.
228 if (expectedStartChange != 0) {
229 if ((int) Math.signum(startChange) != expectedStartChang e) return false;
230 }
231
232 int endChange = getSelectionEnd() - initialSelectionEnd;
233 if (expectedEndChange != 0) {
234 if ((int) Math.signum(endChange) != expectedEndChange) r eturn false;
235 }
236
237 return true;
238 }
239 }));
240 }
241
242 assertWaitForHandleViewStopped(getStartHandle());
243 assertWaitForHandleViewStopped(getEndHandle());
244 }
245
246 private void assertWaitForSelectionEditableEquals(final boolean expected) th rows Throwable {
247 assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
248 @Override
249 public boolean isSatisfied() {
250 return getContentViewCore().isSelectionEditable() == expected;
251 }
252 }));
253 }
254
255 private void assertWaitForHandleViewStopped(final HandleView handle) throws Throwable {
256 assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
257 private Point position = new Point(-1, -1);
258 @Override
259 public boolean isSatisfied() {
260 Point lastPosition = position;
261 position = getHandlePosition(handle);
262 return !handle.isDragging() &&
263 position.equals(lastPosition);
264 }
265 }));
266 }
267
268 /**
269 * Verifies that when a selection is made within static page text, that the
270 * contextual action bar of the correct type is displayed. Also verified
271 * that the bar disappears upon deselection.
272 */
273 @MediumTest
274 @Feature({ "TextSelection" })
275 public void testNoneditableSelectionActionBar() throws Throwable {
276 doSelectionActionBarTest(TestPageType.NONEDITABLE);
277 }
278
279 /**
280 * Verifies that when a selection is made within editable text, that the
281 * contextual action bar of the correct type is displayed. Also verified
282 * that the bar disappears upon deselection.
283 */
284 @MediumTest
285 @Feature({ "TextSelection" })
286 public void testEditableSelectionActionBar() throws Throwable {
287 doSelectionActionBarTest(TestPageType.EDITABLE);
288 }
289
290 private void doSelectionActionBarTest(TestPageType pageType) throws Throwabl e {
291 launchWithUrl(pageType.dataUrl);
292 assertFalse(getContentViewCore().isSelectActionBarShowing());
293 clickNodeToShowSelectionHandles(pageType.nodeId);
294 assertWaitForSelectActionBarShowingEquals(true);
295 clickToDismissHandles();
296 assertWaitForSelectActionBarShowingEquals(false);
297 }
298
299 private static Point getHandlePosition(final HandleView handle) {
300 return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Point>( ) {
301 @Override
302 public Point call() {
303 return new Point(handle.getAdjustedPositionX(), handle.getAdjust edPositionY());
304 }
305 });
306 }
307
308 private static boolean isHandleNear(HandleView handle, int x, int y) {
309 Point position = getHandlePosition(handle);
310 return (Math.abs(position.x - x) < HANDLE_POSITION_X_TOLERANCE_PIX) &&
311 (Math.abs(position.y - y) < HANDLE_POSITION_Y_TOLERANCE_PIX);
312 }
313
314 private void assertWaitForHandleNear(final HandleView handle, final int x, f inal int y)
315 throws Throwable {
316 assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
317 @Override
318 public boolean isSatisfied() {
319 return isHandleNear(handle, x, y);
320 }
321 }));
322 }
323
324 private void assertWaitForEitherHandleNear(final int x, final int y) throws Throwable {
325 final HandleView startHandle = getStartHandle();
326 final HandleView endHandle = getEndHandle();
327 assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
328 @Override
329 public boolean isSatisfied() {
330 return isHandleNear(startHandle, x, y) || isHandleNear(endHandle , x, y);
331 }
332 }));
333 }
334
335 private void assertWaitForHandlesShowingEquals(final boolean shouldBeShowing ) throws Throwable {
336 assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
337 @Override
338 public boolean isSatisfied() {
339 SelectionHandleController shc =
340 getContentViewCore().getSelectionHandleControllerForTest ();
341 boolean isShowing = shc != null && shc.isShowing();
342 return shouldBeShowing == isShowing;
343 }
344 }));
345 }
346
347
348 private void dragHandleTo(final HandleView handle, final int dragToX, final int dragToY,
349 final int steps) throws Throwable {
350 assertTrue(ThreadUtils.runOnUiThreadBlocking(new Callable<Boolean>() {
351 @Override
352 public Boolean call() {
353 int adjustedX = handle.getAdjustedPositionX();
354 int adjustedY = handle.getAdjustedPositionY();
355 int realX = handle.getPositionX();
356 int realY = handle.getPositionY();
357
358 int realDragToX = dragToX + (realX - adjustedX);
359 int realDragToY = dragToY + (realY - adjustedY);
360
361 ViewGroup view = getContentViewCore().getContainerView();
362 int[] fromLocation = TestTouchUtils.getAbsoluteLocationFromRelat ive(
363 view, realX, realY);
364 int[] toLocation = TestTouchUtils.getAbsoluteLocationFromRelativ e(
365 view, realDragToX, realDragToY);
366
367 long downTime = SystemClock.uptimeMillis();
368 MotionEvent event = MotionEvent.obtain(downTime, downTime, Motio nEvent.ACTION_DOWN,
369 fromLocation[0], fromLocation[1], 0);
370 handle.dispatchTouchEvent(event);
371
372 if (!handle.isDragging()) return false;
373
374 for (int i = 0; i < steps; i++) {
375 float scale = (float) (i + 1) / steps;
376 int x = fromLocation[0] + (int) (scale * (toLocation[0] - fr omLocation[0]));
377 int y = fromLocation[1] + (int) (scale * (toLocation[1] - fr omLocation[1]));
378 long eventTime = SystemClock.uptimeMillis();
379 event = MotionEvent.obtain(downTime, eventTime, MotionEvent. ACTION_MOVE,
380 x, y, 0);
381 handle.dispatchTouchEvent(event);
382 }
383 long upTime = SystemClock.uptimeMillis();
384 event = MotionEvent.obtain(downTime, upTime, MotionEvent.ACTION_ UP,
385 toLocation[0], toLocation[1], 0);
386 handle.dispatchTouchEvent(event);
387
388 return !handle.isDragging();
389 }
390 }));
391 }
392
393 private Rect getNodeBoundsPix(String nodeId) throws Throwable {
394 Rect nodeBounds = DOMUtils.getNodeBounds(getContentViewCore(), nodeId);
395
396 RenderCoordinates renderCoordinates = getContentViewCore().getRenderCoor dinates();
397 int offsetX = getContentViewCore().getViewportSizeOffsetWidthPix();
398 int offsetY = getContentViewCore().getViewportSizeOffsetHeightPix();
399
400 int left = (int) renderCoordinates.fromLocalCssToPix(nodeBounds.left) + offsetX;
401 int right = (int) renderCoordinates.fromLocalCssToPix(nodeBounds.right) + offsetX;
402 int top = (int) renderCoordinates.fromLocalCssToPix(nodeBounds.top) + of fsetY;
403 int bottom = (int) renderCoordinates.fromLocalCssToPix(nodeBounds.bottom ) + offsetY;
404
405 return new Rect(left, top, right, bottom);
406 }
407
408 private void clickNodeToShowSelectionHandles(String nodeId) throws Throwable {
409 Rect nodeWindowBounds = getNodeBoundsPix(nodeId);
410
411 TouchCommon touchCommon = new TouchCommon(this);
412 int centerX = nodeWindowBounds.centerX();
413 int centerY = nodeWindowBounds.centerY();
414 touchCommon.longPressView(getContentViewCore().getContainerView(), cente rX, centerY);
415
416 assertWaitForHandlesShowingEquals(true);
417 assertWaitForHandleViewStopped(getStartHandle());
418
419 // No words wrap in the sample text so handles should be at the same y
420 // position.
421 assertEquals(getStartHandle().getPositionY(), getEndHandle().getPosition Y());
422 }
423
424 private void clickToDismissHandles() throws Throwable {
425 TestTouchUtils.sleepForDoubleTapTimeout(getInstrumentation());
426 new TouchCommon(this).singleClickView(getContentViewCore().getContainerV iew(), 0, 0);
427 assertWaitForHandlesShowingEquals(false);
428 }
429
430 private void assertWaitForSelectActionBarShowingEquals(final boolean shouldB eShowing)
431 throws InterruptedException {
432 assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
433 @Override
434 public boolean isSatisfied() {
435 return shouldBeShowing == getContentViewCore().isSelectActionBar Showing();
436 }
437 }));
438 }
439
440 public void assertWaitForHasInputConnection() {
441 try {
442 assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
443 @Override
444 public boolean isSatisfied() {
445 return getContentViewCore().getInputConnectionForTest() != n ull;
446 }
447 }));
448 } catch (InterruptedException e) {
449 fail();
450 }
451 }
452
453 private ImeAdapter getImeAdapter() {
454 return getContentViewCore().getImeAdapterForTest();
455 }
456
457 private int getSelectionStart() {
458 return Selection.getSelectionStart(getEditable());
459 }
460
461 private int getSelectionEnd() {
462 return Selection.getSelectionEnd(getEditable());
463 }
464
465 private Editable getEditable() {
466 // We have to wait for the input connection (with the IME) to be created before accessing
467 // the ContentViewCore's editable.
468 assertWaitForHasInputConnection();
469 return getContentViewCore().getEditableForTest();
470 }
471
472 private HandleView getStartHandle() {
473 SelectionHandleController shc = getContentViewCore().getSelectionHandleC ontrollerForTest();
474 return shc.getStartHandleViewForTest();
475 }
476
477 private HandleView getEndHandle() {
478 SelectionHandleController shc = getContentViewCore().getSelectionHandleC ontrollerForTest();
479 return shc.getEndHandleViewForTest();
480 }
481 }
OLDNEW
« no previous file with comments | « content/public/android/javatests/src/org/chromium/content/browser/input/InsertionHandleTest.java ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698