| Index: content/browser/accessibility/dump_accessibility_browsertest_base.cc
|
| diff --git a/content/browser/accessibility/dump_accessibility_browsertest_base.cc b/content/browser/accessibility/dump_accessibility_browsertest_base.cc
|
| index b5dd3296ef3106a32025b15a1a7cf93b335578a6..0b141deaed6b5c5ffaaa7eea2a0a856fc2ad87d0 100644
|
| --- a/content/browser/accessibility/dump_accessibility_browsertest_base.cc
|
| +++ b/content/browser/accessibility/dump_accessibility_browsertest_base.cc
|
| @@ -26,10 +26,15 @@
|
| #include "content/public/common/content_paths.h"
|
| #include "content/public/common/content_switches.h"
|
| #include "content/public/common/url_constants.h"
|
| +#include "content/public/test/browser_test_utils.h"
|
| #include "content/public/test/content_browser_test.h"
|
| #include "content/public/test/content_browser_test_utils.h"
|
| +#include "content/public/test/test_utils.h"
|
| #include "content/shell/browser/shell.h"
|
| #include "content/test/accessibility_browser_test_utils.h"
|
| +#include "content/test/content_browser_test_utils_internal.h"
|
| +#include "net/dns/mock_host_resolver.h"
|
| +#include "net/test/embedded_test_server/embedded_test_server.h"
|
|
|
| namespace content {
|
|
|
| @@ -40,6 +45,55 @@ const char kMarkSkipFile[] = "#<skip";
|
| const char kMarkEndOfFile[] = "<-- End-of-file -->";
|
| const char kSignalDiff[] = "*";
|
|
|
| +// Helper function to be used with FrameTree::ForEach, so that
|
| +// AccessibilityNotificationWaiter can listen for accessibility
|
| +// events in all frames.
|
| +bool ListenToFrame(AccessibilityNotificationWaiter* waiter,
|
| + FrameTreeNode* frame_tree_node) {
|
| + waiter->ListenToAdditionalFrame(frame_tree_node->current_frame_host());
|
| + return true;
|
| +}
|
| +
|
| +// Helper function to be used with FrameTree::ForEach, to get the
|
| +// url of all frames.
|
| +//
|
| +// Ignore about:blank urls because of the case where a parent frame A
|
| +// has a child iframe B and it writes to the document using
|
| +// contentDocument.open() on the child frame B.
|
| +//
|
| +// In this scenario, B's contentWindow.location.href matches A's url,
|
| +// but B's url in the browser frame tree is still "about:blank".
|
| +bool GetFrameUrl(std::vector<std::string>* all_frame_urls,
|
| + FrameTreeNode* frame_tree_node) {
|
| + std::string url = frame_tree_node->current_url().spec();
|
| + if (url != url::kAboutBlankURL)
|
| + all_frame_urls->push_back(url);
|
| + return true;
|
| +}
|
| +
|
| +// Searches recursively and returns true if an accessibility node is found
|
| +// that represents a fully loaded web document with the given url.
|
| +bool AccessibilityTreeContainsLoadedDocWithUrl(BrowserAccessibility* node,
|
| + const std::string& url) {
|
| + if ((node->GetRole() == ui::AX_ROLE_WEB_AREA ||
|
| + node->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA) &&
|
| + node->GetStringAttribute(ui::AX_ATTR_URL) == url) {
|
| + // If possible, ensure the doc has finished loading. That's currently
|
| + // not possible with same-process iframes until https://crbug.com/532249
|
| + // is fixed.
|
| + return (node->manager()->GetTreeData().url != url ||
|
| + node->manager()->GetTreeData().loaded);
|
| + }
|
| +
|
| + for (unsigned i = 0; i < node->PlatformChildCount(); i++) {
|
| + if (AccessibilityTreeContainsLoadedDocWithUrl(
|
| + node->PlatformGetChild(i), url)) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| } // namespace
|
|
|
| typedef AccessibilityTreeFormatter::Filter Filter;
|
| @@ -50,6 +104,17 @@ DumpAccessibilityTestBase::DumpAccessibilityTestBase() {
|
| DumpAccessibilityTestBase::~DumpAccessibilityTestBase() {
|
| }
|
|
|
| +void DumpAccessibilityTestBase::SetUpCommandLine(
|
| + base::CommandLine* command_line) {
|
| + IsolateAllSitesForTesting(command_line);
|
| +}
|
| +
|
| +void DumpAccessibilityTestBase::SetUpOnMainThread() {
|
| + host_resolver()->AddRule("*", "127.0.0.1");
|
| + ASSERT_TRUE(embedded_test_server()->Start());
|
| + SetupCrossSiteRedirector(embedded_test_server());
|
| +}
|
| +
|
| base::string16
|
| DumpAccessibilityTestBase::DumpUnfilteredAccessibilityTreeAsString() {
|
| scoped_ptr<AccessibilityTreeFormatter> formatter(
|
| @@ -95,7 +160,7 @@ std::vector<int> DumpAccessibilityTestBase::DiffLines(
|
| void DumpAccessibilityTestBase::ParseHtmlForExtraDirectives(
|
| const std::string& test_html,
|
| std::vector<Filter>* filters,
|
| - std::string* wait_for) {
|
| + std::vector<std::string>* wait_for) {
|
| for (const std::string& line :
|
| base::SplitString(test_html, "\n", base::TRIM_WHITESPACE,
|
| base::SPLIT_WANT_ALL)) {
|
| @@ -120,7 +185,7 @@ void DumpAccessibilityTestBase::ParseHtmlForExtraDirectives(
|
| Filter::DENY));
|
| } else if (base::StartsWith(line, wait_str,
|
| base::CompareCase::SENSITIVE)) {
|
| - *wait_for = line.substr(wait_str.size());
|
| + wait_for->push_back(line.substr(wait_str.size()));
|
| }
|
| }
|
| }
|
| @@ -193,42 +258,83 @@ void DumpAccessibilityTestBase::RunTestForPlatform(
|
| }
|
|
|
| // Parse filters and other directives in the test file.
|
| - std::string wait_for;
|
| + std::vector<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(file_dir, file_path.BaseName().MaybeAsASCII().c_str());
|
| -
|
| - // 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.
|
| + // Load the test html and wait for the "load complete" AX event.
|
| + GURL url(embedded_test_server()->GetURL(
|
| + "/" + std::string(file_dir) + "/" + file_path.BaseName().MaybeAsASCII()));
|
| + AccessibilityNotificationWaiter accessibility_waiter(
|
| + shell(),
|
| + AccessibilityModeComplete,
|
| + ui::AX_EVENT_LOAD_COMPLETE);
|
| NavigateToURL(shell(), url);
|
| + accessibility_waiter.WaitForNotification();
|
| +
|
| + // Get the url of every frame in the frame tree.
|
| + WebContentsImpl* web_contents = static_cast<WebContentsImpl*>(
|
| + shell()->web_contents());
|
| + FrameTree* frame_tree = web_contents->GetFrameTree();
|
| + std::vector<std::string> all_frame_urls;
|
| + frame_tree->ForEach(base::Bind(GetFrameUrl, &all_frame_urls));
|
| +
|
| + // Wait for the accessibility tree to fully load for all frames,
|
| + // by searching for the WEB_AREA node in the accessibility tree
|
| + // with the url of each frame in our frame tree. Note that this
|
| + // doesn't support cases where there are two iframes with the
|
| + // exact same url. If all frames haven't loaded yet, set up a
|
| + // listener for accessibility events on any frame and block
|
| + // until the next one is received.
|
| + //
|
| + // If the original page has a @WAIT-FOR directive, don't break until
|
| + // the text we're waiting for appears in the full text dump of the
|
| + // accessibility tree, either.
|
| + for (;;) {
|
| + VLOG(1) << "Top of loop";
|
| + RenderFrameHostImpl* main_frame = static_cast<RenderFrameHostImpl*>(
|
| + web_contents->GetMainFrame());
|
| + BrowserAccessibilityManager* manager =
|
| + main_frame->browser_accessibility_manager();
|
| + if (manager) {
|
| + BrowserAccessibility* accessibility_root =
|
| + manager->GetRoot();
|
| +
|
| + // Check to see if all frames have loaded.
|
| + bool all_frames_loaded = true;
|
| + for (const auto& url : all_frame_urls) {
|
| + if (!AccessibilityTreeContainsLoadedDocWithUrl(
|
| + accessibility_root, url)) {
|
| + VLOG(1) << "Still waiting on this frame to load: " << url;
|
| + all_frames_loaded = false;
|
| + break;
|
| + }
|
| + }
|
|
|
| - // 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.
|
| - do {
|
| - waiter->WaitForNotification();
|
| - if (!wait_for.empty()) {
|
| + // Check to see if the @WAIT-FOR text has appeared yet.
|
| + bool all_wait_for_strings_found = true;
|
| base::string16 tree_dump = DumpUnfilteredAccessibilityTreeAsString();
|
| - if (base::UTF16ToUTF8(tree_dump).find(wait_for) != std::string::npos)
|
| - wait_for.clear();
|
| + for (const auto& str : wait_for) {
|
| + if (base::UTF16ToUTF8(tree_dump).find(str) == std::string::npos) {
|
| + VLOG(1) << "Still waiting on this text to be found: " << str;
|
| + all_wait_for_strings_found = false;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + // If all frames have loaded and the @WAIT-FOR text has appeared,
|
| + // we're done.
|
| + if (all_frames_loaded && all_wait_for_strings_found)
|
| + break;
|
| }
|
| - } while (!wait_for.empty());
|
| +
|
| + // Block until the next accessibility notification in any frame.
|
| + VLOG(1) << "Waiting until the next accessibility event";
|
| + AccessibilityNotificationWaiter accessibility_waiter(main_frame,
|
| + ui::AX_EVENT_NONE);
|
| + frame_tree->ForEach(base::Bind(ListenToFrame, &accessibility_waiter));
|
| + accessibility_waiter.WaitForNotification();
|
| + }
|
|
|
| // Call the subclass to dump the output.
|
| std::vector<std::string> actual_lines = Dump();
|
|
|