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 ce29eef87566c2319d2193e07301e2b8577c928b..8e0b8ef8c213329aeb5b6f1f984a67b1faabc8fb 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,111 @@ namespace blink { |
using namespace HTMLNames; |
+class AddSparseAttributeHelper { |
+ public: |
+ virtual void Run(const AXObject*, |
+ AXSparseAttributeMap&, |
+ const AtomicString& value) = 0; |
+}; |
+ |
+typedef HashMap<QualifiedName, AddSparseAttributeHelper*> |
+ AXSparseAttributeHelperMap; |
+ |
+class AddBoolAttributeHelper : public AddSparseAttributeHelper { |
+ public: |
+ AddBoolAttributeHelper(AXBoolAttribute attribute) : m_attribute(attribute) {} |
+ |
+ private: |
+ AXBoolAttribute m_attribute; |
+ |
+ void Run(const AXObject* obj, |
+ AXSparseAttributeMap& attributeMap, |
+ const AtomicString& value) override { |
+ attributeMap.addBoolAttribute(m_attribute, |
+ equalIgnoringCase(value, "true")); |
+ } |
+}; |
+ |
+class AddStringAttributeHelper : public AddSparseAttributeHelper { |
+ public: |
+ AddStringAttributeHelper(AXStringAttribute attribute) |
+ : m_attribute(attribute) {} |
+ |
+ private: |
+ AXStringAttribute m_attribute; |
+ |
+ void Run(const AXObject* obj, |
+ AXSparseAttributeMap& attributeMap, |
+ const AtomicString& value) override { |
+ attributeMap.addStringAttribute(m_attribute, value); |
+ } |
+}; |
+ |
+class AddObjectAttributeHelper : public AddSparseAttributeHelper { |
+ public: |
+ AddObjectAttributeHelper(AXObjectAttribute attribute) |
+ : m_attribute(attribute) {} |
+ |
+ private: |
+ AXObjectAttribute m_attribute; |
+ |
+ void Run(const AXObject* obj, |
+ AXSparseAttributeMap& 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 AddObjectVectorAttributeHelper : public AddSparseAttributeHelper { |
+ public: |
+ AddObjectVectorAttributeHelper(AXObjectVectorAttribute attribute) |
+ : m_attribute(attribute) {} |
+ |
+ private: |
+ AXObjectVectorAttribute m_attribute; |
+ |
+ void Run(const AXObject* obj, |
+ AXSparseAttributeMap& 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); |
+ } |
+}; |
+ |
AXNodeObject::AXNodeObject(Node* node, AXObjectCacheImpl& axObjectCache) |
: AXObject(axObjectCache), |
m_ariaRole(UnknownRole), |
@@ -752,6 +858,53 @@ void AXNodeObject::detach() { |
m_node = nullptr; |
} |
+void AXNodeObject::getSparseAXAttributes( |
+ AXSparseAttributeMap& sparseAttributeMap) const { |
+ Node* node = this->getNode(); |
+ if (!node || !node->isElementNode()) |
+ return; |
+ |
+ // 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(AXSparseAttributeHelperMap, axSparseAttributeHelperMap, |
+ ()); |
+ if (axSparseAttributeHelperMap.isEmpty()) { |
+ axSparseAttributeHelperMap.set( |
aboxhall
2017/01/10 05:59:59
Perhaps pull this block out into a helper function
dmazzoni
2017/01/12 03:36:24
Done.
|
+ aria_activedescendantAttr, |
+ new AddObjectAttributeHelper(AXObjectAttribute::AriaActiveDescendant)); |
+ axSparseAttributeHelperMap.set(aria_controlsAttr, |
+ new AddObjectVectorAttributeHelper( |
+ AXObjectVectorAttribute::AriaControls)); |
+ axSparseAttributeHelperMap.set(aria_detailsAttr, |
+ new AddObjectVectorAttributeHelper( |
+ AXObjectVectorAttribute::AriaDetails)); |
+ axSparseAttributeHelperMap.set( |
+ aria_errormessageAttr, |
+ new AddObjectAttributeHelper(AXObjectAttribute::AriaErrorMessage)); |
+ axSparseAttributeHelperMap.set(aria_flowtoAttr, |
+ new AddObjectVectorAttributeHelper( |
+ AXObjectVectorAttribute::AriaFlowTo)); |
+ axSparseAttributeHelperMap.set( |
+ aria_keyshortcutsAttr, |
+ new AddStringAttributeHelper(AXStringAttribute::AriaKeyShortcuts)); |
+ axSparseAttributeHelperMap.set( |
+ aria_modalAttr, new AddBoolAttributeHelper(AXBoolAttribute::AriaModal)); |
+ axSparseAttributeHelperMap.set( |
+ aria_roledescriptionAttr, |
+ new AddStringAttributeHelper(AXStringAttribute::AriaRoleDescription)); |
+ } |
+ |
+ AttributeCollection attributes = toElement(node)->attributesWithoutUpdate(); |
+ for (const Attribute& attr : attributes) { |
+ AddSparseAttributeHelper* helper = |
+ axSparseAttributeHelperMap.get(attr.name()); |
+ if (helper) |
+ helper->Run(this, sparseAttributeMap, attr.value()); |
+ } |
+} |
+ |
bool AXNodeObject::isAnchor() const { |
return !isNativeImage() && isLink(); |
} |