diff options
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> |