Index: third_party/WebKit/Source/modules/accessibility/AXObject.cpp |
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXObject.cpp |
index a42851d0de31cac768b73194897a01fab759e9e3..5dd1f4cdf24a955960b99b94f821d2d4921cc735 100644 |
--- a/third_party/WebKit/Source/modules/accessibility/AXObject.cpp |
+++ b/third_party/WebKit/Source/modules/accessibility/AXObject.cpp |
@@ -676,6 +676,13 @@ bool AXObject::isPresentationalChild() const |
return m_cachedIsPresentationalChild; |
} |
+String AXObject::computedName() const |
+{ |
+ AXNameFrom nameFrom; |
+ AXObject::AXObjectVector nameObjects; |
+ return name(nameFrom, &nameObjects); |
+} |
+ |
String AXObject::name(AXNameFrom& nameFrom, AXObject::AXObjectVector* nameObjects) const |
{ |
HeapHashSet<Member<const AXObject>> visited; |
@@ -683,7 +690,7 @@ String AXObject::name(AXNameFrom& nameFrom, AXObject::AXObjectVector* nameObject |
String text = textAlternative(false, false, visited, nameFrom, &relatedObjects, nullptr); |
if (!node() || !isHTMLBRElement(node())) |
- text = text.simplifyWhiteSpace(isHTMLSpace<UChar>); |
+ text = text.simplifyWhiteSpace(isHTMLSpace<UChar>, WTF::DoNotStripWhiteSpace); |
if (nameObjects) { |
nameObjects->clear(); |
@@ -709,6 +716,154 @@ String AXObject::recursiveTextAlternative(const AXObject& axObj, bool inAriaLabe |
return axObj.textAlternative(true, inAriaLabelledByTraversal, visited, tmpNameFrom, nullptr, nullptr); |
} |
+String AXObject::ariaTextAlternative(bool recursive, bool inAriaLabelledByTraversal, AXObjectSet& visited, AXNameFrom& nameFrom, AXRelatedObjectVector* relatedObjects, NameSources* nameSources, bool* foundTextAlternative) const |
+{ |
+ String textAlternative; |
+ bool alreadyVisited = visited.contains(this); |
+ visited.add(this); |
+ |
+ // Step 2A from: http://www.w3.org/TR/accname-aam-1.1 |
+ if (!recursive && layoutObject() |
+ && layoutObject()->style()->visibility() != VISIBLE |
+ && !equalIgnoringCase(getAttribute(aria_hiddenAttr), "false")) { |
+ return String(); |
+ } |
+ |
+ // Step 2B from: http://www.w3.org/TR/accname-aam-1.1 |
+ if (!inAriaLabelledByTraversal && !alreadyVisited) { |
+ const QualifiedName& attr = hasAttribute(aria_labeledbyAttr) && !hasAttribute(aria_labelledbyAttr) ? aria_labeledbyAttr : aria_labelledbyAttr; |
+ nameFrom = AXNameFromRelatedElement; |
+ if (nameSources) { |
+ nameSources->append(NameSource(*foundTextAlternative, attr)); |
+ nameSources->last().type = nameFrom; |
+ } |
+ |
+ const AtomicString& ariaLabelledby = getAttribute(attr); |
+ if (!ariaLabelledby.isNull()) { |
+ if (nameSources) |
+ nameSources->last().attributeValue = ariaLabelledby; |
+ |
+ textAlternative = textFromAriaLabelledby(visited, relatedObjects); |
+ |
+ if (!textAlternative.isNull()) { |
+ if (nameSources) { |
+ NameSource& source = nameSources->last(); |
+ source.type = nameFrom; |
+ source.relatedObjects = *relatedObjects; |
+ source.text = textAlternative; |
+ *foundTextAlternative = true; |
+ } else { |
+ *foundTextAlternative = true; |
+ return textAlternative; |
+ } |
+ } else if (nameSources) { |
+ nameSources->last().invalid = true; |
+ } |
+ } |
+ } |
+ |
+ // Step 2C from: http://www.w3.org/TR/accname-aam-1.1 |
+ nameFrom = AXNameFromAttribute; |
+ if (nameSources) { |
+ nameSources->append(NameSource(*foundTextAlternative, aria_labelAttr)); |
+ nameSources->last().type = nameFrom; |
+ } |
+ const AtomicString& ariaLabel = getAttribute(aria_labelAttr); |
+ if (!ariaLabel.isEmpty()) { |
+ textAlternative = ariaLabel; |
+ |
+ if (nameSources) { |
+ NameSource& source = nameSources->last(); |
+ source.text = textAlternative; |
+ source.attributeValue = ariaLabel; |
+ *foundTextAlternative = true; |
+ } else { |
+ *foundTextAlternative = true; |
+ return textAlternative; |
+ } |
+ } |
+ |
+ return textAlternative; |
+} |
+ |
+String AXObject::textFromElements(bool inAriaLabelledbyTraversal, AXObjectSet& visited, WillBeHeapVector<RawPtrWillBeMember<Element>>& elements, AXRelatedObjectVector* relatedObjects) const |
+{ |
+ StringBuilder accumulatedText; |
+ bool foundValidElement = false; |
+ AXRelatedObjectVector localRelatedObjects; |
+ |
+ for (const auto& element : elements) { |
+ AXObject* axElement = axObjectCache().getOrCreate(element); |
+ if (axElement) { |
+ foundValidElement = true; |
+ |
+ String result = recursiveTextAlternative(*axElement, inAriaLabelledbyTraversal, visited); |
+ localRelatedObjects.append(new NameSourceRelatedObject(axElement, result)); |
+ if (!result.isEmpty()) { |
+ if (!accumulatedText.isEmpty()) |
+ accumulatedText.append(" "); |
+ accumulatedText.append(result); |
+ } |
+ } |
+ } |
+ if (!foundValidElement) |
+ return String(); |
+ if (relatedObjects) |
+ *relatedObjects = localRelatedObjects; |
+ return accumulatedText.toString(); |
+} |
+ |
+void AXObject::tokenVectorFromAttribute(Vector<String>& tokens, const QualifiedName& attribute) const |
+{ |
+ Node* node = this->node(); |
+ if (!node || !node->isElementNode()) |
+ return; |
+ |
+ String attributeValue = getAttribute(attribute).string(); |
+ if (attributeValue.isEmpty()) |
+ return; |
+ |
+ attributeValue.simplifyWhiteSpace(); |
+ attributeValue.split(' ', tokens); |
+} |
+ |
+void AXObject::elementsFromAttribute(WillBeHeapVector<RawPtrWillBeMember<Element>>& elements, const QualifiedName& attribute) const |
+{ |
+ Vector<String> ids; |
+ tokenVectorFromAttribute(ids, attribute); |
+ if (ids.isEmpty()) |
+ return; |
+ |
+ TreeScope& scope = node()->treeScope(); |
+ for (const auto& id : ids) { |
+ if (Element* idElement = scope.getElementById(AtomicString(id))) |
+ elements.append(idElement); |
+ } |
+} |
+ |
+void AXObject::ariaLabelledbyElementVector(WillBeHeapVector<RawPtrWillBeMember<Element>>& elements) const |
+{ |
+ // Try both spellings, but prefer aria-labelledby, which is the official spec. |
+ elementsFromAttribute(elements, aria_labelledbyAttr); |
+ if (!elements.size()) |
+ elementsFromAttribute(elements, aria_labeledbyAttr); |
+} |
+ |
+String AXObject::textFromAriaLabelledby(AXObjectSet& visited, AXRelatedObjectVector* relatedObjects) const |
+{ |
+ WillBeHeapVector<RawPtrWillBeMember<Element>> elements; |
+ ariaLabelledbyElementVector(elements); |
+ return textFromElements(true, visited, elements, relatedObjects); |
+} |
+ |
+String AXObject::textFromAriaDescribedby(AXRelatedObjectVector* relatedObjects) const |
+{ |
+ AXObjectSet visited; |
+ WillBeHeapVector<RawPtrWillBeMember<Element>> elements; |
+ elementsFromAttribute(elements, aria_describedbyAttr); |
+ return textFromElements(true, visited, elements, relatedObjects); |
+} |
+ |
AccessibilityOrientation AXObject::orientation() const |
{ |
// In ARIA 1.1, the default value for aria-orientation changed from |
@@ -1480,13 +1635,13 @@ bool AXObject::nameFromContents() const |
case MenuItemCheckBoxRole: |
case MenuItemRadioRole: |
case MenuListOptionRole: |
+ case PopUpButtonRole: |
case RadioButtonRole: |
case StaticTextRole: |
case StatusRole: |
case SwitchRole: |
case TabRole: |
case ToggleButtonRole: |
- case TreeItemRole: |
return true; |
default: |
return false; |