1// Copyright (c) 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 5package org.chromium.content.browser.test.util; 6 7import android.graphics.Rect; 8import android.test.ActivityInstrumentationTestCase2; 9import android.util.JsonReader; 10 11import java.io.IOException; 12import java.io.StringReader; 13import java.util.concurrent.TimeoutException; 14 15import junit.framework.Assert; 16 17import org.chromium.content.browser.ContentView; 18 19/** 20 * Collection of DOM-based utilities. 21 */ 22public class DOMUtils { 23 24 /** 25 * Returns the rect boundaries for a node by its id. 26 */ 27 public static Rect getNodeBounds( 28 final ContentView view, TestCallbackHelperContainer viewClient, String nodeId) 29 throws InterruptedException, TimeoutException { 30 StringBuilder sb = new StringBuilder(); 31 sb.append("(function() {"); 32 sb.append(" var node = document.getElementById('" + nodeId + "');"); 33 sb.append(" if (!node) return null;"); 34 sb.append(" var width = node.offsetWidth;"); 35 sb.append(" var height = node.offsetHeight;"); 36 sb.append(" var x = -window.scrollX;"); 37 sb.append(" var y = -window.scrollY;"); 38 sb.append(" do {"); 39 sb.append(" x += node.offsetLeft;"); 40 sb.append(" y += node.offsetTop;"); 41 sb.append(" } while (node = node.offsetParent);"); 42 sb.append(" return [ x, y, width, height ];"); 43 sb.append("})();"); 44 45 String jsonText = JavaScriptUtils.executeJavaScriptAndWaitForResult( 46 view, viewClient, sb.toString()); 47 48 Assert.assertFalse("Failed to retrieve bounds for " + nodeId, 49 jsonText.trim().equalsIgnoreCase("null")); 50 51 JsonReader jsonReader = new JsonReader(new StringReader(jsonText)); 52 int[] bounds = new int[4]; 53 try { 54 jsonReader.beginArray(); 55 int i = 0; 56 while (jsonReader.hasNext()) { 57 bounds[i++] = jsonReader.nextInt(); 58 } 59 jsonReader.endArray(); 60 Assert.assertEquals("Invalid bounds returned.", 4, i); 61 62 jsonReader.close(); 63 } catch (IOException exception) { 64 Assert.fail("Failed to evaluate JavaScript: " + jsonText + "\n" + exception); 65 } 66 67 return new Rect(bounds[0], bounds[1], bounds[0] + bounds[2], bounds[1] + bounds[3]); 68 } 69 70 /** 71 * Focus a DOM node by its id. 72 */ 73 public static void focusNode(ActivityInstrumentationTestCase2 activityTestCase, 74 final ContentView view, TestCallbackHelperContainer viewClient, String nodeId) 75 throws InterruptedException, TimeoutException { 76 StringBuilder sb = new StringBuilder(); 77 sb.append("(function() {"); 78 sb.append(" var node = document.getElementById('" + nodeId + "');"); 79 sb.append(" if (node) node.focus();"); 80 sb.append("})();"); 81 82 JavaScriptUtils.executeJavaScriptAndWaitForResult(view, viewClient, sb.toString()); 83 } 84 85 /** 86 * Click a DOM node by its id. 87 */ 88 public static void clickNode(ActivityInstrumentationTestCase2 activityTestCase, 89 final ContentView view, TestCallbackHelperContainer viewClient, String nodeId) 90 throws InterruptedException, TimeoutException { 91 int[] clickTarget = getClickTargetForNode(view, viewClient, nodeId); 92 TouchCommon touchCommon = new TouchCommon(activityTestCase); 93 touchCommon.singleClickView(view, clickTarget[0], clickTarget[1]); 94 } 95 96 /** 97 * Long-press a DOM node by its id. 98 */ 99 public static void longPressNode(ActivityInstrumentationTestCase2 activityTestCase, 100 final ContentView view, TestCallbackHelperContainer viewClient, String nodeId) 101 throws InterruptedException, TimeoutException { 102 int[] clickTarget = getClickTargetForNode(view, viewClient, nodeId); 103 TouchCommon touchCommon = new TouchCommon(activityTestCase); 104 touchCommon.longPressView(view, clickTarget[0], clickTarget[1]); 105 } 106 107 /** 108 * Scrolls the view to ensure that the required DOM node is visible. 109 */ 110 public static void scrollNodeIntoView(final ContentView view, 111 TestCallbackHelperContainer viewClient, String nodeId) 112 throws InterruptedException, TimeoutException { 113 JavaScriptUtils.executeJavaScriptAndWaitForResult(view, viewClient, 114 "document.getElementById('" + nodeId + "').scrollIntoView()"); 115 } 116 117 /** 118 * Returns the contents of the node by its id. 119 */ 120 public static String getNodeContents(final ContentView view, 121 TestCallbackHelperContainer viewClient, String nodeId) 122 throws InterruptedException, TimeoutException { 123 return getNodeField("textContent", view, viewClient, nodeId); 124 } 125 126 /** 127 * Returns the value of the node by its id. 128 */ 129 public static String getNodeValue(final ContentView view, 130 TestCallbackHelperContainer viewClient, String nodeId) 131 throws InterruptedException, TimeoutException { 132 return getNodeField("value", view, viewClient, nodeId); 133 } 134 135 private static String getNodeField(String fieldName, final ContentView view, 136 TestCallbackHelperContainer viewClient, String nodeId) 137 throws InterruptedException, TimeoutException { 138 StringBuilder sb = new StringBuilder(); 139 sb.append("(function() {"); 140 sb.append(" var node = document.getElementById('" + nodeId + "');"); 141 sb.append(" if (!node) return null;"); 142 sb.append(" if (!node." + fieldName +") return null;"); 143 sb.append(" return [ node." + fieldName + " ];"); 144 sb.append("})();"); 145 146 String jsonText = JavaScriptUtils.executeJavaScriptAndWaitForResult( 147 view, viewClient, sb.toString()); 148 Assert.assertFalse("Failed to retrieve contents for " + nodeId, 149 jsonText.trim().equalsIgnoreCase("null")); 150 151 JsonReader jsonReader = new JsonReader(new StringReader(jsonText)); 152 String value = null; 153 try { 154 jsonReader.beginArray(); 155 if (jsonReader.hasNext()) value = jsonReader.nextString(); 156 jsonReader.endArray(); 157 Assert.assertNotNull("Invalid contents returned.", value); 158 159 jsonReader.close(); 160 } catch (IOException exception) { 161 Assert.fail("Failed to evaluate JavaScript: " + jsonText + "\n" + exception); 162 } 163 return value; 164 } 165 166 /** 167 * Returns click targets for a given DOM node. 168 */ 169 private static int[] getClickTargetForNode(final ContentView view, 170 TestCallbackHelperContainer viewClient, String nodeName) 171 throws InterruptedException, TimeoutException { 172 Rect bounds = getNodeBounds(view, viewClient, nodeName); 173 Assert.assertNotNull("Failed to get DOM element bounds of '" + nodeName + "'.", bounds); 174 175 int clickX = (int) view.getRenderCoordinates().fromLocalCssToPix(bounds.exactCenterX()) 176 + (int) view.getContentViewCore().getViewportSizeOffsetWidthPix(); 177 int clickY = (int) view.getRenderCoordinates().fromLocalCssToPix(bounds.exactCenterY()) 178 + (int) view.getContentViewCore().getViewportSizeOffsetHeightPix(); 179 return new int[] { clickX, clickY }; 180 } 181} 182