diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-29 10:46:47 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-11-02 12:02:10 +0000 |
commit | 99677208ff3b216fdfec551fbe548da5520cd6fb (patch) | |
tree | 476a4865c10320249360e859d8fdd3e01833b03a /chromium/content/browser/accessibility | |
parent | c30a6232df03e1efbd9f3b226777b07e087a1122 (diff) | |
download | qtwebengine-chromium-99677208ff3b216fdfec551fbe548da5520cd6fb.tar.gz |
BASELINE: Update Chromium to 86.0.4240.124
Change-Id: Ide0ff151e94cd665ae6521a446995d34a9d1d644
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/content/browser/accessibility')
58 files changed, 2057 insertions, 903 deletions
diff --git a/chromium/content/browser/accessibility/accessibility_action_browsertest.cc b/chromium/content/browser/accessibility/accessibility_action_browsertest.cc index efe6f4683be..5600f91336e 100644 --- a/chromium/content/browser/accessibility/accessibility_action_browsertest.cc +++ b/chromium/content/browser/accessibility/accessibility_action_browsertest.cc @@ -19,6 +19,7 @@ #include "content/shell/browser/shell.h" #include "net/base/data_url.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/accessibility/accessibility_switches.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/gfx/codec/png_codec.h" #include "url/gurl.h" @@ -877,4 +878,48 @@ IN_PROC_BROWSER_TEST_F(AccessibilityActionBrowserTest, ClickSVG) { #endif // !defined(OS_ANDROID) } +// This test ony makes sense on platforms where the popup menu is implemented +// internally as an HTML page in a popup, not where it's a native popup. +#if defined(OS_WIN) || defined(OS_CHROMEOS) || defined(USE_ATK) +IN_PROC_BROWSER_TEST_F(AccessibilityActionBrowserTest, + OpenSelectPopupWithNoAXMenuList) { + base::CommandLine::ForCurrentProcess()->AppendSwitch( + ::switches::kDisableAXMenuList); + + LoadInitialAccessibilityTreeFromHtml(R"HTML( + <head><title>No AXMenuList</title></head> + <body> + <select> + <option selected>One</option> + <option>Two</option> + <option>Three</option> + </select> + </body> + )HTML"); + + BrowserAccessibility* target = FindNode(ax::mojom::Role::kPopUpButton, "One"); + ASSERT_NE(nullptr, target); + + EXPECT_EQ(0U, target->PlatformChildCount()); + EXPECT_EQ(nullptr, FindNode(ax::mojom::Role::kListBox, "")); + + // Call DoDefaultAction. + AccessibilityNotificationWaiter waiter2( + shell()->web_contents(), ui::kAXModeComplete, ax::mojom::Event::kClicked); + GetManager()->DoDefaultAction(*target); + waiter2.WaitForNotification(); + + WaitForAccessibilityTreeToContainNodeWithName(shell()->web_contents(), + "Three"); + + ASSERT_EQ(1U, target->PlatformChildCount()); + BrowserAccessibility* popup_web_area = target->PlatformGetChild(0); + EXPECT_EQ(ax::mojom::Role::kRootWebArea, popup_web_area->GetRole()); + + BrowserAccessibility* listbox = FindNode(ax::mojom::Role::kListBox, ""); + ASSERT_TRUE(listbox); + EXPECT_EQ(3U, listbox->PlatformChildCount()); +} +#endif // defined(OS_WIN) || defined(OS_CHROMEOS) || defined(USE_ATK) + } // namespace content diff --git a/chromium/content/browser/accessibility/accessibility_auralinux_browsertest.cc b/chromium/content/browser/accessibility/accessibility_auralinux_browsertest.cc index 71a757df88e..657d4b4f218 100644 --- a/chromium/content/browser/accessibility/accessibility_auralinux_browsertest.cc +++ b/chromium/content/browser/accessibility/accessibility_auralinux_browsertest.cc @@ -1123,7 +1123,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityAuraLinuxBrowserTest, } #endif // defined(ATK_CHECK_VERSION) && ATK_CHECK_VERSION(2, 32, 0) -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) // Flaky on crbug.com/1026149 #define MAYBE_TestSetSelection DISABLED_TestSetSelection #else diff --git a/chromium/content/browser/accessibility/accessibility_event_recorder.cc b/chromium/content/browser/accessibility/accessibility_event_recorder.cc index 555251c9218..1a636008c9e 100644 --- a/chromium/content/browser/accessibility/accessibility_event_recorder.cc +++ b/chromium/content/browser/accessibility/accessibility_event_recorder.cc @@ -16,7 +16,7 @@ AccessibilityEventRecorder::AccessibilityEventRecorder( AccessibilityEventRecorder::~AccessibilityEventRecorder() = default; -#if !defined(OS_WIN) && !defined(OS_MACOSX) && !BUILDFLAG(USE_ATK) +#if !defined(OS_WIN) && !defined(OS_MAC) && !BUILDFLAG(USE_ATK) // static std::unique_ptr<AccessibilityEventRecorder> AccessibilityEventRecorder::Create( BrowserAccessibilityManager* manager, diff --git a/chromium/content/browser/accessibility/accessibility_event_recorder.h b/chromium/content/browser/accessibility/accessibility_event_recorder.h index 6a9adf70381..dfed6174acf 100644 --- a/chromium/content/browser/accessibility/accessibility_event_recorder.h +++ b/chromium/content/browser/accessibility/accessibility_event_recorder.h @@ -9,6 +9,7 @@ #include <string> #include <vector> +#include "base/bind_helpers.h" #include "base/callback.h" #include "base/macros.h" #include "base/process/process_handle.h" @@ -68,6 +69,8 @@ class CONTENT_EXPORT AccessibilityEventRecorder { callback_ = std::move(callback); } + void StopListeningToEvents() { callback_ = base::NullCallback(); } + // Called to ensure the event recorder has finished recording async events. virtual void FlushAsyncEvents() {} diff --git a/chromium/content/browser/accessibility/accessibility_event_recorder_auralinux.cc b/chromium/content/browser/accessibility/accessibility_event_recorder_auralinux.cc index ceca7d9f66b..bfe6f5a3c73 100644 --- a/chromium/content/browser/accessibility/accessibility_event_recorder_auralinux.cc +++ b/chromium/content/browser/accessibility/accessibility_event_recorder_auralinux.cc @@ -166,7 +166,10 @@ void AccessibilityEventRecorderAuraLinux::AddATKEventListeners() { AddATKEventListener("ATK:AtkText:text-remove"); AddATKEventListener("ATK:AtkText:text-selection-changed"); AddATKEventListener("ATK:AtkText:text-caret-moved"); + AddATKEventListener("ATK:AtkText:text-attributes-changed"); AddATKEventListener("ATK:AtkSelection:selection-changed"); + AddATKEventListener("ATK:AtkTable:column-reordered"); + AddATKEventListener("ATK:AtkTable:row-reordered"); } void AccessibilityEventRecorderAuraLinux::RemoveATKEventListeners() { @@ -326,6 +329,7 @@ const char* const kEventNames[] = { "object:row-reordered", "object:selection-changed", "object:state-changed", + "object:text-attributes-changed", "object:text-caret-moved", "object:text-changed", "object:text-selection-changed", diff --git a/chromium/content/browser/accessibility/accessibility_event_recorder_win.cc b/chromium/content/browser/accessibility/accessibility_event_recorder_win.cc index 73c7d6214ca..7d8ee27f05b 100644 --- a/chromium/content/browser/accessibility/accessibility_event_recorder_win.cc +++ b/chromium/content/browser/accessibility/accessibility_event_recorder_win.cc @@ -41,7 +41,7 @@ std::string RoleVariantToString(const base::win::ScopedVariant& role) { HRESULT QueryIAccessible2(IAccessible* accessible, IAccessible2** accessible2) { Microsoft::WRL::ComPtr<IServiceProvider> service_provider; - HRESULT hr = accessible->QueryInterface(service_provider.GetAddressOf()); + HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider)); return SUCCEEDED(hr) ? service_provider->QueryService(IID_IAccessible2, accessible2) : hr; @@ -50,7 +50,7 @@ HRESULT QueryIAccessible2(IAccessible* accessible, IAccessible2** accessible2) { HRESULT QueryIAccessibleText(IAccessible* accessible, IAccessibleText** accessible_text) { Microsoft::WRL::ComPtr<IServiceProvider> service_provider; - HRESULT hr = accessible->QueryInterface(service_provider.GetAddressOf()); + HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider)); return SUCCEEDED(hr) ? service_provider->QueryService(IID_IAccessibleText, accessible_text) : hr; @@ -199,8 +199,7 @@ void AccessibilityEventRecorderWin::OnWinEventHook(HWINEVENTHOOK handle, DWORD event_time) { Microsoft::WRL::ComPtr<IAccessible> browser_accessible; HRESULT hr = AccessibleObjectFromWindowWrapper( - hwnd, obj_id, IID_IAccessible, - reinterpret_cast<void**>(browser_accessible.GetAddressOf())); + hwnd, obj_id, IID_PPV_ARGS(&browser_accessible)); if (FAILED(hr)) { // Note: our event hook will pick up some superfluous events we // don't care about, so it's safe to just ignore these failures. @@ -211,8 +210,7 @@ void AccessibilityEventRecorderWin::OnWinEventHook(HWINEVENTHOOK handle, base::win::ScopedVariant childid_variant(child_id); Microsoft::WRL::ComPtr<IDispatch> dispatch; - hr = browser_accessible->get_accChild(childid_variant, - dispatch.GetAddressOf()); + hr = browser_accessible->get_accChild(childid_variant, &dispatch); if (hr != S_OK || !dispatch) { VLOG(1) << "Ignoring result " << hr << " and result " << dispatch.Get() << " from get_accChild"; @@ -220,7 +218,7 @@ void AccessibilityEventRecorderWin::OnWinEventHook(HWINEVENTHOOK handle, } Microsoft::WRL::ComPtr<IAccessible> iaccessible; - hr = dispatch.CopyTo(iaccessible.GetAddressOf()); + hr = dispatch.As(&iaccessible); if (FAILED(hr)) { VLOG(1) << "Ignoring result " << hr << " from QueryInterface"; return; @@ -257,13 +255,13 @@ void AccessibilityEventRecorderWin::OnWinEventHook(HWINEVENTHOOK handle, return; Microsoft::WRL::ComPtr<IServiceProvider> service_provider; - hr = iaccessible->QueryInterface(service_provider.GetAddressOf()); + hr = iaccessible->QueryInterface(IID_PPV_ARGS(&service_provider)); if (FAILED(hr)) return; Microsoft::WRL::ComPtr<IAccessible> content_document; hr = service_provider->QueryService(GUID_IAccessibleContentDocument, - content_document.GetAddressOf()); + IID_PPV_ARGS(&content_document)); if (FAILED(hr)) return; } @@ -291,7 +289,7 @@ void AccessibilityEventRecorderWin::OnWinEventHook(HWINEVENTHOOK handle, AccessibleStates ia2_state = 0; Microsoft::WRL::ComPtr<IAccessible2> iaccessible2; - hr = QueryIAccessible2(iaccessible.Get(), iaccessible2.GetAddressOf()); + hr = QueryIAccessible2(iaccessible.Get(), &iaccessible2); bool has_ia2 = SUCCEEDED(hr) && iaccessible2; base::string16 html_tag; @@ -306,13 +304,13 @@ void AccessibilityEventRecorderWin::OnWinEventHook(HWINEVENTHOOK handle, base::string16(attributes_bstr.Get(), attributes_bstr.Length()), base::string16(1, ';'), base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); for (base::string16& attr : ia2_attributes) { - if (base::StringPiece16(attr).starts_with(L"class:")) + if (base::StartsWith(attr, L"class:")) obj_class = attr.substr(6); // HTML or view class - if (base::StringPiece16(attr).starts_with(L"id:")) { + if (base::StartsWith(attr, L"id:")) { html_id = base::string16(L"#"); html_id += attr.substr(3); } - if (base::StringPiece16(attr).starts_with(L"tag:")) { + if (base::StartsWith(attr, L"tag:")) { html_tag = attr.substr(4); } } @@ -367,7 +365,7 @@ void AccessibilityEventRecorderWin::OnWinEventHook(HWINEVENTHOOK handle, // For TEXT_REMOVED and TEXT_INSERTED events, query the text that was // inserted or removed and include that in the log. Microsoft::WRL::ComPtr<IAccessibleText> accessible_text; - hr = QueryIAccessibleText(iaccessible.Get(), accessible_text.GetAddressOf()); + hr = QueryIAccessibleText(iaccessible.Get(), &accessible_text); if (SUCCEEDED(hr)) { if (event == IA2_EVENT_TEXT_REMOVED) { IA2TextSegment old_text; diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_android.cc b/chromium/content/browser/accessibility/accessibility_tree_formatter_android.cc index afeb78a3c21..67b75709615 100644 --- a/chromium/content/browser/accessibility/accessibility_tree_formatter_android.cc +++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_android.cc @@ -22,24 +22,39 @@ using base::StringPrintf; namespace content { namespace { - +// clang-format off const char* const BOOL_ATTRIBUTES[] = { - "checkable", "checked", - "clickable", "collection", - "collection_item", "content_invalid", - "disabled", "dismissable", - "editable_text", "focusable", - "focused", "has_character_locations", - "has_image", "has_non_empty_value", - "heading", "hierarchical", - "invisible", "link", - "multiline", "password", - "range", "scrollable", - "selected", "interesting"}; + "checkable", + "checked", + "clickable", + "collection", + "collection_item", + "content_invalid", + "disabled", + "dismissable", + "editable_text", + "focusable", + "focused", + "has_character_locations", + "has_image", + "has_non_empty_value", + "heading", + "hierarchical", + "invisible", + "link", + "multiline", + "multiselectable", + "password", + "range", + "scrollable", + "selected", + "interesting" +}; const char* const STRING_ATTRIBUTES[] = { "name", "hint", + "state_description", }; const char* const INT_ATTRIBUTES[] = { @@ -59,7 +74,7 @@ const char* const INT_ATTRIBUTES[] = { "text_change_added_count", "text_change_removed_count", }; - +// clang-format on } // namespace class AccessibilityTreeFormatterAndroid @@ -89,6 +104,7 @@ class AccessibilityTreeFormatterAndroid const std::string GetAllowString() override; const std::string GetDenyString() override; const std::string GetDenyNodeString() override; + const std::string GetRunUntilEventString() override; void RecursiveBuildAccessibilityTree(const BrowserAccessibility& node, base::DictionaryValue* dict) const; @@ -210,6 +226,7 @@ void AccessibilityTreeFormatterAndroid::AddProperties( dict->SetBoolean("invisible", !android_node->IsVisibleToUser()); dict->SetBoolean("link", android_node->IsLink()); dict->SetBoolean("multiline", android_node->IsMultiLine()); + dict->SetBoolean("multiselectable", android_node->IsMultiselectable()); dict->SetBoolean("range", android_node->IsRangeType()); dict->SetBoolean("password", android_node->IsPasswordField()); dict->SetBoolean("scrollable", android_node->IsScrollable()); @@ -220,6 +237,7 @@ void AccessibilityTreeFormatterAndroid::AddProperties( dict->SetString("name", android_node->GetInnerText()); dict->SetString("hint", android_node->GetHint()); dict->SetString("role_description", android_node->GetRoleDescription()); + dict->SetString("state_description", android_node->GetStateDescription()); // Int attributes. dict->SetInteger("item_index", android_node->GetItemIndex()); @@ -324,4 +342,8 @@ const std::string AccessibilityTreeFormatterAndroid::GetDenyNodeString() { return "@ANDROID-DENY-NODE:"; } +const std::string AccessibilityTreeFormatterAndroid::GetRunUntilEventString() { + return "@ANDROID-RUN-UNTIL-EVENT:"; +} + } // namespace content diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc b/chromium/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc index b09bf27719f..91f6ff6b7d9 100644 --- a/chromium/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc +++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc @@ -36,6 +36,7 @@ class AccessibilityTreeFormatterAuraLinux const std::string GetAllowString() override; const std::string GetDenyString() override; const std::string GetDenyNodeString() override; + const std::string GetRunUntilEventString() override; base::string16 ProcessTreeForOutput( const base::DictionaryValue& node, @@ -603,6 +604,7 @@ const char* const ATK_OBJECT_ATTRIBUTES[] = { "src", "table-cell-index", "tag", + "text-align", "text-input-type", "valuemin", "valuemax", @@ -746,4 +748,9 @@ const std::string AccessibilityTreeFormatterAuraLinux::GetDenyNodeString() { return "@AURALINUX-DENY-NODE:"; } +const std::string +AccessibilityTreeFormatterAuraLinux::GetRunUntilEventString() { + return "@AURALINUX-RUN-UNTIL-EVENT:"; +} + } // namespace content diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_base.cc b/chromium/content/browser/accessibility/accessibility_tree_formatter_base.cc index 706b036cab9..7b1f9188212 100644 --- a/chromium/content/browser/accessibility/accessibility_tree_formatter_base.cc +++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_base.cc @@ -43,18 +43,23 @@ PropertyNode PropertyNode::FromPropertyFilter( // Property invocation: property_str expected format is // prop_name or prop_name(arg1, ... argN). PropertyNode root; - Parse(&root, filter.property_str.begin(), filter.property_str.end()); + const std::string& property_str = filter.property_str; + Parse(&root, property_str.begin(), property_str.end()); PropertyNode* node = &root.parameters[0]; - node->original_property = filter.property_str; + + // Expel a trailing wildcard if any. + node->original_property = + property_str.substr(0, property_str.find_last_of('*')); // Line indexes filter: filter_str expected format is // :line_num_1, ... :line_num_N, a comma separated list of line indexes // the property should be queried for. For example, ":1,:5,:7" indicates that // the property should called for objects placed on 1, 5 and 7 lines only. - if (!filter.filter_str.empty()) { + const std::string& filter_str = filter.filter_str; + if (!filter_str.empty()) { node->line_indexes = - base::SplitString(filter.filter_str, base::string16(1, ','), + base::SplitString(filter_str, std::string(1, ','), base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); } @@ -83,12 +88,19 @@ PropertyNode::operator bool() const { return !name_or_value.empty(); } +bool PropertyNode::IsMatching(const std::string& pattern) const { + // Looking for exact property match. Expel a trailing whildcard from + // the property filter to handle filters like AXRole*. + return name_or_value.compare(0, name_or_value.find_last_of('*'), pattern) == + 0; +} + bool PropertyNode::IsArray() const { - return name_or_value == base::ASCIIToUTF16("[]"); + return name_or_value == "[]"; } bool PropertyNode::IsDict() const { - return name_or_value == base::ASCIIToUTF16("{}"); + return name_or_value == "{}"; } base::Optional<int> PropertyNode::AsInt() const { @@ -99,9 +111,19 @@ base::Optional<int> PropertyNode::AsInt() const { return value; } -base::Optional<base::string16> PropertyNode::FindKey(const char* refkey) const { +const PropertyNode* PropertyNode::FindKey(const char* refkey) const { for (const auto& param : parameters) { - if (param.key == base::ASCIIToUTF16(refkey)) { + if (param.key == refkey) { + return ¶m; + } + } + return nullptr; +} + +base::Optional<std::string> PropertyNode::FindStringKey( + const char* refkey) const { + for (const auto& param : parameters) { + if (param.key == refkey) { return param.name_or_value; } } @@ -110,7 +132,7 @@ base::Optional<base::string16> PropertyNode::FindKey(const char* refkey) const { base::Optional<int> PropertyNode::FindIntKey(const char* refkey) const { for (const auto& param : parameters) { - if (param.key == base::ASCIIToUTF16(refkey)) { + if (param.key == refkey) { return param.AsInt(); } } @@ -123,16 +145,16 @@ std::string PropertyNode::ToString() const { if (!out.empty()) { out += ','; } - out += base::UTF16ToUTF8(index); + out += index; } if (!out.empty()) { out += ';'; } if (!key.empty()) { - out += base::UTF16ToUTF8(key) + ": "; + out += key + ": "; } - out += base::UTF16ToUTF8(name_or_value); + out += name_or_value; if (parameters.size()) { out += '('; for (size_t i = 0; i < parameters.size(); i++) { @@ -149,7 +171,7 @@ std::string PropertyNode::ToString() const { // private PropertyNode::PropertyNode(PropertyNode::iterator key_begin, PropertyNode::iterator key_end, - const base::string16& name_or_value) + const std::string& name_or_value) : key(key_begin, key_end), name_or_value(name_or_value) {} PropertyNode::PropertyNode(PropertyNode::iterator begin, PropertyNode::iterator end) @@ -179,8 +201,7 @@ PropertyNode::iterator PropertyNode::Parse(PropertyNode* node, // Subnode begins: a special case for arrays, which have [arg1, ..., argN] // form. if (*iter == '[') { - node->parameters.push_back( - PropertyNode(key_begin, key_end, base::UTF8ToUTF16("[]"))); + node->parameters.push_back(PropertyNode(key_begin, key_end, "[]")); key_begin = key_end = end; begin = iter = Parse(&node->parameters.back(), ++iter, end); continue; @@ -189,8 +210,7 @@ PropertyNode::iterator PropertyNode::Parse(PropertyNode* node, // Subnode begins: a special case for dictionaries of {key1: value1, ..., // key2: value2} form. if (*iter == '{') { - node->parameters.push_back( - PropertyNode(key_begin, key_end, base::UTF8ToUTF16("{}"))); + node->parameters.push_back(PropertyNode(key_begin, key_end, "{}")); key_begin = key_end = end; begin = iter = Parse(&node->parameters.back(), ++iter, end); continue; @@ -249,7 +269,7 @@ AccessibilityTreeFormatter::PropertyFilter::PropertyFilter( const PropertyFilter&) = default; AccessibilityTreeFormatter::PropertyFilter::PropertyFilter( - const base::string16& str, + const std::string& str, Type type) : match_str(str), type(type) { size_t index = str.find(';'); @@ -291,17 +311,26 @@ base::string16 AccessibilityTreeFormatterBase::DumpAccessibilityTreeFromManager( bool AccessibilityTreeFormatter::MatchesPropertyFilters( const std::vector<PropertyFilter>& property_filters, - const base::string16& text, + const std::string& text, bool default_result) { bool allow = default_result; for (const auto& filter : property_filters) { - if (base::MatchPattern(text, filter.match_str)) { + // Either + // 1) the line matches a filter pattern, for example, AXSubrole=* filter + // will match AXSubrole=AXTerm line or + // 2) a property on the line is exactly equal to the filter pattern, for + // example, AXSubrole filter will match AXSubrole=AXTerm line. + if (base::MatchPattern(text, filter.match_str) || + (filter.match_str.length() > 0 && + filter.match_str.find('=') == std::string::npos && + filter.match_str[filter.match_str.length() - 1] != '*' && + base::MatchPattern(text, filter.match_str + "=*"))) { switch (filter.type) { case PropertyFilter::ALLOW_EMPTY: allow = true; break; case PropertyFilter::ALLOW: - allow = (!base::MatchPattern(text, base::UTF8ToUTF16("*=''"))); + allow = (!base::MatchPattern(text, "*=''")); break; case PropertyFilter::DENY: allow = false; @@ -419,14 +448,14 @@ AccessibilityTreeFormatterBase::GetVersionSpecificExpectedFileSuffix() { return FILE_PATH_LITERAL(""); } -PropertyNode AccessibilityTreeFormatterBase::GetMatchingPropertyNode( - const base::string16& line_index, - const base::string16& property_name) { - // Find the first allow-filter matching the line index and the property name. +std::vector<PropertyNode> +AccessibilityTreeFormatterBase::PropertyFilterNodesFor( + const std::string& line_index) const { + std::vector<PropertyNode> list; for (const auto& filter : property_filters_) { PropertyNode property_node = PropertyNode::FromPropertyFilter(filter); - // Skip if the line index filter doesn't matched (if specified). + // Filter out if doesn't match line index (if specified). if (!property_node.line_indexes.empty() && std::find(property_node.line_indexes.begin(), property_node.line_indexes.end(), @@ -434,27 +463,31 @@ PropertyNode AccessibilityTreeFormatterBase::GetMatchingPropertyNode( continue; } - // The filter should be either an exact property match or a wildcard - // matching to support filter collections like AXRole* which matches - // AXRoleDescription. - if (property_name == property_node.name_or_value || - base::MatchPattern(property_name, property_node.name_or_value)) { - switch (filter.type) { - case PropertyFilter::ALLOW_EMPTY: - case PropertyFilter::ALLOW: - return property_node; - case PropertyFilter::DENY: - break; - default: - break; - } + switch (filter.type) { + case PropertyFilter::ALLOW_EMPTY: + case PropertyFilter::ALLOW: + list.push_back(std::move(property_node)); + break; + case PropertyFilter::DENY: + break; + default: + break; } } - return PropertyNode(); + return list; +} + +bool AccessibilityTreeFormatterBase::HasMatchAllPropertyFilter() const { + for (const auto& filter : property_filters_) { + if (filter.type == PropertyFilter::ALLOW && filter.match_str == "*") { + return true; + } + } + return false; } bool AccessibilityTreeFormatterBase::MatchesPropertyFilters( - const base::string16& text, + const std::string& text, bool default_result) const { return AccessibilityTreeFormatter::MatchesPropertyFilters( property_filters_, text, default_result); @@ -507,7 +540,7 @@ bool AccessibilityTreeFormatterBase::WriteAttribute(bool include_by_default, base::string16* line) { if (attr.empty()) return false; - if (!MatchesPropertyFilters(attr, include_by_default)) + if (!MatchesPropertyFilters(base::UTF16ToUTF8(attr), include_by_default)) return false; if (!line->empty()) *line += base::ASCIIToUTF16(" "); @@ -519,7 +552,7 @@ void AccessibilityTreeFormatterBase::AddPropertyFilter( std::vector<PropertyFilter>* property_filters, std::string filter, PropertyFilter::Type type) { - property_filters->push_back(PropertyFilter(base::ASCIIToUTF16(filter), type)); + property_filters->push_back(PropertyFilter(filter, type)); } void AccessibilityTreeFormatterBase::AddDefaultFilters( diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_base.h b/chromium/content/browser/accessibility/accessibility_tree_formatter_base.h index 13ebfad47d3..20f52d52c39 100644 --- a/chromium/content/browser/accessibility/accessibility_tree_formatter_base.h +++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_base.h @@ -48,10 +48,10 @@ class CONTENT_EXPORT PropertyNode final { explicit operator bool() const; // Key name in case of { key: value } dictionary. - base::string16 key; + std::string key; // Value or a property name, for example 3 or AXLineForIndex - base::string16 name_or_value; + std::string name_or_value; // Parameters if it's a property, for example, it is a vector of a single // value 3 in case of AXLineForIndex(3) @@ -59,27 +59,30 @@ class CONTENT_EXPORT PropertyNode final { // Used to store the origianl unparsed property including invocation // parameters if any. - base::string16 original_property; + std::string original_property; // The list of line indexes of accessible objects the property is allowed to // be called for. - std::vector<base::string16> line_indexes; + std::vector<std::string> line_indexes; + + bool IsMatching(const std::string& pattern) const; // Argument conversion methods. bool IsArray() const; bool IsDict() const; base::Optional<int> AsInt() const; - base::Optional<base::string16> FindKey(const char* refkey) const; + const PropertyNode* FindKey(const char* refkey) const; + base::Optional<std::string> FindStringKey(const char* refkey) const; base::Optional<int> FindIntKey(const char* key) const; std::string ToString() const; private: - using iterator = base::string16::const_iterator; + using iterator = std::string::const_iterator; explicit PropertyNode(iterator key_begin, iterator key_end, - const base::string16&); + const std::string&); PropertyNode(iterator begin, iterator end); PropertyNode(iterator key_begin, iterator key_end, @@ -150,11 +153,13 @@ class CONTENT_EXPORT AccessibilityTreeFormatterBase // Overridden by platform subclasses. // - // Returns a property node struct built for a matching property filter, - // which includes a property name and invocation parameters if any. - // If no matching property filter, then empty property node is returned. - PropertyNode GetMatchingPropertyNode(const base::string16& line_index, - const base::string16& property_name); + // Returns property nodes complying to the line index filter for all + // allow/allow_empty property filters. + std::vector<PropertyNode> PropertyFilterNodesFor( + const std::string& line_index) const; + + // Return true if match-all filter is present. + bool HasMatchAllPropertyFilter() const; // Process accessibility tree with filters for output. // Given a dictionary that contains a platform-specific dictionary @@ -205,7 +210,7 @@ class CONTENT_EXPORT AccessibilityTreeFormatterBase base::string16* contents, int depth = 0); - bool MatchesPropertyFilters(const base::string16& text, + bool MatchesPropertyFilters(const std::string& text, bool default_result) const; bool MatchesNodeFilters(const base::DictionaryValue& dict) const; diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_base_unittest.cc b/chromium/content/browser/accessibility/accessibility_tree_formatter_base_unittest.cc index a2d5fdfb547..fb5712420f4 100644 --- a/chromium/content/browser/accessibility/accessibility_tree_formatter_base_unittest.cc +++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_base_unittest.cc @@ -32,8 +32,7 @@ class AccessibilityTreeFormatterBaseTest : public testing::Test { PropertyNode Parse(const char* input) { AccessibilityTreeFormatter::PropertyFilter filter( - base::UTF8ToUTF16(input), - AccessibilityTreeFormatter::PropertyFilter::ALLOW); + input, AccessibilityTreeFormatter::PropertyFilter::ALLOW); return PropertyNode::FromPropertyFilter(filter); } @@ -70,6 +69,10 @@ TEST_F(AccessibilityTreeFormatterBaseTest, ParseProperty) { ParseAndCheck("Text({dict: [1, 2]})", "Text({}(dict: [](1, 2)))"); ParseAndCheck("Text({dict: ValueFor(1)})", "Text({}(dict: ValueFor(1)))"); + // Nested arguments + ParseAndCheck("AXIndexForTextMarker(AXTextMarkerForIndex(0))", + "AXIndexForTextMarker(AXTextMarkerForIndex(0))"); + // Line indexes filter. ParseAndCheck(":3,:5;AXDOMClassList", ":3,:5;AXDOMClassList"); @@ -84,17 +87,38 @@ TEST_F(AccessibilityTreeFormatterBaseTest, ParseProperty) { EXPECT_EQ(GetArgumentNode("ChildAt(3)").IsDict(), false); EXPECT_EQ(GetArgumentNode("ChildAt(3)").IsArray(), false); EXPECT_EQ(GetArgumentNode("ChildAt(3)").AsInt(), 3); - EXPECT_EQ(GetArgumentNode("Text({start: :1, dir: forward})").FindKey("start"), - base::ASCIIToUTF16(":1")); - EXPECT_EQ(GetArgumentNode("Text({start: :1, dir: forward})").FindKey("dir"), - base::ASCIIToUTF16("forward")); + + // Dict: FindStringKey EXPECT_EQ( - GetArgumentNode("Text({start: :1, dir: forward})").FindKey("notexists"), - base::nullopt); + GetArgumentNode("Text({start: :1, dir: forward})").FindStringKey("start"), + ":1"); + EXPECT_EQ( + GetArgumentNode("Text({start: :1, dir: forward})").FindStringKey("dir"), + "forward"); + EXPECT_EQ(GetArgumentNode("Text({start: :1, dir: forward})") + .FindStringKey("notexists"), + base::nullopt); + + // Dict: FindIntKey EXPECT_EQ(GetArgumentNode("Text({loc: 3, len: 2})").FindIntKey("loc"), 3); EXPECT_EQ(GetArgumentNode("Text({loc: 3, len: 2})").FindIntKey("len"), 2); EXPECT_EQ(GetArgumentNode("Text({loc: 3, len: 2})").FindIntKey("notexists"), base::nullopt); + + // Dict: FindKey + EXPECT_EQ(GetArgumentNode("Text({anchor: {:1, 0, up}})") + .FindKey("anchor") + ->ToString(), + "anchor: {}(:1, 0, up)"); + + EXPECT_EQ(GetArgumentNode("Text({anchor: {:1, 0, up}})").FindKey("focus"), + nullptr); + + EXPECT_EQ(GetArgumentNode("AXStringForTextMarkerRange({anchor: {:2, 1, " + "down}, focus: {:2, 2, down}})") + .FindKey("anchor") + ->ToString(), + "anchor: {}(:2, 1, down)"); } } // namespace content diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_blink.cc b/chromium/content/browser/accessibility/accessibility_tree_formatter_blink.cc index abc989bacd5..2f5be04faf4 100644 --- a/chromium/content/browser/accessibility/accessibility_tree_formatter_blink.cc +++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_blink.cc @@ -89,12 +89,14 @@ std::string IntAttrToString(const BrowserAccessibility& node, return ui::ToString(static_cast<ax::mojom::Restriction>(value)); case ax::mojom::IntAttribute::kSortDirection: return ui::ToString(static_cast<ax::mojom::SortDirection>(value)); + case ax::mojom::IntAttribute::kTextAlign: + return ui::ToString(static_cast<ax::mojom::TextAlign>(value)); case ax::mojom::IntAttribute::kTextOverlineStyle: case ax::mojom::IntAttribute::kTextStrikethroughStyle: case ax::mojom::IntAttribute::kTextUnderlineStyle: return ui::ToString(static_cast<ax::mojom::TextDecorationStyle>(value)); case ax::mojom::IntAttribute::kTextDirection: - return ui::ToString(static_cast<ax::mojom::TextDirection>(value)); + return ui::ToString(static_cast<ax::mojom::WritingDirection>(value)); case ax::mojom::IntAttribute::kTextPosition: return ui::ToString(static_cast<ax::mojom::TextPosition>(value)); case ax::mojom::IntAttribute::kImageAnnotationStatus: @@ -164,7 +166,6 @@ void AccessibilityTreeFormatterBlink::AddDefaultFilters( // Too flaky: hovered, offscreen // States AddPropertyFilter(property_filters, "collapsed"); - AddPropertyFilter(property_filters, "haspopup"); AddPropertyFilter(property_filters, "invisible"); AddPropertyFilter(property_filters, "multiline"); AddPropertyFilter(property_filters, "protected"); @@ -631,4 +632,8 @@ const std::string AccessibilityTreeFormatterBlink::GetDenyNodeString() { return "@BLINK-DENY-NODE:"; } +const std::string AccessibilityTreeFormatterBlink::GetRunUntilEventString() { + return "@BLINK-RUN-UNTIL-EVENT:"; +} + } // namespace content diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_blink.h b/chromium/content/browser/accessibility/accessibility_tree_formatter_blink.h index 29f82dfd6af..440ced2e774 100644 --- a/chromium/content/browser/accessibility/accessibility_tree_formatter_blink.h +++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_blink.h @@ -42,6 +42,7 @@ class CONTENT_EXPORT AccessibilityTreeFormatterBlink const std::string GetAllowString() override; const std::string GetDenyString() override; const std::string GetDenyNodeString() override; + const std::string GetRunUntilEventString() override; void RecursiveBuildAccessibilityTree(const BrowserAccessibility& node, base::DictionaryValue* dict) const; diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_mac.mm b/chromium/content/browser/accessibility/accessibility_tree_formatter_mac.mm index bc23c9cea29..f60a93dd95c 100644 --- a/chromium/content/browser/accessibility/accessibility_tree_formatter_mac.mm +++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_mac.mm @@ -4,17 +4,14 @@ #include "content/browser/accessibility/accessibility_tree_formatter_base.h" -#import <Cocoa/Cocoa.h> - #include "base/files/file_path.h" -#include "base/json/json_writer.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "content/browser/accessibility/accessibility_tree_formatter_blink.h" -#include "content/browser/accessibility/browser_accessibility_cocoa.h" +#include "content/browser/accessibility/accessibility_tree_formatter_utils_mac.h" #include "content/browser/accessibility/browser_accessibility_mac.h" #include "content/browser/accessibility/browser_accessibility_manager.h" @@ -26,7 +23,11 @@ using base::StringPrintf; using base::SysNSStringToUTF8; using base::SysNSStringToUTF16; +using base::SysUTF16ToNSString; using std::string; +using content::a11y::LineIndexesMap; +using content::a11y::OptionalNSObject; +using content::a11y::AttributeInvoker; namespace content { @@ -46,34 +47,6 @@ const char kConstValuePrefix[] = "_const_"; const char kNULLValue[] = "_const_NULL"; const char kFailedToParseArgsError[] = "_const_ERROR:FAILED_TO_PARSE_ARGS"; -#define INT_FAIL(propnode, msg) \ - LOG(ERROR) << "Failed to parse " << propnode.original_property \ - << " to Int: " << msg; \ - return nil; - -#define INTARRAY_FAIL(propnode, msg) \ - LOG(ERROR) << "Failed to parse " << propnode.original_property \ - << " to IntArray: " << msg; \ - return nil; - -#define NSRANGE_FAIL(propnode, msg) \ - LOG(ERROR) << "Failed to parse " << propnode.original_property \ - << " to NSRange: " << msg; \ - return nil; - -#define UIELEMENT_FAIL(propnode, msg) \ - LOG(ERROR) << "Failed to parse " << propnode.original_property \ - << " to UIElement: " << msg; \ - return nil; - -#define TEXTMARKER_FAIL(propnode, msg) \ - LOG(ERROR) << "Failed to parse " << propnode.original_property \ - << " to AXTextMarker: " << msg \ - << ". Expected format: {anchor, offset, affinity}, where anchor " \ - "is :line_num, offset is integer, affinity is either down, " \ - "up or none"; \ - return nil; - } // namespace class AccessibilityTreeFormatterMac : public AccessibilityTreeFormatterBase { @@ -95,57 +68,26 @@ class AccessibilityTreeFormatterMac : public AccessibilityTreeFormatterBase { const base::StringPiece& pattern) override; private: - using LineIndexesMap = - std::map<const gfx::NativeViewAccessible, base::string16>; - void RecursiveBuildAccessibilityTree(const BrowserAccessibilityCocoa* node, const LineIndexesMap& line_indexes_map, base::DictionaryValue* dict); - void RecursiveBuildLineIndexesMap(const BrowserAccessibilityCocoa* node, - LineIndexesMap* line_indexes_map, - int* counter); base::FilePath::StringType GetExpectedFileSuffix() override; const std::string GetAllowEmptyString() override; const std::string GetAllowString() override; const std::string GetDenyString() override; const std::string GetDenyNodeString() override; + const std::string GetRunUntilEventString() override; void AddProperties(const BrowserAccessibilityCocoa* node, const LineIndexesMap& line_indexes_map, base::Value* dict); - // Helper class used to compute a parameter for a parameterized attribute - // call. Can be either id or error. Similar to base::Optional, but allows nil - // id as a valid value. - class IdOrError { - public: - IdOrError() : value(nil), error(false) {} - - IdOrError& operator=(id other_value) { - error = !other_value; - value = other_value; - return *this; - } - - bool IsError() const { return error; } - bool IsNotNil() const { return !!value; } - constexpr const id& operator*() const& { return value; } - - private: - id value; - bool error; - }; - - IdOrError ParamByPropertyNode(const PropertyNode&, - const LineIndexesMap&) const; - NSNumber* PropertyNodeToInt(const PropertyNode&) const; - NSArray* PropertyNodeToIntArray(const PropertyNode&) const; - NSValue* PropertyNodeToRange(const PropertyNode&) const; - gfx::NativeViewAccessible PropertyNodeToUIElement( - const PropertyNode&, - const LineIndexesMap&) const; - id PropertyNodeToTextMarker(const PropertyNode&, const LineIndexesMap&) const; + // Invokes an attributes by a property node. + OptionalNSObject InvokeAttributeFor( + const BrowserAccessibilityCocoa* cocoa_node, + const PropertyNode& property_node, + const LineIndexesMap& line_indexes_map) const; base::Value PopulateSize(const BrowserAccessibilityCocoa*) const; base::Value PopulatePosition(const BrowserAccessibilityCocoa*) const; @@ -159,9 +101,6 @@ class AccessibilityTreeFormatterMac : public AccessibilityTreeFormatterBase { const LineIndexesMap& line_indexes_map) const; std::string NodeToLineIndex(id, const LineIndexesMap&) const; - gfx::NativeViewAccessible LineIndexToNode( - const base::string16 line_index, - const LineIndexesMap& line_indexes_map) const; base::string16 ProcessTreeForOutput( const base::DictionaryValue& node, @@ -212,9 +151,7 @@ AccessibilityTreeFormatterMac::BuildAccessibilityTree( BrowserAccessibilityCocoa* cocoa_root = ToBrowserAccessibilityCocoa(root); - int counter = 0; - LineIndexesMap line_indexes_map; - RecursiveBuildLineIndexesMap(cocoa_root, &line_indexes_map, &counter); + LineIndexesMap line_indexes_map(cocoa_root); std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue); RecursiveBuildAccessibilityTree(cocoa_root, line_indexes_map, dict.get()); @@ -259,18 +196,6 @@ void AccessibilityTreeFormatterMac::RecursiveBuildAccessibilityTree( dict->Set(kChildrenDictAttr, std::move(children)); } -void AccessibilityTreeFormatterMac::RecursiveBuildLineIndexesMap( - const BrowserAccessibilityCocoa* cocoa_node, - LineIndexesMap* line_indexes_map, - int* counter) { - const base::string16 line_index = - base::string16(1, ':') + base::NumberToString16(++(*counter)); - line_indexes_map->insert({cocoa_node, line_index}); - for (BrowserAccessibilityCocoa* cocoa_child in [cocoa_node children]) { - RecursiveBuildLineIndexesMap(cocoa_child, line_indexes_map, counter); - } -} - void AccessibilityTreeFormatterMac::AddProperties( const BrowserAccessibilityCocoa* cocoa_node, const LineIndexesMap& line_indexes_map, @@ -279,42 +204,32 @@ void AccessibilityTreeFormatterMac::AddProperties( BrowserAccessibility* node = [cocoa_node owner]; dict->SetKey("id", base::Value(base::NumberToString16(node->GetId()))); - base::string16 line_index = base::ASCIIToUTF16("-1"); - if (line_indexes_map.find(cocoa_node) != line_indexes_map.end()) { - line_index = line_indexes_map.at(cocoa_node); - } - - // Attributes - for (NSString* supportedAttribute in - [cocoa_node accessibilityAttributeNames]) { - if (GetMatchingPropertyNode(line_index, - SysNSStringToUTF16(supportedAttribute))) { - id value = [cocoa_node accessibilityAttributeValue:supportedAttribute]; - if (value != nil) { - dict->SetPath(SysNSStringToUTF8(supportedAttribute), - PopulateObject(value, line_indexes_map)); - } + // Dump all attributes if match-all filter is specified. + if (HasMatchAllPropertyFilter()) { + for (NSString* attribute : [cocoa_node accessibilityAttributeNames]) { + dict->SetPath( + SysNSStringToUTF8(attribute), + PopulateObject([cocoa_node accessibilityAttributeValue:attribute], + line_indexes_map)); } + return; } - // Parameterized attributes - for (NSString* supportedAttribute in - [cocoa_node accessibilityParameterizedAttributeNames]) { - auto propnode = GetMatchingPropertyNode( - line_index, SysNSStringToUTF16(supportedAttribute)); - IdOrError param = ParamByPropertyNode(propnode, line_indexes_map); - if (param.IsError()) { - dict->SetPath(base::UTF16ToUTF8(propnode.original_property), - base::Value(kFailedToParseArgsError)); + // Otherwise dump attributes matching allow filters only. + std::string line_index = line_indexes_map.IndexBy(cocoa_node); + for (const PropertyNode& property_node : PropertyFilterNodesFor(line_index)) { + AttributeInvoker invoker(cocoa_node, line_indexes_map); + OptionalNSObject value = invoker.Invoke(property_node); + if (value.IsNotApplicable()) { continue; } - - if (param.IsNotNil()) { - id value = [cocoa_node accessibilityAttributeValue:supportedAttribute - forParameter:*param]; - dict->SetPath(base::UTF16ToUTF8(propnode.original_property), - PopulateObject(value, line_indexes_map)); + if (value.IsError()) { + dict->SetPath(property_node.original_property, + base::Value(kFailedToParseArgsError)); + continue; } + dict->SetPath(property_node.original_property, + PopulateObject(*value, line_indexes_map)); } // Position and size @@ -322,151 +237,6 @@ void AccessibilityTreeFormatterMac::AddProperties( dict->SetPath(kSizeDictAttr, PopulateSize(cocoa_node)); } -AccessibilityTreeFormatterMac::IdOrError -AccessibilityTreeFormatterMac::ParamByPropertyNode( - const PropertyNode& property_node, - const LineIndexesMap& line_indexes_map) const { - IdOrError param; - std::string property_name = base::UTF16ToASCII(property_node.name_or_value); - - if (property_name == "AXLineForIndex") { // Int - param = PropertyNodeToInt(property_node); - } else if (property_name == "AXCellForColumnAndRow") { // IntArray - param = PropertyNodeToIntArray(property_node); - } else if (property_name == "AXStringForRange") { // NSRange - param = PropertyNodeToRange(property_node); - } else if (property_name == "AXIndexForChildUIElement") { // UIElement - param = PropertyNodeToUIElement(property_node, line_indexes_map); - } else if (property_name == "AXIndexForTextMarker") { // TextMarker - param = PropertyNodeToTextMarker(property_node, line_indexes_map); - } - - return param; -} - -// NSNumber. Format: integer. -NSNumber* AccessibilityTreeFormatterMac::PropertyNodeToInt( - const PropertyNode& propnode) const { - if (propnode.parameters.size() != 1) { - INT_FAIL(propnode, "single argument is expected") - } - - const auto& intnode = propnode.parameters[0]; - base::Optional<int> param = intnode.AsInt(); - if (!param) { - INT_FAIL(propnode, "not a number") - } - return [NSNumber numberWithInt:*param]; -} - -// NSArray of two NSNumber. Format: [integer, integer]. -NSArray* AccessibilityTreeFormatterMac::PropertyNodeToIntArray( - const PropertyNode& propnode) const { - if (propnode.parameters.size() != 1) { - INTARRAY_FAIL(propnode, "single argument is expected") - } - - const auto& arraynode = propnode.parameters[0]; - if (arraynode.name_or_value != base::ASCIIToUTF16("[]")) { - INTARRAY_FAIL(propnode, "not array") - } - - NSMutableArray* array = - [[NSMutableArray alloc] initWithCapacity:arraynode.parameters.size()]; - for (const auto& paramnode : arraynode.parameters) { - base::Optional<int> param = paramnode.AsInt(); - if (!param) { - INTARRAY_FAIL(propnode, paramnode.name_or_value + - base::UTF8ToUTF16(" is not a number")) - } - [array addObject:@(*param)]; - } - return array; -} - -// NSRange. Format: {loc: integer, len: integer}. -NSValue* AccessibilityTreeFormatterMac::PropertyNodeToRange( - const PropertyNode& propnode) const { - if (propnode.parameters.size() != 1) { - NSRANGE_FAIL(propnode, "single argument is expected") - } - - const auto& dictnode = propnode.parameters[0]; - if (!dictnode.IsDict()) { - NSRANGE_FAIL(propnode, "dictionary is expected") - } - - base::Optional<int> loc = dictnode.FindIntKey("loc"); - if (!loc) { - NSRANGE_FAIL(propnode, "no loc or loc is not a number") - } - - base::Optional<int> len = dictnode.FindIntKey("len"); - if (!len) { - NSRANGE_FAIL(propnode, "no len or len is not a number") - } - - return [NSValue valueWithRange:NSMakeRange(*loc, *len)]; -} - -// UIElement. Format: :line_num. -gfx::NativeViewAccessible -AccessibilityTreeFormatterMac::PropertyNodeToUIElement( - const PropertyNode& propnode, - const LineIndexesMap& line_indexes_map) const { - if (propnode.parameters.size() != 1) { - UIELEMENT_FAIL(propnode, "single argument is expected") - } - - gfx::NativeViewAccessible uielement = - LineIndexToNode(propnode.parameters[0].name_or_value, line_indexes_map); - if (!uielement) { - UIELEMENT_FAIL(propnode, "no corresponding UIElement was found in the tree") - } - return uielement; -} - -id AccessibilityTreeFormatterMac::PropertyNodeToTextMarker( - const PropertyNode& propnode, - const LineIndexesMap& line_indexes_map) const { - if (propnode.parameters.size() != 1) { - TEXTMARKER_FAIL(propnode, "single argument is expected") - } - - const auto& tmnode = propnode.parameters[0]; - if (!tmnode.IsDict()) { - TEXTMARKER_FAIL(propnode, "dictionary is expected") - } - if (tmnode.parameters.size() != 3) { - TEXTMARKER_FAIL(propnode, "wrong number of dictionary elements") - } - - BrowserAccessibilityCocoa* anchor_cocoa = - LineIndexToNode(tmnode.parameters[0].name_or_value, line_indexes_map); - if (!anchor_cocoa) { - TEXTMARKER_FAIL(propnode, "1st argument: wrong anchor") - } - - base::Optional<int> offset = tmnode.parameters[1].AsInt(); - if (!offset) { - TEXTMARKER_FAIL(propnode, "2nd argument: wrong offset") - } - - ax::mojom::TextAffinity affinity; - const base::string16& affinity_str = tmnode.parameters[2].name_or_value; - if (affinity_str == base::UTF8ToUTF16("none")) { - affinity = ax::mojom::TextAffinity::kNone; - } else if (affinity_str == base::UTF8ToUTF16("down")) { - affinity = ax::mojom::TextAffinity::kDownstream; - } else if (affinity_str == base::UTF8ToUTF16("up")) { - affinity = ax::mojom::TextAffinity::kUpstream; - } else { - TEXTMARKER_FAIL(propnode, "3rd argument: wrong affinity") - } - - return content::AXTextMarkerFrom(anchor_cocoa, *offset, affinity); -} - base::Value AccessibilityTreeFormatterMac::PopulateSize( const BrowserAccessibilityCocoa* cocoa_node) const { base::Value size(base::Value::Type::DICTIONARY); @@ -483,8 +253,8 @@ base::Value AccessibilityTreeFormatterMac::PopulatePosition( DCHECK(root_manager); // The NSAccessibility position of an object is in global coordinates and - // based on the lower-left corner of the object. To make this easier and less - // confusing, convert it to local window coordinates using the top-left + // based on the lower-left corner of the object. To make this easier and + // less confusing, convert it to local window coordinates using the top-left // corner when dumping the position. BrowserAccessibility* root = root_manager->GetRoot(); BrowserAccessibilityCocoa* cocoa_root = ToBrowserAccessibilityCocoa(root); @@ -617,24 +387,7 @@ base::Value AccessibilityTreeFormatterMac::PopulateArray( std::string AccessibilityTreeFormatterMac::NodeToLineIndex( id cocoa_node, const LineIndexesMap& line_indexes_map) const { - std::string line_index = ":unknown"; - auto index_iterator = line_indexes_map.find(cocoa_node); - if (index_iterator != line_indexes_map.end()) { - line_index = base::UTF16ToUTF8(index_iterator->second); - } - return kConstValuePrefix + line_index; -} - -gfx::NativeViewAccessible AccessibilityTreeFormatterMac::LineIndexToNode( - const base::string16 line_index, - const LineIndexesMap& line_indexes_map) const { - for (std::pair<const gfx::NativeViewAccessible, base::string16> item : - line_indexes_map) { - if (item.second == line_index) { - return item.first; - } - } - return nil; + return kConstValuePrefix + line_indexes_map.IndexBy(cocoa_node); } base::string16 AccessibilityTreeFormatterMac::ProcessTreeForOutput( @@ -771,6 +524,10 @@ const string AccessibilityTreeFormatterMac::GetDenyNodeString() { return "@MAC-DENY-NODE:"; } +const std::string AccessibilityTreeFormatterMac::GetRunUntilEventString() { + return "@MAC-RUN-UNTIL-EVENT:"; +} + } // namespace content #pragma clang diagnostic pop diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_mac_browsertest.mm b/chromium/content/browser/accessibility/accessibility_tree_formatter_mac_browsertest.mm index 5f6db82d33d..e936844370d 100644 --- a/chromium/content/browser/accessibility/accessibility_tree_formatter_mac_browsertest.mm +++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_mac_browsertest.mm @@ -67,8 +67,7 @@ void AccessibilityTreeFormatterMacBrowserTest::TestAndCheck( for (const char* filter : filters) { property_filters.push_back(AccessibilityTreeFormatter::PropertyFilter( - base::UTF8ToUTF16(filter), - AccessibilityTreeFormatter::PropertyFilter::ALLOW_EMPTY)); + filter, AccessibilityTreeFormatter::PropertyFilter::ALLOW_EMPTY)); } formatter->AddDefaultFilters(&property_filters); @@ -122,7 +121,18 @@ IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest, } IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest, - LineIndexFilter) { + Filters_NoWildcardProperty) { + TestAndCheck(R"~~(data:text/html, + <input class='classolasso'>)~~", + {"AXDOMClassList"}, + R"~~(AXWebArea AXDOMClassList=[] +++AXGroup AXDOMClassList=[] +++++AXTextField AXDOMClassList=['classolasso'] +)~~"); +} + +IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest, + Filters_LineIndex) { TestAndCheck(R"~~(data:text/html, <input class='input_at_3rd_line'> <input class='input_at_4th_line'> @@ -154,7 +164,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest, </script>)~~", {":3;AXSelectedTextMarkerRange=*"}, R"~~(AXWebArea ++AXGroup -++++AXStaticText AXSelectedTextMarkerRange={anchor: {:3, 0, down}, focus: {:2, -1, down}} AXValue='Paragraph' +++++AXStaticText AXSelectedTextMarkerRange={anchor: {:2, -1, down}, focus: {:3, 0, down}} AXValue='Paragraph' )~~"); } @@ -290,4 +300,42 @@ IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest, )~~"); } +IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest, + ParameterizedAttributes_TextMarkerRange) { + TestAndCheck(R"~~(data:text/html, + <p>Text</p>)~~", + {":2;AXStringForTextMarkerRange({anchor: {:2, 1, down}, focus: " + "{:2, 3, down}})=*"}, + R"~~(AXWebArea +++AXGroup AXStringForTextMarkerRange({anchor: {:2, 1, down}, focus: {:2, 3, down}})='ex' +++++AXStaticText AXValue='Text' +)~~"); +} + +IN_PROC_BROWSER_TEST_F( + AccessibilityTreeFormatterMacBrowserTest, + ParameterizedAttributes_TextMarkerRange_WrongParameters) { + TestWrongParameters( + R"~~(data:text/html, + <p>Text</p>)~~", + {"1, 2", "2", "{focus: {:2, 1, down}}", "{anchor: {:2, 1, down}}", + "{anchor: {2, 1, down}, focus: {2, 1, down}}"}, + ":1;AXStringForTextMarkerRange(Argument)=*", + R"~~(AXWebArea AXStringForTextMarkerRange(Argument)=ERROR:FAILED_TO_PARSE_ARGS +++AXGroup +++++AXStaticText AXValue='Text' +)~~"); +} + +IN_PROC_BROWSER_TEST_F(AccessibilityTreeFormatterMacBrowserTest, + NestedCalls_Attributes) { + TestAndCheck(R"~~(data:text/html, + <p>Text</p>)~~", + {":1;AXIndexForTextMarker(AXTextMarkerForIndex(0))"}, + R"~~(AXWebArea AXIndexForTextMarker(AXTextMarkerForIndex(0))=0 +++AXGroup +++++AXStaticText AXValue='Text' +)~~"); +} + } // namespace content diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_uia_win.cc b/chromium/content/browser/accessibility/accessibility_tree_formatter_uia_win.cc index b7181143268..b751a10e81f 100644 --- a/chromium/content/browser/accessibility/accessibility_tree_formatter_uia_win.cc +++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_uia_win.cc @@ -1200,4 +1200,8 @@ const std::string AccessibilityTreeFormatterUia::GetDenyNodeString() { return "@UIA-WIN-DENY-NODE:"; } +const std::string AccessibilityTreeFormatterUia::GetRunUntilEventString() { + return "@UIA-WIN-RUN-UNTIL-EVENT:"; +} + } // namespace content diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_uia_win.h b/chromium/content/browser/accessibility/accessibility_tree_formatter_uia_win.h index 45fbaaa46a9..86c85f29731 100644 --- a/chromium/content/browser/accessibility/accessibility_tree_formatter_uia_win.h +++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_uia_win.h @@ -100,6 +100,7 @@ class AccessibilityTreeFormatterUia : public AccessibilityTreeFormatterBase { const std::string GetAllowString() override; const std::string GetDenyString() override; const std::string GetDenyNodeString() override; + const std::string GetRunUntilEventString() override; base::string16 ProcessTreeForOutput( const base::DictionaryValue& node, base::DictionaryValue* filtered_result = nullptr) override; diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_utils_mac.h b/chromium/content/browser/accessibility/accessibility_tree_formatter_utils_mac.h new file mode 100644 index 00000000000..5dd04e900cf --- /dev/null +++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_utils_mac.h @@ -0,0 +1,97 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_UTILS_MAC_H_ +#define CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_UTILS_MAC_H_ + +#include "content/browser/accessibility/accessibility_tree_formatter_base.h" +#include "content/browser/accessibility/browser_accessibility_cocoa.h" + +namespace content { +namespace a11y { + +/** + * Converts cocoa node object to a line index in the formatted accessibility + * tree, the node is placed at, and vice versa. + */ +class LineIndexesMap { + public: + LineIndexesMap(const BrowserAccessibilityCocoa* cocoa_node); + ~LineIndexesMap(); + + std::string IndexBy(const BrowserAccessibilityCocoa* cocoa_node) const; + gfx::NativeViewAccessible NodeBy(const std::string& index) const; + + private: + void Build(const BrowserAccessibilityCocoa* cocoa_node, int* counter); + + std::map<const gfx::NativeViewAccessible, std::string> map; +}; + +// Implements stateful id values. Can be either id or be in +// error or not applciable state. Similar to base::Optional, but tri-state +// allowing nullable values. +class OptionalNSObject final { + public: + enum { ID, ERROR, NOT_APPLICABLE }; + + static OptionalNSObject Error() { return OptionalNSObject(ERROR); } + static OptionalNSObject NotApplicable() { + return OptionalNSObject(NOT_APPLICABLE); + } + static OptionalNSObject NotNilOrError(id other_value) { + return OptionalNSObject(other_value, other_value ? ID : ERROR); + } + static OptionalNSObject NotNullOrNotApplicable(id other_value) { + return OptionalNSObject(other_value, other_value ? ID : NOT_APPLICABLE); + } + + OptionalNSObject(int flag) : value(nil), flag(flag) {} + OptionalNSObject(id value, int flag = ID) : value(value), flag(flag) {} + + bool IsNotApplicable() const { return flag == NOT_APPLICABLE; } + bool IsError() const { return flag == ERROR; } + bool IsNotNil() const { return value != nil; } + constexpr const id& operator*() const& { return value; } + + private: + id value; + int flag; +}; + +// Invokes attributes matching the given property filter. +class AttributeInvoker final { + public: + AttributeInvoker(const BrowserAccessibilityCocoa* cocoa_node, + const LineIndexesMap& line_indexes_map); + + // Invokes an attribute matching to a property filter. + OptionalNSObject Invoke(const PropertyNode& property_node) const; + + private: + // Returns a parameterized attribute parameter by a property node. + OptionalNSObject ParamByPropertyNode(const PropertyNode&) const; + + NSNumber* PropertyNodeToInt(const PropertyNode&) const; + NSArray* PropertyNodeToIntArray(const PropertyNode&) const; + NSValue* PropertyNodeToRange(const PropertyNode&) const; + gfx::NativeViewAccessible PropertyNodeToUIElement(const PropertyNode&) const; + + id DictNodeToTextMarker(const PropertyNode&) const; + id PropertyNodeToTextMarker(const PropertyNode&) const; + id PropertyNodeToTextMarkerRange(const PropertyNode&) const; + + gfx::NativeViewAccessible LineIndexToNode( + const base::string16 line_index) const; + + const BrowserAccessibilityCocoa* cocoa_node; + const LineIndexesMap& line_indexes_map; + const NSArray* attributes; + const NSArray* parameterized_attributes; +}; + +} // namespace a11y +} // namespace content + +#endif diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_utils_mac.mm b/chromium/content/browser/accessibility/accessibility_tree_formatter_utils_mac.mm new file mode 100644 index 00000000000..71126d0224f --- /dev/null +++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_utils_mac.mm @@ -0,0 +1,298 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/accessibility/accessibility_tree_formatter_utils_mac.h" + +#include "base/strings/sys_string_conversions.h" + +// error: 'accessibilityAttributeNames' is deprecated: first deprecated in +// macOS 10.10 - Use the NSAccessibility protocol methods instead (see +// NSAccessibilityProtocols.h +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + +using base::SysNSStringToUTF8; + +namespace content { +namespace a11y { + +namespace { + +#define INT_FAIL(property_node, msg) \ + LOG(ERROR) << "Failed to parse " << property_node.name_or_value \ + << " to Int: " << msg; \ + return nil; + +#define INTARRAY_FAIL(property_node, msg) \ + LOG(ERROR) << "Failed to parse " << property_node.name_or_value \ + << " to IntArray: " << msg; \ + return nil; + +#define NSRANGE_FAIL(property_node, msg) \ + LOG(ERROR) << "Failed to parse " << property_node.name_or_value \ + << " to NSRange: " << msg; \ + return nil; + +#define UIELEMENT_FAIL(property_node, msg) \ + LOG(ERROR) << "Failed to parse " << property_node.name_or_value \ + << " to UIElement: " << msg; \ + return nil; + +#define TEXTMARKER_FAIL(property_node, msg) \ + LOG(ERROR) << "Failed to parse " << property_node.name_or_value \ + << " to AXTextMarker: " << msg \ + << ". Expected format: {anchor, offset, affinity}, where anchor " \ + "is :line_num, offset is integer, affinity is either down, " \ + "up or none"; \ + return nil; + +} // namespace + +LineIndexesMap::LineIndexesMap(const BrowserAccessibilityCocoa* cocoa_node) { + int counter = 0; + Build(cocoa_node, &counter); +} + +LineIndexesMap::~LineIndexesMap() {} + +std::string LineIndexesMap::IndexBy( + const BrowserAccessibilityCocoa* cocoa_node) const { + std::string line_index = ":unknown"; + if (map.find(cocoa_node) != map.end()) { + line_index = map.at(cocoa_node); + } + return line_index; +} + +gfx::NativeViewAccessible LineIndexesMap::NodeBy( + const std::string& line_index) const { + for (std::pair<const gfx::NativeViewAccessible, std::string> item : map) { + if (item.second == line_index) { + return item.first; + } + } + return nil; +} + +void LineIndexesMap::Build(const BrowserAccessibilityCocoa* cocoa_node, + int* counter) { + const std::string line_index = + std::string(1, ':') + base::NumberToString(++(*counter)); + map.insert({cocoa_node, line_index}); + for (BrowserAccessibilityCocoa* cocoa_child in [cocoa_node children]) { + Build(cocoa_child, counter); + } +} + +// AttributeInvoker + +AttributeInvoker::AttributeInvoker(const BrowserAccessibilityCocoa* cocoa_node, + const LineIndexesMap& line_indexes_map) + : cocoa_node(cocoa_node), line_indexes_map(line_indexes_map) { + attributes = [cocoa_node accessibilityAttributeNames]; + parameterized_attributes = + [cocoa_node accessibilityParameterizedAttributeNames]; +} + +OptionalNSObject AttributeInvoker::Invoke( + const PropertyNode& property_node) const { + // Attributes + for (NSString* attribute : attributes) { + if (property_node.IsMatching(SysNSStringToUTF8(attribute))) { + return OptionalNSObject::NotNullOrNotApplicable( + [cocoa_node accessibilityAttributeValue:attribute]); + } + } + + // Parameterized attributes + for (NSString* attribute : parameterized_attributes) { + if (property_node.IsMatching(SysNSStringToUTF8(attribute))) { + OptionalNSObject param = ParamByPropertyNode(property_node); + if (param.IsNotNil()) { + return OptionalNSObject([cocoa_node + accessibilityAttributeValue:attribute + forParameter:*param]); + } + return param; + } + } + + return OptionalNSObject::NotApplicable(); +} + +OptionalNSObject AttributeInvoker::ParamByPropertyNode( + const PropertyNode& property_node) const { + // NSAccessibility attributes always take a single parameter. + if (property_node.parameters.size() != 1) { + LOG(ERROR) << "Failed to parse " << property_node.original_property + << ": single parameter is expected"; + return OptionalNSObject::Error(); + } + + // Nested attribute case: attempt to invoke an attribute for an argument node. + const PropertyNode& arg_node = property_node.parameters[0]; + OptionalNSObject subvalue = Invoke(arg_node); + if (!subvalue.IsNotApplicable()) { + return subvalue; + } + + // Otherwise parse argument node value. + const std::string& property_name = property_node.name_or_value; + if (property_name == "AXLineForIndex" || + property_name == "AXTextMarkerForIndex") { // Int + return OptionalNSObject::NotNilOrError(PropertyNodeToInt(arg_node)); + } + if (property_name == "AXCellForColumnAndRow") { // IntArray + return OptionalNSObject::NotNilOrError(PropertyNodeToIntArray(arg_node)); + } + if (property_name == "AXStringForRange") { // NSRange + return OptionalNSObject::NotNilOrError(PropertyNodeToRange(arg_node)); + } + if (property_name == "AXIndexForChildUIElement") { // UIElement + return OptionalNSObject::NotNilOrError(PropertyNodeToUIElement(arg_node)); + } + if (property_name == "AXIndexForTextMarker") { // TextMarker + return OptionalNSObject::NotNilOrError(PropertyNodeToTextMarker(arg_node)); + } + if (property_name == "AXStringForTextMarkerRange") { // TextMarkerRange + return OptionalNSObject::NotNilOrError( + PropertyNodeToTextMarkerRange(arg_node)); + } + + return OptionalNSObject::NotApplicable(); +} + +// NSNumber. Format: integer. +NSNumber* AttributeInvoker::PropertyNodeToInt( + const PropertyNode& intnode) const { + base::Optional<int> param = intnode.AsInt(); + if (!param) { + INT_FAIL(intnode, "not a number") + } + return [NSNumber numberWithInt:*param]; +} + +// NSArray of two NSNumber. Format: [integer, integer]. +NSArray* AttributeInvoker::PropertyNodeToIntArray( + const PropertyNode& arraynode) const { + if (arraynode.name_or_value != "[]") { + INTARRAY_FAIL(arraynode, "not array") + } + + NSMutableArray* array = + [[NSMutableArray alloc] initWithCapacity:arraynode.parameters.size()]; + for (const auto& paramnode : arraynode.parameters) { + base::Optional<int> param = paramnode.AsInt(); + if (!param) { + INTARRAY_FAIL(arraynode, paramnode.name_or_value + " is not a number") + } + [array addObject:@(*param)]; + } + return array; +} + +// NSRange. Format: {loc: integer, len: integer}. +NSValue* AttributeInvoker::PropertyNodeToRange( + const PropertyNode& dictnode) const { + if (!dictnode.IsDict()) { + NSRANGE_FAIL(dictnode, "dictionary is expected") + } + + base::Optional<int> loc = dictnode.FindIntKey("loc"); + if (!loc) { + NSRANGE_FAIL(dictnode, "no loc or loc is not a number") + } + + base::Optional<int> len = dictnode.FindIntKey("len"); + if (!len) { + NSRANGE_FAIL(dictnode, "no len or len is not a number") + } + + return [NSValue valueWithRange:NSMakeRange(*loc, *len)]; +} + +// UIElement. Format: :line_num. +gfx::NativeViewAccessible AttributeInvoker::PropertyNodeToUIElement( + const PropertyNode& uielement_node) const { + gfx::NativeViewAccessible uielement = + line_indexes_map.NodeBy(uielement_node.name_or_value); + if (!uielement) { + UIELEMENT_FAIL(uielement_node, + "no corresponding UIElement was found in the tree") + } + return uielement; +} + +id AttributeInvoker::DictNodeToTextMarker(const PropertyNode& dictnode) const { + if (!dictnode.IsDict()) { + TEXTMARKER_FAIL(dictnode, "dictionary is expected") + } + if (dictnode.parameters.size() != 3) { + TEXTMARKER_FAIL(dictnode, "wrong number of dictionary elements") + } + + BrowserAccessibilityCocoa* anchor_cocoa = + line_indexes_map.NodeBy(dictnode.parameters[0].name_or_value); + if (!anchor_cocoa) { + TEXTMARKER_FAIL(dictnode, "1st argument: wrong anchor") + } + + base::Optional<int> offset = dictnode.parameters[1].AsInt(); + if (!offset) { + TEXTMARKER_FAIL(dictnode, "2nd argument: wrong offset") + } + + ax::mojom::TextAffinity affinity; + const std::string& affinity_str = dictnode.parameters[2].name_or_value; + if (affinity_str == "none") { + affinity = ax::mojom::TextAffinity::kNone; + } else if (affinity_str == "down") { + affinity = ax::mojom::TextAffinity::kDownstream; + } else if (affinity_str == "up") { + affinity = ax::mojom::TextAffinity::kUpstream; + } else { + TEXTMARKER_FAIL(dictnode, "3rd argument: wrong affinity") + } + + return content::AXTextMarkerFrom(anchor_cocoa, *offset, affinity); +} + +id AttributeInvoker::PropertyNodeToTextMarker( + const PropertyNode& dictnode) const { + return DictNodeToTextMarker(dictnode); +} + +id AttributeInvoker::PropertyNodeToTextMarkerRange( + const PropertyNode& rangenode) const { + if (!rangenode.IsDict()) { + TEXTMARKER_FAIL(rangenode, "dictionary is expected") + } + + const PropertyNode* anchornode = rangenode.FindKey("anchor"); + if (!anchornode) { + TEXTMARKER_FAIL(rangenode, "no anchor") + } + + id anchor_textmarker = DictNodeToTextMarker(*anchornode); + if (!anchor_textmarker) { + TEXTMARKER_FAIL(rangenode, "failed to parse anchor") + } + + const PropertyNode* focusnode = rangenode.FindKey("focus"); + if (!focusnode) { + TEXTMARKER_FAIL(rangenode, "no focus") + } + + id focus_textmarker = DictNodeToTextMarker(*focusnode); + if (!focus_textmarker) { + TEXTMARKER_FAIL(rangenode, "failed to parse focus") + } + + return content::AXTextMarkerRangeFrom(anchor_textmarker, focus_textmarker); +} + +} // namespace a11y +} // namespace content + +#pragma clang diagnostic pop diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_win.cc b/chromium/content/browser/accessibility/accessibility_tree_formatter_win.cc index 6b8e3a375b1..f4f137e8498 100644 --- a/chromium/content/browser/accessibility/accessibility_tree_formatter_win.cc +++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_win.cc @@ -71,6 +71,7 @@ class AccessibilityTreeFormatterWin : public AccessibilityTreeFormatterBase { const std::string GetAllowString() override; const std::string GetDenyString() override; const std::string GetDenyNodeString() override; + const std::string GetRunUntilEventString() override; void AddProperties(const Microsoft::WRL::ComPtr<IAccessible>, base::DictionaryValue* dict, LONG root_x, @@ -184,7 +185,7 @@ static HRESULT QuerySimpleDOMNode(IAccessible* accessible, // IA2 Spec dictates that IServiceProvider should be used instead of // QueryInterface when retrieving IAccessible2. Microsoft::WRL::ComPtr<IServiceProvider> service_provider; - HRESULT hr = accessible->QueryInterface(service_provider.GetAddressOf()); + HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider)); if (FAILED(hr)) return hr; return service_provider->QueryService(IID_ISimpleDOMNode, simple_dom_node); @@ -195,7 +196,7 @@ static HRESULT QueryIAccessible2(IAccessible* accessible, // IA2 Spec dictates that IServiceProvider should be used instead of // QueryInterface when retrieving IAccessible2. Microsoft::WRL::ComPtr<IServiceProvider> service_provider; - HRESULT hr = accessible->QueryInterface(service_provider.GetAddressOf()); + HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider)); if (FAILED(hr)) return hr; return service_provider->QueryService(IID_IAccessible2, accessible2); @@ -206,7 +207,7 @@ static HRESULT QueryIAccessibleAction(IAccessible* accessible, // IA2 Spec dictates that IServiceProvider should be used instead of // QueryInterface when retrieving alternate interfaces. Microsoft::WRL::ComPtr<IServiceProvider> service_provider; - HRESULT hr = accessible->QueryInterface(service_provider.GetAddressOf()); + HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider)); if (FAILED(hr)) return hr; @@ -220,7 +221,7 @@ static HRESULT QueryIAccessibleHypertext( // IA2 Spec dictates that IServiceProvider should be used instead of // QueryInterface when retrieving alternate interfaces. Microsoft::WRL::ComPtr<IServiceProvider> service_provider; - HRESULT hr = accessible->QueryInterface(service_provider.GetAddressOf()); + HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider)); if (FAILED(hr)) return hr; return service_provider->QueryService(IID_IAccessibleHypertext, @@ -232,7 +233,7 @@ static HRESULT QueryIAccessibleTable(IAccessible* accessible, // IA2 Spec dictates that IServiceProvider should be used instead of // QueryInterface when retrieving alternate interfaces. Microsoft::WRL::ComPtr<IServiceProvider> service_provider; - HRESULT hr = accessible->QueryInterface(service_provider.GetAddressOf()); + HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider)); if (FAILED(hr)) return hr; return service_provider->QueryService(IID_IAccessibleTable, accessibleTable); @@ -244,7 +245,7 @@ static HRESULT QueryIAccessibleTableCell( // IA2 Spec dictates that IServiceProvider should be used instead of // QueryInterface when retrieving alternate interfaces. Microsoft::WRL::ComPtr<IServiceProvider> service_provider; - HRESULT hr = accessible->QueryInterface(service_provider.GetAddressOf()); + HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider)); if (FAILED(hr)) return hr; return service_provider->QueryService(IID_IAccessibleTableCell, @@ -256,7 +257,7 @@ static HRESULT QueryIAccessibleText(IAccessible* accessible, // IA2 Spec dictates that IServiceProvider should be used instead of // QueryInterface when retrieving alternate interfaces. Microsoft::WRL::ComPtr<IServiceProvider> service_provider; - HRESULT hr = accessible->QueryInterface(service_provider.GetAddressOf()); + HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider)); if (FAILED(hr)) return hr; return service_provider->QueryService(IID_IAccessibleText, accessibleText); @@ -267,7 +268,7 @@ static HRESULT QueryIAccessibleValue(IAccessible* accessible, // IA2 Spec dictates that IServiceProvider should be used instead of // QueryInterface when retrieving alternate interfaces. Microsoft::WRL::ComPtr<IServiceProvider> service_provider; - HRESULT hr = accessible->QueryInterface(service_provider.GetAddressOf()); + HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider)); if (FAILED(hr)) return hr; return service_provider->QueryService(IID_IAccessibleValue, accessibleValue); @@ -372,7 +373,7 @@ void AccessibilityTreeFormatterWin::RecursiveBuildAccessibilityTree( if (child_variant.type() == VT_DISPATCH) { dispatch = V_DISPATCH(child_variant.ptr()); } else if (child_variant.type() == VT_I4) { - hr = node->get_accChild(child_variant, dispatch.GetAddressOf()); + hr = node->get_accChild(child_variant, &dispatch); if (FAILED(hr)) { child_dict->SetString("error", base::ASCIIToUTF16("[Error retrieving child]")); @@ -496,12 +497,11 @@ void AccessibilityTreeFormatterWin::AddMSAAProperties( temp_bstr.Reset(); Microsoft::WRL::ComPtr<IDispatch> parent_dispatch; - if (SUCCEEDED(node->get_accParent(parent_dispatch.GetAddressOf()))) { + if (SUCCEEDED(node->get_accParent(&parent_dispatch))) { Microsoft::WRL::ComPtr<IAccessible> parent_accessible; if (!parent_dispatch) { dict->SetString("parent", "[null]"); - } else if (SUCCEEDED( - parent_dispatch.CopyTo(parent_accessible.GetAddressOf()))) { + } else if (SUCCEEDED(parent_dispatch.As(&parent_accessible))) { base::win::ScopedVariant parent_ia_role_variant; if (SUCCEEDED(parent_accessible->get_accRole( variant_self, parent_ia_role_variant.Receive()))) @@ -587,7 +587,7 @@ void AccessibilityTreeFormatterWin::AddSimpleDOMNodeProperties( base::DictionaryValue* dict) { Microsoft::WRL::ComPtr<ISimpleDOMNode> simple_dom_node; - if (S_OK != QuerySimpleDOMNode(node.Get(), simple_dom_node.GetAddressOf())) + if (S_OK != QuerySimpleDOMNode(node.Get(), &simple_dom_node)) return; // No IA2Value, we are finished with this node. base::win::ScopedBstr temp_bstr; @@ -603,7 +603,7 @@ bool AccessibilityTreeFormatterWin::AddIA2Properties( const Microsoft::WRL::ComPtr<IAccessible> node, base::DictionaryValue* dict) { Microsoft::WRL::ComPtr<IAccessible2> ia2; - if (S_OK != QueryIAccessible2(node.Get(), ia2.GetAddressOf())) + if (S_OK != QueryIAccessible2(node.Get(), &ia2)) return false; // No IA2, we are finished with this node. LONG ia2_role = 0; @@ -671,7 +671,7 @@ void AccessibilityTreeFormatterWin::AddIA2ActionProperties( const Microsoft::WRL::ComPtr<IAccessible> node, base::DictionaryValue* dict) { Microsoft::WRL::ComPtr<IAccessibleAction> ia2action; - if (S_OK != QueryIAccessibleAction(node.Get(), ia2action.GetAddressOf())) + if (S_OK != QueryIAccessibleAction(node.Get(), &ia2action)) return; // No IA2Value, we are finished with this node. base::win::ScopedBstr temp_bstr; @@ -688,7 +688,7 @@ void AccessibilityTreeFormatterWin::AddIA2HypertextProperties( Microsoft::WRL::ComPtr<IAccessible> node, base::DictionaryValue* dict) { Microsoft::WRL::ComPtr<IAccessibleHypertext> ia2hyper; - if (S_OK != QueryIAccessibleHypertext(node.Get(), ia2hyper.GetAddressOf())) + if (S_OK != QueryIAccessibleHypertext(node.Get(), &ia2hyper)) return; // No IA2, we are finished with this node base::win::ScopedBstr text_bstr; @@ -725,11 +725,10 @@ void AccessibilityTreeFormatterWin::AddIA2HypertextProperties( if (hr == S_OK) { DCHECK_GE(index_of_embed, 0); Microsoft::WRL::ComPtr<IAccessibleHyperlink> embedded_object; - hr = ia2hyper->get_hyperlink(index_of_embed, - embedded_object.GetAddressOf()); + hr = ia2hyper->get_hyperlink(index_of_embed, &embedded_object); DCHECK(SUCCEEDED(hr)); Microsoft::WRL::ComPtr<IAccessible2> ax_embed; - hr = embedded_object.CopyTo(ax_embed.GetAddressOf()); + hr = embedded_object.As(&ax_embed); DCHECK(SUCCEEDED(hr)); hr = ax_embed->get_indexInParent(&child_index); DCHECK(SUCCEEDED(hr)); @@ -757,7 +756,7 @@ void AccessibilityTreeFormatterWin::AddIA2TableProperties( const Microsoft::WRL::ComPtr<IAccessible> node, base::DictionaryValue* dict) { Microsoft::WRL::ComPtr<IAccessibleTable> ia2table; - if (S_OK != QueryIAccessibleTable(node.Get(), ia2table.GetAddressOf())) + if (S_OK != QueryIAccessibleTable(node.Get(), &ia2table)) return; // No IA2Text, we are finished with this node. LONG table_rows; @@ -780,7 +779,7 @@ static base::string16 ProcessAccessiblesArray(IUnknown** accessibles, related_accessibles_string += index > 0 ? L"," : L"<"; Microsoft::WRL::ComPtr<IUnknown> unknown = accessibles[index]; Microsoft::WRL::ComPtr<IAccessible> accessible; - if (SUCCEEDED(unknown.CopyTo(accessible.GetAddressOf()))) { + if (SUCCEEDED(unknown.As(&accessible))) { base::win::ScopedBstr temp_bstr; if (S_OK == accessible->get_accName(variant_self, temp_bstr.Receive())) related_accessibles_string += temp_bstr.Get(); @@ -796,7 +795,7 @@ void AccessibilityTreeFormatterWin::AddIA2TableCellProperties( const Microsoft::WRL::ComPtr<IAccessible> node, base::DictionaryValue* dict) { Microsoft::WRL::ComPtr<IAccessibleTableCell> ia2cell; - if (S_OK != QueryIAccessibleTableCell(node.Get(), ia2cell.GetAddressOf())) + if (S_OK != QueryIAccessibleTableCell(node.Get(), &ia2cell)) return; // No IA2Text, we are finished with this node. LONG n_row_header_cells; @@ -826,7 +825,7 @@ void AccessibilityTreeFormatterWin::AddIA2TextProperties( const Microsoft::WRL::ComPtr<IAccessible> node, base::DictionaryValue* dict) { Microsoft::WRL::ComPtr<IAccessibleText> ia2text; - if (S_OK != QueryIAccessibleText(node.Get(), ia2text.GetAddressOf())) + if (S_OK != QueryIAccessibleText(node.Get(), &ia2text)) return; // No IA2Text, we are finished with this node. LONG n_characters; @@ -892,7 +891,7 @@ void AccessibilityTreeFormatterWin::AddIA2ValueProperties( const Microsoft::WRL::ComPtr<IAccessible> node, base::DictionaryValue* dict) { Microsoft::WRL::ComPtr<IAccessibleValue> ia2value; - if (S_OK != QueryIAccessibleValue(node.Get(), ia2value.GetAddressOf())) + if (S_OK != QueryIAccessibleValue(node.Get(), &ia2value)) return; // No IA2Value, we are finished with this node. base::win::ScopedVariant current_value; @@ -1038,4 +1037,8 @@ const std::string AccessibilityTreeFormatterWin::GetDenyNodeString() { return "@WIN-DENY-NODE:"; } +const std::string AccessibilityTreeFormatterWin::GetRunUntilEventString() { + return "@WIN-RUN-UNTIL-EVENT:"; +} + } // namespace content diff --git a/chromium/content/browser/accessibility/accessibility_win_browsertest.cc b/chromium/content/browser/accessibility/accessibility_win_browsertest.cc index 1c33a05fbef..0520950e461 100644 --- a/chromium/content/browser/accessibility/accessibility_win_browsertest.cc +++ b/chromium/content/browser/accessibility/accessibility_win_browsertest.cc @@ -145,7 +145,7 @@ base::string16 AccessibilityWinBrowserTest::PrintAXTree() const { DCHECK(formatter); formatter->set_show_ids(true); formatter->SetPropertyFilters({AccessibilityTreeFormatter::PropertyFilter( - L"*", AccessibilityTreeFormatter::PropertyFilter::ALLOW)}); + "*", AccessibilityTreeFormatter::PropertyFilter::ALLOW)}); base::string16 str; formatter->FormatAccessibilityTreeForTesting( @@ -2697,19 +2697,16 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestSetSelectionRanges) { ranges = nullptr; n_ranges = 0; - // For native plain text fields, e.g. input and textarea, anchor and active - // offsets are always swapped to be in ascending order by the renderer. The - // selection's directionality is lost. hr = ax_input->get_selectionRanges(&ranges, &n_ranges); EXPECT_EQ(S_OK, hr); EXPECT_EQ(1, n_ranges); ASSERT_NE(nullptr, ranges); ASSERT_NE(nullptr, ranges[0].anchor); EXPECT_EQ(ax_input.Get(), ranges[0].anchor); - EXPECT_EQ(1, ranges[0].anchorOffset); + EXPECT_EQ(contents_string_length, ranges[0].anchorOffset); ASSERT_NE(nullptr, ranges[0].active); EXPECT_EQ(ax_input.Get(), ranges[0].active); - EXPECT_EQ(contents_string_length, ranges[0].activeOffset); + EXPECT_EQ(1, ranges[0].activeOffset); ranges[0].anchor->Release(); ranges[0].active->Release(); @@ -2717,6 +2714,94 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestSetSelectionRanges) { ranges = nullptr; } +IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, + TestSetSelectionRangesIFrame) { + AccessibilityNotificationWaiter waiter(shell()->web_contents(), + ui::kAXModeComplete, + ax::mojom::Event::kLoadComplete); + GURL url( + "data:text/html," + "<!doctype html><html><body>" + "Text before iframe" + "<iframe src='data:text/html," + "<!doctype html><html><body>" + "<button>Text in iframe</button></body></html>" + "'></iframe>" + "<button>Text after iframe</button>" + "</body></html>"); + ASSERT_TRUE(NavigateToURL(shell(), url)); + waiter.WaitForNotification(); + WaitForAccessibilityTreeToContainNodeWithName(shell()->web_contents(), + "Text in iframe"); + + Microsoft::WRL::ComPtr<IAccessible> document(GetRendererAccessible()); + std::vector<base::win::ScopedVariant> document_children = + GetAllAccessibleChildren(document.Get()); + ASSERT_EQ(1u, document_children.size()); + Microsoft::WRL::ComPtr<IAccessible2> body_iaccessible2; + ASSERT_HRESULT_SUCCEEDED(QueryIAccessible2( + GetAccessibleFromVariant(document.Get(), document_children[0].AsInput()) + .Get(), + &body_iaccessible2)); + + std::vector<base::win::ScopedVariant> body_children = + GetAllAccessibleChildren(body_iaccessible2.Get()); + ASSERT_EQ(3u, body_children.size()); + + Microsoft::WRL::ComPtr<IAccessible2> iframe; + ASSERT_HRESULT_SUCCEEDED(QueryIAccessible2( + GetAccessibleFromVariant(document.Get(), body_children[1].AsInput()) + .Get(), + &iframe)); + + std::vector<base::win::ScopedVariant> iframe_children = + GetAllAccessibleChildren(iframe.Get()); + ASSERT_EQ(1u, iframe_children.size()); + + Microsoft::WRL::ComPtr<IAccessible2> iframe_body; + ASSERT_HRESULT_SUCCEEDED(QueryIAccessible2( + GetAccessibleFromVariant(document.Get(), iframe_children[0].AsInput()) + .Get(), + &iframe_body)); + + std::vector<base::win::ScopedVariant> iframe_body_children = + GetAllAccessibleChildren(iframe_body.Get()); + ASSERT_EQ(1u, iframe_body_children.size()); + + Microsoft::WRL::ComPtr<IAccessible2> text_in_iframe; + ASSERT_HRESULT_SUCCEEDED( + QueryIAccessible2(GetAccessibleFromVariant( + document.Get(), iframe_body_children[0].AsInput()) + .Get(), + &text_in_iframe)); + + Microsoft::WRL::ComPtr<IAccessible2> text_after_iframe; + ASSERT_HRESULT_SUCCEEDED(QueryIAccessible2( + GetAccessibleFromVariant(document.Get(), body_children[2].AsInput()) + .Get(), + &text_after_iframe)); + + Microsoft::WRL::ComPtr<IAccessible2_4> text_after_iframe_iaccessible2_4; + ASSERT_HRESULT_SUCCEEDED( + text_after_iframe.As(&text_after_iframe_iaccessible2_4)); + + LONG n_ranges = 1; + IA2Range* ranges = + reinterpret_cast<IA2Range*>(CoTaskMemAlloc(sizeof(IA2Range))); + ranges[0].anchor = text_in_iframe.Get(); + ranges[0].anchorOffset = 0; + ranges[0].active = text_after_iframe.Get(); + ranges[0].activeOffset = 2; + + // This is expected to fail because the anchor and focus nodes are in + // different trees, which Blink doesn't support. + EXPECT_HRESULT_FAILED( + text_after_iframe_iaccessible2_4->setSelectionRanges(n_ranges, ranges)); + + CoTaskMemFree(ranges); + ranges = nullptr; +} + IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestMultiLineSetSelection) { Microsoft::WRL::ComPtr<IAccessibleText> textarea_text; SetUpTextareaField(&textarea_text); @@ -2816,19 +2901,16 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, ranges = nullptr; n_ranges = 0; - // For native plain text fields, e.g. input and textarea, anchor and active - // offsets are always swapped to be in ascending order by the renderer. The - // selection's directionality is lost. hr = ax_textarea->get_selectionRanges(&ranges, &n_ranges); EXPECT_EQ(S_OK, hr); EXPECT_EQ(1, n_ranges); ASSERT_NE(nullptr, ranges); ASSERT_NE(nullptr, ranges[0].anchor); EXPECT_EQ(ax_textarea.Get(), ranges[0].anchor); - EXPECT_EQ(0, ranges[0].anchorOffset); + EXPECT_EQ(contents_string_length - 1, ranges[0].anchorOffset); ASSERT_NE(nullptr, ranges[0].active); EXPECT_EQ(ax_textarea.Get(), ranges[0].active); - EXPECT_EQ(contents_string_length - 1, ranges[0].activeOffset); + EXPECT_EQ(0, ranges[0].activeOffset); ranges[0].anchor->Release(); ranges[0].active->Release(); @@ -3684,57 +3766,62 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, Microsoft::WRL::ComPtr<IAccessibleText> input_text; SetUpInputField(&input_text); - // Trailing punctuation should be included as part of the previous word. - CheckTextAtOffset(input_text, 0, IA2_TEXT_BOUNDARY_WORD, 0, 4, L"Moz/"); - CheckTextAtOffset(input_text, 2, IA2_TEXT_BOUNDARY_WORD, 0, 4, L"Moz/"); + // Trailing punctuation should not be included as part of the previous word. + CheckTextAtOffset(input_text, 0, IA2_TEXT_BOUNDARY_WORD, 0, 3, L"Moz"); + CheckTextAtOffset(input_text, 2, IA2_TEXT_BOUNDARY_WORD, 0, 3, L"Moz"); // If the offset is at the punctuation, it should return - // the previous word. - CheckTextAtOffset(input_text, 3, IA2_TEXT_BOUNDARY_WORD, 0, 4, L"Moz/"); + // the punctuation as a word. + CheckTextAtOffset(input_text, 3, IA2_TEXT_BOUNDARY_WORD, 3, 4, L"/"); // Numbers with a decimal point ("." for U.S), should be treated as one word. - // Also, trailing punctuation that occurs after empty space should be part of - // the word. ("5.0 (" and not "5.0 ".) - CheckTextAtOffset(input_text, 4, IA2_TEXT_BOUNDARY_WORD, 4, 9, L"5.0 ("); - CheckTextAtOffset(input_text, 5, IA2_TEXT_BOUNDARY_WORD, 4, 9, L"5.0 ("); - CheckTextAtOffset(input_text, 6, IA2_TEXT_BOUNDARY_WORD, 4, 9, L"5.0 ("); - CheckTextAtOffset(input_text, 7, IA2_TEXT_BOUNDARY_WORD, 4, 9, L"5.0 ("); + // Also, trailing punctuation that occurs after empty space should not be part + // of the word. ("5.0 " and not "5.0 (".) + CheckTextAtOffset(input_text, 4, IA2_TEXT_BOUNDARY_WORD, 4, 8, L"5.0 "); + CheckTextAtOffset(input_text, 5, IA2_TEXT_BOUNDARY_WORD, 4, 8, L"5.0 "); + CheckTextAtOffset(input_text, 6, IA2_TEXT_BOUNDARY_WORD, 4, 8, L"5.0 "); + CheckTextAtOffset(input_text, 7, IA2_TEXT_BOUNDARY_WORD, 4, 8, L"5.0 "); // Leading punctuation should not be included with the word after it. - CheckTextAtOffset(input_text, 8, IA2_TEXT_BOUNDARY_WORD, 4, 9, L"5.0 ("); + CheckTextAtOffset(input_text, 8, IA2_TEXT_BOUNDARY_WORD, 8, 9, L"("); CheckTextAtOffset(input_text, 11, IA2_TEXT_BOUNDARY_WORD, 9, 12, L"ST "); // Numbers separated from letters with trailing punctuation should - // be split into two words. Same for abreviations like "i.e.". - CheckTextAtOffset(input_text, 12, IA2_TEXT_BOUNDARY_WORD, 12, 14, L"6."); - CheckTextAtOffset(input_text, 15, IA2_TEXT_BOUNDARY_WORD, 14, 17, L"x; "); + // be split into multiple words. Same for abbreviations like "i.e.". + CheckTextAtOffset(input_text, 12, IA2_TEXT_BOUNDARY_WORD, 12, 13, L"6"); + CheckTextAtOffset(input_text, 13, IA2_TEXT_BOUNDARY_WORD, 13, 14, L"."); + CheckTextAtOffset(input_text, 14, IA2_TEXT_BOUNDARY_WORD, 14, 15, L"x"); + CheckTextAtOffset(input_text, 15, IA2_TEXT_BOUNDARY_WORD, 15, 17, L"; "); // Words with numbers should be treated like ordinary words. - CheckTextAtOffset(input_text, 17, IA2_TEXT_BOUNDARY_WORD, 17, 24, L"WWW33) "); - CheckTextAtOffset(input_text, 23, IA2_TEXT_BOUNDARY_WORD, 17, 24, L"WWW33) "); + CheckTextAtOffset(input_text, 17, IA2_TEXT_BOUNDARY_WORD, 17, 22, L"WWW33"); + CheckTextAtOffset(input_text, 23, IA2_TEXT_BOUNDARY_WORD, 22, 24, L") "); // Multiple trailing empty spaces should be part of the word preceding it. - CheckTextAtOffset(input_text, 28, IA2_TEXT_BOUNDARY_WORD, 24, 33, - L"WebKit \""); - CheckTextAtOffset(input_text, 31, IA2_TEXT_BOUNDARY_WORD, 24, 33, - L"WebKit \""); - CheckTextAtOffset(input_text, 32, IA2_TEXT_BOUNDARY_WORD, 24, 33, - L"WebKit \""); - - // Leading punctuation such as quotation marks should not be part of the word. - CheckTextAtOffset(input_text, 33, IA2_TEXT_BOUNDARY_WORD, 33, 40, L"KHTML, "); - CheckTextAtOffset(input_text, 38, IA2_TEXT_BOUNDARY_WORD, 33, 40, L"KHTML, "); - - // Trailing final punctuation should be part of the last word. + CheckTextAtOffset(input_text, 28, IA2_TEXT_BOUNDARY_WORD, 24, 32, + L"WebKit "); + CheckTextAtOffset(input_text, 31, IA2_TEXT_BOUNDARY_WORD, 24, 32, + L"WebKit "); + CheckTextAtOffset(input_text, 32, IA2_TEXT_BOUNDARY_WORD, 32, 33, L"\""); + + // Leading and trailing punctuation such as quotation marks should not be part + // of the word. + CheckTextAtOffset(input_text, 33, IA2_TEXT_BOUNDARY_WORD, 33, 38, L"KHTML"); + CheckTextAtOffset(input_text, 38, IA2_TEXT_BOUNDARY_WORD, 38, 40, L", "); + CheckTextAtOffset(input_text, 39, IA2_TEXT_BOUNDARY_WORD, 38, 40, L", "); + + // Trailing final punctuation should not be part of the last word. int contents_string_length = int{InputContentsString().size()}; - CheckTextAtOffset(input_text, 41, IA2_TEXT_BOUNDARY_WORD, 40, - contents_string_length, L"like\"."); - CheckTextAtOffset(input_text, 45, IA2_TEXT_BOUNDARY_WORD, 40, - contents_string_length, L"like\"."); + CheckTextAtOffset(input_text, 40, IA2_TEXT_BOUNDARY_WORD, 40, 44, L"like"); + CheckTextAtOffset(input_text, 41, IA2_TEXT_BOUNDARY_WORD, 40, 44, L"like"); + CheckTextAtOffset(input_text, 44, IA2_TEXT_BOUNDARY_WORD, 44, + contents_string_length, L"\"."); + CheckTextAtOffset(input_text, 45, IA2_TEXT_BOUNDARY_WORD, 44, + contents_string_length, L"\"."); // Test special offsets. CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_CARET, IA2_TEXT_BOUNDARY_WORD, - 40, contents_string_length, L"like\"."); + 44, contents_string_length, L"\"."); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, @@ -3742,62 +3829,64 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, Microsoft::WRL::ComPtr<IAccessibleText> textarea_text; SetUpTextareaField(&textarea_text); - // Trailing punctuation should be included as part of the previous word. - CheckTextAtOffset(textarea_text, 0, IA2_TEXT_BOUNDARY_WORD, 0, 4, L"Moz/"); - CheckTextAtOffset(textarea_text, 2, IA2_TEXT_BOUNDARY_WORD, 0, 4, L"Moz/"); + // Trailing punctuation should not be included as part of the previous word. + CheckTextAtOffset(textarea_text, 0, IA2_TEXT_BOUNDARY_WORD, 0, 3, L"Moz"); + CheckTextAtOffset(textarea_text, 2, IA2_TEXT_BOUNDARY_WORD, 0, 3, L"Moz"); // If the offset is at the punctuation, it should return - // the previous word. - CheckTextAtOffset(textarea_text, 3, IA2_TEXT_BOUNDARY_WORD, 0, 4, L"Moz/"); + // the punctuation as a word. + CheckTextAtOffset(textarea_text, 3, IA2_TEXT_BOUNDARY_WORD, 3, 4, L"/"); // Numbers with a decimal point ("." for U.S), should be treated as one word. - // Also, trailing punctuation that occurs after empty space should be part of - // the word. ("5.0 (" and not "5.0 ".) - CheckTextAtOffset(textarea_text, 4, IA2_TEXT_BOUNDARY_WORD, 4, 9, L"5.0 ("); - CheckTextAtOffset(textarea_text, 5, IA2_TEXT_BOUNDARY_WORD, 4, 9, L"5.0 ("); - CheckTextAtOffset(textarea_text, 6, IA2_TEXT_BOUNDARY_WORD, 4, 9, L"5.0 ("); - CheckTextAtOffset(textarea_text, 7, IA2_TEXT_BOUNDARY_WORD, 4, 9, L"5.0 ("); + // Also, trailing punctuation that occurs after empty space should not be part + // of the word. ("5.0 " and not "5.0 (".) + CheckTextAtOffset(textarea_text, 4, IA2_TEXT_BOUNDARY_WORD, 4, 8, L"5.0 "); + CheckTextAtOffset(textarea_text, 5, IA2_TEXT_BOUNDARY_WORD, 4, 8, L"5.0 "); + CheckTextAtOffset(textarea_text, 6, IA2_TEXT_BOUNDARY_WORD, 4, 8, L"5.0 "); + CheckTextAtOffset(textarea_text, 7, IA2_TEXT_BOUNDARY_WORD, 4, 8, L"5.0 "); // Leading punctuation should not be included with the word after it. - CheckTextAtOffset(textarea_text, 8, IA2_TEXT_BOUNDARY_WORD, 4, 9, L"5.0 ("); + CheckTextAtOffset(textarea_text, 8, IA2_TEXT_BOUNDARY_WORD, 8, 9, L"("); CheckTextAtOffset(textarea_text, 11, IA2_TEXT_BOUNDARY_WORD, 9, 12, L"ST "); // Numbers separated from letters with trailing punctuation should - // be split into two words. Same for abreviations like "i.e.". - CheckTextAtOffset(textarea_text, 12, IA2_TEXT_BOUNDARY_WORD, 12, 14, L"6."); - CheckTextAtOffset(textarea_text, 15, IA2_TEXT_BOUNDARY_WORD, 14, 17, L"x; "); + // be split into multiple words. Same for abbreviations like "i.e.". + CheckTextAtOffset(textarea_text, 12, IA2_TEXT_BOUNDARY_WORD, 12, 13, L"6"); + CheckTextAtOffset(textarea_text, 13, IA2_TEXT_BOUNDARY_WORD, 13, 14, L"."); + CheckTextAtOffset(textarea_text, 14, IA2_TEXT_BOUNDARY_WORD, 14, 15, L"x"); + CheckTextAtOffset(textarea_text, 15, IA2_TEXT_BOUNDARY_WORD, 15, 17, L"; "); // Words with numbers should be treated like ordinary words. - CheckTextAtOffset(textarea_text, 17, IA2_TEXT_BOUNDARY_WORD, 17, 24, - L"WWW33)\n"); - CheckTextAtOffset(textarea_text, 23, IA2_TEXT_BOUNDARY_WORD, 17, 24, - L"WWW33)\n"); + CheckTextAtOffset(textarea_text, 17, IA2_TEXT_BOUNDARY_WORD, 17, 22, + L"WWW33"); + CheckTextAtOffset(textarea_text, 23, IA2_TEXT_BOUNDARY_WORD, 22, 24, L")\n"); // Multiple trailing empty spaces should be part of the word preceding it. - CheckTextAtOffset(textarea_text, 28, IA2_TEXT_BOUNDARY_WORD, 24, 33, - L"WebKit \n\""); - CheckTextAtOffset(textarea_text, 31, IA2_TEXT_BOUNDARY_WORD, 24, 33, - L"WebKit \n\""); - CheckTextAtOffset(textarea_text, 32, IA2_TEXT_BOUNDARY_WORD, 24, 33, - L"WebKit \n\""); - - // Leading punctuation such as quotation marks should not be part of the word. - CheckTextAtOffset(textarea_text, 33, IA2_TEXT_BOUNDARY_WORD, 33, 40, - L"KHTML, "); - CheckTextAtOffset(textarea_text, 38, IA2_TEXT_BOUNDARY_WORD, 33, 40, - L"KHTML, "); - - // Trailing final punctuation should be part of the last word. + CheckTextAtOffset(textarea_text, 28, IA2_TEXT_BOUNDARY_WORD, 24, 32, + L"WebKit \n"); + CheckTextAtOffset(textarea_text, 31, IA2_TEXT_BOUNDARY_WORD, 24, 32, + L"WebKit \n"); + CheckTextAtOffset(textarea_text, 32, IA2_TEXT_BOUNDARY_WORD, 32, 33, L"\""); + + // Leading and trailing punctuation such as quotation marks should not be part + // of the word. + CheckTextAtOffset(textarea_text, 33, IA2_TEXT_BOUNDARY_WORD, 33, 38, + L"KHTML"); + CheckTextAtOffset(textarea_text, 38, IA2_TEXT_BOUNDARY_WORD, 38, 40, L", "); + CheckTextAtOffset(textarea_text, 39, IA2_TEXT_BOUNDARY_WORD, 38, 40, L", "); + + // Trailing final punctuation should not be part of the last word. int contents_string_length = int{InputContentsString().size()}; - CheckTextAtOffset(textarea_text, 41, IA2_TEXT_BOUNDARY_WORD, 40, - contents_string_length, L"like\"."); - CheckTextAtOffset(textarea_text, 45, IA2_TEXT_BOUNDARY_WORD, 40, - contents_string_length, L"like\"."); + CheckTextAtOffset(textarea_text, 40, IA2_TEXT_BOUNDARY_WORD, 40, 44, L"like"); + CheckTextAtOffset(textarea_text, 41, IA2_TEXT_BOUNDARY_WORD, 40, 44, L"like"); + CheckTextAtOffset(textarea_text, 44, IA2_TEXT_BOUNDARY_WORD, 44, + contents_string_length, L"\"."); + CheckTextAtOffset(textarea_text, 45, IA2_TEXT_BOUNDARY_WORD, 44, + contents_string_length, L"\"."); // Test special offsets. CheckTextAtOffset(textarea_text, IA2_TEXT_OFFSET_CARET, - IA2_TEXT_BOUNDARY_WORD, 40, contents_string_length, - L"like\"."); + IA2_TEXT_BOUNDARY_WORD, 44, contents_string_length, L"\"."); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, @@ -3806,23 +3895,12 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, SetUpSampleParagraph(¶graph_text); base::string16 embedded_character( 1, BrowserAccessibilityComWin::kEmbeddedCharacter); - std::vector<std::wstring> words; - words.push_back(L"Game "); - words.push_back(L"theory "); - words.push_back(L"is \""); - words.push_back(L"the "); - words.push_back(L"study "); - words.push_back(L"of "); - words.push_back(embedded_character); - words.push_back(L"of "); - words.push_back(L"conflict "); - words.push_back(L"and\n"); - words.push_back(L"cooperation "); - words.push_back(L"between "); - words.push_back(L"intelligent "); - words.push_back(L"rational "); - words.push_back(L"decision-"); - words.push_back(L"makers.\""); + std::vector<std::wstring> words = { + L"Game ", L"theory ", L"is ", L"\"", + L"the ", L"study ", L"of ", embedded_character, + L"of ", L"conflict ", L"and\n", L"cooperation ", + L"between ", L"intelligent ", L"rational ", L"decision", + L"-", L"makers", L".\""}; // Try to retrieve one word after another. LONG word_start_offset = 0; diff --git a/chromium/content/browser/accessibility/android_granularity_movement_browsertest.cc b/chromium/content/browser/accessibility/android_granularity_movement_browsertest.cc index 196d21f1e80..5d8fd52a23f 100644 --- a/chromium/content/browser/accessibility/android_granularity_movement_browsertest.cc +++ b/chromium/content/browser/accessibility/android_granularity_movement_browsertest.cc @@ -160,10 +160,10 @@ IN_PROC_BROWSER_TEST_F(AndroidGranularityMovementBrowserTest, BrowserAccessibility* button = button_container->PlatformGetChild(0); ASSERT_EQ(0U, button->PlatformChildCount()); - ASSERT_EQ(base::ASCIIToUTF16("'O', 'n', 'e', ',', ' ', 't', 'w', 'o', " + EXPECT_EQ(base::ASCIIToUTF16("'O', 'n', 'e', ',', ' ', 't', 'w', 'o', " "',', ' ', 't', 'h', 'r', 'e', 'e', '!'"), TraverseNodeAtGranularity(para, GRANULARITY_CHARACTER)); - ASSERT_EQ( + EXPECT_EQ( base::ASCIIToUTF16("'S', 'e', 'v', 'e', 'n', ',', ' ', 'e', 'i', 'g', " "'h', 't', ',', ' ', 'n', 'i', 'n', 'e', '!'"), TraverseNodeAtGranularity(button, GRANULARITY_CHARACTER)); @@ -187,9 +187,9 @@ IN_PROC_BROWSER_TEST_F(AndroidGranularityMovementBrowserTest, NavigateByWords) { BrowserAccessibility* button = button_container->PlatformGetChild(0); ASSERT_EQ(0U, button->PlatformChildCount()); - ASSERT_EQ(base::ASCIIToUTF16("'One', 'two', 'three'"), + EXPECT_EQ(base::ASCIIToUTF16("'One', ',', 'two', ',', 'three', '!'"), TraverseNodeAtGranularity(para, GRANULARITY_WORD)); - ASSERT_EQ(base::ASCIIToUTF16("'Seven', 'eight', 'nine'"), + EXPECT_EQ(base::ASCIIToUTF16("'Seven', 'eight', 'nine'"), TraverseNodeAtGranularity(button, GRANULARITY_WORD)); } @@ -204,7 +204,7 @@ IN_PROC_BROWSER_TEST_F(AndroidGranularityMovementBrowserTest, NavigateByLine) { BrowserAccessibility* pre = root->PlatformGetChild(0); ASSERT_EQ(0U, pre->PlatformChildCount()); - ASSERT_EQ(base::ASCIIToUTF16("'One,', 'two,', 'three!'"), + EXPECT_EQ(base::ASCIIToUTF16("'One,', 'two,', 'three!'"), TraverseNodeAtGranularity(pre, GRANULARITY_LINE)); } diff --git a/chromium/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc b/chromium/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc index 164a92b2a15..f24424ba53c 100644 --- a/chromium/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc +++ b/chromium/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc @@ -2237,30 +2237,30 @@ IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, EntireMarkupSuccessiveMoveByWord) { - AssertMoveByUnitForMarkup(TextUnit_Word, "this is a test.", - {L"this ", L"is ", L"a ", L"test."}); + AssertMoveByUnitForMarkup(TextUnit_Word, "This is a test.", + {L"This ", L"is ", L"a ", L"test", L"."}); AssertMoveByUnitForMarkup(TextUnit_Word, - " this is a test. ", - {L"this ", L"is ", L"a ", L"test."}); + " This is a test. ", + {L"This ", L"is ", L"a ", L"test", L"."}); AssertMoveByUnitForMarkup( TextUnit_Word, "It said: to be continued...", - {L"It ", L"said: ", L"to ", L"be ", L"continued..."}); + {L"It ", L"said", L": ", L"to ", L"be ", L"continued", L"..."}); AssertMoveByUnitForMarkup(TextUnit_Word, - "a <a>link with multiple words</a> and text after.", - {L"a ", L"link ", L"with ", L"multiple ", L"words", - L"and ", L"text ", L"after."}); + "A <a>link with multiple words</a> and text after.", + {L"A ", L"link ", L"with ", L"multiple ", L"words", + L"and ", L"text ", L"after", L"."}); AssertMoveByUnitForMarkup(TextUnit_Word, - "a <span aria-hidden='true'>span with ignored " + "A <span aria-hidden='true'>span with ignored " "text</span> and text after.", - {L"a ", L"and ", L"text ", L"after."}); + {L"A ", L"and ", L"text ", L"after", L"."}); AssertMoveByUnitForMarkup( TextUnit_Word, "<ol><li>item one</li><li>item two</li></ol>", - {L"1. ", L"item ", L"one", L"2. ", L"item ", L"two"}); + {L"1", L". ", L"item ", L"one", L"2", L". ", L"item ", L"two"}); // The following test should be enabled when crbug.com/1028830 is fixed. // AssertMoveByUnitForMarkup(TextUnit_Word, @@ -2488,4 +2488,62 @@ IN_PROC_BROWSER_TEST_F( AssertMoveByUnitForMarkup(TextUnit_Format, html_markup, format_units); } +IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest, + IframeSelect) { + LoadInitialAccessibilityTreeFromHtmlFilePath( + "/accessibility/html/iframe-cross-process.html"); + + WaitForAccessibilityTreeToContainNodeWithName(shell()->web_contents(), + "Text in iframe"); + + auto* node = FindNode(ax::mojom::Role::kStaticText, "Text in iframe"); + ASSERT_NE(nullptr, node); + + ComPtr<ITextRangeProvider> text_range_provider; + GetTextRangeProviderFromTextNode(*node, &text_range_provider); + ASSERT_NE(nullptr, text_range_provider.Get()); + EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"Text in iframe"); + + // First select text entirely in the iframe. To prevent test timeouts, only + // validate the next selection, which spans outside of the iframe. + EXPECT_HRESULT_SUCCEEDED(text_range_provider->Select()); + + // Move the endpoint so it spans outside of the text range and ensure + // selection still works. + EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( + text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Document, + /*count*/ 1, + /*expected_text*/ L"Text in iframe\nAfter frame", + /*expected_count*/ 1); + + // Validiate this selection with a waiter. + AccessibilityNotificationWaiter waiter( + shell()->web_contents(), ui::kAXModeComplete, + ax::mojom::Event::kDocumentSelectionChanged); + EXPECT_HRESULT_SUCCEEDED(text_range_provider->Select()); + + waiter.WaitForNotification(); + ui::AXTree::Selection selection = node->GetUnignoredSelection(); + EXPECT_EQ(selection.anchor_object_id, node->GetId()); + EXPECT_EQ(selection.anchor_offset, 0); + EXPECT_EQ(selection.focus_object_id, node->GetId()); + EXPECT_EQ(selection.focus_offset, 14); + + // Now move the start position to outside of the iframe and select. + EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( + text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Document, + /*count*/ -1, + /*expected_text*/ L"Before frame\nText in iframe\nAfter frame", + /*expected_count*/ -1); + EXPECT_HRESULT_SUCCEEDED(text_range_provider->Select()); + + // Now move the end position so it's inside of the iframe. + EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT( + text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Character, + /*count*/ -12, + /*expected_text*/ L"Before frame\nText in ifram", + /*expected_count*/ -12); + EXPECT_HRESULT_SUCCEEDED(text_range_provider->Select()); +} + } // namespace content diff --git a/chromium/content/browser/accessibility/browser_accessibility.cc b/chromium/content/browser/accessibility/browser_accessibility.cc index a84e5a94575..013b4fb4640 100644 --- a/chromium/content/browser/accessibility/browser_accessibility.cc +++ b/chromium/content/browser/accessibility/browser_accessibility.cc @@ -112,10 +112,6 @@ bool BrowserAccessibility::PlatformIsLeaf() const { return IsLeaf(); } -bool BrowserAccessibility::PlatformIsLeafIncludingIgnored() const { - return node()->IsLeafIncludingIgnored(); -} - bool BrowserAccessibility::CanFireEvents() const { // Allow events unless this object would be trimmed away. return !IsChildOfLeaf(); @@ -207,10 +203,6 @@ bool BrowserAccessibility::IsIgnored() const { return node()->IsIgnored(); } -bool BrowserAccessibility::IsTextOnlyObject() const { - return node()->IsText(); -} - bool BrowserAccessibility::IsLineBreakObject() const { return node()->IsLineBreak(); } @@ -556,11 +548,11 @@ gfx::Rect BrowserAccessibility::GetRootFrameHypertextRangeBoundsRect( // Child objects are of length one, since they are represented by a single // embedded object character. The exception is text-only objects. int child_length_in_parent = 1; - if (child->IsTextOnlyObject()) + if (child->IsText()) child_length_in_parent = static_cast<int>(child->GetHypertext().size()); if (start < child_length_in_parent) { gfx::Rect child_rect; - if (child->IsTextOnlyObject()) { + if (child->IsText()) { child_rect = child->GetRootFrameHypertextRangeBoundsRect( start, len, clipping_behavior, offscreen_result); } else { @@ -668,25 +660,25 @@ gfx::Rect BrowserAccessibility::GetRootFrameHypertextBoundsPastEndOfText( } // Step 2: correct for the thickness of the caret. - auto text_direction = static_cast<ax::mojom::TextDirection>( + auto text_direction = static_cast<ax::mojom::WritingDirection>( GetIntAttribute(ax::mojom::IntAttribute::kTextDirection)); constexpr int kCaretThickness = 1; switch (text_direction) { - case ax::mojom::TextDirection::kNone: - case ax::mojom::TextDirection::kLtr: { + case ax::mojom::WritingDirection::kNone: + case ax::mojom::WritingDirection::kLtr: { bounds.set_width(kCaretThickness); break; } - case ax::mojom::TextDirection::kRtl: { + case ax::mojom::WritingDirection::kRtl: { bounds.set_x(bounds.right() - kCaretThickness); bounds.set_width(kCaretThickness); break; } - case ax::mojom::TextDirection::kTtb: { + case ax::mojom::WritingDirection::kTtb: { bounds.set_height(kCaretThickness); break; } - case ax::mojom::TextDirection::kBtt: { + case ax::mojom::WritingDirection::kBtt: { bounds.set_y(bounds.bottom() - kCaretThickness); bounds.set_height(kCaretThickness); break; @@ -795,25 +787,25 @@ gfx::RectF BrowserAccessibility::GetInlineTextRect(const int start_offset, const int location_height = location.height(); gfx::RectF bounds; - switch (static_cast<ax::mojom::TextDirection>( + switch (static_cast<ax::mojom::WritingDirection>( GetIntAttribute(ax::mojom::IntAttribute::kTextDirection))) { - case ax::mojom::TextDirection::kNone: - case ax::mojom::TextDirection::kLtr: + case ax::mojom::WritingDirection::kNone: + case ax::mojom::WritingDirection::kLtr: bounds = gfx::RectF(start_pixel_offset, 0, end_pixel_offset - start_pixel_offset, location_height); break; - case ax::mojom::TextDirection::kRtl: { + case ax::mojom::WritingDirection::kRtl: { const int left = max_pixel_offset - end_pixel_offset; const int right = max_pixel_offset - start_pixel_offset; bounds = gfx::RectF(left, 0, right - left, location_height); break; } - case ax::mojom::TextDirection::kTtb: + case ax::mojom::WritingDirection::kTtb: bounds = gfx::RectF(0, start_pixel_offset, location_width, end_pixel_offset - start_pixel_offset); break; - case ax::mojom::TextDirection::kBtt: { + case ax::mojom::WritingDirection::kBtt: { const int top = max_pixel_offset - end_pixel_offset; const int bottom = max_pixel_offset - start_pixel_offset; bounds = gfx::RectF(0, top, location_width, bottom - top); @@ -1228,6 +1220,10 @@ bool BrowserAccessibility::IsMinimized() const { return false; } +bool BrowserAccessibility::IsText() const { + return node()->IsText(); +} + bool BrowserAccessibility::IsWebContent() const { return true; } @@ -1500,6 +1496,10 @@ gfx::NativeViewAccessible BrowserAccessibility::ChildAtIndex(int index) { return child->GetNativeViewAccessible(); } +bool BrowserAccessibility::HasModalDialog() const { + return false; +} + gfx::NativeViewAccessible BrowserAccessibility::GetFirstChild() { BrowserAccessibility* child = PlatformGetFirstChild(); if (!child) @@ -1549,12 +1549,17 @@ bool BrowserAccessibility::IsLeaf() const { // children. The only exception to enforce leafiness is when the button has // a single text child and to prevent screen readers from double speak. if (GetRole() == ax::mojom::Role::kButton) { - return InternalChildCount() == 1 && - InternalGetFirstChild()->IsTextOnlyObject(); + uint32_t child_count = InternalChildCount(); + return !child_count || + (child_count == 1 && InternalGetFirstChild()->IsText()); } return node()->IsLeaf(); } +bool BrowserAccessibility::IsToplevelBrowserWindow() { + return false; +} + bool BrowserAccessibility::IsChildOfPlainTextField() const { ui::AXNode* textfield_node = node()->GetTextFieldAncestor(); return textfield_node && textfield_node->data().IsPlainTextField(); @@ -2163,7 +2168,7 @@ std::string BrowserAccessibility::GetInheritedFontFamilyName() const { ui::TextAttributeMap BrowserAccessibility::GetSpellingAndGrammarAttributes() const { ui::TextAttributeMap spelling_attributes; - if (IsTextOnlyObject()) { + if (IsText()) { const std::vector<int32_t>& marker_types = GetIntListAttribute(ax::mojom::IntListAttribute::kMarkerTypes); const std::vector<int>& marker_starts = @@ -2263,6 +2268,8 @@ ui::TextAttributeMap BrowserAccessibility::ComputeTextAttributeMap( return attributes_map; } + DCHECK(PlatformChildCount()); + int start_offset = 0; for (BrowserAccessibility::PlatformChildIterator it = PlatformChildrenBegin(); it != PlatformChildrenEnd(); ++it) { @@ -2286,7 +2293,7 @@ ui::TextAttributeMap BrowserAccessibility::ComputeTextAttributeMap( } } - if (child->IsTextOnlyObject()) { + if (child->IsText()) { const ui::TextAttributeMap spelling_attributes = child->GetSpellingAndGrammarAttributes(); MergeSpellingAndGrammarIntoTextAttributes(spelling_attributes, diff --git a/chromium/content/browser/accessibility/browser_accessibility.h b/chromium/content/browser/accessibility/browser_accessibility.h index df1b7977798..16c7908f65c 100644 --- a/chromium/content/browser/accessibility/browser_accessibility.h +++ b/chromium/content/browser/accessibility/browser_accessibility.h @@ -41,7 +41,7 @@ #define PLATFORM_HAS_NATIVE_ACCESSIBILITY_IMPL 1 #endif -#if defined(OS_MACOSX) +#if defined(OS_MAC) #define PLATFORM_HAS_NATIVE_ACCESSIBILITY_IMPL 1 #endif @@ -53,7 +53,7 @@ #define PLATFORM_HAS_NATIVE_ACCESSIBILITY_IMPL 1 #endif -#if defined(OS_MACOSX) && __OBJC__ +#if defined(OS_MAC) && __OBJC__ @class BrowserAccessibilityCocoa; #endif @@ -111,17 +111,11 @@ class CONTENT_EXPORT BrowserAccessibility : public ui::AXPlatformNodeDelegate { bool IsIgnored() const; - // Returns true if this object is used only for representing text. - bool IsTextOnlyObject() const; - bool IsLineBreakObject() const; // See AXNode::IsLeaf(). bool PlatformIsLeaf() const; - // See AXNode::IsLeafIncludingIgnored(). - bool PlatformIsLeafIncludingIgnored() const; - // Returns true if this object can fire events. virtual bool CanFireEvents() const; @@ -439,6 +433,7 @@ class CONTENT_EXPORT BrowserAccessibility : public ui::AXPlatformNodeDelegate { gfx::NativeViewAccessible GetParent() override; int GetChildCount() const override; gfx::NativeViewAccessible ChildAtIndex(int index) override; + bool HasModalDialog() const override; gfx::NativeViewAccessible GetFirstChild() override; gfx::NativeViewAccessible GetLastChild() override; gfx::NativeViewAccessible GetNextSibling() override; @@ -447,6 +442,7 @@ class CONTENT_EXPORT BrowserAccessibility : public ui::AXPlatformNodeDelegate { bool IsChildOfLeaf() const override; bool IsChildOfPlainTextField() const override; bool IsLeaf() const override; + bool IsToplevelBrowserWindow() override; gfx::NativeViewAccessible GetClosestPlatformObject() const override; std::unique_ptr<ChildIterator> ChildrenBegin() override; @@ -538,6 +534,7 @@ class CONTENT_EXPORT BrowserAccessibility : public ui::AXPlatformNodeDelegate { bool ShouldIgnoreHoveredStateForTesting() override; bool IsOffscreen() const override; bool IsMinimized() const override; + bool IsText() const override; bool IsWebContent() const override; bool HasVisibleCaretOrSelection() const override; ui::AXPlatformNode* GetTargetNodeForRelation( diff --git a/chromium/content/browser/accessibility/browser_accessibility_android.cc b/chromium/content/browser/accessibility/browser_accessibility_android.cc index c219569e5e4..2aae62b5648 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_android.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_android.cc @@ -55,8 +55,6 @@ enum { ANDROID_VIEW_ACCESSIBILITY_RANGE_TYPE_FLOAT = 1 }; namespace content { -const float kContentInvalidTimeoutMillisecs = 6000.0; - // static BrowserAccessibility* BrowserAccessibility::Create() { return new BrowserAccessibilityAndroid(); @@ -176,20 +174,6 @@ bool BrowserAccessibilityAndroid::IsCollectionItem() const { } bool BrowserAccessibilityAndroid::IsContentInvalid() const { - if (IsFocused()) { - // When a node has focus, only report that it's invalid for a short period - // of time. Otherwise it's annoying to hear the invalid message every time - // a character is entered. - if (content_invalid_timer_.Elapsed().InMillisecondsF() < - kContentInvalidTimeoutMillisecs) { - bool invalid_state = - HasIntAttribute(ax::mojom::IntAttribute::kInvalidState) && - GetData().GetInvalidState() != ax::mojom::InvalidState::kFalse; - if (invalid_state) - return true; - } - return false; - } return HasIntAttribute(ax::mojom::IntAttribute::kInvalidState) && GetData().GetInvalidState() != ax::mojom::InvalidState::kFalse; } @@ -269,6 +253,10 @@ bool BrowserAccessibilityAndroid::IsMultiLine() const { return HasState(ax::mojom::State::kMultiline); } +bool BrowserAccessibilityAndroid::IsMultiselectable() const { + return HasState(ax::mojom::State::kMultiselectable); +} + bool BrowserAccessibilityAndroid::IsRangeType() const { return (GetRole() == ax::mojom::Role::kProgressIndicator || GetRole() == ax::mojom::Role::kMeter || @@ -378,7 +366,7 @@ BrowserAccessibilityAndroid::GetSoleInterestingNodeFromSubtree() const { } bool BrowserAccessibilityAndroid::AreInlineTextBoxesLoaded() const { - if (GetRole() == ax::mojom::Role::kStaticText) + if (IsText()) return InternalChildCount() > 0; // Return false if any descendant needs to load inline text boxes. @@ -550,6 +538,47 @@ base::string16 BrowserAccessibilityAndroid::GetHint() const { return base::JoinString(strings, base::ASCIIToUTF16(" ")); } +base::string16 BrowserAccessibilityAndroid::GetStateDescription() const { + // For multiselectable state, generate a state description + if (IsMultiselectable()) + return GetMultiselectableStateDescription(); + + // Otherwise we will not use state description + return base::string16(); +} + +base::string16 BrowserAccessibilityAndroid::GetMultiselectableStateDescription() + const { + content::ContentClient* content_client = content::GetContentClient(); + + // Count the number of children and selected children. + int child_count = 0; + int selected_count = 0; + for (PlatformChildIterator it = PlatformChildrenBegin(); + it != PlatformChildrenEnd(); ++it) { + child_count++; + BrowserAccessibilityAndroid* child = + static_cast<BrowserAccessibilityAndroid*>(it.get()); + if (child->IsSelected()) + selected_count++; + } + + // If none are selected, return special case. + if (!selected_count) + return content_client->GetLocalizedString( + IDS_AX_MULTISELECTABLE_STATE_DESCRIPTION_NONE); + + // Generate a state description of the form: "multiselectable, x of y + // selected.". + std::vector<base::string16> values; + values.push_back(base::NumberToString16(selected_count)); + values.push_back(base::NumberToString16(child_count)); + return base::ReplaceStringPlaceholders( + content_client->GetLocalizedString( + IDS_AX_MULTISELECTABLE_STATE_DESCRIPTION), + values, nullptr); +} + std::string BrowserAccessibilityAndroid::GetRoleString() const { return ui::ToString(GetRole()); } @@ -964,8 +993,6 @@ base::string16 BrowserAccessibilityAndroid::GetRoleDescription() const { message_id = IDS_AX_ROLE_MENU_BAR; break; case ax::mojom::Role::kMenuButton: - message_id = IDS_AX_ROLE_MENU_BUTTON; - break; case ax::mojom::Role::kMenuItem: message_id = IDS_AX_ROLE_MENU_ITEM; break; @@ -1724,7 +1751,7 @@ void BrowserAccessibilityAndroid::GetSuggestions( BrowserAccessibility* node = InternalGetFirstChild(); int start_offset = 0; while (node && node != this) { - if (node->IsTextOnlyObject()) { + if (node->IsText()) { const std::vector<int32_t>& marker_types = node->GetData().GetIntListAttribute( ax::mojom::IntListAttribute::kMarkerTypes); @@ -1801,7 +1828,7 @@ bool BrowserAccessibilityAndroid::HasOnlyTextChildren() const { // This is called from IsLeaf, so don't call PlatformChildCount // from within this! for (auto it = InternalChildrenBegin(); it != InternalChildrenEnd(); ++it) { - if (!it->IsTextOnlyObject()) + if (!it->IsText()) return false; } return true; @@ -1812,8 +1839,7 @@ bool BrowserAccessibilityAndroid::HasOnlyTextAndImageChildren() const { // from within this! for (auto it = InternalChildrenBegin(); it != InternalChildrenEnd(); ++it) { BrowserAccessibility* child = it.get(); - if (child->GetRole() != ax::mojom::Role::kStaticText && - !ui::IsImageOrVideo(child->GetRole())) { + if (!child->IsText() && !ui::IsImageOrVideo(child->GetRole())) { return false; } } @@ -1908,8 +1934,4 @@ base::string16 BrowserAccessibilityAndroid::GetContentInvalidErrorMessage() return base::string16(); } -void BrowserAccessibilityAndroid::ResetContentInvalidTimer() { - content_invalid_timer_ = base::ElapsedTimer(); -} - } // namespace content diff --git a/chromium/content/browser/accessibility/browser_accessibility_android.h b/chromium/content/browser/accessibility/browser_accessibility_android.h index ee55916ca43..ff453ec1d48 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_android.h +++ b/chromium/content/browser/accessibility/browser_accessibility_android.h @@ -13,7 +13,6 @@ #include "base/android/scoped_java_ref.h" #include "base/macros.h" -#include "base/timer/elapsed_timer.h" #include "content/browser/accessibility/browser_accessibility.h" #include "ui/accessibility/platform/ax_platform_node.h" @@ -46,6 +45,7 @@ class CONTENT_EXPORT BrowserAccessibilityAndroid : public BrowserAccessibility { bool IsHierarchical() const; bool IsLink() const; bool IsMultiLine() const; + bool IsMultiselectable() const; bool IsRangeType() const; bool IsScrollable() const; bool IsSelected() const; @@ -89,6 +89,9 @@ class CONTENT_EXPORT BrowserAccessibilityAndroid : public BrowserAccessibility { base::string16 GetContentInvalidErrorMessage() const; + base::string16 GetStateDescription() const; + base::string16 GetMultiselectableStateDescription() const; + base::string16 GetRoleDescription() const; int GetItemIndex() const; @@ -162,11 +165,6 @@ class CONTENT_EXPORT BrowserAccessibilityAndroid : public BrowserAccessibility { void GetSuggestions(std::vector<int>* suggestion_starts, std::vector<int>* suggestion_ends) const; - // Used to keep track of when to stop reporting content_invalid. - // Timer only applies if node has focus. - void ResetContentInvalidTimer(); - base::ElapsedTimer content_invalid_timer_ = base::ElapsedTimer(); - private: // This gives BrowserAccessibility::Create access to the class constructor. friend class BrowserAccessibility; diff --git a/chromium/content/browser/accessibility/browser_accessibility_auralinux_unittest.cc b/chromium/content/browser/accessibility/browser_accessibility_auralinux_unittest.cc index e3f0043c295..74fbe552feb 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_auralinux_unittest.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_auralinux_unittest.cc @@ -305,6 +305,59 @@ TEST_F(BrowserAccessibilityAuraLinuxTest, TestComplexHypertext) { manager.reset(); } +TEST_F(BrowserAccessibilityAuraLinuxTest, TestTextAttributesInButtons) { + ui::AXNodeData root; + root.id = 1; + root.role = ax::mojom::Role::kRootWebArea; + root.AddState(ax::mojom::State::kFocusable); + + ui::AXNodeData button; + button.id = 2; + button.role = ax::mojom::Role::kButton; + button.AddStringAttribute(ax::mojom::StringAttribute::kFontFamily, "Times"); + root.child_ids.push_back(button.id); + + ui::AXNodeData text; + text.id = 3; + text.role = ax::mojom::Role::kStaticText; + text.SetName("OK"); + button.child_ids.push_back(text.id); + + ui::AXNodeData empty_button; + empty_button.id = 4; + empty_button.role = ax::mojom::Role::kButton; + empty_button.AddStringAttribute(ax::mojom::StringAttribute::kFontFamily, + "Times"); + root.child_ids.push_back(empty_button.id); + + ui::AXTreeUpdate update = MakeAXTreeUpdate(root, button, text, empty_button); + std::unique_ptr<BrowserAccessibilityManager> manager( + BrowserAccessibilityManager::Create( + update, test_browser_accessibility_delegate_.get())); + + BrowserAccessibilityAuraLinux* ax_root = + ToBrowserAccessibilityAuraLinux(manager->GetRoot()); + + BrowserAccessibilityAuraLinux* ax_button = + ToBrowserAccessibilityAuraLinux(ax_root->PlatformGetChild(0)); + AtkObject* atk_button = ax_button->GetNode()->GetNativeViewAccessible(); + + int start_offset, end_offset; + AtkAttributeSet* attributes = atk_text_get_run_attributes( + ATK_TEXT(atk_button), 0, &start_offset, &end_offset); + ASSERT_EQ(1U, g_slist_length(attributes)); + atk_attribute_set_free(attributes); + + BrowserAccessibilityAuraLinux* ax_empty_button = + ToBrowserAccessibilityAuraLinux(ax_root->PlatformGetChild(1)); + AtkObject* atk_empty_button = + ax_empty_button->GetNode()->GetNativeViewAccessible(); + attributes = atk_text_get_run_attributes(ATK_TEXT(atk_empty_button), 0, + &start_offset, &end_offset); + ASSERT_EQ(1U, g_slist_length(attributes)); + atk_attribute_set_free(attributes); +} + TEST_F(BrowserAccessibilityAuraLinuxTest, TestTextAttributesInContentEditables) { auto has_attribute = [](AtkAttributeSet* attributes, @@ -763,4 +816,59 @@ TEST_F(BrowserAccessibilityAuraLinuxTest, TextAtkStaticTextChange) { EXPECT_STREQ(base::UTF16ToUTF8(div_node->GetHypertext()).c_str(), "Text2"); } +TEST_F(BrowserAccessibilityAuraLinuxTest, TestAtkTextGetOffesetAtPoint) { + ui::AXNodeData static_text1; + static_text1.id = 1; + static_text1.role = ax::mojom::Role::kStaticText; + static_text1.SetName("Hello"); + static_text1.child_ids = {2}; + + ui::AXNodeData inline_box1; + inline_box1.id = 2; + inline_box1.role = ax::mojom::Role::kInlineTextBox; + inline_box1.SetName("Hello"); + inline_box1.relative_bounds.bounds = gfx::RectF(0, 50, 25, 30); + std::vector<int32_t> character_offsets1; + // The width of each character is 5px. + character_offsets1.push_back(5); + character_offsets1.push_back(10); + character_offsets1.push_back(15); + character_offsets1.push_back(20); + character_offsets1.push_back(25); + inline_box1.AddIntListAttribute( + ax::mojom::IntListAttribute::kCharacterOffsets, character_offsets1); + inline_box1.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts, + std::vector<int32_t>{0}); + inline_box1.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds, + std::vector<int32_t>{5}); + + std::unique_ptr<BrowserAccessibilityManager> manager( + BrowserAccessibilityManager::Create( + MakeAXTreeUpdate(static_text1, inline_box1), + test_browser_accessibility_delegate_.get())); + + ASSERT_NE(nullptr, manager->GetRoot()); + BrowserAccessibilityAuraLinux* ax_root = + ToBrowserAccessibilityAuraLinux(manager->GetRoot()); + ASSERT_NE(nullptr, ax_root); + + AtkObject* root_atk_object = ax_root->GetNode()->GetNativeViewAccessible(); + g_object_ref(root_atk_object); + AtkText* atk_text = ATK_TEXT(root_atk_object); + ASSERT_TRUE(ATK_IS_TEXT(atk_text)); + + int x, y, width, height; + char* text = atk_text_get_text(atk_text, 0, -1); + int root_text_length = g_utf8_strlen(text, -1); + g_free(text); + for (int offset = 0; offset < root_text_length; offset++) { + atk_text_get_character_extents(atk_text, offset, &x, &y, &width, &height, + ATK_XY_SCREEN); + int result = atk_text_get_offset_at_point(atk_text, x, y, ATK_XY_SCREEN); + ASSERT_EQ(offset, result); + } + g_object_unref(root_atk_object); + manager.reset(); +} + } // namespace content diff --git a/chromium/content/browser/accessibility/browser_accessibility_cocoa.h b/chromium/content/browser/accessibility/browser_accessibility_cocoa.h index 947a5482780..cb253906ddd 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_cocoa.h +++ b/chromium/content/browser/accessibility/browser_accessibility_cocoa.h @@ -12,20 +12,25 @@ #include "base/strings/string16.h" #include "content/browser/accessibility/browser_accessibility.h" #include "content/browser/accessibility/browser_accessibility_manager.h" +#include "content/common/content_export.h" namespace content { // Used to store changes in edit fields, required by VoiceOver in order to // support character echo and other announcements during editing. -struct AXTextEdit { - AXTextEdit() = default; - AXTextEdit(base::string16 inserted_text, base::string16 deleted_text) - : inserted_text(inserted_text), deleted_text(deleted_text) {} +struct CONTENT_EXPORT AXTextEdit { + AXTextEdit(); + AXTextEdit(base::string16 inserted_text, + base::string16 deleted_text, + id edit_text_marker); + AXTextEdit(const AXTextEdit& other); + ~AXTextEdit(); bool IsEmpty() const { return inserted_text.empty() && deleted_text.empty(); } base::string16 inserted_text; base::string16 deleted_text; + base::scoped_nsprotocol<id> edit_text_marker; }; // Returns true if the given object is AXTextMarker object. @@ -35,7 +40,7 @@ bool IsAXTextMarker(id); bool IsAXTextMarkerRange(id); // Returns browser accessibility position for the given AXTextMarker. -BrowserAccessibilityPosition::AXPositionInstance AXTextMarkerToPosition(id); +CONTENT_EXPORT BrowserAccessibilityPosition::AXPositionInstance AXTextMarkerToPosition(id); // Returns browser accessibility range for the given AXTextMarkerRange. BrowserAccessibilityPosition::AXRangeType AXTextMarkerRangeToRange(id); @@ -45,6 +50,9 @@ id AXTextMarkerFrom(const BrowserAccessibilityCocoa* anchor, int offset, ax::mojom::TextAffinity affinity); +// Returns AXTextMarkerRange for the given browser accessibility positions. +id AXTextMarkerRangeFrom(id anchor_textmarker, id focus_textmarker); + } // namespace content // BrowserAccessibilityCocoa is a cocoa wrapper around the BrowserAccessibility diff --git a/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm b/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm index 8e2592948d6..d203e3a6a80 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm +++ b/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm @@ -564,7 +564,7 @@ AccessibilityMatchPredicate PredicateForSearchKey(NSString* searchKey) { }; } else if ([searchKey isEqualToString:@"AXStaticTextSearchKey"]) { return [](BrowserAccessibility* start, BrowserAccessibility* current) { - return current->IsTextOnlyObject(); + return current->IsText(); }; } else if ([searchKey isEqualToString:@"AXStyleChangeSearchKey"]) { // TODO(dmazzoni): implement this. @@ -707,6 +707,20 @@ bool IsSelectedStateRelevant(BrowserAccessibility* item) { } // namespace +namespace content { + +AXTextEdit::AXTextEdit() = default; +AXTextEdit::AXTextEdit(base::string16 inserted_text, + base::string16 deleted_text, + id edit_text_marker) + : inserted_text(inserted_text), + deleted_text(deleted_text), + edit_text_marker(edit_text_marker, base::scoped_policy::RETAIN) {} +AXTextEdit::AXTextEdit(const AXTextEdit& other) = default; +AXTextEdit::~AXTextEdit() = default; + +} // namespace content + #if defined(MAC_OS_X_VERSION_10_12) && \ (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) #warning NSAccessibilityRequiredAttributeChrome \ @@ -765,6 +779,12 @@ id content::AXTextMarkerFrom(const BrowserAccessibilityCocoa* anchor, return CreateTextMarker(std::move(position)); } +id content::AXTextMarkerRangeFrom(id anchor_textmarker, id focus_textmarker) { + AXTextMarkerRangeRef cf_marker_range = AXTextMarkerRangeCreate( + kCFAllocatorDefault, anchor_textmarker, focus_textmarker); + return [static_cast<id>(cf_marker_range) autorelease]; +} + @implementation BrowserAccessibilityCocoa + (void)initialize { @@ -1871,10 +1891,11 @@ id content::AXTextMarkerFrom(const BrowserAccessibilityCocoa* anchor, if (size_t{sel_start} == newValue.length() && size_t{sel_end} == newValue.length()) { // Don't include oldValue as it would be announced -- very confusing. - return content::AXTextEdit(newValue, base::string16()); + return content::AXTextEdit(newValue, base::string16(), nil); } } - return content::AXTextEdit(insertedText, deletedText); + return content::AXTextEdit(insertedText, deletedText, + CreateTextMarker(_owner->CreatePositionAt(i))); } - (BOOL)instanceActive { @@ -2244,7 +2265,9 @@ id content::AXTextMarkerFrom(const BrowserAccessibilityCocoa* anchor, - (id)selectedTextMarkerRange { if (![self instanceActive]) return nil; - return CreateTextMarkerRange(GetSelectedRange(*_owner)); + // Voiceover expects this range to be backwards in order to read the selected + // words correctly. + return CreateTextMarkerRange(GetSelectedRange(*_owner).AsBackwardRange()); } - (NSValue*)size { @@ -2607,7 +2630,7 @@ id content::AXTextMarkerFrom(const BrowserAccessibilityCocoa* anchor, NSMutableAttributedString* attributedInnerText = [[[NSMutableAttributedString alloc] initWithString:base::SysUTF16ToNSString(innerText)] autorelease]; - if (!_owner->IsTextOnlyObject()) { + if (!_owner->IsText()) { AXPlatformRange ax_range(_owner->CreatePositionAt(0), _owner->CreatePositionAt(int{innerText.length()})); AddMisspelledTextAttributes(ax_range, attributedInnerText); @@ -3033,7 +3056,7 @@ id content::AXTextMarkerFrom(const BrowserAccessibilityCocoa* anchor, if ([attribute isEqualToString: NSAccessibilityBoundsForRangeParameterizedAttribute]) { - if ([self internalRole] != ax::mojom::Role::kStaticText) + if (!_owner->IsText()) return nil; NSRange range = [(NSValue*)parameter rangeValue]; gfx::Rect rect = _owner->GetUnclippedScreenInnerTextRangeBoundsRect( diff --git a/chromium/content/browser/accessibility/browser_accessibility_cocoa_browsertest.mm b/chromium/content/browser/accessibility/browser_accessibility_cocoa_browsertest.mm index 4148e32e718..48f8d9dbbe3 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_cocoa_browsertest.mm +++ b/chromium/content/browser/accessibility/browser_accessibility_cocoa_browsertest.mm @@ -63,6 +63,43 @@ class BrowserAccessibilityCocoaBrowserTest : public ContentBrowserTest { } // namespace IN_PROC_BROWSER_TEST_F(BrowserAccessibilityCocoaBrowserTest, + AXTextMarkerForTextEdit) { + EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL))); + + AccessibilityNotificationWaiter waiter(shell()->web_contents(), + ui::kAXModeComplete, + ax::mojom::Event::kLoadComplete); + GURL url(R"HTML(data:text/html, + <input />)HTML"); + + EXPECT_TRUE(NavigateToURL(shell(), url)); + waiter.WaitForNotification(); + + BrowserAccessibility* text_field = FindNode(ax::mojom::Role::kTextField); + ASSERT_NE(nullptr, text_field); + EXPECT_TRUE(content::ExecuteScript( + shell()->web_contents(), "document.querySelector('input').focus()")); + + content::SimulateKeyPress(shell()->web_contents(), + ui::DomKey::FromCharacter('B'), ui::DomCode::US_B, + ui::VKEY_B, false, false, false, false); + + base::scoped_nsobject<BrowserAccessibilityCocoa> cocoa_text_field( + [ToBrowserAccessibilityCocoa(text_field) retain]); + AccessibilityNotificationWaiter value_waiter(shell()->web_contents(), + ui::kAXModeComplete, + ax::mojom::Event::kValueChanged); + value_waiter.WaitForNotification(); + AXTextEdit text_edit = [cocoa_text_field computeTextEdit]; + EXPECT_NE(text_edit.edit_text_marker, nil); + + EXPECT_EQ( + content::AXTextMarkerToPosition(text_edit.edit_text_marker)->ToString(), + "TextPosition anchor_id=5 text_offset=1 affinity=downstream " + "annotated_text=B<>"); +} + +IN_PROC_BROWSER_TEST_F(BrowserAccessibilityCocoaBrowserTest, AXCellForColumnAndRow) { EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL))); diff --git a/chromium/content/browser/accessibility/browser_accessibility_com_win.cc b/chromium/content/browser/accessibility/browser_accessibility_com_win.cc index a8a043f82b4..ce8fb53107b 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_com_win.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_com_win.cc @@ -897,7 +897,7 @@ IFACEMETHODIMP BrowserAccessibilityComWin::get_nodeInfo( if (owner()->IsDocument()) { *node_type = NODETYPE_DOCUMENT; - } else if (owner()->IsTextOnlyObject()) { + } else if (owner()->IsText()) { *node_type = NODETYPE_TEXT; } else { *node_type = NODETYPE_ELEMENT; @@ -1447,7 +1447,6 @@ void BrowserAccessibilityComWin::UpdateStep2ComputeHypertext() { } void BrowserAccessibilityComWin::UpdateStep3FireEvents() { - int32_t state = MSAAState(); const bool ignored = owner()->IsIgnored(); // Suppress all of these events when the node is ignored, or when the ignored @@ -1462,14 +1461,6 @@ void BrowserAccessibilityComWin::UpdateStep3FireEvents() { if (description() != old_win_attributes_->description) FireNativeEvent(EVENT_OBJECT_DESCRIPTIONCHANGE); - // Do not fire EVENT_OBJECT_STATECHANGE if the change was due to a focus - // change. - if ((state & ~STATE_SYSTEM_FOCUSED) != - (old_win_attributes_->ia_state & ~STATE_SYSTEM_FOCUSED) || - ComputeIA2State() != old_win_attributes_->ia2_state) { - FireNativeEvent(EVENT_OBJECT_STATECHANGE); - } - // Fire an event if this container object has scrolled. int sx = 0; int sy = 0; diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager.cc b/chromium/content/browser/accessibility/browser_accessibility_manager.cc index 739a19d13eb..6d9157618d7 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_manager.cc @@ -996,7 +996,7 @@ BrowserAccessibility* BrowserAccessibilityManager::PreviousInTreeOrder( BrowserAccessibility* BrowserAccessibilityManager::PreviousTextOnlyObject( const BrowserAccessibility* object) { BrowserAccessibility* previous_object = PreviousInTreeOrder(object, false); - while (previous_object && !previous_object->IsTextOnlyObject()) + while (previous_object && !previous_object->IsText()) previous_object = PreviousInTreeOrder(previous_object, false); return previous_object; @@ -1006,7 +1006,7 @@ BrowserAccessibility* BrowserAccessibilityManager::PreviousTextOnlyObject( BrowserAccessibility* BrowserAccessibilityManager::NextTextOnlyObject( const BrowserAccessibility* object) { BrowserAccessibility* next_object = NextInTreeOrder(object); - while (next_object && !next_object->IsTextOnlyObject()) + while (next_object && !next_object->IsText()) next_object = NextInTreeOrder(next_object); return next_object; @@ -1119,9 +1119,9 @@ BrowserAccessibilityManager::FindTextOnlyObjectsInRange( if (!end_text_object->PlatformIsLeaf()) end_text_object = end_text_object->PlatformDeepestLastChild(); - if (!start_text_object->IsTextOnlyObject()) + if (!start_text_object->IsText()) start_text_object = NextTextOnlyObject(start_text_object); - if (!end_text_object->IsTextOnlyObject()) + if (!end_text_object->IsText()) end_text_object = PreviousTextOnlyObject(end_text_object); if (!start_text_object || !end_text_object) @@ -1190,7 +1190,7 @@ base::string16 BrowserAccessibilityManager::GetTextForRange( const BrowserAccessibility* start_text_object = text_only_objects[0]; // Figure out if the start and end positions have been reversed. const BrowserAccessibility* first_object = &start_object; - if (!first_object->IsTextOnlyObject()) + if (!first_object->IsText()) first_object = NextTextOnlyObject(first_object); if (!first_object || first_object != start_text_object) std::swap(start_offset, end_offset); @@ -1258,7 +1258,7 @@ gfx::Rect BrowserAccessibilityManager::GetRootFrameInnerTextRangeBoundsRect( const BrowserAccessibility* current = first; do { - if (current->IsTextOnlyObject()) { + if (current->IsText()) { int len = static_cast<int>(current->GetInnerText().size()); int start_char_index = 0; int end_char_index = len; diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager.h b/chromium/content/browser/accessibility/browser_accessibility_manager.h index 809ca3a2ad2..38c9fb063d3 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager.h +++ b/chromium/content/browser/accessibility/browser_accessibility_manager.h @@ -49,7 +49,7 @@ class BrowserAccessibilityManagerAndroid; class BrowserAccessibilityManagerWin; #elif BUILDFLAG(USE_ATK) class BrowserAccessibilityManagerAuraLinux; -#elif defined(OS_MACOSX) +#elif defined(OS_MAC) class BrowserAccessibilityManagerMac; #endif @@ -346,7 +346,7 @@ class CONTENT_EXPORT BrowserAccessibilityManager : public ui::AXTreeObserver, ToBrowserAccessibilityManagerAuraLinux(); #endif -#if defined(OS_MACOSX) +#if defined(OS_MAC) BrowserAccessibilityManagerMac* ToBrowserAccessibilityManagerMac(); #endif diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_android.cc b/chromium/content/browser/accessibility/browser_accessibility_manager_android.cc index d52cec13ea1..7abd3eb7e4d 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_android.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_android.cc @@ -129,7 +129,6 @@ void BrowserAccessibilityManagerAndroid::FireFocusEvent( return; BrowserAccessibilityAndroid* android_node = static_cast<BrowserAccessibilityAndroid*>(node); - android_node->ResetContentInvalidTimer(); wcax->HandleFocusChanged(android_node->unique_id()); } @@ -267,6 +266,7 @@ void BrowserAccessibilityManagerAndroid::FireGeneratedEvent( case ui::AXEventGenerator::Event::MULTILINE_STATE_CHANGED: case ui::AXEventGenerator::Event::MULTISELECTABLE_STATE_CHANGED: case ui::AXEventGenerator::Event::NAME_CHANGED: + case ui::AXEventGenerator::Event::OBJECT_ATTRIBUTE_CHANGED: case ui::AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED: case ui::AXEventGenerator::Event::PLACEHOLDER_CHANGED: case ui::AXEventGenerator::Event::PORTAL_ACTIVATED: @@ -282,9 +282,11 @@ void BrowserAccessibilityManagerAndroid::FireGeneratedEvent( case ui::AXEventGenerator::Event::SORT_CHANGED: case ui::AXEventGenerator::Event::STATE_CHANGED: case ui::AXEventGenerator::Event::SUBTREE_CREATED: + case ui::AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED: case ui::AXEventGenerator::Event::VALUE_MAX_CHANGED: case ui::AXEventGenerator::Event::VALUE_MIN_CHANGED: case ui::AXEventGenerator::Event::VALUE_STEP_CHANGED: + case ui::AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED: // There are some notifications that aren't meaningful on Android. // It's okay to skip them. break; diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_auralinux.cc b/chromium/content/browser/accessibility/browser_accessibility_manager_auralinux.cc index e4ee1b221bf..cffd90d4e01 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_auralinux.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_auralinux.cc @@ -78,6 +78,11 @@ void BrowserAccessibilityManagerAuraLinux::FireLoadingEvent( g_signal_emit_by_name(obj, "load_complete"); } +void BrowserAccessibilityManagerAuraLinux::FireEnabledChangedEvent( + BrowserAccessibility* node) { + ToBrowserAccessibilityAuraLinux(node)->GetNode()->OnEnabledChanged(); +} + void BrowserAccessibilityManagerAuraLinux::FireExpandedEvent( BrowserAccessibility* node, bool is_expanded) { @@ -120,6 +125,11 @@ void BrowserAccessibilityManagerAuraLinux::FireSortDirectionChangedEvent( ToBrowserAccessibilityAuraLinux(node)->GetNode()->OnSortDirectionChanged(); } +void BrowserAccessibilityManagerAuraLinux::FireTextAttributesChangedEvent( + BrowserAccessibility* node) { + ToBrowserAccessibilityAuraLinux(node)->GetNode()->OnTextAttributesChanged(); +} + void BrowserAccessibilityManagerAuraLinux::FireSubtreeCreatedEvent( BrowserAccessibility* node) { // Sending events during a load would create a lot of spam, don't do that. @@ -155,6 +165,9 @@ void BrowserAccessibilityManagerAuraLinux::FireGeneratedEvent( case ui::AXEventGenerator::Event::DOCUMENT_TITLE_CHANGED: FireEvent(node, ax::mojom::Event::kDocumentTitleChanged); break; + case ui::AXEventGenerator::Event::ENABLED_CHANGED: + FireEnabledChangedEvent(node); + break; case ui::AXEventGenerator::Event::EXPANDED: FireExpandedEvent(node, true); break; @@ -190,7 +203,58 @@ void BrowserAccessibilityManagerAuraLinux::FireGeneratedEvent( case ui::AXEventGenerator::Event::INVALID_STATUS_CHANGED: FireEvent(node, ax::mojom::Event::kInvalidStatusChanged); break; - default: + case ui::AXEventGenerator::Event::OBJECT_ATTRIBUTE_CHANGED: + // TODO(1108872): Fire event. + break; + case ui::AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED: + FireTextAttributesChangedEvent(node); + break; + case ui::AXEventGenerator::Event::ACCESS_KEY_CHANGED: + case ui::AXEventGenerator::Event::ALERT: + case ui::AXEventGenerator::Event::ATOMIC_CHANGED: + case ui::AXEventGenerator::Event::AUTO_COMPLETE_CHANGED: + case ui::AXEventGenerator::Event::BUSY_CHANGED: + case ui::AXEventGenerator::Event::CHILDREN_CHANGED: + case ui::AXEventGenerator::Event::CONTROLS_CHANGED: + case ui::AXEventGenerator::Event::CLASS_NAME_CHANGED: + case ui::AXEventGenerator::Event::DESCRIBED_BY_CHANGED: + case ui::AXEventGenerator::Event::DROPEFFECT_CHANGED: + case ui::AXEventGenerator::Event::FOCUS_CHANGED: + case ui::AXEventGenerator::Event::FLOW_FROM_CHANGED: + case ui::AXEventGenerator::Event::FLOW_TO_CHANGED: + case ui::AXEventGenerator::Event::GRABBED_CHANGED: + case ui::AXEventGenerator::Event::HASPOPUP_CHANGED: + case ui::AXEventGenerator::Event::HIERARCHICAL_LEVEL_CHANGED: + case ui::AXEventGenerator::Event::IGNORED_CHANGED: + case ui::AXEventGenerator::Event::IMAGE_ANNOTATION_CHANGED: + case ui::AXEventGenerator::Event::KEY_SHORTCUTS_CHANGED: + case ui::AXEventGenerator::Event::LABELED_BY_CHANGED: + case ui::AXEventGenerator::Event::LANGUAGE_CHANGED: + case ui::AXEventGenerator::Event::LAYOUT_INVALIDATED: + case ui::AXEventGenerator::Event::LIVE_REGION_CHANGED: + case ui::AXEventGenerator::Event::LIVE_REGION_CREATED: + case ui::AXEventGenerator::Event::LIVE_REGION_NODE_CHANGED: + case ui::AXEventGenerator::Event::LIVE_RELEVANT_CHANGED: + case ui::AXEventGenerator::Event::LIVE_STATUS_CHANGED: + case ui::AXEventGenerator::Event::MULTILINE_STATE_CHANGED: + case ui::AXEventGenerator::Event::MULTISELECTABLE_STATE_CHANGED: + case ui::AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED: + case ui::AXEventGenerator::Event::PLACEHOLDER_CHANGED: + case ui::AXEventGenerator::Event::PORTAL_ACTIVATED: + case ui::AXEventGenerator::Event::POSITION_IN_SET_CHANGED: + case ui::AXEventGenerator::Event::READONLY_CHANGED: + case ui::AXEventGenerator::Event::RELATED_NODE_CHANGED: + case ui::AXEventGenerator::Event::REQUIRED_STATE_CHANGED: + case ui::AXEventGenerator::Event::ROLE_CHANGED: + case ui::AXEventGenerator::Event::ROW_COUNT_CHANGED: + case ui::AXEventGenerator::Event::SCROLL_HORIZONTAL_POSITION_CHANGED: + case ui::AXEventGenerator::Event::SCROLL_VERTICAL_POSITION_CHANGED: + case ui::AXEventGenerator::Event::SET_SIZE_CHANGED: + case ui::AXEventGenerator::Event::STATE_CHANGED: + case ui::AXEventGenerator::Event::VALUE_MAX_CHANGED: + case ui::AXEventGenerator::Event::VALUE_MIN_CHANGED: + case ui::AXEventGenerator::Event::VALUE_STEP_CHANGED: + case ui::AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED: // Need to implement. break; } diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_auralinux.h b/chromium/content/browser/accessibility/browser_accessibility_manager_auralinux.h index d86cba09251..834f2db06b8 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_auralinux.h +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_auralinux.h @@ -35,11 +35,13 @@ class CONTENT_EXPORT BrowserAccessibilityManagerAuraLinux BrowserAccessibility* node) override; void FireSelectedEvent(BrowserAccessibility* node); + void FireEnabledChangedEvent(BrowserAccessibility* node); void FireExpandedEvent(BrowserAccessibility* node, bool is_expanded); void FireLoadingEvent(BrowserAccessibility* node, bool is_loading); void FireNameChangedEvent(BrowserAccessibility* node); void FireDescriptionChangedEvent(BrowserAccessibility* node); void FireSortDirectionChangedEvent(BrowserAccessibility* node); + void FireTextAttributesChangedEvent(BrowserAccessibility* node); void FireSubtreeCreatedEvent(BrowserAccessibility* node); void OnFindInPageResult(int request_id, int match_index, diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_mac.h b/chromium/content/browser/accessibility/browser_accessibility_manager_mac.h index 8fb447a74cb..b4043f56341 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_mac.h +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_mac.h @@ -60,12 +60,16 @@ class CONTENT_EXPORT BrowserAccessibilityManagerMac NSDictionary* GetUserInfoForValueChangedNotification( const BrowserAccessibilityCocoa* native_node, const base::string16& deleted_text, - const base::string16& inserted_text) const; + const base::string16& inserted_text, + id edit_text_marker) const; void AnnounceActiveDescendant(BrowserAccessibility* node) const; bool IsInGeneratedEventBatch(ui::AXEventGenerator::Event event_type) const; + // Returns whether this page is a new tab page on Chrome. + bool IsChromeNewTabPage(); + // Keeps track of any edits that have been made by the user during a tree // update. Used by NSAccessibilityValueChangedNotification. // Maps AXNode IDs to value attribute changes. @@ -78,6 +82,6 @@ class CONTENT_EXPORT BrowserAccessibilityManagerMac DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityManagerMac); }; -} +} // namespace content #endif // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_MAC_H_ diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_mac.mm b/chromium/content/browser/accessibility/browser_accessibility_manager_mac.mm index 9795a4ce389..5d7acab1b17 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_mac.mm +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_mac.mm @@ -16,6 +16,7 @@ #import "content/browser/accessibility/browser_accessibility_mac.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/web_contents.h" #include "ui/accelerated_widget_mac/accelerated_widget_mac.h" #include "ui/accessibility/ax_role_properties.h" @@ -66,9 +67,9 @@ enum AXTextEditType { AXTextEditTypeAttributesChange }; +// Native mac notifications fired. NSString* const NSAccessibilityAutocorrectionOccurredNotification = @"AXAutocorrectionOccurred"; -NSString* const NSAccessibilityLayoutCompleteNotification = @"AXLayoutComplete"; NSString* const NSAccessibilityLoadCompleteNotification = @"AXLoadComplete"; NSString* const NSAccessibilityInvalidStatusChangedNotification = @"AXInvalidStatusChanged"; @@ -80,6 +81,9 @@ NSString* const NSAccessibilityExpandedChanged = @"AXExpandedChanged"; NSString* const NSAccessibilityMenuItemSelectedNotification = @"AXMenuItemSelected"; +// The following native mac notifications are not fired: +// AXLayoutComplete: Voiceover does not use this, it is considered too spammy. + // Attributes used for NSAccessibilitySelectedTextChangedNotification and // NSAccessibilityValueChangedNotification. NSString* const NSAccessibilityTextStateChangeTypeKey = @@ -94,6 +98,8 @@ NSString* const NSAccessibilityTextSelectionChangedFocus = NSString* const NSAccessibilityTextChangeElement = @"AXTextChangeElement"; NSString* const NSAccessibilityTextEditType = @"AXTextEditType"; NSString* const NSAccessibilityTextChangeValue = @"AXTextChangeValue"; +NSString* const NSAccessibilityChangeValueStartMarker = + @"AXTextChangeValueStartMarker"; NSString* const NSAccessibilityTextChangeValueLength = @"AXTextChangeValueLength"; NSString* const NSAccessibilityTextChangeValues = @"AXTextChangeValues"; @@ -140,11 +146,6 @@ BrowserAccessibility* BrowserAccessibilityManagerMac::GetFocus() const { if (!focus) return nullptr; - // For editable combo boxes, focus should stay on the combo box so the user - // will not be taken out of the combo box while typing. - if (focus->GetRole() == ax::mojom::Role::kTextFieldWithComboBox) - return focus; - // Otherwise, follow the active descendant. return GetActiveDescendant(focus); } @@ -165,9 +166,6 @@ void BrowserAccessibilityManagerMac::FireBlinkEvent( case ax::mojom::Event::kAutocorrectionOccured: mac_notification = NSAccessibilityAutocorrectionOccurredNotification; break; - case ax::mojom::Event::kLayoutComplete: - mac_notification = NSAccessibilityLayoutCompleteNotification; - break; default: return; } @@ -224,19 +222,25 @@ void BrowserAccessibilityManagerMac::FireGeneratedEvent( } break; case ui::AXEventGenerator::Event::LOAD_COMPLETE: - // This notification should only be fired on the top document. - // Iframes should use |ax::mojom::Event::kLayoutComplete| to signify that - // they have finished loading. - if (IsRootTree()) { + // On MacOS 10.15, firing AXLoadComplete causes focus to move to the + // webpage and read content, despite the "Automatically speak the webpage" + // checkbox in Voiceover utility being unchecked. The checkbox is + // unchecked by default in 10.15 so we don't fire AXLoadComplete events to + // support the default behavior. + if (base::mac::IsOS10_15()) + return; + // |NSAccessibilityLoadCompleteNotification| should only be fired on the + // top document and when the document is not Chrome's new tab page. + if (IsRootTree() && !IsChromeNewTabPage()) { mac_notification = NSAccessibilityLoadCompleteNotification; } else { - mac_notification = NSAccessibilityLayoutCompleteNotification; + // Voiceover moves focus to the web content when it receives an + // AXLoadComplete event. On Chrome's new tab page, focus should stay + // in the omnibox, so we purposefully do not fire the AXLoadComplete + // event in this case. + return; } break; - case ui::AXEventGenerator::Event::PORTAL_ACTIVATED: - DCHECK(IsRootTree()); - mac_notification = NSAccessibilityLoadCompleteNotification; - break; case ui::AXEventGenerator::Event::INVALID_STATUS_CHANGED: mac_notification = NSAccessibilityInvalidStatusChangedNotification; break; @@ -313,16 +317,18 @@ void BrowserAccessibilityManagerMac::FireGeneratedEvent( if (base::mac::IsAtLeastOS10_11() && !text_edits_.empty()) { base::string16 deleted_text; base::string16 inserted_text; - int32_t id = node->GetId(); - const auto iterator = text_edits_.find(id); + int32_t node_id = node->GetId(); + const auto iterator = text_edits_.find(node_id); + id edit_text_marker = nil; if (iterator != text_edits_.end()) { AXTextEdit text_edit = iterator->second; deleted_text = text_edit.deleted_text; inserted_text = text_edit.inserted_text; + edit_text_marker = text_edit.edit_text_marker; } NSDictionary* user_info = GetUserInfoForValueChangedNotification( - native_node, deleted_text, inserted_text); + native_node, deleted_text, inserted_text, edit_text_marker); BrowserAccessibility* root = GetRoot(); if (!root) @@ -437,8 +443,10 @@ void BrowserAccessibilityManagerMac::FireGeneratedEvent( case ui::AXEventGenerator::Event::MULTILINE_STATE_CHANGED: case ui::AXEventGenerator::Event::MULTISELECTABLE_STATE_CHANGED: case ui::AXEventGenerator::Event::NAME_CHANGED: + case ui::AXEventGenerator::Event::OBJECT_ATTRIBUTE_CHANGED: case ui::AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED: case ui::AXEventGenerator::Event::PLACEHOLDER_CHANGED: + case ui::AXEventGenerator::Event::PORTAL_ACTIVATED: case ui::AXEventGenerator::Event::POSITION_IN_SET_CHANGED: case ui::AXEventGenerator::Event::READONLY_CHANGED: case ui::AXEventGenerator::Event::RELATED_NODE_CHANGED: @@ -451,9 +459,11 @@ void BrowserAccessibilityManagerMac::FireGeneratedEvent( case ui::AXEventGenerator::Event::SORT_CHANGED: case ui::AXEventGenerator::Event::STATE_CHANGED: case ui::AXEventGenerator::Event::SUBTREE_CREATED: + case ui::AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED: case ui::AXEventGenerator::Event::VALUE_MAX_CHANGED: case ui::AXEventGenerator::Event::VALUE_MIN_CHANGED: case ui::AXEventGenerator::Event::VALUE_STEP_CHANGED: + case ui::AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED: // There are some notifications that aren't meaningful on Mac. // It's okay to skip them. return; @@ -542,29 +552,42 @@ NSDictionary* BrowserAccessibilityManagerMac::GetUserInfoForValueChangedNotification( const BrowserAccessibilityCocoa* native_node, const base::string16& deleted_text, - const base::string16& inserted_text) const { + const base::string16& inserted_text, + id edit_text_marker) const { DCHECK(native_node); if (deleted_text.empty() && inserted_text.empty()) return nil; NSMutableArray* changes = [[[NSMutableArray alloc] init] autorelease]; if (!deleted_text.empty()) { - [changes addObject:@{ - NSAccessibilityTextEditType : @(AXTextEditTypeDelete), - NSAccessibilityTextChangeValueLength : @(deleted_text.length()), - NSAccessibilityTextChangeValue : base::SysUTF16ToNSString(deleted_text) - }]; + NSMutableDictionary* change = + [NSMutableDictionary dictionaryWithDictionary:@{ + NSAccessibilityTextEditType : @(AXTextEditTypeDelete), + NSAccessibilityTextChangeValueLength : @(deleted_text.length()), + NSAccessibilityTextChangeValue : + base::SysUTF16ToNSString(deleted_text) + }]; + if (edit_text_marker) { + change[NSAccessibilityChangeValueStartMarker] = edit_text_marker; + } + [changes addObject:change]; } if (!inserted_text.empty()) { // TODO(nektar): Figure out if this is a paste, insertion or typing. // Changes to Blink would be required. A heuristic is currently used. auto edit_type = inserted_text.length() > 1 ? @(AXTextEditTypeInsert) : @(AXTextEditTypeTyping); - [changes addObject:@{ - NSAccessibilityTextEditType : edit_type, - NSAccessibilityTextChangeValueLength : @(inserted_text.length()), - NSAccessibilityTextChangeValue : base::SysUTF16ToNSString(inserted_text) - }]; + NSMutableDictionary* change = + [NSMutableDictionary dictionaryWithDictionary:@{ + NSAccessibilityTextEditType : edit_type, + NSAccessibilityTextChangeValueLength : @(inserted_text.length()), + NSAccessibilityTextChangeValue : + base::SysUTF16ToNSString(inserted_text) + }]; + if (edit_text_marker) { + change[NSAccessibilityChangeValueStartMarker] = edit_text_marker; + } + [changes addObject:change]; } return @{ @@ -582,4 +605,14 @@ id BrowserAccessibilityManagerMac::GetWindow() { return delegate()->AccessibilityGetNativeViewAccessibleForWindow(); } +bool BrowserAccessibilityManagerMac::IsChromeNewTabPage() { + if (!delegate() || !IsRootTree()) + return false; + content::WebContents* web_contents = delegate()->AccessibilityWebContents(); + const GURL& url = web_contents->GetVisibleURL(); + return url == GURL("chrome://newtab/") || + url == GURL("chrome://new-tab-page") || + url == GURL("chrome-search://local-ntp/local-ntp.html"); +} + } // namespace content diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_unittest.cc b/chromium/content/browser/accessibility/browser_accessibility_manager_unittest.cc index 971f4519665..de5fb715c22 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_unittest.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_unittest.cc @@ -136,7 +136,7 @@ TEST_F(BrowserAccessibilityManagerTest, BoundsForRange) { inline_text1.role = ax::mojom::Role::kInlineTextBox; inline_text1.SetName("Hello, "); inline_text1.relative_bounds.bounds = gfx::RectF(100, 100, 29, 9); - inline_text1.SetTextDirection(ax::mojom::TextDirection::kLtr); + inline_text1.SetTextDirection(ax::mojom::WritingDirection::kLtr); std::vector<int32_t> character_offsets1; character_offsets1.push_back(6); // 0 character_offsets1.push_back(11); // 1 @@ -154,7 +154,7 @@ TEST_F(BrowserAccessibilityManagerTest, BoundsForRange) { inline_text2.role = ax::mojom::Role::kInlineTextBox; inline_text2.SetName("world."); inline_text2.relative_bounds.bounds = gfx::RectF(100, 109, 28, 9); - inline_text2.SetTextDirection(ax::mojom::TextDirection::kLtr); + inline_text2.SetTextDirection(ax::mojom::WritingDirection::kLtr); std::vector<int32_t> character_offsets2; character_offsets2.push_back(5); character_offsets2.push_back(10); @@ -242,7 +242,7 @@ TEST_F(BrowserAccessibilityManagerTest, BoundsForRangeMultiElement) { inline_text1.role = ax::mojom::Role::kInlineTextBox; inline_text1.SetName("ABC"); inline_text1.relative_bounds.bounds = gfx::RectF(0, 20, 33, 9); - inline_text1.SetTextDirection(ax::mojom::TextDirection::kLtr); + inline_text1.SetTextDirection(ax::mojom::WritingDirection::kLtr); std::vector<int32_t> character_offsets{10, 21, 33}; inline_text1.AddIntListAttribute( ax::mojom::IntListAttribute::kCharacterOffsets, character_offsets); @@ -260,7 +260,7 @@ TEST_F(BrowserAccessibilityManagerTest, BoundsForRangeMultiElement) { inline_text2.role = ax::mojom::Role::kInlineTextBox; inline_text2.SetName("ABC"); inline_text2.relative_bounds.bounds = gfx::RectF(10, 40, 33, 9); - inline_text2.SetTextDirection(ax::mojom::TextDirection::kLtr); + inline_text2.SetTextDirection(ax::mojom::WritingDirection::kLtr); inline_text2.AddIntListAttribute( ax::mojom::IntListAttribute::kCharacterOffsets, character_offsets); static_text2.child_ids.push_back(5); @@ -360,7 +360,7 @@ TEST_F(BrowserAccessibilityManagerTest, BoundsForRangeBiDi) { inline_text1.role = ax::mojom::Role::kInlineTextBox; inline_text1.SetName("123"); inline_text1.relative_bounds.bounds = gfx::RectF(100, 100, 30, 20); - inline_text1.SetTextDirection(ax::mojom::TextDirection::kLtr); + inline_text1.SetTextDirection(ax::mojom::WritingDirection::kLtr); std::vector<int32_t> character_offsets1; character_offsets1.push_back(10); // 0 character_offsets1.push_back(20); // 1 @@ -374,7 +374,7 @@ TEST_F(BrowserAccessibilityManagerTest, BoundsForRangeBiDi) { inline_text2.role = ax::mojom::Role::kInlineTextBox; inline_text2.SetName("abc"); inline_text2.relative_bounds.bounds = gfx::RectF(130, 100, 30, 20); - inline_text2.SetTextDirection(ax::mojom::TextDirection::kRtl); + inline_text2.SetTextDirection(ax::mojom::WritingDirection::kRtl); std::vector<int32_t> character_offsets2; character_offsets2.push_back(10); character_offsets2.push_back(20); @@ -457,7 +457,7 @@ TEST_F(BrowserAccessibilityManagerTest, BoundsForRangeScrolledWindow) { inline_text.role = ax::mojom::Role::kInlineTextBox; inline_text.SetName("ABC"); inline_text.relative_bounds.bounds = gfx::RectF(100, 100, 16, 9); - inline_text.SetTextDirection(ax::mojom::TextDirection::kLtr); + inline_text.SetTextDirection(ax::mojom::WritingDirection::kLtr); std::vector<int32_t> character_offsets1; character_offsets1.push_back(6); // 0 character_offsets1.push_back(11); // 1 @@ -536,7 +536,7 @@ TEST_F(BrowserAccessibilityManagerTest, BoundsForRangeOnParentElement) { inline_text1.role = ax::mojom::Role::kInlineTextBox; inline_text1.SetName("AB"); inline_text1.relative_bounds.bounds = gfx::RectF(100, 100, 40, 20); - inline_text1.SetTextDirection(ax::mojom::TextDirection::kLtr); + inline_text1.SetTextDirection(ax::mojom::WritingDirection::kLtr); std::vector<int32_t> character_offsets1; character_offsets1.push_back(20); // 0 character_offsets1.push_back(40); // 1 @@ -548,7 +548,7 @@ TEST_F(BrowserAccessibilityManagerTest, BoundsForRangeOnParentElement) { inline_text2.role = ax::mojom::Role::kInlineTextBox; inline_text2.SetName("CD"); inline_text2.relative_bounds.bounds = gfx::RectF(160, 100, 40, 20); - inline_text2.SetTextDirection(ax::mojom::TextDirection::kLtr); + inline_text2.SetTextDirection(ax::mojom::WritingDirection::kLtr); std::vector<int32_t> character_offsets2; character_offsets2.push_back(20); // 0 character_offsets2.push_back(40); // 1 diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_win.cc b/chromium/content/browser/accessibility/browser_accessibility_manager_win.cc index 579fafab388..d626899fd7a 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_win.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_win.cc @@ -151,10 +151,19 @@ void BrowserAccessibilityManagerWin::FireGeneratedEvent( FireUiaAccessibilityEvent(UIA_SystemAlertEventId, node); break; case ui::AXEventGenerator::Event::ATOMIC_CHANGED: + aria_properties_events_.insert(node); + break; case ui::AXEventGenerator::Event::BUSY_CHANGED: aria_properties_events_.insert(node); break; case ui::AXEventGenerator::Event::CHECKED_STATE_CHANGED: + // https://www.w3.org/TR/core-aam-1.1/#mapping_state-property_table + // SelectionItem.IsSelected is set according to the True or False value of + // aria-checked for 'radio' and 'menuitemradio' roles. + if (ui::IsRadio(node->GetRole())) { + HandleSelectedStateChanged(uia_selection_events_, node, + IsUIANodeSelected(node)); + } FireUiaPropertyChangedEvent(UIA_ToggleToggleStatePropertyId, node); aria_properties_events_.insert(node); break; @@ -214,6 +223,8 @@ void BrowserAccessibilityManagerWin::FireGeneratedEvent( break; // aria-grabbed is deprecated in WAI-ARIA 1.1. case ui::AXEventGenerator::Event::GRABBED_CHANGED: + aria_properties_events_.insert(node); + break; case ui::AXEventGenerator::Event::HASPOPUP_CHANGED: aria_properties_events_.insert(node); break; @@ -292,6 +303,10 @@ void BrowserAccessibilityManagerWin::FireGeneratedEvent( if (node->GetData().GetNameFrom() != ax::mojom::NameFrom::kContents) FireWinAccessibilityEvent(EVENT_OBJECT_NAMECHANGE, node); break; + case ui::AXEventGenerator::Event::OBJECT_ATTRIBUTE_CHANGED: + FireWinAccessibilityEvent(IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED, node); + // TODO(crbug.com/1108871): Fire UIA event. + break; case ui::AXEventGenerator::Event::PLACEHOLDER_CHANGED: FireUiaPropertyChangedEvent(UIA_HelpTextPropertyId, node); break; @@ -324,7 +339,10 @@ void BrowserAccessibilityManagerWin::FireGeneratedEvent( node); break; case ui::AXEventGenerator::Event::SELECTED_CHANGED: - HandleSelectedStateChanged(node); + HandleSelectedStateChanged(ia2_selection_events_, node, + IsIA2NodeSelected(node)); + HandleSelectedStateChanged(uia_selection_events_, node, + IsUIANodeSelected(node)); aria_properties_events_.insert(node); break; case ui::AXEventGenerator::Event::SELECTED_CHILDREN_CHANGED: @@ -346,6 +364,10 @@ void BrowserAccessibilityManagerWin::FireGeneratedEvent( FireUiaAccessibilityEvent(UIA_MenuOpenedEventId, node); } break; + case ui::AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED: + FireWinAccessibilityEvent(IA2_EVENT_TEXT_ATTRIBUTE_CHANGED, node); + FireUiaTextContainerEvent(UIA_Text_TextChangedEventId, node); + break; case ui::AXEventGenerator::Event::VALUE_CHANGED: FireWinAccessibilityEvent(EVENT_OBJECT_VALUECHANGE, node); if (node->GetData().IsRangeValueSupported()) { @@ -374,6 +396,9 @@ void BrowserAccessibilityManagerWin::FireGeneratedEvent( FireUiaPropertyChangedEvent(UIA_RangeValueLargeChangePropertyId, node); } break; + case ui::AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED: + FireWinAccessibilityEvent(EVENT_OBJECT_STATECHANGE, node); + break; case ui::AXEventGenerator::Event::AUTO_COMPLETE_CHANGED: case ui::AXEventGenerator::Event::DOCUMENT_TITLE_CHANGED: case ui::AXEventGenerator::Event::FOCUS_CHANGED: @@ -465,8 +490,7 @@ void BrowserAccessibilityManagerWin::FireUiaPropertyChangedEvent( // UIA_AriaPropertiesPropertyId-hidden event on non-text node marked as // ignored. if (node->IsIgnored() || base::Contains(ignored_changed_nodes_, node)) { - if (uia_property != UIA_AriaPropertiesPropertyId || - node->IsTextOnlyObject()) + if (uia_property != UIA_AriaPropertiesPropertyId || node->IsText()) return; } @@ -636,34 +660,144 @@ void BrowserAccessibilityManagerWin::OnAtomicUpdateFinished( } } -void BrowserAccessibilityManagerWin::HandleSelectedStateChanged( +// static +bool BrowserAccessibilityManagerWin::IsIA2NodeSelected( BrowserAccessibility* node) { - const bool is_selected = - node->GetBoolAttribute(ax::mojom::BoolAttribute::kSelected); - - // Nodes that have selection container may support multiselect, for such nodes - // we add them to |selection_events_|, which FinalizeAccessibilityEvents - // handles selection item events firing. - // For nodes that do not have selection container, only single select is - // supported, selection item events firing are handled here. - if (auto* selection_container = node->PlatformGetSelectionContainer()) { - if (is_selected) { - selection_events_[selection_container].added.push_back(node); - } else { - selection_events_[selection_container].removed.push_back(node); + return node->GetBoolAttribute(ax::mojom::BoolAttribute::kSelected); +} + +// static +bool BrowserAccessibilityManagerWin::IsUIANodeSelected( + BrowserAccessibility* node) { + // https://www.w3.org/TR/core-aam-1.1/#mapping_state-property_table + // SelectionItem.IsSelected is set according to the True or False value of + // aria-checked for 'radio' and 'menuitemradio' roles. + if (ui::IsRadio(node->GetRole())) + return node->GetData().GetCheckedState() == ax::mojom::CheckedState::kTrue; + + return node->GetBoolAttribute(ax::mojom::BoolAttribute::kSelected); +} + +void BrowserAccessibilityManagerWin::FireIA2SelectionEvents( + BrowserAccessibility* container, + BrowserAccessibility* only_selected_child, + const SelectionEvents& changes) { + if (only_selected_child) { + // Fire 'ElementSelected' on the only selected child. + FireWinAccessibilityEvent(EVENT_OBJECT_SELECTION, only_selected_child); + } else { + const bool container_is_multiselectable = + container && container->HasState(ax::mojom::State::kMultiselectable); + for (auto* item : changes.added) { + if (container_is_multiselectable) + FireWinAccessibilityEvent(EVENT_OBJECT_SELECTIONADD, item); + else + FireWinAccessibilityEvent(EVENT_OBJECT_SELECTION, item); } + for (auto* item : changes.removed) + FireWinAccessibilityEvent(EVENT_OBJECT_SELECTIONREMOVE, item); + } +} + +void BrowserAccessibilityManagerWin::FireUIASelectionEvents( + BrowserAccessibility* container, + BrowserAccessibility* only_selected_child, + const SelectionEvents& changes) { + if (only_selected_child) { + // Fire 'ElementSelected' on the only selected child. + FireUiaAccessibilityEvent(UIA_SelectionItem_ElementSelectedEventId, + only_selected_child); + FireUiaPropertyChangedEvent(UIA_SelectionItemIsSelectedPropertyId, + only_selected_child); + for (auto* item : changes.removed) + FireUiaPropertyChangedEvent(UIA_SelectionItemIsSelectedPropertyId, item); } else { - if (is_selected) { - FireWinAccessibilityEvent(EVENT_OBJECT_SELECTION, node); - FireUiaAccessibilityEvent(UIA_SelectionItem_ElementSelectedEventId, node); + // Per UIA documentation, beyond the "invalidate limit" we're supposed to + // fire a 'SelectionInvalidated' event. The exact value isn't specified, + // but System.Windows.Automation.Provider uses a value of 20. + static const size_t kInvalidateLimit = 20; + if ((changes.added.size() + changes.removed.size()) > kInvalidateLimit) { + DCHECK_NE(container, nullptr); + FireUiaAccessibilityEvent(UIA_Selection_InvalidatedEventId, container); } else { - FireWinAccessibilityEvent(EVENT_OBJECT_SELECTIONREMOVE, node); - FireUiaAccessibilityEvent( - UIA_SelectionItem_ElementRemovedFromSelectionEventId, node); + const bool container_is_multiselectable = + container && container->HasState(ax::mojom::State::kMultiselectable); + for (auto* item : changes.added) { + if (container_is_multiselectable) { + FireUiaAccessibilityEvent( + UIA_SelectionItem_ElementAddedToSelectionEventId, item); + } else { + FireUiaAccessibilityEvent(UIA_SelectionItem_ElementSelectedEventId, + item); + } + FireUiaPropertyChangedEvent(UIA_SelectionItemIsSelectedPropertyId, + item); + } + for (auto* item : changes.removed) { + FireUiaAccessibilityEvent( + UIA_SelectionItem_ElementRemovedFromSelectionEventId, item); + FireUiaPropertyChangedEvent(UIA_SelectionItemIsSelectedPropertyId, + item); + } } } } +// static +void BrowserAccessibilityManagerWin::HandleSelectedStateChanged( + SelectionEventsMap& selection_events_map, + BrowserAccessibility* node, + bool is_selected) { + // If |node| belongs to a selection container, then map the events with the + // selection container as the key because |FinalizeSelectionEvents| needs to + // determine whether or not there is only one element selected in order to + // optimize what platform events are sent. + BrowserAccessibility* key = node; + if (auto* selection_container = node->PlatformGetSelectionContainer()) + key = selection_container; + + if (is_selected) + selection_events_map[key].added.push_back(node); + else + selection_events_map[key].removed.push_back(node); +} + +// static +void BrowserAccessibilityManagerWin::FinalizeSelectionEvents( + SelectionEventsMap& selection_events_map, + IsSelectedPredicate is_selected_predicate, + FirePlatformSelectionEventsCallback fire_platform_events_callback) { + for (auto&& selected : selection_events_map) { + BrowserAccessibility* key_node = selected.first; + SelectionEvents& changes = selected.second; + + // Determine if |node| is a selection container with one selected child in + // order to optimize what platform events are sent. + BrowserAccessibility* container = nullptr; + BrowserAccessibility* only_selected_child = nullptr; + if (ui::IsContainerWithSelectableChildren(key_node->GetRole())) { + container = key_node; + for (auto it = container->InternalChildrenBegin(); + it != container->InternalChildrenEnd(); ++it) { + auto* child = it.get(); + if (is_selected_predicate.Run(child)) { + if (!only_selected_child) { + only_selected_child = child; + continue; + } + + only_selected_child = nullptr; + break; + } + } + } + + fire_platform_events_callback.Run(container, only_selected_child, changes); + } + + selection_events_map.clear(); +} + void BrowserAccessibilityManagerWin::BeforeAccessibilityEvents() { BrowserAccessibilityManager::BeforeAccessibilityEvents(); @@ -700,56 +834,17 @@ void BrowserAccessibilityManagerWin::FinalizeAccessibilityEvents() { text_selection_changed_events_.clear(); // Finalize selection item events. - for (auto&& selected : selection_events_) { - auto* container = selected.first; - auto&& changes = selected.second; - - // Count the number of selected items - size_t selected_count = 0; - BrowserAccessibility* first_selected_child = nullptr; - for (auto it = container->InternalChildrenBegin(); - it != container->InternalChildrenEnd(); ++it) { - auto* child = it.get(); - if (child->GetBoolAttribute(ax::mojom::BoolAttribute::kSelected)) { - if (!first_selected_child) - first_selected_child = child; - selected_count++; - } - } + FinalizeSelectionEvents( + ia2_selection_events_, base::BindRepeating(&IsIA2NodeSelected), + base::BindRepeating( + &BrowserAccessibilityManagerWin::FireIA2SelectionEvents, + base::Unretained(this))); + FinalizeSelectionEvents( + uia_selection_events_, base::BindRepeating(&IsUIANodeSelected), + base::BindRepeating( + &BrowserAccessibilityManagerWin::FireUIASelectionEvents, + base::Unretained(this))); - if (selected_count == 1) { - // Fire 'ElementSelected' on the only selected child - FireWinAccessibilityEvent(EVENT_OBJECT_SELECTION, first_selected_child); - FireUiaAccessibilityEvent(UIA_SelectionItem_ElementSelectedEventId, - first_selected_child); - } else { - // Per UIA documentation, beyond the "invalidate limit" we're supposed to - // fire a 'SelectionInvalidated' event. The exact value isn't specified, - // but System.Windows.Automation.Provider uses a value of 20. - static const size_t kInvalidateLimit = 20; - if ((changes.added.size() + changes.removed.size()) > kInvalidateLimit) { - FireUiaAccessibilityEvent(UIA_Selection_InvalidatedEventId, container); - } else { - for (auto* item : changes.added) { - if (container->HasState(ax::mojom::State::kMultiselectable)) { - FireWinAccessibilityEvent(EVENT_OBJECT_SELECTIONADD, item); - FireUiaAccessibilityEvent( - UIA_SelectionItem_ElementAddedToSelectionEventId, item); - } else { - FireWinAccessibilityEvent(EVENT_OBJECT_SELECTION, item); - FireUiaAccessibilityEvent(UIA_SelectionItem_ElementSelectedEventId, - item); - } - } - for (auto* item : changes.removed) { - FireWinAccessibilityEvent(EVENT_OBJECT_SELECTIONREMOVE, item); - FireUiaAccessibilityEvent( - UIA_SelectionItem_ElementRemovedFromSelectionEventId, item); - } - } - } - } - selection_events_.clear(); ignored_changed_nodes_.clear(); } diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_win.h b/chromium/content/browser/accessibility/browser_accessibility_manager_win.h index 20c72281451..63a9ed71660 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_win.h +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_win.h @@ -77,7 +77,40 @@ class CONTENT_EXPORT BrowserAccessibilityManagerWin const std::vector<ui::AXTreeObserver::Change>& changes) override; private: - void HandleSelectedStateChanged(BrowserAccessibility* node); + struct SelectionEvents { + std::vector<BrowserAccessibility*> added; + std::vector<BrowserAccessibility*> removed; + SelectionEvents(); + ~SelectionEvents(); + }; + + using SelectionEventsMap = std::map<BrowserAccessibility*, SelectionEvents>; + using IsSelectedPredicate = + base::RepeatingCallback<bool(BrowserAccessibility*)>; + using FirePlatformSelectionEventsCallback = + base::RepeatingCallback<void(BrowserAccessibility*, + BrowserAccessibility*, + const SelectionEvents&)>; + + static bool IsIA2NodeSelected(BrowserAccessibility* node); + static bool IsUIANodeSelected(BrowserAccessibility* node); + + void FireIA2SelectionEvents(BrowserAccessibility* container, + BrowserAccessibility* only_selected_child, + const SelectionEvents& changes); + void FireUIASelectionEvents(BrowserAccessibility* container, + BrowserAccessibility* only_selected_child, + const SelectionEvents& changes); + + static void HandleSelectedStateChanged( + SelectionEventsMap& selection_events_map, + BrowserAccessibility* node, + bool is_selected); + + static void FinalizeSelectionEvents( + SelectionEventsMap& selection_events_map, + IsSelectedPredicate is_selected_predicate, + FirePlatformSelectionEventsCallback fire_platform_events_callback); // Give BrowserAccessibilityManager::Create access to our constructor. friend class BrowserAccessibilityManager; @@ -109,13 +142,8 @@ class CONTENT_EXPORT BrowserAccessibilityManagerWin // Keep track of selection changes so we can optimize UIA event firing. // Pointers are only stored for the duration of |OnAccessibilityEvents|, and // the map is cleared in |FinalizeAccessibilityEvents|. - struct SelectionEvents { - std::vector<BrowserAccessibility*> added; - std::vector<BrowserAccessibility*> removed; - SelectionEvents(); - ~SelectionEvents(); - }; - std::map<BrowserAccessibility*, SelectionEvents> selection_events_; + SelectionEventsMap ia2_selection_events_; + SelectionEventsMap uia_selection_events_; DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityManagerWin); }; diff --git a/chromium/content/browser/accessibility/browser_accessibility_position.cc b/chromium/content/browser/accessibility/browser_accessibility_position.cc index f3faffebcda..ac66598c49b 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_position.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_position.cc @@ -46,7 +46,7 @@ bool BrowserAccessibilityPosition::IsInTextObject() const { if (IsNullPosition()) return false; DCHECK(GetAnchor()); - return GetAnchor()->IsTextOnlyObject(); + return GetAnchor()->IsText(); } bool BrowserAccessibilityPosition::IsInWhiteSpace() const { @@ -170,7 +170,7 @@ bool BrowserAccessibilityPosition::IsEmbeddedObjectInParent() const { #if defined(OS_WIN) || BUILDFLAG(USE_ATK) // Not all objects in the internal accessibility tree are exposed to platform // APIs. - return !IsNullPosition() && !GetAnchor()->IsTextOnlyObject() && + return !IsNullPosition() && !GetAnchor()->IsText() && !GetAnchor()->IsChildOfLeaf(); #else return false; diff --git a/chromium/content/browser/accessibility/browser_accessibility_state_impl.cc b/chromium/content/browser/accessibility/browser_accessibility_state_impl.cc index 68243d5ec48..8cf7fb7f787 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_state_impl.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_state_impl.cc @@ -157,11 +157,12 @@ void BrowserAccessibilityStateImpl::UpdateHistogramsForTesting() { UpdateHistogramsOnOtherThread(); } +void BrowserAccessibilityStateImpl::SetCaretBrowsingState(bool enabled) { + caret_browsing_enabled_ = enabled; +} + bool BrowserAccessibilityStateImpl::IsCaretBrowsingEnabled() const { - // TODO(crbug.com/1018947): Refine this check once UX provided to toggle caret - // browsing mode. - return base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableCaretBrowsing); + return caret_browsing_enabled_; } void BrowserAccessibilityStateImpl::UpdateHistogramsOnUIThread() { @@ -199,7 +200,7 @@ ui::AXMode BrowserAccessibilityStateImpl::GetAccessibilityMode() { return accessibility_mode_; } -#if !defined(OS_ANDROID) && !defined(OS_WIN) && !defined(OS_MACOSX) +#if !defined(OS_ANDROID) && !defined(OS_WIN) && !defined(OS_MAC) void BrowserAccessibilityStateImpl::PlatformInitialize() {} void BrowserAccessibilityStateImpl:: diff --git a/chromium/content/browser/accessibility/browser_accessibility_state_impl.h b/chromium/content/browser/accessibility/browser_accessibility_state_impl.h index 49aaf7ddec5..d266cb612bf 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_state_impl.h +++ b/chromium/content/browser/accessibility/browser_accessibility_state_impl.h @@ -60,10 +60,11 @@ class CONTENT_EXPORT BrowserAccessibilityStateImpl bool IsAccessibleBrowser() override; void AddUIThreadHistogramCallback(base::OnceClosure callback) override; void AddOtherThreadHistogramCallback(base::OnceClosure callback) override; - void UpdateHistogramsForTesting() override; + void SetCaretBrowsingState(bool enabled) override; - // Returns whether caret browsing is enabled for this browser session. + // Returns whether caret browsing is enabled for the most recently + // used profile. bool IsCaretBrowsingEnabled() const; // AXModeObserver @@ -114,6 +115,10 @@ class CONTENT_EXPORT BrowserAccessibilityStateImpl bool disable_hot_tracking_; + // Keeps track of whether caret browsing is enabled for the most + // recently used profile. + bool caret_browsing_enabled_ = false; + #if defined(OS_WIN) // Only used on Windows std::unique_ptr<gfx::SingletonHwndObserver> singleton_hwnd_observer_; diff --git a/chromium/content/browser/accessibility/browser_accessibility_state_impl_android.cc b/chromium/content/browser/accessibility/browser_accessibility_state_impl_android.cc index b8c53a642ce..e04b542ff2d 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_state_impl_android.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_state_impl_android.cc @@ -56,7 +56,7 @@ void JNI_BrowserAccessibilityState_OnAnimatorDurationScaleChanged(JNIEnv* env) { gfx::Animation::UpdatePrefersReducedMotion(); for (WebContentsImpl* wc : WebContentsImpl::GetAllWebContents()) { - wc->GetRenderViewHost()->OnWebkitPreferencesChanged(); + wc->OnWebPreferencesChanged(); } } diff --git a/chromium/content/browser/accessibility/browser_accessibility_state_impl_mac.mm b/chromium/content/browser/accessibility/browser_accessibility_state_impl_mac.mm index 7a59582ea63..6950df73139 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_state_impl_mac.mm +++ b/chromium/content/browser/accessibility/browser_accessibility_state_impl_mac.mm @@ -46,7 +46,7 @@ void SetupAccessibilityDisplayOptionsNotifier() { gfx::Animation::UpdatePrefersReducedMotion(); for (WebContentsImpl* wc : WebContentsImpl::GetAllWebContents()) { - wc->GetRenderViewHost()->OnWebkitPreferencesChanged(); + wc->OnWebPreferencesChanged(); } }]; } diff --git a/chromium/content/browser/accessibility/browser_accessibility_state_impl_win.cc b/chromium/content/browser/accessibility/browser_accessibility_state_impl_win.cc index 3da5f132da5..fffcddd99fc 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_state_impl_win.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_state_impl_win.cc @@ -85,7 +85,7 @@ void OnWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { if (message == WM_SETTINGCHANGE && wparam == SPI_SETCLIENTAREAANIMATION) { gfx::Animation::UpdatePrefersReducedMotion(); for (WebContentsImpl* wc : WebContentsImpl::GetAllWebContents()) { - wc->GetRenderViewHost()->OnWebkitPreferencesChanged(); + wc->OnWebPreferencesChanged(); } } } diff --git a/chromium/content/browser/accessibility/browser_accessibility_unittest.cc b/chromium/content/browser/accessibility/browser_accessibility_unittest.cc index ed559c391b0..c4800985bb7 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_unittest.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_unittest.cc @@ -289,7 +289,7 @@ TEST_F(BrowserAccessibilityTest, GetInnerTextRangeBoundsRect) { inline_text1.role = ax::mojom::Role::kInlineTextBox; inline_text1.SetName("Hello, "); inline_text1.relative_bounds.bounds = gfx::RectF(100, 100, 29, 9); - inline_text1.SetTextDirection(ax::mojom::TextDirection::kLtr); + inline_text1.SetTextDirection(ax::mojom::WritingDirection::kLtr); std::vector<int32_t> character_offsets1; character_offsets1.push_back(6); character_offsets1.push_back(11); @@ -307,7 +307,7 @@ TEST_F(BrowserAccessibilityTest, GetInnerTextRangeBoundsRect) { inline_text2.role = ax::mojom::Role::kInlineTextBox; inline_text2.SetName("world."); inline_text2.relative_bounds.bounds = gfx::RectF(100, 109, 28, 9); - inline_text2.SetTextDirection(ax::mojom::TextDirection::kLtr); + inline_text2.SetTextDirection(ax::mojom::WritingDirection::kLtr); std::vector<int32_t> character_offsets2; character_offsets2.push_back(5); character_offsets2.push_back(10); @@ -410,7 +410,7 @@ TEST_F(BrowserAccessibilityTest, GetInnerTextRangeBoundsRectMultiElement) { inline_text1.role = ax::mojom::Role::kInlineTextBox; inline_text1.SetName("ABC"); inline_text1.relative_bounds.bounds = gfx::RectF(0, 20, 33, 9); - inline_text1.SetTextDirection(ax::mojom::TextDirection::kLtr); + inline_text1.SetTextDirection(ax::mojom::WritingDirection::kLtr); std::vector<int32_t> character_offsets{10, 21, 33}; inline_text1.AddIntListAttribute( ax::mojom::IntListAttribute::kCharacterOffsets, character_offsets); @@ -428,7 +428,7 @@ TEST_F(BrowserAccessibilityTest, GetInnerTextRangeBoundsRectMultiElement) { inline_text2.role = ax::mojom::Role::kInlineTextBox; inline_text2.SetName("ABC"); inline_text2.relative_bounds.bounds = gfx::RectF(10, 40, 33, 9); - inline_text2.SetTextDirection(ax::mojom::TextDirection::kLtr); + inline_text2.SetTextDirection(ax::mojom::WritingDirection::kLtr); inline_text2.AddIntListAttribute( ax::mojom::IntListAttribute::kCharacterOffsets, character_offsets); static_text2.child_ids.push_back(5); @@ -537,7 +537,7 @@ TEST_F(BrowserAccessibilityTest, GetInnerTextRangeBoundsRectBiDi) { inline_text1.role = ax::mojom::Role::kInlineTextBox; inline_text1.SetName("123"); inline_text1.relative_bounds.bounds = gfx::RectF(100, 100, 30, 20); - inline_text1.SetTextDirection(ax::mojom::TextDirection::kLtr); + inline_text1.SetTextDirection(ax::mojom::WritingDirection::kLtr); std::vector<int32_t> character_offsets1; character_offsets1.push_back(10); // 0 character_offsets1.push_back(20); // 1 @@ -551,7 +551,7 @@ TEST_F(BrowserAccessibilityTest, GetInnerTextRangeBoundsRectBiDi) { inline_text2.role = ax::mojom::Role::kInlineTextBox; inline_text2.SetName("abc"); inline_text2.relative_bounds.bounds = gfx::RectF(130, 100, 30, 20); - inline_text2.SetTextDirection(ax::mojom::TextDirection::kRtl); + inline_text2.SetTextDirection(ax::mojom::WritingDirection::kRtl); std::vector<int32_t> character_offsets2; character_offsets2.push_back(10); character_offsets2.push_back(20); @@ -637,7 +637,7 @@ TEST_F(BrowserAccessibilityTest, GetInnerTextRangeBoundsRectScrolledWindow) { inline_text.role = ax::mojom::Role::kInlineTextBox; inline_text.SetName("ABC"); inline_text.relative_bounds.bounds = gfx::RectF(100, 100, 16, 9); - inline_text.SetTextDirection(ax::mojom::TextDirection::kLtr); + inline_text.SetTextDirection(ax::mojom::WritingDirection::kLtr); std::vector<int32_t> character_offsets1; character_offsets1.push_back(6); // 0 character_offsets1.push_back(11); // 1 diff --git a/chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc b/chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc index ca9831ee1e2..3e914e2c530 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc @@ -14,6 +14,7 @@ #include <memory> #include <utility> +#include "base/command_line.h" #include "base/macros.h" #include "base/strings/utf_string_conversions.h" #include "base/test/task_environment.h" @@ -24,10 +25,10 @@ #include "content/browser/accessibility/browser_accessibility_state_impl.h" #include "content/browser/accessibility/test_browser_accessibility_delegate.h" #include "content/browser/renderer_host/legacy_render_widget_host_win.h" -#include "content/common/ax_content_node_data.h" #include "content/public/test/browser_task_environment.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/accessibility/accessibility_switches.h" +#include "ui/accessibility/ax_node_data.h" #include "ui/accessibility/platform/ax_platform_node_win.h" #include "ui/base/win/atl_module.h" @@ -214,11 +215,11 @@ TEST_F(BrowserAccessibilityWinTest, TestChildrenChange) { Microsoft::WRL::ComPtr<IDispatch> text_dispatch; HRESULT hr = ToBrowserAccessibilityWin(manager->GetRoot()) ->GetCOM() - ->get_accChild(one, text_dispatch.GetAddressOf()); + ->get_accChild(one, &text_dispatch); ASSERT_EQ(S_OK, hr); Microsoft::WRL::ComPtr<IAccessible> text_accessible; - hr = text_dispatch.CopyTo(text_accessible.GetAddressOf()); + hr = text_dispatch.As(&text_accessible); ASSERT_EQ(S_OK, hr); base::win::ScopedVariant childid_self(CHILDID_SELF); @@ -232,7 +233,7 @@ TEST_F(BrowserAccessibilityWinTest, TestChildrenChange) { text_accessible.Reset(); // Notify the BrowserAccessibilityManager that the text child has changed. - AXContentNodeData text2; + ui::AXNodeData text2; text2.id = 2; text2.role = ax::mojom::Role::kStaticText; text2.SetName("new text"); @@ -245,10 +246,10 @@ TEST_F(BrowserAccessibilityWinTest, TestChildrenChange) { // as its value. hr = ToBrowserAccessibilityWin(manager->GetRoot()) ->GetCOM() - ->get_accChild(one, text_dispatch.GetAddressOf()); + ->get_accChild(one, &text_dispatch); ASSERT_EQ(S_OK, hr); - hr = text_dispatch.CopyTo(text_accessible.GetAddressOf()); + hr = text_dispatch.As(&text_accessible); ASSERT_EQ(S_OK, hr); hr = text_accessible->get_accName(childid_self, name.Receive()); @@ -641,13 +642,11 @@ TEST_F(BrowserAccessibilityWinTest, TestSimpleHypertext) { EXPECT_EQ(0, hyperlink_count); Microsoft::WRL::ComPtr<IAccessibleHyperlink> hyperlink; + EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(-1, &hyperlink)); + EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(0, &hyperlink)); + EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(text_name_len, &hyperlink)); EXPECT_EQ(E_INVALIDARG, - root_obj->get_hyperlink(-1, hyperlink.GetAddressOf())); - EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(0, hyperlink.GetAddressOf())); - EXPECT_EQ(E_INVALIDARG, - root_obj->get_hyperlink(text_name_len, hyperlink.GetAddressOf())); - EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(text_name_len + 1, - hyperlink.GetAddressOf())); + root_obj->get_hyperlink(text_name_len + 1, &hyperlink)); LONG hyperlink_index; EXPECT_EQ(S_FALSE, root_obj->get_hyperlinkIndex(0, &hyperlink_index)); @@ -762,14 +761,13 @@ TEST_F(BrowserAccessibilityWinTest, TestComplexHypertext) { Microsoft::WRL::ComPtr<IAccessibleHyperlink> hyperlink; Microsoft::WRL::ComPtr<IAccessibleText> hypertext; - EXPECT_EQ(E_INVALIDARG, - root_obj->get_hyperlink(-1, hyperlink.GetAddressOf())); - EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(4, hyperlink.GetAddressOf())); + EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(-1, &hyperlink)); + EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(4, &hyperlink)); // Get the text of the combo box. // It should be its value. - EXPECT_EQ(S_OK, root_obj->get_hyperlink(0, hyperlink.GetAddressOf())); - EXPECT_EQ(S_OK, hyperlink.CopyTo(hypertext.GetAddressOf())); + EXPECT_EQ(S_OK, root_obj->get_hyperlink(0, &hyperlink)); + EXPECT_EQ(S_OK, hyperlink.As(&hypertext)); EXPECT_EQ(S_OK, hypertext->get_text(0, IA2_TEXT_OFFSET_LENGTH, text.Receive())); EXPECT_STREQ(combo_box_value.c_str(), text.Get()); @@ -779,8 +777,8 @@ TEST_F(BrowserAccessibilityWinTest, TestComplexHypertext) { // Get the text of the check box. // It should be its name. - EXPECT_EQ(S_OK, root_obj->get_hyperlink(1, hyperlink.GetAddressOf())); - EXPECT_EQ(S_OK, hyperlink.CopyTo(hypertext.GetAddressOf())); + EXPECT_EQ(S_OK, root_obj->get_hyperlink(1, &hyperlink)); + EXPECT_EQ(S_OK, hyperlink.As(&hypertext)); EXPECT_EQ(S_OK, hypertext->get_text(0, IA2_TEXT_OFFSET_LENGTH, text.Receive())); EXPECT_STREQ(check_box_name.c_str(), text.Get()); @@ -789,8 +787,8 @@ TEST_F(BrowserAccessibilityWinTest, TestComplexHypertext) { hypertext.Reset(); // Get the text of the button. - EXPECT_EQ(S_OK, root_obj->get_hyperlink(2, hyperlink.GetAddressOf())); - EXPECT_EQ(S_OK, hyperlink.CopyTo(hypertext.GetAddressOf())); + EXPECT_EQ(S_OK, root_obj->get_hyperlink(2, &hyperlink)); + EXPECT_EQ(S_OK, hyperlink.As(&hypertext)); EXPECT_EQ(S_OK, hypertext->get_text(0, IA2_TEXT_OFFSET_LENGTH, text.Receive())); EXPECT_STREQ(button_text_name.c_str(), text.Get()); @@ -799,8 +797,8 @@ TEST_F(BrowserAccessibilityWinTest, TestComplexHypertext) { hypertext.Reset(); // Get the text of the link. - EXPECT_EQ(S_OK, root_obj->get_hyperlink(3, hyperlink.GetAddressOf())); - EXPECT_EQ(S_OK, hyperlink.CopyTo(hypertext.GetAddressOf())); + EXPECT_EQ(S_OK, root_obj->get_hyperlink(3, &hyperlink)); + EXPECT_EQ(S_OK, hyperlink.As(&hypertext)); EXPECT_EQ(S_OK, hypertext->get_text(0, 4, text.Receive())); EXPECT_STREQ(link_text_name.c_str(), text.Get()); text.Reset(); @@ -1385,12 +1383,10 @@ TEST_F(BrowserAccessibilityWinTest, TestWordBoundariesInTextControls) { Microsoft::WRL::ComPtr<IAccessibleText> textarea_object; EXPECT_HRESULT_SUCCEEDED(textarea_accessible->GetCOM()->QueryInterface( - IID_IAccessibleText, - reinterpret_cast<void**>(textarea_object.GetAddressOf()))); + IID_PPV_ARGS(&textarea_object))); Microsoft::WRL::ComPtr<IAccessibleText> text_field_object; EXPECT_HRESULT_SUCCEEDED(text_field_accessible->GetCOM()->QueryInterface( - IID_IAccessibleText, - reinterpret_cast<void**>(text_field_object.GetAddressOf()))); + IID_PPV_ARGS(&text_field_object))); LONG offset = 0; while (offset < static_cast<LONG>(text.length())) { @@ -3046,26 +3042,26 @@ TEST_F(BrowserAccessibilityWinTest, UniqueIdWinInvalidAfterDeletingTree) { base::win::ScopedVariant old_root_variant(-root_unique_id); Microsoft::WRL::ComPtr<IDispatch> old_root_dispatch; HRESULT hr = ToBrowserAccessibilityWin(root)->GetCOM()->get_accChild( - old_root_variant, old_root_dispatch.GetAddressOf()); + old_root_variant, &old_root_dispatch); EXPECT_EQ(E_INVALIDARG, hr); base::win::ScopedVariant old_child_variant(-child_unique_id); Microsoft::WRL::ComPtr<IDispatch> old_child_dispatch; hr = ToBrowserAccessibilityWin(root)->GetCOM()->get_accChild( - old_child_variant, old_child_dispatch.GetAddressOf()); + old_child_variant, &old_child_dispatch); EXPECT_EQ(E_INVALIDARG, hr); // Trying to access the unique IDs of the new objects should succeed. base::win::ScopedVariant new_root_variant(-root_unique_id_2); Microsoft::WRL::ComPtr<IDispatch> new_root_dispatch; hr = ToBrowserAccessibilityWin(root)->GetCOM()->get_accChild( - new_root_variant, new_root_dispatch.GetAddressOf()); + new_root_variant, &new_root_dispatch); EXPECT_EQ(S_OK, hr); base::win::ScopedVariant new_child_variant(-child_unique_id_2); Microsoft::WRL::ComPtr<IDispatch> new_child_dispatch; hr = ToBrowserAccessibilityWin(root)->GetCOM()->get_accChild( - new_child_variant, new_child_dispatch.GetAddressOf()); + new_child_variant, &new_child_dispatch); EXPECT_EQ(S_OK, hr); } @@ -3090,11 +3086,11 @@ TEST_F(BrowserAccessibilityWinTest, AccChildOnlyReturnsDescendants) { Microsoft::WRL::ComPtr<IDispatch> result; EXPECT_EQ(E_INVALIDARG, ToBrowserAccessibilityWin(child)->GetCOM()->get_accChild( - root_unique_id_variant, result.GetAddressOf())); + root_unique_id_variant, &result)); base::win::ScopedVariant child_unique_id_variant(-GetUniqueId(child)); EXPECT_EQ(S_OK, ToBrowserAccessibilityWin(root)->GetCOM()->get_accChild( - child_unique_id_variant, result.GetAddressOf())); + child_unique_id_variant, &result)); } // TODO(crbug.com/929563): Disabled due to flakiness. @@ -3145,7 +3141,7 @@ TEST_F(BrowserAccessibilityWinTest, DISABLED_TestIAccessible2Relations) { EXPECT_EQ(1, n_relations); EXPECT_HRESULT_SUCCEEDED( - ax_root->GetCOM()->get_relation(0, describedby_relation.GetAddressOf())); + ax_root->GetCOM()->get_relation(0, &describedby_relation)); EXPECT_HRESULT_SUCCEEDED( describedby_relation->get_relationType(relation_type.Receive())); EXPECT_EQ(L"describedBy", base::string16(relation_type.Get())); @@ -3154,17 +3150,15 @@ TEST_F(BrowserAccessibilityWinTest, DISABLED_TestIAccessible2Relations) { EXPECT_HRESULT_SUCCEEDED(describedby_relation->get_nTargets(&n_targets)); EXPECT_EQ(2, n_targets); - EXPECT_HRESULT_SUCCEEDED( - describedby_relation->get_target(0, target.GetAddressOf())); - target.CopyTo(ax_target.GetAddressOf()); + EXPECT_HRESULT_SUCCEEDED(describedby_relation->get_target(0, &target)); + target.As(&ax_target); EXPECT_HRESULT_SUCCEEDED(ax_target->get_uniqueID(&unique_id)); EXPECT_EQ(-GetUniqueId(ax_child1), unique_id); ax_target.Reset(); target.Reset(); - EXPECT_HRESULT_SUCCEEDED( - describedby_relation->get_target(1, target.GetAddressOf())); - target.CopyTo(ax_target.GetAddressOf()); + EXPECT_HRESULT_SUCCEEDED(describedby_relation->get_target(1, &target)); + target.As(&ax_target); EXPECT_HRESULT_SUCCEEDED(ax_target->get_uniqueID(&unique_id)); EXPECT_EQ(-GetUniqueId(ax_child2), unique_id); ax_target.Reset(); @@ -3175,8 +3169,8 @@ TEST_F(BrowserAccessibilityWinTest, DISABLED_TestIAccessible2Relations) { EXPECT_HRESULT_SUCCEEDED(ax_child1->GetCOM()->get_nRelations(&n_relations)); EXPECT_EQ(1, n_relations); - EXPECT_HRESULT_SUCCEEDED(ax_child1->GetCOM()->get_relation( - 0, description_for_relation.GetAddressOf())); + EXPECT_HRESULT_SUCCEEDED( + ax_child1->GetCOM()->get_relation(0, &description_for_relation)); EXPECT_HRESULT_SUCCEEDED( description_for_relation->get_relationType(relation_type.Receive())); EXPECT_EQ(L"descriptionFor", base::string16(relation_type.Get())); @@ -3185,9 +3179,8 @@ TEST_F(BrowserAccessibilityWinTest, DISABLED_TestIAccessible2Relations) { EXPECT_HRESULT_SUCCEEDED(description_for_relation->get_nTargets(&n_targets)); EXPECT_EQ(1, n_targets); - EXPECT_HRESULT_SUCCEEDED( - description_for_relation->get_target(0, target.GetAddressOf())); - target.CopyTo(ax_target.GetAddressOf()); + EXPECT_HRESULT_SUCCEEDED(description_for_relation->get_target(0, &target)); + target.As(&ax_target); EXPECT_HRESULT_SUCCEEDED(ax_target->get_uniqueID(&unique_id)); EXPECT_EQ(-GetUniqueId(ax_root), unique_id); ax_target.Reset(); @@ -3197,8 +3190,8 @@ TEST_F(BrowserAccessibilityWinTest, DISABLED_TestIAccessible2Relations) { EXPECT_HRESULT_SUCCEEDED(ax_child2->GetCOM()->get_nRelations(&n_relations)); EXPECT_EQ(1, n_relations); - EXPECT_HRESULT_SUCCEEDED(ax_child2->GetCOM()->get_relation( - 0, description_for_relation.GetAddressOf())); + EXPECT_HRESULT_SUCCEEDED( + ax_child2->GetCOM()->get_relation(0, &description_for_relation)); EXPECT_HRESULT_SUCCEEDED( description_for_relation->get_relationType(relation_type.Receive())); EXPECT_EQ(L"descriptionFor", base::string16(relation_type.Get())); @@ -3207,9 +3200,8 @@ TEST_F(BrowserAccessibilityWinTest, DISABLED_TestIAccessible2Relations) { EXPECT_HRESULT_SUCCEEDED(description_for_relation->get_nTargets(&n_targets)); EXPECT_EQ(1, n_targets); - EXPECT_HRESULT_SUCCEEDED( - description_for_relation->get_target(0, target.GetAddressOf())); - target.CopyTo(ax_target.GetAddressOf()); + EXPECT_HRESULT_SUCCEEDED(description_for_relation->get_target(0, &target)); + target.As(&ax_target); EXPECT_HRESULT_SUCCEEDED(ax_target->get_uniqueID(&unique_id)); EXPECT_EQ(-GetUniqueId(ax_root), unique_id); ax_target.Reset(); diff --git a/chromium/content/browser/accessibility/captioning_controller.cc b/chromium/content/browser/accessibility/captioning_controller.cc index cc5d6b72d50..f9a5f7ea1f4 100644 --- a/chromium/content/browser/accessibility/captioning_controller.cc +++ b/chromium/content/browser/accessibility/captioning_controller.cc @@ -89,10 +89,7 @@ void CaptioningController::SetTextTrackSettings( const JavaParamRef<jstring>& textTrackTextColor, const JavaParamRef<jstring>& textTrackTextShadow, const JavaParamRef<jstring>& textTrackTextSize) { - auto web_prefs = web_contents() - ->GetMainFrame() - ->GetRenderViewHost() - ->GetWebkitPreferences(); + auto web_prefs = web_contents()->GetOrCreateWebPreferences(); web_prefs.text_tracks_enabled = textTracksEnabled; web_prefs.text_track_background_color = AddCSSImportant(ConvertJavaStringToUTF8(env, textTrackBackgroundColor)); @@ -108,8 +105,7 @@ void CaptioningController::SetTextTrackSettings( AddCSSImportant(ConvertJavaStringToUTF8(env, textTrackTextShadow)); web_prefs.text_track_text_size = AddCSSImportant(ConvertJavaStringToUTF8(env, textTrackTextSize)); - web_contents()->GetMainFrame()->GetRenderViewHost()->UpdateWebkitPreferences( - web_prefs); + web_contents()->SetWebPreferences(web_prefs); } jlong JNI_CaptioningController_Init( diff --git a/chromium/content/browser/accessibility/cross_platform_accessibility_browsertest.cc b/chromium/content/browser/accessibility/cross_platform_accessibility_browsertest.cc index 9754f150103..34f0a76f92b 100644 --- a/chromium/content/browser/accessibility/cross_platform_accessibility_browsertest.cc +++ b/chromium/content/browser/accessibility/cross_platform_accessibility_browsertest.cc @@ -13,6 +13,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/test/scoped_feature_list.h" #include "build/build_config.h" +#include "build/chromecast_buildflags.h" #include "content/browser/accessibility/browser_accessibility.h" #include "content/browser/accessibility/browser_accessibility_manager.h" #include "content/browser/renderer_host/render_view_host_impl.h" @@ -27,6 +28,7 @@ #include "content/public/test/content_browser_test_utils.h" #include "content/shell/browser/shell.h" #include "third_party/blink/public/common/features.h" +#include "ui/accessibility/accessibility_switches.h" #include "ui/accessibility/ax_node.h" #include "ui/accessibility/ax_tree.h" @@ -66,6 +68,14 @@ class CrossPlatformAccessibilityBrowserTest : public ContentBrowserTest { void SetUpOnMainThread() override; void TearDownOnMainThread() override; + void SetUpCommandLine(base::CommandLine* command_line) override { + ContentBrowserTest::SetUpCommandLine(command_line); + // kDisableAXMenuList is true on Chrome OS by default. Make it consistent + // for these cross-platform tests. + base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( + switches::kDisableAXMenuList, "false"); + } + protected: void LoadInitialAccessibilityTreeFromUrl( const GURL& url, @@ -93,6 +103,36 @@ class CrossPlatformAccessibilityBrowserTest : public ContentBrowserTest { return web_contents->GetRootBrowserAccessibilityManager(); } + BrowserAccessibility* FindNode(const std::string& name_or_value) { + return FindNodeInSubtree(*GetManager()->GetRoot(), name_or_value); + } + + BrowserAccessibility* FindNodeInSubtree(BrowserAccessibility& node, + const std::string& name_or_value) { + const std::string& name = + node.GetStringAttribute(ax::mojom::StringAttribute::kName); + // Note that in the case of a text field, "BrowserAccessibility::GetValue" + // has the added functionality of computing the value of an ARIA text box + // from its inner text. + // + // <div contenteditable="true" role="textbox">Hello world.</div> + // Will expose no HTML value attribute, but some screen readers, such as + // Jaws, VoiceOver and Talkback, require one to be computed. + const std::string& value = base::UTF16ToUTF8(node.GetValue()); + if ((name == name_or_value || value == name_or_value)) { + return &node; + } + + for (unsigned int i = 0; i < node.PlatformChildCount(); ++i) { + BrowserAccessibility* result = + FindNodeInSubtree(*node.PlatformGetChild(i), name_or_value); + if (result) + return result; + } + + return nullptr; + } + std::string GetAttr(const ui::AXNode* node, const ax::mojom::StringAttribute attr); int GetIntAttr(const ui::AXNode* node, const ax::mojom::IntAttribute attr); @@ -427,6 +467,103 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest, GetAttr(button3->node(), ax::mojom::StringAttribute::kName).c_str()); } +// Android's text representation is different, so disable the test there. +#if !defined(OS_ANDROID) +IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest, + AXNodePositionTreeBoundary) { + AccessibilityNotificationWaiter waiter(shell()->web_contents(), + ui::kAXModeComplete, + ax::mojom::Event::kLoadComplete); + GURL url( + "data:text/html," + "<!doctype html><html><body>" + "Text before iframe" + "<iframe src='data:text/html," + "<!doctype html><html><body>Text in iframe</body></html>" + "'></iframe>" + "Text after iframe" + "</body></html>"); + + EXPECT_TRUE(NavigateToURL(shell(), url)); + waiter.WaitForNotification(); + WaitForAccessibilityTreeToContainNodeWithName(shell()->web_contents(), + "Text in iframe"); + + const BrowserAccessibility* root = GetManager()->GetRoot(); + ASSERT_NE(root, nullptr); + const BrowserAccessibility* body = root->PlatformGetChild(0); + ASSERT_NE(body, nullptr); + const BrowserAccessibility* text_before_iframe = + FindNode("Text before iframe"); + ASSERT_NE(text_before_iframe, nullptr); + const BrowserAccessibility* iframe = body->PlatformGetChild(1); + ASSERT_NE(iframe, nullptr); + const BrowserAccessibility* sub_document = iframe->PlatformGetChild(0); + ASSERT_NE(sub_document, nullptr); + const BrowserAccessibility* sub_body = sub_document->PlatformGetChild(0); + ASSERT_NE(sub_body, nullptr); + + const BrowserAccessibility* text_in_iframe = FindNode("Text in iframe"); + ASSERT_NE(text_in_iframe, nullptr); + const BrowserAccessibility* text_after_iframe = FindNode("Text after iframe"); + ASSERT_NE(text_after_iframe, nullptr); + + // Start at the beginning of the document. Anchor IDs can vary across + // platforms and test runs, so only check text offsets and tree IDs. In this + // case, the tree ID of position should match test_position since a tree + // boundary is not crossed. + ui::AXNodePosition::AXPositionInstance position = + text_before_iframe->CreateTextPositionAt(1); + EXPECT_EQ(position->text_offset(), 1); + EXPECT_FALSE(position->AtStartOfAXTree()); + EXPECT_FALSE(position->AtEndOfAXTree()); + ui::AXNodePosition::AXPositionInstance test_position = + position->CreatePositionAtStartOfAXTree(); + EXPECT_EQ(test_position->tree_id(), position->tree_id()); + EXPECT_EQ(test_position->text_offset(), 0); + EXPECT_TRUE(test_position->AtStartOfAXTree()); + EXPECT_FALSE(test_position->AtEndOfAXTree()); + test_position = position->CreatePositionAtEndOfAXTree(); + EXPECT_EQ(test_position->tree_id(), position->tree_id()); + EXPECT_EQ(test_position->text_offset(), 17); + EXPECT_FALSE(test_position->AtStartOfAXTree()); + EXPECT_TRUE(test_position->AtEndOfAXTree()); + + // Test inside iframe. + position = text_in_iframe->CreateTextPositionAt(3); + EXPECT_EQ(position->text_offset(), 3); + EXPECT_NE(test_position->tree_id(), position->tree_id()); + EXPECT_FALSE(position->AtStartOfAXTree()); + EXPECT_FALSE(position->AtEndOfAXTree()); + test_position = position->CreatePositionAtStartOfAXTree(); + EXPECT_TRUE(test_position->AtStartOfAXTree()); + EXPECT_FALSE(test_position->AtEndOfAXTree()); + EXPECT_EQ(test_position->tree_id(), position->tree_id()); + EXPECT_EQ(test_position->text_offset(), 0); + test_position = position->CreatePositionAtEndOfAXTree(); + EXPECT_EQ(test_position->tree_id(), position->tree_id()); + EXPECT_EQ(test_position->text_offset(), 14); + EXPECT_FALSE(test_position->AtStartOfAXTree()); + EXPECT_TRUE(test_position->AtEndOfAXTree()); + + // Test after iframe. + position = text_after_iframe->CreateTextPositionAt(3); + EXPECT_FALSE(position->AtStartOfAXTree()); + EXPECT_FALSE(position->AtEndOfAXTree()); + EXPECT_NE(test_position->tree_id(), position->tree_id()); + test_position = position->CreatePositionAtStartOfAXTree(); + EXPECT_EQ(test_position->tree_id(), position->tree_id()); + EXPECT_EQ(test_position->text_offset(), 0); + EXPECT_TRUE(test_position->AtStartOfAXTree()); + EXPECT_FALSE(test_position->AtEndOfAXTree()); + test_position = position->CreatePositionAtEndOfAXTree(); + EXPECT_EQ(test_position->tree_id(), position->tree_id()); + EXPECT_EQ(test_position->text_offset(), 17); + EXPECT_FALSE(test_position->AtStartOfAXTree()); + EXPECT_TRUE(test_position->AtEndOfAXTree()); +} +#endif + IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest, PlatformIterator) { AccessibilityNotificationWaiter waiter(shell()->web_contents(), @@ -634,7 +771,7 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest, } // TODO(https://crbug.com/1020456) re-enable when crashing on linux is resolved. -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) #define MAYBE_LocalizedRoleDescription DISABLED_LocalizedRoleDescription #else #define MAYBE_LocalizedRoleDescription LocalizedRoleDescription @@ -826,7 +963,7 @@ IN_PROC_BROWSER_TEST_F( // On Android root scroll offset is handled by the Java layer. The final rect // bounds is device specific. -#ifndef OS_ANDROID +#if !defined(OS_ANDROID) IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest, GetBoundsRectUnclippedRootFrameFromIFrame) { // Start by loading a document with iframes. @@ -935,6 +1072,7 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest, const char url_str[] = R"HTML(data:text/html,<!DOCTYPE html> <html> + <div style="margin-top: 100px;"></div> <input type="datetime-local" aria-label="datetime" aria-controls="button1"> <button id="button1">button</button> @@ -999,6 +1137,13 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest, manager->GetFromID(controls_ids[1]); ASSERT_NE(nullptr, popup_area); EXPECT_EQ(ax::mojom::Role::kRootWebArea, popup_area->GetRole()); + +#if !BUILDFLAG(IS_CHROMECAST) + // Ensure that the bounding box of the popup area is at least 100 + // pixels down the page. + gfx::Rect popup_bounds = popup_area->GetUnclippedRootFrameBoundsRect(); + EXPECT_GT(popup_bounds.y(), 100); +#endif } } @@ -1148,7 +1293,7 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest, root_accessibility_manager->GetFocus()); } } -#endif // ifndef OS_ANDROID +#endif // !defined(OS_ANDROID) class CrossPlatformAccessibilityBrowserTestWithImplicitRootScrolling : public CrossPlatformAccessibilityBrowserTest { diff --git a/chromium/content/browser/accessibility/dump_accessibility_browsertest_base.cc b/chromium/content/browser/accessibility/dump_accessibility_browsertest_base.cc index cd07323d855..0664002302f 100644 --- a/chromium/content/browser/accessibility/dump_accessibility_browsertest_base.cc +++ b/chromium/content/browser/accessibility/dump_accessibility_browsertest_base.cc @@ -145,8 +145,7 @@ base::string16 DumpAccessibilityTestBase::DumpUnfilteredAccessibilityTreeAsString() { std::unique_ptr<AccessibilityTreeFormatter> formatter(formatter_factory_()); std::vector<PropertyFilter> property_filters; - property_filters.push_back( - PropertyFilter(base::ASCIIToUTF16("*"), PropertyFilter::ALLOW)); + property_filters.emplace_back("*", PropertyFilter::ALLOW); formatter->SetPropertyFilters(property_filters); formatter->set_show_ids(true); base::string16 ax_tree_dump; @@ -171,21 +170,16 @@ void DumpAccessibilityTestBase::ParseHtmlForExtraDirectives( const std::string& no_load_expected_str = "@NO-LOAD-EXPECTED:"; const std::string& wait_str = "@WAIT-FOR:"; const std::string& execute_str = "@EXECUTE-AND-WAIT-FOR:"; - const std::string& until_str = "@RUN-UNTIL-EVENT:"; + const std::string& until_str = formatter_->GetRunUntilEventString(); const std::string& default_action_on_str = "@DEFAULT-ACTION-ON:"; if (base::StartsWith(line, allow_empty_str, base::CompareCase::SENSITIVE)) { - property_filters_.push_back( - PropertyFilter(base::UTF8ToUTF16(line.substr(allow_empty_str.size())), - PropertyFilter::ALLOW_EMPTY)); + property_filters_.emplace_back( + line.substr(allow_empty_str.size()), PropertyFilter::ALLOW_EMPTY); } else if (base::StartsWith(line, allow_str, base::CompareCase::SENSITIVE)) { - property_filters_.push_back( - PropertyFilter(base::UTF8ToUTF16(line.substr(allow_str.size())), - PropertyFilter::ALLOW)); + property_filters_.emplace_back(line.substr(allow_str.size()), PropertyFilter::ALLOW); } else if (base::StartsWith(line, deny_str, base::CompareCase::SENSITIVE)) { - property_filters_.push_back( - PropertyFilter(base::UTF8ToUTF16(line.substr(deny_str.size())), - PropertyFilter::DENY)); + property_filters_.emplace_back(line.substr(deny_str.size()), PropertyFilter::DENY); } else if (base::StartsWith(line, deny_node_str, base::CompareCase::SENSITIVE)) { const auto& node_filter = line.substr(deny_node_str.size()); diff --git a/chromium/content/browser/accessibility/dump_accessibility_events_browsertest.cc b/chromium/content/browser/accessibility/dump_accessibility_events_browsertest.cc index 25a997f2935..d3af2983315 100644 --- a/chromium/content/browser/accessibility/dump_accessibility_events_browsertest.cc +++ b/chromium/content/browser/accessibility/dump_accessibility_events_browsertest.cc @@ -20,6 +20,7 @@ #include "content/browser/accessibility/accessibility_event_recorder.h" #include "content/browser/accessibility/browser_accessibility.h" #include "content/browser/accessibility/browser_accessibility_manager.h" +#include "content/browser/accessibility/browser_accessibility_state_impl.h" #include "content/browser/accessibility/dump_accessibility_browsertest_base.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/accessibility_tree_formatter.h" @@ -30,6 +31,7 @@ #include "content/public/test/content_browser_test_utils.h" #include "content/public/test/test_utils.h" #include "content/shell/browser/shell.h" +#include "third_party/blink/public/mojom/renderer_preferences.mojom.h" namespace content { @@ -73,16 +75,13 @@ class DumpAccessibilityEventsTest : public DumpAccessibilityTestBase { std::vector<PropertyFilter>* property_filters) override { // Suppress spurious focus events on the document object. property_filters->push_back( - PropertyFilter(base::ASCIIToUTF16("EVENT_OBJECT_FOCUS*DOCUMENT*"), - PropertyFilter::DENY)); - property_filters->push_back( - PropertyFilter(base::ASCIIToUTF16("AutomationFocusChanged*document*"), - PropertyFilter::DENY)); + PropertyFilter("EVENT_OBJECT_FOCUS*DOCUMENT*", PropertyFilter::DENY)); + property_filters->push_back(PropertyFilter( + "AutomationFocusChanged*document*", PropertyFilter::DENY)); // Implementing IRawElementProviderAdviseEvents causes Win7 to fire // spurious focus events (regardless of what the implementation does). property_filters->push_back(PropertyFilter( - base::ASCIIToUTF16("AutomationFocusChanged on role=region"), - PropertyFilter::DENY)); + "AutomationFocusChanged on role=region", PropertyFilter::DENY)); } std::vector<std::string> Dump(std::vector<std::string>& run_until) override; @@ -91,13 +90,18 @@ class DumpAccessibilityEventsTest : public DumpAccessibilityTestBase { void RunEventTest(const base::FilePath::CharType* file_path); private: + void OnEventRecorded(AccessibilityNotificationWaiter* waiter, + const std::string& event) { + waiter->Quit(); + } + base::string16 initial_tree_; base::string16 final_tree_; }; bool IsRecordingComplete(AccessibilityEventRecorder& event_recorder, std::vector<std::string>& run_until) { - // If no @RUN-UNTIL-EVENT directives, then having any events is enough. + // If no @*-RUN-UNTIL-EVENT directives, then having any events is enough. LOG(ERROR) << "=== IsRecordingComplete#1 run_until size=" << run_until.size(); if (run_until.empty()) return true; @@ -143,16 +147,27 @@ std::vector<std::string> DumpAccessibilityEventsTest::Dump( waiter.reset(new AccessibilityNotificationWaiter( shell()->web_contents(), ui::kAXModeComplete, ax::mojom::Event::kNone)); + // It's possible for platform events to be received after all blink or + // generated events have been fired. Unblock the |waiter| when this happens. + event_recorder->ListenToEvents( + base::BindRepeating(&DumpAccessibilityEventsTest::OnEventRecorded, + base::Unretained(this), waiter.get())); + base::Value go_results = ExecuteScriptAndGetValue(web_contents->GetMainFrame(), "go()"); run_go_again = go_results.is_bool() && go_results.GetBool(); for (;;) { - waiter->WaitForNotification(); // Run at least once. + // Wait for at least one event. This may unblock either when |waiter| + // observes either an ax::mojom::Event or ui::AXEventGenerator::Event, or + // when |event_recorder| records a platform event. + waiter->WaitForNotification(); if (IsRecordingComplete(*event_recorder, run_until)) break; } + event_recorder->StopListeningToEvents(); + // More than one accessibility event could have been generated. // To make sure we've received all accessibility events, add a // sentinel by calling SignalEndOfTest and waiting for a kEndOfTest @@ -181,7 +196,7 @@ std::vector<std::string> DumpAccessibilityEventsTest::Dump( for (size_t i = 0; i < event_logs.size(); ++i) { if (AccessibilityTreeFormatter::MatchesPropertyFilters( - property_filters_, base::UTF8ToUTF16(event_logs[i]), true)) { + property_filters_, event_logs[i], true)) { result.push_back(event_logs[i]); } } @@ -470,6 +485,11 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, RunEventTest(FILE_PATH_LITERAL("checked-state-changed.html")); } +IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, + AccessibilityEventsCheckedMixedChanged) { + RunEventTest(FILE_PATH_LITERAL("checked-mixed-changed.html")); +} + // http:/crbug.com/889013 IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, DISABLED_AccessibilityEventsCaretHide) { @@ -497,9 +517,12 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, #endif IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, MAYBE_AccessibilityEventsCaretBrowsingEnabled) { - // Add command line switch that forces caret browsing on. - base::CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kEnableCaretBrowsing); + // This actually enables caret browsing without setting the pref. + shell()->web_contents()->GetMutableRendererPrefs()->caret_browsing_enabled = + true; + // This notifies accessibility that caret browsing is on so that it sends + // accessibility events when the caret moves. + BrowserAccessibilityStateImpl::GetInstance()->SetCaretBrowsingState(true); RunEventTest(FILE_PATH_LITERAL("caret-browsing-enabled.html")); } @@ -519,6 +542,22 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, } IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, + AccessibilityEventsAriaHiddenDescendants) { + RunEventTest(FILE_PATH_LITERAL("aria-hidden-descendants.html")); +} + +IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, + AccessibilityEventsAriaHiddenDescendantsAlreadyIgnored) { + RunEventTest( + FILE_PATH_LITERAL("aria-hidden-descendants-already-ignored.html")); +} + +IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, + AccessibilityEventsCSSDisplayDescendants) { + RunEventTest(FILE_PATH_LITERAL("css-display-descendants.html")); +} + +IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, AccessibilityEventsCSSFlexTextUpdate) { RunEventTest(FILE_PATH_LITERAL("css-flex-text-update.html")); } @@ -529,6 +568,11 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, } IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, + AccessibilityEventsCSSVisibilityDescendants) { + RunEventTest(FILE_PATH_LITERAL("css-visibility-descendants.html")); +} + +IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, AccessibilityEventsCSSCollapse) { RunEventTest(FILE_PATH_LITERAL("css-visibility-collapse.html")); } @@ -731,7 +775,7 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, } // TODO(aboxhall): Fix flakiness on Windows and Mac -#if defined(OS_WIN) || defined(OS_MACOSX) +#if defined(OS_WIN) || defined(OS_MAC) #define MAYBE_AccessibilityEventsReportValidityInvalidField \ DISABLED_AccessibilityEventsReportValidityInvalidField #else @@ -760,6 +804,11 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, } IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, + AccessibilityEventsStyleChanged) { + RunEventTest(FILE_PATH_LITERAL("style-changed.html")); +} + +IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, AccessibilityEventsTabindexAddedOnPlainDiv) { RunEventTest(FILE_PATH_LITERAL("tabindex-added-on-plain-div.html")); } @@ -786,6 +835,11 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, } IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, + AccessibilityEventsTextAlignChanged) { + RunEventTest(FILE_PATH_LITERAL("text-align-changed.html")); +} + +IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, AccessibilityEventsTextChanged) { RunEventTest(FILE_PATH_LITERAL("text-changed.html")); } @@ -801,6 +855,16 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, } IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, + AccessibilityEventsTextSelectionInsideHiddenElement) { + RunEventTest(FILE_PATH_LITERAL("text-selection-inside-hidden-element.html")); +} + +IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, + AccessibilityEventsTextSelectionInsideVideo) { + RunEventTest(FILE_PATH_LITERAL("text-selection-inside-video.html")); +} + +IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, AccessibilityEventsAriaCheckedChanged) { RunEventTest(FILE_PATH_LITERAL("aria-checked-changed.html")); } @@ -902,7 +966,7 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest, } // Test is flaky on Linux. See crbug.com/990847 for more details. -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) #define MAYBE_DeleteSubtree DISABLED_DeleteSubtree #else #define MAYBE_DeleteSubtree DeleteSubtree diff --git a/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc index 86b7113203c..ca807ad5ae7 100644 --- a/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc +++ b/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc @@ -30,7 +30,7 @@ #include "ui/accessibility/accessibility_features.h" #include "ui/accessibility/accessibility_switches.h" -#if defined(OS_MACOSX) +#if defined(OS_MAC) #include "base/mac/mac_util.h" #endif @@ -62,10 +62,9 @@ class DumpAccessibilityTreeTest : public DumpAccessibilityTestBase { void AddDefaultFilters( std::vector<PropertyFilter>* property_filters) override; void AddPropertyFilter(std::vector<PropertyFilter>* property_filters, - std::string filter, + const std::string& filter, PropertyFilter::Type type = PropertyFilter::ALLOW) { - property_filters->push_back( - PropertyFilter(base::ASCIIToUTF16(filter), type)); + property_filters->push_back(PropertyFilter(filter, type)); } void SetUpCommandLine(base::CommandLine* command_line) override { @@ -79,6 +78,10 @@ class DumpAccessibilityTreeTest : public DumpAccessibilityTestBase { // Enable display locking, used in some tests. base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( switches::kEnableBlinkFeatures, "CSSContentVisibilityHiddenMatchable"); + // kDisableAXMenuList is true on Chrome OS by default. Make it consistent + // for these cross-platform tests. + base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( + switches::kDisableAXMenuList, "false"); } void RunAriaTest(const base::FilePath::CharType* file_path) { @@ -420,6 +423,11 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, RunHtmlTest(FILE_PATH_LITERAL("combobox-optgroup.html")); } +IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, + AccessibilitySvgStyleElement) { + RunHtmlTest(FILE_PATH_LITERAL("svg-style-element.html")); +} + IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityAomBusy) { RunAomTest(FILE_PATH_LITERAL("aom-busy.html")); } @@ -721,7 +729,14 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, RunAriaTest(FILE_PATH_LITERAL("aria-grid-extra-wrap-elems.html")); } -IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityAriaGridCell) { +// https://crbug.com/1117594 +#if defined(OS_ANDROID) +#define MAYBE_AccessibilityAriaGridCell DISABLED_AccessibilityAriaGridCell +#else +#define MAYBE_AccessibilityAriaGridCell AccessibilityAriaGridCell +#endif +IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, + MAYBE_AccessibilityAriaGridCell) { RunAriaTest(FILE_PATH_LITERAL("aria-gridcell.html")); } @@ -786,6 +801,10 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityAriaListBox) { RunAriaTest(FILE_PATH_LITERAL("aria-listbox.html")); } +IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, + AccessibilityAriaListBoxDisabled) { + RunAriaTest(FILE_PATH_LITERAL("aria-listbox-disabled.html")); +} // TODO(crbug.com/983802): Flaky. IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, DISABLED_AccessibilityAriaListBoxActiveDescendant) { @@ -843,6 +862,16 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityAriaMenuItem) { RunAriaTest(FILE_PATH_LITERAL("aria-menuitem.html")); } +#if defined(OS_ANDROID) +#define MAYBE_AccessibilityAriaMenuItemInGroup \ + DISABLED_AccessibilityAriaMenuItemInGroup +#else +#define MAYBE_AccessibilityAriaMenuItemInGroup AccessibilityAriaMenuItemInGroup +#endif +IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, + MAYBE_AccessibilityAriaMenuItemInGroup) { + RunAriaTest(FILE_PATH_LITERAL("aria-menuitem-in-group.html")); +} // crbug.com/442278 will stop creating new text elements representing title. // Re-baseline after the Blink change goes in IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, @@ -1387,7 +1416,7 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityGraphicsRoles) { RunAriaTest(FILE_PATH_LITERAL("graphics-roles.html")); } -#if defined(OS_ANDROID) || defined(OS_MACOSX) +#if defined(OS_ANDROID) || defined(OS_MAC) // Flaky failures: http://crbug.com/445929. // Mac failures: http://crbug.com/571712. #define MAYBE_AccessibilityContenteditableDescendants \ @@ -1406,7 +1435,7 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, RunHtmlTest(FILE_PATH_LITERAL("element-class-id-src-attr.html")); } -#if defined(OS_ANDROID) || defined(OS_MACOSX) +#if defined(OS_ANDROID) || defined(OS_MAC) // Flaky failures: http://crbug.com/445929. // Mac failures: http://crbug.com/571712. #define MAYBE_AccessibilityContenteditableDescendantsWithSelection \ @@ -1705,7 +1734,7 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityInputDateTime) { } // Fails on OS X 10.9 and higher <https://crbug.com/430622>. -#if !defined(OS_MACOSX) +#if !defined(OS_MAC) IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityInputDateTimeLocal) { RunHtmlTest(FILE_PATH_LITERAL("input-datetime-local.html")); @@ -1716,7 +1745,14 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityInputEmail) { RunHtmlTest(FILE_PATH_LITERAL("input-email.html")); } -IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityInputFile) { +// http://crbug.com/1114193 +#if defined(OS_ANDROID) +#define MAYBE_AccessibilityInputFile DISABLED_AccessibilityInputFile +#else +#define MAYBE_AccessibilityInputFile AccessibilityInputFile +#endif +IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, + MAYBE_AccessibilityInputFile) { RunHtmlTest(FILE_PATH_LITERAL("input-file.html")); } @@ -1855,7 +1891,7 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, RunHtmlTest(FILE_PATH_LITERAL("input-text-with-selection.html")); } -#if defined(OS_MACOSX) +#if defined(OS_MAC) // TODO(1038813): The /blink test pass is different on Windows and Mac, versus // Linux. #define MAYBE_AccessibilityInputTime DISABLED_AccessibilityInputTime @@ -2098,10 +2134,12 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, RunHtmlTest(FILE_PATH_LITERAL("portal-name-from-text.html")); } +// Flaky on all platforms: crbug.com/1103753. IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, - AccessibilityPortalWithWidgetInside) { + DISABLED_AccessibilityPortalWithWidgetInside) { RunHtmlTest(FILE_PATH_LITERAL("portal-with-widget-inside.html")); } + IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityPortalNameFromVisibleText) { RunHtmlTest(FILE_PATH_LITERAL("portal-name-from-visible-text.html")); @@ -2144,7 +2182,13 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, RunHtmlTest(FILE_PATH_LITERAL("selection-container.html")); } -IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilitySelect) { +// https://crbug.com/1117594 +#if defined(OS_ANDROID) +#define MAYBE_AccessibilitySelect DISABLED_AccessibilitySelect +#else +#define MAYBE_AccessibilitySelect AccessibilitySelect +#endif +IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, MAYBE_AccessibilitySelect) { RunHtmlTest(FILE_PATH_LITERAL("select.html")); } @@ -2159,12 +2203,20 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, FILE_PATH_LITERAL("select-follows-focus-aria-selected-false.html")); } +// https://crbug.com/1117594 +#if defined(OS_ANDROID) +#define MAYBE_AccessibilitySelectFollowsFocusMultiselect \ + DISABLED_AccessibilitySelectFollowsFocusMultiselect +#else +#define MAYBE_AccessibilitySelectFollowsFocusMultiselect \ + AccessibilitySelectFollowsFocusMultiselect +#endif IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, - AccessibilitySelectFollowsFocusMultiselect) { + MAYBE_AccessibilitySelectFollowsFocusMultiselect) { RunHtmlTest(FILE_PATH_LITERAL("select-follows-focus-multiselect.html")); } -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) #define MAYBE_AccessibilitySource DISABLED_AccessibilitySource #else #define MAYBE_AccessibilitySource AccessibilitySource @@ -2205,6 +2257,10 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilitySvg) { RunHtmlTest(FILE_PATH_LITERAL("svg.html")); } +IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilitySvgG) { + RunHtmlTest(FILE_PATH_LITERAL("svg-g.html")); +} + IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityTableSimple) { RunHtmlTest(FILE_PATH_LITERAL("table-simple.html")); } @@ -2257,6 +2313,10 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, RunHtmlTest(FILE_PATH_LITERAL("table-multiple-row-and-column-headers.html")); } +IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityTextAlign) { + RunHtmlTest(FILE_PATH_LITERAL("text-align.html")); +} + IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityTextDecorationStyles) { RunHtmlTest(FILE_PATH_LITERAL("text-decoration-styles.html")); @@ -2288,7 +2348,7 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityTitleChanged) { RunHtmlTest(FILE_PATH_LITERAL("title-changed.html")); } -#if defined(OS_WIN) || defined(OS_MACOSX) +#if defined(OS_WIN) || defined(OS_MAC) // Flaky on Win/Mac: crbug.com/508532 #define MAYBE_AccessibilityTransition DISABLED_AccessibilityTransition #else @@ -2307,6 +2367,11 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityUl) { RunHtmlTest(FILE_PATH_LITERAL("ul.html")); } +IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, + AccessibilityNotUserSelectable) { + RunCSSTest(FILE_PATH_LITERAL("user-select.html")); +} + IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityVar) { RunHtmlTest(FILE_PATH_LITERAL("var.html")); } @@ -2316,6 +2381,10 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, DISABLED_AccessibilityVideo) { RunHtmlTest(FILE_PATH_LITERAL("video.html")); } +IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityVideoTextOnly) { + RunHtmlTest(FILE_PATH_LITERAL("video-text-only.html")); +} + IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityNodeChangedCrashInEditableText) { RunHtmlTest(FILE_PATH_LITERAL("node-changed-crash-in-editable-text.html")); @@ -2462,6 +2531,10 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, RunLanguageDetectionTest(FILE_PATH_LITERAL("dynamic-reparenting.html")); } +IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, ComboboxItemVisibility) { + RunHtmlTest(FILE_PATH_LITERAL("combobox-item-visibility.html")); +} + // // These tests cover features of the testing infrastructure itself. // diff --git a/chromium/content/browser/accessibility/hit_testing_browsertest.cc b/chromium/content/browser/accessibility/hit_testing_browsertest.cc index 26cbd0452b3..f0ca9de1290 100644 --- a/chromium/content/browser/accessibility/hit_testing_browsertest.cc +++ b/chromium/content/browser/accessibility/hit_testing_browsertest.cc @@ -268,12 +268,9 @@ AccessibilityHitTestingBrowserTest::FormatHitTestAccessibilityTree() { AccessibilityTreeFormatterBlink::CreateBlink(); accessibility_tree_formatter->set_show_ids(true); accessibility_tree_formatter->SetPropertyFilters( - {{base::ASCIIToUTF16("name=*"), - AccessibilityTreeFormatter::PropertyFilter::ALLOW}, - {base::ASCIIToUTF16("location=*"), - AccessibilityTreeFormatter::PropertyFilter::ALLOW}, - {base::ASCIIToUTF16("size=*"), - AccessibilityTreeFormatter::PropertyFilter::ALLOW}}); + {{"name=*", AccessibilityTreeFormatter::PropertyFilter::ALLOW}, + {"location=*", AccessibilityTreeFormatter::PropertyFilter::ALLOW}, + {"size=*", AccessibilityTreeFormatter::PropertyFilter::ALLOW}}); base::string16 accessibility_tree; accessibility_tree_formatter->FormatAccessibilityTreeForTesting( GetRootAndAssertNonNull(), &accessibility_tree); @@ -573,7 +570,7 @@ IN_PROC_BROWSER_TEST_P(AccessibilityHitTestingBrowserTest, } } -#if !defined(OS_ANDROID) && !defined(OS_MACOSX) +#if !defined(OS_ANDROID) && !defined(OS_MAC) IN_PROC_BROWSER_TEST_P(AccessibilityHitTestingBrowserTest, CachingAsyncHitTest_WithPinchZoom) { ASSERT_TRUE(embedded_test_server()->Start()); @@ -665,7 +662,7 @@ IN_PROC_BROWSER_TEST_P(AccessibilityHitTestingBrowserTest, } // Timeouts on Linux. TODO(crbug.com/1083805): Enable this test. -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) #define MAYBE_CachingAsyncHitTestMissesElement_WithPinchZoom \ DISABLED_CachingAsyncHitTestMissesElement_WithPinchZoom #else @@ -728,7 +725,7 @@ IN_PROC_BROWSER_TEST_P(AccessibilityHitTestingBrowserTest, } } -#endif // !defined(OS_ANDROID) && !defined(OS_MACOSX) +#endif // !defined(OS_ANDROID) && !defined(OS_MAC) // GetAXPlatformNode is currently only supported on windows and linux (excluding // Chrome OS or Chromecast) diff --git a/chromium/content/browser/accessibility/one_shot_accessibility_tree_search.cc b/chromium/content/browser/accessibility/one_shot_accessibility_tree_search.cc index 6a36c176910..ab9e0e55316 100644 --- a/chromium/content/browser/accessibility/one_shot_accessibility_tree_search.cc +++ b/chromium/content/browser/accessibility/one_shot_accessibility_tree_search.cc @@ -236,7 +236,6 @@ bool AccessibilityButtonPredicate(BrowserAccessibility* start, BrowserAccessibility* node) { switch (node->GetRole()) { case ax::mojom::Role::kButton: - case ax::mojom::Role::kMenuButton: case ax::mojom::Role::kPopUpButton: case ax::mojom::Role::kSwitch: case ax::mojom::Role::kToggleButton: diff --git a/chromium/content/browser/accessibility/web_contents_accessibility_android.cc b/chromium/content/browser/accessibility/web_contents_accessibility_android.cc index b53ca692ff8..120d6e64a90 100644 --- a/chromium/content/browser/accessibility/web_contents_accessibility_android.cc +++ b/chromium/content/browser/accessibility/web_contents_accessibility_android.cc @@ -797,7 +797,9 @@ jboolean WebContentsAccessibilityAndroid::PopulateAccessibilityNodeInfo( base::android::ConvertUTF16ToJavaString( env, node->GetInheritedString16Attribute( ax::mojom::StringAttribute::kLanguage)), - suggestion_starts_java, suggestion_ends_java, suggestion_text_java); + suggestion_starts_java, suggestion_ends_java, suggestion_text_java, + base::android::ConvertUTF16ToJavaString(env, + node->GetStateDescription())); base::string16 element_id; if (node->GetHtmlAttribute("id", &element_id)) { @@ -808,9 +810,9 @@ jboolean WebContentsAccessibilityAndroid::PopulateAccessibilityNodeInfo( UpdateAccessibilityNodeInfoBoundsRect(env, obj, info, unique_id, node); - Java_WebContentsAccessibilityImpl_setAccessibilityNodeInfoLollipopAttributes( - env, obj, info, node->CanOpenPopup(), node->IsContentInvalid(), - node->IsDismissable(), node->IsMultiLine(), node->AndroidInputType(), + Java_WebContentsAccessibilityImpl_setAccessibilityNodeInfoAttributes( + env, obj, info, node->CanOpenPopup(), node->IsDismissable(), + node->IsMultiLine(), node->AndroidInputType(), node->AndroidLiveRegionType(), base::android::ConvertUTF16ToJavaString( env, node->GetContentInvalidErrorMessage())); @@ -894,30 +896,6 @@ jboolean WebContentsAccessibilityAndroid::PopulateAccessibilityEvent( break; } - Java_WebContentsAccessibilityImpl_setAccessibilityEventLollipopAttributes( - env, obj, event, node->CanOpenPopup(), node->IsContentInvalid(), - node->IsDismissable(), node->IsMultiLine(), node->AndroidInputType(), - node->AndroidLiveRegionType()); - if (node->IsCollection()) { - Java_WebContentsAccessibilityImpl_setAccessibilityEventCollectionInfo( - env, obj, event, node->RowCount(), node->ColumnCount(), - node->IsHierarchical()); - } - if (node->IsHeading()) { - Java_WebContentsAccessibilityImpl_setAccessibilityEventHeadingFlag( - env, obj, event, true); - } - if (node->IsCollectionItem()) { - Java_WebContentsAccessibilityImpl_setAccessibilityEventCollectionItemInfo( - env, obj, event, node->RowIndex(), node->RowSpan(), node->ColumnIndex(), - node->ColumnSpan()); - } - if (node->IsRangeType()) { - Java_WebContentsAccessibilityImpl_setAccessibilityEventRangeInfo( - env, obj, event, node->AndroidRangeType(), node->RangeMin(), - node->RangeMax(), node->RangeCurrentValue()); - } - return true; } |