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