|  | // 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 "modules/accessibility/AXSparseAttributeSetter.h" | 
|  | #include "modules/accessibility/AXObjectCacheImpl.h" | 
|  |  | 
|  | namespace blink { | 
|  |  | 
|  | using namespace HTMLNames; | 
|  |  | 
|  | class BoolAttributeSetter : public AXSparseAttributeSetter { | 
|  | public: | 
|  | BoolAttributeSetter(AXBoolAttribute attribute) : attribute_(attribute) {} | 
|  |  | 
|  | private: | 
|  | AXBoolAttribute attribute_; | 
|  |  | 
|  | void Run(const AXObject& obj, | 
|  | AXSparseAttributeClient& attribute_map, | 
|  | const AtomicString& value) { | 
|  | // 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 StringAttributeSetter : public AXSparseAttributeSetter { | 
|  | public: | 
|  | StringAttributeSetter(AXStringAttribute attribute) : attribute_(attribute) {} | 
|  |  | 
|  | private: | 
|  | AXStringAttribute attribute_; | 
|  |  | 
|  | void Run(const AXObject& obj, | 
|  | AXSparseAttributeClient& attribute_map, | 
|  | const AtomicString& value) { | 
|  | 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) { | 
|  | if (value.IsNull() || value.IsEmpty()) | 
|  | return; | 
|  |  | 
|  | Node* node = obj.GetNode(); | 
|  | if (!node || !node->IsElementNode()) | 
|  | return; | 
|  | Element* target = ToElement(node)->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) { | 
|  | Node* node = obj.GetNode(); | 
|  | if (!node || !node->IsElementNode()) | 
|  | return; | 
|  |  | 
|  | String attribute_value = value.GetString(); | 
|  | if (attribute_value.IsEmpty()) | 
|  | return; | 
|  |  | 
|  | attribute_value.SimplifyWhiteSpace(); | 
|  | 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 && !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( | 
|  | aria_activedescendantAttr, | 
|  | new ObjectAttributeSetter(AXObjectAttribute::kAriaActiveDescendant)); | 
|  | ax_sparse_attribute_setter_map.Set( | 
|  | aria_controlsAttr, new ObjectVectorAttributeSetter( | 
|  | AXObjectVectorAttribute::kAriaControls)); | 
|  | ax_sparse_attribute_setter_map.Set( | 
|  | aria_flowtoAttr, | 
|  | new ObjectVectorAttributeSetter(AXObjectVectorAttribute::kAriaFlowTo)); | 
|  | ax_sparse_attribute_setter_map.Set( | 
|  | aria_detailsAttr, | 
|  | new ObjectAttributeSetter(AXObjectAttribute::kAriaDetails)); | 
|  | ax_sparse_attribute_setter_map.Set( | 
|  | aria_errormessageAttr, | 
|  | new ObjectAttributeSetter(AXObjectAttribute::kAriaErrorMessage)); | 
|  | ax_sparse_attribute_setter_map.Set( | 
|  | aria_keyshortcutsAttr, | 
|  | new StringAttributeSetter(AXStringAttribute::kAriaKeyShortcuts)); | 
|  | ax_sparse_attribute_setter_map.Set( | 
|  | aria_roledescriptionAttr, | 
|  | new StringAttributeSetter(AXStringAttribute::kAriaRoleDescription)); | 
|  | ax_sparse_attribute_setter_map.Set( | 
|  | aria_busyAttr, new BoolAttributeSetter(AXBoolAttribute::kAriaBusy)); | 
|  | } | 
|  | 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 (size_t 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 |