diff options
author | Simon Hausmann <simon.hausmann@digia.com> | 2012-10-22 15:40:17 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@digia.com> | 2012-10-22 15:40:17 +0200 |
commit | 43a42f108af6bcbd91f2672731c3047c26213af1 (patch) | |
tree | 7fa092e5f5d873c72f2486a70e26be26f7a38bec /Source/WebCore/accessibility/AccessibilityNodeObject.cpp | |
parent | d9cf437c840c6eb7417bdd97e6c40979255d3158 (diff) | |
download | qtwebkit-43a42f108af6bcbd91f2672731c3047c26213af1.tar.gz |
Imported WebKit commit 302e7806bff028bd1167a1ec7c86a1ee00ecfb49 (http://svn.webkit.org/repository/webkit/trunk@132067)
New snapshot that fixes build without QtWidgets
Diffstat (limited to 'Source/WebCore/accessibility/AccessibilityNodeObject.cpp')
-rw-r--r-- | Source/WebCore/accessibility/AccessibilityNodeObject.cpp | 303 |
1 files changed, 281 insertions, 22 deletions
diff --git a/Source/WebCore/accessibility/AccessibilityNodeObject.cpp b/Source/WebCore/accessibility/AccessibilityNodeObject.cpp index 0f94838e7..49b83ea6d 100644 --- a/Source/WebCore/accessibility/AccessibilityNodeObject.cpp +++ b/Source/WebCore/accessibility/AccessibilityNodeObject.cpp @@ -59,6 +59,7 @@ #include "HTMLTextFormControlElement.h" #include "HitTestRequest.h" #include "HitTestResult.h" +#include "LabelableElement.h" #include "LocalizedStrings.h" #include "MathMLNames.h" #include "NodeList.h" @@ -221,6 +222,26 @@ LayoutRect AccessibilityNodeObject::elementRect() const { return boundingBoxRect(); } + +LayoutRect AccessibilityNodeObject::boundingBoxRect() const +{ + // AccessibilityNodeObjects have no mechanism yet to return a size or position. + // For now, let's return the position of the ancestor that does have a position, + // and make it the width of that parent, and about the height of a line of text, so that it's clear the object is a child of the parent. + + LayoutRect boundingBox; + + for (AccessibilityObject* positionProvider = parentObject(); positionProvider; positionProvider = positionProvider->parentObject()) { + if (positionProvider->isAccessibilityRenderObject()) { + LayoutRect parentRect = positionProvider->elementRect(); + boundingBox.setSize(LayoutSize(parentRect.width(), FractionalLayoutUnit(std::min(10.0f, parentRect.height().toFloat())))); + boundingBox.setLocation(parentRect.location()); + break; + } + } + + return boundingBox; +} void AccessibilityNodeObject::setNode(Node* node) { @@ -283,6 +304,32 @@ AccessibilityRole AccessibilityNodeObject::determineAccessibilityRole() return UnknownRole; } +void AccessibilityNodeObject::insertChild(AccessibilityObject* child, unsigned index) +{ + if (!child) + return; + + // If the parent is asking for this child's children, then either it's the first time (and clearing is a no-op), + // or its visibility has changed. In the latter case, this child may have a stale child cached. + // This can prevent aria-hidden changes from working correctly. Hence, whenever a parent is getting children, ensure data is not stale. + child->clearChildren(); + + if (child->accessibilityIsIgnored()) { + AccessibilityChildrenVector children = child->children(); + size_t length = children.size(); + for (size_t i = 0; i < length; ++i) + m_children.insert(index + i, children[i]); + } else { + ASSERT(child->parentObject() == this); + m_children.insert(index, child); + } +} + +void AccessibilityNodeObject::addChild(AccessibilityObject* child) +{ + insertChild(child, m_children.size()); +} + void AccessibilityNodeObject::addChildren() { // If the need to add more children in addition to existing children arises, @@ -298,19 +345,8 @@ void AccessibilityNodeObject::addChildren() if (renderer() && !m_node->hasTagName(canvasTag)) return; - for (Node* child = m_node->firstChild(); child; child = child->nextSibling()) { - RefPtr<AccessibilityObject> obj = axObjectCache()->getOrCreate(child); - obj->clearChildren(); - if (obj->accessibilityIsIgnored()) { - AccessibilityChildrenVector children = obj->children(); - size_t length = children.size(); - for (size_t i = 0; i < length; ++i) - m_children.append(children[i]); - } else { - ASSERT(obj->parentObject() == this); - m_children.append(obj); - } - } + for (Node* child = m_node->firstChild(); child; child = child->nextSibling()) + addChild(axObjectCache()->getOrCreate(child)); } bool AccessibilityNodeObject::canHaveChildren() const @@ -1001,14 +1037,18 @@ bool AccessibilityNodeObject::isGenericFocusableElement() const HTMLLabelElement* AccessibilityNodeObject::labelForElement(Element* element) const { - RefPtr<NodeList> list = element->document()->getElementsByTagName("label"); - unsigned len = list->length(); - for (unsigned i = 0; i < len; i++) { - if (list->item(i)->hasTagName(labelTag)) { - HTMLLabelElement* label = static_cast<HTMLLabelElement*>(list->item(i)); - if (label->control() == element) - return label; - } + if (!element->isHTMLElement() || !toHTMLElement(element)->isLabelable()) + return 0; + + const AtomicString& id = element->getIdAttribute(); + if (!id.isEmpty()) { + if (HTMLLabelElement* label = element->treeScope()->labelElementForId(id)) + return label; + } + + for (Element* parent = element->parentElement(); parent; parent = parent->parentElement()) { + if (parent->hasTagName(labelTag)) + return static_cast<HTMLLabelElement*>(parent); } return 0; @@ -1074,6 +1114,215 @@ AccessibilityObject* AccessibilityNodeObject::menuButtonForMenu() const return 0; } +void AccessibilityNodeObject::titleElementText(Vector<AccessibilityText>& textOrder) +{ + Node* node = this->node(); + if (!node) + return; + + bool isInputTag = node->hasTagName(inputTag); + if (isInputTag || AccessibilityObject::isARIAInput(ariaRoleAttribute()) || isControl()) { + HTMLLabelElement* label = labelForElement(toElement(node)); + if (label) { + AccessibilityObject* labelObject = axObjectCache()->getOrCreate(label); + textOrder.append(AccessibilityText(label->innerText(), LabelByElementText, labelObject)); + return; + } + } + + AccessibilityObject* titleUIElement = this->titleUIElement(); + if (titleUIElement) + textOrder.append(AccessibilityText(String(), LabelByElementText, titleUIElement)); +} + +void AccessibilityNodeObject::alternativeText(Vector<AccessibilityText>& textOrder) const +{ + if (isWebArea()) { + String webAreaText = alternativeTextForWebArea(); + if (!webAreaText.isEmpty()) + textOrder.append(AccessibilityText(webAreaText, AlternativeText)); + return; + } + + ariaLabeledByText(textOrder); + + const AtomicString& ariaLabel = getAttribute(aria_labelAttr); + if (!ariaLabel.isEmpty()) + textOrder.append(AccessibilityText(ariaLabel, AlternativeText)); + + if (isImage() || isInputImage() || isNativeImage() || isCanvas()) { + // Images should use alt as long as the attribute is present, even if empty. + // Otherwise, it should fallback to other methods, like the title attribute. + const AtomicString& alt = getAttribute(altAttr); + if (!alt.isNull()) + textOrder.append(AccessibilityText(alt, AlternativeText)); + } + +#if ENABLE(MATHML) + Node* node = this->node(); + if (node && node->isElementNode() && toElement(node)->isMathMLElement()) + textOrder.append(AccessibilityText(getAttribute(MathMLNames::alttextAttr), AlternativeText)); +#endif +} + +void AccessibilityNodeObject::visibleText(Vector<AccessibilityText>& textOrder) const +{ + Node* node = this->node(); + if (!node) + return; + + bool isInputTag = node->hasTagName(inputTag); + if (isInputTag) { + HTMLInputElement* input = static_cast<HTMLInputElement*>(node); + if (input->isTextButton()) { + textOrder.append(AccessibilityText(input->valueWithDefault(), VisibleText)); + return; + } + } + + // If this node isn't rendered, there's no inner text we can extract from a select element. + if (!isAccessibilityRenderObject() && node->hasTagName(selectTag)) + return; + + bool useTextUnderElement = false; + + switch (roleValue()) { + case PopUpButtonRole: + case ButtonRole: + case ToggleButtonRole: + case CheckBoxRole: + case ListBoxOptionRole: + case MenuButtonRole: + case MenuItemRole: + case RadioButtonRole: + case TabRole: + useTextUnderElement = true; + break; + default: + break; + } + + // If it's focusable but it's not content editable or a known control type, then it will appear to + // the user as a single atomic object, so we should use its text as the default title. + if (isHeading() || isLink() || isGenericFocusableElement()) + useTextUnderElement = true; + + if (useTextUnderElement) { + String text = textUnderElement(); + if (!text.isEmpty()) + textOrder.append(AccessibilityText(text, ChildrenText)); + } +} + +void AccessibilityNodeObject::helpText(Vector<AccessibilityText>& textOrder) const +{ + const AtomicString& ariaHelp = getAttribute(aria_helpAttr); + if (!ariaHelp.isEmpty()) + textOrder.append(AccessibilityText(ariaHelp, HelpText)); + + String describedBy = ariaDescribedByAttribute(); + if (!describedBy.isEmpty()) + textOrder.append(AccessibilityText(describedBy, SummaryText)); + + // Add help type text that is derived from ancestors. + for (Node* curr = node(); curr; curr = curr->parentNode()) { + const AtomicString& summary = getAttribute(summaryAttr); + if (!summary.isEmpty()) + textOrder.append(AccessibilityText(summary, SummaryText)); + + // The title attribute should be used as help text unless it is already being used as descriptive text. + const AtomicString& title = getAttribute(titleAttr); + if (!title.isEmpty()) + textOrder.append(AccessibilityText(title, TitleTagText)); + + // Only take help text from an ancestor element if its a group or an unknown role. If help was + // added to those kinds of elements, it is likely it was meant for a child element. + AccessibilityObject* axObj = axObjectCache()->getOrCreate(curr); + if (!axObj) + return; + + AccessibilityRole role = axObj->roleValue(); + if (role != GroupRole && role != UnknownRole) + break; + } +} + +void AccessibilityNodeObject::accessibilityText(Vector<AccessibilityText>& textOrder) +{ + titleElementText(textOrder); + alternativeText(textOrder); + visibleText(textOrder); + helpText(textOrder); + + String placeholder = placeholderValue(); + if (!placeholder.isEmpty()) + textOrder.append(AccessibilityText(placeholder, PlaceholderText)); +} + +void AccessibilityNodeObject::ariaLabeledByText(Vector<AccessibilityText>& textOrder) const +{ + String ariaLabeledBy = ariaLabeledByAttribute(); + if (!ariaLabeledBy.isEmpty()) { + Vector<Element*> elements; + ariaLabeledByElements(elements); + + Vector<RefPtr<AccessibilityObject> > axElements; + unsigned length = elements.size(); + for (unsigned k = 0; k < length; k++) { + RefPtr<AccessibilityObject> axElement = axObjectCache()->getOrCreate(elements[k]); + axElements.append(axElement); + } + + textOrder.append(AccessibilityText(ariaLabeledBy, AlternativeText, axElements)); + } +} + +String AccessibilityNodeObject::alternativeTextForWebArea() const +{ + // The WebArea description should follow this order: + // aria-label on the <html> + // title on the <html> + // <title> inside the <head> (of it was set through JS) + // name on the <html> + // For iframes: + // aria-label on the <iframe> + // title on the <iframe> + // name on the <iframe> + + Document* document = this->document(); + if (!document) + return String(); + + // Check if the HTML element has an aria-label for the webpage. + if (Element* documentElement = document->documentElement()) { + const AtomicString& ariaLabel = documentElement->getAttribute(aria_labelAttr); + if (!ariaLabel.isEmpty()) + return ariaLabel; + } + + Node* owner = document->ownerElement(); + if (owner) { + if (owner->hasTagName(frameTag) || owner->hasTagName(iframeTag)) { + const AtomicString& title = static_cast<HTMLFrameElementBase*>(owner)->getAttribute(titleAttr); + if (!title.isEmpty()) + return title; + return static_cast<HTMLFrameElementBase*>(owner)->getNameAttribute(); + } + if (owner->isHTMLElement()) + return toHTMLElement(owner)->getNameAttribute(); + } + + String documentTitle = document->title(); + if (!documentTitle.isEmpty()) + return documentTitle; + + owner = document->body(); + if (owner && owner->isHTMLElement()) + return toHTMLElement(owner)->getNameAttribute(); + + return String(); +} + String AccessibilityNodeObject::accessibilityDescription() const { // Static text should not have a description, it should only have a stringValue. @@ -1186,7 +1435,9 @@ String AccessibilityNodeObject::textUnderElement() const // If this could be fixed, it'd be more accurate use TextIterator here. if (node->isElementNode()) return toElement(node)->innerText(); - + else if (node->isTextNode()) + return toText(node)->wholeText(); + return String(); } @@ -1341,6 +1592,14 @@ String AccessibilityNodeObject::accessibilityDescriptionForElements(Vector<Eleme return builder.toString(); } +String AccessibilityNodeObject::ariaDescribedByAttribute() const +{ + Vector<Element*> elements; + elementsFromAttribute(elements, aria_describedbyAttr); + + return accessibilityDescriptionForElements(elements); +} + void AccessibilityNodeObject::elementsFromAttribute(Vector<Element*>& elements, const QualifiedName& attribute) const { Node* node = this->node(); |