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.
5 package org.chromium.content.browser.test.util;
7 import android.graphics.Rect;
8 import android.test.ActivityInstrumentationTestCase2;
9 import android.util.JsonReader;
11 import junit.framework.Assert;
13 import org.chromium.content.browser.ContentViewCore;
14 import org.chromium.content_public.browser.WebContents;
16 import java.io.IOException;
17 import java.io.StringReader;
18 import java.util.concurrent.TimeoutException;
21 * Collection of DOM-based utilities.
23 public class DOMUtils {
25 * Pauses the video with given {@code nodeId}.
27 public static void pauseVideo(final WebContents webContents, final String nodeId)
28 throws InterruptedException, TimeoutException {
29 StringBuilder sb = new StringBuilder();
30 sb.append("(function() {");
31 sb.append(" var video = document.getElementById('" + nodeId + "');");
32 sb.append(" if (video) video.pause();");
34 JavaScriptUtils.executeJavaScriptAndWaitForResult(
35 webContents, sb.toString());
39 * Returns whether the video with given {@code nodeId} is paused.
41 public static boolean isVideoPaused(final WebContents webContents, final String nodeId)
42 throws InterruptedException, TimeoutException {
43 return getNodeField("paused", webContents, nodeId, Boolean.class);
47 * Waits until the playback of the video with given {@code nodeId} has started.
49 * @return Whether the playback has started.
51 public static boolean waitForVideoPlay(final WebContents webContents, final String nodeId)
52 throws InterruptedException {
53 return CriteriaHelper.pollForCriteria(new Criteria() {
55 public boolean isSatisfied() {
57 return !DOMUtils.isVideoPaused(webContents, nodeId);
58 } catch (InterruptedException e) {
59 // Intentionally do nothing
61 } catch (TimeoutException e) {
62 // Intentionally do nothing
70 * Returns whether the document is in fullscreen.
72 public static boolean isFullscreen(final WebContents webContents)
73 throws InterruptedException, TimeoutException {
74 StringBuilder sb = new StringBuilder();
75 sb.append("(function() {");
76 sb.append(" return [document.webkitIsFullScreen];");
79 String jsonText = JavaScriptUtils.executeJavaScriptAndWaitForResult(
80 webContents, sb.toString());
81 return readValue(jsonText, Boolean.class);
85 * Makes the document exit fullscreen.
87 public static void exitFullscreen(final WebContents webContents) {
88 StringBuilder sb = new StringBuilder();
89 sb.append("(function() {");
90 sb.append(" if (document.webkitExitFullscreen) document.webkitExitFullscreen();");
93 JavaScriptUtils.executeJavaScript(webContents, sb.toString());
97 * Returns the rect boundaries for a node by its id.
99 public static Rect getNodeBounds(final WebContents webContents, String nodeId)
100 throws InterruptedException, TimeoutException {
101 StringBuilder sb = new StringBuilder();
102 sb.append("(function() {");
103 sb.append(" var node = document.getElementById('" + nodeId + "');");
104 sb.append(" if (!node) return null;");
105 sb.append(" var width = Math.round(node.offsetWidth);");
106 sb.append(" var height = Math.round(node.offsetHeight);");
107 sb.append(" var x = -window.scrollX;");
108 sb.append(" var y = -window.scrollY;");
110 sb.append(" x += node.offsetLeft;");
111 sb.append(" y += node.offsetTop;");
112 sb.append(" } while (node = node.offsetParent);");
113 sb.append(" return [ Math.round(x), Math.round(y), width, height ];");
116 String jsonText = JavaScriptUtils.executeJavaScriptAndWaitForResult(
117 webContents, sb.toString());
119 Assert.assertFalse("Failed to retrieve bounds for " + nodeId,
120 jsonText.trim().equalsIgnoreCase("null"));
122 JsonReader jsonReader = new JsonReader(new StringReader(jsonText));
123 int[] bounds = new int[4];
125 jsonReader.beginArray();
127 while (jsonReader.hasNext()) {
128 bounds[i++] = jsonReader.nextInt();
130 jsonReader.endArray();
131 Assert.assertEquals("Invalid bounds returned.", 4, i);
134 } catch (IOException exception) {
135 Assert.fail("Failed to evaluate JavaScript: " + jsonText + "\n" + exception);
138 return new Rect(bounds[0], bounds[1], bounds[0] + bounds[2], bounds[1] + bounds[3]);
142 * Focus a DOM node by its id.
144 public static void focusNode(final WebContents webContents, String nodeId)
145 throws InterruptedException, TimeoutException {
146 StringBuilder sb = new StringBuilder();
147 sb.append("(function() {");
148 sb.append(" var node = document.getElementById('" + nodeId + "');");
149 sb.append(" if (node) node.focus();");
152 JavaScriptUtils.executeJavaScriptAndWaitForResult(webContents, sb.toString());
156 * Click a DOM node by its id.
158 public static void clickNode(ActivityInstrumentationTestCase2 activityTestCase,
159 final ContentViewCore viewCore, String nodeId)
160 throws InterruptedException, TimeoutException {
161 int[] clickTarget = getClickTargetForNode(viewCore, nodeId);
162 TouchCommon.singleClickView(viewCore.getContainerView(), clickTarget[0], clickTarget[1]);
166 * Long-press a DOM node by its id.
168 public static void longPressNode(ActivityInstrumentationTestCase2 activityTestCase,
169 final ContentViewCore viewCore, String nodeId)
170 throws InterruptedException, TimeoutException {
171 int[] clickTarget = getClickTargetForNode(viewCore, nodeId);
172 TouchCommon.longPressView(viewCore.getContainerView(), clickTarget[0], clickTarget[1]);
176 * Scrolls the view to ensure that the required DOM node is visible.
178 public static void scrollNodeIntoView(WebContents webContents, String nodeId)
179 throws InterruptedException, TimeoutException {
180 JavaScriptUtils.executeJavaScriptAndWaitForResult(webContents,
181 "document.getElementById('" + nodeId + "').scrollIntoView()");
185 * Returns the contents of the node by its id.
187 public static String getNodeContents(WebContents webContents, String nodeId)
188 throws InterruptedException, TimeoutException {
189 return getNodeField("textContent", webContents, nodeId, String.class);
193 * Returns the value of the node by its id.
195 public static String getNodeValue(final WebContents webContents, String nodeId)
196 throws InterruptedException, TimeoutException {
197 return getNodeField("value", webContents, nodeId, String.class);
201 * Returns the string value of a field of the node by its id.
203 public static String getNodeField(String fieldName, final WebContents webContents,
205 throws InterruptedException, TimeoutException {
206 return getNodeField(fieldName, webContents, nodeId, String.class);
209 private static <T> T getNodeField(String fieldName, final WebContents webContents,
210 String nodeId, Class<T> valueType)
211 throws InterruptedException, TimeoutException {
212 StringBuilder sb = new StringBuilder();
213 sb.append("(function() {");
214 sb.append(" var node = document.getElementById('" + nodeId + "');");
215 sb.append(" if (!node) return null;");
216 sb.append(" return [ node." + fieldName + " ];");
219 String jsonText = JavaScriptUtils.executeJavaScriptAndWaitForResult(
220 webContents, sb.toString());
221 Assert.assertFalse("Failed to retrieve contents for " + nodeId,
222 jsonText.trim().equalsIgnoreCase("null"));
223 return readValue(jsonText, valueType);
226 private static <T> T readValue(String jsonText, Class<T> valueType) {
227 JsonReader jsonReader = new JsonReader(new StringReader(jsonText));
230 jsonReader.beginArray();
231 if (jsonReader.hasNext()) value = readValue(jsonReader, valueType);
232 jsonReader.endArray();
233 Assert.assertNotNull("Invalid contents returned.", value);
236 } catch (IOException exception) {
237 Assert.fail("Failed to evaluate JavaScript: " + jsonText + "\n" + exception);
242 @SuppressWarnings("unchecked")
243 private static <T> T readValue(JsonReader jsonReader, Class<T> valueType)
245 if (valueType.equals(String.class)) return ((T) jsonReader.nextString());
246 if (valueType.equals(Boolean.class)) return ((T) ((Boolean) jsonReader.nextBoolean()));
247 if (valueType.equals(Integer.class)) return ((T) ((Integer) jsonReader.nextInt()));
248 if (valueType.equals(Long.class)) return ((T) ((Long) jsonReader.nextLong()));
249 if (valueType.equals(Double.class)) return ((T) ((Double) jsonReader.nextDouble()));
251 throw new IllegalArgumentException("Cannot read values of type " + valueType);
255 * Wait until a given node has non-zero bounds.
256 * @return Whether the node started having non-zero bounds.
258 public static boolean waitForNonZeroNodeBounds(final WebContents webContents,
259 final String nodeName)
260 throws InterruptedException {
261 return CriteriaHelper.pollForCriteria(new Criteria() {
263 public boolean isSatisfied() {
265 return !DOMUtils.getNodeBounds(webContents, nodeName).isEmpty();
266 } catch (InterruptedException e) {
267 // Intentionally do nothing
269 } catch (TimeoutException e) {
270 // Intentionally do nothing
278 * Returns click targets for a given DOM node.
280 private static int[] getClickTargetForNode(ContentViewCore viewCore, String nodeName)
281 throws InterruptedException, TimeoutException {
282 Rect bounds = getNodeBounds(viewCore.getWebContents(), nodeName);
283 Assert.assertNotNull("Failed to get DOM element bounds of '" + nodeName + "'.", bounds);
285 int clickX = (int) viewCore.getRenderCoordinates().fromLocalCssToPix(bounds.exactCenterX());
286 int clickY = (int) viewCore.getRenderCoordinates().fromLocalCssToPix(bounds.exactCenterY())
287 + viewCore.getTopControlsLayoutHeightPix();
288 return new int[] { clickX, clickY };