DOMUtils.java revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
1ea91cc579ade536e3a08498a8157921dd4f533d1ager@chromium.org// Copyright (c) 2012 The Chromium Authors. All rights reserved.
243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen// Use of this source code is governed by a BSD-style license that can be
343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen// found in the LICENSE file.
443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansenpackage org.chromium.content.browser.test.util;
643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansenimport android.graphics.Rect;
843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansenimport android.test.ActivityInstrumentationTestCase2;
943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansenimport android.util.JsonReader;
1043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
1143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansenimport java.io.IOException;
1243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansenimport java.io.StringReader;
1343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansenimport java.util.concurrent.TimeoutException;
1443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
1543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansenimport junit.framework.Assert;
1643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
1743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansenimport org.chromium.content.browser.ContentView;
1843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
1943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen/**
2043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen * Collection of DOM-based utilities.
2143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen */
2243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansenpublic class DOMUtils {
2343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
2443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    /**
2543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen     * Returns the rect boundaries for a node by its id.
2643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen     */
2743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    public static Rect getNodeBounds(
2843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            final ContentView view, TestCallbackHelperContainer viewClient, String nodeId)
2943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            throws InterruptedException, TimeoutException {
3043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        StringBuilder sb = new StringBuilder();
3143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        sb.append("(function() {");
32f5aa83707f1db5aecb22f6c3bfd5042f629d5fcfkasperl@chromium.org        sb.append("  var node = document.getElementById('" + nodeId + "');");
33d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org        sb.append("  if (!node) return null;");
34a55512615f5adc085d23bc8589d155c4b579fb7bkasperl@chromium.org        sb.append("  var width = node.offsetWidth;");
3501beca7f8d9f549e04ec575a0bca96d859ab55a5ager@chromium.org        sb.append("  var height = node.offsetHeight;");
3643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        sb.append("  var x = -window.scrollX;");
37f5aa83707f1db5aecb22f6c3bfd5042f629d5fcfkasperl@chromium.org        sb.append("  var y = -window.scrollY;");
381456e708d277e725ca42a03463af16fe471c9210jkummerow@chromium.org        sb.append("  do {");
39a55512615f5adc085d23bc8589d155c4b579fb7bkasperl@chromium.org        sb.append("    x += node.offsetLeft;");
40f5aa83707f1db5aecb22f6c3bfd5042f629d5fcfkasperl@chromium.org        sb.append("    y += node.offsetTop;");
417c537e2abe09729ed6cb827b4dd206470d8c4a42ager@chromium.org        sb.append("  } while (node = node.offsetParent);");
42a55512615f5adc085d23bc8589d155c4b579fb7bkasperl@chromium.org        sb.append("  return [ x, y, width, height ];");
4343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        sb.append("})();");
4471affb54842da76b24f0bb3184e9f0960523f89dkasperl@chromium.org
4571affb54842da76b24f0bb3184e9f0960523f89dkasperl@chromium.org        String jsonText = JavaScriptUtils.executeJavaScriptAndWaitForResult(
4643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                view, viewClient, sb.toString());
4743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
4843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        Assert.assertFalse("Failed to retrieve bounds for " + nodeId,
4943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                jsonText.trim().equalsIgnoreCase("null"));
5043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
5143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        JsonReader jsonReader = new JsonReader(new StringReader(jsonText));
5243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        int[] bounds = new int[4];
5343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        try {
54ea88ce93dcb41a9200ec8747ae7642a5db1f4ce7sgjesse@chromium.org            jsonReader.beginArray();
5543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            int i = 0;
5643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            while (jsonReader.hasNext()) {
5743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                bounds[i++] = jsonReader.nextInt();
5843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            }
5943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            jsonReader.endArray();
60245aa859d34fd516161c48ef4c69d38d9b889284iposva@chromium.org            Assert.assertEquals("Invalid bounds returned.", 4, i);
61245aa859d34fd516161c48ef4c69d38d9b889284iposva@chromium.org
62245aa859d34fd516161c48ef4c69d38d9b889284iposva@chromium.org            jsonReader.close();
6343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        } catch (IOException exception) {
6443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            Assert.fail("Failed to evaluate JavaScript: " + jsonText + "\n" + exception);
6543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        }
6643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
6743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        return new Rect(bounds[0], bounds[1], bounds[0] + bounds[2], bounds[1] + bounds[3]);
6843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    }
6943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
7043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    /**
7143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen     * Focus a DOM node by its id.
7243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen     */
7343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    public static void focusNode(ActivityInstrumentationTestCase2 activityTestCase,
7443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            final ContentView view, TestCallbackHelperContainer viewClient, String nodeId)
7543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            throws InterruptedException, TimeoutException {
7643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        StringBuilder sb = new StringBuilder();
7743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        sb.append("(function() {");
7843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        sb.append("  var node = document.getElementById('" + nodeId + "');");
7943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        sb.append("  if (node) node.focus();");
8043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        sb.append("})();");
8143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
8243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        JavaScriptUtils.executeJavaScriptAndWaitForResult(view, viewClient, sb.toString());
8343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    }
84245aa859d34fd516161c48ef4c69d38d9b889284iposva@chromium.org
85ea88ce93dcb41a9200ec8747ae7642a5db1f4ce7sgjesse@chromium.org    /**
86ea88ce93dcb41a9200ec8747ae7642a5db1f4ce7sgjesse@chromium.org     * Click a DOM node by its id.
87ea88ce93dcb41a9200ec8747ae7642a5db1f4ce7sgjesse@chromium.org     */
88245aa859d34fd516161c48ef4c69d38d9b889284iposva@chromium.org    public static void clickNode(ActivityInstrumentationTestCase2 activityTestCase,
8943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            final ContentView view, TestCallbackHelperContainer viewClient, String nodeId)
9043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            throws InterruptedException, TimeoutException {
9143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        int[] clickTarget = getClickTargetForNode(view, viewClient, nodeId);
9243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        TouchCommon touchCommon = new TouchCommon(activityTestCase);
936d786c9805481bd13ecb29c3155540f2f32950e1svenpanne@chromium.org        touchCommon.singleClickView(view, clickTarget[0], clickTarget[1]);
9443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    }
9543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
9643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    /**
9743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen     * Long-press a DOM node by its id.
9843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen     */
9943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    public static void longPressNode(ActivityInstrumentationTestCase2 activityTestCase,
10043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            final ContentView view, TestCallbackHelperContainer viewClient, String nodeId)
10143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            throws InterruptedException, TimeoutException {
10243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        int[] clickTarget = getClickTargetForNode(view, viewClient, nodeId);
1033811b436bf328d2ace6fe79ce99aeda71f9f06d3ager@chromium.org        TouchCommon touchCommon = new TouchCommon(activityTestCase);
1043811b436bf328d2ace6fe79ce99aeda71f9f06d3ager@chromium.org        touchCommon.longPressView(view, clickTarget[0], clickTarget[1]);
1053811b436bf328d2ace6fe79ce99aeda71f9f06d3ager@chromium.org    }
10643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
10743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    /**
108245aa859d34fd516161c48ef4c69d38d9b889284iposva@chromium.org     * Scrolls the view to ensure that the required DOM node is visible.
109245aa859d34fd516161c48ef4c69d38d9b889284iposva@chromium.org     */
110ea88ce93dcb41a9200ec8747ae7642a5db1f4ce7sgjesse@chromium.org    public static void scrollNodeIntoView(final ContentView view,
11143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            TestCallbackHelperContainer viewClient, String nodeId)
11243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            throws InterruptedException, TimeoutException {
11343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        JavaScriptUtils.executeJavaScriptAndWaitForResult(view, viewClient,
11443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                "document.getElementById('" + nodeId + "').scrollIntoView()");
11543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    }
11643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
11743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    /**
1189a4089a092cad9ff23b6416b92cd5d818dc101d1mads.s.ager@gmail.com     * Returns the contents of the node by its id.
11943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen     */
12074f333bce721daf6b1f9d7d3d3faa623f77658d7vegorov@chromium.org    public static String getNodeContents(final ContentView view,
121ea88ce93dcb41a9200ec8747ae7642a5db1f4ce7sgjesse@chromium.org            TestCallbackHelperContainer viewClient, String nodeId)
122a55512615f5adc085d23bc8589d155c4b579fb7bkasperl@chromium.org            throws InterruptedException, TimeoutException {
12343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        return getNodeField("textContent", view, viewClient, nodeId);
12443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    }
12543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
1264a5224e84636d192e82f288bfab0d308bdae5c37whesse@chromium.org    /**
12743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen     * Returns the value of the node by its id.
12843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen     */
12943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    public static String getNodeValue(final ContentView view,
13043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            TestCallbackHelperContainer viewClient, String nodeId)
131a55512615f5adc085d23bc8589d155c4b579fb7bkasperl@chromium.org            throws InterruptedException, TimeoutException {
13243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        return getNodeField("value", view, viewClient, nodeId);
133a55512615f5adc085d23bc8589d155c4b579fb7bkasperl@chromium.org    }
13471affb54842da76b24f0bb3184e9f0960523f89dkasperl@chromium.org
13543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    private static String getNodeField(String fieldName, final ContentView view,
13643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            TestCallbackHelperContainer viewClient, String nodeId)
13743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            throws InterruptedException, TimeoutException {
138a55512615f5adc085d23bc8589d155c4b579fb7bkasperl@chromium.org        StringBuilder sb = new StringBuilder();
13943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        sb.append("(function() {");
1405323a9c29497eb5a52821d396990c6d75a37baf7jkummerow@chromium.org        sb.append("  var node = document.getElementById('" + nodeId + "');");
14143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        sb.append("  if (!node) return null;");
14243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        sb.append("  if (!node." + fieldName +") return null;");
14343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        sb.append("  return [ node." + fieldName + " ];");
14443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        sb.append("})();");
14543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
14643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        String jsonText = JavaScriptUtils.executeJavaScriptAndWaitForResult(
14743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                view, viewClient, sb.toString());
14843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        Assert.assertFalse("Failed to retrieve contents for " + nodeId,
14943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                jsonText.trim().equalsIgnoreCase("null"));
15043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
151ea88ce93dcb41a9200ec8747ae7642a5db1f4ce7sgjesse@chromium.org        JsonReader jsonReader = new JsonReader(new StringReader(jsonText));
1526d786c9805481bd13ecb29c3155540f2f32950e1svenpanne@chromium.org        String value = null;
1536d786c9805481bd13ecb29c3155540f2f32950e1svenpanne@chromium.org        try {
1545d00b60c201d860c74821f553fdc34f4e057b411lrn@chromium.org            jsonReader.beginArray();
1553811b436bf328d2ace6fe79ce99aeda71f9f06d3ager@chromium.org            if (jsonReader.hasNext()) value = jsonReader.nextString();
1563811b436bf328d2ace6fe79ce99aeda71f9f06d3ager@chromium.org            jsonReader.endArray();
1573811b436bf328d2ace6fe79ce99aeda71f9f06d3ager@chromium.org            Assert.assertNotNull("Invalid contents returned.", value);
1583811b436bf328d2ace6fe79ce99aeda71f9f06d3ager@chromium.org
159ea88ce93dcb41a9200ec8747ae7642a5db1f4ce7sgjesse@chromium.org            jsonReader.close();
160ea88ce93dcb41a9200ec8747ae7642a5db1f4ce7sgjesse@chromium.org        } catch (IOException exception) {
16143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            Assert.fail("Failed to evaluate JavaScript: " + jsonText + "\n" + exception);
16243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        }
16343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        return value;
16443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    }
1653811b436bf328d2ace6fe79ce99aeda71f9f06d3ager@chromium.org
1663811b436bf328d2ace6fe79ce99aeda71f9f06d3ager@chromium.org    /**
1673811b436bf328d2ace6fe79ce99aeda71f9f06d3ager@chromium.org     * Returns click targets for a given DOM node.
168068ea0a6ea115c058d1d9798029bd7fa1eaaa955mstarzinger@chromium.org     */
16943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    private static int[] getClickTargetForNode(final ContentView view,
17043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            TestCallbackHelperContainer viewClient, String nodeName)
17143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            throws InterruptedException, TimeoutException {
17243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        Rect bounds = getNodeBounds(view, viewClient, nodeName);
17343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        Assert.assertNotNull("Failed to get DOM element bounds of '" + nodeName + "'.", bounds);
17443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
175e27d617298263725e8a48c2aa14029759b952623mstarzinger@chromium.org        int clickX = (int) view.getRenderCoordinates().fromLocalCssToPix(bounds.exactCenterX())
176e27d617298263725e8a48c2aa14029759b952623mstarzinger@chromium.org                + (int) view.getContentViewCore().getViewportSizeOffsetWidthPix();
17743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        int clickY = (int) view.getRenderCoordinates().fromLocalCssToPix(bounds.exactCenterY())
178e27d617298263725e8a48c2aa14029759b952623mstarzinger@chromium.org                + (int) view.getContentViewCore().getViewportSizeOffsetHeightPix();
17943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        return new int[] { clickX, clickY };
18043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    }
18143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen}
18243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen