Index: third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp |
diff --git a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp |
index 58b6cd37e5ce8175a7e970004de4385f78d8dd0f..b757252a60da4b9ab39b34ab36d08473fbd29c93 100644 |
--- a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp |
+++ b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp |
@@ -32,6 +32,7 @@ |
#include "core/dom/DocumentUserGestureToken.h" |
#include "core/dom/Element.h" |
#include "core/dom/NodeTraversal.h" |
+#include "core/dom/QualifiedName.h" |
#include "core/dom/Text.h" |
#include "core/dom/shadow/FlatTreeTraversal.h" |
#include "core/editing/EditingUtilities.h" |
@@ -70,6 +71,132 @@ namespace blink { |
using namespace HTMLNames; |
+class SparseAttributeSetter { |
+ USING_FAST_MALLOC(SparseAttributeSetter); |
+ |
+ public: |
+ virtual void run(const AXObject&, |
+ AXSparseAttributeClient&, |
+ const AtomicString& value) = 0; |
+}; |
+ |
+class BoolAttributeSetter : public SparseAttributeSetter { |
+ public: |
+ BoolAttributeSetter(AXBoolAttribute attribute) : m_attribute(attribute) {} |
+ |
+ private: |
+ AXBoolAttribute m_attribute; |
+ |
+ void run(const AXObject& obj, |
+ AXSparseAttributeClient& attributeMap, |
+ const AtomicString& value) override { |
+ attributeMap.addBoolAttribute(m_attribute, |
+ equalIgnoringCase(value, "true")); |
+ } |
+}; |
+ |
+class StringAttributeSetter : public SparseAttributeSetter { |
+ public: |
+ StringAttributeSetter(AXStringAttribute attribute) : m_attribute(attribute) {} |
+ |
+ private: |
+ AXStringAttribute m_attribute; |
+ |
+ void run(const AXObject& obj, |
+ AXSparseAttributeClient& attributeMap, |
+ const AtomicString& value) override { |
+ attributeMap.addStringAttribute(m_attribute, value); |
+ } |
+}; |
+ |
+class ObjectAttributeSetter : public SparseAttributeSetter { |
+ public: |
+ ObjectAttributeSetter(AXObjectAttribute attribute) : m_attribute(attribute) {} |
+ |
+ private: |
+ AXObjectAttribute m_attribute; |
+ |
+ void run(const AXObject& obj, |
+ AXSparseAttributeClient& attributeMap, |
+ const AtomicString& value) override { |
+ if (value.isNull() || value.isEmpty()) |
+ return; |
+ |
+ Node* node = obj.getNode(); |
+ if (!node || !node->isElementNode()) |
+ return; |
+ Element* target = toElement(node)->treeScope().getElementById(value); |
+ if (!target) |
+ return; |
+ AXObject* axTarget = obj.axObjectCache().getOrCreate(target); |
+ if (axTarget) |
+ attributeMap.addObjectAttribute(m_attribute, *axTarget); |
+ } |
+}; |
+ |
+class ObjectVectorAttributeSetter : public SparseAttributeSetter { |
+ public: |
+ ObjectVectorAttributeSetter(AXObjectVectorAttribute attribute) |
+ : m_attribute(attribute) {} |
+ |
+ private: |
+ AXObjectVectorAttribute m_attribute; |
+ |
+ void run(const AXObject& obj, |
+ AXSparseAttributeClient& attributeMap, |
+ const AtomicString& value) override { |
+ Node* node = obj.getNode(); |
+ if (!node || !node->isElementNode()) |
+ return; |
+ |
+ String attributeValue = value.getString(); |
+ if (attributeValue.isEmpty()) |
+ return; |
+ |
+ attributeValue.simplifyWhiteSpace(); |
+ Vector<String> ids; |
+ attributeValue.split(' ', ids); |
+ if (ids.isEmpty()) |
+ return; |
+ |
+ HeapVector<Member<AXObject>> objects; |
+ TreeScope& scope = node->treeScope(); |
+ for (const auto& id : ids) { |
+ if (Element* idElement = scope.getElementById(AtomicString(id))) { |
+ AXObject* axIdElement = obj.axObjectCache().getOrCreate(idElement); |
+ if (axIdElement && !axIdElement->accessibilityIsIgnored()) |
+ objects.append(axIdElement); |
+ } |
+ } |
+ |
+ attributeMap.addObjectVectorAttribute(m_attribute, objects); |
+ } |
+}; |
+ |
+using AXSparseAttributeSetterMap = |
+ HashMap<QualifiedName, SparseAttributeSetter*>; |
+ |
+static 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, axSparseAttributeSetterMap, |
+ ()); |
+ if (axSparseAttributeSetterMap.isEmpty()) { |
+ axSparseAttributeSetterMap.set( |
+ aria_activedescendantAttr, |
+ new ObjectAttributeSetter(AXObjectAttribute::AriaActiveDescendant)); |
+ axSparseAttributeSetterMap.set( |
+ aria_controlsAttr, |
+ new ObjectVectorAttributeSetter(AXObjectVectorAttribute::AriaControls)); |
+ axSparseAttributeSetterMap.set( |
+ aria_flowtoAttr, |
+ new ObjectVectorAttributeSetter(AXObjectVectorAttribute::AriaFlowTo)); |
+ } |
+ return axSparseAttributeSetterMap; |
+} |
+ |
AXNodeObject::AXNodeObject(Node* node, AXObjectCacheImpl& axObjectCache) |
: AXObject(axObjectCache), |
m_ariaRole(UnknownRole), |
@@ -753,6 +880,22 @@ void AXNodeObject::detach() { |
m_node = nullptr; |
} |
+void AXNodeObject::getSparseAXAttributes( |
+ AXSparseAttributeClient& sparseAttributeClient) const { |
+ Node* node = this->getNode(); |
+ if (!node || !node->isElementNode()) |
+ return; |
+ |
+ AXSparseAttributeSetterMap& axSparseAttributeSetterMap = |
+ getSparseAttributeSetterMap(); |
+ AttributeCollection attributes = toElement(node)->attributesWithoutUpdate(); |
+ for (const Attribute& attr : attributes) { |
+ SparseAttributeSetter* setter = axSparseAttributeSetterMap.get(attr.name()); |
+ if (setter) |
+ setter->run(*this, sparseAttributeClient, attr.value()); |
+ } |
+} |
+ |
bool AXNodeObject::isAnchor() const { |
return !isNativeImage() && isLink(); |
} |