Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(235)

Unified Diff: Source/core/accessibility/AXNodeObject.cpp

Issue 713933002: Create Source/modules/accessibility/ and move most of core/accessibility/* into it (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: rebase Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « Source/core/accessibility/AXNodeObject.h ('k') | Source/core/accessibility/AXObject.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: Source/core/accessibility/AXNodeObject.cpp
diff --git a/Source/core/accessibility/AXNodeObject.cpp b/Source/core/accessibility/AXNodeObject.cpp
deleted file mode 100644
index 917f64a04c8d02f4d37d2d1937f2474c6f17f19c..0000000000000000000000000000000000000000
--- a/Source/core/accessibility/AXNodeObject.cpp
+++ /dev/null
@@ -1,1783 +0,0 @@
-/*
-* 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 "core/accessibility/AXNodeObject.h"
-
-#include "core/InputTypeNames.h"
-#include "core/accessibility/AXObjectCacheImpl.h"
-#include "core/dom/NodeTraversal.h"
-#include "core/dom/Text.h"
-#include "core/html/HTMLDListElement.h"
-#include "core/html/HTMLFieldSetElement.h"
-#include "core/html/HTMLFrameElementBase.h"
-#include "core/html/HTMLInputElement.h"
-#include "core/html/HTMLLabelElement.h"
-#include "core/html/HTMLLegendElement.h"
-#include "core/html/HTMLPlugInElement.h"
-#include "core/html/HTMLSelectElement.h"
-#include "core/html/HTMLTextAreaElement.h"
-#include "core/rendering/RenderObject.h"
-#include "platform/UserGestureIndicator.h"
-#include "wtf/text/StringBuilder.h"
-
-
-namespace blink {
-
-using namespace HTMLNames;
-
-AXNodeObject::AXNodeObject(Node* node)
- : AXObject()
- , m_ariaRole(UnknownRole)
- , m_childrenDirty(false)
-#if ENABLE(ASSERT)
- , m_initialized(false)
-#endif
- , m_node(node)
-{
-}
-
-PassRefPtr<AXNodeObject> AXNodeObject::create(Node* node)
-{
- return adoptRef(new AXNodeObject(node));
-}
-
-AXNodeObject::~AXNodeObject()
-{
- ASSERT(isDetached());
-}
-
-// This function implements the ARIA accessible name as described by the Mozilla
-// ARIA Implementer's Guide.
-static String accessibleNameForNode(Node* node)
-{
- if (!node)
- return String();
-
- if (node->isTextNode())
- return toText(node)->data();
-
- if (isHTMLInputElement(*node))
- return toHTMLInputElement(*node).value();
-
- if (node->isHTMLElement()) {
- const AtomicString& alt = toHTMLElement(node)->getAttribute(altAttr);
- if (!alt.isEmpty())
- return alt;
- }
-
- return String();
-}
-
-String AXNodeObject::accessibilityDescriptionForElements(WillBeHeapVector<RawPtrWillBeMember<Element> > &elements) const
-{
- StringBuilder builder;
- unsigned size = elements.size();
- for (unsigned i = 0; i < size; ++i) {
- Element* idElement = elements[i];
-
- builder.append(accessibleNameForNode(idElement));
- for (Node& n : NodeTraversal::descendantsOf(*idElement))
- builder.append(accessibleNameForNode(&n));
-
- if (i != size - 1)
- builder.append(' ');
- }
- return builder.toString();
-}
-
-void AXNodeObject::alterSliderValue(bool increase)
-{
- if (roleValue() != SliderRole)
- return;
-
- if (!getAttribute(stepAttr).isEmpty())
- changeValueByStep(increase);
- else
- changeValueByPercent(increase ? 5 : -5);
-}
-
-String AXNodeObject::ariaAccessibilityDescription() const
-{
- String ariaLabeledBy = ariaLabeledByAttribute();
- if (!ariaLabeledBy.isEmpty())
- return ariaLabeledBy;
-
- const AtomicString& ariaLabel = getAttribute(aria_labelAttr);
- if (!ariaLabel.isEmpty())
- return ariaLabel;
-
- return String();
-}
-
-
-void AXNodeObject::ariaLabeledByElements(WillBeHeapVector<RawPtrWillBeMember<Element> >& elements) const
-{
- elementsFromAttribute(elements, aria_labeledbyAttr);
- if (!elements.size())
- elementsFromAttribute(elements, aria_labelledbyAttr);
-}
-
-void AXNodeObject::changeValueByStep(bool increase)
-{
- float step = stepValueForRange();
- float value = valueForRange();
-
- value += increase ? step : -step;
-
- setValue(String::number(value));
-
- axObjectCache()->postNotification(node(), AXObjectCacheImpl::AXValueChanged, true);
-}
-
-bool AXNodeObject::computeAccessibilityIsIgnored() const
-{
-#if ENABLE(ASSERT)
- // Double-check that an AXObject is never accessed before
- // it's been initialized.
- ASSERT(m_initialized);
-#endif
-
- // If this element is within a parent that cannot have children, it should not be exposed.
- if (isDescendantOfBarrenParent())
- return true;
-
- // Ignore labels that are already referenced by a control's title UI element.
- AXObject* controlObject = correspondingControlForLabelElement();
- if (controlObject && !controlObject->exposesTitleUIElement() && controlObject->isCheckboxOrRadio())
- return true;
-
- return m_role == UnknownRole;
-}
-
-AccessibilityRole AXNodeObject::determineAccessibilityRole()
-{
- if (!node())
- return UnknownRole;
-
- m_ariaRole = determineAriaRoleAttribute();
-
- AccessibilityRole ariaRole = ariaRoleAttribute();
- if (ariaRole != UnknownRole)
- return ariaRole;
-
- if (node()->isLink())
- return LinkRole;
- if (node()->isTextNode())
- return StaticTextRole;
- if (isHTMLButtonElement(*node()))
- return buttonRoleType();
- if (isHTMLDetailsElement(*node()))
- return DetailsRole;
- if (isHTMLSummaryElement(*node())) {
- if (node()->parentNode() && isHTMLDetailsElement(node()->parentNode()))
- return DisclosureTriangleRole;
- return UnknownRole;
- }
-
- if (isHTMLInputElement(*node())) {
- HTMLInputElement& input = toHTMLInputElement(*node());
- const AtomicString& type = input.type();
- if (type == InputTypeNames::button) {
- if ((node()->parentNode() && isHTMLMenuElement(node()->parentNode())) || (parentObject() && parentObject()->roleValue() == MenuRole))
- return MenuItemRole;
- return buttonRoleType();
- }
- if (type == InputTypeNames::checkbox) {
- if ((node()->parentNode() && isHTMLMenuElement(node()->parentNode())) || (parentObject() && parentObject()->roleValue() == MenuRole))
- return MenuItemCheckBoxRole;
- return CheckBoxRole;
- }
- if (type == InputTypeNames::date)
- return DateRole;
- if (type == InputTypeNames::datetime
- || type == InputTypeNames::datetime_local
- || type == InputTypeNames::month
- || type == InputTypeNames::week)
- return DateTimeRole;
- if (type == InputTypeNames::radio) {
- if ((node()->parentNode() && isHTMLMenuElement(node()->parentNode())) || (parentObject() && parentObject()->roleValue() == MenuRole))
- return MenuItemRadioRole;
- return RadioButtonRole;
- }
- if (input.isTextButton())
- return buttonRoleType();
- if (type == InputTypeNames::range)
- return SliderRole;
- if (type == InputTypeNames::color)
- return ColorWellRole;
- if (type == InputTypeNames::time)
- return TimeRole;
- return TextFieldRole;
- }
- if (isHTMLSelectElement(*node())) {
- HTMLSelectElement& selectElement = toHTMLSelectElement(*node());
- return selectElement.multiple() ? ListBoxRole : PopUpButtonRole;
- }
- if (isHTMLTextAreaElement(*node()))
- return TextAreaRole;
- if (headingLevel())
- return HeadingRole;
- if (isHTMLDivElement(*node()))
- return DivRole;
- if (isHTMLMeterElement(*node()))
- return MeterRole;
- if (isHTMLOutputElement(*node()))
- return StatusRole;
- if (isHTMLParagraphElement(*node()))
- return ParagraphRole;
- if (isHTMLLabelElement(*node()))
- return LabelRole;
- if (isHTMLRubyElement(*node()))
- return RubyRole;
- if (isHTMLDListElement(*node()))
- return DescriptionListRole;
- if (node()->isElementNode() && node()->hasTagName(blockquoteTag))
- return BlockquoteRole;
- if (node()->isElementNode() && node()->hasTagName(figcaptionTag))
- return FigcaptionRole;
- if (node()->isElementNode() && node()->hasTagName(figureTag))
- return FigureRole;
- if (node()->isElementNode() && toElement(node())->isFocusable())
- return GroupRole;
- if (isHTMLAnchorElement(*node()) && isClickable())
- return LinkRole;
- if (isHTMLIFrameElement(*node()))
- return IframeRole;
- if (isEmbeddedObject())
- return EmbeddedObjectRole;
-
- return UnknownRole;
-}
-
-AccessibilityRole AXNodeObject::determineAriaRoleAttribute() const
-{
- const AtomicString& ariaRole = getAttribute(roleAttr);
- if (ariaRole.isNull() || ariaRole.isEmpty())
- return UnknownRole;
-
- AccessibilityRole role = ariaRoleToWebCoreRole(ariaRole);
-
- // ARIA states if an item can get focus, it should not be presentational.
- if ((role == NoneRole || role == PresentationalRole) && canSetFocusAttribute())
- return UnknownRole;
-
- if (role == ButtonRole)
- role = buttonRoleType();
-
- if (role == TextAreaRole && !ariaIsMultiline())
- role = TextFieldRole;
-
- role = remapAriaRoleDueToParent(role);
-
- if (role)
- return role;
-
- return UnknownRole;
-}
-
-void AXNodeObject::elementsFromAttribute(WillBeHeapVector<RawPtrWillBeMember<Element> >& elements, const QualifiedName& attribute) const
-{
- Node* node = this->node();
- if (!node || !node->isElementNode())
- return;
-
- TreeScope& scope = node->treeScope();
-
- String idList = getAttribute(attribute).string();
- if (idList.isEmpty())
- return;
-
- idList.replace('\n', ' ');
- Vector<String> idVector;
- idList.split(' ', idVector);
-
- unsigned size = idVector.size();
- for (unsigned i = 0; i < size; ++i) {
- AtomicString idName(idVector[i]);
- Element* idElement = scope.getElementById(idName);
- if (idElement)
- elements.append(idElement);
- }
-}
-
-// If you call node->hasEditableStyle() since that will return true if an ancestor is editable.
-// This only returns true if this is the element that actually has the contentEditable attribute set.
-bool AXNodeObject::hasContentEditableAttributeSet() const
-{
- if (!hasAttribute(contenteditableAttr))
- return false;
- const AtomicString& contentEditableValue = getAttribute(contenteditableAttr);
- // Both "true" (case-insensitive) and the empty string count as true.
- return contentEditableValue.isEmpty() || equalIgnoringCase(contentEditableValue, "true");
-}
-
-bool AXNodeObject::isDescendantOfBarrenParent() const
-{
- for (AXObject* object = parentObject(); object; object = object->parentObject()) {
- if (!object->canHaveChildren())
- return true;
- }
-
- return false;
-}
-
-bool AXNodeObject::isGenericFocusableElement() const
-{
- if (!canSetFocusAttribute())
- return false;
-
- // If it's a control, it's not generic.
- if (isControl())
- return false;
-
- // If it has an aria role, it's not generic.
- if (m_ariaRole != UnknownRole)
- return false;
-
- // If the content editable attribute is set on this element, that's the reason
- // it's focusable, and existing logic should handle this case already - so it's not a
- // generic focusable element.
-
- if (hasContentEditableAttributeSet())
- return false;
-
- // The web area and body element are both focusable, but existing logic handles these
- // cases already, so we don't need to include them here.
- if (roleValue() == WebAreaRole)
- return false;
- if (isHTMLBodyElement(node()))
- return false;
-
- // An SVG root is focusable by default, but it's probably not interactive, so don't
- // include it. It can still be made accessible by giving it an ARIA role.
- if (roleValue() == SVGRootRole)
- return false;
-
- return true;
-}
-
-HTMLLabelElement* AXNodeObject::labelForElement(Element* element) const
-{
- 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;
- }
-
- return Traversal<HTMLLabelElement>::firstAncestor(*element);
-}
-
-AXObject* AXNodeObject::menuButtonForMenu() const
-{
- Element* menuItem = menuItemElementForMenu();
-
- if (menuItem) {
- // ARIA just has generic menu items. AppKit needs to know if this is a top level items like MenuBarButton or MenuBarItem
- AXObject* menuItemAX = axObjectCache()->getOrCreate(menuItem);
- if (menuItemAX && menuItemAX->isMenuButton())
- return menuItemAX;
- }
- return 0;
-}
-
-static Element* siblingWithAriaRole(String role, Node* node)
-{
- Node* parent = node->parentNode();
- if (!parent)
- return 0;
-
- for (Element* sibling = ElementTraversal::firstChild(*parent); sibling; sibling = ElementTraversal::nextSibling(*sibling)) {
- const AtomicString& siblingAriaRole = sibling->getAttribute(roleAttr);
- if (equalIgnoringCase(siblingAriaRole, role))
- return sibling;
- }
-
- return 0;
-}
-
-Element* AXNodeObject::menuItemElementForMenu() const
-{
- if (ariaRoleAttribute() != MenuRole)
- return 0;
-
- return siblingWithAriaRole("menuitem", node());
-}
-
-Element* AXNodeObject::mouseButtonListener() const
-{
- Node* node = this->node();
- if (!node)
- return 0;
-
- // check if our parent is a mouse button listener
- if (!node->isElementNode())
- node = node->parentElement();
-
- if (!node)
- return 0;
-
- // FIXME: Do the continuation search like anchorElement does
- for (Element* element = toElement(node); element; element = element->parentElement()) {
- if (element->getAttributeEventListener(EventTypeNames::click) || element->getAttributeEventListener(EventTypeNames::mousedown) || element->getAttributeEventListener(EventTypeNames::mouseup))
- return element;
- }
-
- return 0;
-}
-
-AccessibilityRole AXNodeObject::remapAriaRoleDueToParent(AccessibilityRole role) const
-{
- // Some objects change their role based on their parent.
- // However, asking for the unignoredParent calls accessibilityIsIgnored(), which can trigger a loop.
- // While inside the call stack of creating an element, we need to avoid accessibilityIsIgnored().
- // https://bugs.webkit.org/show_bug.cgi?id=65174
-
- if (role != ListBoxOptionRole && role != MenuItemRole)
- return role;
-
- for (AXObject* parent = parentObject(); parent && !parent->accessibilityIsIgnored(); parent = parent->parentObject()) {
- AccessibilityRole parentAriaRole = parent->ariaRoleAttribute();
-
- // Selects and listboxes both have options as child roles, but they map to different roles within WebCore.
- if (role == ListBoxOptionRole && parentAriaRole == MenuRole)
- return MenuItemRole;
- // An aria "menuitem" may map to MenuButton or MenuItem depending on its parent.
- if (role == MenuItemRole && parentAriaRole == GroupRole)
- return MenuButtonRole;
-
- // If the parent had a different role, then we don't need to continue searching up the chain.
- if (parentAriaRole)
- break;
- }
-
- return role;
-}
-
-void AXNodeObject::init()
-{
-#if ENABLE(ASSERT)
- ASSERT(!m_initialized);
- m_initialized = true;
-#endif
- m_role = determineAccessibilityRole();
-}
-
-void AXNodeObject::detach()
-{
- clearChildren();
- AXObject::detach();
- m_node = 0;
-}
-
-bool AXNodeObject::isAnchor() const
-{
- return !isNativeImage() && isLink();
-}
-
-bool AXNodeObject::isControl() const
-{
- Node* node = this->node();
- if (!node)
- return false;
-
- return ((node->isElementNode() && toElement(node)->isFormControlElement())
- || AXObject::isARIAControl(ariaRoleAttribute()));
-}
-
-bool AXNodeObject::isEmbeddedObject() const
-{
- return isHTMLPlugInElement(node());
-}
-
-bool AXNodeObject::isFieldset() const
-{
- return isHTMLFieldSetElement(node());
-}
-
-bool AXNodeObject::isHeading() const
-{
- return roleValue() == HeadingRole;
-}
-
-bool AXNodeObject::isHovered() const
-{
- Node* node = this->node();
- if (!node)
- return false;
-
- return node->hovered();
-}
-
-bool AXNodeObject::isImage() const
-{
- return roleValue() == ImageRole;
-}
-
-bool AXNodeObject::isImageButton() const
-{
- return isNativeImage() && isButton();
-}
-
-bool AXNodeObject::isInputImage() const
-{
- Node* node = this->node();
- if (roleValue() == ButtonRole && isHTMLInputElement(node))
- return toHTMLInputElement(*node).type() == InputTypeNames::image;
-
- return false;
-}
-
-bool AXNodeObject::isLink() const
-{
- return roleValue() == LinkRole;
-}
-
-bool AXNodeObject::isMenu() const
-{
- return roleValue() == MenuRole;
-}
-
-bool AXNodeObject::isMenuButton() const
-{
- return roleValue() == MenuButtonRole;
-}
-
-bool AXNodeObject::isMultiSelectable() const
-{
- const AtomicString& ariaMultiSelectable = getAttribute(aria_multiselectableAttr);
- if (equalIgnoringCase(ariaMultiSelectable, "true"))
- return true;
- if (equalIgnoringCase(ariaMultiSelectable, "false"))
- return false;
-
- return isHTMLSelectElement(node()) && toHTMLSelectElement(*node()).multiple();
-}
-
-bool AXNodeObject::isNativeCheckboxOrRadio() const
-{
- Node* node = this->node();
- if (!isHTMLInputElement(node))
- return false;
-
- HTMLInputElement* input = toHTMLInputElement(node);
- return input->type() == InputTypeNames::checkbox || input->type() == InputTypeNames::radio;
-}
-
-bool AXNodeObject::isNativeImage() const
-{
- Node* node = this->node();
- if (!node)
- return false;
-
- if (isHTMLImageElement(*node))
- return true;
-
- if (isHTMLPlugInElement(*node))
- return true;
-
- if (isHTMLInputElement(*node))
- return toHTMLInputElement(*node).type() == InputTypeNames::image;
-
- return false;
-}
-
-bool AXNodeObject::isNativeTextControl() const
-{
- Node* node = this->node();
- if (!node)
- return false;
-
- if (isHTMLTextAreaElement(*node))
- return true;
-
- if (isHTMLInputElement(*node))
- return toHTMLInputElement(node)->isTextField();
-
- return false;
-}
-
-bool AXNodeObject::isNonNativeTextControl() const
-{
- if (isNativeTextControl())
- return false;
-
- if (hasContentEditableAttributeSet())
- return true;
-
- if (isARIATextControl())
- return true;
-
- return false;
-}
-
-bool AXNodeObject::isPasswordField() const
-{
- Node* node = this->node();
- if (!isHTMLInputElement(node))
- return false;
-
- if (ariaRoleAttribute() != UnknownRole)
- return false;
-
- return toHTMLInputElement(node)->type() == InputTypeNames::password;
-}
-
-bool AXNodeObject::isProgressIndicator() const
-{
- return roleValue() == ProgressIndicatorRole;
-}
-
-bool AXNodeObject::isSlider() const
-{
- return roleValue() == SliderRole;
-}
-
-bool AXNodeObject::isChecked() const
-{
- Node* node = this->node();
- if (!node)
- return false;
-
- // First test for native checkedness semantics
- if (isHTMLInputElement(*node))
- return toHTMLInputElement(*node).shouldAppearChecked();
-
- // Else, if this is an ARIA checkbox or radio OR ARIA role menuitemcheckbox
- // or menuitemradio, respect the aria-checked attribute
- AccessibilityRole ariaRole = ariaRoleAttribute();
- if (ariaRole == RadioButtonRole || ariaRole == CheckBoxRole
- || ariaRole == MenuItemCheckBoxRole || ariaRole == MenuItemRadioRole) {
- if (equalIgnoringCase(getAttribute(aria_checkedAttr), "true"))
- return true;
- return false;
- }
-
- // Otherwise it's not checked
- return false;
-}
-
-bool AXNodeObject::isClickable() const
-{
- if (node()) {
- if (node()->isElementNode() && toElement(node())->isDisabledFormControl())
- return false;
-
- // Note: we can't call node()->willRespondToMouseClickEvents() because that triggers a style recalc and can delete this.
- if (node()->hasEventListeners(EventTypeNames::mouseup) || node()->hasEventListeners(EventTypeNames::mousedown) || node()->hasEventListeners(EventTypeNames::click) || node()->hasEventListeners(EventTypeNames::DOMActivate))
- return true;
- }
-
- return AXObject::isClickable();
-}
-
-bool AXNodeObject::isEnabled() const
-{
- if (equalIgnoringCase(getAttribute(aria_disabledAttr), "true"))
- return false;
-
- Node* node = this->node();
- if (!node || !node->isElementNode())
- return true;
-
- return !toElement(node)->isDisabledFormControl();
-}
-
-AccessibilityExpanded AXNodeObject::isExpanded() const
-{
- const AtomicString& expanded = getAttribute(aria_expandedAttr);
- if (equalIgnoringCase(expanded, "true"))
- return ExpandedExpanded;
- if (equalIgnoringCase(expanded, "false"))
- return ExpandedCollapsed;
-
- return ExpandedUndefined;
-}
-
-bool AXNodeObject::isIndeterminate() const
-{
- Node* node = this->node();
- if (!isHTMLInputElement(node))
- return false;
-
- return toHTMLInputElement(node)->shouldAppearIndeterminate();
-}
-
-bool AXNodeObject::isPressed() const
-{
- if (!isButton())
- return false;
-
- Node* node = this->node();
- if (!node)
- return false;
-
- // If this is an ARIA button, check the aria-pressed attribute rather than node()->active()
- if (ariaRoleAttribute() == ButtonRole) {
- if (equalIgnoringCase(getAttribute(aria_pressedAttr), "true"))
- return true;
- return false;
- }
-
- return node->active();
-}
-
-bool AXNodeObject::isReadOnly() const
-{
- Node* node = this->node();
- if (!node)
- return true;
-
- if (isHTMLTextAreaElement(*node))
- return toHTMLTextAreaElement(*node).isReadOnly();
-
- if (isHTMLInputElement(*node)) {
- HTMLInputElement& input = toHTMLInputElement(*node);
- if (input.isTextField())
- return input.isReadOnly();
- }
-
- return !node->hasEditableStyle();
-}
-
-bool AXNodeObject::isRequired() const
-{
- if (equalIgnoringCase(getAttribute(aria_requiredAttr), "true"))
- return true;
-
- Node* n = this->node();
- if (n && (n->isElementNode() && toElement(n)->isFormControlElement()))
- return toHTMLFormControlElement(n)->isRequired();
-
- return false;
-}
-
-bool AXNodeObject::canSetFocusAttribute() const
-{
- Node* node = this->node();
- if (!node)
- return false;
-
- if (isWebArea())
- return true;
-
- // NOTE: It would be more accurate to ask the document whether setFocusedNode() would
- // do anything. For example, setFocusedNode() will do nothing if the current focused
- // node will not relinquish the focus.
- if (!node)
- return false;
-
- if (isDisabledFormControl(node))
- return false;
-
- return node->isElementNode() && toElement(node)->supportsFocus();
-}
-
-bool AXNodeObject::canSetValueAttribute() const
-{
- if (equalIgnoringCase(getAttribute(aria_readonlyAttr), "true"))
- return false;
-
- if (isProgressIndicator() || isSlider())
- return true;
-
- if (isTextControl() && !isNativeTextControl())
- return true;
-
- // Any node could be contenteditable, so isReadOnly should be relied upon
- // for this information for all elements.
- return !isReadOnly();
-}
-
-bool AXNodeObject::canvasHasFallbackContent() const
-{
- Node* node = this->node();
- if (!isHTMLCanvasElement(node))
- return false;
-
- // If it has any children that are elements, we'll assume it might be fallback
- // content. If it has no children or its only children are not elements
- // (e.g. just text nodes), it doesn't have fallback content.
- return ElementTraversal::firstChild(*node);
-}
-
-bool AXNodeObject::exposesTitleUIElement() const
-{
- if (!isControl())
- return false;
-
- // If this control is ignored (because it's invisible),
- // then the label needs to be exposed so it can be visible to accessibility.
- if (accessibilityIsIgnored())
- return true;
-
- // ARIA: section 2A, bullet #3 says if aria-labeledby or aria-label appears, it should
- // override the "label" element association.
- bool hasTextAlternative = (!ariaLabeledByAttribute().isEmpty() || !getAttribute(aria_labelAttr).isEmpty());
-
- // Checkboxes and radio buttons use the text of their title ui element as their own AXTitle.
- // This code controls whether the title ui element should appear in the AX tree (usually, no).
- // It should appear if the control already has a label (which will be used as the AXTitle instead).
- if (isCheckboxOrRadio())
- return hasTextAlternative;
-
- // When controls have their own descriptions, the title element should be ignored.
- if (hasTextAlternative)
- return false;
-
- return true;
-}
-
-int AXNodeObject::headingLevel() const
-{
- // headings can be in block flow and non-block flow
- Node* node = this->node();
- if (!node)
- return 0;
-
- if (ariaRoleAttribute() == HeadingRole)
- return getAttribute(aria_levelAttr).toInt();
-
- if (!node->isHTMLElement())
- return 0;
-
- HTMLElement& element = toHTMLElement(*node);
- if (element.hasTagName(h1Tag))
- return 1;
-
- if (element.hasTagName(h2Tag))
- return 2;
-
- if (element.hasTagName(h3Tag))
- return 3;
-
- if (element.hasTagName(h4Tag))
- return 4;
-
- if (element.hasTagName(h5Tag))
- return 5;
-
- if (element.hasTagName(h6Tag))
- return 6;
-
- return 0;
-}
-
-unsigned AXNodeObject::hierarchicalLevel() const
-{
- Node* node = this->node();
- if (!node || !node->isElementNode())
- return 0;
- Element* element = toElement(node);
- String ariaLevel = element->getAttribute(aria_levelAttr);
- if (!ariaLevel.isEmpty())
- return ariaLevel.toInt();
-
- // Only tree item will calculate its level through the DOM currently.
- if (roleValue() != TreeItemRole)
- return 0;
-
- // Hierarchy leveling starts at 1, to match the aria-level spec.
- // We measure tree hierarchy by the number of groups that the item is within.
- unsigned level = 1;
- for (AXObject* parent = parentObject(); parent; parent = parent->parentObject()) {
- AccessibilityRole parentRole = parent->roleValue();
- if (parentRole == GroupRole)
- level++;
- else if (parentRole == TreeRole)
- break;
- }
-
- return level;
-}
-
-String AXNodeObject::text() const
-{
- // If this is a user defined static text, use the accessible name computation.
- if (ariaRoleAttribute() == StaticTextRole)
- return ariaAccessibilityDescription();
-
- if (!isTextControl())
- return String();
-
- Node* node = this->node();
- if (!node)
- return String();
-
- if (isNativeTextControl() && (isHTMLTextAreaElement(*node) || isHTMLInputElement(*node)))
- return toHTMLTextFormControlElement(*node).value();
-
- if (!node->isElementNode())
- return String();
-
- return toElement(node)->innerText();
-}
-
-AXObject* AXNodeObject::titleUIElement() const
-{
- if (!node() || !node()->isElementNode())
- return 0;
-
- if (isFieldset())
- return axObjectCache()->getOrCreate(toHTMLFieldSetElement(node())->legend());
-
- HTMLLabelElement* label = labelForElement(toElement(node()));
- if (label)
- return axObjectCache()->getOrCreate(label);
-
- return 0;
-}
-
-AccessibilityButtonState AXNodeObject::checkboxOrRadioValue() const
-{
- if (isNativeCheckboxOrRadio())
- return isChecked() ? ButtonStateOn : ButtonStateOff;
-
- return AXObject::checkboxOrRadioValue();
-}
-
-void AXNodeObject::colorValue(int& r, int& g, int& b) const
-{
- r = 0;
- g = 0;
- b = 0;
-
- if (!isColorWell())
- return;
-
- if (!isHTMLInputElement(node()))
- return;
-
- HTMLInputElement* input = toHTMLInputElement(node());
- const AtomicString& type = input->getAttribute(typeAttr);
- if (!equalIgnoringCase(type, "color"))
- return;
-
- // HTMLInputElement::value always returns a string parseable by Color.
- Color color;
- bool success = color.setFromString(input->value());
- ASSERT_UNUSED(success, success);
- r = color.red();
- g = color.green();
- b = color.blue();
-}
-
-String AXNodeObject::valueDescription() const
-{
- if (!supportsRangeValue())
- return String();
-
- return getAttribute(aria_valuetextAttr).string();
-}
-
-float AXNodeObject::valueForRange() const
-{
- if (hasAttribute(aria_valuenowAttr))
- return getAttribute(aria_valuenowAttr).toFloat();
-
- if (isHTMLInputElement(node())) {
- HTMLInputElement& input = toHTMLInputElement(*node());
- if (input.type() == InputTypeNames::range)
- return input.valueAsNumber();
- }
-
- return 0.0;
-}
-
-float AXNodeObject::maxValueForRange() const
-{
- if (hasAttribute(aria_valuemaxAttr))
- return getAttribute(aria_valuemaxAttr).toFloat();
-
- if (isHTMLInputElement(node())) {
- HTMLInputElement& input = toHTMLInputElement(*node());
- if (input.type() == InputTypeNames::range)
- return input.maximum();
- }
-
- return 0.0;
-}
-
-float AXNodeObject::minValueForRange() const
-{
- if (hasAttribute(aria_valueminAttr))
- return getAttribute(aria_valueminAttr).toFloat();
-
- if (isHTMLInputElement(node())) {
- HTMLInputElement& input = toHTMLInputElement(*node());
- if (input.type() == InputTypeNames::range)
- return input.minimum();
- }
-
- return 0.0;
-}
-
-float AXNodeObject::stepValueForRange() const
-{
- return getAttribute(stepAttr).toFloat();
-}
-
-String AXNodeObject::stringValue() const
-{
- Node* node = this->node();
- if (!node)
- return String();
-
- if (ariaRoleAttribute() == StaticTextRole) {
- String staticText = text();
- if (!staticText.length())
- staticText = textUnderElement();
- return staticText;
- }
-
- if (node->isTextNode())
- return textUnderElement();
-
- if (isHTMLSelectElement(*node)) {
- HTMLSelectElement& selectElement = toHTMLSelectElement(*node);
- int selectedIndex = selectElement.selectedIndex();
- const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& listItems = selectElement.listItems();
- if (selectedIndex >= 0 && static_cast<size_t>(selectedIndex) < listItems.size()) {
- const AtomicString& overriddenDescription = listItems[selectedIndex]->fastGetAttribute(aria_labelAttr);
- if (!overriddenDescription.isNull())
- return overriddenDescription;
- }
- if (!selectElement.multiple())
- return selectElement.value();
- return String();
- }
-
- if (isTextControl())
- return text();
-
- // FIXME: We might need to implement a value here for more types
- // FIXME: It would be better not to advertise a value at all for the types for which we don't implement one;
- // this would require subclassing or making accessibilityAttributeNames do something other than return a
- // single static array.
- return String();
-}
-
-
-const AtomicString& AXNodeObject::textInputType() const
-{
- Node* node = this->node();
- if (!isHTMLInputElement(node))
- return nullAtom;
-
- HTMLInputElement& input = toHTMLInputElement(*node);
- if (input.isTextField())
- return input.type();
- return nullAtom;
-}
-
-String AXNodeObject::ariaDescribedByAttribute() const
-{
- WillBeHeapVector<RawPtrWillBeMember<Element> > elements;
- elementsFromAttribute(elements, aria_describedbyAttr);
-
- return accessibilityDescriptionForElements(elements);
-}
-
-String AXNodeObject::ariaLabeledByAttribute() const
-{
- WillBeHeapVector<RawPtrWillBeMember<Element> > elements;
- ariaLabeledByElements(elements);
-
- return accessibilityDescriptionForElements(elements);
-}
-
-AccessibilityRole AXNodeObject::ariaRoleAttribute() const
-{
- return m_ariaRole;
-}
-
-// When building the textUnderElement for an object, determine whether or not
-// we should include the inner text of this given descendant object or skip it.
-static bool shouldUseAccessibilityObjectInnerText(AXObject* obj)
-{
- // Consider this hypothetical example:
- // <div tabindex=0>
- // <h2>
- // Table of contents
- // </h2>
- // <a href="#start">Jump to start of book</a>
- // <ul>
- // <li><a href="#1">Chapter 1</a></li>
- // <li><a href="#1">Chapter 2</a></li>
- // </ul>
- // </div>
- //
- // The goal is to return a reasonable title for the outer container div, because
- // it's focusable - but without making its title be the full inner text, which is
- // quite long. As a heuristic, skip links, controls, and elements that are usually
- // containers with lots of children.
-
- // Skip hidden children
- if (obj->isInertOrAriaHidden())
- return false;
-
- // Skip focusable children, so we don't include the text of links and controls.
- if (obj->canSetFocusAttribute())
- return false;
-
- // Skip big container elements like lists, tables, etc.
- if (obj->isList() || obj->isAXTable() || obj->isTree() || obj->isCanvas())
- return false;
-
- return true;
-}
-
-String AXNodeObject::textUnderElement() const
-{
- Node* node = this->node();
- if (node && node->isTextNode())
- return toText(node)->wholeText();
-
- StringBuilder builder;
- for (AXObject* child = firstChild(); child; child = child->nextSibling()) {
- if (!shouldUseAccessibilityObjectInnerText(child))
- continue;
-
- if (child->isAXNodeObject()) {
- Vector<AccessibilityText> textOrder;
- toAXNodeObject(child)->alternativeText(textOrder);
- if (textOrder.size() > 0) {
- builder.append(textOrder[0].text);
- continue;
- }
- }
-
- builder.append(child->textUnderElement());
- }
-
- return builder.toString();
-}
-
-String AXNodeObject::accessibilityDescription() const
-{
- // Static text should not have a description, it should only have a stringValue.
- if (roleValue() == StaticTextRole)
- return String();
-
- String ariaDescription = ariaAccessibilityDescription();
- if (!ariaDescription.isEmpty())
- return ariaDescription;
-
- 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())
- return alt;
- }
-
- // An element's descriptive text is comprised of title() (what's visible on the screen) and accessibilityDescription() (other descriptive text).
- // Both are used to generate what a screen reader speaks.
- // If this point is reached (i.e. there's no accessibilityDescription) and there's no title(), we should fallback to using the title attribute.
- // The title attribute is normally used as help text (because it is a tooltip), but if there is nothing else available, this should be used (according to ARIA).
- if (title().isEmpty())
- return getAttribute(titleAttr);
-
- return String();
-}
-
-String AXNodeObject::title() const
-{
- Node* node = this->node();
- if (!node)
- return String();
-
- bool isInputElement = isHTMLInputElement(*node);
- if (isInputElement) {
- HTMLInputElement& input = toHTMLInputElement(*node);
- if (input.isTextButton())
- return input.valueWithDefault();
- }
-
- if (isInputElement || AXObject::isARIAInput(ariaRoleAttribute()) || isControl()) {
- HTMLLabelElement* label = labelForElement(toElement(node));
- if (label && !exposesTitleUIElement())
- return label->innerText();
- }
-
- // If this node isn't rendered, there's no inner text we can extract from a select element.
- if (!isAXRenderObject() && isHTMLSelectElement(*node))
- return String();
-
- switch (roleValue()) {
- case PopUpButtonRole:
- // Native popup buttons should not use their button children's text as a title. That value is retrieved through stringValue().
- if (isHTMLSelectElement(*node))
- return String();
- case ButtonRole:
- case ToggleButtonRole:
- case CheckBoxRole:
- case ListBoxOptionRole:
- case MenuButtonRole:
- case MenuItemRole:
- case MenuItemCheckBoxRole:
- case MenuItemRadioRole:
- case RadioButtonRole:
- case TabRole:
- return textUnderElement();
- // SVGRoots should not use the text under itself as a title. That could include the text of objects like <text>.
- case SVGRootRole:
- return String();
- default:
- break;
- }
-
- if (isHeading() || isLink())
- return textUnderElement();
-
- // 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 (isGenericFocusableElement())
- return textUnderElement();
-
- return String();
-}
-
-String AXNodeObject::helpText() const
-{
- Node* node = this->node();
- if (!node)
- return String();
-
- const AtomicString& ariaHelp = getAttribute(aria_helpAttr);
- if (!ariaHelp.isEmpty())
- return ariaHelp;
-
- String describedBy = ariaDescribedByAttribute();
- if (!describedBy.isEmpty())
- return describedBy;
-
- String description = accessibilityDescription();
- for (Node* curr = node; curr; curr = curr->parentNode()) {
- if (curr->isHTMLElement()) {
- const AtomicString& summary = toElement(curr)->getAttribute(summaryAttr);
- if (!summary.isEmpty())
- return summary;
-
- // The title attribute should be used as help text unless it is already being used as descriptive text.
- const AtomicString& title = toElement(curr)->getAttribute(titleAttr);
- if (!title.isEmpty() && description != title)
- return title;
- }
-
- // 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.
- AXObject* axObj = axObjectCache()->getOrCreate(curr);
- if (axObj) {
- AccessibilityRole role = axObj->roleValue();
- if (role != GroupRole && role != UnknownRole)
- break;
- }
- }
-
- return String();
-}
-
-LayoutRect AXNodeObject::elementRect() const
-{
- // First check if it has a custom rect, for example if this element is tied to a canvas path.
- if (!m_explicitElementRect.isEmpty())
- return m_explicitElementRect;
-
- // FIXME: If there are a lot of elements in the canvas, it will be inefficient.
- // We can avoid the inefficient calculations by using AXComputedObjectAttributeCache.
- if (node()->parentElement()->isInCanvasSubtree()) {
- LayoutRect rect;
-
- for (Node* child = node()->firstChild(); child; child = child->nextSibling()) {
- if (child->isHTMLElement()) {
- if (AXObject* obj = axObjectCache()->get(child)) {
- if (rect.isEmpty())
- rect = obj->elementRect();
- else
- rect.unite(obj->elementRect());
- }
- }
- }
-
- if (!rect.isEmpty())
- return rect;
- }
-
- // If this object doesn't have an explicit element rect or computable from its children,
- // 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 (AXObject* positionProvider = parentObject(); positionProvider; positionProvider = positionProvider->parentObject()) {
- if (positionProvider->isAXRenderObject()) {
- LayoutRect parentRect = positionProvider->elementRect();
- boundingBox.setSize(LayoutSize(parentRect.width(), LayoutUnit(std::min(10.0f, parentRect.height().toFloat()))));
- boundingBox.setLocation(parentRect.location());
- break;
- }
- }
-
- return boundingBox;
-}
-
-AXObject* AXNodeObject::computeParent() const
-{
- if (!node())
- return 0;
-
- Node* parentObj = node()->parentNode();
- if (parentObj)
- return axObjectCache()->getOrCreate(parentObj);
-
- return 0;
-}
-
-AXObject* AXNodeObject::computeParentIfExists() const
-{
- if (!node())
- return 0;
-
- Node* parentObj = node()->parentNode();
- if (parentObj)
- return axObjectCache()->get(parentObj);
-
- return 0;
-}
-
-AXObject* AXNodeObject::firstChild() const
-{
- if (!node())
- return 0;
-
- Node* firstChild = node()->firstChild();
-
- if (!firstChild)
- return 0;
-
- return axObjectCache()->getOrCreate(firstChild);
-}
-
-AXObject* AXNodeObject::nextSibling() const
-{
- if (!node())
- return 0;
-
- Node* nextSibling = node()->nextSibling();
- if (!nextSibling)
- return 0;
-
- return axObjectCache()->getOrCreate(nextSibling);
-}
-
-void AXNodeObject::addChildren()
-{
- // If the need to add more children in addition to existing children arises,
- // childrenChanged should have been called, leaving the object with no children.
- ASSERT(!m_haveChildren);
-
- if (!m_node)
- return;
-
- m_haveChildren = true;
-
- // The only time we add children from the DOM tree to a node with a renderer is when it's a canvas.
- if (renderer() && !isHTMLCanvasElement(*m_node))
- return;
-
- for (Node* child = m_node->firstChild(); child; child = child->nextSibling())
- addChild(axObjectCache()->getOrCreate(child));
-
- for (unsigned i = 0; i < m_children.size(); ++i)
- m_children[i].get()->setParent(this);
-}
-
-void AXNodeObject::addChild(AXObject* child)
-{
- insertChild(child, m_children.size());
-}
-
-void AXNodeObject::insertChild(AXObject* 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);
- }
-}
-
-bool AXNodeObject::canHaveChildren() const
-{
- // If this is an AXRenderObject, then it's okay if this object
- // doesn't have a node - there are some renderers that don't have associated
- // nodes, like scroll areas and css-generated text.
- if (!node() && !isAXRenderObject())
- return false;
-
- // Elements that should not have children
- switch (roleValue()) {
- case ImageRole:
- case ButtonRole:
- case PopUpButtonRole:
- case CheckBoxRole:
- case RadioButtonRole:
- case TabRole:
- case ToggleButtonRole:
- case ListBoxOptionRole:
- case ScrollBarRole:
- return false;
- case StaticTextRole:
- if (!axObjectCache()->inlineTextBoxAccessibilityEnabled())
- return false;
- default:
- return true;
- }
-}
-
-Element* AXNodeObject::actionElement() const
-{
- Node* node = this->node();
- if (!node)
- return 0;
-
- if (isHTMLInputElement(*node)) {
- HTMLInputElement& input = toHTMLInputElement(*node);
- if (!input.isDisabledFormControl() && (isCheckboxOrRadio() || input.isTextButton()))
- return &input;
- } else if (isHTMLButtonElement(*node)) {
- return toElement(node);
- }
-
- if (isFileUploadButton())
- return toElement(node);
-
- if (AXObject::isARIAInput(ariaRoleAttribute()))
- return toElement(node);
-
- if (isImageButton())
- return toElement(node);
-
- if (isHTMLSelectElement(*node))
- return toElement(node);
-
- switch (roleValue()) {
- case ButtonRole:
- case PopUpButtonRole:
- case ToggleButtonRole:
- case TabRole:
- case MenuItemRole:
- case MenuItemCheckBoxRole:
- case MenuItemRadioRole:
- case ListItemRole:
- return toElement(node);
- default:
- break;
- }
-
- Element* elt = anchorElement();
- if (!elt)
- elt = mouseButtonListener();
- return elt;
-}
-
-Element* AXNodeObject::anchorElement() const
-{
- Node* node = this->node();
- if (!node)
- return 0;
-
- AXObjectCacheImpl* cache = axObjectCache();
-
- // search up the DOM tree for an anchor element
- // NOTE: this assumes that any non-image with an anchor is an HTMLAnchorElement
- for ( ; node; node = node->parentNode()) {
- if (isHTMLAnchorElement(*node) || (node->renderer() && cache->getOrCreate(node->renderer())->isAnchor()))
- return toElement(node);
- }
-
- return 0;
-}
-
-Document* AXNodeObject::document() const
-{
- if (!node())
- return 0;
- return &node()->document();
-}
-
-void AXNodeObject::setNode(Node* node)
-{
- m_node = node;
-}
-
-AXObject* AXNodeObject::correspondingControlForLabelElement() const
-{
- HTMLLabelElement* labelElement = labelElementContainer();
- if (!labelElement)
- return 0;
-
- HTMLElement* correspondingControl = labelElement->control();
- if (!correspondingControl)
- return 0;
-
- // Make sure the corresponding control isn't a descendant of this label
- // that's in the middle of being destroyed.
- if (correspondingControl->renderer() && !correspondingControl->renderer()->parent())
- return 0;
-
- return axObjectCache()->getOrCreate(correspondingControl);
-}
-
-HTMLLabelElement* AXNodeObject::labelElementContainer() const
-{
- if (!node())
- return 0;
-
- // the control element should not be considered part of the label
- if (isControl())
- return 0;
-
- // find if this has a ancestor that is a label
- return Traversal<HTMLLabelElement>::firstAncestorOrSelf(*node());
-}
-
-void AXNodeObject::setFocused(bool on)
-{
- if (!canSetFocusAttribute())
- return;
-
- Document* document = this->document();
- if (!on) {
- document->setFocusedElement(nullptr);
- } else {
- Node* node = this->node();
- if (node && node->isElementNode()) {
- // If this node is already the currently focused node, then calling focus() won't do anything.
- // That is a problem when focus is removed from the webpage to chrome, and then returns.
- // In these cases, we need to do what keyboard and mouse focus do, which is reset focus first.
- if (document->focusedElement() == node)
- document->setFocusedElement(nullptr);
-
- toElement(node)->focus();
- } else {
- document->setFocusedElement(nullptr);
- }
- }
-}
-
-void AXNodeObject::increment()
-{
- UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
- alterSliderValue(true);
-}
-
-void AXNodeObject::decrement()
-{
- UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
- alterSliderValue(false);
-}
-
-void AXNodeObject::childrenChanged()
-{
- // This method is meant as a quick way of marking a portion of the accessibility tree dirty.
- if (!node() && !renderer())
- return;
-
- axObjectCache()->postNotification(this, document(), AXObjectCacheImpl::AXChildrenChanged, true);
-
- // Go up the accessibility parent chain, but only if the element already exists. This method is
- // called during render layouts, minimal work should be done.
- // If AX elements are created now, they could interrogate the render tree while it's in a funky state.
- // At the same time, process ARIA live region changes.
- for (AXObject* parent = this; parent; parent = parent->parentObjectIfExists()) {
- parent->setNeedsToUpdateChildren();
-
- // These notifications always need to be sent because screenreaders are reliant on them to perform.
- // In other words, they need to be sent even when the screen reader has not accessed this live region since the last update.
-
- // If this element supports ARIA live regions, then notify the AT of changes.
- if (parent->isLiveRegion())
- axObjectCache()->postNotification(parent, parent->document(), AXObjectCacheImpl::AXLiveRegionChanged, true);
-
- // If this element is an ARIA text box or content editable, post a "value changed" notification on it
- // so that it behaves just like a native input element or textarea.
- if (isNonNativeTextControl())
- axObjectCache()->postNotification(parent, parent->document(), AXObjectCacheImpl::AXValueChanged, true);
- }
-}
-
-void AXNodeObject::selectionChanged()
-{
- // Post the selected text changed event on the first ancestor that's
- // focused (to handle form controls, ARIA text boxes and contentEditable),
- // or the web area if the selection is just in the document somewhere.
- if (isFocused() || isWebArea())
- axObjectCache()->postNotification(this, document(), AXObjectCacheImpl::AXSelectedTextChanged, true);
- else
- AXObject::selectionChanged(); // Calls selectionChanged on parent.
-}
-
-void AXNodeObject::textChanged()
-{
- // If this element supports ARIA live regions, or is part of a region with an ARIA editable role,
- // then notify the AT of changes.
- AXObjectCacheImpl* cache = axObjectCache();
- for (Node* parentNode = node(); parentNode; parentNode = parentNode->parentNode()) {
- AXObject* parent = cache->get(parentNode);
- if (!parent)
- continue;
-
- if (parent->isLiveRegion())
- cache->postNotification(parentNode, AXObjectCacheImpl::AXLiveRegionChanged, true);
-
- // If this element is an ARIA text box or content editable, post a "value changed" notification on it
- // so that it behaves just like a native input element or textarea.
- if (parent->isNonNativeTextControl())
- cache->postNotification(parentNode, AXObjectCacheImpl::AXValueChanged, true);
- }
-}
-
-void AXNodeObject::updateAccessibilityRole()
-{
- bool ignoredStatus = accessibilityIsIgnored();
- m_role = determineAccessibilityRole();
-
- // The AX hierarchy only needs to be updated if the ignored status of an element has changed.
- if (ignoredStatus != accessibilityIsIgnored())
- childrenChanged();
-}
-
-String AXNodeObject::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;
- }
-
- if (HTMLFrameOwnerElement* owner = document->ownerElement()) {
- if (isHTMLFrameElementBase(*owner)) {
- const AtomicString& title = owner->getAttribute(titleAttr);
- if (!title.isEmpty())
- return title;
- }
- return owner->getNameAttribute();
- }
-
- String documentTitle = document->title();
- if (!documentTitle.isEmpty())
- return documentTitle;
-
- if (HTMLElement* body = document->body())
- return body->getNameAttribute();
-
- return String();
-}
-
-void AXNodeObject::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));
- }
-}
-
-void AXNodeObject::ariaLabeledByText(Vector<AccessibilityText>& textOrder) const
-{
- String ariaLabeledBy = ariaLabeledByAttribute();
- if (!ariaLabeledBy.isEmpty()) {
- WillBeHeapVector<RawPtrWillBeMember<Element> > elements;
- ariaLabeledByElements(elements);
-
- unsigned length = elements.size();
- for (unsigned k = 0; k < length; k++) {
- RefPtr<AXObject> axElement = axObjectCache()->getOrCreate(elements[k]);
- textOrder.append(AccessibilityText(ariaLabeledBy, AlternativeText, axElement));
- }
- }
-}
-
-void AXNodeObject::changeValueByPercent(float percentChange)
-{
- float range = maxValueForRange() - minValueForRange();
- float value = valueForRange();
-
- value += range * (percentChange / 100);
- setValue(String::number(value));
-
- axObjectCache()->postNotification(node(), AXObjectCacheImpl::AXValueChanged, true);
-}
-
-} // namespace blink
« no previous file with comments | « Source/core/accessibility/AXNodeObject.h ('k') | Source/core/accessibility/AXObject.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698