summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-24 10:41:02 +0000
committerdmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-24 10:41:02 +0000
commitd77baf89f9ad0ca243aae70c623e2783deb04f76 (patch)
treeae831207de7ff110e902eaf8613735c20715b5b0
parent8329185f195615db8cbcb38c331802bc2a16aae4 (diff)
downloadchromium_src-d77baf89f9ad0ca243aae70c623e2783deb04f76.zip
chromium_src-d77baf89f9ad0ca243aae70c623e2783deb04f76.tar.gz
chromium_src-d77baf89f9ad0ca243aae70c623e2783deb04f76.tar.bz2
Correctly update the bounds of objects in the accessibility tree.
Fixes regression that happened when moving to the new AXTree code. Improves on it by only sending location changes when there's an AXLayoutChanged event, since that will always be fired when there's a layout and objects might have moved. To make it testable, adds a new feature to DumpAccessibilityTree tests where you can make it wait until a certain magic string appears in the dump before the dump is diffed with the expectation. In this test, we start a CSS transition that animates changing a button's size, and make sure the final dump reflects the final size. BUG=268296 Review URL: https://codereview.chromium.org/401643003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@285174 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--content/browser/accessibility/dump_accessibility_tree_browsertest.cc80
-rw-r--r--content/renderer/accessibility/renderer_accessibility_complete.cc29
-rw-r--r--content/test/accessibility_browser_test_utils.cc6
-rw-r--r--content/test/accessibility_browser_test_utils.h1
-rw-r--r--content/test/data/accessibility/transition-expected-android.txt5
-rw-r--r--content/test/data/accessibility/transition-expected-mac.txt4
-rw-r--r--content/test/data/accessibility/transition-expected-win.txt4
-rw-r--r--content/test/data/accessibility/transition.html58
8 files changed, 163 insertions, 24 deletions
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 543b06c..de73995 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -93,8 +93,27 @@ class DumpAccessibilityTreeTest : public ContentBrowserTest {
filters->push_back(Filter(base::ASCIIToUTF16("*=''"), Filter::DENY));
}
- void ParseFilters(const std::string& test_html,
- std::vector<Filter>* filters) {
+ // Parse the test html file and parse special directives, usually
+ // beginning with an '@' and inside an HTML comment, that control how the
+ // test is run and how the results are interpreted.
+ //
+ // When the accessibility tree is dumped as text, each attribute is
+ // run through filters before being appended to the string. An "allow"
+ // filter specifies attribute strings that should be dumped, and a "deny"
+ // filter specifies strings that should be suppressed. As an example,
+ // @MAC-ALLOW:AXSubrole=* means that the AXSubrole attribute should be
+ // printed, while @MAC-ALLOW:AXSubrole=AXList* means that any subrole
+ // beginning with the text "AXList" should be printed.
+ //
+ // The @WAIT-FOR:text directive allows the test to specify that the document
+ // may dynamically change after initial load, and the test is to wait
+ // until the given string (e.g., "text") appears in the resulting dump.
+ // A test can make some changes to the document, then append a magic string
+ // indicating that the test is done, and this framework will wait for that
+ // string to appear before comparing the results.
+ void ParseHtmlForExtraDirectives(const std::string& test_html,
+ std::vector<Filter>* filters,
+ std::string* wait_for) {
std::vector<std::string> lines;
base::SplitString(test_html, '\n', &lines);
for (std::vector<std::string>::const_iterator iter = lines.begin();
@@ -107,6 +126,7 @@ class DumpAccessibilityTreeTest : public ContentBrowserTest {
AccessibilityTreeFormatter::GetAllowString();
const std::string& deny_str =
AccessibilityTreeFormatter::GetDenyString();
+ const std::string& wait_str = "@WAIT-FOR:";
if (StartsWithASCII(line, allow_empty_str, true)) {
filters->push_back(
Filter(base::UTF8ToUTF16(line.substr(allow_empty_str.size())),
@@ -119,6 +139,8 @@ class DumpAccessibilityTreeTest : public ContentBrowserTest {
filters->push_back(Filter(base::UTF8ToUTF16(
line.substr(deny_str.size())),
Filter::DENY));
+ } else if (StartsWithASCII(line, wait_str, true)) {
+ *wait_for = line.substr(wait_str.size());
}
}
}
@@ -170,32 +192,52 @@ void DumpAccessibilityTreeTest::RunTest(
return;
}
+ // Parse filters and other directives in the test file.
+ std::vector<Filter> filters;
+ std::string wait_for;
+ AddDefaultFilters(&filters);
+ ParseHtmlForExtraDirectives(html_contents, &filters, &wait_for);
+
// Load the page.
base::string16 html_contents16;
html_contents16 = base::UTF8ToUTF16(html_contents);
GURL url = GetTestUrl("accessibility",
html_file.BaseName().MaybeAsASCII().c_str());
- AccessibilityNotificationWaiter waiter(
- shell(), AccessibilityModeComplete,
- ui::AX_EVENT_LOAD_COMPLETE);
+
+ // If there's a @WAIT-FOR directive, set up an accessibility notification
+ // waiter that returns on any event; we'll stop when we get the text we're
+ // waiting for, or time out. Otherwise just wait specifically for
+ // the "load complete" event.
+ scoped_ptr<AccessibilityNotificationWaiter> waiter;
+ if (!wait_for.empty()) {
+ waiter.reset(new AccessibilityNotificationWaiter(
+ shell(), AccessibilityModeComplete, ui::AX_EVENT_NONE));
+ } else {
+ waiter.reset(new AccessibilityNotificationWaiter(
+ shell(), AccessibilityModeComplete, ui::AX_EVENT_LOAD_COMPLETE));
+ }
+
+ // Load the test html.
NavigateToURL(shell(), url);
- waiter.WaitForNotification();
+ // Wait for notifications. If there's a @WAIT-FOR directive, break when
+ // the text we're waiting for appears in the dump, otherwise break after
+ // the first notification, which will be a load complete.
RenderWidgetHostViewBase* host_view = static_cast<RenderWidgetHostViewBase*>(
shell()->web_contents()->GetRenderWidgetHostView());
- AccessibilityTreeFormatter formatter(
- host_view->GetBrowserAccessibilityManager()->GetRoot());
-
- // Parse filters in the test file.
- std::vector<Filter> filters;
- AddDefaultFilters(&filters);
- ParseFilters(html_contents, &filters);
- formatter.SetFilters(filters);
+ std::string actual_contents;
+ do {
+ waiter->WaitForNotification();
+ base::string16 actual_contents_utf16;
+ AccessibilityTreeFormatter formatter(
+ host_view->GetBrowserAccessibilityManager()->GetRoot());
+ formatter.SetFilters(filters);
+ formatter.FormatAccessibilityTree(&actual_contents_utf16);
+ actual_contents = base::UTF16ToUTF8(actual_contents_utf16);
+ } while (!wait_for.empty() &&
+ actual_contents.find(wait_for) == std::string::npos);
// Perform a diff (or write the initial baseline).
- base::string16 actual_contents_utf16;
- formatter.FormatAccessibilityTree(&actual_contents_utf16);
- std::string actual_contents = base::UTF16ToUTF8(actual_contents_utf16);
std::vector<std::string> actual_lines, expected_lines;
Tokenize(actual_contents, "\n", &actual_lines);
Tokenize(expected_contents, "\n", &expected_lines);
@@ -530,6 +572,10 @@ IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityTableSpans) {
RunTest(FILE_PATH_LITERAL("table-spans.html"));
}
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityTransition) {
+ RunTest(FILE_PATH_LITERAL("transition.html"));
+}
+
IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
AccessibilityToggleButton) {
RunTest(FILE_PATH_LITERAL("togglebutton.html"));
diff --git a/content/renderer/accessibility/renderer_accessibility_complete.cc b/content/renderer/accessibility/renderer_accessibility_complete.cc
index 2e72dde..e7765d4 100644
--- a/content/renderer/accessibility/renderer_accessibility_complete.cc
+++ b/content/renderer/accessibility/renderer_accessibility_complete.cc
@@ -177,16 +177,21 @@ void RendererAccessibilityComplete::SendPendingAccessibilityEvents() {
// Generate an event message from each Blink event.
std::vector<AccessibilityHostMsg_EventParams> event_msgs;
+ // If there's a layout complete message, we need to send location changes.
+ bool had_layout_complete_messages = false;
+
// Loop over each event and generate an updated event message.
for (size_t i = 0; i < src_events.size(); ++i) {
- AccessibilityHostMsg_EventParams& event =
- src_events[i];
+ AccessibilityHostMsg_EventParams& event = src_events[i];
+ if (event.event_type == ui::AX_EVENT_LAYOUT_COMPLETE)
+ had_layout_complete_messages = true;
+
+ WebAXObject obj = document.accessibilityObjectFromID(event.id);
- WebAXObject obj = document.accessibilityObjectFromID(
- event.id);
// Make sure the object still exists.
if (!obj.updateBackingStoreAndCheckValidity())
continue;
+
// Make sure it's a descendant of our root node - exceptions include the
// scroll area that's the parent of the main document (we ignore it), and
// possibly nodes attached to a different document.
@@ -207,6 +212,13 @@ void RendererAccessibilityComplete::SendPendingAccessibilityEvents() {
serializer_.SerializeChanges(obj, &event_msg.update);
event_msgs.push_back(event_msg);
+ // For each node in the update, set the location in our map from
+ // ids to locations.
+ for (size_t i = 0; i < event_msg.update.nodes.size(); ++i) {
+ locations_[event_msg.update.nodes[i].id] =
+ event_msg.update.nodes[i].location;
+ }
+
VLOG(0) << "Accessibility event: " << ui::ToString(event.event_type)
<< " on node id " << event_msg.id
<< "\n" << event_msg.update.ToString();
@@ -214,7 +226,8 @@ void RendererAccessibilityComplete::SendPendingAccessibilityEvents() {
Send(new AccessibilityHostMsg_Events(routing_id(), event_msgs));
- SendLocationChanges();
+ if (had_layout_complete_messages)
+ SendLocationChanges();
}
void RendererAccessibilityComplete::SendLocationChanges() {
@@ -246,6 +259,12 @@ void RendererAccessibilityComplete::SendLocationChanges() {
// Save the new location.
new_locations[id] = new_location;
+
+ // Explore children of this object.
+ std::vector<blink::WebAXObject> children;
+ tree_source_.GetChildren(obj, &children);
+ for (size_t i = 0; i < children.size(); ++i)
+ objs_to_explore.push(children[i]);
}
locations_.swap(new_locations);
diff --git a/content/test/accessibility_browser_test_utils.cc b/content/test/accessibility_browser_test_utils.cc
index ba0331c..deb3deb 100644
--- a/content/test/accessibility_browser_test_utils.cc
+++ b/content/test/accessibility_browser_test_utils.cc
@@ -56,6 +56,10 @@ AccessibilityNotificationWaiter::~AccessibilityNotificationWaiter() {
void AccessibilityNotificationWaiter::WaitForNotification() {
loop_runner_->Run();
+
+ // Each loop runner can only be called once. Create a new one in case
+ // the caller wants to call this again to wait for the next notification.
+ loop_runner_ = new MessageLoopRunner();
}
const ui::AXTree& AccessibilityNotificationWaiter::GetAXTree() const {
@@ -64,7 +68,7 @@ const ui::AXTree& AccessibilityNotificationWaiter::GetAXTree() const {
void AccessibilityNotificationWaiter::OnAccessibilityEvent(
ui::AXEvent event_type, int event_target_id) {
- if (!IsAboutBlank() && (event_to_wait_for_ == ui::AX_EVENT_NONE ||
+ if (!IsAboutBlank() && (event_to_wait_for_ == ui::AX_EVENT_NONE ||
event_to_wait_for_ == event_type)) {
event_target_id_ = event_target_id;
loop_runner_->Quit();
diff --git a/content/test/accessibility_browser_test_utils.h b/content/test/accessibility_browser_test_utils.h
index 5111e3c..3c134ea 100644
--- a/content/test/accessibility_browser_test_utils.h
+++ b/content/test/accessibility_browser_test_utils.h
@@ -24,7 +24,6 @@ class Shell;
class AccessibilityNotificationWaiter {
public:
explicit AccessibilityNotificationWaiter(Shell* shell);
-
AccessibilityNotificationWaiter(
Shell* shell,
AccessibilityMode accessibility_mode,
diff --git a/content/test/data/accessibility/transition-expected-android.txt b/content/test/data/accessibility/transition-expected-android.txt
new file mode 100644
index 0000000..37ef40e
--- /dev/null
+++ b/content/test/data/accessibility/transition-expected-android.txt
@@ -0,0 +1,5 @@
+#<skip - Android doesn't output the location and size of objects.>
+android.webkit.WebView focusable scrollable
+ android.view.View
+ android.widget.Button clickable focusable focused name='GrowButton'
+ android.view.View clickable name='Done'
diff --git a/content/test/data/accessibility/transition-expected-mac.txt b/content/test/data/accessibility/transition-expected-mac.txt
new file mode 100644
index 0000000..f2043b1
--- /dev/null
+++ b/content/test/data/accessibility/transition-expected-mac.txt
@@ -0,0 +1,4 @@
+AXWebArea
+ AXGroup
+ AXButton AXTitle='GrowButton' size=(600, 300)
+ AXStaticText AXValue='Done'
diff --git a/content/test/data/accessibility/transition-expected-win.txt b/content/test/data/accessibility/transition-expected-win.txt
new file mode 100644
index 0000000..743cb39
--- /dev/null
+++ b/content/test/data/accessibility/transition-expected-win.txt
@@ -0,0 +1,4 @@
+ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
+ IA2_ROLE_SECTION READONLY
+ ROLE_SYSTEM_PUSHBUTTON name='GrowButton' FOCUSABLE size=(600, 300)
+ ROLE_SYSTEM_STATICTEXT name='Done'
diff --git a/content/test/data/accessibility/transition.html b/content/test/data/accessibility/transition.html
new file mode 100644
index 0000000..df42c6a
--- /dev/null
+++ b/content/test/data/accessibility/transition.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<!--
+This tests that location changes are sent when an element animates
+using CSS transitions. The test animates the size of a button when
+focused, then adds the magic text "Done" to the document when
+the transition finishes. The WAIT-FOR directive below instructs
+the test framework to keep waiting for accessibility events and
+not diff the dump against the expectations until the text "Done"
+appears in the dump.
+
+@MAC-ALLOW:size=(400, 200)
+@MAC-ALLOW:size=(600, 300)
+@WIN-ALLOW:size=(400, 200)
+@WIN-ALLOW:size=(600, 300)
+@WAIT-FOR:Done
+-->
+<html>
+<head>
+<style>
+body {
+ width: 800px;
+ height: 600px;
+ margin: 0;
+ padding: 0;
+ border: 0;
+ overflow: hidden;
+}
+#growbutton {
+ width: 400px;
+ height: 200px;
+ margin: 0;
+ padding: 0;
+}
+#growbutton:focus {
+ width: 600px;
+ height: 300px;
+ transition: all 0.1s ease-in-out;
+}
+</style>
+</head>
+<body>
+
+<button id="growbutton">GrowButton</button>
+
+<script>
+ var growButton = document.getElementById('growbutton');
+ var done = false;
+ growButton.addEventListener('webkitTransitionEnd', function() {
+ if (!done) {
+ document.body.appendChild(document.createTextNode('Done'));
+ done = true;
+ }
+ }, false);
+ growButton.focus();
+</script>
+
+</body>
+</html>