| // Copyright 2017 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 "third_party/blink/renderer/modules/accessibility/ax_sparse_attribute_setter.h" |
| #include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h" |
| |
| namespace blink { |
| |
| using namespace html_names; |
| |
| class BoolAttributeSetter : public AXSparseAttributeSetter { |
| public: |
| BoolAttributeSetter(AXBoolAttribute attribute) : attribute_(attribute) {} |
| |
| private: |
| AXBoolAttribute attribute_; |
| |
| void Run(const AXObject& obj, |
| AXSparseAttributeClient& attribute_map, |
| const AtomicString& value) override { |
| // ARIA booleans are true if not "false" and not specifically undefined. |
| bool is_true = !AccessibleNode::IsUndefinedAttrValue(value) && |
| !EqualIgnoringASCIICase(value, "false"); |
| if (is_true) // Not necessary to add if false |
| attribute_map.AddBoolAttribute(attribute_, true); |
| } |
| }; |
| |
| class IntAttributeSetter : public AXSparseAttributeSetter { |
| public: |
| IntAttributeSetter(AXIntAttribute attribute) : attribute_(attribute) {} |
| |
| private: |
| AXIntAttribute attribute_; |
| |
| void Run(const AXObject& obj, |
| AXSparseAttributeClient& attribute_map, |
| const AtomicString& value) override { |
| attribute_map.AddIntAttribute(attribute_, value.ToInt()); |
| } |
| }; |
| |
| class UIntAttributeSetter : public AXSparseAttributeSetter { |
| public: |
| UIntAttributeSetter(AXUIntAttribute attribute) : attribute_(attribute) {} |
| |
| private: |
| AXUIntAttribute attribute_; |
| |
| void Run(const AXObject& obj, |
| AXSparseAttributeClient& attribute_map, |
| const AtomicString& value) override { |
| attribute_map.AddUIntAttribute(attribute_, value.ToInt()); |
| } |
| }; |
| |
| class StringAttributeSetter : public AXSparseAttributeSetter { |
| public: |
| StringAttributeSetter(AXStringAttribute attribute) : attribute_(attribute) {} |
| |
| private: |
| AXStringAttribute attribute_; |
| |
| void Run(const AXObject& obj, |
| AXSparseAttributeClient& attribute_map, |
| const AtomicString& value) override { |
| attribute_map.AddStringAttribute(attribute_, value); |
| } |
| }; |
| |
| class ObjectAttributeSetter : public AXSparseAttributeSetter { |
| public: |
| ObjectAttributeSetter(AXObjectAttribute attribute) : attribute_(attribute) {} |
| |
| private: |
| AXObjectAttribute attribute_; |
| |
| void Run(const AXObject& obj, |
| AXSparseAttributeClient& attribute_map, |
| const AtomicString& value) override { |
| if (value.IsNull() || value.IsEmpty()) |
| return; |
| |
| auto* element = DynamicTo<Element>(obj.GetNode()); |
| if (!element) |
| return; |
| Element* target = element->GetTreeScope().getElementById(value); |
| if (!target) |
| return; |
| AXObject* ax_target = obj.AXObjectCache().GetOrCreate(target); |
| if (ax_target) |
| attribute_map.AddObjectAttribute(attribute_, *ax_target); |
| } |
| }; |
| |
| class ObjectVectorAttributeSetter : public AXSparseAttributeSetter { |
| public: |
| ObjectVectorAttributeSetter(AXObjectVectorAttribute attribute) |
| : attribute_(attribute) {} |
| |
| private: |
| AXObjectVectorAttribute attribute_; |
| |
| void Run(const AXObject& obj, |
| AXSparseAttributeClient& attribute_map, |
| const AtomicString& value) override { |
| Node* node = obj.GetNode(); |
| if (!node || !node->IsElementNode()) |
| return; |
| |
| String attribute_value = value.GetString(); |
| if (attribute_value.IsEmpty()) |
| return; |
| |
| Vector<String> ids; |
| attribute_value.Split(' ', ids); |
| if (ids.IsEmpty()) |
| return; |
| |
| HeapVector<Member<AXObject>> objects; |
| TreeScope& scope = node->GetTreeScope(); |
| for (const auto& id : ids) { |
| if (Element* id_element = scope.getElementById(AtomicString(id))) { |
| AXObject* ax_id_element = obj.AXObjectCache().GetOrCreate(id_element); |
| if (!ax_id_element) |
| continue; |
| if (AXObject* parent = ax_id_element->ParentObject()) |
| parent->UpdateChildrenIfNecessary(); |
| if (!ax_id_element->AccessibilityIsIgnored()) |
| objects.push_back(ax_id_element); |
| } |
| } |
| |
| attribute_map.AddObjectVectorAttribute(attribute_, objects); |
| } |
| }; |
| |
| AXSparseAttributeSetterMap& GetSparseAttributeSetterMap() { |
| // Use a map from attribute name to properties of that attribute. |
| // That way we only need to iterate over the list of attributes once, |
| // rather than calling getAttribute() once for each possible obscure |
| // accessibility attribute. |
| DEFINE_STATIC_LOCAL(AXSparseAttributeSetterMap, |
| ax_sparse_attribute_setter_map, ()); |
| if (ax_sparse_attribute_setter_map.IsEmpty()) { |
| ax_sparse_attribute_setter_map.Set( |
| kAriaActivedescendantAttr, |
| new ObjectAttributeSetter(AXObjectAttribute::kAriaActiveDescendant)); |
| ax_sparse_attribute_setter_map.Set( |
| kAriaControlsAttr, new ObjectVectorAttributeSetter( |
| AXObjectVectorAttribute::kAriaControls)); |
| ax_sparse_attribute_setter_map.Set( |
| kAriaFlowtoAttr, |
| new ObjectVectorAttributeSetter(AXObjectVectorAttribute::kAriaFlowTo)); |
| ax_sparse_attribute_setter_map.Set( |
| kAriaDetailsAttr, |
| new ObjectAttributeSetter(AXObjectAttribute::kAriaDetails)); |
| ax_sparse_attribute_setter_map.Set( |
| kAriaErrormessageAttr, |
| new ObjectAttributeSetter(AXObjectAttribute::kAriaErrorMessage)); |
| ax_sparse_attribute_setter_map.Set( |
| kAriaKeyshortcutsAttr, |
| new StringAttributeSetter(AXStringAttribute::kAriaKeyShortcuts)); |
| ax_sparse_attribute_setter_map.Set( |
| kAriaRoledescriptionAttr, |
| new StringAttributeSetter(AXStringAttribute::kAriaRoleDescription)); |
| ax_sparse_attribute_setter_map.Set( |
| kAriaBusyAttr, new BoolAttributeSetter(AXBoolAttribute::kAriaBusy)); |
| ax_sparse_attribute_setter_map.Set( |
| kAriaColcountAttr, |
| new IntAttributeSetter(AXIntAttribute::kAriaColumnCount)); |
| ax_sparse_attribute_setter_map.Set( |
| kAriaColindexAttr, |
| new UIntAttributeSetter(AXUIntAttribute::kAriaColumnIndex)); |
| ax_sparse_attribute_setter_map.Set( |
| kAriaColspanAttr, |
| new UIntAttributeSetter(AXUIntAttribute::kAriaColumnSpan)); |
| ax_sparse_attribute_setter_map.Set( |
| kAriaRowcountAttr, |
| new IntAttributeSetter(AXIntAttribute::kAriaRowCount)); |
| ax_sparse_attribute_setter_map.Set( |
| kAriaRowindexAttr, |
| new UIntAttributeSetter(AXUIntAttribute::kAriaRowIndex)); |
| ax_sparse_attribute_setter_map.Set( |
| kAriaRowspanAttr, |
| new UIntAttributeSetter(AXUIntAttribute::kAriaRowSpan)); |
| } |
| return ax_sparse_attribute_setter_map; |
| } |
| |
| void AXSparseAttributeAOMPropertyClient::AddStringProperty( |
| AOMStringProperty property, |
| const String& value) { |
| AXStringAttribute attribute; |
| switch (property) { |
| case AOMStringProperty::kKeyShortcuts: |
| attribute = AXStringAttribute::kAriaKeyShortcuts; |
| break; |
| case AOMStringProperty::kRoleDescription: |
| attribute = AXStringAttribute::kAriaRoleDescription; |
| break; |
| default: |
| return; |
| } |
| sparse_attribute_client_.AddStringAttribute(attribute, value); |
| } |
| |
| void AXSparseAttributeAOMPropertyClient::AddBooleanProperty( |
| AOMBooleanProperty property, |
| bool value) { |
| AXBoolAttribute attribute; |
| switch (property) { |
| case AOMBooleanProperty::kBusy: |
| attribute = AXBoolAttribute::kAriaBusy; |
| break; |
| default: |
| return; |
| } |
| sparse_attribute_client_.AddBoolAttribute(attribute, value); |
| } |
| |
| void AXSparseAttributeAOMPropertyClient::AddIntProperty(AOMIntProperty property, |
| int32_t value) {} |
| |
| void AXSparseAttributeAOMPropertyClient::AddUIntProperty( |
| AOMUIntProperty property, |
| uint32_t value) {} |
| |
| void AXSparseAttributeAOMPropertyClient::AddFloatProperty( |
| AOMFloatProperty property, |
| float value) {} |
| |
| void AXSparseAttributeAOMPropertyClient::AddRelationProperty( |
| AOMRelationProperty property, |
| const AccessibleNode& value) { |
| AXObjectAttribute attribute; |
| switch (property) { |
| case AOMRelationProperty::kActiveDescendant: |
| attribute = AXObjectAttribute::kAriaActiveDescendant; |
| break; |
| case AOMRelationProperty::kDetails: |
| attribute = AXObjectAttribute::kAriaDetails; |
| break; |
| case AOMRelationProperty::kErrorMessage: |
| attribute = AXObjectAttribute::kAriaErrorMessage; |
| break; |
| default: |
| return; |
| } |
| |
| Element* target_element = value.element(); |
| AXObject* target_obj = ax_object_cache_->GetOrCreate(target_element); |
| if (target_element) |
| sparse_attribute_client_.AddObjectAttribute(attribute, *target_obj); |
| } |
| |
| void AXSparseAttributeAOMPropertyClient::AddRelationListProperty( |
| AOMRelationListProperty property, |
| const AccessibleNodeList& relations) { |
| AXObjectVectorAttribute attribute; |
| switch (property) { |
| case AOMRelationListProperty::kControls: |
| attribute = AXObjectVectorAttribute::kAriaControls; |
| break; |
| case AOMRelationListProperty::kFlowTo: |
| attribute = AXObjectVectorAttribute::kAriaFlowTo; |
| break; |
| default: |
| return; |
| } |
| |
| HeapVector<Member<AXObject>> objects; |
| for (unsigned i = 0; i < relations.length(); ++i) { |
| AccessibleNode* accessible_node = relations.item(i); |
| if (accessible_node) { |
| Element* element = accessible_node->element(); |
| AXObject* ax_element = ax_object_cache_->GetOrCreate(element); |
| if (ax_element && !ax_element->AccessibilityIsIgnored()) |
| objects.push_back(ax_element); |
| } |
| } |
| |
| sparse_attribute_client_.AddObjectVectorAttribute(attribute, objects); |
| } |
| |
| } // namespace blink |