Chromium Code Reviews| Index: Source/modules/accessibility/AXNodeObject.cpp |
| diff --git a/Source/modules/accessibility/AXNodeObject.cpp b/Source/modules/accessibility/AXNodeObject.cpp |
| index cebfae8fc3569c106e8850435ea596f25a634808..b48bf894227ad946e7eeb07a48fc73c8926de044 100644 |
| --- a/Source/modules/accessibility/AXNodeObject.cpp |
| +++ b/Source/modules/accessibility/AXNodeObject.cpp |
| @@ -1,30 +1,30 @@ |
| /* |
| -* Copyright (C) 2012, Google Inc. All rights reserved. |
| -* |
| -* Redistribution and use in source and binary forms, with or without |
| -* modification, are permitted provided that the following conditions |
| -* are met: |
| -* |
| -* 1. Redistributions of source code must retain the above copyright |
| -* notice, this list of conditions and the following disclaimer. |
| -* 2. Redistributions in binary form must reproduce the above copyright |
| -* notice, this list of conditions and the following disclaimer in the |
| -* documentation and/or other materials provided with the distribution. |
| -* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
| -* its contributors may be used to endorse or promote products derived |
| -* from this software without specific prior written permission. |
| -* |
| -* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
| -* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| -* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| -* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| -*/ |
| + * Copyright (C) 2012, Google Inc. All rights reserved. |
| + * |
| + * Redistribution and use in source and binary forms, with or without |
| + * modification, are permitted provided that the following conditions |
| + * are met: |
| + * |
| + * 1. Redistributions of source code must retain the above copyright |
| + * notice, this list of conditions and the following disclaimer. |
| + * 2. Redistributions in binary form must reproduce the above copyright |
| + * notice, this list of conditions and the following disclaimer in the |
| + * documentation and/or other materials provided with the distribution. |
| + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
| + * its contributors may be used to endorse or promote products derived |
| + * from this software without specific prior written permission. |
| + * |
| + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
| + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| + */ |
| #include "config.h" |
| #include "modules/accessibility/AXNodeObject.h" |
| @@ -32,6 +32,7 @@ |
| #include "core/InputTypeNames.h" |
| #include "core/dom/NodeTraversal.h" |
| #include "core/dom/Text.h" |
| +#include "core/dom/shadow/ComposedTreeTraversal.h" |
| #include "core/html/HTMLDListElement.h" |
| #include "core/html/HTMLFieldSetElement.h" |
| #include "core/html/HTMLFrameElementBase.h" |
| @@ -51,9 +52,11 @@ |
| #include "core/html/HTMLTextAreaElement.h" |
| #include "core/html/parser/HTMLParserIdioms.h" |
| #include "core/html/shadow/MediaControlElements.h" |
| +#include "core/layout/LayoutBlockFlow.h" |
| #include "core/layout/LayoutObject.h" |
| #include "modules/accessibility/AXObjectCacheImpl.h" |
| #include "platform/UserGestureIndicator.h" |
| +#include "platform/text/PlatformLocale.h" |
| #include "wtf/text/StringBuilder.h" |
| @@ -250,7 +253,7 @@ static bool isRequiredOwnedElement(AXObject* parent, AccessibilityRole currentRo |
| return isListElement(parentNode); |
| if (currentRole == ListMarkerRole) |
| return isHTMLLIElement(*parentNode); |
| - if (currentRole == MenuItemCheckBoxRole || currentRole == MenuItemRole || currentRole == MenuItemRadioRole) |
| + if (currentRole == MenuItemCheckBoxRole || currentRole == MenuItemRole || currentRole == MenuItemRadioRole) |
| return isHTMLMenuElement(*parentNode); |
| if (!currentElement) |
| @@ -267,7 +270,6 @@ static bool isRequiredOwnedElement(AXObject* parent, AccessibilityRole currentRo |
| return false; |
| } |
| - |
| const AXObject* AXNodeObject::inheritsPresentationalRoleFrom() const |
| { |
| // ARIA states if an item can get focus, it should not be presentational. |
| @@ -336,7 +338,7 @@ AccessibilityRole AXNodeObject::determineAccessibilityRoleUtil() |
| return DetailsRole; |
| if (isHTMLSummaryElement(*node())) { |
| - if (node()->parentNode() && isHTMLDetailsElement(node()->parentNode())) |
| + if (ComposedTreeTraversal::parent(*node()) && isHTMLDetailsElement(ComposedTreeTraversal::parent(*node()))) |
|
esprehn
2015/08/30 00:07:47
ComposedTreeTraversal::parent is not cheap enough
aboxhall
2015/08/30 22:25:21
Done.
|
| return DisclosureTriangleRole; |
| return UnknownRole; |
| } |
| @@ -1539,14 +1541,34 @@ static bool shouldUseAccessibilityObjectInnerText(AXObject* obj) |
| return true; |
| } |
| -// Returns true if |r1| and |r2| are both non-null and are contained within the |
| -// same LayoutBox. |
| -static bool isSameLayoutBox(LayoutObject* r1, LayoutObject* r2) |
| +// Returns the nearest LayoutBlockFlow ancestor which does not have an |
| +// inlineBoxWrapper - i.e. is not itself an inline object. |
| +static LayoutBlockFlow* nonInlineBlockFlow(LayoutObject* obj) |
|
esprehn
2015/08/30 00:07:48
object
|
| +{ |
| + LayoutObject* curr = obj; |
|
esprehn
2015/08/30 00:07:47
current, we don't abbreviate like this now.
|
| + while (curr) { |
| + if (curr->isLayoutBlockFlow()) { |
| + LayoutBlockFlow* lbf = toLayoutBlockFlow(curr); |
|
esprehn
2015/08/30 00:07:47
blockFlow
|
| + if (!lbf->inlineBoxWrapper()) |
| + return lbf; |
| + } |
| + curr = curr->parent(); |
| + } |
| + |
| + ASSERT_NOT_REACHED(); |
| + return nullptr; |
| +} |
| + |
| +// Returns true if |r1| and |r2| are both non-null, both inline, and are contained |
| +// within the same non-inline LayoutBlockFlow. |
| +static bool isSameNonInlineBlockFlow(LayoutObject* r1, LayoutObject* r2) |
|
esprehn
2015/08/30 00:07:47
isInSameNonInlineBlockFlow? isSame makes me think
aboxhall
2015/08/30 22:25:21
Done.
|
| { |
| if (!r1 || !r2) |
| return false; |
| - LayoutBox* b1 = r1->enclosingBox(); |
| - LayoutBox* b2 = r2->enclosingBox(); |
| + if (!r1->isInline() || !r2->isInline()) |
| + return false; |
| + LayoutBlockFlow* b1 = nonInlineBlockFlow(r1); |
| + LayoutBlockFlow* b2 = nonInlineBlockFlow(r2); |
| return b1 && b2 && b1 == b2; |
| } |
| @@ -1579,7 +1601,7 @@ String AXNodeObject::deprecatedTextUnderElement(TextUnderElementMode mode) const |
| // so we should return "HelloWorld", but given <div>Hello</div><div>World</div> the |
| // strings are in separate boxes so we should return "Hello World". |
| if (previous && builder.length() && !isHTMLSpace(builder[builder.length() - 1])) { |
| - if (!isSameLayoutBox(child->layoutObject(), previous->layoutObject())) |
| + if (!isSameNonInlineBlockFlow(child->layoutObject(), previous->layoutObject())) |
| builder.append(' '); |
| } |
| @@ -1778,8 +1800,12 @@ String AXNodeObject::computedName() const |
| // New AX name calculation. |
| // |
| -String AXNodeObject::textAlternative(bool recursive, bool inAriaLabelledByTraversal, AXObjectSet& visited, AXNameFrom& nameFrom, AXObjectVector& nameObjects, NameSources* nameSources) const |
| +String AXNodeObject::textAlternative(bool recursive, bool inAriaLabelledByTraversal, AXObjectSet& visited, AXNameFrom& nameFrom, AXObjectVector* nameObjects, NameSources* nameSources) const |
| { |
| + // If nameSources is non-null, nameObjects is used in filling it in, so it must be non-null as well. |
| + if (nameSources) |
| + assert(nameObjects); |
| + |
| bool alreadyVisited = visited.contains(this); |
| bool foundTextAlternative = false; |
| visited.add(this); |
| @@ -1814,7 +1840,7 @@ String AXNodeObject::textAlternative(bool recursive, bool inAriaLabelledByTraver |
| if (nameSources) { |
| NameSource& source = nameSources->last(); |
| source.type = nameFrom; |
| - source.nameObjects = nameObjects; |
| + source.nameObjects = *nameObjects; |
| source.text = textAlternative; |
| foundTextAlternative = true; |
| } else { |
| @@ -1854,7 +1880,16 @@ String AXNodeObject::textAlternative(bool recursive, bool inAriaLabelledByTraver |
| return textAlternative; |
| // Step 2E from: http://www.w3.org/TR/accname-aam-1.1 |
| - |
| + if (recursive && !inAriaLabelledByTraversal && isControl()) { |
| + // No need to set any name source info in a recursive call. |
| + if (roleValue() == TextFieldRole || roleValue() == ComboBoxRole) |
| + return text(); |
| + if (isRange()) { |
| + if (hasAttribute(aria_valuetextAttr)) |
| + return getAttribute(aria_valuetextAttr).string(); |
|
esprehn
2015/08/30 00:07:47
calling hasAttribute and then getAttribute will be
aboxhall
2015/08/30 22:25:21
Done.
|
| + return String::number(valueForRange()); |
| + } |
| + } |
| // Step 2F / 2G from: http://www.w3.org/TR/accname-aam-1.1 |
| if (recursive || nameFromContents()) { |
| @@ -1870,22 +1905,45 @@ String AXNodeObject::textAlternative(bool recursive, bool inAriaLabelledByTraver |
| else |
| textAlternative = textFromDescendants(visited); |
| - if (nameSources) { |
| - foundTextAlternative = true; |
| - nameSources->last().text = textAlternative; |
| - } else { |
| - return textAlternative; |
| + if (!textAlternative.isEmpty()) { |
| + if (nameSources) { |
| + foundTextAlternative = true; |
| + nameSources->last().text = textAlternative; |
| + } else { |
| + return textAlternative; |
| + } |
| + } |
| + } |
| + |
| + // Step 2H from: http://www.w3.org/TR/accname-aam-1.1 |
| + nameFrom = AXNameFromAttribute; |
| + if (nameSources) { |
| + nameSources->append(NameSource(foundTextAlternative, titleAttr)); |
| + nameSources->last().type = nameFrom; |
| + } |
| + if (hasAttribute(titleAttr)) { |
|
esprehn
2015/08/30 00:07:47
ditto
aboxhall
2015/08/30 22:25:22
Done.
|
| + const AtomicString& title = getAttribute(titleAttr); |
| + if (!title.isEmpty()) { |
| + textAlternative = title; |
| + if (nameSources) { |
| + foundTextAlternative = true; |
| + nameSources->last().text = textAlternative; |
| + } else { |
| + return textAlternative; |
| + } |
| } |
| } |
| nameFrom = AXNameFromUninitialized; |
| - if (nameSources && !nameSources->isEmpty()) { |
| + if (foundTextAlternative) { |
| for (size_t i = 0; i < nameSources->size(); ++i) { |
| if (!(*nameSources)[i].text.isNull() && !(*nameSources)[i].superseded) { |
| - nameFrom = (*nameSources)[i].type; |
| - nameObjects = (*nameSources)[i].nameObjects; |
| - return (*nameSources)[i].text; |
| + NameSource& nameSource = (*nameSources)[i]; |
| + nameFrom = nameSource.type; |
| + if (!nameSource.nameObjects.isEmpty()) |
| + *nameObjects = nameSource.nameObjects; |
| + return nameSource.text; |
| } |
| } |
| } |
| @@ -1904,13 +1962,11 @@ String AXNodeObject::textFromDescendants(AXObjectSet& visited) const |
| // so we should return "HelloWorld", but given <div>Hello</div><div>World</div> the |
| // strings are in separate boxes so we should return "Hello World". |
| if (previous && accumulatedText.length() && !isHTMLSpace(accumulatedText[accumulatedText.length() - 1])) { |
| - if (!isSameLayoutBox(child->layoutObject(), previous->layoutObject())) |
| + if (!isSameNonInlineBlockFlow(child->layoutObject(), previous->layoutObject())) |
| accumulatedText.append(' '); |
| } |
| - AXNameFrom nameFrom; |
| - AXObjectVector nameObjects; |
| - String result = child->textAlternative(true, false, visited, nameFrom, nameObjects, nullptr); |
| + String result = recursiveTextAlternative(*child, false, visited); |
| accumulatedText.append(result); |
| previous = child; |
| } |
| @@ -1918,7 +1974,7 @@ String AXNodeObject::textFromDescendants(AXObjectSet& visited) const |
| return accumulatedText.toString(); |
| } |
| -String AXNodeObject::textFromElements(bool inAriaLabelledbyTraversal, AXObjectSet& visited, WillBeHeapVector<RawPtrWillBeMember<Element>>& elements, AXObjectVector& nameObjects) const |
| +String AXNodeObject::textFromElements(bool inAriaLabelledbyTraversal, AXObjectSet& visited, WillBeHeapVector<RawPtrWillBeMember<Element>>& elements, AXObjectVector* nameObjects) const |
| { |
| StringBuilder accumulatedText; |
| bool foundValidElement = false; |
| @@ -1940,11 +1996,12 @@ String AXNodeObject::textFromElements(bool inAriaLabelledbyTraversal, AXObjectSe |
| } |
| if (!foundValidElement) |
| return String(); |
| - nameObjects = localNameObjects; |
| + if (nameObjects) |
| + *nameObjects = localNameObjects; |
|
esprehn
2015/08/30 00:07:47
I'm not a huge fan of this kind of optional out pa
aboxhall
2015/08/30 22:25:22
In recursive calls, nameObjects will always be nul
|
| return accumulatedText.toString(); |
| } |
| -String AXNodeObject::textFromAriaLabelledby(AXObjectSet& visited, AXObjectVector& nameObjects) const |
| +String AXNodeObject::textFromAriaLabelledby(AXObjectSet& visited, AXObjectVector* nameObjects) const |
| { |
| WillBeHeapVector<RawPtrWillBeMember<Element>> elements; |
| ariaLabelledbyElements(elements); |
| @@ -2147,8 +2204,7 @@ Element* AXNodeObject::actionElement() const |
| if (isHTMLInputElement(*node)) { |
| HTMLInputElement& input = toHTMLInputElement(*node); |
| - if (!input.isDisabledFormControl() && (isCheckboxOrRadio() || input.isTextButton() |
| - || input.type() == InputTypeNames::file)) |
| + if (!input.isDisabledFormControl() && (isCheckboxOrRadio() || input.isTextButton() || input.type() == InputTypeNames::file)) |
| return &input; |
| } else if (isHTMLButtonElement(*node)) { |
| return toElement(node); |
| @@ -2454,11 +2510,15 @@ void AXNodeObject::deprecatedAriaLabelledbyText(WillBeHeapVector<OwnPtrWillBeMem |
| } |
| // Based on http://rawgit.com/w3c/aria/master/html-aam/html-aam.html#accessible-name-and-description-calculation |
| -String AXNodeObject::nativeTextAlternative(AXObjectSet& visited, AXNameFrom& nameFrom, AXObjectVector& nameObjects, NameSources* nameSources, bool* foundTextAlternative) const |
| +String AXNodeObject::nativeTextAlternative(AXObjectSet& visited, AXNameFrom& nameFrom, AXObjectVector* nameObjects, NameSources* nameSources, bool* foundTextAlternative) const |
| { |
| if (!node()) |
| return String(); |
| + // If nameSources is non-null, nameObjects is used in filling it in, so it must be non-null as well. |
| + if (nameSources) |
| + assert(nameObjects); |
| + |
| String textAlternative; |
| AXObjectVector localNameObjects; |
| @@ -2466,6 +2526,46 @@ String AXNodeObject::nativeTextAlternative(AXObjectSet& visited, AXNameFrom& nam |
| if (isHTMLInputElement(node())) |
| inputElement = toHTMLInputElement(node()); |
| + // 5.1/5.5 Text inputs, Other labelable Elements |
| + HTMLElement* htmlElement = nullptr; |
| + if (node()->isHTMLElement()) |
| + htmlElement = toHTMLElement(node()); |
| + if (htmlElement && htmlElement->isLabelable()) { |
| + // label |
| + nameFrom = AXNameFromRelatedElement; |
| + if (nameSources) { |
| + nameSources->append(NameSource(*foundTextAlternative)); |
| + nameSources->last().type = nameFrom; |
| + nameSources->last().nativeSource = AXTextFromNativeHTMLLabel; |
| + } |
| + HTMLLabelElement* label = labelForElement(htmlElement); |
| + if (label) { |
| + RefPtrWillBeRawPtr<AXObject> labelAXObject = axObjectCache().getOrCreate(label); |
| + // Avoid an infinite loop for label wrapped |
| + if (labelAXObject && !visited.contains(labelAXObject.get())) { |
| + if (nameObjects) { |
|
esprehn
2015/08/30 00:07:48
having both nameSources and nameObjects is confusi
aboxhall
2015/08/30 22:25:21
nameSources will be used by devtools frontend to d
|
| + localNameObjects.append(labelAXObject.get()); |
| + *nameObjects = localNameObjects; |
| + localNameObjects.clear(); |
| + } |
| + textAlternative = recursiveTextAlternative(*labelAXObject, false, visited); |
| + |
| + if (nameSources) { |
| + NameSource& source = nameSources->last(); |
| + source.nameObjects = *nameObjects; |
| + source.text = textAlternative; |
| + if (label->getAttribute(forAttr).isNull()) |
| + source.nativeSource = AXTextFromNativeHTMLLabelWrapped; |
| + else |
| + source.nativeSource = AXTextFromNativeHTMLLabelFor; |
| + *foundTextAlternative = true; |
| + } else { |
| + return textAlternative; |
| + } |
| + } |
| + } |
| + } |
| + |
| // 5.2 input type="button", input type="submit" and input type="reset" |
| if (inputElement && inputElement->isTextButton()) { |
| // value attribue |
| @@ -2485,23 +2585,6 @@ String AXNodeObject::nativeTextAlternative(AXObjectSet& visited, AXNameFrom& nam |
| return textAlternative; |
| } |
| } |
| - |
| - // localised default value ("Submit" or "Reset") |
| - String defaultValue = inputElement->defaultValue(); |
| - if (!defaultValue.isNull()) { |
| - // defaultValue is always set for submit and reset buttons, and never set for anything else. |
| - nameFrom = AXNameFromAttribute; |
| - textAlternative = defaultValue; |
| - if (nameSources) { |
| - nameSources->append(NameSource(*foundTextAlternative, typeAttr)); |
| - NameSource& source = nameSources->last(); |
| - source.attributeValue = inputElement->getAttribute(typeAttr); |
| - source.text = textAlternative; |
| - *foundTextAlternative = true; |
| - } else { |
| - return textAlternative; |
| - } |
| - } |
| return textAlternative; |
| } |
| @@ -2544,37 +2627,39 @@ String AXNodeObject::nativeTextAlternative(AXObjectSet& visited, AXNameFrom& nam |
| } |
| } |
| + // localised default value ("Submit") |
| + nameFrom = AXNameFromAttribute; |
| + textAlternative = inputElement->locale().queryString(WebLocalizedString::SubmitButtonDefaultLabel); |
| + if (nameSources) { |
| + nameSources->append(NameSource(*foundTextAlternative, typeAttr)); |
| + NameSource& source = nameSources->last(); |
| + source.attributeValue = inputElement->getAttribute(typeAttr); |
| + source.type = nameFrom; |
| + source.text = textAlternative; |
| + *foundTextAlternative = true; |
| + } else { |
| + return textAlternative; |
| + } |
| return textAlternative; |
| } |
| - // 5.5 Other labelable Elements |
| - if (node()->isHTMLElement() && toHTMLElement(node())->isLabelable()) { |
| - // label |
| - nameFrom = AXNameFromRelatedElement; |
| + // 5.1 Text inputs - step 3 (placeholder attribute) |
| + if (htmlElement && htmlElement->isTextFormControl()) { |
| + nameFrom = AXNameFromPlaceholder; |
| if (nameSources) { |
| - nameSources->append(NameSource(*foundTextAlternative)); |
| - nameSources->last().type = nameFrom; |
| - nameSources->last().nativeSource = AXTextFromNativeHTMLLabel; |
| + nameSources->append(NameSource(*foundTextAlternative, placeholderAttr)); |
| + NameSource& source = nameSources->last(); |
| + source.type = nameFrom; |
| } |
| - HTMLLabelElement* label = labelForElement(toHTMLElement(node())); |
| - if (label) { |
| - RefPtrWillBeRawPtr<AXObject> labelAXObject = axObjectCache().getOrCreate(label); |
| - if (labelAXObject) { |
| - localNameObjects.append(labelAXObject.get()); |
| - nameObjects = localNameObjects; |
| - localNameObjects.clear(); |
| - |
| - textAlternative = recursiveTextAlternative(*labelAXObject, false, visited); |
| - |
| + HTMLElement* element = toHTMLElement(node()); |
| + if (element->fastHasAttribute(placeholderAttr)) { |
|
esprehn
2015/08/30 00:07:47
checking has and then get is slower than just get
aboxhall
2015/08/30 22:25:21
Done.
|
| + const AtomicString& placeholder = element->fastGetAttribute(placeholderAttr); |
| + if (!placeholder.isEmpty()) { |
| + textAlternative = placeholder; |
| if (nameSources) { |
| NameSource& source = nameSources->last(); |
| - source.nameObjects = nameObjects; |
| source.text = textAlternative; |
| - if (label->getAttribute(forAttr).isNull()) |
| - source.nativeSource = AXTextFromNativeHTMLLabelWrapped; |
| - else |
| - source.nativeSource = AXTextFromNativeHTMLLabelFor; |
| - *foundTextAlternative = true; |
| + source.attributeValue = placeholder; |
| } else { |
| return textAlternative; |
| } |
| @@ -2602,15 +2687,17 @@ String AXNodeObject::nativeTextAlternative(AXObjectSet& visited, AXNameFrom& nam |
| if (figcaption) { |
| RefPtrWillBeRawPtr<AXObject> figcaptionAXObject = axObjectCache().getOrCreate(figcaption); |
| if (figcaptionAXObject) { |
| - localNameObjects.append(figcaptionAXObject.get()); |
| - nameObjects = localNameObjects; |
| - localNameObjects.clear(); |
| + if (nameObjects) { |
| + localNameObjects.append(figcaptionAXObject.get()); |
| + *nameObjects = localNameObjects; |
| + localNameObjects.clear(); |
| + } |
| textAlternative = recursiveTextAlternative(*figcaptionAXObject, false, visited); |
| if (nameSources) { |
| NameSource& source = nameSources->last(); |
| - source.nameObjects = nameObjects; |
| + source.nameObjects = *nameObjects; |
| source.text = textAlternative; |
| } else { |
| return textAlternative; |
| @@ -2622,16 +2709,14 @@ String AXNodeObject::nativeTextAlternative(AXObjectSet& visited, AXNameFrom& nam |
| // 5.8 img Element |
| if (isHTMLImageElement(node())) { |
| - HTMLImageElement* imgElement = toHTMLImageElement(node()); |
| - |
| // alt |
| nameFrom = AXNameFromAttribute; |
| if (nameSources) { |
| nameSources->append(NameSource(*foundTextAlternative, altAttr)); |
| nameSources->last().type = nameFrom; |
| } |
| - if (imgElement->hasAttribute(altAttr)) { |
| - AtomicString alt = imgElement->getAttribute(altAttr); |
| + if (hasAttribute(altAttr)) { |
| + AtomicString alt = getAttribute(altAttr); |
| textAlternative = alt; |
| if (nameSources) { |
| NameSource& source = nameSources->last(); |
| @@ -2660,20 +2745,42 @@ String AXNodeObject::nativeTextAlternative(AXObjectSet& visited, AXNameFrom& nam |
| if (caption) { |
| RefPtrWillBeRawPtr<AXObject> captionAXObject = axObjectCache().getOrCreate(caption); |
| if (captionAXObject) { |
| - localNameObjects.append(captionAXObject.get()); |
| - nameObjects = localNameObjects; |
| - localNameObjects.clear(); |
| + if (nameObjects) { |
| + localNameObjects.append(captionAXObject.get()); |
| + *nameObjects = localNameObjects; |
| + localNameObjects.clear(); |
| + } |
| textAlternative = recursiveTextAlternative(*captionAXObject, false, visited); |
| if (nameSources) { |
| NameSource& source = nameSources->last(); |
| - source.nameObjects = nameObjects; |
| + source.nameObjects = *nameObjects; |
| source.text = textAlternative; |
| } else { |
| return textAlternative; |
| } |
| } |
| } |
| + |
| + // summary |
| + nameFrom = AXNameFromAttribute; |
| + if (nameSources) { |
| + nameSources->append(NameSource(*foundTextAlternative)); |
| + nameSources->last().type = nameFrom; |
| + } |
| + if (hasAttribute(summaryAttr)) { |
| + AtomicString summary = getAttribute(summaryAttr); |
|
esprehn
2015/08/30 00:07:47
ditto
aboxhall
2015/08/30 22:25:21
Done.
|
| + textAlternative = summary; |
| + if (nameSources) { |
| + NameSource& source = nameSources->last(); |
| + source.attributeValue = summary; |
| + source.text = textAlternative; |
| + *foundTextAlternative = true; |
| + } else { |
| + return textAlternative; |
| + } |
| + } |
| + |
| return textAlternative; |
| } |