| Index: Source/modules/accessibility/AXNodeObject.cpp
|
| diff --git a/Source/modules/accessibility/AXNodeObject.cpp b/Source/modules/accessibility/AXNodeObject.cpp
|
| index cebfae8fc3569c106e8850435ea596f25a634808..fe3d6ed2bc2e045e42a33b499007d597a5b7d4b3 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,8 @@ AccessibilityRole AXNodeObject::determineAccessibilityRoleUtil()
|
| return DetailsRole;
|
|
|
| if (isHTMLSummaryElement(*node())) {
|
| - if (node()->parentNode() && isHTMLDetailsElement(node()->parentNode()))
|
| + ContainerNode* parent = ComposedTreeTraversal::parent(*node());
|
| + if (parent && isHTMLDetailsElement(parent))
|
| return DisclosureTriangleRole;
|
| return UnknownRole;
|
| }
|
| @@ -589,9 +592,9 @@ void AXNodeObject::accessibilityChildrenFromAttribute(QualifiedName attr, Access
|
| // also return true if an ancestor is editable.
|
| bool AXNodeObject::hasContentEditableAttributeSet() const
|
| {
|
| - if (!hasAttribute(contenteditableAttr))
|
| - return false;
|
| const AtomicString& contentEditableValue = getAttribute(contenteditableAttr);
|
| + if (contentEditableValue.isNull())
|
| + return false;
|
| // Both "true" (case-insensitive) and the empty string count as true.
|
| return contentEditableValue.isEmpty() || equalIgnoringCase(contentEditableValue, "true");
|
| }
|
| @@ -1539,14 +1542,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* object)
|
| +{
|
| + LayoutObject* current = object;
|
| + while (current) {
|
| + if (current->isLayoutBlockFlow()) {
|
| + LayoutBlockFlow* blockFlow = toLayoutBlockFlow(current);
|
| + if (!blockFlow->inlineBoxWrapper())
|
| + return blockFlow;
|
| + }
|
| + current = current->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 isInSameNonInlineBlockFlow(LayoutObject* r1, LayoutObject* r2)
|
| {
|
| 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 +1602,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 (!isInSameNonInlineBlockFlow(child->layoutObject(), previous->layoutObject()))
|
| builder.append(' ');
|
| }
|
|
|
| @@ -1778,8 +1801,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);
|
| @@ -1804,9 +1831,10 @@ String AXNodeObject::textAlternative(bool recursive, bool inAriaLabelledByTraver
|
| nameSources->last().type = nameFrom;
|
| }
|
|
|
| - if (hasAttribute(attr)) {
|
| + const AtomicString& ariaLabelledby = getAttribute(attr);
|
| + if (!ariaLabelledby.isNull()) {
|
| if (nameSources)
|
| - nameSources->last().attributeValue = getAttribute(attr);
|
| + nameSources->last().attributeValue = ariaLabelledby;
|
|
|
| textAlternative = textFromAriaLabelledby(visited, nameObjects);
|
|
|
| @@ -1814,7 +1842,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 {
|
| @@ -1832,19 +1860,17 @@ String AXNodeObject::textAlternative(bool recursive, bool inAriaLabelledByTraver
|
| nameSources->append(NameSource(foundTextAlternative, aria_labelAttr));
|
| nameSources->last().type = nameFrom;
|
| }
|
| - if (hasAttribute(aria_labelAttr)) {
|
| - const AtomicString& ariaLabel = getAttribute(aria_labelAttr);
|
| - if (!ariaLabel.isEmpty()) {
|
| - textAlternative = ariaLabel;
|
| + 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 {
|
| - return textAlternative;
|
| - }
|
| + if (nameSources) {
|
| + NameSource& source = nameSources->last();
|
| + source.text = textAlternative;
|
| + source.attributeValue = ariaLabel;
|
| + foundTextAlternative = true;
|
| + } else {
|
| + return textAlternative;
|
| }
|
| }
|
|
|
| @@ -1854,7 +1880,17 @@ 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()) {
|
| + const AtomicString& ariaValuetext = getAttribute(aria_valuetextAttr);
|
| + if (!ariaValuetext.isNull())
|
| + return ariaValuetext.string();
|
| + return String::number(valueForRange());
|
| + }
|
| + }
|
|
|
| // Step 2F / 2G from: http://www.w3.org/TR/accname-aam-1.1
|
| if (recursive || nameFromContents()) {
|
| @@ -1870,6 +1906,25 @@ String AXNodeObject::textAlternative(bool recursive, bool inAriaLabelledByTraver
|
| else
|
| textAlternative = textFromDescendants(visited);
|
|
|
| + 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;
|
| + }
|
| + const AtomicString& title = getAttribute(titleAttr);
|
| + if (!title.isEmpty()) {
|
| + textAlternative = title;
|
| if (nameSources) {
|
| foundTextAlternative = true;
|
| nameSources->last().text = textAlternative;
|
| @@ -1880,12 +1935,14 @@ String AXNodeObject::textAlternative(bool recursive, bool inAriaLabelledByTraver
|
|
|
| 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 +1961,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 (!isInSameNonInlineBlockFlow(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 +1973,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 +1995,12 @@ String AXNodeObject::textFromElements(bool inAriaLabelledbyTraversal, AXObjectSe
|
| }
|
| if (!foundValidElement)
|
| return String();
|
| - nameObjects = localNameObjects;
|
| + if (nameObjects)
|
| + *nameObjects = localNameObjects;
|
| 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 +2203,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 +2509,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 +2525,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) {
|
| + 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 +2584,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;
|
| }
|
|
|
| @@ -2513,8 +2595,8 @@ String AXNodeObject::nativeTextAlternative(AXObjectSet& visited, AXNameFrom& nam
|
| nameSources->append(NameSource(*foundTextAlternative, altAttr));
|
| nameSources->last().type = nameFrom;
|
| }
|
| - if (inputElement->hasAttribute(altAttr)) {
|
| - AtomicString alt = inputElement->getAttribute(altAttr);
|
| + const AtomicString& alt = inputElement->getAttribute(altAttr);
|
| + if (!alt.isNull()) {
|
| textAlternative = alt;
|
| if (nameSources) {
|
| NameSource& source = nameSources->last();
|
| @@ -2544,40 +2626,40 @@ 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);
|
| -
|
| - 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;
|
| - }
|
| + HTMLElement* element = toHTMLElement(node());
|
| + const AtomicString& placeholder = element->fastGetAttribute(placeholderAttr);
|
| + if (!placeholder.isEmpty()) {
|
| + textAlternative = placeholder;
|
| + if (nameSources) {
|
| + NameSource& source = nameSources->last();
|
| + source.text = textAlternative;
|
| + source.attributeValue = placeholder;
|
| + } else {
|
| + return textAlternative;
|
| }
|
| }
|
| return textAlternative;
|
| @@ -2602,15 +2684,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 +2706,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);
|
| + const AtomicString& alt = getAttribute(altAttr);
|
| + if (!alt.isNull()) {
|
| textAlternative = alt;
|
| if (nameSources) {
|
| NameSource& source = nameSources->last();
|
| @@ -2660,20 +2742,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;
|
| + }
|
| + const AtomicString& summary = getAttribute(summaryAttr);
|
| + if (!summary.isNull()) {
|
| + textAlternative = summary;
|
| + if (nameSources) {
|
| + NameSource& source = nameSources->last();
|
| + source.attributeValue = summary;
|
| + source.text = textAlternative;
|
| + *foundTextAlternative = true;
|
| + } else {
|
| + return textAlternative;
|
| + }
|
| + }
|
| +
|
| return textAlternative;
|
| }
|
|
|
|
|