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(); |