Chromium Code Reviews| 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(); |
| } |