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;
15 import java.io.IOException;
16 import java.io.StringReader;
17 import java.util.concurrent.TimeoutException;
20 * Collection of DOM-based utilities.
22 public class DOMUtils {
25 * Returns whether the video with given {@code nodeId} has ended.
27 public static boolean hasVideoEnded(final ContentViewCore viewCore, final String nodeId)
28 throws InterruptedException, TimeoutException {
29 return getNodeField("ended", viewCore, nodeId, Boolean.class);
33 * Wait until the end of the video with given {@code nodeId}.
34 * @return Whether the video has ended.
36 public static boolean waitForEndOfVideo(final ContentViewCore viewCore, final String nodeId)
37 throws InterruptedException {
38 return CriteriaHelper.pollForCriteria(new Criteria() {
40 public boolean isSatisfied() {
42 return DOMUtils.hasVideoEnded(viewCore, nodeId);
43 } catch (InterruptedException e) {
44 // Intentionally do nothing
46 } catch (TimeoutException e) {
47 // Intentionally do nothing
55 * Makes the document exit fullscreen.
57 public static void exitFullscreen(final ContentViewCore viewCore) {
58 StringBuilder sb = new StringBuilder();
59 sb.append("(function() {");
60 sb.append(" if (document.webkitExitFullscreen) document.webkitExitFullscreen();");
63 JavaScriptUtils.executeJavaScript(viewCore, sb.toString());
67 * Returns the rect boundaries for a node by its id.
69 public static Rect getNodeBounds(final ContentViewCore viewCore, String nodeId)
70 throws InterruptedException, TimeoutException {
71 StringBuilder sb = new StringBuilder();
72 sb.append("(function() {");
73 sb.append(" var node = document.getElementById('" + nodeId + "');");
74 sb.append(" if (!node) return null;");
75 sb.append(" var width = Math.round(node.offsetWidth);");
76 sb.append(" var height = Math.round(node.offsetHeight);");
77 sb.append(" var x = -window.scrollX;");
78 sb.append(" var y = -window.scrollY;");
80 sb.append(" x += node.offsetLeft;");
81 sb.append(" y += node.offsetTop;");
82 sb.append(" } while (node = node.offsetParent);");
83 sb.append(" return [ Math.round(x), Math.round(y), width, height ];");
86 String jsonText = JavaScriptUtils.executeJavaScriptAndWaitForResult(
87 viewCore, sb.toString());
89 Assert.assertFalse("Failed to retrieve bounds for " + nodeId,
90 jsonText.trim().equalsIgnoreCase("null"));
92 JsonReader jsonReader = new JsonReader(new StringReader(jsonText));
93 int[] bounds = new int[4];
95 jsonReader.beginArray();
97 while (jsonReader.hasNext()) {
98 bounds[i++] = jsonReader.nextInt();
100 jsonReader.endArray();
101 Assert.assertEquals("Invalid bounds returned.", 4, i);
104 } catch (IOException exception) {
105 Assert.fail("Failed to evaluate JavaScript: " + jsonText + "\n" + exception);
108 return new Rect(bounds[0], bounds[1], bounds[0] + bounds[2], bounds[1] + bounds[3]);
112 * Focus a DOM node by its id.
114 public static void focusNode(final ContentViewCore viewCore, String nodeId)
115 throws InterruptedException, TimeoutException {
116 StringBuilder sb = new StringBuilder();
117 sb.append("(function() {");
118 sb.append(" var node = document.getElementById('" + nodeId + "');");
119 sb.append(" if (node) node.focus();");
122 JavaScriptUtils.executeJavaScriptAndWaitForResult(viewCore, sb.toString());
126 * Click a DOM node by its id.
128 public static void clickNode(ActivityInstrumentationTestCase2 activityTestCase,
129 final ContentViewCore viewCore, String nodeId)
130 throws InterruptedException, TimeoutException {
131 int[] clickTarget = getClickTargetForNode(viewCore, nodeId);
132 TouchCommon touchCommon = new TouchCommon(activityTestCase);
133 touchCommon.singleClickView(viewCore.getContainerView(), clickTarget[0], clickTarget[1]);
137 * Long-press a DOM node by its id.
139 public static void longPressNode(ActivityInstrumentationTestCase2 activityTestCase,
140 final ContentViewCore viewCore, String nodeId)
141 throws InterruptedException, TimeoutException {
142 int[] clickTarget = getClickTargetForNode(viewCore, nodeId);
143 TouchCommon touchCommon = new TouchCommon(activityTestCase);
144 touchCommon.longPressView(viewCore.getContainerView(), clickTarget[0], clickTarget[1]);
148 * Scrolls the view to ensure that the required DOM node is visible.
150 public static void scrollNodeIntoView(ContentViewCore viewCore, String nodeId)
151 throws InterruptedException, TimeoutException {
152 JavaScriptUtils.executeJavaScriptAndWaitForResult(viewCore,
153 "document.getElementById('" + nodeId + "').scrollIntoView()");
157 * Returns the contents of the node by its id.
159 public static String getNodeContents(ContentViewCore viewCore, String nodeId)
160 throws InterruptedException, TimeoutException {
161 return getNodeField("textContent", viewCore, nodeId, String.class);
165 * Returns the value of the node by its id.
167 public static String getNodeValue(final ContentViewCore viewCore, String nodeId)
168 throws InterruptedException, TimeoutException {
169 return getNodeField("value", viewCore, nodeId, String.class);
173 * Returns the string value of a field of the node by its id.
175 public static String getNodeField(String fieldName, final ContentViewCore viewCore,
177 throws InterruptedException, TimeoutException {
178 return getNodeField(fieldName, viewCore, nodeId, String.class);
181 private static <T> T getNodeField(String fieldName, final ContentViewCore viewCore,
182 String nodeId, Class<T> valueType)
183 throws InterruptedException, TimeoutException {
184 StringBuilder sb = new StringBuilder();
185 sb.append("(function() {");
186 sb.append(" var node = document.getElementById('" + nodeId + "');");
187 sb.append(" if (!node) return null;");
188 sb.append(" return [ node." + fieldName + " ];");
191 String jsonText = JavaScriptUtils.executeJavaScriptAndWaitForResult(
192 viewCore, sb.toString());
193 Assert.assertFalse("Failed to retrieve contents for " + nodeId,
194 jsonText.trim().equalsIgnoreCase("null"));
196 JsonReader jsonReader = new JsonReader(new StringReader(jsonText));
199 jsonReader.beginArray();
200 if (jsonReader.hasNext()) value = readValue(jsonReader, valueType);
201 jsonReader.endArray();
202 Assert.assertNotNull("Invalid contents returned.", value);
205 } catch (IOException exception) {
206 Assert.fail("Failed to evaluate JavaScript: " + jsonText + "\n" + exception);
211 @SuppressWarnings("unchecked")
212 private static <T> T readValue(JsonReader jsonReader, Class<T> valueType)
214 if (valueType.equals(String.class)) return ((T) jsonReader.nextString());
215 if (valueType.equals(Boolean.class)) return ((T) ((Boolean) jsonReader.nextBoolean()));
216 if (valueType.equals(Integer.class)) return ((T) ((Integer) jsonReader.nextInt()));
217 if (valueType.equals(Long.class)) return ((T) ((Long) jsonReader.nextLong()));
218 if (valueType.equals(Double.class)) return ((T) ((Double) jsonReader.nextDouble()));
220 throw new IllegalArgumentException("Cannot read values of type " + valueType);
224 * Wait until a given node has non-zero bounds.
225 * @return Whether the node started having non-zero bounds.
227 public static boolean waitForNonZeroNodeBounds(final ContentViewCore viewCore,
228 final String nodeName)
229 throws InterruptedException {
230 return CriteriaHelper.pollForCriteria(new Criteria() {
232 public boolean isSatisfied() {
234 return !DOMUtils.getNodeBounds(viewCore, nodeName).isEmpty();
235 } catch (InterruptedException e) {
236 // Intentionally do nothing
238 } catch (TimeoutException e) {
239 // Intentionally do nothing
247 * Returns click targets for a given DOM node.
249 private static int[] getClickTargetForNode(ContentViewCore viewCore, String nodeName)
250 throws InterruptedException, TimeoutException {
251 Rect bounds = getNodeBounds(viewCore, nodeName);
252 Assert.assertNotNull("Failed to get DOM element bounds of '" + nodeName + "'.", bounds);
254 int clickX = (int) viewCore.getRenderCoordinates().fromLocalCssToPix(bounds.exactCenterX())
255 + viewCore.getViewportSizeOffsetWidthPix();
256 int clickY = (int) viewCore.getRenderCoordinates().fromLocalCssToPix(bounds.exactCenterY())
257 + viewCore.getViewportSizeOffsetHeightPix();
258 return new int[] { clickX, clickY };