Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/accessibility/dump_accessibility_browsertest_base.h" | 5 #include "content/browser/accessibility/dump_accessibility_browsertest_base.h" |
| 6 | 6 |
| 7 #include <set> | 7 #include <set> |
| 8 #include <string> | 8 #include <string> |
| 9 #include <vector> | 9 #include <vector> |
| 10 | 10 |
| 11 #include "base/command_line.h" | 11 #include "base/command_line.h" |
| 12 #include "base/path_service.h" | 12 #include "base/path_service.h" |
| 13 #include "base/strings/string16.h" | 13 #include "base/strings/string16.h" |
| 14 #include "base/strings/string_split.h" | 14 #include "base/strings/string_split.h" |
| 15 #include "base/strings/string_util.h" | 15 #include "base/strings/string_util.h" |
| 16 #include "base/strings/stringprintf.h" | 16 #include "base/strings/stringprintf.h" |
| 17 #include "base/strings/utf_string_conversions.h" | 17 #include "base/strings/utf_string_conversions.h" |
| 18 #include "build/build_config.h" | 18 #include "build/build_config.h" |
| 19 #include "content/browser/accessibility/accessibility_tree_formatter.h" | 19 #include "content/browser/accessibility/accessibility_tree_formatter.h" |
| 20 #include "content/browser/accessibility/accessibility_tree_formatter_blink.h" | 20 #include "content/browser/accessibility/accessibility_tree_formatter_blink.h" |
| 21 #include "content/browser/accessibility/browser_accessibility.h" | 21 #include "content/browser/accessibility/browser_accessibility.h" |
| 22 #include "content/browser/accessibility/browser_accessibility_manager.h" | 22 #include "content/browser/accessibility/browser_accessibility_manager.h" |
| 23 #include "content/browser/accessibility/browser_accessibility_state_impl.h" | 23 #include "content/browser/accessibility/browser_accessibility_state_impl.h" |
| 24 #include "content/browser/web_contents/web_contents_impl.h" | 24 #include "content/browser/web_contents/web_contents_impl.h" |
| 25 #include "content/public/browser/web_contents.h" | 25 #include "content/public/browser/web_contents.h" |
| 26 #include "content/public/common/content_paths.h" | 26 #include "content/public/common/content_paths.h" |
| 27 #include "content/public/common/content_switches.h" | 27 #include "content/public/common/content_switches.h" |
| 28 #include "content/public/common/url_constants.h" | 28 #include "content/public/common/url_constants.h" |
| 29 #include "content/public/test/browser_test_utils.h" | |
| 29 #include "content/public/test/content_browser_test.h" | 30 #include "content/public/test/content_browser_test.h" |
| 30 #include "content/public/test/content_browser_test_utils.h" | 31 #include "content/public/test/content_browser_test_utils.h" |
| 32 #include "content/public/test/test_utils.h" | |
| 31 #include "content/shell/browser/shell.h" | 33 #include "content/shell/browser/shell.h" |
| 32 #include "content/test/accessibility_browser_test_utils.h" | 34 #include "content/test/accessibility_browser_test_utils.h" |
| 35 #include "content/test/content_browser_test_utils_internal.h" | |
| 36 #include "net/dns/mock_host_resolver.h" | |
| 37 #include "net/test/embedded_test_server/embedded_test_server.h" | |
| 33 | 38 |
| 34 namespace content { | 39 namespace content { |
| 35 | 40 |
| 36 namespace { | 41 namespace { |
| 37 | 42 |
| 38 const char kCommentToken = '#'; | 43 const char kCommentToken = '#'; |
| 39 const char kMarkSkipFile[] = "#<skip"; | 44 const char kMarkSkipFile[] = "#<skip"; |
| 40 const char kMarkEndOfFile[] = "<-- End-of-file -->"; | 45 const char kMarkEndOfFile[] = "<-- End-of-file -->"; |
| 41 const char kSignalDiff[] = "*"; | 46 const char kSignalDiff[] = "*"; |
| 42 | 47 |
| 48 // Helper function to be used with FrameTree::ForEach, so that | |
| 49 // AccessibilityNotificationWaiter can listen for accessibility | |
| 50 // events in all frames. | |
| 51 bool ListenToFrame(AccessibilityNotificationWaiter* waiter, | |
| 52 FrameTreeNode* frame_tree_node) { | |
| 53 waiter->ListenToAdditionalFrame(frame_tree_node->current_frame_host()); | |
| 54 return true; | |
| 55 } | |
| 56 | |
| 57 // Helper function to be used with FrameTree::ForEach, to get the | |
| 58 // url of all frames. | |
| 59 bool GetFrameUrl(std::vector<std::string>* all_frame_urls, | |
| 60 FrameTreeNode* frame_tree_node) { | |
| 61 all_frame_urls->push_back(frame_tree_node->current_url().spec()); | |
| 62 return true; | |
| 63 } | |
| 64 | |
| 65 // Searches recursively and returns true if an accessibility node is found | |
| 66 // that represents a fully loaded web document with the given url. | |
| 67 bool AccessibilityTreeContainsLoadedDocWithUrl(BrowserAccessibility* node, | |
| 68 const std::string& url) { | |
| 69 if ((node->GetRole() == ui::AX_ROLE_WEB_AREA || | |
| 70 node->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA) && | |
| 71 node->GetStringAttribute(ui::AX_ATTR_URL) == url) { | |
| 72 // If possible, ensure the doc has finished loading. That's currently | |
| 73 // not possible with same-process iframes until http://crbug.com/532249 | |
|
nasko
2016/01/08 22:57:45
nit: httpS ;)
dmazzoni
2016/01/11 19:04:41
Done.
| |
| 74 // is fixed. | |
| 75 return (node->manager()->GetTreeData().url != url || | |
| 76 node->manager()->GetTreeData().loaded); | |
| 77 } | |
| 78 | |
| 79 for (unsigned i = 0; i < node->PlatformChildCount(); i++) { | |
| 80 if (AccessibilityTreeContainsLoadedDocWithUrl( | |
| 81 node->PlatformGetChild(i), url)) { | |
| 82 return true; | |
| 83 } | |
| 84 } | |
| 85 return false; | |
| 86 } | |
| 87 | |
| 43 } // namespace | 88 } // namespace |
| 44 | 89 |
| 45 typedef AccessibilityTreeFormatter::Filter Filter; | 90 typedef AccessibilityTreeFormatter::Filter Filter; |
| 46 | 91 |
| 47 DumpAccessibilityTestBase::DumpAccessibilityTestBase() { | 92 DumpAccessibilityTestBase::DumpAccessibilityTestBase() { |
| 48 } | 93 } |
| 49 | 94 |
| 50 DumpAccessibilityTestBase::~DumpAccessibilityTestBase() { | 95 DumpAccessibilityTestBase::~DumpAccessibilityTestBase() { |
| 51 } | 96 } |
| 52 | 97 |
| 98 void DumpAccessibilityTestBase::SetUpCommandLine( | |
| 99 base::CommandLine* command_line) { | |
| 100 IsolateAllSitesForTesting(command_line); | |
| 101 } | |
| 102 | |
| 103 void DumpAccessibilityTestBase::SetUpOnMainThread() { | |
| 104 host_resolver()->AddRule("*", "127.0.0.1"); | |
| 105 ASSERT_TRUE(embedded_test_server()->Start()); | |
| 106 SetupCrossSiteRedirector(embedded_test_server()); | |
| 107 } | |
| 108 | |
| 53 base::string16 | 109 base::string16 |
| 54 DumpAccessibilityTestBase::DumpUnfilteredAccessibilityTreeAsString() { | 110 DumpAccessibilityTestBase::DumpUnfilteredAccessibilityTreeAsString() { |
| 55 scoped_ptr<AccessibilityTreeFormatter> formatter( | 111 scoped_ptr<AccessibilityTreeFormatter> formatter( |
| 56 CreateAccessibilityTreeFormatter()); | 112 CreateAccessibilityTreeFormatter()); |
| 57 std::vector<Filter> filters; | 113 std::vector<Filter> filters; |
| 58 filters.push_back(Filter(base::ASCIIToUTF16("*"), Filter::ALLOW)); | 114 filters.push_back(Filter(base::ASCIIToUTF16("*"), Filter::ALLOW)); |
| 59 formatter->SetFilters(filters); | 115 formatter->SetFilters(filters); |
| 60 formatter->set_show_ids(true); | 116 formatter->set_show_ids(true); |
| 61 WebContentsImpl* web_contents = static_cast<WebContentsImpl*>( | 117 WebContentsImpl* web_contents = static_cast<WebContentsImpl*>( |
| 62 shell()->web_contents()); | 118 shell()->web_contents()); |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 88 ++j; | 144 ++j; |
| 89 } | 145 } |
| 90 | 146 |
| 91 // Actual file has been fully checked. | 147 // Actual file has been fully checked. |
| 92 return diff_lines; | 148 return diff_lines; |
| 93 } | 149 } |
| 94 | 150 |
| 95 void DumpAccessibilityTestBase::ParseHtmlForExtraDirectives( | 151 void DumpAccessibilityTestBase::ParseHtmlForExtraDirectives( |
| 96 const std::string& test_html, | 152 const std::string& test_html, |
| 97 std::vector<Filter>* filters, | 153 std::vector<Filter>* filters, |
| 98 std::string* wait_for) { | 154 std::vector<std::string>* wait_for) { |
| 99 for (const std::string& line : | 155 for (const std::string& line : |
| 100 base::SplitString(test_html, "\n", base::TRIM_WHITESPACE, | 156 base::SplitString(test_html, "\n", base::TRIM_WHITESPACE, |
| 101 base::SPLIT_WANT_ALL)) { | 157 base::SPLIT_WANT_ALL)) { |
| 102 const std::string& allow_empty_str = formatter_->GetAllowEmptyString(); | 158 const std::string& allow_empty_str = formatter_->GetAllowEmptyString(); |
| 103 const std::string& allow_str = formatter_->GetAllowString(); | 159 const std::string& allow_str = formatter_->GetAllowString(); |
| 104 const std::string& deny_str = formatter_->GetDenyString(); | 160 const std::string& deny_str = formatter_->GetDenyString(); |
| 105 const std::string& wait_str = "@WAIT-FOR:"; | 161 const std::string& wait_str = "@WAIT-FOR:"; |
| 106 if (base::StartsWith(line, allow_empty_str, | 162 if (base::StartsWith(line, allow_empty_str, |
| 107 base::CompareCase::SENSITIVE)) { | 163 base::CompareCase::SENSITIVE)) { |
| 108 filters->push_back( | 164 filters->push_back( |
| 109 Filter(base::UTF8ToUTF16(line.substr(allow_empty_str.size())), | 165 Filter(base::UTF8ToUTF16(line.substr(allow_empty_str.size())), |
| 110 Filter::ALLOW_EMPTY)); | 166 Filter::ALLOW_EMPTY)); |
| 111 } else if (base::StartsWith(line, allow_str, | 167 } else if (base::StartsWith(line, allow_str, |
| 112 base::CompareCase::SENSITIVE)) { | 168 base::CompareCase::SENSITIVE)) { |
| 113 filters->push_back(Filter(base::UTF8ToUTF16( | 169 filters->push_back(Filter(base::UTF8ToUTF16( |
| 114 line.substr(allow_str.size())), | 170 line.substr(allow_str.size())), |
| 115 Filter::ALLOW)); | 171 Filter::ALLOW)); |
| 116 } else if (base::StartsWith(line, deny_str, | 172 } else if (base::StartsWith(line, deny_str, |
| 117 base::CompareCase::SENSITIVE)) { | 173 base::CompareCase::SENSITIVE)) { |
| 118 filters->push_back(Filter(base::UTF8ToUTF16( | 174 filters->push_back(Filter(base::UTF8ToUTF16( |
| 119 line.substr(deny_str.size())), | 175 line.substr(deny_str.size())), |
| 120 Filter::DENY)); | 176 Filter::DENY)); |
| 121 } else if (base::StartsWith(line, wait_str, | 177 } else if (base::StartsWith(line, wait_str, |
| 122 base::CompareCase::SENSITIVE)) { | 178 base::CompareCase::SENSITIVE)) { |
| 123 *wait_for = line.substr(wait_str.size()); | 179 wait_for->push_back(line.substr(wait_str.size())); |
| 124 } | 180 } |
| 125 } | 181 } |
| 126 } | 182 } |
| 127 | 183 |
| 128 AccessibilityTreeFormatter* | 184 AccessibilityTreeFormatter* |
| 129 DumpAccessibilityTestBase::CreateAccessibilityTreeFormatter() { | 185 DumpAccessibilityTestBase::CreateAccessibilityTreeFormatter() { |
| 130 if (is_blink_pass_) | 186 if (is_blink_pass_) |
| 131 return new AccessibilityTreeFormatterBlink(); | 187 return new AccessibilityTreeFormatterBlink(); |
| 132 else | 188 else |
| 133 return AccessibilityTreeFormatter::Create(); | 189 return AccessibilityTreeFormatter::Create(); |
| 134 } | 190 } |
| 135 | 191 |
| 136 void DumpAccessibilityTestBase::RunTest( | 192 void DumpAccessibilityTestBase::RunTest( |
| 137 const base::FilePath file_path, const char* file_dir) { | 193 const base::FilePath file_path, const char* file_dir) { |
| 138 #if !defined(OS_ANDROID) | 194 #if !defined(OS_ANDROID) |
| 139 // The blink tree is different on Android because we exclude inline | 195 // The blink tree is different on Android because we exclude inline |
| 140 // text boxes, for performance. | 196 // text boxes, for performance. |
| 141 is_blink_pass_ = true; | 197 is_blink_pass_ = true; |
| 142 RunTestForPlatform(file_path, file_dir); | 198 RunTestForPlatform(file_path, file_dir); |
| 143 #endif | 199 #endif |
| 144 is_blink_pass_ = false; | 200 is_blink_pass_ = false; |
| 145 RunTestForPlatform(file_path, file_dir); | 201 RunTestForPlatform(file_path, file_dir); |
| 146 } | 202 } |
| 147 | 203 |
| 148 void DumpAccessibilityTestBase::RunTestForPlatform( | 204 void DumpAccessibilityTestBase::RunTestForPlatform( |
| 149 const base::FilePath file_path, const char* file_dir) { | 205 const base::FilePath file_path, const char* file_dir) { |
| 206 WebContentsImpl* web_contents = static_cast<WebContentsImpl*>( | |
| 207 shell()->web_contents()); | |
| 208 web_contents->AddAccessibilityMode(AccessibilityModeComplete); | |
| 209 | |
| 150 formatter_.reset(CreateAccessibilityTreeFormatter()); | 210 formatter_.reset(CreateAccessibilityTreeFormatter()); |
| 151 | 211 |
| 152 // Disable the "hot tracked" state (set when the mouse is hovering over | 212 // Disable the "hot tracked" state (set when the mouse is hovering over |
| 153 // an object) because it makes test output change based on the mouse position. | 213 // an object) because it makes test output change based on the mouse position. |
| 154 BrowserAccessibilityStateImpl::GetInstance()-> | 214 BrowserAccessibilityStateImpl::GetInstance()-> |
| 155 set_disable_hot_tracking_for_testing(true); | 215 set_disable_hot_tracking_for_testing(true); |
| 156 | 216 |
| 157 NavigateToURL(shell(), GURL(url::kAboutBlankURL)); | 217 NavigateToURL(shell(), GURL(url::kAboutBlankURL)); |
| 158 | 218 |
| 159 // Output the test path to help anyone who encounters a failure and needs | 219 // Output the test path to help anyone who encounters a failure and needs |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 186 // normalize by deleting all \r from the file (if any) to leave only \n. | 246 // normalize by deleting all \r from the file (if any) to leave only \n. |
| 187 std::string expected_contents; | 247 std::string expected_contents; |
| 188 base::RemoveChars(expected_contents_raw, "\r", &expected_contents); | 248 base::RemoveChars(expected_contents_raw, "\r", &expected_contents); |
| 189 | 249 |
| 190 if (!expected_contents.compare(0, strlen(kMarkSkipFile), kMarkSkipFile)) { | 250 if (!expected_contents.compare(0, strlen(kMarkSkipFile), kMarkSkipFile)) { |
| 191 LOG(INFO) << "Skipping this test on this platform."; | 251 LOG(INFO) << "Skipping this test on this platform."; |
| 192 return; | 252 return; |
| 193 } | 253 } |
| 194 | 254 |
| 195 // Parse filters and other directives in the test file. | 255 // Parse filters and other directives in the test file. |
| 196 std::string wait_for; | 256 std::vector<std::string> wait_for; |
| 197 AddDefaultFilters(&filters_); | 257 AddDefaultFilters(&filters_); |
| 198 ParseHtmlForExtraDirectives(html_contents, &filters_, &wait_for); | 258 ParseHtmlForExtraDirectives(html_contents, &filters_, &wait_for); |
| 199 | 259 |
| 200 // Load the page. | 260 // Load the page. |
| 201 base::string16 html_contents16; | 261 GURL url(embedded_test_server()->GetURL( |
| 202 html_contents16 = base::UTF8ToUTF16(html_contents); | 262 "/" + std::string(file_dir) + "/" + file_path.BaseName().MaybeAsASCII())); |
| 203 GURL url = GetTestUrl(file_dir, file_path.BaseName().MaybeAsASCII().c_str()); | |
| 204 | |
| 205 // If there's a @WAIT-FOR directive, set up an accessibility notification | |
| 206 // waiter that returns on any event; we'll stop when we get the text we're | |
| 207 // waiting for, or time out. Otherwise just wait specifically for | |
| 208 // the "load complete" event. | |
| 209 scoped_ptr<AccessibilityNotificationWaiter> waiter; | |
| 210 if (!wait_for.empty()) { | |
| 211 waiter.reset(new AccessibilityNotificationWaiter( | |
| 212 shell(), AccessibilityModeComplete, ui::AX_EVENT_NONE)); | |
| 213 } else { | |
| 214 waiter.reset(new AccessibilityNotificationWaiter( | |
| 215 shell(), AccessibilityModeComplete, ui::AX_EVENT_LOAD_COMPLETE)); | |
| 216 } | |
| 217 | 263 |
| 218 // Load the test html. | 264 // Load the test html. |
| 219 NavigateToURL(shell(), url); | 265 NavigateToURL(shell(), url); |
| 220 | 266 |
| 221 // Wait for notifications. If there's a @WAIT-FOR directive, break when | 267 // Get the url of every frame in the frame tree. |
| 222 // the text we're waiting for appears in the dump, otherwise break after | 268 FrameTree* frame_tree = web_contents->GetFrameTree(); |
| 223 // the first notification, which will be a load complete. | 269 std::vector<std::string> all_frame_urls; |
| 224 do { | 270 frame_tree->ForEach(base::Bind(GetFrameUrl, &all_frame_urls)); |
| 225 waiter->WaitForNotification(); | 271 |
| 226 if (!wait_for.empty()) { | 272 // Wait for the accessibility tree to fully load for all frames, |
| 227 base::string16 tree_dump = DumpUnfilteredAccessibilityTreeAsString(); | 273 // by searching for the WEB_AREA node in the accessibility tree |
| 228 if (base::UTF16ToUTF8(tree_dump).find(wait_for) != std::string::npos) | 274 // with the url of each frame in our frame tree. Note that this |
| 229 wait_for.clear(); | 275 // doesn't support cases where there are two iframes with the |
| 276 // exact same url. If all frames haven't loaded yet, set up a | |
| 277 // listener for accessibility events on any frame and block | |
| 278 // until the next one is received. | |
| 279 // | |
| 280 // If the original page has a @WAIT-FOR directive, don't break until | |
| 281 // the text we're waiting for appears in the full text dump of the | |
| 282 // accessibility tree, either. | |
| 283 for (;;) { | |
| 284 VLOG(1) << "Top of loop"; | |
| 285 RenderFrameHostImpl* main_frame = static_cast<RenderFrameHostImpl*>( | |
| 286 web_contents->GetMainFrame()); | |
| 287 BrowserAccessibility* accessibility_root = | |
| 288 main_frame->browser_accessibility_manager()->GetRoot(); | |
| 289 | |
| 290 // Check to see if all frames have loaded. | |
| 291 bool all_frames_loaded = true; | |
| 292 for (const auto& url : all_frame_urls) { | |
| 293 if (!AccessibilityTreeContainsLoadedDocWithUrl(accessibility_root, url)) { | |
| 294 VLOG(1) << "Still waiting on this frame to load: " << url; | |
| 295 all_frames_loaded = false; | |
| 296 break; | |
| 297 } | |
| 230 } | 298 } |
| 231 } while (!wait_for.empty()); | 299 |
| 300 // Check to see if the @WAIT-FOR text has appeared yet. | |
| 301 bool all_wait_for_strings_found = true; | |
| 302 base::string16 tree_dump = DumpUnfilteredAccessibilityTreeAsString(); | |
| 303 for (const auto& str : wait_for) { | |
| 304 if (base::UTF16ToUTF8(tree_dump).find(str) == std::string::npos) { | |
| 305 VLOG(1) << "Still waiting on this text to be found: " << str; | |
| 306 all_wait_for_strings_found = false; | |
| 307 break; | |
| 308 } | |
| 309 } | |
| 310 | |
| 311 // If all frames have loaded and the @WAIT-FOR text has appeared, | |
| 312 // we're done. | |
| 313 if (all_frames_loaded && all_wait_for_strings_found) | |
| 314 break; | |
| 315 | |
| 316 // Block until the next accessibility notification in any frame. | |
| 317 VLOG(1) << "Waiting until the next accessibility event"; | |
| 318 AccessibilityNotificationWaiter accessibility_waiter(main_frame, | |
| 319 ui::AX_EVENT_NONE); | |
| 320 frame_tree->ForEach(base::Bind(ListenToFrame, &accessibility_waiter)); | |
| 321 accessibility_waiter.WaitForNotification(); | |
| 322 } | |
| 232 | 323 |
| 233 // Call the subclass to dump the output. | 324 // Call the subclass to dump the output. |
| 234 std::vector<std::string> actual_lines = Dump(); | 325 std::vector<std::string> actual_lines = Dump(); |
| 235 | 326 |
| 236 // Perform a diff (or write the initial baseline). | 327 // Perform a diff (or write the initial baseline). |
| 237 std::vector<std::string> expected_lines = base::SplitString( | 328 std::vector<std::string> expected_lines = base::SplitString( |
| 238 expected_contents, "\n", base::KEEP_WHITESPACE, | 329 expected_contents, "\n", base::KEEP_WHITESPACE, |
| 239 base::SPLIT_WANT_NONEMPTY); | 330 base::SPLIT_WANT_NONEMPTY); |
| 240 // Marking the end of the file with a line of text ensures that | 331 // Marking the end of the file with a line of text ensures that |
| 241 // file length differences are found. | 332 // file length differences are found. |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 278 expected_file, actual_contents.c_str(), actual_contents.size())); | 369 expected_file, actual_contents.c_str(), actual_contents.size())); |
| 279 LOG(INFO) << "Wrote expectations to: " | 370 LOG(INFO) << "Wrote expectations to: " |
| 280 << expected_file.LossyDisplayName(); | 371 << expected_file.LossyDisplayName(); |
| 281 } | 372 } |
| 282 } else { | 373 } else { |
| 283 LOG(INFO) << "Test output matches expectations."; | 374 LOG(INFO) << "Test output matches expectations."; |
| 284 } | 375 } |
| 285 } | 376 } |
| 286 | 377 |
| 287 } // namespace content | 378 } // namespace content |
| OLD | NEW |