OLD | NEW |
| (Empty) |
1 // Copyright 2012 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.content.ClipData; | |
8 import android.content.ClipboardManager; | |
9 import android.content.Context; | |
10 import android.graphics.Rect; | |
11 import android.test.suitebuilder.annotation.MediumTest; | |
12 import android.text.Editable; | |
13 import android.text.Selection; | |
14 import android.view.KeyEvent; | |
15 import android.view.View; | |
16 | |
17 import org.chromium.base.test.util.DisabledTest; | |
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 /** | |
30 * Tests the insertion handle that allows users to paste text. | |
31 */ | |
32 public class InsertionHandleTest extends ContentShellTestBase { | |
33 private static final String META_DISABLE_ZOOM = | |
34 "<meta name=\"viewport\" content=\"" + | |
35 "height=device-height," + | |
36 "width=device-width," + | |
37 "initial-scale=1.0," + | |
38 "minimum-scale=1.0," + | |
39 "maximum-scale=1.0," + | |
40 "\" />"; | |
41 | |
42 private static final String TEXTAREA_ID = "textarea"; | |
43 private static final String TEXTAREA_DATA_URL = UrlUtils.encodeHtmlDataUri( | |
44 "<html><head>" + META_DISABLE_ZOOM + "</head><body>" + | |
45 "<textarea id=\"" + TEXTAREA_ID + "\" cols=\"20\" rows=\"10\">" + | |
46 "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do ei
usmod tempor " + | |
47 "incididunt ut labore et dolore magna aliqua. Ut enim ad minim venia
m, quis nostrud " + | |
48 "exercitation ullamco laboris nisi ut aliquip ex ea commodo consequa
t. Duis aute " + | |
49 "irure dolor in reprehenderit in voluptate velit esse cillum dolore
eu fugiat nulla " + | |
50 "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in c
ulpa qui " + | |
51 "officia deserunt mollit anim id est laborum." + | |
52 "</textarea>" + | |
53 "</body></html>"); | |
54 | |
55 private static final String INPUT_TEXT_ID = "input_text"; | |
56 private static final String INPUT_TEXT_DATA_URL = UrlUtils.encodeHtmlDataUri
( | |
57 "<html><head>" + META_DISABLE_ZOOM + "</head><body>" + | |
58 "<input id=\"input_text\" type=\"text\" value=\"" + | |
59 "T0D0(cjhopman): put amusing sample text here. Make sure it is at le
ast " + | |
60 "100 characters. 123456789012345678901234567890\" size=20></input>"
+ | |
61 "</body></html>"); | |
62 | |
63 // Offset to compensate for the fact that the handle is below the text. | |
64 private static final int VERTICAL_OFFSET = 10; | |
65 private static final int HANDLE_POSITION_TOLERANCE = 20; | |
66 private static final String PASTE_TEXT = "**test text to paste**"; | |
67 | |
68 | |
69 public void launchWithUrl(String url) throws Throwable { | |
70 launchContentShellWithUrl(url); | |
71 assertTrue("Page failed to load", waitForActiveShellToBeDoneLoading()); | |
72 assertWaitForPageScaleFactorMatch(1.0f); | |
73 | |
74 // The TestInputMethodManagerWrapper intercepts showSoftInput so that a
keyboard is never | |
75 // brought up. | |
76 getContentViewCore().getImeAdapterForTest().setInputMethodManagerWrapper
( | |
77 new TestInputMethodManagerWrapper(getContentViewCore())); | |
78 } | |
79 | |
80 @MediumTest | |
81 @Feature({"TextSelection", "TextInput", "Main"}) | |
82 public void testUnselectHidesHandle() throws Throwable { | |
83 launchWithUrl(TEXTAREA_DATA_URL); | |
84 clickNodeToShowInsertionHandle(TEXTAREA_ID); | |
85 | |
86 // Unselecting should cause the handle to disappear. | |
87 unselectOnMainSync(); | |
88 assertTrue(waitForHandleShowingEquals(false)); | |
89 } | |
90 | |
91 @MediumTest | |
92 @Feature({"TextSelection", "TextInput", "Main"}) | |
93 public void testUpdateContainerViewAndUnselectHidesHandle() throws Throwable
{ | |
94 launchWithUrl(TEXTAREA_DATA_URL); | |
95 replaceContainerView(); | |
96 | |
97 clickNodeToShowInsertionHandle(TEXTAREA_ID); | |
98 | |
99 // Unselecting should cause the handle to disappear. | |
100 unselectOnMainSync(); | |
101 assertTrue(waitForHandleShowingEquals(false)); | |
102 } | |
103 | |
104 /** | |
105 * @MediumTest | |
106 * @Feature({"TextSelection", "TextInput", "Main"}) | |
107 * http://crbug.com/169648 | |
108 */ | |
109 @DisabledTest | |
110 public void testKeyEventHidesHandle() throws Throwable { | |
111 launchWithUrl(TEXTAREA_DATA_URL); | |
112 clickNodeToShowInsertionHandle(TEXTAREA_ID); | |
113 | |
114 getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN, KeyE
vent.KEYCODE_X)); | |
115 getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_UP, KeyEve
nt.KEYCODE_X)); | |
116 assertTrue(waitForHandleShowingEquals(false)); | |
117 } | |
118 | |
119 /** | |
120 * @MediumTest | |
121 * @Feature({"TextSelection", "TextInput", "Main"}) | |
122 * http://crbug.com/169648 | |
123 */ | |
124 @DisabledTest | |
125 public void testDragInsertionHandle() throws Throwable { | |
126 launchWithUrl(TEXTAREA_DATA_URL); | |
127 | |
128 clickNodeToShowInsertionHandle(TEXTAREA_ID); | |
129 | |
130 InsertionHandleController ihc = getContentViewCore().getInsertionHandleC
ontrollerForTest(); | |
131 HandleView handle = ihc.getHandleViewForTest(); | |
132 | |
133 int initialX = handle.getPositionX(); | |
134 int initialY = handle.getPositionY(); | |
135 int dragToX = initialX + 120; | |
136 int dragToY = initialY + 120; | |
137 | |
138 dragHandleTo(dragToX, dragToY); | |
139 assertWaitForHandleNear(dragToX, dragToY); | |
140 } | |
141 | |
142 | |
143 @MediumTest | |
144 @Feature({"TextSelection", "TextInput", "Main"}) | |
145 public void testPasteAtInsertionHandle() throws Throwable { | |
146 launchWithUrl(TEXTAREA_DATA_URL); | |
147 | |
148 clickNodeToShowInsertionHandle(TEXTAREA_ID); | |
149 | |
150 int offset = getSelectionStart(); | |
151 String initialText = getEditableText(); | |
152 | |
153 saveToClipboard(PASTE_TEXT); | |
154 pasteOnMainSync(); | |
155 | |
156 String expectedText = | |
157 initialText.substring(0, offset) + PASTE_TEXT + initialText.subst
ring(offset); | |
158 assertTrue(waitForEditableTextEquals(expectedText)); | |
159 assertTrue(waitForHandleShowingEquals(false)); | |
160 } | |
161 | |
162 /** | |
163 * @MediumTest | |
164 * @Feature({"TextSelection", "TextInput", "Main"}) | |
165 * http://crbug.com/169648 | |
166 */ | |
167 @DisabledTest | |
168 public void testDragInsertionHandleInputText() throws Throwable { | |
169 launchWithUrl(INPUT_TEXT_DATA_URL); | |
170 | |
171 clickNodeToShowInsertionHandle(INPUT_TEXT_ID); | |
172 | |
173 InsertionHandleController ihc = getContentViewCore().getInsertionHandleC
ontrollerForTest(); | |
174 HandleView handle = ihc.getHandleViewForTest(); | |
175 | |
176 int initialX = handle.getPositionX(); | |
177 int initialY = handle.getPositionY(); | |
178 int dragToX = initialX + 120; | |
179 int dragToY = initialY; | |
180 dragHandleTo(dragToX, dragToY); | |
181 assertWaitForHandleNear(dragToX, initialY); | |
182 | |
183 TestTouchUtils.sleepForDoubleTapTimeout(getInstrumentation()); | |
184 | |
185 initialX = handle.getPositionX(); | |
186 initialY = handle.getPositionY(); | |
187 dragToY = initialY + 120; | |
188 dragHandleTo(initialX, dragToY); | |
189 // Vertical drag should not change the y-position. | |
190 assertWaitForHandleNear(initialX, initialY); | |
191 } | |
192 | |
193 /** | |
194 * @MediumTest | |
195 * @Feature({"TextSelection", "TextInput", "Main"}) | |
196 * http://crbug.com/169648 | |
197 */ | |
198 @DisabledTest | |
199 public void testDragInsertionHandleInputTextOutsideBounds() throws Throwable
{ | |
200 launchWithUrl(INPUT_TEXT_DATA_URL); | |
201 | |
202 clickNodeToShowInsertionHandle(INPUT_TEXT_ID); | |
203 | |
204 InsertionHandleController ihc = getContentViewCore().getInsertionHandleC
ontrollerForTest(); | |
205 HandleView handle = ihc.getHandleViewForTest(); | |
206 | |
207 int initialX = handle.getPositionX(); | |
208 int initialY = handle.getPositionY(); | |
209 int dragToX = initialX; | |
210 int dragToY = initialY + 150; | |
211 | |
212 // A vertical drag should not move the insertion handle. | |
213 dragHandleTo(dragToX, dragToY); | |
214 assertWaitForHandleNear(initialX, initialY); | |
215 | |
216 // The input box does not go to the edge of the screen, and neither shou
ld the insertion | |
217 // handle. | |
218 dragToX = getContentViewCore().getContainerView().getWidth(); | |
219 dragHandleTo(dragToX, dragToY); | |
220 assertTrue(handle.getPositionX() < dragToX - 100); | |
221 } | |
222 | |
223 @Override | |
224 protected void tearDown() throws Exception { | |
225 super.tearDown(); | |
226 // No way to just clear clipboard, so setting to empty string instead. | |
227 saveToClipboard(""); | |
228 } | |
229 | |
230 private void clickNodeToShowInsertionHandle(String nodeId) throws Throwable
{ | |
231 // On the first click the keyboard will be displayed but no insertion ha
ndles. On the second | |
232 // click (only if it changes the selection), the insertion handle is dis
played. So that the | |
233 // second click changes the selection, the two clicks should be in suffi
ciently different | |
234 // locations. | |
235 Rect nodeBounds = DOMUtils.getNodeBounds(getContentViewCore(), nodeId); | |
236 | |
237 RenderCoordinates renderCoordinates = getContentViewCore().getRenderCoor
dinates(); | |
238 int offsetX = getContentViewCore().getViewportSizeOffsetWidthPix(); | |
239 int offsetY = getContentViewCore().getViewportSizeOffsetHeightPix(); | |
240 float left = renderCoordinates.fromLocalCssToPix(nodeBounds.left) + offs
etX; | |
241 float right = renderCoordinates.fromLocalCssToPix(nodeBounds.right) + of
fsetX; | |
242 float top = renderCoordinates.fromLocalCssToPix(nodeBounds.top) + offset
Y; | |
243 float bottom = renderCoordinates.fromLocalCssToPix(nodeBounds.bottom) +
offsetY; | |
244 | |
245 TouchCommon touchCommon = new TouchCommon(this); | |
246 touchCommon.singleClickView(getContentViewCore().getContainerView(), | |
247 (int)(left + 3 * (right - left) / 4), (int)(top + (bottom - top)
/ 2)); | |
248 | |
249 | |
250 TestTouchUtils.sleepForDoubleTapTimeout(getInstrumentation()); | |
251 assertTrue(waitForHasSelectionPosition()); | |
252 | |
253 // TODO(cjhopman): Wait for keyboard display finished? | |
254 touchCommon.singleClickView(getContentViewCore().getContainerView(), | |
255 (int)(left + (right - left) / 4), (int)(top + (bottom - top) / 2
)); | |
256 assertTrue(waitForHandleShowingEquals(true)); | |
257 assertTrue(waitForHandleViewStopped()); | |
258 } | |
259 | |
260 private boolean waitForHandleViewStopped() throws Throwable { | |
261 // If the polling interval is too short, slowly moving may be detected a
s not moving. | |
262 final int POLLING_INTERVAL = 200; | |
263 return CriteriaHelper.pollForCriteria(new Criteria() { | |
264 int mPositionX = -1; | |
265 int mPositionY = -1; | |
266 @Override | |
267 public boolean isSatisfied() { | |
268 int lastPositionX = mPositionX; | |
269 int lastPositionY = mPositionY; | |
270 InsertionHandleController ihc = | |
271 getContentViewCore().getInsertionHandleControllerForTest
(); | |
272 HandleView handle = ihc.getHandleViewForTest(); | |
273 mPositionX = handle.getPositionX(); | |
274 mPositionY = handle.getPositionY(); | |
275 return !handle.isDragging() && | |
276 mPositionX == lastPositionX && mPositionY == lastPositio
nY; | |
277 } | |
278 }, CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL, POLLING_INTERVAL); | |
279 } | |
280 | |
281 private boolean waitForEditableTextEquals(final String expectedText) | |
282 throws Throwable { | |
283 return CriteriaHelper.pollForCriteria(new Criteria() { | |
284 @Override | |
285 public boolean isSatisfied() { | |
286 return getEditableText().trim().equals(expectedText.trim()); | |
287 } | |
288 }); | |
289 } | |
290 | |
291 private boolean waitForHasSelectionPosition() | |
292 throws Throwable { | |
293 return CriteriaHelper.pollForCriteria(new Criteria() { | |
294 @Override | |
295 public boolean isSatisfied() { | |
296 int start = getSelectionStart(); | |
297 int end = getSelectionEnd(); | |
298 return start > 0 && start == end; | |
299 } | |
300 }); | |
301 } | |
302 | |
303 private void dragHandleTo(int dragToX, int dragToY, int steps) throws Throwa
ble { | |
304 InsertionHandleController ihc = getContentViewCore().getInsertionHandleC
ontrollerForTest(); | |
305 HandleView handle = ihc.getHandleViewForTest(); | |
306 int initialX = handle.getPositionX(); | |
307 int initialY = handle.getPositionY(); | |
308 View view = getContentViewCore().getContainerView(); | |
309 | |
310 int fromLocation[] = TestTouchUtils.getAbsoluteLocationFromRelative(view
, initialX, | |
311 initialY + VERTICAL_OFFSET); | |
312 int toLocation[] = TestTouchUtils.getAbsoluteLocationFromRelative(view,
dragToX, | |
313 dragToY + VERTICAL_OFFSET); | |
314 | |
315 long downTime = TestTouchUtils.dragStart(getInstrumentation(), fromLocat
ion[0], | |
316 fromLocation[1]); | |
317 assertWaitForHandleDraggingEquals(true); | |
318 TestTouchUtils.dragTo(getInstrumentation(), fromLocation[0], toLocation[
0], | |
319 fromLocation[1], toLocation[1], steps, downTime); | |
320 TestTouchUtils.dragEnd(getInstrumentation(), toLocation[0], toLocation[1
], downTime); | |
321 assertWaitForHandleDraggingEquals(false); | |
322 assertTrue(waitForHandleViewStopped()); | |
323 } | |
324 | |
325 private void dragHandleTo(int dragToX, int dragToY) throws Throwable { | |
326 dragHandleTo(dragToX, dragToY, 5); | |
327 } | |
328 | |
329 private void assertWaitForHandleDraggingEquals(final boolean expected) throw
s Throwable { | |
330 InsertionHandleController ihc = getContentViewCore().getInsertionHandleC
ontrollerForTest(); | |
331 final HandleView handle = ihc.getHandleViewForTest(); | |
332 assertTrue(CriteriaHelper.pollForCriteria(new Criteria() { | |
333 @Override | |
334 public boolean isSatisfied() { | |
335 return handle.isDragging() == expected; | |
336 } | |
337 })); | |
338 } | |
339 | |
340 private static boolean isHandleNear(HandleView handle, int x, int y) { | |
341 return (Math.abs(handle.getPositionX() - x) < HANDLE_POSITION_TOLERANCE)
&& | |
342 (Math.abs(handle.getPositionY() - VERTICAL_OFFSET - y) < HANDLE_
POSITION_TOLERANCE); | |
343 } | |
344 | |
345 private void assertWaitForHandleNear(final int x, final int y) throws Throwa
ble { | |
346 InsertionHandleController ihc = getContentViewCore().getInsertionHandleC
ontrollerForTest(); | |
347 final HandleView handle = ihc.getHandleViewForTest(); | |
348 assertTrue(CriteriaHelper.pollForCriteria(new Criteria() { | |
349 @Override | |
350 public boolean isSatisfied() { | |
351 return isHandleNear(handle, x, y); | |
352 } | |
353 })); | |
354 } | |
355 | |
356 private boolean waitForHandleShowingEquals(final boolean shouldBeShowing) | |
357 throws Throwable { | |
358 return CriteriaHelper.pollForCriteria(new Criteria() { | |
359 @Override | |
360 public boolean isSatisfied() { | |
361 InsertionHandleController ihc = | |
362 getContentViewCore().getInsertionHandleControllerForTest
(); | |
363 boolean isShowing = ihc != null && ihc.isShowing(); | |
364 return isShowing == shouldBeShowing; | |
365 } | |
366 }); | |
367 } | |
368 | |
369 private void pasteOnMainSync() { | |
370 getInstrumentation().runOnMainSync(new Runnable() { | |
371 @Override | |
372 public void run() { | |
373 getContentViewCore().getInsertionHandleControllerForTest().paste
(); | |
374 } | |
375 }); | |
376 } | |
377 | |
378 private void unselectOnMainSync() { | |
379 getInstrumentation().runOnMainSync(new Runnable() { | |
380 @Override | |
381 public void run() { | |
382 getContentViewCore().getImeAdapterForTest().unselect(); | |
383 } | |
384 }); | |
385 } | |
386 | |
387 private int getSelectionStart() { | |
388 return Selection.getSelectionStart(getEditable()); | |
389 } | |
390 | |
391 private int getSelectionEnd() { | |
392 return Selection.getSelectionEnd(getEditable()); | |
393 } | |
394 | |
395 private Editable getEditable() { | |
396 return getContentViewCore().getEditableForTest(); | |
397 } | |
398 | |
399 private String getEditableText() { | |
400 return getEditable().toString(); | |
401 } | |
402 | |
403 private void saveToClipboard(String text) { | |
404 ClipboardManager clipMgr = | |
405 (ClipboardManager) getActivity().getSystemService(Context.CLIPBO
ARD_SERVICE); | |
406 clipMgr.setPrimaryClip(ClipData.newPlainText(null, text)); | |
407 } | |
408 } | |
OLD | NEW |