Index: Source/core/accessibility/AXRenderObject.cpp |
diff --git a/Source/core/accessibility/AXRenderObject.cpp b/Source/core/accessibility/AXRenderObject.cpp |
deleted file mode 100644 |
index efe92b306a943aa55dd907236aceae31105bb6f0..0000000000000000000000000000000000000000 |
--- a/Source/core/accessibility/AXRenderObject.cpp |
+++ /dev/null |
@@ -1,2444 +0,0 @@ |
-/* |
-* Copyright (C) 2008 Apple 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/AXRenderObject.h" |
- |
-#include "bindings/core/v8/ExceptionStatePlaceholder.h" |
-#include "core/InputTypeNames.h" |
-#include "core/accessibility/AXImageMapLink.h" |
-#include "core/accessibility/AXInlineTextBox.h" |
-#include "core/accessibility/AXObjectCacheImpl.h" |
-#include "core/accessibility/AXSVGRoot.h" |
-#include "core/accessibility/AXSpinButton.h" |
-#include "core/accessibility/AXTable.h" |
-#include "core/dom/ElementTraversal.h" |
-#include "core/dom/shadow/ShadowRoot.h" |
-#include "core/editing/FrameSelection.h" |
-#include "core/editing/RenderedPosition.h" |
-#include "core/editing/TextIterator.h" |
-#include "core/editing/VisibleUnits.h" |
-#include "core/editing/htmlediting.h" |
-#include "core/frame/LocalFrame.h" |
-#include "core/frame/Settings.h" |
-#include "core/html/HTMLImageElement.h" |
-#include "core/html/HTMLLabelElement.h" |
-#include "core/html/HTMLOptionElement.h" |
-#include "core/html/HTMLSelectElement.h" |
-#include "core/html/HTMLTextAreaElement.h" |
-#include "core/html/shadow/ShadowElementNames.h" |
-#include "core/loader/ProgressTracker.h" |
-#include "core/page/Page.h" |
-#include "core/rendering/HitTestResult.h" |
-#include "core/rendering/RenderFieldset.h" |
-#include "core/rendering/RenderFileUploadControl.h" |
-#include "core/rendering/RenderHTMLCanvas.h" |
-#include "core/rendering/RenderImage.h" |
-#include "core/rendering/RenderInline.h" |
-#include "core/rendering/RenderLayer.h" |
-#include "core/rendering/RenderListMarker.h" |
-#include "core/rendering/RenderMenuList.h" |
-#include "core/rendering/RenderPart.h" |
-#include "core/rendering/RenderTextControlSingleLine.h" |
-#include "core/rendering/RenderTextFragment.h" |
-#include "core/rendering/RenderView.h" |
-#include "core/svg/SVGDocumentExtensions.h" |
-#include "core/svg/SVGSVGElement.h" |
-#include "core/svg/graphics/SVGImage.h" |
-#include "platform/text/PlatformLocale.h" |
-#include "wtf/StdLibExtras.h" |
- |
-using blink::WebLocalizedString; |
- |
-namespace blink { |
- |
-using namespace HTMLNames; |
- |
-static inline RenderObject* firstChildInContinuation(const RenderInline& renderer) |
-{ |
- RenderBoxModelObject* r = renderer.continuation(); |
- |
- while (r) { |
- if (r->isRenderBlock()) |
- return r; |
- if (RenderObject* child = r->slowFirstChild()) |
- return child; |
- r = toRenderInline(r)->continuation(); |
- } |
- |
- return 0; |
-} |
- |
-static inline bool isInlineWithContinuation(RenderObject* object) |
-{ |
- if (!object->isBoxModelObject()) |
- return false; |
- |
- RenderBoxModelObject* renderer = toRenderBoxModelObject(object); |
- if (!renderer->isRenderInline()) |
- return false; |
- |
- return toRenderInline(renderer)->continuation(); |
-} |
- |
-static inline RenderObject* firstChildConsideringContinuation(RenderObject* renderer) |
-{ |
- RenderObject* firstChild = renderer->slowFirstChild(); |
- |
- if (!firstChild && isInlineWithContinuation(renderer)) |
- firstChild = firstChildInContinuation(toRenderInline(*renderer)); |
- |
- return firstChild; |
-} |
- |
-static inline RenderInline* startOfContinuations(RenderObject* r) |
-{ |
- if (r->isInlineElementContinuation()) { |
- return toRenderInline(r->node()->renderer()); |
- } |
- |
- // Blocks with a previous continuation always have a next continuation |
- if (r->isRenderBlock() && toRenderBlock(r)->inlineElementContinuation()) |
- return toRenderInline(toRenderBlock(r)->inlineElementContinuation()->node()->renderer()); |
- |
- return 0; |
-} |
- |
-static inline RenderObject* endOfContinuations(RenderObject* renderer) |
-{ |
- RenderObject* prev = renderer; |
- RenderObject* cur = renderer; |
- |
- if (!cur->isRenderInline() && !cur->isRenderBlock()) |
- return renderer; |
- |
- while (cur) { |
- prev = cur; |
- if (cur->isRenderInline()) { |
- cur = toRenderInline(cur)->inlineElementContinuation(); |
- ASSERT(cur || !toRenderInline(prev)->continuation()); |
- } else { |
- cur = toRenderBlock(cur)->inlineElementContinuation(); |
- } |
- } |
- |
- return prev; |
-} |
- |
-static inline bool lastChildHasContinuation(RenderObject* renderer) |
-{ |
- RenderObject* lastChild = renderer->slowLastChild(); |
- return lastChild && isInlineWithContinuation(lastChild); |
-} |
- |
-static RenderBoxModelObject* nextContinuation(RenderObject* renderer) |
-{ |
- ASSERT(renderer); |
- if (renderer->isRenderInline() && !renderer->isReplaced()) |
- return toRenderInline(renderer)->continuation(); |
- if (renderer->isRenderBlock()) |
- return toRenderBlock(renderer)->inlineElementContinuation(); |
- return 0; |
-} |
- |
-AXRenderObject::AXRenderObject(RenderObject* renderer) |
- : AXNodeObject(renderer->node()) |
- , m_renderer(renderer) |
- , m_cachedElementRectDirty(true) |
-{ |
-#if ENABLE(ASSERT) |
- m_renderer->setHasAXObject(true); |
-#endif |
-} |
- |
-PassRefPtr<AXRenderObject> AXRenderObject::create(RenderObject* renderer) |
-{ |
- return adoptRef(new AXRenderObject(renderer)); |
-} |
- |
-AXRenderObject::~AXRenderObject() |
-{ |
- ASSERT(isDetached()); |
-} |
- |
-LayoutRect AXRenderObject::elementRect() const |
-{ |
- if (!m_explicitElementRect.isEmpty()) |
- return m_explicitElementRect; |
- if (!m_renderer) |
- return LayoutRect(); |
- if (!m_renderer->isBox()) |
- return computeElementRect(); |
- |
- for (const AXObject* obj = this; obj; obj = obj->parentObject()) { |
- if (obj->isAXRenderObject()) |
- toAXRenderObject(obj)->checkCachedElementRect(); |
- } |
- for (const AXObject* obj = this; obj; obj = obj->parentObject()) { |
- if (obj->isAXRenderObject()) |
- toAXRenderObject(obj)->updateCachedElementRect(); |
- } |
- |
- return m_cachedElementRect; |
-} |
- |
-void AXRenderObject::setRenderer(RenderObject* renderer) |
-{ |
- m_renderer = renderer; |
- setNode(renderer->node()); |
-} |
- |
-RenderBoxModelObject* AXRenderObject::renderBoxModelObject() const |
-{ |
- if (!m_renderer || !m_renderer->isBoxModelObject()) |
- return 0; |
- return toRenderBoxModelObject(m_renderer); |
-} |
- |
-Document* AXRenderObject::topDocument() const |
-{ |
- if (!document()) |
- return 0; |
- return &document()->topDocument(); |
-} |
- |
-bool AXRenderObject::shouldNotifyActiveDescendant() const |
-{ |
- // We want to notify that the combo box has changed its active descendant, |
- // but we do not want to change the focus, because focus should remain with the combo box. |
- if (isComboBox()) |
- return true; |
- |
- return shouldFocusActiveDescendant(); |
-} |
- |
-ScrollableArea* AXRenderObject::getScrollableAreaIfScrollable() const |
-{ |
- // If the parent is a FrameView, then this object isn't really scrollable; the parent should handle the scrolling. |
- if (parentObject() && parentObject()->isAXScrollView()) |
- return 0; |
- |
- if (!m_renderer || !m_renderer->isBox()) |
- return 0; |
- |
- RenderBox* box = toRenderBox(m_renderer); |
- if (!box->canBeScrolledAndHasScrollableArea()) |
- return 0; |
- |
- return box->scrollableArea(); |
-} |
- |
-AccessibilityRole AXRenderObject::determineAccessibilityRole() |
-{ |
- if (!m_renderer) |
- return UnknownRole; |
- |
- m_ariaRole = determineAriaRoleAttribute(); |
- |
- Node* node = m_renderer->node(); |
- AccessibilityRole ariaRole = ariaRoleAttribute(); |
- if (ariaRole != UnknownRole) |
- return ariaRole; |
- |
- RenderBoxModelObject* cssBox = renderBoxModelObject(); |
- |
- if (node && node->isLink()) { |
- if (cssBox && cssBox->isImage()) |
- return ImageMapRole; |
- return LinkRole; |
- } |
- if ((cssBox && cssBox->isListItem()) || isHTMLLIElement(node)) |
- return ListItemRole; |
- if (m_renderer->isListMarker()) |
- return ListMarkerRole; |
- if (isHTMLButtonElement(node)) |
- return buttonRoleType(); |
- if (isHTMLDetailsElement(node)) |
- return DetailsRole; |
- if (isHTMLSummaryElement(node)) { |
- if (node->parentElement() && isHTMLDetailsElement(node->parentElement())) |
- return DisclosureTriangleRole; |
- return UnknownRole; |
- } |
- if (isHTMLLegendElement(node)) |
- return LegendRole; |
- if (m_renderer->isText()) |
- return StaticTextRole; |
- if (cssBox && cssBox->isImage()) { |
- if (isHTMLInputElement(node)) |
- return ariaHasPopup() ? PopUpButtonRole : ButtonRole; |
- if (isSVGImage()) |
- return SVGRootRole; |
- return ImageRole; |
- } |
- |
- // Note: if JavaScript is disabled, the renderer won't be a RenderHTMLCanvas. |
- if (isHTMLCanvasElement(node) && m_renderer->isCanvas()) |
- return CanvasRole; |
- |
- if (cssBox && cssBox->isRenderView()) |
- return WebAreaRole; |
- |
- if (cssBox && cssBox->isTextField()) |
- return TextFieldRole; |
- |
- if (cssBox && cssBox->isTextArea()) |
- return TextAreaRole; |
- |
- 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::color) |
- return ColorWellRole; |
- if (type == InputTypeNames::time) |
- return TimeRole; |
- } |
- |
- if (isFileUploadButton()) |
- return ButtonRole; |
- |
- if (cssBox && cssBox->isMenuList()) |
- return PopUpButtonRole; |
- |
- if (headingLevel()) |
- return HeadingRole; |
- |
- if (m_renderer->isSVGImage()) |
- return ImageRole; |
- if (m_renderer->isSVGRoot()) |
- return SVGRootRole; |
- |
- if (node && node->hasTagName(ddTag)) |
- return DescriptionListDetailRole; |
- |
- if (node && node->hasTagName(dlTag)) |
- return DescriptionListRole; |
- |
- if (node && node->hasTagName(dtTag)) |
- return DescriptionListTermRole; |
- |
- if (node && (node->hasTagName(rpTag) || node->hasTagName(rtTag))) |
- return AnnotationRole; |
- |
- // Table sections should be ignored. |
- if (m_renderer->isTableSection()) |
- return IgnoredRole; |
- |
- if (m_renderer->isHR()) |
- return HorizontalRuleRole; |
- |
- if (isHTMLOutputElement(node)) |
- return StatusRole; |
- |
- if (isHTMLParagraphElement(node)) |
- return ParagraphRole; |
- |
- if (isHTMLLabelElement(node)) |
- return LabelRole; |
- |
- if (isHTMLRubyElement(node)) |
- return RubyRole; |
- |
- if (isHTMLDivElement(node)) |
- return DivRole; |
- |
- if (isHTMLMeterElement(node)) |
- return MeterRole; |
- |
- if (isHTMLFormElement(node)) |
- return FormRole; |
- |
- if (node && node->hasTagName(articleTag)) |
- return ArticleRole; |
- |
- if (node && node->hasTagName(blockquoteTag)) |
- return BlockquoteRole; |
- |
- if (node && node->hasTagName(mainTag)) |
- return MainRole; |
- |
- if (node && node->hasTagName(navTag)) |
- return NavigationRole; |
- |
- if (node && node->hasTagName(asideTag)) |
- return ComplementaryRole; |
- |
- if (node && node->hasTagName(preTag)) |
- return PreRole; |
- |
- if (node && node->hasTagName(sectionTag)) |
- return RegionRole; |
- |
- if (node && node->hasTagName(addressTag)) |
- return ContentInfoRole; |
- |
- if (node && node->hasTagName(dialogTag)) |
- return DialogRole; |
- |
- // The HTML element should not be exposed as an element. That's what the RenderView element does. |
- if (isHTMLHtmlElement(node)) |
- return IgnoredRole; |
- |
- if (node && node->hasTagName(iframeTag)) |
- return IframeRole; |
- |
- if (isEmbeddedObject()) |
- return EmbeddedObjectRole; |
- |
- if (node && node->hasTagName(figcaptionTag)) |
- return FigcaptionRole; |
- |
- if (node && node->hasTagName(figureTag)) |
- return FigureRole; |
- |
- // There should only be one banner/contentInfo per page. If header/footer are being used within an article or section |
- // then it should not be exposed as whole page's banner/contentInfo |
- if (node && node->hasTagName(headerTag) && !isDescendantOfElementType(articleTag) && !isDescendantOfElementType(sectionTag)) |
- return BannerRole; |
- if (node && node->hasTagName(footerTag) && !isDescendantOfElementType(articleTag) && !isDescendantOfElementType(sectionTag)) |
- return FooterRole; |
- |
- if (isHTMLAnchorElement(node) && isClickable()) |
- return LinkRole; |
- |
- if (m_renderer->isRenderBlockFlow()) |
- return GroupRole; |
- |
- // If the element does not have role, but it has ARIA attributes, accessibility should fallback to exposing it as a group. |
- if (supportsARIAAttributes()) |
- return GroupRole; |
- |
- return UnknownRole; |
-} |
- |
-void AXRenderObject::init() |
-{ |
- AXNodeObject::init(); |
-} |
- |
-void AXRenderObject::detach() |
-{ |
- AXNodeObject::detach(); |
- |
- detachRemoteSVGRoot(); |
- |
-#if ENABLE(ASSERT) |
- if (m_renderer) |
- m_renderer->setHasAXObject(false); |
-#endif |
- m_renderer = 0; |
-} |
- |
-// |
-// Check object role or purpose. |
-// |
- |
-bool AXRenderObject::isAttachment() const |
-{ |
- RenderBoxModelObject* renderer = renderBoxModelObject(); |
- if (!renderer) |
- return false; |
- // Widgets are the replaced elements that we represent to AX as attachments |
- bool isRenderPart = renderer->isRenderPart(); |
- ASSERT(!isRenderPart || (renderer->isReplaced() && !isImage())); |
- return isRenderPart; |
-} |
- |
-bool AXRenderObject::isFileUploadButton() const |
-{ |
- return m_renderer && isHTMLInputElement(m_renderer->node()) && toHTMLInputElement(*m_renderer->node()).type() == InputTypeNames::file; |
-} |
- |
-static bool isLinkable(const AXObject& object) |
-{ |
- if (!object.renderer()) |
- return false; |
- |
- // See https://wiki.mozilla.org/Accessibility/AT-Windows-API for the elements |
- // Mozilla considers linkable. |
- return object.isLink() || object.isImage() || object.renderer()->isText(); |
-} |
- |
-bool AXRenderObject::isLinked() const |
-{ |
- if (!isLinkable(*this)) |
- return false; |
- |
- Element* anchor = anchorElement(); |
- if (!isHTMLAnchorElement(anchor)) |
- return false; |
- |
- return !toHTMLAnchorElement(*anchor).href().isEmpty(); |
-} |
- |
-bool AXRenderObject::isLoaded() const |
-{ |
- return !m_renderer->document().parser(); |
-} |
- |
-bool AXRenderObject::isOffScreen() const |
-{ |
- ASSERT(m_renderer); |
- IntRect contentRect = pixelSnappedIntRect(m_renderer->absoluteClippedOverflowRect()); |
- FrameView* view = m_renderer->frame()->view(); |
- IntRect viewRect = view->visibleContentRect(); |
- viewRect.intersect(contentRect); |
- return viewRect.isEmpty(); |
-} |
- |
-bool AXRenderObject::isReadOnly() const |
-{ |
- ASSERT(m_renderer); |
- |
- if (isWebArea()) { |
- Document& document = m_renderer->document(); |
- HTMLElement* body = document.body(); |
- if (body && body->hasEditableStyle()) |
- return false; |
- |
- return !document.hasEditableStyle(); |
- } |
- |
- return AXNodeObject::isReadOnly(); |
-} |
- |
-bool AXRenderObject::isVisited() const |
-{ |
- // FIXME: Is it a privacy violation to expose visited information to accessibility APIs? |
- return m_renderer->style()->isLink() && m_renderer->style()->insideLink() == InsideVisitedLink; |
-} |
- |
-// |
-// Check object state. |
-// |
- |
-bool AXRenderObject::isFocused() const |
-{ |
- if (!m_renderer) |
- return false; |
- |
- Document& document = m_renderer->document(); |
- Element* focusedElement = document.focusedElement(); |
- if (!focusedElement) |
- return false; |
- |
- // A web area is represented by the Document node in the DOM tree, which isn't focusable. |
- // Check instead if the frame's selection controller is focused |
- if (focusedElement == m_renderer->node() |
- || (roleValue() == WebAreaRole && document.frame()->selection().isFocusedAndActive())) |
- return true; |
- |
- return false; |
-} |
- |
-bool AXRenderObject::isSelected() const |
-{ |
- if (!m_renderer) |
- return false; |
- |
- Node* node = m_renderer->node(); |
- if (!node) |
- return false; |
- |
- const AtomicString& ariaSelected = getAttribute(aria_selectedAttr); |
- if (equalIgnoringCase(ariaSelected, "true")) |
- return true; |
- |
- if (isTabItem() && isTabItemSelected()) |
- return true; |
- |
- return false; |
-} |
- |
-// |
-// Whether objects are ignored, i.e. not included in the tree. |
-// |
- |
-AXObjectInclusion AXRenderObject::defaultObjectInclusion() const |
-{ |
- // The following cases can apply to any element that's a subclass of AXRenderObject. |
- |
- if (!m_renderer) |
- return IgnoreObject; |
- |
- if (m_renderer->style()->visibility() != VISIBLE) { |
- // aria-hidden is meant to override visibility as the determinant in AX hierarchy inclusion. |
- if (equalIgnoringCase(getAttribute(aria_hiddenAttr), "false")) |
- return DefaultBehavior; |
- |
- return IgnoreObject; |
- } |
- |
- return AXObject::defaultObjectInclusion(); |
-} |
- |
-bool AXRenderObject::computeAccessibilityIsIgnored() const |
-{ |
-#if ENABLE(ASSERT) |
- ASSERT(m_initialized); |
-#endif |
- |
- // Check first if any of the common reasons cause this element to be ignored. |
- // Then process other use cases that need to be applied to all the various roles |
- // that AXRenderObjects take on. |
- AXObjectInclusion decision = defaultObjectInclusion(); |
- if (decision == IncludeObject) |
- return false; |
- if (decision == IgnoreObject) |
- return true; |
- |
- // If this element is within a parent that cannot have children, it should not be exposed. |
- if (isDescendantOfBarrenParent()) |
- return true; |
- |
- if (roleValue() == IgnoredRole) |
- return true; |
- |
- if ((roleValue() == NoneRole || roleValue() == PresentationalRole) || inheritsPresentationalRole()) |
- return true; |
- |
- // An ARIA tree can only have tree items and static text as children. |
- if (!isAllowedChildOfTree()) |
- return true; |
- |
- // TODO: we should refactor this - but right now this is necessary to make |
- // sure scroll areas stay in the tree. |
- if (isAttachment()) |
- return false; |
- |
- // ignore popup menu items because AppKit does |
- for (RenderObject* parent = m_renderer->parent(); parent; parent = parent->parent()) { |
- if (parent->isBoxModelObject() && toRenderBoxModelObject(parent)->isMenuList()) |
- return true; |
- } |
- |
- // find out if this element is inside of a label element. |
- // if so, it may be ignored because it's the label for a checkbox or radio button |
- AXObject* controlObject = correspondingControlForLabelElement(); |
- if (controlObject && !controlObject->exposesTitleUIElement() && controlObject->isCheckboxOrRadio()) |
- return true; |
- |
- // NOTE: BRs always have text boxes now, so the text box check here can be removed |
- if (m_renderer->isText()) { |
- // static text beneath MenuItems and MenuButtons are just reported along with the menu item, so it's ignored on an individual level |
- AXObject* parent = parentObjectUnignored(); |
- if (parent && (parent->ariaRoleAttribute() == MenuItemRole || parent->ariaRoleAttribute() == MenuButtonRole)) |
- return true; |
- RenderText* renderText = toRenderText(m_renderer); |
- if (m_renderer->isBR() || !renderText->firstTextBox()) |
- return true; |
- |
- // Don't ignore static text in editable text controls. |
- for (AXObject* parent = parentObject(); parent; parent = parent->parentObject()) { |
- if (parent->roleValue() == TextFieldRole || parent->roleValue() == TextAreaRole) |
- return false; |
- } |
- |
- // text elements that are just empty whitespace should not be returned |
- // FIXME(dmazzoni): we probably shouldn't ignore this if the style is 'pre', or similar... |
- return renderText->text().impl()->containsOnlyWhitespace(); |
- } |
- |
- if (isHeading()) |
- return false; |
- |
- if (isLandmarkRelated()) |
- return false; |
- |
- if (isLink()) |
- return false; |
- |
- // all controls are accessible |
- if (isControl()) |
- return false; |
- |
- if (ariaRoleAttribute() != UnknownRole) |
- return false; |
- |
- // don't ignore labels, because they serve as TitleUIElements |
- Node* node = m_renderer->node(); |
- if (isHTMLLabelElement(node)) |
- return false; |
- |
- // Anything that is content editable should not be ignored. |
- // However, one cannot just call node->hasEditableStyle() since that will ask if its parents |
- // are also editable. Only the top level content editable region should be exposed. |
- if (hasContentEditableAttributeSet()) |
- return false; |
- |
- // List items play an important role in defining the structure of lists. They should not be ignored. |
- if (roleValue() == ListItemRole) |
- return false; |
- |
- if (roleValue() == BlockquoteRole) |
- return false; |
- |
- if (roleValue() == DialogRole) |
- return false; |
- |
- if (roleValue() == FigcaptionRole) |
- return false; |
- |
- if (roleValue() == FigureRole) |
- return false; |
- |
- if (roleValue() == DetailsRole) |
- return false; |
- |
- if (roleValue() == MeterRole) |
- return false; |
- |
- if (roleValue() == RubyRole) |
- return false; |
- |
- // if this element has aria attributes on it, it should not be ignored. |
- if (supportsARIAAttributes()) |
- return false; |
- |
- // <span> tags are inline tags and not meant to convey information if they have no other aria |
- // information on them. If we don't ignore them, they may emit signals expected to come from |
- // their parent. In addition, because included spans are GroupRole objects, and GroupRole |
- // objects are often containers with meaningful information, the inclusion of a span can have |
- // the side effect of causing the immediate parent accessible to be ignored. This is especially |
- // problematic for platforms which have distinct roles for textual block elements. |
- if (isHTMLSpanElement(node)) |
- return true; |
- |
- if (m_renderer->isRenderBlockFlow() && m_renderer->childrenInline() && !canSetFocusAttribute()) |
- return !toRenderBlockFlow(m_renderer)->firstLineBox() && !mouseButtonListener(); |
- |
- // ignore images seemingly used as spacers |
- if (isImage()) { |
- |
- // If the image can take focus, it should not be ignored, lest the user not be able to interact with something important. |
- if (canSetFocusAttribute()) |
- return false; |
- |
- if (node && node->isElementNode()) { |
- Element* elt = toElement(node); |
- const AtomicString& alt = elt->getAttribute(altAttr); |
- // don't ignore an image that has an alt tag |
- if (!alt.string().containsOnlyWhitespace()) |
- return false; |
- // informal standard is to ignore images with zero-length alt strings |
- if (!alt.isNull()) |
- return true; |
- } |
- |
- if (isNativeImage() && m_renderer->isImage()) { |
- // check for one-dimensional image |
- RenderImage* image = toRenderImage(m_renderer); |
- if (image->height() <= 1 || image->width() <= 1) |
- return true; |
- |
- // check whether rendered image was stretched from one-dimensional file image |
- if (image->cachedImage()) { |
- LayoutSize imageSize = image->cachedImage()->imageSizeForRenderer(m_renderer, image->view()->zoomFactor()); |
- return imageSize.height() <= 1 || imageSize.width() <= 1; |
- } |
- } |
- return false; |
- } |
- |
- if (isCanvas()) { |
- if (canvasHasFallbackContent()) |
- return false; |
- RenderHTMLCanvas* canvas = toRenderHTMLCanvas(m_renderer); |
- if (canvas->height() <= 1 || canvas->width() <= 1) |
- return true; |
- // Otherwise fall through; use presence of help text, title, or description to decide. |
- } |
- |
- if (isWebArea() || m_renderer->isListMarker()) |
- return false; |
- |
- // Using the help text, title or accessibility description (so we |
- // check if there's some kind of accessible name for the element) |
- // to decide an element's visibility is not as definitive as |
- // previous checks, so this should remain as one of the last. |
- // |
- // These checks are simplified in the interest of execution speed; |
- // for example, any element having an alt attribute will make it |
- // not ignored, rather than just images. |
- if (!getAttribute(aria_helpAttr).isEmpty() || !getAttribute(aria_describedbyAttr).isEmpty() || !getAttribute(altAttr).isEmpty() || !getAttribute(titleAttr).isEmpty()) |
- return false; |
- |
- // Don't ignore generic focusable elements like <div tabindex=0> |
- // unless they're completely empty, with no children. |
- if (isGenericFocusableElement() && node->hasChildren()) |
- return false; |
- |
- if (!ariaAccessibilityDescription().isEmpty()) |
- return false; |
- |
- // By default, objects should be ignored so that the AX hierarchy is not |
- // filled with unnecessary items. |
- return true; |
-} |
- |
-// |
-// Properties of static elements. |
-// |
- |
-const AtomicString& AXRenderObject::accessKey() const |
-{ |
- Node* node = m_renderer->node(); |
- if (!node) |
- return nullAtom; |
- if (!node->isElementNode()) |
- return nullAtom; |
- return toElement(node)->getAttribute(accesskeyAttr); |
-} |
- |
-AccessibilityOrientation AXRenderObject::orientation() const |
-{ |
- const AtomicString& ariaOrientation = getAttribute(aria_orientationAttr); |
- if (equalIgnoringCase(ariaOrientation, "horizontal")) |
- return AccessibilityOrientationHorizontal; |
- if (equalIgnoringCase(ariaOrientation, "vertical")) |
- return AccessibilityOrientationVertical; |
- |
- return AXObject::orientation(); |
-} |
- |
-String AXRenderObject::text() const |
-{ |
- if (isPasswordFieldAndShouldHideValue()) |
- return String(); |
- |
- return AXNodeObject::text(); |
-} |
- |
-int AXRenderObject::textLength() const |
-{ |
- if (!isTextControl()) |
- return -1; |
- |
- if (isPasswordFieldAndShouldHideValue()) |
- return -1; // need to return something distinct from 0 |
- |
- return text().length(); |
-} |
- |
-KURL AXRenderObject::url() const |
-{ |
- if (isAnchor() && isHTMLAnchorElement(m_renderer->node())) { |
- if (HTMLAnchorElement* anchor = toHTMLAnchorElement(anchorElement())) |
- return anchor->href(); |
- } |
- |
- if (isWebArea()) |
- return m_renderer->document().url(); |
- |
- if (isImage() && isHTMLImageElement(m_renderer->node())) |
- return toHTMLImageElement(*m_renderer->node()).src(); |
- |
- if (isInputImage()) |
- return toHTMLInputElement(m_renderer->node())->src(); |
- |
- return KURL(); |
-} |
- |
-// |
-// Properties of interactive elements. |
-// |
- |
-static String queryString(WebLocalizedString::Name name) |
-{ |
- return Locale::defaultLocale().queryString(name); |
-} |
- |
-String AXRenderObject::actionVerb() const |
-{ |
- switch (roleValue()) { |
- case ButtonRole: |
- case ToggleButtonRole: |
- return queryString(WebLocalizedString::AXButtonActionVerb); |
- case TextFieldRole: |
- case TextAreaRole: |
- return queryString(WebLocalizedString::AXTextFieldActionVerb); |
- case RadioButtonRole: |
- return queryString(WebLocalizedString::AXRadioButtonActionVerb); |
- case CheckBoxRole: |
- return queryString(isChecked() ? WebLocalizedString::AXCheckedCheckBoxActionVerb : WebLocalizedString::AXUncheckedCheckBoxActionVerb); |
- case LinkRole: |
- return queryString(WebLocalizedString::AXLinkActionVerb); |
- default: |
- return emptyString(); |
- } |
-} |
- |
-String AXRenderObject::stringValue() const |
-{ |
- if (!m_renderer) |
- return String(); |
- |
- if (isPasswordFieldAndShouldHideValue()) |
- return String(); |
- |
- RenderBoxModelObject* cssBox = renderBoxModelObject(); |
- |
- if (ariaRoleAttribute() == StaticTextRole) { |
- String staticText = text(); |
- if (!staticText.length()) |
- staticText = textUnderElement(); |
- return staticText; |
- } |
- |
- if (m_renderer->isText()) |
- return textUnderElement(); |
- |
- if (cssBox && cssBox->isMenuList()) { |
- // RenderMenuList will go straight to the text() of its selected item. |
- // This has to be overridden in the case where the selected item has an ARIA label. |
- HTMLSelectElement* selectElement = toHTMLSelectElement(m_renderer->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; |
- } |
- return toRenderMenuList(m_renderer)->text(); |
- } |
- |
- if (m_renderer->isListMarker()) |
- return toRenderListMarker(m_renderer)->text(); |
- |
- if (isWebArea()) { |
- // FIXME: Why would a renderer exist when the Document isn't attached to a frame? |
- if (m_renderer->frame()) |
- return String(); |
- |
- ASSERT_NOT_REACHED(); |
- } |
- |
- if (isTextControl()) |
- return text(); |
- |
- if (m_renderer->isFileUploadControl()) |
- return toRenderFileUploadControl(m_renderer)->fileTextValue(); |
- |
- // 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(); |
-} |
- |
-// |
-// ARIA attributes. |
-// |
- |
-AXObject* AXRenderObject::activeDescendant() const |
-{ |
- if (!m_renderer) |
- return 0; |
- |
- if (m_renderer->node() && !m_renderer->node()->isElementNode()) |
- return 0; |
- |
- Element* element = toElement(m_renderer->node()); |
- if (!element) |
- return 0; |
- |
- const AtomicString& activeDescendantAttrStr = element->getAttribute(aria_activedescendantAttr); |
- if (activeDescendantAttrStr.isNull() || activeDescendantAttrStr.isEmpty()) |
- return 0; |
- |
- Element* target = element->treeScope().getElementById(activeDescendantAttrStr); |
- if (!target) |
- return 0; |
- |
- AXObject* obj = axObjectCache()->getOrCreate(target); |
- |
- // An activedescendant is only useful if it has a renderer, because that's what's needed to post the notification. |
- if (obj && obj->isAXRenderObject()) |
- return obj; |
- |
- return 0; |
-} |
- |
-void AXRenderObject::accessibilityChildrenFromAttribute(QualifiedName attr, AccessibilityChildrenVector& children) const |
-{ |
- WillBeHeapVector<RawPtrWillBeMember<Element> > elements; |
- elementsFromAttribute(elements, attr); |
- |
- AXObjectCacheImpl* cache = axObjectCache(); |
- unsigned count = elements.size(); |
- for (unsigned k = 0; k < count; ++k) { |
- Element* element = elements[k]; |
- AXObject* child = cache->getOrCreate(element); |
- if (child) |
- children.append(child); |
- } |
-} |
- |
-void AXRenderObject::ariaFlowToElements(AccessibilityChildrenVector& flowTo) const |
-{ |
- accessibilityChildrenFromAttribute(aria_flowtoAttr, flowTo); |
-} |
- |
-void AXRenderObject::ariaControlsElements(AccessibilityChildrenVector& controls) const |
-{ |
- accessibilityChildrenFromAttribute(aria_controlsAttr, controls); |
-} |
- |
-void AXRenderObject::ariaDescribedbyElements(AccessibilityChildrenVector& describedby) const |
-{ |
- accessibilityChildrenFromAttribute(aria_describedbyAttr, describedby); |
-} |
- |
-void AXRenderObject::ariaLabelledbyElements(AccessibilityChildrenVector& labelledby) const |
-{ |
- accessibilityChildrenFromAttribute(aria_labelledbyAttr, labelledby); |
-} |
- |
-void AXRenderObject::ariaOwnsElements(AccessibilityChildrenVector& owns) const |
-{ |
- accessibilityChildrenFromAttribute(aria_ownsAttr, owns); |
-} |
- |
-bool AXRenderObject::ariaHasPopup() const |
-{ |
- return elementAttributeValue(aria_haspopupAttr); |
-} |
- |
-bool AXRenderObject::ariaRoleHasPresentationalChildren() const |
-{ |
- switch (m_ariaRole) { |
- case ButtonRole: |
- case SliderRole: |
- case ImageRole: |
- case ProgressIndicatorRole: |
- case SpinButtonRole: |
- // case SeparatorRole: |
- return true; |
- default: |
- return false; |
- } |
-} |
- |
-bool AXRenderObject::isPresentationalChildOfAriaRole() const |
-{ |
- // Walk the parent chain looking for a parent that has presentational children |
- AXObject* parent; |
- for (parent = parentObject(); parent && !parent->ariaRoleHasPresentationalChildren(); parent = parent->parentObject()) |
- { } |
- |
- return parent; |
-} |
- |
-bool AXRenderObject::shouldFocusActiveDescendant() const |
-{ |
- switch (ariaRoleAttribute()) { |
- case ComboBoxRole: |
- case GridRole: |
- case GroupRole: |
- case ListBoxRole: |
- case MenuRole: |
- case MenuBarRole: |
- case OutlineRole: |
- case PopUpButtonRole: |
- case ProgressIndicatorRole: |
- case RadioGroupRole: |
- case RowRole: |
- case TabListRole: |
- case ToolbarRole: |
- case TreeRole: |
- case TreeGridRole: |
- return true; |
- default: |
- return false; |
- } |
-} |
- |
-bool AXRenderObject::supportsARIADragging() const |
-{ |
- const AtomicString& grabbed = getAttribute(aria_grabbedAttr); |
- return equalIgnoringCase(grabbed, "true") || equalIgnoringCase(grabbed, "false"); |
-} |
- |
-bool AXRenderObject::supportsARIADropping() const |
-{ |
- const AtomicString& dropEffect = getAttribute(aria_dropeffectAttr); |
- return !dropEffect.isEmpty(); |
-} |
- |
-bool AXRenderObject::supportsARIAFlowTo() const |
-{ |
- return !getAttribute(aria_flowtoAttr).isEmpty(); |
-} |
- |
-bool AXRenderObject::supportsARIAOwns() const |
-{ |
- if (!m_renderer) |
- return false; |
- const AtomicString& ariaOwns = getAttribute(aria_ownsAttr); |
- |
- return !ariaOwns.isEmpty(); |
-} |
- |
-// |
-// ARIA live-region features. |
-// |
- |
-const AtomicString& AXRenderObject::liveRegionStatus() const |
-{ |
- DEFINE_STATIC_LOCAL(const AtomicString, liveRegionStatusAssertive, ("assertive", AtomicString::ConstructFromLiteral)); |
- DEFINE_STATIC_LOCAL(const AtomicString, liveRegionStatusPolite, ("polite", AtomicString::ConstructFromLiteral)); |
- DEFINE_STATIC_LOCAL(const AtomicString, liveRegionStatusOff, ("off", AtomicString::ConstructFromLiteral)); |
- |
- const AtomicString& liveRegionStatus = getAttribute(aria_liveAttr); |
- // These roles have implicit live region status. |
- if (liveRegionStatus.isEmpty()) { |
- switch (roleValue()) { |
- case AlertDialogRole: |
- case AlertRole: |
- return liveRegionStatusAssertive; |
- case LogRole: |
- case StatusRole: |
- return liveRegionStatusPolite; |
- case TimerRole: |
- case MarqueeRole: |
- return liveRegionStatusOff; |
- default: |
- break; |
- } |
- } |
- |
- return liveRegionStatus; |
-} |
- |
-const AtomicString& AXRenderObject::liveRegionRelevant() const |
-{ |
- DEFINE_STATIC_LOCAL(const AtomicString, defaultLiveRegionRelevant, ("additions text", AtomicString::ConstructFromLiteral)); |
- const AtomicString& relevant = getAttribute(aria_relevantAttr); |
- |
- // Default aria-relevant = "additions text". |
- if (relevant.isEmpty()) |
- return defaultLiveRegionRelevant; |
- |
- return relevant; |
-} |
- |
-bool AXRenderObject::liveRegionAtomic() const |
-{ |
- return elementAttributeValue(aria_atomicAttr); |
-} |
- |
-bool AXRenderObject::liveRegionBusy() const |
-{ |
- return elementAttributeValue(aria_busyAttr); |
-} |
- |
-// |
-// Accessibility Text. |
-// |
- |
-String AXRenderObject::textUnderElement() const |
-{ |
- if (!m_renderer) |
- return String(); |
- |
- if (m_renderer->isFileUploadControl()) |
- return toRenderFileUploadControl(m_renderer)->buttonValue(); |
- |
- if (m_renderer->isText()) |
- return toRenderText(m_renderer)->plainText(); |
- |
- return AXNodeObject::textUnderElement(); |
-} |
- |
-// |
-// Accessibility Text - (To be deprecated). |
-// |
- |
-String AXRenderObject::helpText() const |
-{ |
- if (!m_renderer) |
- 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 (RenderObject* curr = m_renderer; curr; curr = curr->parent()) { |
- if (curr->node() && curr->node()->isHTMLElement()) { |
- const AtomicString& summary = toElement(curr->node())->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->node())->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(); |
-} |
- |
-// |
-// Position and size. |
-// |
- |
-void AXRenderObject::checkCachedElementRect() const |
-{ |
- if (m_cachedElementRectDirty) |
- return; |
- |
- if (!m_renderer) |
- return; |
- |
- if (!m_renderer->isBox()) |
- return; |
- |
- bool dirty = false; |
- RenderBox* box = toRenderBox(m_renderer); |
- if (box->frameRect() != m_cachedFrameRect) |
- dirty = true; |
- |
- if (box->canBeScrolledAndHasScrollableArea()) { |
- ScrollableArea* scrollableArea = box->scrollableArea(); |
- if (scrollableArea && scrollableArea->scrollPosition() != m_cachedScrollPosition) |
- dirty = true; |
- } |
- |
- if (dirty) |
- markCachedElementRectDirty(); |
-} |
- |
-void AXRenderObject::updateCachedElementRect() const |
-{ |
- if (!m_cachedElementRectDirty) |
- return; |
- |
- if (!m_renderer) |
- return; |
- |
- if (!m_renderer->isBox()) |
- return; |
- |
- RenderBox* box = toRenderBox(m_renderer); |
- m_cachedFrameRect = box->frameRect(); |
- |
- if (box->canBeScrolledAndHasScrollableArea()) { |
- ScrollableArea* scrollableArea = box->scrollableArea(); |
- if (scrollableArea) |
- m_cachedScrollPosition = scrollableArea->scrollPosition(); |
- } |
- |
- m_cachedElementRect = computeElementRect(); |
- m_cachedElementRectDirty = false; |
-} |
- |
-void AXRenderObject::markCachedElementRectDirty() const |
-{ |
- if (m_cachedElementRectDirty) |
- return; |
- |
- // Marks children recursively, if this element changed. |
- m_cachedElementRectDirty = true; |
- for (AXObject* child = firstChild(); child; child = child->nextSibling()) |
- child->markCachedElementRectDirty(); |
-} |
- |
-IntPoint AXRenderObject::clickPoint() |
-{ |
- // Headings are usually much wider than their textual content. If the mid point is used, often it can be wrong. |
- if (isHeading() && children().size() == 1) |
- return children()[0]->clickPoint(); |
- |
- // use the default position unless this is an editable web area, in which case we use the selection bounds. |
- if (!isWebArea() || isReadOnly()) |
- return AXObject::clickPoint(); |
- |
- IntRect bounds = pixelSnappedIntRect(elementRect()); |
- return IntPoint(bounds.x() + (bounds.width() / 2), bounds.y() - (bounds.height() / 2)); |
-} |
- |
-// |
-// Hit testing. |
-// |
- |
-AXObject* AXRenderObject::accessibilityHitTest(const IntPoint& point) const |
-{ |
- if (!m_renderer || !m_renderer->hasLayer()) |
- return 0; |
- |
- RenderLayer* layer = toRenderBox(m_renderer)->layer(); |
- |
- HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active); |
- HitTestResult hitTestResult = HitTestResult(point); |
- layer->hitTest(request, hitTestResult); |
- if (!hitTestResult.innerNode()) |
- return 0; |
- |
- Node* node = hitTestResult.innerNode(); |
- |
- // Allow the hit test to return media control buttons. |
- if (node->isInShadowTree() && (!isHTMLInputElement(*node) || !node->isMediaControlElement())) |
- node = node->shadowHost(); |
- |
- if (isHTMLAreaElement(node)) |
- return accessibilityImageMapHitTest(toHTMLAreaElement(node), point); |
- |
- if (isHTMLOptionElement(node)) |
- node = toHTMLOptionElement(*node).ownerSelectElement(); |
- |
- RenderObject* obj = node->renderer(); |
- if (!obj) |
- return 0; |
- |
- AXObject* result = toAXObjectCacheImpl(obj->document().axObjectCache())->getOrCreate(obj); |
- result->updateChildrenIfNecessary(); |
- |
- // Allow the element to perform any hit-testing it might need to do to reach non-render children. |
- result = result->elementAccessibilityHitTest(point); |
- |
- if (result && result->accessibilityIsIgnored()) { |
- // If this element is the label of a control, a hit test should return the control. |
- if (result->isAXRenderObject()) { |
- AXObject* controlObject = toAXRenderObject(result)->correspondingControlForLabelElement(); |
- if (controlObject && !controlObject->exposesTitleUIElement()) |
- return controlObject; |
- } |
- |
- result = result->parentObjectUnignored(); |
- } |
- |
- return result; |
-} |
- |
-AXObject* AXRenderObject::elementAccessibilityHitTest(const IntPoint& point) const |
-{ |
- if (isSVGImage()) |
- return remoteSVGElementHitTest(point); |
- |
- return AXObject::elementAccessibilityHitTest(point); |
-} |
- |
-// |
-// High-level accessibility tree access. |
-// |
- |
-AXObject* AXRenderObject::computeParent() const |
-{ |
- if (!m_renderer) |
- return 0; |
- |
- if (ariaRoleAttribute() == MenuBarRole) |
- return axObjectCache()->getOrCreate(m_renderer->parent()); |
- |
- // menuButton and its corresponding menu are DOM siblings, but Accessibility needs them to be parent/child |
- if (ariaRoleAttribute() == MenuRole) { |
- AXObject* parent = menuButtonForMenu(); |
- if (parent) |
- return parent; |
- } |
- |
- RenderObject* parentObj = renderParentObject(); |
- if (parentObj) |
- return axObjectCache()->getOrCreate(parentObj); |
- |
- // WebArea's parent should be the scroll view containing it. |
- if (isWebArea()) |
- return axObjectCache()->getOrCreate(m_renderer->frame()->view()); |
- |
- return 0; |
-} |
- |
-AXObject* AXRenderObject::computeParentIfExists() const |
-{ |
- if (!m_renderer) |
- return 0; |
- |
- if (ariaRoleAttribute() == MenuBarRole) |
- return axObjectCache()->get(m_renderer->parent()); |
- |
- // menuButton and its corresponding menu are DOM siblings, but Accessibility needs them to be parent/child |
- if (ariaRoleAttribute() == MenuRole) { |
- AXObject* parent = menuButtonForMenu(); |
- if (parent) |
- return parent; |
- } |
- |
- RenderObject* parentObj = renderParentObject(); |
- if (parentObj) |
- return axObjectCache()->get(parentObj); |
- |
- // WebArea's parent should be the scroll view containing it. |
- if (isWebArea()) |
- return axObjectCache()->get(m_renderer->frame()->view()); |
- |
- return 0; |
-} |
- |
-// |
-// Low-level accessibility tree exploration, only for use within the accessibility module. |
-// |
- |
-AXObject* AXRenderObject::firstChild() const |
-{ |
- if (!m_renderer) |
- return 0; |
- |
- RenderObject* firstChild = firstChildConsideringContinuation(m_renderer); |
- |
- if (!firstChild) |
- return 0; |
- |
- return axObjectCache()->getOrCreate(firstChild); |
-} |
- |
-AXObject* AXRenderObject::nextSibling() const |
-{ |
- if (!m_renderer) |
- return 0; |
- |
- RenderObject* nextSibling = 0; |
- |
- RenderInline* inlineContinuation = m_renderer->isRenderBlock() ? toRenderBlock(m_renderer)->inlineElementContinuation() : 0; |
- if (inlineContinuation) { |
- // Case 1: node is a block and has an inline continuation. Next sibling is the inline continuation's first child. |
- nextSibling = firstChildConsideringContinuation(inlineContinuation); |
- } else if (m_renderer->isAnonymousBlock() && lastChildHasContinuation(m_renderer)) { |
- // Case 2: Anonymous block parent of the start of a continuation - skip all the way to |
- // after the parent of the end, since everything in between will be linked up via the continuation. |
- RenderObject* lastParent = endOfContinuations(toRenderBlock(m_renderer)->lastChild())->parent(); |
- while (lastChildHasContinuation(lastParent)) |
- lastParent = endOfContinuations(lastParent->slowLastChild())->parent(); |
- nextSibling = lastParent->nextSibling(); |
- } else if (RenderObject* ns = m_renderer->nextSibling()) { |
- // Case 3: node has an actual next sibling |
- nextSibling = ns; |
- } else if (isInlineWithContinuation(m_renderer)) { |
- // Case 4: node is an inline with a continuation. Next sibling is the next sibling of the end |
- // of the continuation chain. |
- nextSibling = endOfContinuations(m_renderer)->nextSibling(); |
- } else if (isInlineWithContinuation(m_renderer->parent())) { |
- // Case 5: node has no next sibling, and its parent is an inline with a continuation. |
- RenderObject* continuation = toRenderInline(m_renderer->parent())->continuation(); |
- |
- if (continuation->isRenderBlock()) { |
- // Case 5a: continuation is a block - in this case the block itself is the next sibling. |
- nextSibling = continuation; |
- } else { |
- // Case 5b: continuation is an inline - in this case the inline's first child is the next sibling. |
- nextSibling = firstChildConsideringContinuation(continuation); |
- } |
- } |
- |
- if (!nextSibling) |
- return 0; |
- |
- return axObjectCache()->getOrCreate(nextSibling); |
-} |
- |
-void AXRenderObject::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); |
- |
- m_haveChildren = true; |
- |
- if (!canHaveChildren()) |
- return; |
- |
- for (RefPtr<AXObject> obj = firstChild(); obj; obj = obj->nextSibling()) |
- addChild(obj.get()); |
- |
- addHiddenChildren(); |
- addAttachmentChildren(); |
- addPopupChildren(); |
- addImageMapChildren(); |
- addTextFieldChildren(); |
- addCanvasChildren(); |
- addRemoteSVGChildren(); |
- addInlineTextBoxChildren(); |
- |
- for (unsigned i = 0; i < m_children.size(); ++i) { |
- if (!m_children[i].get()->cachedParentObject()) |
- m_children[i].get()->setParent(this); |
- } |
-} |
- |
-bool AXRenderObject::canHaveChildren() const |
-{ |
- if (!m_renderer) |
- return false; |
- |
- return AXNodeObject::canHaveChildren(); |
-} |
- |
-void AXRenderObject::updateChildrenIfNecessary() |
-{ |
- if (needsToUpdateChildren()) |
- clearChildren(); |
- |
- AXObject::updateChildrenIfNecessary(); |
-} |
- |
-void AXRenderObject::clearChildren() |
-{ |
- AXObject::clearChildren(); |
- m_childrenDirty = false; |
-} |
- |
-AXObject* AXRenderObject::observableObject() const |
-{ |
- // Find the object going up the parent chain that is used in accessibility to monitor certain notifications. |
- for (RenderObject* renderer = m_renderer; renderer && renderer->node(); renderer = renderer->parent()) { |
- if (renderObjectIsObservable(renderer)) |
- return axObjectCache()->getOrCreate(renderer); |
- } |
- |
- return 0; |
-} |
- |
-// |
-// Properties of the object's owning document or page. |
-// |
- |
-double AXRenderObject::estimatedLoadingProgress() const |
-{ |
- if (!m_renderer) |
- return 0; |
- |
- if (isLoaded()) |
- return 1.0; |
- |
- if (LocalFrame* frame = m_renderer->document().frame()) |
- return frame->loader().progress().estimatedProgress(); |
- return 0; |
-} |
- |
-// |
-// DOM and Render tree access. |
-// |
- |
-Node* AXRenderObject::node() const |
-{ |
- return m_renderer ? m_renderer->node() : 0; |
-} |
- |
-Document* AXRenderObject::document() const |
-{ |
- if (!m_renderer) |
- return 0; |
- return &m_renderer->document(); |
-} |
- |
-FrameView* AXRenderObject::documentFrameView() const |
-{ |
- if (!m_renderer) |
- return 0; |
- |
- // this is the RenderObject's Document's LocalFrame's FrameView |
- return m_renderer->document().view(); |
-} |
- |
-Element* AXRenderObject::anchorElement() const |
-{ |
- if (!m_renderer) |
- return 0; |
- |
- AXObjectCacheImpl* cache = axObjectCache(); |
- RenderObject* currRenderer; |
- |
- // Search up the render tree for a RenderObject with a DOM node. Defer to an earlier continuation, though. |
- for (currRenderer = m_renderer; currRenderer && !currRenderer->node(); currRenderer = currRenderer->parent()) { |
- if (currRenderer->isAnonymousBlock()) { |
- RenderObject* continuation = toRenderBlock(currRenderer)->continuation(); |
- if (continuation) |
- return cache->getOrCreate(continuation)->anchorElement(); |
- } |
- } |
- |
- // bail if none found |
- if (!currRenderer) |
- return 0; |
- |
- // search up the DOM tree for an anchor element |
- // NOTE: this assumes that any non-image with an anchor is an HTMLAnchorElement |
- Node* node = currRenderer->node(); |
- for ( ; node; node = node->parentNode()) { |
- if (isHTMLAnchorElement(*node) || (node->renderer() && cache->getOrCreate(node->renderer())->isAnchor())) |
- return toElement(node); |
- } |
- |
- return 0; |
-} |
- |
-Widget* AXRenderObject::widgetForAttachmentView() const |
-{ |
- if (!isAttachment()) |
- return 0; |
- return toRenderPart(m_renderer)->widget(); |
-} |
- |
-// |
-// Selected text. |
-// |
- |
-AXObject::PlainTextRange AXRenderObject::selectedTextRange() const |
-{ |
- if (!isTextControl()) |
- return PlainTextRange(); |
- |
- if (isPasswordFieldAndShouldHideValue()) |
- return PlainTextRange(); |
- |
- AccessibilityRole ariaRole = ariaRoleAttribute(); |
- if (isNativeTextControl() && ariaRole == UnknownRole && m_renderer->isTextControl()) { |
- HTMLTextFormControlElement* textControl = toRenderTextControl(m_renderer)->textFormControlElement(); |
- return PlainTextRange(textControl->selectionStart(), textControl->selectionEnd() - textControl->selectionStart()); |
- } |
- |
- if (ariaRole == UnknownRole) |
- return PlainTextRange(); |
- |
- return ariaSelectedTextRange(); |
-} |
- |
-VisibleSelection AXRenderObject::selection() const |
-{ |
- return m_renderer->frame()->selection().selection(); |
-} |
- |
-// |
-// Modify or take an action on an object. |
-// |
- |
-void AXRenderObject::setSelectedTextRange(const PlainTextRange& range) |
-{ |
- if (isNativeTextControl() && m_renderer->isTextControl()) { |
- HTMLTextFormControlElement* textControl = toRenderTextControl(m_renderer)->textFormControlElement(); |
- textControl->setSelectionRange(range.start, range.start + range.length); |
- return; |
- } |
- |
- Document& document = m_renderer->document(); |
- LocalFrame* frame = document.frame(); |
- if (!frame) |
- return; |
- Node* node = m_renderer->node(); |
- frame->selection().setSelection(VisibleSelection(Position(node, range.start, Position::PositionIsOffsetInAnchor), |
- Position(node, range.start + range.length, Position::PositionIsOffsetInAnchor), DOWNSTREAM)); |
-} |
- |
-void AXRenderObject::setValue(const String& string) |
-{ |
- if (!node() || !node()->isElementNode()) |
- return; |
- if (!m_renderer || !m_renderer->isBoxModelObject()) |
- return; |
- |
- RenderBoxModelObject* renderer = toRenderBoxModelObject(m_renderer); |
- if (renderer->isTextField() && isHTMLInputElement(*node())) |
- toHTMLInputElement(*node()).setValue(string); |
- else if (renderer->isTextArea() && isHTMLTextAreaElement(*node())) |
- toHTMLTextAreaElement(*node()).setValue(string); |
-} |
- |
-// FIXME: This function should use an IntSize to avoid the conversion below. |
-void AXRenderObject::scrollTo(const IntPoint& point) const |
-{ |
- if (!m_renderer || !m_renderer->isBox()) |
- return; |
- |
- RenderBox* box = toRenderBox(m_renderer); |
- if (!box->canBeScrolledAndHasScrollableArea()) |
- return; |
- |
- box->scrollToOffset(IntSize(point.x(), point.y())); |
-} |
- |
-// |
-// Notifications that this object may have changed. |
-// |
- |
-void AXRenderObject::handleActiveDescendantChanged() |
-{ |
- Element* element = toElement(renderer()->node()); |
- if (!element) |
- return; |
- Document& doc = renderer()->document(); |
- if (!doc.frame()->selection().isFocusedAndActive() || doc.focusedElement() != element) |
- return; |
- AXRenderObject* activedescendant = toAXRenderObject(activeDescendant()); |
- |
- if (activedescendant && shouldNotifyActiveDescendant()) |
- toAXObjectCacheImpl(doc.axObjectCache())->postNotification(m_renderer, AXObjectCacheImpl::AXActiveDescendantChanged, true); |
-} |
- |
-void AXRenderObject::handleAriaExpandedChanged() |
-{ |
- // Find if a parent of this object should handle aria-expanded changes. |
- AXObject* containerParent = this->parentObject(); |
- while (containerParent) { |
- bool foundParent = false; |
- |
- switch (containerParent->roleValue()) { |
- case TreeRole: |
- case TreeGridRole: |
- case GridRole: |
- case TableRole: |
- case BrowserRole: |
- foundParent = true; |
- break; |
- default: |
- break; |
- } |
- |
- if (foundParent) |
- break; |
- |
- containerParent = containerParent->parentObject(); |
- } |
- |
- // Post that the row count changed. |
- if (containerParent) |
- axObjectCache()->postNotification(containerParent, document(), AXObjectCacheImpl::AXRowCountChanged, true); |
- |
- // Post that the specific row either collapsed or expanded. |
- AccessibilityExpanded expanded = isExpanded(); |
- if (!expanded) |
- return; |
- |
- if (roleValue() == RowRole || roleValue() == TreeItemRole) { |
- AXObjectCacheImpl::AXNotification notification = AXObjectCacheImpl::AXRowExpanded; |
- if (expanded == ExpandedCollapsed) |
- notification = AXObjectCacheImpl::AXRowCollapsed; |
- |
- axObjectCache()->postNotification(this, document(), notification, true); |
- } |
-} |
- |
-void AXRenderObject::textChanged() |
-{ |
- if (!m_renderer) |
- return; |
- |
- Settings* settings = document()->settings(); |
- if (settings && settings->inlineTextBoxAccessibilityEnabled() && roleValue() == StaticTextRole) |
- childrenChanged(); |
- |
- // Do this last - AXNodeObject::textChanged posts live region announcements, |
- // and we should update the inline text boxes first. |
- AXNodeObject::textChanged(); |
-} |
- |
-// |
-// Text metrics. Most of these should be deprecated, needs major cleanup. |
-// |
- |
-// NOTE: Consider providing this utility method as AX API |
-int AXRenderObject::index(const VisiblePosition& position) const |
-{ |
- if (position.isNull() || !isTextControl()) |
- return -1; |
- |
- if (renderObjectContainsPosition(m_renderer, position.deepEquivalent())) |
- return indexForVisiblePosition(position); |
- |
- return -1; |
-} |
- |
-VisiblePosition AXRenderObject::visiblePositionForIndex(int index) const |
-{ |
- if (!m_renderer) |
- return VisiblePosition(); |
- |
- if (isNativeTextControl() && m_renderer->isTextControl()) |
- return toRenderTextControl(m_renderer)->textFormControlElement()->visiblePositionForIndex(index); |
- |
- if (!allowsTextRanges() && !m_renderer->isText()) |
- return VisiblePosition(); |
- |
- Node* node = m_renderer->node(); |
- if (!node) |
- return VisiblePosition(); |
- |
- if (index <= 0) |
- return VisiblePosition(firstPositionInOrBeforeNode(node), DOWNSTREAM); |
- |
- Position start, end; |
- bool selected = Range::selectNodeContents(node, start, end); |
- if (!selected) |
- return VisiblePosition(); |
- |
- CharacterIterator it(start, end); |
- it.advance(index - 1); |
- return VisiblePosition(Position(it.endContainer(), it.endOffset(), Position::PositionIsOffsetInAnchor), UPSTREAM); |
-} |
- |
-int AXRenderObject::indexForVisiblePosition(const VisiblePosition& pos) const |
-{ |
- if (isNativeTextControl() && m_renderer->isTextControl()) { |
- HTMLTextFormControlElement* textControl = toRenderTextControl(m_renderer)->textFormControlElement(); |
- return textControl->indexForVisiblePosition(pos); |
- } |
- |
- if (!isTextControl()) |
- return 0; |
- |
- Node* node = m_renderer->node(); |
- if (!node) |
- return 0; |
- |
- Position indexPosition = pos.deepEquivalent(); |
- if (indexPosition.isNull() || highestEditableRoot(indexPosition, HasEditableAXRole) != node) |
- return 0; |
- |
- RefPtrWillBeRawPtr<Range> range = Range::create(m_renderer->document()); |
- range->setStart(node, 0, IGNORE_EXCEPTION); |
- range->setEnd(indexPosition, IGNORE_EXCEPTION); |
- |
- return TextIterator::rangeLength(range.get()); |
-} |
- |
-void AXRenderObject::addInlineTextBoxChildren() |
-{ |
- Settings* settings = document()->settings(); |
- if (!settings || !settings->inlineTextBoxAccessibilityEnabled()) |
- return; |
- |
- if (!renderer() || !renderer()->isText()) |
- return; |
- |
- if (renderer()->needsLayout()) { |
- // If a RenderText needs layout, its inline text boxes are either |
- // nonexistent or invalid, so defer until the layout happens and |
- // the renderer calls AXObjectCacheImpl::inlineTextBoxesUpdated. |
- return; |
- } |
- |
- RenderText* renderText = toRenderText(renderer()); |
- for (RefPtr<AbstractInlineTextBox> box = renderText->firstAbstractInlineTextBox(); box.get(); box = box->nextInlineTextBox()) { |
- AXObject* axObject = axObjectCache()->getOrCreate(box.get()); |
- if (!axObject->accessibilityIsIgnored()) |
- m_children.append(axObject); |
- } |
-} |
- |
-void AXRenderObject::lineBreaks(Vector<int>& lineBreaks) const |
-{ |
- if (!isTextControl()) |
- return; |
- |
- VisiblePosition visiblePos = visiblePositionForIndex(0); |
- VisiblePosition savedVisiblePos = visiblePos; |
- visiblePos = nextLinePosition(visiblePos, 0); |
- while (!visiblePos.isNull() && visiblePos != savedVisiblePos) { |
- lineBreaks.append(indexForVisiblePosition(visiblePos)); |
- savedVisiblePos = visiblePos; |
- visiblePos = nextLinePosition(visiblePos, 0); |
- } |
-} |
- |
-// |
-// Private. |
-// |
- |
-bool AXRenderObject::isAllowedChildOfTree() const |
-{ |
- // Determine if this is in a tree. If so, we apply special behavior to make it work like an AXOutline. |
- AXObject* axObj = parentObject(); |
- bool isInTree = false; |
- while (axObj) { |
- if (axObj->isTree()) { |
- isInTree = true; |
- break; |
- } |
- axObj = axObj->parentObject(); |
- } |
- |
- // If the object is in a tree, only tree items should be exposed (and the children of tree items). |
- if (isInTree) { |
- AccessibilityRole role = roleValue(); |
- if (role != TreeItemRole && role != StaticTextRole) |
- return false; |
- } |
- return true; |
-} |
- |
-void AXRenderObject::ariaListboxSelectedChildren(AccessibilityChildrenVector& result) |
-{ |
- bool isMulti = isMultiSelectable(); |
- |
- AccessibilityChildrenVector childObjects = children(); |
- unsigned childrenSize = childObjects.size(); |
- for (unsigned k = 0; k < childrenSize; ++k) { |
- // Every child should have aria-role option, and if so, check for selected attribute/state. |
- AXObject* child = childObjects[k].get(); |
- if (child->isSelected() && child->ariaRoleAttribute() == ListBoxOptionRole) { |
- result.append(child); |
- if (!isMulti) |
- return; |
- } |
- } |
-} |
- |
-AXObject::PlainTextRange AXRenderObject::ariaSelectedTextRange() const |
-{ |
- Node* node = m_renderer->node(); |
- if (!node) |
- return PlainTextRange(); |
- |
- VisibleSelection visibleSelection = selection(); |
- RefPtrWillBeRawPtr<Range> currentSelectionRange = visibleSelection.toNormalizedRange(); |
- if (!currentSelectionRange || !currentSelectionRange->intersectsNode(node, IGNORE_EXCEPTION)) |
- return PlainTextRange(); |
- |
- int start = indexForVisiblePosition(visibleSelection.visibleStart()); |
- int end = indexForVisiblePosition(visibleSelection.visibleEnd()); |
- |
- return PlainTextRange(start, end - start); |
-} |
- |
-bool AXRenderObject::nodeIsTextControl(const Node* node) const |
-{ |
- if (!node) |
- return false; |
- |
- const AXObject* axObjectForNode = axObjectCache()->getOrCreate(const_cast<Node*>(node)); |
- if (!axObjectForNode) |
- return false; |
- |
- return axObjectForNode->isTextControl(); |
-} |
- |
-bool AXRenderObject::isTabItemSelected() const |
-{ |
- if (!isTabItem() || !m_renderer) |
- return false; |
- |
- Node* node = m_renderer->node(); |
- if (!node || !node->isElementNode()) |
- return false; |
- |
- // The ARIA spec says a tab item can also be selected if it is aria-labeled by a tabpanel |
- // that has keyboard focus inside of it, or if a tabpanel in its aria-controls list has KB |
- // focus inside of it. |
- AXObject* focusedElement = focusedUIElement(); |
- if (!focusedElement) |
- return false; |
- |
- WillBeHeapVector<RawPtrWillBeMember<Element> > elements; |
- elementsFromAttribute(elements, aria_controlsAttr); |
- |
- unsigned count = elements.size(); |
- for (unsigned k = 0; k < count; ++k) { |
- Element* element = elements[k]; |
- AXObject* tabPanel = axObjectCache()->getOrCreate(element); |
- |
- // A tab item should only control tab panels. |
- if (!tabPanel || tabPanel->roleValue() != TabPanelRole) |
- continue; |
- |
- AXObject* checkFocusElement = focusedElement; |
- // Check if the focused element is a descendant of the element controlled by the tab item. |
- while (checkFocusElement) { |
- if (tabPanel == checkFocusElement) |
- return true; |
- checkFocusElement = checkFocusElement->parentObject(); |
- } |
- } |
- |
- return false; |
-} |
- |
-AXObject* AXRenderObject::accessibilityImageMapHitTest(HTMLAreaElement* area, const IntPoint& point) const |
-{ |
- if (!area) |
- return 0; |
- |
- AXObject* parent = axObjectCache()->getOrCreate(area->imageElement()); |
- if (!parent) |
- return 0; |
- |
- AXObject::AccessibilityChildrenVector children = parent->children(); |
- unsigned count = children.size(); |
- for (unsigned k = 0; k < count; ++k) { |
- if (children[k]->elementRect().contains(point)) |
- return children[k].get(); |
- } |
- |
- return 0; |
-} |
- |
-bool AXRenderObject::renderObjectIsObservable(RenderObject* renderer) const |
-{ |
- // AX clients will listen for AXValueChange on a text control. |
- if (renderer->isTextControl()) |
- return true; |
- |
- // AX clients will listen for AXSelectedChildrenChanged on listboxes. |
- Node* node = renderer->node(); |
- if (nodeHasRole(node, "listbox") || (renderer->isBoxModelObject() && toRenderBoxModelObject(renderer)->isListBox())) |
- return true; |
- |
- // Textboxes should send out notifications. |
- if (nodeHasRole(node, "textbox")) |
- return true; |
- |
- return false; |
-} |
- |
-RenderObject* AXRenderObject::renderParentObject() const |
-{ |
- if (!m_renderer) |
- return 0; |
- |
- RenderObject* startOfConts = m_renderer->isRenderBlock() ? startOfContinuations(m_renderer) : 0; |
- if (startOfConts) { |
- // Case 1: node is a block and is an inline's continuation. Parent |
- // is the start of the continuation chain. |
- return startOfConts; |
- } |
- |
- RenderObject* parent = m_renderer->parent(); |
- startOfConts = parent && parent->isRenderInline() ? startOfContinuations(parent) : 0; |
- if (startOfConts) { |
- // Case 2: node's parent is an inline which is some node's continuation; parent is |
- // the earliest node in the continuation chain. |
- return startOfConts; |
- } |
- |
- RenderObject* firstChild = parent ? parent->slowFirstChild() : 0; |
- if (firstChild && firstChild->node()) { |
- // Case 3: The first sibling is the beginning of a continuation chain. Find the origin of that continuation. |
- // Get the node's renderer and follow that continuation chain until the first child is found. |
- for (RenderObject* nodeRenderFirstChild = firstChild->node()->renderer(); nodeRenderFirstChild != firstChild; nodeRenderFirstChild = firstChild->node()->renderer()) { |
- for (RenderObject* contsTest = nodeRenderFirstChild; contsTest; contsTest = nextContinuation(contsTest)) { |
- if (contsTest == firstChild) { |
- parent = nodeRenderFirstChild->parent(); |
- break; |
- } |
- } |
- RenderObject* newFirstChild = parent->slowFirstChild(); |
- if (firstChild == newFirstChild) |
- break; |
- firstChild = newFirstChild; |
- if (!firstChild->node()) |
- break; |
- } |
- } |
- |
- return parent; |
-} |
- |
-bool AXRenderObject::isDescendantOfElementType(const HTMLQualifiedName& tagName) const |
-{ |
- for (RenderObject* parent = m_renderer->parent(); parent; parent = parent->parent()) { |
- if (parent->node() && parent->node()->hasTagName(tagName)) |
- return true; |
- } |
- return false; |
-} |
- |
-bool AXRenderObject::isSVGImage() const |
-{ |
- return remoteSVGRootElement(); |
-} |
- |
-void AXRenderObject::detachRemoteSVGRoot() |
-{ |
- if (AXSVGRoot* root = remoteSVGRootElement()) |
- root->setParent(0); |
-} |
- |
-AXSVGRoot* AXRenderObject::remoteSVGRootElement() const |
-{ |
- if (!m_renderer || !m_renderer->isRenderImage()) |
- return 0; |
- |
- ImageResource* cachedImage = toRenderImage(m_renderer)->cachedImage(); |
- if (!cachedImage) |
- return 0; |
- |
- Image* image = cachedImage->image(); |
- if (!image || !image->isSVGImage()) |
- return 0; |
- |
- FrameView* frameView = toSVGImage(image)->frameView(); |
- if (!frameView) |
- return 0; |
- Document* doc = frameView->frame().document(); |
- if (!doc || !doc->isSVGDocument()) |
- return 0; |
- |
- Settings* settings = doc->settings(); |
- if (settings && !settings->accessibilityEnabled()) |
- settings->setAccessibilityEnabled(true); |
- |
- SVGSVGElement* rootElement = doc->accessSVGExtensions().rootElement(); |
- if (!rootElement) |
- return 0; |
- RenderObject* rendererRoot = rootElement->renderer(); |
- if (!rendererRoot) |
- return 0; |
- |
- AXObject* rootSVGObject = toAXObjectCacheImpl(doc->axObjectCache())->getOrCreate(rendererRoot); |
- |
- // In order to connect the AX hierarchy from the SVG root element from the loaded resource |
- // the parent must be set, because there's no other way to get back to who created the image. |
- ASSERT(rootSVGObject && rootSVGObject->isAXSVGRoot()); |
- if (!rootSVGObject->isAXSVGRoot()) |
- return 0; |
- |
- return toAXSVGRoot(rootSVGObject); |
-} |
- |
-AXObject* AXRenderObject::remoteSVGElementHitTest(const IntPoint& point) const |
-{ |
- AXObject* remote = remoteSVGRootElement(); |
- if (!remote) |
- return 0; |
- |
- IntSize offset = point - roundedIntPoint(elementRect().location()); |
- return remote->accessibilityHitTest(IntPoint(offset)); |
-} |
- |
-// The boundingBox for elements within the remote SVG element needs to be offset by its position |
-// within the parent page, otherwise they are in relative coordinates only. |
-void AXRenderObject::offsetBoundingBoxForRemoteSVGElement(LayoutRect& rect) const |
-{ |
- for (AXObject* parent = parentObject(); parent; parent = parent->parentObject()) { |
- if (parent->isAXSVGRoot()) { |
- rect.moveBy(parent->parentObject()->elementRect().location()); |
- break; |
- } |
- } |
-} |
- |
-// Hidden children are those that are not rendered or visible, but are specifically marked as aria-hidden=false, |
-// meaning that they should be exposed to the AX hierarchy. |
-void AXRenderObject::addHiddenChildren() |
-{ |
- Node* node = this->node(); |
- if (!node) |
- return; |
- |
- // First do a quick run through to determine if we have any hidden nodes (most often we will not). |
- // If we do have hidden nodes, we need to determine where to insert them so they match DOM order as close as possible. |
- bool shouldInsertHiddenNodes = false; |
- for (Node* child = node->firstChild(); child; child = child->nextSibling()) { |
- if (!child->renderer() && isNodeAriaVisible(child)) { |
- shouldInsertHiddenNodes = true; |
- break; |
- } |
- } |
- |
- if (!shouldInsertHiddenNodes) |
- return; |
- |
- // Iterate through all of the children, including those that may have already been added, and |
- // try to insert hidden nodes in the correct place in the DOM order. |
- unsigned insertionIndex = 0; |
- for (Node* child = node->firstChild(); child; child = child->nextSibling()) { |
- if (child->renderer()) { |
- // Find out where the last render sibling is located within m_children. |
- AXObject* childObject = axObjectCache()->get(child->renderer()); |
- if (childObject && childObject->accessibilityIsIgnored()) { |
- AccessibilityChildrenVector children = childObject->children(); |
- if (children.size()) |
- childObject = children.last().get(); |
- else |
- childObject = 0; |
- } |
- |
- if (childObject) |
- insertionIndex = m_children.find(childObject) + 1; |
- continue; |
- } |
- |
- if (!isNodeAriaVisible(child)) |
- continue; |
- |
- unsigned previousSize = m_children.size(); |
- if (insertionIndex > previousSize) |
- insertionIndex = previousSize; |
- |
- insertChild(axObjectCache()->getOrCreate(child), insertionIndex); |
- insertionIndex += (m_children.size() - previousSize); |
- } |
-} |
- |
-void AXRenderObject::addTextFieldChildren() |
-{ |
- Node* node = this->node(); |
- if (!isHTMLInputElement(node)) |
- return; |
- |
- HTMLInputElement& input = toHTMLInputElement(*node); |
- Element* spinButtonElement = input.userAgentShadowRoot()->getElementById(ShadowElementNames::spinButton()); |
- if (!spinButtonElement || !spinButtonElement->isSpinButtonElement()) |
- return; |
- |
- AXSpinButton* axSpinButton = toAXSpinButton(axObjectCache()->getOrCreate(SpinButtonRole)); |
- axSpinButton->setSpinButtonElement(toSpinButtonElement(spinButtonElement)); |
- axSpinButton->setParent(this); |
- m_children.append(axSpinButton); |
-} |
- |
-void AXRenderObject::addImageMapChildren() |
-{ |
- RenderBoxModelObject* cssBox = renderBoxModelObject(); |
- if (!cssBox || !cssBox->isRenderImage()) |
- return; |
- |
- HTMLMapElement* map = toRenderImage(cssBox)->imageMap(); |
- if (!map) |
- return; |
- |
- for (HTMLAreaElement& area : Traversal<HTMLAreaElement>::descendantsOf(*map)) { |
- // add an <area> element for this child if it has a link |
- if (area.isLink()) { |
- AXImageMapLink* areaObject = toAXImageMapLink(axObjectCache()->getOrCreate(ImageMapLinkRole)); |
- areaObject->setHTMLAreaElement(&area); |
- areaObject->setHTMLMapElement(map); |
- areaObject->setParent(this); |
- if (!areaObject->accessibilityIsIgnored()) |
- m_children.append(areaObject); |
- else |
- axObjectCache()->remove(areaObject->axObjectID()); |
- } |
- } |
-} |
- |
-void AXRenderObject::addCanvasChildren() |
-{ |
- if (!isHTMLCanvasElement(node())) |
- return; |
- |
- // If it's a canvas, it won't have rendered children, but it might have accessible fallback content. |
- // Clear m_haveChildren because AXNodeObject::addChildren will expect it to be false. |
- ASSERT(!m_children.size()); |
- m_haveChildren = false; |
- AXNodeObject::addChildren(); |
-} |
- |
-void AXRenderObject::addAttachmentChildren() |
-{ |
- if (!isAttachment()) |
- return; |
- |
- // FrameView's need to be inserted into the AX hierarchy when encountered. |
- Widget* widget = widgetForAttachmentView(); |
- if (!widget || !widget->isFrameView()) |
- return; |
- |
- AXObject* axWidget = axObjectCache()->getOrCreate(widget); |
- if (!axWidget->accessibilityIsIgnored()) |
- m_children.append(axWidget); |
-} |
- |
-void AXRenderObject::addPopupChildren() |
-{ |
- if (!isHTMLInputElement(node())) |
- return; |
- if (AXObject* axPopup = toHTMLInputElement(node())->popupRootAXObject()) |
- m_children.append(axPopup); |
-} |
- |
-void AXRenderObject::addRemoteSVGChildren() |
-{ |
- AXSVGRoot* root = remoteSVGRootElement(); |
- if (!root) |
- return; |
- |
- root->setParent(this); |
- |
- if (root->accessibilityIsIgnored()) { |
- AccessibilityChildrenVector children = root->children(); |
- unsigned length = children.size(); |
- for (unsigned i = 0; i < length; ++i) |
- m_children.append(children[i]); |
- } else { |
- m_children.append(root); |
- } |
-} |
- |
-void AXRenderObject::ariaSelectedRows(AccessibilityChildrenVector& result) |
-{ |
- // Get all the rows. |
- AccessibilityChildrenVector allRows; |
- if (isTree()) |
- ariaTreeRows(allRows); |
- else if (isAXTable() && toAXTable(this)->supportsSelectedRows()) |
- allRows = toAXTable(this)->rows(); |
- |
- // Determine which rows are selected. |
- bool isMulti = isMultiSelectable(); |
- |
- // Prefer active descendant over aria-selected. |
- AXObject* activeDesc = activeDescendant(); |
- if (activeDesc && (activeDesc->isTreeItem() || activeDesc->isTableRow())) { |
- result.append(activeDesc); |
- if (!isMulti) |
- return; |
- } |
- |
- unsigned count = allRows.size(); |
- for (unsigned k = 0; k < count; ++k) { |
- if (allRows[k]->isSelected()) { |
- result.append(allRows[k]); |
- if (!isMulti) |
- break; |
- } |
- } |
-} |
- |
-bool AXRenderObject::elementAttributeValue(const QualifiedName& attributeName) const |
-{ |
- if (!m_renderer) |
- return false; |
- |
- return equalIgnoringCase(getAttribute(attributeName), "true"); |
-} |
- |
-bool AXRenderObject::inheritsPresentationalRole() const |
-{ |
- // ARIA states if an item can get focus, it should not be presentational. |
- if (canSetFocusAttribute()) |
- return false; |
- |
- // ARIA spec says that when a parent object is presentational, and it has required child elements, |
- // those child elements are also presentational. For example, <li> becomes presentational from <ul>. |
- // http://www.w3.org/WAI/PF/aria/complete#presentation |
- if (roleValue() != ListItemRole && roleValue() != ListMarkerRole) |
- return false; |
- |
- AXObject* parent = parentObject(); |
- if (!parent->isAXRenderObject()) |
- return false; |
- |
- Node* elementNode = toAXRenderObject(parent)->node(); |
- if (!elementNode || !elementNode->isElementNode()) |
- return false; |
- |
- QualifiedName tagName = toElement(elementNode)->tagQName(); |
- if (tagName == ulTag || tagName == olTag || tagName == dlTag) |
- return (parent->roleValue() == NoneRole || parent->roleValue() == PresentationalRole); |
- |
- return false; |
-} |
- |
-LayoutRect AXRenderObject::computeElementRect() const |
-{ |
- RenderObject* obj = m_renderer; |
- |
- if (!obj) |
- return LayoutRect(); |
- |
- if (obj->node()) // If we are a continuation, we want to make sure to use the primary renderer. |
- obj = obj->node()->renderer(); |
- |
- // absoluteFocusRingBoundingBox will query the hierarchy below this element, which for large webpages can be very slow. |
- // For a web area, which will have the most elements of any element, absoluteQuads should be used. |
- // We should also use absoluteQuads for SVG elements, otherwise transforms won't be applied. |
- |
- LayoutRect result; |
- if (obj->isText()) { |
- Vector<FloatQuad> quads; |
- toRenderText(obj)->absoluteQuads(quads, 0, RenderText::ClipToEllipsis); |
- result = boundingBoxForQuads(obj, quads); |
- } else if (isWebArea() || obj->isSVGRoot()) { |
- result = obj->absoluteBoundingBoxRect(); |
- } else { |
- result = obj->absoluteFocusRingBoundingBoxRect(); |
- } |
- |
- Document* document = this->document(); |
- if (document && document->isSVGDocument()) |
- offsetBoundingBoxForRemoteSVGElement(result); |
- if (document && document->frame() && document->frame()->pagePopupOwner()) { |
- IntPoint popupOrigin = document->view()->contentsToScreen(IntRect()).location(); |
- IntPoint mainOrigin = axObjectCache()->rootObject()->documentFrameView()->contentsToScreen(IntRect()).location(); |
- result.moveBy(IntPoint(popupOrigin - mainOrigin)); |
- } |
- |
- // The size of the web area should be the content size, not the clipped size. |
- if (isWebArea() && obj->frame()->view()) |
- result.setSize(obj->frame()->view()->contentsSize()); |
- |
- // Checkboxes and radio buttons include their label as part of their rect. |
- if (isCheckboxOrRadio()) { |
- HTMLLabelElement* label = labelForElement(toElement(m_renderer->node())); |
- if (label && label->renderer()) { |
- LayoutRect labelRect = axObjectCache()->getOrCreate(label)->elementRect(); |
- result.unite(labelRect); |
- } |
- } |
- |
- return result; |
-} |
- |
-} // namespace blink |