2 * Copyright (C) 2008-2020 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include "AccessibilityRenderObject.h"
33 #include "AXObjectCache.h"
34 #include "AccessibilityImageMapLink.h"
35 #include "AccessibilityLabel.h"
36 #include "AccessibilityListBox.h"
37 #include "AccessibilitySVGRoot.h"
38 #include "AccessibilitySpinButton.h"
39 #include "AccessibilityTable.h"
40 #include "CachedImage.h"
43 #include "ElementIterator.h"
44 #include "EventHandler.h"
45 #include "FloatRect.h"
47 #include "FrameLoader.h"
48 #include "FrameSelection.h"
49 #include "GeometryUtilities.h"
50 #include "HTMLAreaElement.h"
51 #include "HTMLAudioElement.h"
52 #include "HTMLDetailsElement.h"
53 #include "HTMLFormElement.h"
54 #include "HTMLFrameElementBase.h"
55 #include "HTMLImageElement.h"
56 #include "HTMLInputElement.h"
57 #include "HTMLLabelElement.h"
58 #include "HTMLMapElement.h"
59 #include "HTMLMeterElement.h"
60 #include "HTMLNames.h"
61 #include "HTMLOptionElement.h"
62 #include "HTMLOptionsCollection.h"
63 #include "HTMLParserIdioms.h"
64 #include "HTMLSelectElement.h"
65 #include "HTMLSummaryElement.h"
66 #include "HTMLTableElement.h"
67 #include "HTMLTextAreaElement.h"
68 #include "HTMLVideoElement.h"
69 #include "HitTestRequest.h"
70 #include "HitTestResult.h"
72 #include "LocalizedStrings.h"
75 #include "ProgressTracker.h"
77 #include "RenderButton.h"
78 #include "RenderFileUploadControl.h"
79 #include "RenderHTMLCanvas.h"
80 #include "RenderImage.h"
81 #include "RenderInline.h"
82 #include "RenderIterator.h"
83 #include "RenderLayer.h"
84 #include "RenderLineBreak.h"
85 #include "RenderListBox.h"
86 #include "RenderListItem.h"
87 #include "RenderListMarker.h"
88 #include "RenderMathMLBlock.h"
89 #include "RenderMenuList.h"
90 #include "RenderSVGRoot.h"
91 #include "RenderSVGShape.h"
92 #include "RenderTableCell.h"
93 #include "RenderText.h"
94 #include "RenderTextControl.h"
95 #include "RenderTextControlSingleLine.h"
96 #include "RenderTextFragment.h"
97 #include "RenderTheme.h"
98 #include "RenderView.h"
99 #include "RenderWidget.h"
100 #include "RenderedPosition.h"
101 #include "SVGDocument.h"
102 #include "SVGImage.h"
103 #include "SVGSVGElement.h"
105 #include "TextControlInnerElements.h"
106 #include "TextIterator.h"
107 #include "VisibleUnits.h"
108 #include <wtf/NeverDestroyed.h>
109 #include <wtf/StdLibExtras.h>
110 #include <wtf/unicode/CharacterNames.h>
114 using namespace HTMLNames;
116 AccessibilityRenderObject::AccessibilityRenderObject(RenderObject* renderer)
117 : AccessibilityNodeObject(renderer->node())
118 , m_renderer(makeWeakPtr(renderer))
121 m_renderer->setHasAXObject(true);
125 AccessibilityRenderObject::~AccessibilityRenderObject()
127 ASSERT(isDetached());
130 void AccessibilityRenderObject::init()
132 AccessibilityNodeObject::init();
135 Ref<AccessibilityRenderObject> AccessibilityRenderObject::create(RenderObject* renderer)
137 return adoptRef(*new AccessibilityRenderObject(renderer));
140 void AccessibilityRenderObject::detachRemoteParts(AccessibilityDetachmentType detachmentType)
142 AccessibilityNodeObject::detachRemoteParts(detachmentType);
144 detachRemoteSVGRoot();
148 m_renderer->setHasAXObject(false);
150 m_renderer = nullptr;
153 RenderBoxModelObject* AccessibilityRenderObject::renderBoxModelObject() const
155 if (!is<RenderBoxModelObject>(renderer()))
157 return downcast<RenderBoxModelObject>(renderer());
160 void AccessibilityRenderObject::setRenderer(RenderObject* renderer)
162 m_renderer = makeWeakPtr(renderer);
163 setNode(renderer->node());
166 static inline bool isInlineWithContinuation(RenderObject& object)
168 return is<RenderInline>(object) && downcast<RenderInline>(object).continuation();
171 static inline RenderObject* firstChildInContinuation(RenderInline& renderer)
173 auto* continuation = renderer.continuation();
175 while (continuation) {
176 if (is<RenderBlock>(*continuation))
178 if (RenderObject* child = continuation->firstChild())
180 continuation = downcast<RenderInline>(*continuation).continuation();
186 static inline RenderObject* firstChildConsideringContinuation(RenderObject& renderer)
188 RenderObject* firstChild = renderer.firstChildSlow();
190 // We don't want to include the end of a continuation as the firstChild of the
191 // anonymous parent, because everything has already been linked up via continuation.
192 // CSS first-letter selector is an example of this case.
193 if (renderer.isAnonymous() && is<RenderInline>(firstChild) && downcast<RenderInline>(*firstChild).isContinuation())
194 firstChild = nullptr;
196 if (!firstChild && isInlineWithContinuation(renderer))
197 firstChild = firstChildInContinuation(downcast<RenderInline>(renderer));
203 static inline RenderObject* lastChildConsideringContinuation(RenderObject& renderer)
205 if (!is<RenderInline>(renderer) && !is<RenderBlock>(renderer))
208 RenderObject* lastChild = downcast<RenderBoxModelObject>(renderer).lastChild();
209 for (auto* current = &downcast<RenderBoxModelObject>(renderer); current; ) {
210 if (RenderObject* newLastChild = current->lastChild())
211 lastChild = newLastChild;
213 current = current->inlineContinuation();
219 AccessibilityObject* AccessibilityRenderObject::firstChild() const
224 RenderObject* firstChild = firstChildConsideringContinuation(*m_renderer);
226 // If an object can't have children, then it is using this method to help
227 // calculate some internal property (like its description).
228 // In this case, it should check the Node level for children in case they're
229 // not rendered (like a <meter> element).
230 if (!firstChild && !canHaveChildren())
231 return AccessibilityNodeObject::firstChild();
233 auto objectCache = axObjectCache();
234 return objectCache ? objectCache->getOrCreate(firstChild) : nullptr;
237 AccessibilityObject* AccessibilityRenderObject::lastChild() const
242 RenderObject* lastChild = lastChildConsideringContinuation(*m_renderer);
244 if (!lastChild && !canHaveChildren())
245 return AccessibilityNodeObject::lastChild();
247 auto objectCache = axObjectCache();
248 return objectCache ? objectCache->getOrCreate(lastChild) : nullptr;
251 static inline RenderInline* startOfContinuations(RenderObject& renderer)
253 if (!is<RenderElement>(renderer))
255 auto& renderElement = downcast<RenderElement>(renderer);
256 if (is<RenderInline>(renderElement) && renderElement.isContinuation() && is<RenderInline>(renderElement.element()->renderer()))
257 return downcast<RenderInline>(renderer.node()->renderer());
259 // Blocks with a previous continuation always have a next continuation
260 if (is<RenderBlock>(renderElement) && downcast<RenderBlock>(renderElement).inlineContinuation())
261 return downcast<RenderInline>(downcast<RenderBlock>(renderElement).inlineContinuation()->element()->renderer());
266 static inline RenderObject* endOfContinuations(RenderObject& renderer)
268 if (!is<RenderInline>(renderer) && !is<RenderBlock>(renderer))
271 auto* previous = &downcast<RenderBoxModelObject>(renderer);
272 for (auto* current = previous; current; ) {
274 current = current->inlineContinuation();
281 static inline RenderObject* childBeforeConsideringContinuations(RenderInline* renderer, RenderObject* child)
283 RenderObject* previous = nullptr;
284 for (RenderBoxModelObject* currentContainer = renderer; currentContainer; ) {
285 if (is<RenderInline>(*currentContainer)) {
286 auto* current = currentContainer->firstChild();
288 if (current == child)
291 current = current->nextSibling();
294 currentContainer = currentContainer->continuation();
295 } else if (is<RenderBlock>(*currentContainer)) {
296 if (currentContainer == child)
299 previous = currentContainer;
300 currentContainer = currentContainer->inlineContinuation();
304 ASSERT_NOT_REACHED();
308 static inline bool firstChildIsInlineContinuation(RenderElement& renderer)
310 RenderObject* child = renderer.firstChild();
311 return is<RenderInline>(child) && downcast<RenderInline>(*child).isContinuation();
314 AccessibilityObject* AccessibilityRenderObject::previousSibling() const
319 RenderObject* previousSibling = nullptr;
321 // Case 1: The node is a block and is an inline's continuation. In that case, the inline's
322 // last child is our previous sibling (or further back in the continuation chain)
323 RenderInline* startOfConts;
324 if (is<RenderBox>(*m_renderer) && (startOfConts = startOfContinuations(*m_renderer)))
325 previousSibling = childBeforeConsideringContinuations(startOfConts, renderer());
327 // Case 2: Anonymous block parent of the end of a continuation - skip all the way to before
328 // the parent of the start, since everything in between will be linked up via the continuation.
329 else if (m_renderer->isAnonymousBlock() && firstChildIsInlineContinuation(downcast<RenderBlock>(*m_renderer))) {
330 RenderBlock& renderBlock = downcast<RenderBlock>(*m_renderer);
331 auto* firstParent = startOfContinuations(*renderBlock.firstChild())->parent();
333 while (firstChildIsInlineContinuation(*firstParent))
334 firstParent = startOfContinuations(*firstParent->firstChild())->parent();
335 previousSibling = firstParent->previousSibling();
338 // Case 3: The node has an actual previous sibling
339 else if (RenderObject* ps = m_renderer->previousSibling())
340 previousSibling = ps;
342 // Case 4: This node has no previous siblings, but its parent is an inline,
343 // and is another node's inline continutation. Follow the continuation chain.
344 else if (is<RenderInline>(m_renderer->parent()) && (startOfConts = startOfContinuations(*m_renderer->parent())))
345 previousSibling = childBeforeConsideringContinuations(startOfConts, m_renderer->parent()->firstChild());
347 if (!previousSibling)
350 if (auto* objectCache = axObjectCache())
351 return objectCache->getOrCreate(previousSibling);
356 static inline bool lastChildHasContinuation(RenderElement& renderer)
358 RenderObject* child = renderer.lastChild();
359 return child && isInlineWithContinuation(*child);
362 AccessibilityObject* AccessibilityRenderObject::nextSibling() const
364 if (!m_renderer || is<RenderView>(*m_renderer))
367 RenderObject* nextSibling = nullptr;
369 // Case 1: node is a block and has an inline continuation. Next sibling is the inline continuation's
371 RenderInline* inlineContinuation;
372 if (is<RenderBlock>(*m_renderer) && (inlineContinuation = downcast<RenderBlock>(*m_renderer).inlineContinuation()))
373 nextSibling = firstChildConsideringContinuation(*inlineContinuation);
375 // Case 2: Anonymous block parent of the start of a continuation - skip all the way to
376 // after the parent of the end, since everything in between will be linked up via the continuation.
377 else if (m_renderer->isAnonymousBlock() && lastChildHasContinuation(downcast<RenderBlock>(*m_renderer))) {
378 RenderElement* lastParent = endOfContinuations(*downcast<RenderBlock>(*m_renderer).lastChild())->parent();
380 while (lastChildHasContinuation(*lastParent))
381 lastParent = endOfContinuations(*lastParent->lastChild())->parent();
382 nextSibling = lastParent->nextSibling();
385 // Case 3: node has an actual next sibling
386 else if (RenderObject* ns = m_renderer->nextSibling())
389 // Case 4: node is an inline with a continuation. Next sibling is the next sibling of the end
390 // of the continuation chain.
391 else if (isInlineWithContinuation(*m_renderer))
392 nextSibling = endOfContinuations(*m_renderer)->nextSibling();
394 // Case 5: node has no next sibling, and its parent is an inline with a continuation.
395 // Case 5.1: After case 4, (the element was inline w/ continuation but had no sibling), then check it's parent.
396 if (!nextSibling && m_renderer->parent() && isInlineWithContinuation(*m_renderer->parent())) {
397 auto& continuation = *downcast<RenderInline>(*m_renderer->parent()).continuation();
399 // Case 5a: continuation is a block - in this case the block itself is the next sibling.
400 if (is<RenderBlock>(continuation))
401 nextSibling = &continuation;
402 // Case 5b: continuation is an inline - in this case the inline's first child is the next sibling
404 nextSibling = firstChildConsideringContinuation(continuation);
406 // After case 4, there are chances that nextSibling has the same node as the current renderer,
407 // which might lead to adding the same child repeatedly.
408 if (nextSibling && nextSibling->node() == m_renderer->node()) {
409 if (AccessibilityObject* nextObj = axObjectCache()->getOrCreate(nextSibling))
410 return nextObj->nextSibling();
417 auto* objectCache = axObjectCache();
421 // Make sure next sibling has the same parent.
422 auto* nextObject = objectCache->getOrCreate(nextSibling);
423 if (nextObject && nextObject->parentObject() != this->parentObject())
429 static RenderBoxModelObject* nextContinuation(RenderObject& renderer)
431 if (is<RenderInline>(renderer) && !renderer.isReplaced())
432 return downcast<RenderInline>(renderer).continuation();
433 if (is<RenderBlock>(renderer))
434 return downcast<RenderBlock>(renderer).inlineContinuation();
438 RenderObject* AccessibilityRenderObject::renderParentObject() const
443 RenderElement* parent = m_renderer->parent();
445 // Case 1: node is a block and is an inline's continuation. Parent
446 // is the start of the continuation chain.
447 RenderInline* startOfConts = nullptr;
448 RenderObject* firstChild = nullptr;
449 if (is<RenderBlock>(*m_renderer) && (startOfConts = startOfContinuations(*m_renderer)))
450 parent = startOfConts;
452 // Case 2: node's parent is an inline which is some node's continuation; parent is
453 // the earliest node in the continuation chain.
454 else if (is<RenderInline>(parent) && (startOfConts = startOfContinuations(*parent)))
455 parent = startOfConts;
457 // Case 3: The first sibling is the beginning of a continuation chain. Find the origin of that continuation.
458 else if (parent && (firstChild = parent->firstChild()) && firstChild->node()) {
459 // Get the node's renderer and follow that continuation chain until the first child is found
460 RenderObject* nodeRenderFirstChild = firstChild->node()->renderer();
461 while (nodeRenderFirstChild != firstChild) {
462 for (RenderObject* contsTest = nodeRenderFirstChild; contsTest; contsTest = nextContinuation(*contsTest)) {
463 if (contsTest == firstChild) {
464 parent = nodeRenderFirstChild->parent();
468 RenderObject* parentFirstChild = parent->firstChild();
469 if (firstChild == parentFirstChild)
471 firstChild = parentFirstChild;
472 if (!firstChild->node())
474 nodeRenderFirstChild = firstChild->node()->renderer();
481 AccessibilityObject* AccessibilityRenderObject::parentObjectIfExists() const
483 AXObjectCache* cache = axObjectCache();
487 // WebArea's parent should be the scroll view containing it.
489 return cache->get(&m_renderer->view().frameView());
491 return cache->get(renderParentObject());
494 AccessibilityObject* AccessibilityRenderObject::parentObject() const
499 if (ariaRoleAttribute() == AccessibilityRole::MenuBar)
500 return axObjectCache()->getOrCreate(m_renderer->parent());
502 // menuButton and its corresponding menu are DOM siblings, but Accessibility needs them to be parent/child
503 if (ariaRoleAttribute() == AccessibilityRole::Menu) {
504 AccessibilityObject* parent = menuButtonForMenu();
509 AXObjectCache* cache = axObjectCache();
513 RenderObject* parentObj = renderParentObject();
515 return cache->getOrCreate(parentObj);
517 // WebArea's parent should be the scroll view containing it.
519 return cache->getOrCreate(&m_renderer->view().frameView());
524 bool AccessibilityRenderObject::isAttachment() const
526 RenderBoxModelObject* renderer = renderBoxModelObject();
529 // Widgets are the replaced elements that we represent to AX as attachments
530 bool isWidget = renderer->isWidget();
532 return isWidget && ariaRoleAttribute() == AccessibilityRole::Unknown;
535 bool AccessibilityRenderObject::isFileUploadButton() const
537 if (m_renderer && is<HTMLInputElement>(m_renderer->node())) {
538 HTMLInputElement& input = downcast<HTMLInputElement>(*m_renderer->node());
539 return input.isFileUpload();
545 bool AccessibilityRenderObject::isOffScreen() const
550 IntRect contentRect = snappedIntRect(m_renderer->absoluteClippedOverflowRect());
551 // FIXME: unclear if we need LegacyIOSDocumentVisibleRect.
552 IntRect viewRect = m_renderer->view().frameView().visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
553 viewRect.intersect(contentRect);
554 return viewRect.isEmpty();
557 Element* AccessibilityRenderObject::anchorElement() const
562 AXObjectCache* cache = axObjectCache();
566 RenderObject* currentRenderer;
568 // Search up the render tree for a RenderObject with a DOM node. Defer to an earlier continuation, though.
569 for (currentRenderer = renderer(); currentRenderer && !currentRenderer->node(); currentRenderer = currentRenderer->parent()) {
570 if (currentRenderer->isAnonymousBlock()) {
571 if (RenderObject* continuation = downcast<RenderBlock>(*currentRenderer).continuation())
572 return cache->getOrCreate(continuation)->anchorElement();
576 // bail if none found
577 if (!currentRenderer)
580 // search up the DOM tree for an anchor element
581 // NOTE: this assumes that any non-image with an anchor is an HTMLAnchorElement
582 for (Node* node = currentRenderer->node(); node; node = node->parentNode()) {
583 if (is<HTMLAnchorElement>(*node) || (node->renderer() && cache->getOrCreate(node->renderer())->isLink()))
584 return downcast<Element>(node);
590 String AccessibilityRenderObject::helpText() const
595 const AtomString& ariaHelp = getAttribute(aria_helpAttr);
596 if (!ariaHelp.isEmpty())
599 String describedBy = ariaDescribedByAttribute();
600 if (!describedBy.isEmpty())
603 String description = accessibilityDescription();
604 for (RenderObject* ancestor = renderer(); ancestor; ancestor = ancestor->parent()) {
605 if (is<HTMLElement>(ancestor->node())) {
606 HTMLElement& element = downcast<HTMLElement>(*ancestor->node());
607 const AtomString& summary = element.getAttribute(summaryAttr);
608 if (!summary.isEmpty())
611 // The title attribute should be used as help text unless it is already being used as descriptive text.
612 const AtomString& title = element.getAttribute(titleAttr);
613 if (!title.isEmpty() && description != title)
617 // Only take help text from an ancestor element if its a group or an unknown role. If help was
618 // added to those kinds of elements, it is likely it was meant for a child element.
619 if (AccessibilityObject* axObj = axObjectCache()->getOrCreate(ancestor)) {
620 if (!axObj->isGroup() && axObj->roleValue() != AccessibilityRole::Unknown)
628 String AccessibilityRenderObject::textUnderElement(AccessibilityTextUnderElementMode mode) const
633 if (is<RenderFileUploadControl>(*m_renderer))
634 return downcast<RenderFileUploadControl>(*m_renderer).buttonValue();
636 // Reflect when a content author has explicitly marked a line break.
637 if (m_renderer->isBR())
640 if (shouldGetTextFromNode(mode))
641 return AccessibilityNodeObject::textUnderElement(mode);
643 // We use a text iterator for text objects AND for those cases where we are
644 // explicitly asking for the full text under a given element.
645 if (is<RenderText>(*m_renderer) || mode.childrenInclusion == AccessibilityTextUnderElementMode::TextUnderElementModeIncludeAllChildren) {
646 // If possible, use a text iterator to get the text, so that whitespace
647 // is handled consistently.
648 Document* nodeDocument = nullptr;
649 Optional<SimpleRange> textRange;
650 if (Node* node = m_renderer->node()) {
651 nodeDocument = &node->document();
652 textRange = makeRangeSelectingNodeContents(*node);
654 // For anonymous blocks, we work around not having a direct node to create a range from
655 // defining one based in the two external positions defining the boundaries of the subtree.
656 RenderObject* firstChildRenderer = m_renderer->firstChildSlow();
657 RenderObject* lastChildRenderer = m_renderer->lastChildSlow();
658 if (firstChildRenderer && firstChildRenderer->node() && lastChildRenderer && lastChildRenderer->node()) {
659 // We define the start and end positions for the range as the ones right before and after
660 // the first and the last nodes in the DOM tree that is wrapped inside the anonymous block.
661 auto& firstNodeInBlock = *firstChildRenderer->node();
662 nodeDocument = &firstNodeInBlock.document();
663 textRange = makeSimpleRange(positionInParentBeforeNode(&firstNodeInBlock), positionInParentAfterNode(lastChildRenderer->node()));
667 if (nodeDocument && textRange) {
668 if (Frame* frame = nodeDocument->frame()) {
669 // catch stale WebCoreAXObject (see <rdar://problem/3960196>)
670 if (frame->document() != nodeDocument)
673 // Renders referenced by accessibility objects could get destroyed, if TextIterator ends up triggering
674 // style update/layout here. See also AXObjectCache::deferTextChangedIfNeeded().
675 ASSERT_WITH_SECURITY_IMPLICATION(!nodeDocument->childNeedsStyleRecalc());
676 ASSERT_WITH_SECURITY_IMPLICATION(!nodeDocument->view()->layoutContext().isInRenderTreeLayout());
677 return plainText(*textRange, textIteratorBehaviorForTextRange());
681 // Sometimes text fragments don't have Nodes associated with them (like when
682 // CSS content is used to insert text or when a RenderCounter is used.)
683 if (is<RenderText>(*m_renderer)) {
684 RenderText& renderTextObject = downcast<RenderText>(*m_renderer);
685 if (is<RenderTextFragment>(renderTextObject)) {
686 RenderTextFragment& renderTextFragment = downcast<RenderTextFragment>(renderTextObject);
687 // The alt attribute may be set on a text fragment through CSS, which should be honored.
688 const String& altText = renderTextFragment.altText();
689 if (!altText.isEmpty())
691 return renderTextFragment.contentString();
694 return renderTextObject.text();
698 return AccessibilityNodeObject::textUnderElement(mode);
701 bool AccessibilityRenderObject::shouldGetTextFromNode(AccessibilityTextUnderElementMode mode) const
706 // AccessibilityRenderObject::textUnderElement() gets the text of anonymous blocks by using
707 // the child nodes to define positions. CSS tables and their anonymous descendants lack
708 // children with nodes.
709 if (m_renderer->isAnonymous() && m_renderer->isTablePart())
710 return mode.childrenInclusion == AccessibilityTextUnderElementMode::TextUnderElementModeIncludeAllChildren;
712 // AccessibilityRenderObject::textUnderElement() calls rangeOfContents() to create the text
713 // range. rangeOfContents() does not include CSS-generated content.
714 if (m_renderer->isBeforeOrAfterContent())
716 if (Node* node = m_renderer->node()) {
717 Node* firstChild = node->pseudoAwareFirstChild();
718 Node* lastChild = node->pseudoAwareLastChild();
719 if ((firstChild && firstChild->isPseudoElement()) || (lastChild && lastChild->isPseudoElement()))
726 Node* AccessibilityRenderObject::node() const
730 if (m_renderer->isRenderView())
731 return &m_renderer->document();
732 return m_renderer->node();
735 String AccessibilityRenderObject::stringValue() const
740 if (isPasswordField())
741 return passwordFieldValue();
743 RenderBoxModelObject* cssBox = renderBoxModelObject();
745 if (isARIAStaticText()) {
746 String staticText = text();
747 if (!staticText.length())
748 staticText = textUnderElement();
752 if (is<RenderText>(*m_renderer))
753 return textUnderElement();
755 if (is<RenderMenuList>(cssBox)) {
756 // RenderMenuList will go straight to the text() of its selected item.
757 // This has to be overridden in the case where the selected item has an ARIA label.
758 HTMLSelectElement& selectElement = downcast<HTMLSelectElement>(*m_renderer->node());
759 int selectedIndex = selectElement.selectedIndex();
760 const Vector<HTMLElement*>& listItems = selectElement.listItems();
761 if (selectedIndex >= 0 && static_cast<size_t>(selectedIndex) < listItems.size()) {
762 const AtomString& overriddenDescription = listItems[selectedIndex]->attributeWithoutSynchronization(aria_labelAttr);
763 if (!overriddenDescription.isNull())
764 return overriddenDescription;
766 return downcast<RenderMenuList>(*m_renderer).text();
769 if (is<RenderListMarker>(*m_renderer))
770 return downcast<RenderListMarker>(*m_renderer).text();
778 #if PLATFORM(IOS_FAMILY)
779 if (isInputTypePopupButton())
780 return textUnderElement();
783 if (is<RenderFileUploadControl>(*m_renderer))
784 return downcast<RenderFileUploadControl>(*m_renderer).fileTextValue();
786 // FIXME: We might need to implement a value here for more types
787 // FIXME: It would be better not to advertise a value at all for the types for which we don't implement one;
788 // this would require subclassing or making accessibilityAttributeNames do something other than return a
789 // single static array.
793 bool AccessibilityRenderObject::canHavePlainText() const
795 return isARIAStaticText() || is<RenderText>(*m_renderer) || isTextControl();
798 HTMLLabelElement* AccessibilityRenderObject::labelElementContainer() const
803 // the control element should not be considered part of the label
807 // find if this has a parent that is a label
808 for (Node* parentNode = m_renderer->node(); parentNode; parentNode = parentNode->parentNode()) {
809 if (is<HTMLLabelElement>(*parentNode))
810 return downcast<HTMLLabelElement>(parentNode);
816 // The boundingBox for elements within the remote SVG element needs to be offset by its position
817 // within the parent page, otherwise they are in relative coordinates only.
818 void AccessibilityRenderObject::offsetBoundingBoxForRemoteSVGElement(LayoutRect& rect) const
820 for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
821 if (parent->isAccessibilitySVGRoot()) {
822 rect.moveBy(parent->parentObject()->boundingBoxRect().location());
828 LayoutRect AccessibilityRenderObject::boundingBoxRect() const
830 RenderObject* obj = renderer();
835 if (obj->node()) // If we are a continuation, we want to make sure to use the primary renderer.
836 obj = obj->node()->renderer();
838 // absoluteFocusRingQuads will query the hierarchy below this element, which for large webpages can be very slow.
839 // For a web area, which will have the most elements of any element, absoluteQuads should be used.
840 // We should also use absoluteQuads for SVG elements, otherwise transforms won't be applied.
841 Vector<FloatQuad> quads;
842 bool isSVGRoot = false;
844 if (obj->isSVGRoot())
847 if (is<RenderText>(*obj))
848 quads = downcast<RenderText>(*obj).absoluteQuadsClippedToEllipsis();
849 else if (isWebArea() || isSVGRoot)
850 obj->absoluteQuads(quads);
852 obj->absoluteFocusRingQuads(quads);
854 LayoutRect result = boundingBoxForQuads(obj, quads);
856 Document* document = this->document();
857 if (document && document->isSVGDocument())
858 offsetBoundingBoxForRemoteSVGElement(result);
860 // The size of the web area should be the content size, not the clipped size.
862 result.setSize(obj->view().frameView().contentsSize());
867 LayoutRect AccessibilityRenderObject::checkboxOrRadioRect() const
872 HTMLLabelElement* label = labelForElement(downcast<Element>(m_renderer->node()));
873 if (!label || !label->renderer())
874 return boundingBoxRect();
876 LayoutRect labelRect = axObjectCache()->getOrCreate(label)->elementRect();
877 labelRect.unite(boundingBoxRect());
881 LayoutRect AccessibilityRenderObject::elementRect() const
883 // a checkbox or radio button should encompass its label
884 if (isCheckboxOrRadio())
885 return checkboxOrRadioRect();
887 return boundingBoxRect();
890 bool AccessibilityRenderObject::supportsPath() const
892 return is<RenderSVGShape>(renderer());
895 Path AccessibilityRenderObject::elementPath() const
897 if (is<RenderSVGShape>(renderer()) && downcast<RenderSVGShape>(*m_renderer).hasPath()) {
898 Path path = downcast<RenderSVGShape>(*m_renderer).path();
900 // The SVG path is in terms of the parent's bounding box. The path needs to be offset to frame coordinates.
901 if (auto svgRoot = ancestorsOfType<RenderSVGRoot>(*m_renderer).first()) {
902 LayoutPoint parentOffset = axObjectCache()->getOrCreate(&*svgRoot)->elementRect().location();
903 path.transform(AffineTransform().translate(parentOffset.x(), parentOffset.y()));
912 IntPoint AccessibilityRenderObject::linkClickPoint()
915 /* A link bounding rect can contain points that are not part of the link.
916 For instance, a link that starts at the end of a line and finishes at the
917 beginning of the next line will have a bounding rect that includes the
918 entire two lines. In such a case, the middle point of the bounding rect
919 may not belong to the link element and thus may not activate the link.
920 Hence, return the middle point of the first character in the link if exists.
922 if (auto range = elementRange()) {
923 auto start = VisiblePosition { createLegacyEditingPosition(range->start) };
924 auto end = nextVisiblePosition(start);
925 if (isPointInRange(*range, makeBoundaryPoint(end)))
926 return { boundsForRange(*makeSimpleRange(start, end)).center() };
928 return AccessibilityObject::clickPoint();
931 IntPoint AccessibilityRenderObject::clickPoint()
933 // Headings are usually much wider than their textual content. If the mid point is used, often it can be wrong.
934 if (isHeading() && children().size() == 1)
935 return children().first()->clickPoint();
938 return linkClickPoint();
940 // use the default position unless this is an editable web area, in which case we use the selection bounds.
941 if (!isWebArea() || !canSetValueAttribute())
942 return AccessibilityObject::clickPoint();
944 return boundsForVisiblePositionRange(selection()).center();
947 AccessibilityObject* AccessibilityRenderObject::internalLinkElement() const
949 auto element = anchorElement();
951 // Right now, we do not support ARIA links as internal link elements
952 if (!is<HTMLAnchorElement>(element))
954 auto& anchor = downcast<HTMLAnchorElement>(*element);
956 auto linkURL = anchor.href();
957 auto fragmentIdentifier = linkURL.fragmentIdentifier();
958 if (fragmentIdentifier.isEmpty())
961 // check if URL is the same as current URL
962 auto documentURL = m_renderer->document().url();
963 if (!equalIgnoringFragmentIdentifier(documentURL, linkURL))
966 auto linkedNode = m_renderer->document().findAnchor(fragmentIdentifier.toStringWithoutCopying());
970 // The element we find may not be accessible, so find the first accessible object.
971 return firstAccessibleObjectFromNode(linkedNode);
974 OptionSet<SpeakAs> AccessibilityRenderObject::speakAsProperty() const
977 return AccessibilityObject::speakAsProperty();
979 return m_renderer->style().speakAs();
982 void AccessibilityRenderObject::addRadioButtonGroupChildren(AXCoreObject* parent, AccessibilityChildrenVector& linkedUIElements) const
984 for (const auto& child : parent->children()) {
985 if (child->roleValue() == AccessibilityRole::RadioButton)
986 linkedUIElements.append(child);
988 addRadioButtonGroupChildren(child.get(), linkedUIElements);
992 void AccessibilityRenderObject::addRadioButtonGroupMembers(AccessibilityChildrenVector& linkedUIElements) const
994 if (roleValue() != AccessibilityRole::RadioButton)
997 Node* node = this->node();
998 if (is<HTMLInputElement>(node)) {
999 HTMLInputElement& input = downcast<HTMLInputElement>(*node);
1000 for (auto& radioSibling : input.radioButtonGroup()) {
1001 if (AccessibilityObject* object = axObjectCache()->getOrCreate(radioSibling.ptr()))
1002 linkedUIElements.append(object);
1005 // If we didn't find any radio button siblings with the traditional naming, lets search for a radio group role and find its children.
1006 for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
1007 if (parent->roleValue() == AccessibilityRole::RadioGroup)
1008 addRadioButtonGroupChildren(parent, linkedUIElements);
1013 // linked ui elements could be all the related radio buttons in a group
1014 // or an internal anchor connection
1015 void AccessibilityRenderObject::linkedUIElements(AccessibilityChildrenVector& linkedUIElements) const
1017 ariaFlowToElements(linkedUIElements);
1020 AccessibilityObject* linkedAXElement = internalLinkElement();
1021 if (linkedAXElement)
1022 linkedUIElements.append(linkedAXElement);
1025 if (roleValue() == AccessibilityRole::RadioButton)
1026 addRadioButtonGroupMembers(linkedUIElements);
1029 bool AccessibilityRenderObject::hasTextAlternative() const
1031 // ARIA: section 2A, bullet #3 says if aria-labeledby or aria-label appears, it should
1032 // override the "label" element association.
1033 return ariaAccessibilityDescription().length();
1036 bool AccessibilityRenderObject::hasPopup() const
1038 // Return true if this has the aria-haspopup attribute, or if it has an ancestor of type link with the aria-haspopup attribute.
1039 return Accessibility::findAncestor<AccessibilityObject>(*this, true, [this] (const AccessibilityObject& object) {
1040 return (this == &object) ? !equalLettersIgnoringASCIICase(object.popupValue(), "false")
1041 : object.isLink() && !equalLettersIgnoringASCIICase(object.popupValue(), "false");
1045 bool AccessibilityRenderObject::supportsDropping() const
1047 return determineDropEffects().size();
1050 bool AccessibilityRenderObject::supportsDragging() const
1052 const AtomString& grabbed = getAttribute(aria_grabbedAttr);
1053 return equalLettersIgnoringASCIICase(grabbed, "true") || equalLettersIgnoringASCIICase(grabbed, "false") || hasAttribute(draggableAttr);
1056 bool AccessibilityRenderObject::isGrabbed()
1058 #if ENABLE(DRAG_SUPPORT)
1059 if (mainFrame() && mainFrame()->eventHandler().draggingElement() == element())
1063 return elementAttributeValue(aria_grabbedAttr);
1066 Vector<String> AccessibilityRenderObject::determineDropEffects() const
1068 // Order is aria-dropeffect, dropzone, webkitdropzone
1069 const AtomString& dropEffects = getAttribute(aria_dropeffectAttr);
1070 if (!dropEffects.isEmpty()) {
1071 String dropEffectsString = dropEffects.string();
1072 dropEffectsString.replace('\n', ' ');
1073 return dropEffectsString.split(' ');
1076 auto dropzone = getAttribute(dropzoneAttr);
1077 if (!dropzone.isEmpty())
1078 return Vector<String> { dropzone };
1080 auto webkitdropzone = getAttribute(webkitdropzoneAttr);
1081 if (!webkitdropzone.isEmpty())
1082 return Vector<String> { webkitdropzone };
1087 bool AccessibilityRenderObject::exposesTitleUIElement() const
1089 if (!isControl() && !isFigureElement())
1092 // If this control is ignored (because it's invisible),
1093 // then the label needs to be exposed so it can be visible to accessibility.
1094 if (accessibilityIsIgnored())
1097 // When controls have their own descriptions, the title element should be ignored.
1098 if (hasTextAlternative())
1101 // When <label> element has aria-label or aria-labelledby on it, we shouldn't expose it as the
1102 // titleUIElement, otherwise its inner text will be announced by a screenreader.
1103 if (isLabelable()) {
1104 if (HTMLLabelElement* label = labelForElement(downcast<Element>(node()))) {
1105 if (!label->attributeWithoutSynchronization(aria_labelAttr).isEmpty())
1107 if (AccessibilityObject* labelObject = axObjectCache()->getOrCreate(label)) {
1108 if (!labelObject->ariaLabeledByAttribute().isEmpty())
1110 // To simplify instances where the labeling element includes widget descendants
1111 // which it does not label.
1112 if (is<AccessibilityLabel>(*labelObject)
1113 && downcast<AccessibilityLabel>(*labelObject).containsUnrelatedControls())
1122 #if ENABLE(APPLE_PAY)
1123 String AccessibilityRenderObject::applePayButtonDescription() const
1125 switch (applePayButtonType()) {
1126 case ApplePayButtonType::Plain:
1127 return AXApplePayPlainLabel();
1128 case ApplePayButtonType::Buy:
1129 return AXApplePayBuyLabel();
1130 case ApplePayButtonType::SetUp:
1131 return AXApplePaySetupLabel();
1132 case ApplePayButtonType::Donate:
1133 return AXApplePayDonateLabel();
1134 case ApplePayButtonType::CheckOut:
1135 return AXApplePayCheckOutLabel();
1136 case ApplePayButtonType::Book:
1137 return AXApplePayBookLabel();
1138 case ApplePayButtonType::Subscribe:
1139 return AXApplePaySubscribeLabel();
1140 #if ENABLE(APPLE_PAY_NEW_BUTTON_TYPES)
1141 case ApplePayButtonType::Reload:
1142 return AXApplePayReloadLabel();
1143 case ApplePayButtonType::AddMoney:
1144 return AXApplePayAddMoneyLabel();
1145 case ApplePayButtonType::TopUp:
1146 return AXApplePayTopUpLabel();
1147 case ApplePayButtonType::Order:
1148 return AXApplePayOrderLabel();
1149 case ApplePayButtonType::Rent:
1150 return AXApplePayRentLabel();
1151 case ApplePayButtonType::Support:
1152 return AXApplePaySupportLabel();
1153 case ApplePayButtonType::Contribute:
1154 return AXApplePayContributeLabel();
1155 case ApplePayButtonType::Tip:
1156 return AXApplePayTipLabel();
1162 void AccessibilityRenderObject::titleElementText(Vector<AccessibilityText>& textOrder) const
1164 #if ENABLE(APPLE_PAY)
1165 if (isApplePayButton()) {
1166 textOrder.append(AccessibilityText(applePayButtonDescription(), AccessibilityTextSource::Alternative));
1171 AccessibilityNodeObject::titleElementText(textOrder);
1174 AccessibilityObject* AccessibilityRenderObject::titleUIElement() const
1179 // if isFieldset is true, the renderer is guaranteed to be a RenderFieldset
1181 return axObjectCache()->getOrCreate(downcast<RenderBlock>(*m_renderer).findFieldsetLegend(RenderBlock::FieldsetIncludeFloatingOrOutOfFlow));
1183 if (isFigureElement())
1184 return captionForFigure();
1186 Node* node = m_renderer->node();
1187 if (!is<Element>(node))
1189 HTMLLabelElement* label = labelForElement(downcast<Element>(node));
1190 if (label && label->renderer())
1191 return axObjectCache()->getOrCreate(label);
1196 bool AccessibilityRenderObject::isAllowedChildOfTree() const
1198 // Determine if this is in a tree. If so, we apply special behavior to make it work like an AXOutline.
1199 AccessibilityObject* axObj = parentObject();
1200 bool isInTree = false;
1201 bool isTreeItemDescendant = false;
1203 if (axObj->roleValue() == AccessibilityRole::TreeItem)
1204 isTreeItemDescendant = true;
1205 if (axObj->isTree()) {
1209 axObj = axObj->parentObject();
1212 // If the object is in a tree, only tree items should be exposed (and the children of tree items).
1214 AccessibilityRole role = roleValue();
1215 if (role != AccessibilityRole::TreeItem && role != AccessibilityRole::StaticText && !isTreeItemDescendant)
1221 static AccessibilityObjectInclusion objectInclusionFromAltText(const String& altText)
1223 // Don't ignore an image that has an alt tag.
1224 if (!altText.isAllSpecialCharacters<isHTMLSpace>())
1225 return AccessibilityObjectInclusion::IncludeObject;
1227 // The informal standard is to ignore images with zero-length alt strings:
1228 // https://www.w3.org/WAI/tutorials/images/decorative/.
1229 if (!altText.isNull())
1230 return AccessibilityObjectInclusion::IgnoreObject;
1232 return AccessibilityObjectInclusion::DefaultBehavior;
1235 AccessibilityObjectInclusion AccessibilityRenderObject::defaultObjectInclusion() const
1237 // The following cases can apply to any element that's a subclass of AccessibilityRenderObject.
1240 return AccessibilityObjectInclusion::IgnoreObject;
1242 if (m_renderer->style().visibility() != Visibility::Visible) {
1243 // aria-hidden is meant to override visibility as the determinant in AX hierarchy inclusion.
1244 if (equalLettersIgnoringASCIICase(getAttribute(aria_hiddenAttr), "false"))
1245 return AccessibilityObjectInclusion::DefaultBehavior;
1247 return AccessibilityObjectInclusion::IgnoreObject;
1250 return AccessibilityObject::defaultObjectInclusion();
1253 static bool webAreaIsPresentational(RenderObject* renderer)
1255 if (!renderer || !is<RenderView>(*renderer))
1258 if (auto ownerElement = renderer->document().ownerElement())
1259 return nodeHasPresentationRole(ownerElement);
1264 bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const
1266 AXTRACE("AccessibilityRenderObject::computeAccessibilityIsIgnored");
1268 ASSERT(m_initialized);
1274 // Check first if any of the common reasons cause this element to be ignored.
1275 // Then process other use cases that need to be applied to all the various roles
1276 // that AccessibilityRenderObjects take on.
1277 AccessibilityObjectInclusion decision = defaultObjectInclusion();
1278 if (decision == AccessibilityObjectInclusion::IncludeObject)
1280 if (decision == AccessibilityObjectInclusion::IgnoreObject)
1283 // If this element is within a parent that cannot have children, it should not be exposed.
1284 if (isDescendantOfBarrenParent())
1287 if (roleValue() == AccessibilityRole::Ignored)
1290 if (roleValue() == AccessibilityRole::Presentational || inheritsPresentationalRole())
1293 // WebAreas should be ignored if their iframe container is marked as presentational.
1294 if (webAreaIsPresentational(renderer()))
1297 // An ARIA tree can only have tree items and static text as children.
1298 if (!isAllowedChildOfTree())
1301 // Allow the platform to decide if the attachment is ignored or not.
1303 return accessibilityIgnoreAttachment();
1305 // ignore popup menu items because AppKit does
1306 if (m_renderer && ancestorsOfType<RenderMenuList>(*m_renderer).first())
1309 // https://webkit.org/b/161276 Getting the controlObject might cause the m_renderer to be nullptr.
1313 if (m_renderer->isBR())
1316 if (is<RenderText>(*m_renderer)) {
1317 // static text beneath MenuItems and MenuButtons are just reported along with the menu item, so it's ignored on an individual level
1318 AXCoreObject* parent = parentObjectUnignored();
1319 if (parent && (parent->isMenuItem() || parent->ariaRoleAttribute() == AccessibilityRole::MenuButton))
1321 auto& renderText = downcast<RenderText>(*m_renderer);
1322 if (!renderText.hasRenderedText())
1325 if (renderText.parent()->isFirstLetter())
1328 // static text beneath TextControls is reported along with the text control text so it's ignored.
1329 for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
1330 if (parent->roleValue() == AccessibilityRole::TextField)
1334 // Walking up the parent chain might reset the m_renderer.
1338 // The alt attribute may be set on a text fragment through CSS, which should be honored.
1339 if (is<RenderTextFragment>(renderText)) {
1340 AccessibilityObjectInclusion altTextInclusion = objectInclusionFromAltText(downcast<RenderTextFragment>(renderText).altText());
1341 if (altTextInclusion == AccessibilityObjectInclusion::IgnoreObject)
1343 if (altTextInclusion == AccessibilityObjectInclusion::IncludeObject)
1347 // text elements that are just empty whitespace should not be returned
1348 return renderText.text().isAllSpecialCharacters<isHTMLSpace>();
1360 // all controls are accessible
1364 if (isFigureElement())
1367 switch (roleValue()) {
1368 case AccessibilityRole::Audio:
1369 case AccessibilityRole::DescriptionListTerm:
1370 case AccessibilityRole::DescriptionListDetail:
1371 case AccessibilityRole::Details:
1372 case AccessibilityRole::DocumentArticle:
1373 case AccessibilityRole::Footer:
1374 case AccessibilityRole::LandmarkRegion:
1375 case AccessibilityRole::ListItem:
1376 case AccessibilityRole::Time:
1377 case AccessibilityRole::Video:
1384 // If the image can take focus, it should not be ignored, lest the user not be able to interact with something important.
1385 if (canSetFocusAttribute())
1388 // First check the RenderImage's altText (which can be set through a style sheet, or come from the Element).
1389 // However, if this is not a native image, fallback to the attribute on the Element.
1390 AccessibilityObjectInclusion altTextInclusion = AccessibilityObjectInclusion::DefaultBehavior;
1391 bool isRenderImage = is<RenderImage>(renderer());
1393 altTextInclusion = objectInclusionFromAltText(downcast<RenderImage>(*m_renderer).altText());
1395 altTextInclusion = objectInclusionFromAltText(getAttribute(altAttr).string());
1397 if (altTextInclusion == AccessibilityObjectInclusion::IgnoreObject)
1399 if (altTextInclusion == AccessibilityObjectInclusion::IncludeObject)
1402 // If an image has the title or label attributes, accessibility should be lenient and allow it to appear in the hierarchy (according to WAI-ARIA).
1403 if (!getAttribute(titleAttr).isEmpty() || !getAttribute(aria_labelAttr).isEmpty())
1406 if (isRenderImage) {
1407 // check for one-dimensional image
1408 RenderImage& image = downcast<RenderImage>(*m_renderer);
1409 if (image.height() <= 1 || image.width() <= 1)
1412 // check whether rendered image was stretched from one-dimensional file image
1413 if (image.cachedImage()) {
1414 LayoutSize imageSize = image.cachedImage()->imageSizeForRenderer(&image, image.view().zoomFactor());
1415 return imageSize.height() <= 1 || imageSize.width() <= 1;
1421 if (ariaRoleAttribute() != AccessibilityRole::Unknown)
1424 if (roleValue() == AccessibilityRole::HorizontalRule)
1427 // don't ignore labels, because they serve as TitleUIElements
1428 Node* node = m_renderer->node();
1429 if (is<HTMLLabelElement>(node))
1432 // Anything that is content editable should not be ignored.
1433 // However, one cannot just call node->hasEditableStyle() since that will ask if its parents
1434 // are also editable. Only the top level content editable region should be exposed.
1435 if (hasContentEditableAttributeSet())
1438 // if this element has aria attributes on it, it should not be ignored.
1439 if (supportsARIAAttributes())
1443 // First check if this is a special case within the math tree that needs to be ignored.
1444 if (isIgnoredElementWithinMathTree())
1446 // Otherwise all other math elements are in the tree.
1447 if (isMathElement())
1451 if (is<RenderBlockFlow>(*m_renderer) && m_renderer->childrenInline() && !canSetFocusAttribute())
1452 return !downcast<RenderBlockFlow>(*m_renderer).hasLines() && !mouseButtonListener();
1455 if (canvasHasFallbackContent())
1458 if (is<RenderBox>(*m_renderer)) {
1459 auto& canvasBox = downcast<RenderBox>(*m_renderer);
1460 if (canvasBox.height() <= 1 || canvasBox.width() <= 1)
1463 // Otherwise fall through; use presence of help text, title, or description to decide.
1466 if (m_renderer->isListMarker()) {
1467 AXCoreObject* parent = parentObjectUnignored();
1468 return parent && !parent->isListItem();
1474 // The render tree of meter includes a RenderBlock (meter) and a RenderMeter (div).
1475 // We expose the latter and thus should ignore the former. However, if the author
1476 // includes a title attribute on the element, hasAttributesRequiredForInclusion()
1477 // will return true, potentially resulting in a redundant accessible object.
1478 if (is<HTMLMeterElement>(node))
1481 // Using the presence of an accessible name to decide an element's visibility is not
1482 // as definitive as previous checks, so this should remain as one of the last.
1483 if (hasAttributesRequiredForInclusion())
1486 // Don't ignore generic focusable elements like <div tabindex=0>
1487 // unless they're completely empty, with no children.
1488 if (isGenericFocusableElement() && node->firstChild())
1491 // <span> tags are inline tags and not meant to convey information if they have no other aria
1492 // information on them. If we don't ignore them, they may emit signals expected to come from
1493 // their parent. In addition, because included spans are AccessibilityRole::Group objects, and AccessibilityRole::Group
1494 // objects are often containers with meaningful information, the inclusion of a span can have
1495 // the side effect of causing the immediate parent accessible to be ignored. This is especially
1496 // problematic for platforms which have distinct roles for textual block elements.
1497 if (node && node->hasTagName(spanTag))
1500 // Other non-ignored host language elements
1501 if (node && node->hasTagName(dfnTag))
1504 if (isStyleFormatGroup())
1507 // Make sure that ruby containers are not ignored.
1508 if (m_renderer->isRubyRun() || m_renderer->isRubyBlock() || m_renderer->isRubyInline())
1511 // Find out if this element is inside of a label element.
1512 // If so, it may be ignored because it's the label for a checkbox or radio button.
1513 AccessibilityObject* controlObject = correspondingControlForLabelElement();
1514 if (controlObject && !controlObject->exposesTitleUIElement() && controlObject->isCheckboxOrRadio())
1517 // By default, objects should be ignored so that the AX hierarchy is not
1518 // filled with unnecessary items.
1522 bool AccessibilityRenderObject::isLoaded() const
1524 return m_renderer ? !m_renderer->document().parser() : false;
1527 double AccessibilityRenderObject::estimatedLoadingProgress() const
1535 return m_renderer->page().progress().estimatedProgress();
1538 int AccessibilityRenderObject::layoutCount() const
1540 if (!m_renderer || !is<RenderView>(*m_renderer))
1542 return downcast<RenderView>(*m_renderer).frameView().layoutContext().layoutCount();
1545 String AccessibilityRenderObject::text() const
1547 if (isPasswordField())
1548 return passwordFieldValue();
1550 return AccessibilityNodeObject::text();
1553 int AccessibilityRenderObject::textLength() const
1555 ASSERT(isTextControl());
1557 if (isPasswordField())
1558 return passwordFieldValue().length();
1560 return text().length();
1563 PlainTextRange AccessibilityRenderObject::documentBasedSelectedTextRange() const
1565 Node* node = m_renderer->node();
1567 return PlainTextRange();
1569 auto visibleSelection = selection();
1570 auto selectionRange = visibleSelection.firstRange();
1571 if (!selectionRange || !intersects(*selectionRange, *node))
1572 return PlainTextRange();
1574 int start = indexForVisiblePosition(visibleSelection.start());
1575 int end = indexForVisiblePosition(visibleSelection.end());
1576 return PlainTextRange(start, end - start);
1579 String AccessibilityRenderObject::selectedText() const
1581 ASSERT(isTextControl());
1583 if (isPasswordField())
1584 return String(); // need to return something distinct from empty string
1586 if (isNativeTextControl()) {
1587 HTMLTextFormControlElement& textControl = downcast<RenderTextControl>(*m_renderer).textFormControlElement();
1588 return textControl.selectedText();
1591 return doAXStringForRange(documentBasedSelectedTextRange());
1594 String AccessibilityRenderObject::accessKey() const
1599 Node* node = m_renderer->node();
1600 if (!is<Element>(node))
1603 return downcast<Element>(*node).attributeWithoutSynchronization(accesskeyAttr);
1606 VisibleSelection AccessibilityRenderObject::selection() const
1608 return m_renderer->frame().selection().selection();
1611 PlainTextRange AccessibilityRenderObject::selectedTextRange() const
1613 ASSERT(isTextControl());
1615 if (isPasswordField())
1616 return PlainTextRange();
1618 // Use the text control native range if it's a native object.
1619 if (isNativeTextControl()) {
1620 auto& textControl = downcast<RenderTextControl>(*m_renderer).textFormControlElement();
1621 return PlainTextRange(textControl.selectionStart(), textControl.selectionEnd() - textControl.selectionStart());
1624 return documentBasedSelectedTextRange();
1627 static void setTextSelectionIntent(AXObjectCache* cache, AXTextStateChangeType type)
1631 AXTextStateChangeIntent intent(type, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown, false });
1632 cache->setTextSelectionIntent(intent);
1633 cache->setIsSynchronizingSelection(true);
1636 static void clearTextSelectionIntent(AXObjectCache* cache)
1640 cache->setTextSelectionIntent(AXTextStateChangeIntent());
1641 cache->setIsSynchronizingSelection(false);
1644 void AccessibilityRenderObject::setSelectedTextRange(const PlainTextRange& range)
1646 setTextSelectionIntent(axObjectCache(), range.length ? AXTextStateChangeTypeSelectionExtend : AXTextStateChangeTypeSelectionMove);
1648 if (isNativeTextControl()) {
1649 HTMLTextFormControlElement& textControl = downcast<RenderTextControl>(*m_renderer).textFormControlElement();
1650 textControl.setSelectionRange(range.start, range.start + range.length);
1653 auto& node = *this->node();
1654 auto elementRange = this->elementRange();
1655 auto start = visiblePositionForIndexUsingCharacterIterator(node, range.start);
1656 if (!isPointInRange(*elementRange, makeBoundaryPoint(start)))
1657 start = createLegacyEditingPosition(elementRange->start);
1658 auto end = visiblePositionForIndexUsingCharacterIterator(node, range.start + range.length);
1659 if (!isPointInRange(*elementRange, makeBoundaryPoint(end)))
1660 end = createLegacyEditingPosition(elementRange->start);
1661 m_renderer->frame().selection().setSelection(VisibleSelection(start, end), FrameSelection::defaultSetSelectionOptions(UserTriggered));
1664 clearTextSelectionIntent(axObjectCache());
1667 URL AccessibilityRenderObject::url() const
1669 auto* node = this->node();
1670 if (isLink() && is<HTMLAnchorElement>(node)) {
1671 if (HTMLAnchorElement* anchor = downcast<HTMLAnchorElement>(anchorElement()))
1672 return anchor->href();
1675 if (m_renderer && isWebArea())
1676 return m_renderer->document().url();
1678 if (isImage() && is<HTMLImageElement>(node))
1679 return downcast<HTMLImageElement>(node)->src();
1681 if (isInputImage() && is<HTMLInputElement>(node))
1682 return downcast<HTMLInputElement>(node)->src();
1687 bool AccessibilityRenderObject::isUnvisited() const
1692 // FIXME: Is it a privacy violation to expose unvisited information to accessibility APIs?
1693 return m_renderer->style().isLink() && m_renderer->style().insideLink() == InsideLink::InsideUnvisited;
1696 bool AccessibilityRenderObject::isVisited() const
1701 // FIXME: Is it a privacy violation to expose visited information to accessibility APIs?
1702 return m_renderer->style().isLink() && m_renderer->style().insideLink() == InsideLink::InsideVisited;
1705 void AccessibilityRenderObject::setElementAttributeValue(const QualifiedName& attributeName, bool value)
1710 Node* node = m_renderer->node();
1711 if (!is<Element>(node))
1714 downcast<Element>(*node).setAttribute(attributeName, (value) ? "true" : "false");
1717 bool AccessibilityRenderObject::elementAttributeValue(const QualifiedName& attributeName) const
1722 return equalLettersIgnoringASCIICase(getAttribute(attributeName), "true");
1725 bool AccessibilityRenderObject::isSelected() const
1730 if (!m_renderer->node())
1733 if (equalLettersIgnoringASCIICase(getAttribute(aria_selectedAttr), "true"))
1736 if (isTabItem() && isTabItemSelected())
1739 // Menu items are considered selectable by assistive technologies
1741 return isFocused() || parentObjectUnignored()->activeDescendant() == this;
1746 bool AccessibilityRenderObject::isTabItemSelected() const
1748 if (!isTabItem() || !m_renderer)
1751 Node* node = m_renderer->node();
1752 if (!node || !node->isElementNode())
1755 // The ARIA spec says a tab item can also be selected if it is aria-labeled by a tabpanel
1756 // that has keyboard focus inside of it, or if a tabpanel in its aria-controls list has KB
1757 // focus inside of it.
1758 AccessibilityObject* focusedElement = static_cast<AccessibilityObject*>(focusedUIElement());
1759 if (!focusedElement)
1762 Vector<Element*> elements;
1763 elementsFromAttribute(elements, aria_controlsAttr);
1765 AXObjectCache* cache = axObjectCache();
1769 for (const auto& element : elements) {
1770 AccessibilityObject* tabPanel = cache->getOrCreate(element);
1772 // A tab item should only control tab panels.
1773 if (!tabPanel || tabPanel->roleValue() != AccessibilityRole::TabPanel)
1776 AccessibilityObject* checkFocusElement = focusedElement;
1777 // Check if the focused element is a descendant of the element controlled by the tab item.
1778 while (checkFocusElement) {
1779 if (tabPanel == checkFocusElement)
1781 checkFocusElement = checkFocusElement->parentObject();
1788 bool AccessibilityRenderObject::isFocused() const
1793 Document& document = m_renderer->document();
1795 Element* focusedElement = document.focusedElement();
1796 if (!focusedElement)
1799 // A web area is represented by the Document node in the DOM tree, which isn't focusable.
1800 // Check instead if the frame's selection controller is focused
1801 if (focusedElement == m_renderer->node()
1802 || (roleValue() == AccessibilityRole::WebArea && document.frame()->selection().isFocusedAndActive()))
1808 void AccessibilityRenderObject::setFocused(bool on)
1810 if (!canSetFocusAttribute())
1813 Document* document = this->document();
1814 Node* node = this->node();
1816 if (!on || !is<Element>(node)) {
1817 document->setFocusedElement(nullptr);
1821 // When a node is told to set focus, that can cause it to be deallocated, which means that doing
1822 // anything else inside this object will crash. To fix this, we added a RefPtr to protect this object
1823 // long enough for duration.
1824 RefPtr<AccessibilityObject> protectedThis(this);
1826 // If this node is already the currently focused node, then calling focus() won't do anything.
1827 // That is a problem when focus is removed from the webpage to chrome, and then returns.
1828 // In these cases, we need to do what keyboard and mouse focus do, which is reset focus first.
1829 if (document->focusedElement() == node)
1830 document->setFocusedElement(nullptr);
1832 // If we return from setFocusedElement and our element has been removed from a tree, axObjectCache() may be null.
1833 if (AXObjectCache* cache = axObjectCache()) {
1834 cache->setIsSynchronizingSelection(true);
1835 downcast<Element>(*node).focus();
1836 cache->setIsSynchronizingSelection(false);
1840 void AccessibilityRenderObject::setSelectedRows(AccessibilityChildrenVector& selectedRows)
1842 // Setting selected only makes sense in trees and tables (and tree-tables).
1843 AccessibilityRole role = roleValue();
1844 if (role != AccessibilityRole::Tree && role != AccessibilityRole::TreeGrid && role != AccessibilityRole::Table && role != AccessibilityRole::Grid)
1847 bool isMulti = isMultiSelectable();
1848 unsigned count = selectedRows.size();
1849 if (count > 1 && !isMulti)
1852 for (const auto& selectedRow : selectedRows)
1853 selectedRow->setSelected(true);
1856 bool AccessibilityRenderObject::setValue(const String& string)
1858 if (!m_renderer || !is<Element>(m_renderer->node()))
1861 Element& element = downcast<Element>(*m_renderer->node());
1862 RenderObject& renderer = *m_renderer;
1864 // We should use the editor's insertText to mimic typing into the field.
1865 // Also only do this when the field is in editing mode.
1866 if (Frame* frame = renderer.document().frame()) {
1867 Editor& editor = frame->editor();
1868 if (element.shouldUseInputMethod()) {
1870 editor.insertText(string, nullptr);
1874 // FIXME: Do we want to do anything here for ARIA textboxes?
1875 if (renderer.isTextField() && is<HTMLInputElement>(element)) {
1876 downcast<HTMLInputElement>(element).setValue(string);
1879 if (renderer.isTextArea() && is<HTMLTextAreaElement>(element)) {
1880 downcast<HTMLTextAreaElement>(element).setValue(string);
1887 bool AccessibilityRenderObject::supportsARIAOwns() const
1891 const AtomString& ariaOwns = getAttribute(aria_ownsAttr);
1893 return !ariaOwns.isEmpty();
1896 RenderView* AccessibilityRenderObject::topRenderer() const
1898 Document* topDoc = topDocument();
1902 return topDoc->renderView();
1905 Document* AccessibilityRenderObject::document() const
1909 return &m_renderer->document();
1912 Widget* AccessibilityRenderObject::widget() const
1914 if (!m_renderer || !is<RenderWidget>(*m_renderer))
1916 return downcast<RenderWidget>(*m_renderer).widget();
1919 AccessibilityObject* AccessibilityRenderObject::accessibilityParentForImageMap(HTMLMapElement* map) const
1921 // find an image that is using this map
1925 HTMLImageElement* imageElement = map->imageElement();
1929 if (AXObjectCache* cache = axObjectCache())
1930 return cache->getOrCreate(imageElement);
1935 AXCoreObject::AccessibilityChildrenVector AccessibilityRenderObject::documentLinks()
1937 AccessibilityChildrenVector result;
1938 Document& document = m_renderer->document();
1939 Ref<HTMLCollection> links = document.links();
1940 for (unsigned i = 0; auto* current = links->item(i); ++i) {
1941 if (auto* renderer = current->renderer()) {
1942 RefPtr<AccessibilityObject> axObject = document.axObjectCache()->getOrCreate(renderer);
1944 if (!axObject->accessibilityIsIgnored() && axObject->isLink())
1945 result.append(axObject);
1947 auto* parent = current->parentNode();
1948 if (is<HTMLAreaElement>(*current) && is<HTMLMapElement>(parent)) {
1949 auto& areaObject = downcast<AccessibilityImageMapLink>(*axObjectCache()->create(AccessibilityRole::ImageMapLink));
1950 HTMLMapElement& map = downcast<HTMLMapElement>(*parent);
1951 areaObject.setHTMLAreaElement(downcast<HTMLAreaElement>(current));
1952 areaObject.setHTMLMapElement(&map);
1953 areaObject.setParent(accessibilityParentForImageMap(&map));
1955 result.append(&areaObject);
1963 FrameView* AccessibilityRenderObject::documentFrameView() const
1968 // this is the RenderObject's Document's Frame's FrameView
1969 return &m_renderer->view().frameView();
1972 Widget* AccessibilityRenderObject::widgetForAttachmentView() const
1974 if (!isAttachment())
1976 return downcast<RenderWidget>(*m_renderer).widget();
1979 // This function is like a cross-platform version of - (WebCoreTextMarkerRange*)textMarkerRange. It returns
1980 // a Range that we can convert to a WebCoreTextMarkerRange in the Obj-C file
1981 VisiblePositionRange AccessibilityRenderObject::visiblePositionRange() const
1984 return VisiblePositionRange();
1986 Node* node = m_renderer->node();
1988 return VisiblePositionRange();
1990 VisiblePosition startPos = firstPositionInOrBeforeNode(node);
1991 VisiblePosition endPos = lastPositionInOrAfterNode(node);
1993 // the VisiblePositions are equal for nodes like buttons, so adjust for that
1994 // FIXME: Really? [button, 0] and [button, 1] are distinct (before and after the button)
1995 // I expect this code is only hit for things like empty divs? In which case I don't think
1996 // the behavior is correct here -- eseidel
1997 if (startPos == endPos) {
1998 endPos = endPos.next();
1999 if (endPos.isNull())
2003 return { WTFMove(startPos), WTFMove(endPos) };
2006 VisiblePositionRange AccessibilityRenderObject::visiblePositionRangeForLine(unsigned lineCount) const
2008 if (!lineCount || !m_renderer)
2009 return VisiblePositionRange();
2011 // iterate over the lines
2012 // FIXME: This is wrong when lineNumber is lineCount+1, because nextLinePosition takes you to the last offset of the last line.
2013 VisiblePosition position = m_renderer->view().positionForPoint(IntPoint(), nullptr);
2014 while (--lineCount) {
2015 auto previousLinePosition = position;
2016 position = nextLinePosition(position, 0);
2017 if (position.isNull() || position == previousLinePosition)
2018 return VisiblePositionRange();
2021 // make a caret selection for the marker position, then extend it to the line
2022 // NOTE: Ignores results of sel.modify because it returns false when starting at an empty line.
2023 // The resulting selection in that case will be a caret at position.
2024 FrameSelection selection;
2025 selection.setSelection(position);
2026 selection.modify(FrameSelection::AlterationExtend, SelectionDirection::Right, TextGranularity::LineBoundary);
2027 return selection.selection();
2030 VisiblePosition AccessibilityRenderObject::visiblePositionForIndex(int index) const
2033 return VisiblePosition();
2035 if (isNativeTextControl())
2036 return downcast<RenderTextControl>(*m_renderer).textFormControlElement().visiblePositionForIndex(index);
2038 if (!allowsTextRanges() && !is<RenderText>(*m_renderer))
2039 return VisiblePosition();
2041 Node* node = m_renderer->node();
2043 return VisiblePosition();
2045 return visiblePositionForIndexUsingCharacterIterator(*node, index);
2048 int AccessibilityRenderObject::indexForVisiblePosition(const VisiblePosition& position) const
2050 if (isNativeTextControl())
2051 return downcast<RenderTextControl>(*m_renderer).textFormControlElement().indexForVisiblePosition(position);
2053 if (!isTextControl())
2056 Node* node = m_renderer->node();
2060 Position indexPosition = position.deepEquivalent();
2061 if (indexPosition.isNull() || highestEditableRoot(indexPosition, HasEditableAXRole) != node)
2065 // We need to consider replaced elements for GTK, as they will be
2066 // presented with the 'object replacement character' (0xFFFC).
2067 bool forSelectionPreservation = true;
2069 bool forSelectionPreservation = false;
2072 return WebCore::indexForVisiblePosition(*node, position, forSelectionPreservation);
2075 Element* AccessibilityRenderObject::rootEditableElementForPosition(const Position& position) const
2077 // Find the root editable or pseudo-editable (i.e. having an editable ARIA role) element.
2078 Element* result = nullptr;
2080 Element* rootEditableElement = position.rootEditableElement();
2082 for (Element* e = position.element(); e && e != rootEditableElement; e = e->parentElement()) {
2083 if (nodeIsTextControl(e))
2085 if (e->hasTagName(bodyTag))
2092 return rootEditableElement;
2095 bool AccessibilityRenderObject::nodeIsTextControl(const Node* node) const
2100 if (AXObjectCache* cache = axObjectCache()) {
2101 if (AccessibilityObject* axObjectForNode = cache->getOrCreate(const_cast<Node*>(node)))
2102 return axObjectForNode->isTextControl();
2108 static IntRect boundsForRects(const LayoutRect& rect1, const LayoutRect& rect2, const SimpleRange& dataRange)
2110 LayoutRect ourRect = rect1;
2111 ourRect.unite(rect2);
2113 // If the rectangle spans lines and contains multiple text characters, use the range's bounding box intead.
2114 if (rect1.maxY() != rect2.maxY() && characterCount(dataRange) > 1) {
2115 if (auto boundingBox = unionRect(RenderObject::absoluteTextRects(dataRange)); !boundingBox.isEmpty())
2116 ourRect = boundingBox;
2119 return snappedIntRect(ourRect);
2122 IntRect AccessibilityRenderObject::boundsForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
2124 if (visiblePositionRange.isNull())
2127 // Create a mutable VisiblePositionRange.
2128 VisiblePositionRange range(visiblePositionRange);
2129 LayoutRect rect1 = range.start.absoluteCaretBounds();
2130 LayoutRect rect2 = range.end.absoluteCaretBounds();
2132 // readjust for position at the edge of a line. This is to exclude line rect that doesn't need to be accounted in the range bounds
2133 if (rect2.y() != rect1.y()) {
2134 VisiblePosition endOfFirstLine = endOfLine(range.start);
2135 if (range.start == endOfFirstLine) {
2136 range.start.setAffinity(Affinity::Downstream);
2137 rect1 = range.start.absoluteCaretBounds();
2139 if (range.end == endOfFirstLine) {
2140 range.end.setAffinity(Affinity::Upstream);
2141 rect2 = range.end.absoluteCaretBounds();
2145 return boundsForRects(rect1, rect2, *makeSimpleRange(range));
2148 IntRect AccessibilityRenderObject::boundsForRange(const SimpleRange& range) const
2150 auto cache = axObjectCache();
2154 auto start = cache->startOrEndCharacterOffsetForRange(range, true);
2155 auto end = cache->startOrEndCharacterOffsetForRange(range, false);
2157 auto rect1 = cache->absoluteCaretBoundsForCharacterOffset(start);
2158 auto rect2 = cache->absoluteCaretBoundsForCharacterOffset(end);
2160 // Readjust for position at the edge of a line. This is to exclude line rect that doesn't need to be accounted in the range bounds.
2161 if (rect2.y() != rect1.y()) {
2162 auto endOfFirstLine = cache->endCharacterOffsetOfLine(start);
2163 if (start.isEqual(endOfFirstLine)) {
2164 start = cache->nextCharacterOffset(start, false);
2165 rect1 = cache->absoluteCaretBoundsForCharacterOffset(start);
2167 if (end.isEqual(endOfFirstLine)) {
2168 end = cache->previousCharacterOffset(end, false);
2169 rect2 = cache->absoluteCaretBoundsForCharacterOffset(end);
2173 return boundsForRects(rect1, rect2, range);
2176 bool AccessibilityRenderObject::isVisiblePositionRangeInDifferentDocument(const VisiblePositionRange& range) const
2178 if (range.start.isNull() || range.end.isNull())
2181 VisibleSelection newSelection = VisibleSelection(range.start, range.end);
2182 if (Document* newSelectionDocument = newSelection.base().document()) {
2183 if (RefPtr<Frame> newSelectionFrame = newSelectionDocument->frame()) {
2184 Frame* frame = this->frame();
2185 if (!frame || (newSelectionFrame != frame && newSelectionDocument != frame->document()))
2193 void AccessibilityRenderObject::setSelectedVisiblePositionRange(const VisiblePositionRange& range) const
2198 // In WebKit1, when the top web area sets the selection to be an input element in an iframe, the caret will disappear.
2199 // FrameSelection::setSelectionWithoutUpdatingAppearance is setting the selection on the new frame in this case, and causing this behavior.
2200 if (isWebArea() && parentObject() && parentObject()->isAttachment()
2201 && isVisiblePositionRangeInDifferentDocument(range))
2204 // make selection and tell the document to use it. if it's zero length, then move to that position
2205 if (range.start == range.end) {
2206 setTextSelectionIntent(axObjectCache(), AXTextStateChangeTypeSelectionMove);
2208 auto start = range.start;
2209 if (auto elementRange = this->elementRange()) {
2210 if (!isPointInRange(*elementRange, makeBoundaryPoint(start)))
2211 start = createLegacyEditingPosition(elementRange->start);
2214 m_renderer->frame().selection().moveTo(start, UserTriggered);
2216 setTextSelectionIntent(axObjectCache(), AXTextStateChangeTypeSelectionExtend);
2218 VisibleSelection newSelection = VisibleSelection(range.start, range.end);
2219 m_renderer->frame().selection().setSelection(newSelection, FrameSelection::defaultSetSelectionOptions());
2222 clearTextSelectionIntent(axObjectCache());
2225 VisiblePosition AccessibilityRenderObject::visiblePositionForPoint(const IntPoint& point) const
2228 return VisiblePosition();
2230 // convert absolute point to view coordinates
2231 RenderView* renderView = topRenderer();
2233 return VisiblePosition();
2236 FrameView* frameView = &renderView->frameView();
2239 Node* innerNode = nullptr;
2241 // Locate the node containing the point
2242 // FIXME: Remove this loop and instead add HitTestRequest::AllowVisibleChildFrameContentOnly to the hit test request type.
2243 LayoutPoint pointResult;
2245 LayoutPoint pointToUse;
2247 pointToUse = frameView->screenToContents(point);
2251 constexpr OptionSet<HitTestRequest::RequestType> hitType { HitTestRequest::ReadOnly, HitTestRequest::Active };
2252 HitTestResult result { pointToUse };
2253 renderView->document().hitTest(hitType, result);
2254 innerNode = result.innerNode();
2256 return VisiblePosition();
2258 RenderObject* renderer = innerNode->renderer();
2260 return VisiblePosition();
2262 pointResult = result.localPoint();
2264 // done if hit something other than a widget
2265 if (!is<RenderWidget>(*renderer))
2268 // descend into widget (FRAME, IFRAME, OBJECT...)
2269 Widget* widget = downcast<RenderWidget>(*renderer).widget();
2270 if (!is<FrameView>(widget))
2272 Frame& frame = downcast<FrameView>(*widget).frame();
2273 renderView = frame.document()->renderView();
2275 frameView = downcast<FrameView>(widget);
2279 return innerNode->renderer()->positionForPoint(pointResult, nullptr);
2282 // NOTE: Consider providing this utility method as AX API
2283 VisiblePosition AccessibilityRenderObject::visiblePositionForIndex(unsigned indexValue, bool lastIndexOK) const
2285 if (!isTextControl())
2286 return VisiblePosition();
2288 // lastIndexOK specifies whether the position after the last character is acceptable
2289 if (indexValue >= text().length()) {
2290 if (!lastIndexOK || indexValue > text().length())
2291 return VisiblePosition();
2293 VisiblePosition position = visiblePositionForIndex(indexValue);
2294 position.setAffinity(Affinity::Downstream);
2298 // NOTE: Consider providing this utility method as AX API
2299 int AccessibilityRenderObject::index(const VisiblePosition& position) const
2301 if (position.isNull() || !isTextControl())
2304 if (renderObjectContainsPosition(renderer(), position.deepEquivalent()))
2305 return indexForVisiblePosition(position);
2310 void AccessibilityRenderObject::lineBreaks(Vector<int>& lineBreaks) const
2312 if (!isTextControl())
2315 VisiblePosition visiblePos = visiblePositionForIndex(0);
2316 VisiblePosition savedVisiblePos = visiblePos;
2317 visiblePos = nextLinePosition(visiblePos, 0);
2318 while (!visiblePos.isNull() && visiblePos != savedVisiblePos) {
2319 lineBreaks.append(indexForVisiblePosition(visiblePos));
2320 savedVisiblePos = visiblePos;
2321 visiblePos = nextLinePosition(visiblePos, 0);
2325 // Given a line number, the range of characters of the text associated with this accessibility
2326 // object that contains the line number.
2327 PlainTextRange AccessibilityRenderObject::doAXRangeForLine(unsigned lineNumber) const
2329 if (!isTextControl())
2330 return PlainTextRange();
2332 // iterate to the specified line
2333 VisiblePosition visiblePos = visiblePositionForIndex(0);
2334 VisiblePosition savedVisiblePos;
2335 for (unsigned lineCount = lineNumber; lineCount; lineCount -= 1) {
2336 savedVisiblePos = visiblePos;
2337 visiblePos = nextLinePosition(visiblePos, 0);
2338 if (visiblePos.isNull() || visiblePos == savedVisiblePos)
2339 return PlainTextRange();
2342 // Get the end of the line based on the starting position.
2343 VisiblePosition endPosition = endOfLine(visiblePos);
2345 int index1 = indexForVisiblePosition(visiblePos);
2346 int index2 = indexForVisiblePosition(endPosition);
2348 // add one to the end index for a line break not caused by soft line wrap (to match AppKit)
2349 if (endPosition.affinity() == Affinity::Downstream && endPosition.next().isNotNull())
2352 // return nil rather than an zero-length range (to match AppKit)
2353 if (index1 == index2)
2354 return PlainTextRange();
2356 return PlainTextRange(index1, index2 - index1);
2359 // The composed character range in the text associated with this accessibility object that
2360 // is specified by the given index value. This parameterized attribute returns the complete
2361 // range of characters (including surrogate pairs of multi-byte glyphs) at the given index.
2362 PlainTextRange AccessibilityRenderObject::doAXRangeForIndex(unsigned index) const
2364 if (!isTextControl())
2365 return PlainTextRange();
2367 String elementText = text();
2368 if (!elementText.length() || index > elementText.length() - 1)
2369 return PlainTextRange();
2371 return PlainTextRange(index, 1);
2374 // A substring of the text associated with this accessibility object that is
2375 // specified by the given character range.
2376 String AccessibilityRenderObject::doAXStringForRange(const PlainTextRange& range) const
2381 if (!isTextControl())
2384 String elementText = isPasswordField() ? passwordFieldValue() : text();
2385 return elementText.substring(range.start, range.length);
2388 // The bounding rectangle of the text associated with this accessibility object that is
2389 // specified by the given range. This is the bounding rectangle a sighted user would see
2390 // on the display screen, in pixels.
2391 IntRect AccessibilityRenderObject::doAXBoundsForRange(const PlainTextRange& range) const
2393 if (allowsTextRanges())
2394 return boundsForVisiblePositionRange(visiblePositionRangeForRange(range));
2398 IntRect AccessibilityRenderObject::doAXBoundsForRangeUsingCharacterOffset(const PlainTextRange& characterRange) const
2400 if (!allowsTextRanges())
2402 auto range = rangeForPlainTextRange(characterRange);
2405 return boundsForRange(*range);
2408 AXCoreObject* AccessibilityRenderObject::accessibilityImageMapHitTest(HTMLAreaElement* area, const IntPoint& point) const
2413 auto* mapParent = ancestorsOfType<HTMLMapElement>(*area).first();
2417 auto* parent = accessibilityParentForImageMap(mapParent);
2421 for (const auto& child : parent->children()) {
2422 if (child->elementRect().contains(point))
2429 AXCoreObject* AccessibilityRenderObject::remoteSVGElementHitTest(const IntPoint& point) const
2431 AccessibilityObject* remote = remoteSVGRootElement(Create);
2435 IntSize offset = point - roundedIntPoint(boundingBoxRect().location());
2436 return remote->accessibilityHitTest(IntPoint(offset));
2439 AXCoreObject* AccessibilityRenderObject::elementAccessibilityHitTest(const IntPoint& point) const
2442 return remoteSVGElementHitTest(point);
2444 return AccessibilityObject::elementAccessibilityHitTest(point);
2447 static bool shouldUseShadowHostForHitTesting(Node* shadowHost)
2449 // We need to allow automation of mouse events on video tags.
2450 return shadowHost && !shadowHost->hasTagName(videoTag);
2453 AXCoreObject* AccessibilityRenderObject::accessibilityHitTest(const IntPoint& point) const
2455 if (!m_renderer || !m_renderer->hasLayer())
2458 m_renderer->document().updateLayout();
2460 if (!m_renderer || !m_renderer->hasLayer())
2463 RenderLayer* layer = downcast<RenderBox>(*m_renderer).layer();
2465 constexpr OptionSet<HitTestRequest::RequestType> hitType { HitTestRequest::ReadOnly, HitTestRequest::Active, HitTestRequest::AccessibilityHitTest };
2466 HitTestResult hitTestResult { point };
2467 layer->hitTest(hitType, hitTestResult);
2468 Node* node = hitTestResult.innerNode();
2471 Node* shadowAncestorNode = node->shadowHost();
2472 if (shouldUseShadowHostForHitTesting(shadowAncestorNode))
2473 node = shadowAncestorNode;
2476 if (is<HTMLAreaElement>(*node))
2477 return accessibilityImageMapHitTest(downcast<HTMLAreaElement>(node), point);
2479 if (is<HTMLOptionElement>(*node))
2480 node = downcast<HTMLOptionElement>(*node).ownerSelectElement();
2482 RenderObject* obj = node->renderer();
2486 AXCoreObject* result = obj->document().axObjectCache()->getOrCreate(obj);
2487 result->updateChildrenIfNecessary();
2489 // Allow the element to perform any hit-testing it might need to do to reach non-render children.
2490 result = static_cast<AccessibilityObject*>(result->elementAccessibilityHitTest(point));
2492 if (result && result->accessibilityIsIgnored()) {
2493 // If this element is the label of a control, a hit test should return the control.
2494 AXCoreObject* controlObject = result->correspondingControlForLabelElement();
2495 if (controlObject && !controlObject->exposesTitleUIElement())
2496 return controlObject;
2498 result = result->parentObjectUnignored();
2504 bool AccessibilityRenderObject::shouldNotifyActiveDescendant() const
2507 // According to the Core AAM spec, ATK expects object:state-changed:focused notifications
2508 // whenever the active descendant changes.
2511 // We want to notify that the combo box has changed its active descendant,
2512 // but we do not want to change the focus, because focus should remain with the combo box.
2516 return shouldFocusActiveDescendant();
2519 bool AccessibilityRenderObject::shouldFocusActiveDescendant() const
2521 switch (ariaRoleAttribute()) {
2522 case AccessibilityRole::ApplicationGroup:
2523 case AccessibilityRole::ListBox:
2524 case AccessibilityRole::Menu:
2525 case AccessibilityRole::MenuBar:
2526 case AccessibilityRole::RadioGroup:
2527 case AccessibilityRole::Row:
2528 case AccessibilityRole::PopUpButton:
2529 case AccessibilityRole::Meter:
2530 case AccessibilityRole::ProgressIndicator:
2531 case AccessibilityRole::Toolbar:
2532 case AccessibilityRole::Outline:
2533 case AccessibilityRole::Tree:
2534 case AccessibilityRole::Grid:
2535 /* FIXME: replace these with actual roles when they are added to AccessibilityRole
2548 AccessibilityObject* AccessibilityRenderObject::activeDescendant() const
2553 const AtomString& activeDescendantAttrStr = getAttribute(aria_activedescendantAttr);
2554 if (activeDescendantAttrStr.isNull() || activeDescendantAttrStr.isEmpty())
2556 Element* element = this->element();
2560 Element* target = element->treeScope().getElementById(activeDescendantAttrStr);
2564 if (AXObjectCache* cache = axObjectCache()) {
2565 AccessibilityObject* obj = cache->getOrCreate(target);
2566 if (obj && obj->isAccessibilityRenderObject())
2567 // an activedescendant is only useful if it has a renderer, because that's what's needed to post the notification
2574 RenderObject* AccessibilityRenderObject::targetElementForActiveDescendant(const QualifiedName& attributeName, AccessibilityObject* activeDescendant) const
2576 AccessibilityObject::AccessibilityChildrenVector elements;
2577 ariaElementsFromAttribute(elements, attributeName);
2578 for (const auto& element : elements) {
2579 if (activeDescendant->isDescendantOfObject(element.get()))
2580 return element->renderer();
2586 void AccessibilityRenderObject::handleActiveDescendantChanged()
2588 Element* element = downcast<Element>(renderer()->node());
2591 if (!renderer()->frame().selection().isFocusedAndActive() || renderer()->document().focusedElement() != element)
2594 auto* activeDescendant = this->activeDescendant();
2595 if (activeDescendant && shouldNotifyActiveDescendant()) {
2596 auto* targetRenderer = renderer();
2599 // If the combobox's activeDescendant is inside another object, the target element should be that parent.
2601 if (auto* ariaOwner = targetElementForActiveDescendant(aria_ownsAttr, activeDescendant))
2602 targetRenderer = ariaOwner;
2603 else if (auto* ariaController = targetElementForActiveDescendant(aria_controlsAttr, activeDescendant))
2604 targetRenderer = ariaController;
2608 renderer()->document().axObjectCache()->postNotification(targetRenderer, AXObjectCache::AXActiveDescendantChanged);
2612 AccessibilityObject* AccessibilityRenderObject::correspondingControlForLabelElement() const
2614 HTMLLabelElement* labelElement = labelElementContainer();
2618 auto correspondingControl = labelElement->control();
2619 if (!correspondingControl)
2622 // Make sure the corresponding control isn't a descendant of this label that's in the middle of being destroyed.
2623 if (correspondingControl->renderer() && !correspondingControl->renderer()->parent())
2626 return axObjectCache()->getOrCreate(correspondingControl.get());
2629 AccessibilityObject* AccessibilityRenderObject::correspondingLabelForControlElement() const
2634 // ARIA: section 2A, bullet #3 says if aria-labeledby or aria-label appears, it should
2635 // override the "label" element association.
2636 if (hasTextAlternative())
2639 Node* node = m_renderer->node();
2640 if (is<HTMLElement>(node)) {
2641 if (HTMLLabelElement* label = labelForElement(downcast<HTMLElement>(node)))
2642 return axObjectCache()->getOrCreate(label);
2648 bool AccessibilityRenderObject::renderObjectIsObservable(RenderObject& renderer) const
2650 // AX clients will listen for AXValueChange on a text control.
2651 if (is<RenderTextControl>(renderer))
2654 // AX clients will listen for AXSelectedChildrenChanged on listboxes.
2655 Node* node = renderer.node();
2659 if (nodeHasRole(node, "listbox") || (is<RenderBoxModelObject>(renderer) && downcast<RenderBoxModelObject>(renderer).isListBox()))
2662 // Textboxes should send out notifications.
2663 if (nodeHasRole(node, "textbox") || (is<Element>(*node) && contentEditableAttributeIsEnabled(downcast<Element>(node))))
2669 AccessibilityObject* AccessibilityRenderObject::observableObject() const
2671 // Find the object going up the parent chain that is used in accessibility to monitor certain notifications.
2672 for (RenderObject* renderer = this->renderer(); renderer && renderer->node(); renderer = renderer->parent()) {
2673 if (renderObjectIsObservable(*renderer)) {
2674 if (AXObjectCache* cache = axObjectCache())
2675 return cache->getOrCreate(renderer);
2682 bool AccessibilityRenderObject::isDescendantOfElementType(const HashSet<QualifiedName>& tagNames) const
2684 for (auto& ancestor : ancestorsOfType<RenderElement>(*m_renderer)) {
2685 if (ancestor.element() && tagNames.contains(ancestor.element()->tagQName()))
2691 bool AccessibilityRenderObject::isDescendantOfElementType(const QualifiedName& tagName) const
2693 for (auto& ancestor : ancestorsOfType<RenderElement>(*m_renderer)) {
2694 if (ancestor.element() && ancestor.element()->hasTagName(tagName))
2700 String AccessibilityRenderObject::expandedTextValue() const
2702 if (AccessibilityObject* parent = parentObject()) {
2703 if (parent->hasTagName(abbrTag) || parent->hasTagName(acronymTag))
2704 return parent->getAttribute(titleAttr);
2710 bool AccessibilityRenderObject::supportsExpandedTextValue() const
2712 if (roleValue() == AccessibilityRole::StaticText) {
2713 if (AccessibilityObject* parent = parentObject())
2714 return parent->hasTagName(abbrTag) || parent->hasTagName(acronymTag);
2720 AccessibilityRole AccessibilityRenderObject::determineAccessibilityRole()
2722 AXTRACE("AccessibilityRenderObject::determineAccessibilityRole");
2724 return AccessibilityRole::Unknown;
2726 #if ENABLE(APPLE_PAY)
2727 if (isApplePayButton())
2728 return AccessibilityRole::Button;
2731 // Sometimes we need to ignore the attribute role. Like if a tree is malformed,
2732 // we want to ignore the treeitem's attribute role.
2733 if ((m_ariaRole = determineAriaRoleAttribute()) != AccessibilityRole::Unknown && !shouldIgnoreAttributeRole())
2736 Node* node = m_renderer->node();
2737 RenderBoxModelObject* cssBox = renderBoxModelObject();
2739 if (node && node->isLink())
2740 return AccessibilityRole::WebCoreLink;
2741 if (node && is<HTMLImageElement>(*node) && downcast<HTMLImageElement>(*node).hasAttributeWithoutSynchronization(usemapAttr))
2742 return AccessibilityRole::ImageMap;
2743 if ((cssBox && cssBox->isListItem()) || (node && node->hasTagName(liTag)))
2744 return AccessibilityRole::ListItem;
2745 if (m_renderer->isListMarker())
2746 return AccessibilityRole::ListMarker;
2747 if (node && node->hasTagName(buttonTag))
2748 return buttonRoleType();
2749 if (node && node->hasTagName(legendTag))
2750 return AccessibilityRole::Legend;
2751 if (m_renderer->isText())
2752 return AccessibilityRole::StaticText;
2753 if (cssBox && cssBox->isImage()) {
2754 if (is<HTMLInputElement>(node))
2755 return hasPopup() ? AccessibilityRole::PopUpButton : AccessibilityRole::Button;
2757 return AccessibilityRole::SVGRoot;
2758 return AccessibilityRole::Image;
2761 if (node && node->hasTagName(canvasTag))
2762 return AccessibilityRole::Canvas;
2764 if (cssBox && cssBox->isRenderView())
2765 return AccessibilityRole::WebArea;
2767 if (cssBox && cssBox->isTextField()) {
2768 if (is<HTMLInputElement>(node))
2769 return downcast<HTMLInputElement>(*node).isSearchField() ? AccessibilityRole::SearchField : AccessibilityRole::TextField;
2772 if (cssBox && cssBox->isTextArea())
2773 return AccessibilityRole::TextArea;
2775 if (is<HTMLInputElement>(node)) {
2776 HTMLInputElement& input = downcast<HTMLInputElement>(*node);
2777 if (input.isCheckbox())
2778 return AccessibilityRole::CheckBox;
2779 if (input.isRadioButton())
2780 return AccessibilityRole::RadioButton;
2781 if (input.isTextButton())
2782 return buttonRoleType();
2783 // On iOS, the date field and time field are popup buttons. On other platforms they are text fields.
2784 #if PLATFORM(IOS_FAMILY)
2785 if (input.isDateField() || input.isTimeField())
2786 return AccessibilityRole::PopUpButton;
2788 #if ENABLE(INPUT_TYPE_COLOR)
2789 if (input.isColorControl())
2790 return AccessibilityRole::ColorWell;
2794 if (hasContentEditableAttributeSet())
2795 return AccessibilityRole::TextArea;
2797 if (isFileUploadButton())
2798 return AccessibilityRole::Button;
2800 if (cssBox && cssBox->isMenuList())
2801 return AccessibilityRole::PopUpButton;
2804 return AccessibilityRole::Heading;
2806 if (m_renderer->isSVGRoot())
2807 return AccessibilityRole::SVGRoot;
2809 if (isStyleFormatGroup()) {
2810 if (node->hasTagName(delTag))
2811 return AccessibilityRole::Deletion;
2812 if (node->hasTagName(insTag))
2813 return AccessibilityRole::Insertion;
2814 if (node->hasTagName(subTag))
2815 return AccessibilityRole::Subscript;
2816 if (node->hasTagName(supTag))
2817 return AccessibilityRole::Superscript;
2818 return is<RenderInline>(*m_renderer) ? AccessibilityRole::Inline : AccessibilityRole::TextGroup;
2821 if (node && node->hasTagName(ddTag))
2822 return AccessibilityRole::DescriptionListDetail;
2824 if (node && node->hasTagName(dtTag))
2825 return AccessibilityRole::DescriptionListTerm;
2827 if (node && node->hasTagName(dlTag))
2828 return AccessibilityRole::DescriptionList;
2830 if (node && node->hasTagName(fieldsetTag))
2831 return AccessibilityRole::Group;
2833 if (node && node->hasTagName(figureTag))
2834 return AccessibilityRole::Figure;
2836 // Check for Ruby elements
2837 if (m_renderer->isRubyText())
2838 return AccessibilityRole::RubyText;
2839 if (m_renderer->isRubyBase())
2840 return AccessibilityRole::RubyBase;
2841 if (m_renderer->isRubyRun())
2842 return AccessibilityRole::RubyRun;
2843 if (m_renderer->isRubyBlock())
2844 return AccessibilityRole::RubyBlock;
2845 if (m_renderer->isRubyInline())
2846 return AccessibilityRole::RubyInline;
2848 // This return value is what will be used if AccessibilityTableCell determines
2849 // the cell should not be treated as a cell (e.g. because it is a layout table.
2850 if (is<RenderTableCell>(renderer()))
2851 return AccessibilityRole::TextGroup;
2853 // Table sections should be ignored.
2854 if (m_renderer->isTableSection())
2855 return AccessibilityRole::Ignored;
2857 if (m_renderer->isHR())
2858 return AccessibilityRole::HorizontalRule;
2860 if (node && node->hasTagName(pTag))
2861 return AccessibilityRole::Paragraph;
2863 if (is<HTMLLabelElement>(node))
2864 return AccessibilityRole::Label;
2866 if (node && node->hasTagName(dfnTag))
2867 return AccessibilityRole::Definition;
2869 if (node && node->hasTagName(divTag))
2870 return AccessibilityRole::Div;
2872 if (is<HTMLFormElement>(node))
2873 return AccessibilityRole::Form;
2875 if (node && node->hasTagName(articleTag))
2876 return AccessibilityRole::DocumentArticle;
2878 if (node && node->hasTagName(mainTag))
2879 return AccessibilityRole::LandmarkMain;
2881 if (node && node->hasTagName(navTag))
2882 return AccessibilityRole::LandmarkNavigation;
2884 if (node && node->hasTagName(asideTag))
2885 return AccessibilityRole::LandmarkComplementary;
2887 // The default role attribute value for the section element, region, became a landmark in ARIA 1.1.
2888 // The HTML AAM spec says it is "strongly recommended" that ATs only convey and provide navigation
2889 // for section elements which have names.
2890 if (node && node->hasTagName(sectionTag))
2891 return hasAttribute(aria_labelAttr) || hasAttribute(aria_labelledbyAttr) ? AccessibilityRole::LandmarkRegion : AccessibilityRole::TextGroup;
2893 if (node && node->hasTagName(addressTag))
2894 return AccessibilityRole::Group;
2896 if (node && node->hasTagName(blockquoteTag))
2897 return AccessibilityRole::Blockquote;
2899 if (node && node->hasTagName(captionTag))
2900 return AccessibilityRole::Caption;
2902 if (node && node->hasTagName(markTag))
2903 return AccessibilityRole::Mark;
2905 if (node && node->hasTagName(preTag))
2906 return AccessibilityRole::Pre;
2908 if (is<HTMLDetailsElement>(node))
2909 return AccessibilityRole::Details;
2910 if (is<HTMLSummaryElement>(node))
2911 return AccessibilityRole::Summary;
2913 // http://rawgit.com/w3c/aria/master/html-aam/html-aam.html
2914 // Output elements should be mapped to status role.
2916 return AccessibilityRole::ApplicationStatus;
2919 if (is<HTMLVideoElement>(node))
2920 return AccessibilityRole::Video;
2921 if (is<HTMLAudioElement>(node))
2922 return AccessibilityRole::Audio;
2925 // The HTML element should not be exposed as an element. That's what the RenderView element does.
2926 if (node && node->hasTagName(htmlTag))
2927 return AccessibilityRole::Ignored;
2929 // There should only be one banner/contentInfo per page. If header/footer are being used within an article or section
2930 // then it should not be exposed as whole page's banner/contentInfo
2931 if (node && node->hasTagName(headerTag) && !isDescendantOfElementType({ articleTag, sectionTag }))
2932 return AccessibilityRole::LandmarkBanner;
2934 // http://webkit.org/b/190138 Footers should become contentInfo's if scoped to body (and consequently become a landmark).
2935 // It should remain a footer if scoped to main, sectioning elements (article, section) or root sectioning element (blockquote, details, dialog, fieldset, figure, td).
2936 if (node && node->hasTagName(footerTag)) {
2937 if (!isDescendantOfElementType({ articleTag, sectionTag, mainTag, blockquoteTag, detailsTag, fieldsetTag, figureTag, tdTag }))
2938 return AccessibilityRole::LandmarkContentInfo;
2939 return AccessibilityRole::Footer;
2942 // menu tags with toolbar type should have Toolbar role.
2943 if (node && node->hasTagName(menuTag) && equalLettersIgnoringASCIICase(getAttribute(typeAttr), "toolbar"))
2944 return AccessibilityRole::Toolbar;
2946 if (node && node->hasTagName(timeTag))
2947 return AccessibilityRole::Time;
2949 // If the element does not have role, but it has ARIA attributes, or accepts tab focus, accessibility should fallback to exposing it as a group.
2950 if (supportsARIAAttributes() || canSetFocusAttribute())
2951 return AccessibilityRole::Group;
2953 if (m_renderer->isRenderBlockFlow())
2954 return m_renderer->isAnonymousBlock() ? AccessibilityRole::TextGroup : AccessibilityRole::Group;
2956 // InlineRole is the final fallback before assigning AccessibilityRole::Unknown to an object. It makes it
2957 // possible to distinguish truly unknown objects from non-focusable inline text elements
2958 // which have an event handler or attribute suggesting possible inclusion by the platform.
2959 if (is<RenderInline>(*m_renderer)
2960 && (hasAttributesRequiredForInclusion()
2961 || (node && node->hasEventListeners())
2962 || (supportsDatetimeAttribute() && !getAttribute(datetimeAttr).isEmpty())))
2963 return AccessibilityRole::Inline;
2965 return AccessibilityRole::Unknown;
2968 AccessibilityOrientation AccessibilityRenderObject::orientation() const
2970 const AtomString& ariaOrientation = getAttribute(aria_orientationAttr);
2971 if (equalLettersIgnoringASCIICase(ariaOrientation, "horizontal"))
2972 return AccessibilityOrientation::Horizontal;
2973 if (equalLettersIgnoringASCIICase(ariaOrientation, "vertical"))
2974 return AccessibilityOrientation::Vertical;
2975 if (equalLettersIgnoringASCIICase(ariaOrientation, "undefined"))
2976 return AccessibilityOrientation::Undefined;
2978 // In ARIA 1.1, the implicit value of aria-orientation changed from horizontal
2979 // to undefined on all roles that don't have their own role-specific values. In
2980 // addition, the implicit value of combobox became undefined.
2981 if (isComboBox() || isRadioGroup() || isTreeGrid())
2982 return AccessibilityOrientation::Undefined;
2984 if (isScrollbar() || isListBox() || isMenu() || isTree())
2985 return AccessibilityOrientation::Vertical;
2987 if (isMenuBar() || isSplitter() || isTabList() || isToolbar() || isSlider())
2988 return AccessibilityOrientation::Horizontal;
2990 return AccessibilityObject::orientation();
2993 bool AccessibilityRenderObject::inheritsPresentationalRole() const
2995 // ARIA states if an item can get focus, it should not be presentational.
2996 if (canSetFocusAttribute())
2999 // ARIA spec says that when a parent object is presentational, and it has required child elements,
3000 // those child elements are also presentational. For example, <li> becomes presentational from <ul>.
3001 // http://www.w3.org/WAI/PF/aria/complete#presentation
3003 const Vector<const HTMLQualifiedName*>* parentTags;
3004 switch (roleValue()) {
3005 case AccessibilityRole::ListItem:
3006 case AccessibilityRole::ListMarker: {
3007 static const auto listItemParents = makeNeverDestroyed(Vector<const HTMLQualifiedName*> { &dlTag.get(), &olTag.get(), &ulTag.get() });
3008 parentTags = &listItemParents.get();
3011 case AccessibilityRole::GridCell:
3012 case AccessibilityRole::Cell: {
3013 static const auto tableCellParents = makeNeverDestroyed(Vector<const HTMLQualifiedName*> { &tableTag.get() });
3014 parentTags = &tableCellParents.get();
3018 // Not all elements need to do the following check, only ones that are required children.
3022 for (auto* parent = parentObject(); parent; parent = parent->parentObject()) {
3023 if (!is<AccessibilityRenderObject>(*parent))
3026 Node* node = downcast<AccessibilityRenderObject>(*parent).node();
3027 if (!is<Element>(node))
3030 // If native tag of the parent element matches an acceptable name, then return
3031 // based on its presentational status.
3032 auto& name = downcast<Element>(*node).tagQName();
3033 if (std::any_of(parentTags->begin(), parentTags->end(), [&name] (auto* possibleName) { return *possibleName == name; }))
3034 return parent->roleValue() == AccessibilityRole::Presentational;
3040 bool AccessibilityRenderObject::isPresentationalChildOfAriaRole() const
3042 // Walk the parent chain looking for a parent that has presentational children
3043 AccessibilityObject* parent;
3044 for (parent = parentObject(); parent && !parent->ariaRoleHasPresentationalChildren(); parent = parent->parentObject())
3050 bool AccessibilityRenderObject::ariaRoleHasPresentationalChildren() const
3052 switch (m_ariaRole) {
3053 case AccessibilityRole::Button:
3054 case AccessibilityRole::Slider:
3055 case AccessibilityRole::Image:
3056 case AccessibilityRole::ProgressIndicator:
3057 case AccessibilityRole::SpinButton:
3058 // case SeparatorRole:
3065 bool AccessibilityRenderObject::canSetExpandedAttribute() const
3067 if (roleValue() == AccessibilityRole::Details)
3070 // An object can be expanded if it aria-expanded is true or false.
3071 const AtomString& expanded = getAttribute(aria_expandedAttr);
3072 if (equalLettersIgnoringASCIICase(expanded, "true") || equalLettersIgnoringASCIICase(expanded, "false"))
3077 bool AccessibilityRenderObject::canSetTextRangeAttributes() const
3079 return isTextControl();
3082 void AccessibilityRenderObject::textChanged()
3084 // If this element supports ARIA live regions, or is part of a region with an ARIA editable role,
3085 // then notify the AT of changes.
3086 AXObjectCache* cache = axObjectCache();
3090 for (RenderObject* renderParent = renderer(); renderParent; renderParent = renderParent->parent()) {
3091 AccessibilityObject* parent = cache->get(renderParent);
3095 if (parent->supportsLiveRegion())
3096 cache->postLiveRegionChangeNotification(parent);
3098 if (parent->isNonNativeTextControl())
3099 cache->postNotification(renderParent, AXObjectCache::AXValueChanged);
3103 void AccessibilityRenderObject::clearChildren()
3105 AccessibilityObject::clearChildren();
3106 m_childrenDirty = false;
3109 void AccessibilityRenderObject::addImageMapChildren()
3111 RenderBoxModelObject* cssBox = renderBoxModelObject();
3112 if (!is<RenderImage>(cssBox))
3115 HTMLMapElement* map = downcast<RenderImage>(*cssBox).imageMap();
3119 for (auto& area : descendantsOfType<HTMLAreaElement>(*map)) {
3120 // add an <area> element for this child if it has a link
3123 auto& areaObject = downcast<AccessibilityImageMapLink>(*axObjectCache()->create(AccessibilityRole::ImageMapLink));
3124 areaObject.setHTMLAreaElement(&area);
3125 areaObject.setHTMLMapElement(map);
3126 areaObject.setParent(this);
3127 if (!areaObject.accessibilityIsIgnored())
3128 m_children.append(&areaObject);
3130 axObjectCache()->remove(areaObject.objectID());
3134 void AccessibilityRenderObject::updateChildrenIfNecessary()
3136 if (needsToUpdateChildren())
3139 AccessibilityObject::updateChildrenIfNecessary();
3142 void AccessibilityRenderObject::addTextFieldChildren()
3144 Node* node = this->node();
3145 if (!is<HTMLInputElement>(node))
3148 HTMLInputElement& input = downcast<HTMLInputElement>(*node);
3149 if (HTMLElement* autoFillElement = input.autoFillButtonElement()) {
3150 if (AccessibilityObject* axAutoFill = axObjectCache()->getOrCreate(autoFillElement))
3151 m_children.append(axAutoFill);
3154 HTMLElement* spinButtonElement = input.innerSpinButtonElement();
3155 if (!is<SpinButtonElement>(spinButtonElement))
3158 auto& axSpinButton = downcast<AccessibilitySpinButton>(*axObjectCache()->create(AccessibilityRole::SpinButton));
3159 axSpinButton.setSpinButtonElement(downcast<SpinButtonElement>(spinButtonElement));
3160 axSpinButton.setParent(this);
3161 m_children.append(&axSpinButton);
3164 bool AccessibilityRenderObject::isSVGImage() const
3166 return remoteSVGRootElement(Create);
3169 void AccessibilityRenderObject::detachRemoteSVGRoot()
3171 if (AccessibilitySVGRoot* root = remoteSVGRootElement(Retrieve))
3172 root->setParent(nullptr);
3175 AccessibilitySVGRoot* AccessibilityRenderObject::remoteSVGRootElement(CreationChoice createIfNecessary) const
3177 if (!is<RenderImage>(renderer()))
3180 CachedImage* cachedImage = downcast<RenderImage>(*m_renderer).cachedImage();
3184 Image* image = cachedImage->image();
3185 if (!is<SVGImage>(image))
3188 FrameView* frameView = downcast<SVGImage>(*image).frameView();
3191 Frame& frame = frameView->frame();
3193 Document* document = frame.document();
3194 if (!is<SVGDocument>(document))
3197 auto rootElement = SVGDocument::rootElement(*document);
3200 RenderObject* rendererRoot = rootElement->renderer();
3204 AXObjectCache* cache = frame.document()->axObjectCache();
3207 AccessibilityObject* rootSVGObject = createIfNecessary == Create ? cache->getOrCreate(rendererRoot) : cache->get(rendererRoot);
3209 // In order to connect the AX hierarchy from the SVG root element from the loaded resource
3210 // the parent must be set, because there's no other way to get back to who created the image.
3211 ASSERT(!createIfNecessary || rootSVGObject);
3212 if (!is<AccessibilitySVGRoot>(rootSVGObject))
3215 return downcast<AccessibilitySVGRoot>(rootSVGObject);
3218 void AccessibilityRenderObject::addRemoteSVGChildren()
3220 AccessibilitySVGRoot* root = remoteSVGRootElement(Create);
3224 root->setParent(this);
3226 if (root->accessibilityIsIgnored()) {
3227 for (const auto& child : root->children())
3228 m_children.append(child);
3230 m_children.append(root);
3233 void AccessibilityRenderObject::addCanvasChildren()
3235 // Add the unrendered canvas children as AX nodes, unless we're not using a canvas renderer
3236 // because JS is disabled for example.
3237 if (!node() || !node()->hasTagName(canvasTag) || (renderer() && !renderer()->isCanvas()))
3240 // If it's a canvas, it won't have rendered children, but it might have accessible fallback content.
3241 // Clear m_haveChildren because AccessibilityNodeObject::addChildren will expect it to be false.
3242 ASSERT(!m_children.size());
3243 m_haveChildren = false;
3244 AccessibilityNodeObject::addChildren();
3247 void AccessibilityRenderObject::addAttachmentChildren()
3249 if (!isAttachment())
3252 // FrameView's need to be inserted into the AX hierarchy when encountered.
3253 Widget* widget = widgetForAttachmentView();
3254 if (!widget || !widget->isFrameView())
3257 addChild(axObjectCache()