commit d02c24909ae6997ab33f63f487906ab643ae4c27
Author: Mark Schillaci <
mschi...@google.com>
AuthorDate: Fri Nov 05 23:19:50 2021
Commit: Chromium LUCI CQ <
chromiu...@luci-project-accounts.iam.gserviceaccount.com>
CommitDate: Fri Nov 05 23:19:50 2021
Improve test outputs for static accessibility tree tests on Android
This CL continues the series of CLs that will expand the Android
accessibility test suite to include a solution to dump tree tests using
the full Java-side and native-side code.
With this CL we improve the various custom toString methods to make the
output of AccessibilityNodeInfo actions easier to read, and remove the
need for reflection. We also add a new test case for the aria
directory, and generalize the performTest method to allow both aria or
html tests. We also remove the html elements output from the root
web area, since it will be the same on every test and simply assert
the value before printing.
AX-Relnotes: N/A
Bug: 1258230
Change-Id: Ic5c2a8eb7977d7bca43ed6db6021c0d126418a4b
Reviewed-on:
https://chromium-review.googlesource.com/c/chromium/src/+/3256401
Commit-Queue: Mark Schillaci <
mschi...@google.com>
Reviewed-by: Jinsuk Kim <
jins...@chromium.org>
Reviewed-by: David Tseng <
dts...@chromium.org>
Cr-Commit-Position: refs/heads/main@{#939009}
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index c9b4d05..5759fc8 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -604,6 +604,7 @@
]
data = [
+ "//content/test/data/accessibility/aria/",
"//content/test/data/accessibility/event/",
"//content/test/data/accessibility/html/",
"//content/test/data/android/",
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java
index c2aa7d3..5b95bc46 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java
@@ -7,16 +7,18 @@
import static org.chromium.content.browser.accessibility.AccessibilityContentShellActivityTestRule.EVENTS_ERROR;
import static org.chromium.content.browser.accessibility.AccessibilityContentShellActivityTestRule.RESULTS_NULL;
import static org.chromium.content.browser.accessibility.AccessibilityContentShellTestUtils.sClassNameMatcher;
-import static org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl.ACTION_CONTEXT_CLICK;
-import static org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl.ACTION_SHOW_ON_SCREEN;
+import static org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl.EXTRAS_KEY_SUPPORTED_ELEMENTS;
import static org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl.EXTRAS_KEY_UNCLIPPED_BOTTOM;
import static org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl.EXTRAS_KEY_UNCLIPPED_TOP;
+import static java.lang.String.CASE_INSENSITIVE_ORDER;
+
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.os.Build;
import android.os.Bundle;
import android.text.InputType;
+import android.text.TextUtils;
import android.view.accessibility.AccessibilityNodeInfo;
import androidx.test.filters.SmallTest;
@@ -29,7 +31,9 @@
import org.chromium.base.test.util.MinAndroidSdkLevel;
import org.chromium.content_public.browser.test.ContentJUnit4ClassRunner;
-import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
/**
* Tests for WebContentsAccessibilityImpl integration with accessibility services.
@@ -40,7 +44,18 @@
@SuppressLint("VisibleForTests")
public class WebContentsAccessibilityTreeTest {
// File path that holds all the relevant tests.
- private static final String BASE_FILE_PATH = "content/test/data/accessibility/html/";
+ private static final String BASE_ARIA_FILE_PATH = "content/test/data/accessibility/aria/";
+ private static final String BASE_HTML_FILE_PATH = "content/test/data/accessibility/html/";
+
+ // All HTML elements that can be traversed through, should be the same for all tests. Taken
+ // from: content/browser/accessibility/web_contents_accessibility_android.cc
+ private static final String ALL_HTML_ELEMENT_PREDICATES =
+ "ARTICLE,BUTTON,CHECKBOX,COMBOBOX,CONTROL,FOCUSABLE,FRAME,GRAPHIC,H1,H2,H3,H4,H5,H6,"
+ + "HEADING,LANDMARK,LINK,LIST,LIST_ITEM,MAIN,MEDIA,RADIO,SECTION,TABLE,TEXT_FIELD,"
+ + "UNVISITED_LINK,VISITED_LINK";
+ private static final String PREDICATES_ERROR =
+ "HTML predicates did not match expectations, was a new predicate added to predicate "
+ + "map of web_contents_accessibility_android.cc?";
@Rule
public AccessibilityContentShellActivityTestRule mActivityTestRule =
@@ -65,6 +80,16 @@
assertResults(expectationFilePath + expectationFile, generateAccessibilityNodeInfoTree());
}
+ // Helper methods to pass-through to the performTest method so each individual test does
+ // not need to include its own filepath.
+ private void performAriaTest(String inputFile, String expectationFile) {
+ performTest(inputFile, expectationFile, BASE_ARIA_FILE_PATH);
+ }
+
+ private void performHtmlTest(String inputFile, String expectationFile) {
+ performTest(inputFile, expectationFile, BASE_HTML_FILE_PATH);
+ }
+
/**
* Helper method to compare test outputs with expected results. Reads content of expectations
* file, asserts non-null, then compares with results.
@@ -128,27 +153,6 @@
}
/**
- * Helper method to use reflection to convert AccessibilityNodeInfo actions to human-
- * readable text. The method is private so we set accessible to true.
- *
- * @param node The object that contains the action
- * @param action Action int to convert to a String
- * @return String Human-readable representation of the action int
- */
- @SuppressLint("SoonBlockedPrivateApi")
- private String getSymbolicName(AccessibilityNodeInfo node, int action) {
- try {
- Method getActionSymbolicName = AccessibilityNodeInfo.class.getDeclaredMethod(
- "getActionSymbolicName", int.class);
- getActionSymbolicName.setAccessible(true);
- return (String) getActionSymbolicName.invoke(node, Integer.valueOf(action));
- } catch (Exception ex) {
- Assert.fail("Unable to call hidden AccessibilityNodeInfo method: " + ex.toString());
- return "";
- }
- }
-
- /**
* Helper method to perform a custom toString on a given AccessibilityNodeInfo object.
*
* @param node Object to create a toString for
@@ -157,24 +161,30 @@
private String customToString(AccessibilityNodeInfo node) {
StringBuilder builder = new StringBuilder();
- // Classname and text should always be printed.
- builder.append(node.getClassName());
- builder.append("; text: ").append(node.getText()).append(";");
+ // Print classname first, but only print content after the last period to remove redundancy.
+ String[] classNameParts = node.getClassName().toString().split("\\.");
+ builder.append(classNameParts[classNameParts.length - 1]);
+
+ // Print text unless it is empty (null is allowed)
+ if (node.getText() == null || !node.getText().toString().isEmpty()) {
+ builder.append(" text:\"").append(node.getText()).append("\"");
+ }
// Text properties - Only print when non-null
if (node.getContentDescription() != null) {
- builder.append(" contentDescription: ")
+ builder.append(" contentDescription:\"")
.append(node.getContentDescription())
- .append(";");
+ .append("\"");
}
if (node.getViewIdResourceName() != null) {
- builder.append(" viewIdResName: ").append(node.getViewIdResourceName()).append(";");
+ builder.append(" viewIdResName:\"").append(node.getViewIdResourceName()).append("\"");
}
if (node.getError() != null) {
- builder.append(" error: ").append(node.getError()).append(";");
+ builder.append(" error:\"").append(node.getError()).append("\"");
}
- // Boolean properties - Only print when set to true.
+ // Boolean properties - Only print when set to true except for enabled and visibleToUser,
+ // which are both mostly true, so only print when they are false.
if (node.canOpenPopup()) {
builder.append(" canOpenPopUp");
}
@@ -196,8 +206,8 @@
if (node.isEditable()) {
builder.append(" editable");
}
- if (node.isEnabled()) {
- builder.append(" enabled");
+ if (!node.isEnabled()) {
+ builder.append(" disabled");
}
if (node.isFocusable()) {
builder.append(" focusable");
@@ -217,88 +227,178 @@
if (node.isSelected()) {
builder.append(" selected");
}
- if (node.isVisibleToUser()) {
- builder.append(" visibleToUser");
+ if (!node.isVisibleToUser()) {
+ builder.append(" notVisibleToUser");
}
// Integer properties - Only print when not default values.
if (node.getInputType() != InputType.TYPE_NULL) {
- builder.append("; inputType: ").append(node.getInputType());
+ builder.append(" inputType:").append(node.getInputType());
}
if (node.getTextSelectionStart() != -1) {
- builder.append("; textSelectionStart: ").append(node.getTextSelectionStart());
+ builder.append(" textSelectionStart:").append(node.getTextSelectionStart());
}
if (node.getTextSelectionEnd() != -1) {
- builder.append("; textSelectionEnd: ").append(node.getTextSelectionEnd());
+ builder.append(" textSelectionEnd:").append(node.getTextSelectionEnd());
}
if (node.getMaxTextLength() != -1) {
- builder.append("; maxTextLength: ").append(node.getMaxTextLength());
+ builder.append(" maxTextLength:").append(node.getMaxTextLength());
}
// Child objects - print for non-null cases.
if (node.getCollectionInfo() != null) {
- builder.append("; CollectionInfo: ")
+ builder.append(" CollectionInfo:")
.append(collectionInfoToString(node.getCollectionInfo()));
}
if (node.getCollectionItemInfo() != null) {
- builder.append("; CollectionItemInfo: ")
+ builder.append(" CollectionItemInfo:")
.append(collectionItemInfoToString(node.getCollectionItemInfo()));
}
if (node.getRangeInfo() != null) {
- builder.append("; RangeInfo: ").append(rangeInfoToString(node.getRangeInfo()));
+ builder.append(" RangeInfo:").append(rangeInfoToString(node.getRangeInfo()));
}
// Actions and Bundle extras - Always print.
- builder.append("; actions: ").append(actionsToString(node));
- builder.append("; bundle: ").append(bundleToString(node.getExtras()));
+ builder.append(" actions:").append(actionsToString(node));
+ builder.append(" bundle:").append(bundleToString(node.getExtras()));
return builder.toString();
}
// Various helper methods to print custom toStrings for objects.
private String collectionInfoToString(AccessibilityNodeInfo.CollectionInfo info) {
- return "[" + info.isHierarchical() + ", " + info.getSelectionMode() + ", "
- + info.getRowCount() + ", " + info.getColumnCount() + "]";
+ // Only include the isHierarchical boolean if true, since it is more often false, and
+ // ignore selection mode, which is not set by Chrome.
+ String prefix = "[";
+ if (info.isHierarchical()) {
+ prefix += "hierarchical, ";
+ }
+ return prefix + "rows=" + info.getRowCount() + ", cols=" + info.getColumnCount() + "]";
}
private String collectionItemInfoToString(AccessibilityNodeInfo.CollectionItemInfo info) {
- return "[" + info.isHeading() + ", " + info.isSelected() + ", " + info.getRowIndex() + ", "
- + info.getRowSpan() + ", " + info.getColumnIndex() + ", " + info.getColumnSpan()
- + "]";
+ // Only include isHeading and isSelected if true, since both are more often false.
+ String prefix = "[";
+ if (info.isHeading()) {
+ prefix += "heading, ";
+ }
+ if (info.isSelected()) {
+ prefix += "selected, ";
+ }
+ return prefix + "rowIndex=" + info.getRowIndex() + ", rowSpan=" + info.getRowSpan()
+ + ", colIndex=" + info.getColumnIndex() + ", colSpan=" + info.getColumnSpan() + "]";
}
private String rangeInfoToString(AccessibilityNodeInfo.RangeInfo info) {
- return "[" + info.getType() + ", " + info.getCurrent() + ", " + info.getMin() + ", "
- + info.getMax() + "]";
+ // Chrome always uses the float range type, so only print values of RangeInfo.
+ return "[current=" + info.getCurrent() + ", min=" + info.getMin() + ", max=" + info.getMax()
+ + "]";
}
private String actionsToString(AccessibilityNodeInfo node) {
+ // Sort actions list to ensure consistent output of tests.
+ Collections.sort(node.getActionList(), (a1, b2) -> Integer.compare(a1.getId(), b2.getId()));
+
+ List<String> actionStrings = new ArrayList<String>();
StringBuilder builder = new StringBuilder();
builder.append("[");
for (AccessibilityNodeInfo.AccessibilityAction action : node.getActionList()) {
// Four actions are set on all nodes, so ignore those when printing the tree.
if (action.getId() == AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT
|| action.getId() == AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT
- || action.getId() == ACTION_SHOW_ON_SCREEN
- || action.getId() == ACTION_CONTEXT_CLICK) {
+ || action.getId() == WebContentsAccessibilityImpl.ACTION_SHOW_ON_SCREEN
+ || action.getId() == WebContentsAccessibilityImpl.ACTION_CONTEXT_CLICK) {
continue;
}
- builder.append(getSymbolicName(node, action.getId())).append(", ");
+ actionStrings.add(actionToString(action.getId()));
}
- // Remove the extra comma for cleanliness
- if (builder.length() > 2) {
- builder.setLength(builder.length() - 2);
- }
+ builder.append(TextUtils.join(", ", actionStrings)).append("]");
- builder.append("]");
return builder.toString();
}
+ private String actionToString(int action) {
+ switch (action) {
+ // These could potentially be added to any given node.
+ case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
+ return "NEXT";
+ case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
+ return "PREVIOUS";
+ case AccessibilityNodeInfo.ACTION_SET_TEXT:
+ return "SET_TEXT";
+ case AccessibilityNodeInfo.ACTION_PASTE:
+ return "PASTE";
+ case WebContentsAccessibilityImpl.ACTION_IME_ENTER:
+ return "IME_ENTER";
+ case AccessibilityNodeInfo.ACTION_SET_SELECTION:
+ return "SET_SELECTION";
+ case AccessibilityNodeInfo.ACTION_CUT:
+ return "CUT";
+ case AccessibilityNodeInfo.ACTION_COPY:
+ return "COPY";
+ case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
+ return "SCROLL_FORWARD";
+ case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+ return "SCROLL_BACK";
+ case WebContentsAccessibilityImpl.ACTION_SCROLL_UP:
+ return "SCROLL_UP";
+ case WebContentsAccessibilityImpl.ACTION_PAGE_UP:
+ return "PAGE_UP";
+ case WebContentsAccessibilityImpl.ACTION_SCROLL_DOWN:
+ return "SCROLL_DOWN";
+ case WebContentsAccessibilityImpl.ACTION_PAGE_DOWN:
+ return "PAGE_DOWN";
+ case WebContentsAccessibilityImpl.ACTION_SCROLL_LEFT:
+ return "SCROLL_LEFT";
+ case WebContentsAccessibilityImpl.ACTION_PAGE_LEFT:
+ return "PAGE_LEFT";
+ case WebContentsAccessibilityImpl.ACTION_SCROLL_RIGHT:
+ return "SCROLL_RIGHT";
+ case WebContentsAccessibilityImpl.ACTION_PAGE_RIGHT:
+ return "PAGE_RIGHT";
+ case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS:
+ return "CLEAR_FOCUS";
+ case AccessibilityNodeInfo.ACTION_FOCUS:
+ return "FOCUS";
+ case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
+ return "CLEAR_A11Y_FOCUS";
+ case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
+ return "A11Y_FOCUS";
+ case AccessibilityNodeInfo.ACTION_CLICK:
+ return "CLICK";
+ case AccessibilityNodeInfo.ACTION_EXPAND:
+ return "EXPAND";
+ case AccessibilityNodeInfo.ACTION_COLLAPSE:
+ return "COLLAPSE";
+ case WebContentsAccessibilityImpl.ACTION_SET_PROGRESS:
+ return "SET_PROGRESS";
+
+ // The long click action is deliberately never be added to a node.
+ case AccessibilityNodeInfo.ACTION_LONG_CLICK:
+ // These are the remaining potential actions which Chrome does not implement.
+ case AccessibilityNodeInfo.ACTION_DISMISS:
+ case AccessibilityNodeInfo.ACTION_SELECT:
+ case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION:
+ case WebContentsAccessibilityImpl.ACTION_SCROLL_TO_POSITION:
+ case WebContentsAccessibilityImpl.ACTION_MOVE_WINDOW:
+ case WebContentsAccessibilityImpl.ACTION_SHOW_TOOLTIP:
+ case WebContentsAccessibilityImpl.ACTION_HIDE_TOOLTIP:
+ case WebContentsAccessibilityImpl.ACTION_PRESS_AND_HOLD:
+ default:
+ return "NOT_IMPLEMENTED";
+ }
+ }
+
private String bundleToString(Bundle extras) {
+ // Sort keys to ensure consistent output of tests.
+ List<String> sortedKeySet = new ArrayList<String>(extras.keySet());
+ Collections.sort(sortedKeySet, CASE_INSENSITIVE_ORDER);
+
+ List<String> bundleStrings = new ArrayList<>();
StringBuilder builder = new StringBuilder();
builder.append("[");
- for (String key : extras.keySet()) {
+ for (String key : sortedKeySet) {
// Two Bundle extras are related to bounding boxes, these should be ignored so the
// tests can safely run on varying devices and not be screen-dependent.
if (key.equals(EXTRAS_KEY_UNCLIPPED_TOP) || key.equals(EXTRAS_KEY_UNCLIPPED_BOTTOM)) {
@@ -307,32 +407,42 @@
// Since every node has a few Bundle extras, and some are often empty, we will only
// print non-null and not empty values.
- if (extras.get(key).toString().isEmpty()) {
+ if (extras.get(key) == null || extras.get(key).toString().isEmpty()) {
continue;
}
- builder.append(key).append(" - ").append(extras.get(key).toString()).append(", ");
- }
- // Remove the extra comma for cleanliness
- if (builder.length() > 2) {
- builder.setLength(builder.length() - 2);
- }
+ // For the special case of the supported HTML elements, which prints the same for the
+ // rootWebArea on each test, assert consistency and suppress from results.
+ if (key.equals(EXTRAS_KEY_SUPPORTED_ELEMENTS)) {
+ Assert.assertEquals(
+ PREDICATES_ERROR, ALL_HTML_ELEMENT_PREDICATES, extras.get(key).toString());
+ continue;
+ }
- builder.append("]");
+ // Simplify the key String before printing to make test outputs easier to read.
+ bundleStrings.add(key.replace("AccessibilityNodeInfo.", "") + "=\""
+ + extras.get(key).toString() + "\"");
+ }
+ builder.append(TextUtils.join(", ", bundleStrings)).append("]");
+
return builder.toString();
}
@Test
@SmallTest
+ public void test_annotationRoles() {
+ performAriaTest("annotation-roles.html", "annotation-roles-expected-android-external.txt");
+ }
+
+ @Test
+ @SmallTest
public void test_tableSimple() {
- performTest(
- "table-simple.html", "table-simple-expected-android-external.txt", BASE_FILE_PATH);
+ performHtmlTest("table-simple.html", "table-simple-expected-android-external.txt");
}
@Test
@SmallTest
public void test_clickableScore() {
- performTest("clickable-score.html", "clickable-score-expected-android-external.txt",
- BASE_FILE_PATH);
+ performHtmlTest("clickable-score.html", "clickable-score-expected-android-external.txt");
}
}
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/annotation-roles-expected-android-external.txt b/content/test/data/accessibility/aria/annotation-roles-expected-android-external.txt
new file mode 100644
index 0000000..c30b135
--- /dev/null
+++ b/content/test/data/accessibility/aria/annotation-roles-expected-android-external.txt
@@ -0,0 +1,7 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, A11Y_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"comment" actions:[A11Y_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="comment", roleDescription="comment"]
+++View text:"suggestion" actions:[A11Y_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="suggestion", roleDescription="suggestion"]
+++View actions:[A11Y_FOCUS] bundle:[chromeRole="paragraph"]
+++++TextView text:"This is " actions:[A11Y_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++View text:"highlighted" actions:[A11Y_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="mark", roleDescription="highlight"]
+++++TextView text:"." actions:[A11Y_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/clickable-score-expected-android-external.txt b/content/test/data/accessibility/html/clickable-score-expected-android-external.txt
index 354d75bb..12eaab6 100644
--- a/content/test/data/accessibility/html/clickable-score-expected-android-external.txt
+++ b/content/test/data/accessibility/html/clickable-score-expected-android-external.txt
@@ -1,9 +1,9 @@
-android.webkit.WebView; text: ; enabled focusable focused scrollable visibleToUser; actions: [ACTION_CLEAR_FOCUS, ACTION_ACCESSIBILITY_FOCUS]; bundle: [AccessibilityNodeInfo.chromeRole - rootWebArea, ACTION_ARGUMENT_HTML_ELEMENT_STRING_VALUES - ARTICLE,BUTTON,CHECKBOX,COMBOBOX,CONTROL,FOCUSABLE,FRAME,GRAPHIC,H1,H2,H3,H4,H5,H6,HEADING,LANDMARK,LINK,LIST,LIST_ITEM,MAIN,MEDIA,RADIO,SECTION,TABLE,TEXT_FIELD,UNVISITED_LINK,VISITED_LINK]
-++android.widget.Button; text: High score; clickable enabled focusable visibleToUser; actions: [ACTION_NEXT_AT_MOVEMENT_GRANULARITY, ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, ACTION_FOCUS, ACTION_ACCESSIBILITY_FOCUS, ACTION_CLICK]; bundle: [AccessibilityNodeInfo.roleDescription - button, AccessibilityNodeInfo.clickableScore - 300, AccessibilityNodeInfo.chromeRole - button]
-++android.widget.Button; text: High score; clickable enabled visibleToUser; actions: [ACTION_NEXT_AT_MOVEMENT_GRANULARITY, ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, ACTION_ACCESSIBILITY_FOCUS, ACTION_CLICK]; bundle: [AccessibilityNodeInfo.roleDescription - button, AccessibilityNodeInfo.clickableScore - 300, AccessibilityNodeInfo.chromeRole - button]
-++android.widget.Button; text: No score; clickable enabled visibleToUser; actions: [ACTION_NEXT_AT_MOVEMENT_GRANULARITY, ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, ACTION_ACCESSIBILITY_FOCUS, ACTION_CLICK]; bundle: [AccessibilityNodeInfo.roleDescription - button, AccessibilityNodeInfo.chromeRole - button]
-++android.widget.TextView; text: Medium score; clickable enabled visibleToUser; actions: [ACTION_NEXT_AT_MOVEMENT_GRANULARITY, ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, ACTION_ACCESSIBILITY_FOCUS, ACTION_CLICK]; bundle: [AccessibilityNodeInfo.clickableScore - 200, AccessibilityNodeInfo.chromeRole - genericContainer]
-++android.view.View; text: ; viewIdResName: medium score; clickable enabled visibleToUser; actions: [ACTION_ACCESSIBILITY_FOCUS, ACTION_CLICK]; bundle: [AccessibilityNodeInfo.clickableScore - 200, AccessibilityNodeInfo.chromeRole - genericContainer]
-++++android.widget.TextView; text: Low score; enabled visibleToUser; actions: [ACTION_NEXT_AT_MOVEMENT_GRANULARITY, ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, ACTION_ACCESSIBILITY_FOCUS]; bundle: [AccessibilityNodeInfo.clickableScore - 100, AccessibilityNodeInfo.chromeRole - paragraph]
-++++android.widget.Button; text: High score; clickable enabled visibleToUser; actions: [ACTION_NEXT_AT_MOVEMENT_GRANULARITY, ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, ACTION_ACCESSIBILITY_FOCUS, ACTION_CLICK]; bundle: [AccessibilityNodeInfo.roleDescription - button, AccessibilityNodeInfo.clickableScore - 300, AccessibilityNodeInfo.chromeRole - button]
-++android.widget.TextView; text: No score; enabled visibleToUser; actions: [ACTION_NEXT_AT_MOVEMENT_GRANULARITY, ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, ACTION_ACCESSIBILITY_FOCUS]; bundle: [AccessibilityNodeInfo.chromeRole - paragraph]
\ No newline at end of file
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, A11Y_FOCUS] bundle:[chromeRole="rootWebArea"]
+++Button text:"High score" clickable focusable actions:[FOCUS, CLICK, A11Y_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", clickableScore="300", roleDescription="button"]
+++Button text:"High score" clickable actions:[CLICK, A11Y_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", clickableScore="300", roleDescription="button"]
+++Button text:"No score" clickable actions:[CLICK, A11Y_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="button"]
+++TextView text:"Medium score" clickable actions:[CLICK, A11Y_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer", clickableScore="200"]
+++View viewIdResName:"medium score" clickable actions:[CLICK, A11Y_FOCUS] bundle:[chromeRole="genericContainer", clickableScore="200"]
+++++TextView text:"Low score" actions:[A11Y_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph", clickableScore="100"]
+++++Button text:"High score" clickable actions:[CLICK, A11Y_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", clickableScore="300", roleDescription="button"]
+++TextView text:"No score" actions:[A11Y_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/table-simple-expected-android-external.txt b/content/test/data/accessibility/html/table-simple-expected-android-external.txt
index 59c2a1cf..27096a6a 100644
--- a/content/test/data/accessibility/html/table-simple-expected-android-external.txt
+++ b/content/test/data/accessibility/html/table-simple-expected-android-external.txt
@@ -1,11 +1,11 @@
-android.webkit.WebView; text: Table example; enabled focusable focused scrollable visibleToUser; actions: [ACTION_NEXT_AT_MOVEMENT_GRANULARITY, ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, ACTION_CLEAR_FOCUS, ACTION_ACCESSIBILITY_FOCUS]; bundle: [AccessibilityNodeInfo.chromeRole - rootWebArea, ACTION_ARGUMENT_HTML_ELEMENT_STRING_VALUES - ARTICLE,BUTTON,CHECKBOX,COMBOBOX,CONTROL,FOCUSABLE,FRAME,GRAPHIC,H1,H2,H3,H4,H5,H6,HEADING,LANDMARK,LINK,LIST,LIST_ITEM,MAIN,MEDIA,RADIO,SECTION,TABLE,TEXT_FIELD,UNVISITED_LINK,VISITED_LINK]
-++android.widget.GridView; text: ; enabled visibleToUser; CollectionInfo: [false, 0, 3, 2]; actions: [ACTION_ACCESSIBILITY_FOCUS]; bundle: [AccessibilityNodeInfo.roleDescription - table, AccessibilityNodeInfo.chromeRole - table]
-++++android.view.View; text: ; enabled visibleToUser; actions: [ACTION_ACCESSIBILITY_FOCUS]; bundle: [AccessibilityNodeInfo.chromeRole - row]
-++++++android.view.View; text: Pair; enabled visibleToUser; CollectionItemInfo: [true, false, 0, 1, 0, 1]; actions: [ACTION_NEXT_AT_MOVEMENT_GRANULARITY, ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, ACTION_ACCESSIBILITY_FOCUS]; bundle: [AccessibilityNodeInfo.roleDescription - column header, AccessibilityNodeInfo.chromeRole - columnHeader]
-++++++android.view.View; text: Single; enabled visibleToUser; CollectionItemInfo: [true, false, 0, 1, 1, 1]; actions: [ACTION_NEXT_AT_MOVEMENT_GRANULARITY, ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, ACTION_ACCESSIBILITY_FOCUS]; bundle: [AccessibilityNodeInfo.roleDescription - column header, AccessibilityNodeInfo.chromeRole - columnHeader]
-++++android.view.View; text: ; enabled visibleToUser; actions: [ACTION_ACCESSIBILITY_FOCUS]; bundle: [AccessibilityNodeInfo.chromeRole - row]
-++++++android.view.View; text: AB; enabled visibleToUser; CollectionItemInfo: [false, false, 1, 1, 0, 1]; actions: [ACTION_NEXT_AT_MOVEMENT_GRANULARITY, ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, ACTION_ACCESSIBILITY_FOCUS]; bundle: [AccessibilityNodeInfo.chromeRole - cell]
-++++++android.view.View; text: B; enabled visibleToUser; CollectionItemInfo: [false, false, 1, 1, 1, 1]; actions: [ACTION_NEXT_AT_MOVEMENT_GRANULARITY, ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, ACTION_ACCESSIBILITY_FOCUS]; bundle: [AccessibilityNodeInfo.chromeRole - cell]
-++++android.view.View; text: ; enabled visibleToUser; actions: [ACTION_ACCESSIBILITY_FOCUS]; bundle: [AccessibilityNodeInfo.chromeRole - row]
-++++++android.view.View; text: CD; enabled visibleToUser; CollectionItemInfo: [false, false, 2, 1, 0, 1]; actions: [ACTION_NEXT_AT_MOVEMENT_GRANULARITY, ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, ACTION_ACCESSIBILITY_FOCUS]; bundle: [AccessibilityNodeInfo.chromeRole - cell]
-++++++android.view.View; text: D; enabled visibleToUser; CollectionItemInfo: [false, false, 2, 1, 1, 1]; actions: [ACTION_NEXT_AT_MOVEMENT_GRANULARITY, ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, ACTION_ACCESSIBILITY_FOCUS]; bundle: [AccessibilityNodeInfo.chromeRole - cell]
\ No newline at end of file
+WebView text:"Table example" focusable focused scrollable actions:[CLEAR_FOCUS, A11Y_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="rootWebArea"]
+++GridView CollectionInfo:[rows=3, cols=2] actions:[A11Y_FOCUS] bundle:[chromeRole="table", roleDescription="table"]
+++++View actions:[A11Y_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Pair" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[A11Y_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++++View text:"Single" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[A11Y_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++View actions:[A11Y_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"AB" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[A11Y_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"B" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=1, colSpan=1] actions:[A11Y_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++View actions:[A11Y_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"CD" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=0, colSpan=1] actions:[A11Y_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"D" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=1, colSpan=1] actions:[A11Y_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
\ No newline at end of file