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 // |
| 60 // Ignore about:blank urls because of the case where a parent frame A |
| 61 // has a child iframe B and it writes to the document using |
| 62 // contentDocument.open() on the child frame B. |
| 63 // |
| 64 // In this scenario, B's contentWindow.location.href matches A's url, |
| 65 // but B's url in the browser frame tree is still "about:blank". |
| 66 bool GetFrameUrl(std::vector<std::string>* all_frame_urls, |
| 67 FrameTreeNode* frame_tree_node) { |
| 68 std::string url = frame_tree_node->current_url().spec(); |
| 69 if (url != url::kAboutBlankURL) |
| 70 all_frame_urls->push_back(url); |
| 71 return true; |
| 72 } |
| 73 |
| 74 // Searches recursively and returns true if an accessibility node is found |
| 75 // that represents a fully loaded web document with the given url. |
| 76 bool AccessibilityTreeContainsLoadedDocWithUrl(BrowserAccessibility* node, |
| 77 const std::string& url) { |
| 78 if ((node->GetRole() == ui::AX_ROLE_WEB_AREA || |
| 79 node->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA) && |
| 80 node->GetStringAttribute(ui::AX_ATTR_URL) == url) { |
| 81 // If possible, ensure the doc has finished loading. That's currently |
| 82 // not possible with same-process iframes until https://crbug.com/532249 |
| 83 // is fixed. |
| 84 return (node->manager()->GetTreeData().url != url || |
| 85 node->manager()->GetTreeData().loaded); |
| 86 } |
| 87 |
| 88 for (unsigned i = 0; i < node->PlatformChildCount(); i++) { |
| 89 if (AccessibilityTreeContainsLoadedDocWithUrl( |
| 90 node->PlatformGetChild(i), url)) { |
| 91 return true; |
| 92 } |
| 93 } |
| 94 return false; |
| 95 } |
| 96 |
43 } // namespace | 97 } // namespace |
44 | 98 |
45 typedef AccessibilityTreeFormatter::Filter Filter; | 99 typedef AccessibilityTreeFormatter::Filter Filter; |
46 | 100 |
47 DumpAccessibilityTestBase::DumpAccessibilityTestBase() { | 101 DumpAccessibilityTestBase::DumpAccessibilityTestBase() { |
48 } | 102 } |
49 | 103 |
50 DumpAccessibilityTestBase::~DumpAccessibilityTestBase() { | 104 DumpAccessibilityTestBase::~DumpAccessibilityTestBase() { |
51 } | 105 } |
52 | 106 |
| 107 void DumpAccessibilityTestBase::SetUpCommandLine( |
| 108 base::CommandLine* command_line) { |
| 109 IsolateAllSitesForTesting(command_line); |
| 110 } |
| 111 |
| 112 void DumpAccessibilityTestBase::SetUpOnMainThread() { |
| 113 host_resolver()->AddRule("*", "127.0.0.1"); |
| 114 ASSERT_TRUE(embedded_test_server()->Start()); |
| 115 SetupCrossSiteRedirector(embedded_test_server()); |
| 116 } |
| 117 |
53 base::string16 | 118 base::string16 |
54 DumpAccessibilityTestBase::DumpUnfilteredAccessibilityTreeAsString() { | 119 DumpAccessibilityTestBase::DumpUnfilteredAccessibilityTreeAsString() { |
55 scoped_ptr<AccessibilityTreeFormatter> formatter( | 120 scoped_ptr<AccessibilityTreeFormatter> formatter( |
56 CreateAccessibilityTreeFormatter()); | 121 CreateAccessibilityTreeFormatter()); |
57 std::vector<Filter> filters; | 122 std::vector<Filter> filters; |
58 filters.push_back(Filter(base::ASCIIToUTF16("*"), Filter::ALLOW)); | 123 filters.push_back(Filter(base::ASCIIToUTF16("*"), Filter::ALLOW)); |
59 formatter->SetFilters(filters); | 124 formatter->SetFilters(filters); |
60 formatter->set_show_ids(true); | 125 formatter->set_show_ids(true); |
61 WebContentsImpl* web_contents = static_cast<WebContentsImpl*>( | 126 WebContentsImpl* web_contents = static_cast<WebContentsImpl*>( |
62 shell()->web_contents()); | 127 shell()->web_contents()); |
(...skipping 25 matching lines...) Expand all Loading... |
88 ++j; | 153 ++j; |
89 } | 154 } |
90 | 155 |
91 // Actual file has been fully checked. | 156 // Actual file has been fully checked. |
92 return diff_lines; | 157 return diff_lines; |
93 } | 158 } |
94 | 159 |
95 void DumpAccessibilityTestBase::ParseHtmlForExtraDirectives( | 160 void DumpAccessibilityTestBase::ParseHtmlForExtraDirectives( |
96 const std::string& test_html, | 161 const std::string& test_html, |
97 std::vector<Filter>* filters, | 162 std::vector<Filter>* filters, |
98 std::string* wait_for) { | 163 std::vector<std::string>* wait_for) { |
99 for (const std::string& line : | 164 for (const std::string& line : |
100 base::SplitString(test_html, "\n", base::TRIM_WHITESPACE, | 165 base::SplitString(test_html, "\n", base::TRIM_WHITESPACE, |
101 base::SPLIT_WANT_ALL)) { | 166 base::SPLIT_WANT_ALL)) { |
102 const std::string& allow_empty_str = formatter_->GetAllowEmptyString(); | 167 const std::string& allow_empty_str = formatter_->GetAllowEmptyString(); |
103 const std::string& allow_str = formatter_->GetAllowString(); | 168 const std::string& allow_str = formatter_->GetAllowString(); |
104 const std::string& deny_str = formatter_->GetDenyString(); | 169 const std::string& deny_str = formatter_->GetDenyString(); |
105 const std::string& wait_str = "@WAIT-FOR:"; | 170 const std::string& wait_str = "@WAIT-FOR:"; |
106 if (base::StartsWith(line, allow_empty_str, | 171 if (base::StartsWith(line, allow_empty_str, |
107 base::CompareCase::SENSITIVE)) { | 172 base::CompareCase::SENSITIVE)) { |
108 filters->push_back( | 173 filters->push_back( |
109 Filter(base::UTF8ToUTF16(line.substr(allow_empty_str.size())), | 174 Filter(base::UTF8ToUTF16(line.substr(allow_empty_str.size())), |
110 Filter::ALLOW_EMPTY)); | 175 Filter::ALLOW_EMPTY)); |
111 } else if (base::StartsWith(line, allow_str, | 176 } else if (base::StartsWith(line, allow_str, |
112 base::CompareCase::SENSITIVE)) { | 177 base::CompareCase::SENSITIVE)) { |
113 filters->push_back(Filter(base::UTF8ToUTF16( | 178 filters->push_back(Filter(base::UTF8ToUTF16( |
114 line.substr(allow_str.size())), | 179 line.substr(allow_str.size())), |
115 Filter::ALLOW)); | 180 Filter::ALLOW)); |
116 } else if (base::StartsWith(line, deny_str, | 181 } else if (base::StartsWith(line, deny_str, |
117 base::CompareCase::SENSITIVE)) { | 182 base::CompareCase::SENSITIVE)) { |
118 filters->push_back(Filter(base::UTF8ToUTF16( | 183 filters->push_back(Filter(base::UTF8ToUTF16( |
119 line.substr(deny_str.size())), | 184 line.substr(deny_str.size())), |
120 Filter::DENY)); | 185 Filter::DENY)); |
121 } else if (base::StartsWith(line, wait_str, | 186 } else if (base::StartsWith(line, wait_str, |
122 base::CompareCase::SENSITIVE)) { | 187 base::CompareCase::SENSITIVE)) { |
123 *wait_for = line.substr(wait_str.size()); | 188 wait_for->push_back(line.substr(wait_str.size())); |
124 } | 189 } |
125 } | 190 } |
126 } | 191 } |
127 | 192 |
128 AccessibilityTreeFormatter* | 193 AccessibilityTreeFormatter* |
129 DumpAccessibilityTestBase::CreateAccessibilityTreeFormatter() { | 194 DumpAccessibilityTestBase::CreateAccessibilityTreeFormatter() { |
130 if (is_blink_pass_) | 195 if (is_blink_pass_) |
131 return new AccessibilityTreeFormatterBlink(); | 196 return new AccessibilityTreeFormatterBlink(); |
132 else | 197 else |
133 return AccessibilityTreeFormatter::Create(); | 198 return AccessibilityTreeFormatter::Create(); |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
186 // normalize by deleting all \r from the file (if any) to leave only \n. | 251 // normalize by deleting all \r from the file (if any) to leave only \n. |
187 std::string expected_contents; | 252 std::string expected_contents; |
188 base::RemoveChars(expected_contents_raw, "\r", &expected_contents); | 253 base::RemoveChars(expected_contents_raw, "\r", &expected_contents); |
189 | 254 |
190 if (!expected_contents.compare(0, strlen(kMarkSkipFile), kMarkSkipFile)) { | 255 if (!expected_contents.compare(0, strlen(kMarkSkipFile), kMarkSkipFile)) { |
191 LOG(INFO) << "Skipping this test on this platform."; | 256 LOG(INFO) << "Skipping this test on this platform."; |
192 return; | 257 return; |
193 } | 258 } |
194 | 259 |
195 // Parse filters and other directives in the test file. | 260 // Parse filters and other directives in the test file. |
196 std::string wait_for; | 261 std::vector<std::string> wait_for; |
197 AddDefaultFilters(&filters_); | 262 AddDefaultFilters(&filters_); |
198 ParseHtmlForExtraDirectives(html_contents, &filters_, &wait_for); | 263 ParseHtmlForExtraDirectives(html_contents, &filters_, &wait_for); |
199 | 264 |
200 // Load the page. | 265 // Load the test html and wait for the "load complete" AX event. |
201 base::string16 html_contents16; | 266 GURL url(embedded_test_server()->GetURL( |
202 html_contents16 = base::UTF8ToUTF16(html_contents); | 267 "/" + std::string(file_dir) + "/" + file_path.BaseName().MaybeAsASCII())); |
203 GURL url = GetTestUrl(file_dir, file_path.BaseName().MaybeAsASCII().c_str()); | 268 AccessibilityNotificationWaiter accessibility_waiter( |
| 269 shell(), |
| 270 AccessibilityModeComplete, |
| 271 ui::AX_EVENT_LOAD_COMPLETE); |
| 272 NavigateToURL(shell(), url); |
| 273 accessibility_waiter.WaitForNotification(); |
204 | 274 |
205 // If there's a @WAIT-FOR directive, set up an accessibility notification | 275 // Get the url of every frame in the frame tree. |
206 // waiter that returns on any event; we'll stop when we get the text we're | 276 WebContentsImpl* web_contents = static_cast<WebContentsImpl*>( |
207 // waiting for, or time out. Otherwise just wait specifically for | 277 shell()->web_contents()); |
208 // the "load complete" event. | 278 FrameTree* frame_tree = web_contents->GetFrameTree(); |
209 scoped_ptr<AccessibilityNotificationWaiter> waiter; | 279 std::vector<std::string> all_frame_urls; |
210 if (!wait_for.empty()) { | 280 frame_tree->ForEach(base::Bind(GetFrameUrl, &all_frame_urls)); |
211 waiter.reset(new AccessibilityNotificationWaiter( | 281 |
212 shell(), AccessibilityModeComplete, ui::AX_EVENT_NONE)); | 282 // Wait for the accessibility tree to fully load for all frames, |
213 } else { | 283 // by searching for the WEB_AREA node in the accessibility tree |
214 waiter.reset(new AccessibilityNotificationWaiter( | 284 // with the url of each frame in our frame tree. Note that this |
215 shell(), AccessibilityModeComplete, ui::AX_EVENT_LOAD_COMPLETE)); | 285 // doesn't support cases where there are two iframes with the |
| 286 // exact same url. If all frames haven't loaded yet, set up a |
| 287 // listener for accessibility events on any frame and block |
| 288 // until the next one is received. |
| 289 // |
| 290 // If the original page has a @WAIT-FOR directive, don't break until |
| 291 // the text we're waiting for appears in the full text dump of the |
| 292 // accessibility tree, either. |
| 293 for (;;) { |
| 294 VLOG(1) << "Top of loop"; |
| 295 RenderFrameHostImpl* main_frame = static_cast<RenderFrameHostImpl*>( |
| 296 web_contents->GetMainFrame()); |
| 297 BrowserAccessibilityManager* manager = |
| 298 main_frame->browser_accessibility_manager(); |
| 299 if (manager) { |
| 300 BrowserAccessibility* accessibility_root = |
| 301 manager->GetRoot(); |
| 302 |
| 303 // Check to see if all frames have loaded. |
| 304 bool all_frames_loaded = true; |
| 305 for (const auto& url : all_frame_urls) { |
| 306 if (!AccessibilityTreeContainsLoadedDocWithUrl( |
| 307 accessibility_root, url)) { |
| 308 VLOG(1) << "Still waiting on this frame to load: " << url; |
| 309 all_frames_loaded = false; |
| 310 break; |
| 311 } |
| 312 } |
| 313 |
| 314 // Check to see if the @WAIT-FOR text has appeared yet. |
| 315 bool all_wait_for_strings_found = true; |
| 316 base::string16 tree_dump = DumpUnfilteredAccessibilityTreeAsString(); |
| 317 for (const auto& str : wait_for) { |
| 318 if (base::UTF16ToUTF8(tree_dump).find(str) == std::string::npos) { |
| 319 VLOG(1) << "Still waiting on this text to be found: " << str; |
| 320 all_wait_for_strings_found = false; |
| 321 break; |
| 322 } |
| 323 } |
| 324 |
| 325 // If all frames have loaded and the @WAIT-FOR text has appeared, |
| 326 // we're done. |
| 327 if (all_frames_loaded && all_wait_for_strings_found) |
| 328 break; |
| 329 } |
| 330 |
| 331 // Block until the next accessibility notification in any frame. |
| 332 VLOG(1) << "Waiting until the next accessibility event"; |
| 333 AccessibilityNotificationWaiter accessibility_waiter(main_frame, |
| 334 ui::AX_EVENT_NONE); |
| 335 frame_tree->ForEach(base::Bind(ListenToFrame, &accessibility_waiter)); |
| 336 accessibility_waiter.WaitForNotification(); |
216 } | 337 } |
217 | 338 |
218 // Load the test html. | |
219 NavigateToURL(shell(), url); | |
220 | |
221 // Wait for notifications. If there's a @WAIT-FOR directive, break when | |
222 // the text we're waiting for appears in the dump, otherwise break after | |
223 // the first notification, which will be a load complete. | |
224 do { | |
225 waiter->WaitForNotification(); | |
226 if (!wait_for.empty()) { | |
227 base::string16 tree_dump = DumpUnfilteredAccessibilityTreeAsString(); | |
228 if (base::UTF16ToUTF8(tree_dump).find(wait_for) != std::string::npos) | |
229 wait_for.clear(); | |
230 } | |
231 } while (!wait_for.empty()); | |
232 | |
233 // Call the subclass to dump the output. | 339 // Call the subclass to dump the output. |
234 std::vector<std::string> actual_lines = Dump(); | 340 std::vector<std::string> actual_lines = Dump(); |
235 | 341 |
236 // Perform a diff (or write the initial baseline). | 342 // Perform a diff (or write the initial baseline). |
237 std::vector<std::string> expected_lines = base::SplitString( | 343 std::vector<std::string> expected_lines = base::SplitString( |
238 expected_contents, "\n", base::KEEP_WHITESPACE, | 344 expected_contents, "\n", base::KEEP_WHITESPACE, |
239 base::SPLIT_WANT_NONEMPTY); | 345 base::SPLIT_WANT_NONEMPTY); |
240 // Marking the end of the file with a line of text ensures that | 346 // Marking the end of the file with a line of text ensures that |
241 // file length differences are found. | 347 // file length differences are found. |
242 expected_lines.push_back(kMarkEndOfFile); | 348 expected_lines.push_back(kMarkEndOfFile); |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
278 expected_file, actual_contents.c_str(), actual_contents.size())); | 384 expected_file, actual_contents.c_str(), actual_contents.size())); |
279 LOG(INFO) << "Wrote expectations to: " | 385 LOG(INFO) << "Wrote expectations to: " |
280 << expected_file.LossyDisplayName(); | 386 << expected_file.LossyDisplayName(); |
281 } | 387 } |
282 } else { | 388 } else { |
283 LOG(INFO) << "Test output matches expectations."; | 389 LOG(INFO) << "Test output matches expectations."; |
284 } | 390 } |
285 } | 391 } |
286 | 392 |
287 } // namespace content | 393 } // namespace content |
OLD | NEW |