1/*
2* Copyright (C) 2008 Apple Inc. All rights reserved.
3*
4* Redistribution and use in source and binary forms, with or without
5* modification, are permitted provided that the following conditions
6* are met:
7*
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.
16*
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.
27*/
28
29#include "config.h"
30#include "AccessibilityRenderObject.h"
31
32#include "AXObjectCache.h"
33#include "AccessibilityImageMapLink.h"
34#include "AccessibilityLabel.h"
35#include "AccessibilityListBox.h"
36#include "AccessibilitySVGRoot.h"
37#include "AccessibilitySpinButton.h"
38#include "AccessibilityTable.h"
39#include "CachedImage.h"
40#include "Editing.h"
41#include "Editor.h"
42#include "ElementIterator.h"
43#include "FloatRect.h"
44#include "Frame.h"
45#include "FrameLoader.h"
46#include "FrameSelection.h"
47#include "HTMLAreaElement.h"
48#include "HTMLAudioElement.h"
49#include "HTMLDetailsElement.h"
50#include "HTMLFormElement.h"
51#include "HTMLFrameElementBase.h"
52#include "HTMLImageElement.h"
53#include "HTMLInputElement.h"
54#include "HTMLLabelElement.h"
55#include "HTMLMapElement.h"
56#include "HTMLMeterElement.h"
57#include "HTMLNames.h"
58#include "HTMLOptionElement.h"
59#include "HTMLOptionsCollection.h"
60#include "HTMLParserIdioms.h"
61#include "HTMLSelectElement.h"
62#include "HTMLSummaryElement.h"
63#include "HTMLTableElement.h"
64#include "HTMLTextAreaElement.h"
65#include "HTMLVideoElement.h"
66#include "HitTestRequest.h"
67#include "HitTestResult.h"
68#include "Image.h"
69#include "LocalizedStrings.h"
70#include "NodeList.h"
71#include "Page.h"
72#include "ProgressTracker.h"
73#include "RenderButton.h"
74#include "RenderFileUploadControl.h"
75#include "RenderHTMLCanvas.h"
76#include "RenderImage.h"
77#include "RenderInline.h"
78#include "RenderIterator.h"
79#include "RenderLayer.h"
80#include "RenderLineBreak.h"
81#include "RenderListBox.h"
82#include "RenderListItem.h"
83#include "RenderListMarker.h"
84#include "RenderMathMLBlock.h"
85#include "RenderMenuList.h"
86#include "RenderSVGRoot.h"
87#include "RenderSVGShape.h"
88#include "RenderTableCell.h"
89#include "RenderText.h"
90#include "RenderTextControl.h"
91#include "RenderTextControlSingleLine.h"
92#include "RenderTextFragment.h"
93#include "RenderTheme.h"
94#include "RenderView.h"
95#include "RenderWidget.h"
96#include "RenderedPosition.h"
97#include "SVGDocument.h"
98#include "SVGImage.h"
99#include "SVGSVGElement.h"
100#include "Text.h"
101#include "TextControlInnerElements.h"
102#include "TextIterator.h"
103#include "VisibleUnits.h"
104#include <wtf/NeverDestroyed.h>
105#include <wtf/StdLibExtras.h>
106#include <wtf/unicode/CharacterNames.h>
107
108namespace WebCore {
109
110using namespace HTMLNames;
111
112AccessibilityRenderObject::AccessibilityRenderObject(RenderObject* renderer)
113 : AccessibilityNodeObject(renderer->node())
114 , m_renderer(makeWeakPtr(renderer))
115{
116#ifndef NDEBUG
117 m_renderer->setHasAXObject(true);
118#endif
119}
120
121AccessibilityRenderObject::~AccessibilityRenderObject()
122{
123 ASSERT(isDetached());
124}
125
126void AccessibilityRenderObject::init()
127{
128 AccessibilityNodeObject::init();
129}
130
131Ref<AccessibilityRenderObject> AccessibilityRenderObject::create(RenderObject* renderer)
132{
133 return adoptRef(*new AccessibilityRenderObject(renderer));
134}
135
136void AccessibilityRenderObject::detach(AccessibilityDetachmentType detachmentType, AXObjectCache* cache)
137{
138 AccessibilityNodeObject::detach(detachmentType, cache);
139
140 detachRemoteSVGRoot();
141
142#ifndef NDEBUG
143 if (m_renderer)
144 m_renderer->setHasAXObject(false);
145#endif
146 m_renderer = nullptr;
147}
148
149RenderBoxModelObject* AccessibilityRenderObject::renderBoxModelObject() const
150{
151 if (!is<RenderBoxModelObject>(renderer()))
152 return nullptr;
153 return downcast<RenderBoxModelObject>(renderer());
154}
155
156void AccessibilityRenderObject::setRenderer(RenderObject* renderer)
157{
158 m_renderer = makeWeakPtr(renderer);
159 setNode(renderer->node());
160}
161
162static inline bool isInlineWithContinuation(RenderObject& object)
163{
164 return is<RenderInline>(object) && downcast<RenderInline>(object).continuation();
165}
166
167static inline RenderObject* firstChildInContinuation(RenderInline& renderer)
168{
169 auto* continuation = renderer.continuation();
170
171 while (continuation) {
172 if (is<RenderBlock>(*continuation))
173 return continuation;
174 if (RenderObject* child = continuation->firstChild())
175 return child;
176 continuation = downcast<RenderInline>(*continuation).continuation();
177 }
178
179 return nullptr;
180}
181
182static inline RenderObject* firstChildConsideringContinuation(RenderObject& renderer)
183{
184 RenderObject* firstChild = renderer.firstChildSlow();
185
186 // We don't want to include the end of a continuation as the firstChild of the
187 // anonymous parent, because everything has already been linked up via continuation.
188 // CSS first-letter selector is an example of this case.
189 if (renderer.isAnonymous() && is<RenderInline>(firstChild) && downcast<RenderInline>(*firstChild).isContinuation())
190 firstChild = nullptr;
191
192 if (!firstChild && isInlineWithContinuation(renderer))
193 firstChild = firstChildInContinuation(downcast<RenderInline>(renderer));
194
195 return firstChild;
196}
197
198
199static inline RenderObject* lastChildConsideringContinuation(RenderObject& renderer)
200{
201 if (!is<RenderInline>(renderer) && !is<RenderBlock>(renderer))
202 return &renderer;
203
204 RenderObject* lastChild = downcast<RenderBoxModelObject>(renderer).lastChild();
205 for (auto* current = &downcast<RenderBoxModelObject>(renderer); current; ) {
206 if (RenderObject* newLastChild = current->lastChild())
207 lastChild = newLastChild;
208
209 current = current->inlineContinuation();
210 }
211
212 return lastChild;
213}
214
215AccessibilityObject* AccessibilityRenderObject::firstChild() const
216{
217 if (!m_renderer)
218 return nullptr;
219
220 RenderObject* firstChild = firstChildConsideringContinuation(*m_renderer);
221
222 // If an object can't have children, then it is using this method to help
223 // calculate some internal property (like its description).
224 // In this case, it should check the Node level for children in case they're
225 // not rendered (like a <meter> element).
226 if (!firstChild && !canHaveChildren())
227 return AccessibilityNodeObject::firstChild();
228
229 return axObjectCache()->getOrCreate(firstChild);
230}
231
232AccessibilityObject* AccessibilityRenderObject::lastChild() const
233{
234 if (!m_renderer)
235 return nullptr;
236
237 RenderObject* lastChild = lastChildConsideringContinuation(*m_renderer);
238
239 if (!lastChild && !canHaveChildren())
240 return AccessibilityNodeObject::lastChild();
241
242 return axObjectCache()->getOrCreate(lastChild);
243}
244
245static inline RenderInline* startOfContinuations(RenderObject& renderer)
246{
247 if (!is<RenderElement>(renderer))
248 return nullptr;
249 auto& renderElement = downcast<RenderElement>(renderer);
250 if (is<RenderInline>(renderElement) && renderElement.isContinuation() && is<RenderInline>(renderElement.element()->renderer()))
251 return downcast<RenderInline>(renderer.node()->renderer());
252
253 // Blocks with a previous continuation always have a next continuation
254 if (is<RenderBlock>(renderElement) && downcast<RenderBlock>(renderElement).inlineContinuation())
255 return downcast<RenderInline>(downcast<RenderBlock>(renderElement).inlineContinuation()->element()->renderer());
256
257 return nullptr;
258}
259
260static inline RenderObject* endOfContinuations(RenderObject& renderer)
261{
262 if (!is<RenderInline>(renderer) && !is<RenderBlock>(renderer))
263 return &renderer;
264
265 auto* previous = &downcast<RenderBoxModelObject>(renderer);
266 for (auto* current = previous; current; ) {
267 previous = current;
268 current = current->inlineContinuation();
269 }
270
271 return previous;
272}
273
274
275static inline RenderObject* childBeforeConsideringContinuations(RenderInline* renderer, RenderObject* child)
276{
277 RenderObject* previous = nullptr;
278 for (RenderBoxModelObject* currentContainer = renderer; currentContainer; ) {
279 if (is<RenderInline>(*currentContainer)) {
280 auto* current = currentContainer->firstChild();
281 while (current) {
282 if (current == child)
283 return previous;
284 previous = current;
285 current = current->nextSibling();
286 }
287
288 currentContainer = currentContainer->continuation();
289 } else if (is<RenderBlock>(*currentContainer)) {
290 if (currentContainer == child)
291 return previous;
292
293 previous = currentContainer;
294 currentContainer = currentContainer->inlineContinuation();
295 }
296 }
297
298 ASSERT_NOT_REACHED();
299 return nullptr;
300}
301
302static inline bool firstChildIsInlineContinuation(RenderElement& renderer)
303{
304 RenderObject* child = renderer.firstChild();
305 return is<RenderInline>(child) && downcast<RenderInline>(*child).isContinuation();
306}
307
308AccessibilityObject* AccessibilityRenderObject::previousSibling() const
309{
310 if (!m_renderer)
311 return nullptr;
312
313 RenderObject* previousSibling = nullptr;
314
315 // Case 1: The node is a block and is an inline's continuation. In that case, the inline's
316 // last child is our previous sibling (or further back in the continuation chain)
317 RenderInline* startOfConts;
318 if (is<RenderBox>(*m_renderer) && (startOfConts = startOfContinuations(*m_renderer)))
319 previousSibling = childBeforeConsideringContinuations(startOfConts, renderer());
320
321 // Case 2: Anonymous block parent of the end of a continuation - skip all the way to before
322 // the parent of the start, since everything in between will be linked up via the continuation.
323 else if (m_renderer->isAnonymousBlock() && firstChildIsInlineContinuation(downcast<RenderBlock>(*m_renderer))) {
324 RenderBlock& renderBlock = downcast<RenderBlock>(*m_renderer);
325 auto* firstParent = startOfContinuations(*renderBlock.firstChild())->parent();
326 ASSERT(firstParent);
327 while (firstChildIsInlineContinuation(*firstParent))
328 firstParent = startOfContinuations(*firstParent->firstChild())->parent();
329 previousSibling = firstParent->previousSibling();
330 }
331
332 // Case 3: The node has an actual previous sibling
333 else if (RenderObject* ps = m_renderer->previousSibling())
334 previousSibling = ps;
335
336 // Case 4: This node has no previous siblings, but its parent is an inline,
337 // and is another node's inline continutation. Follow the continuation chain.
338 else if (is<RenderInline>(*m_renderer->parent()) && (startOfConts = startOfContinuations(*m_renderer->parent())))
339 previousSibling = childBeforeConsideringContinuations(startOfConts, m_renderer->parent()->firstChild());
340
341 if (!previousSibling)
342 return nullptr;
343
344 return axObjectCache()->getOrCreate(previousSibling);
345}
346
347static inline bool lastChildHasContinuation(RenderElement& renderer)
348{
349 RenderObject* child = renderer.lastChild();
350 return child && isInlineWithContinuation(*child);
351}
352
353AccessibilityObject* AccessibilityRenderObject::nextSibling() const
354{
355 if (!m_renderer)
356 return nullptr;
357
358 RenderObject* nextSibling = nullptr;
359
360 // Case 1: node is a block and has an inline continuation. Next sibling is the inline continuation's
361 // first child.
362 RenderInline* inlineContinuation;
363 if (is<RenderBlock>(*m_renderer) && (inlineContinuation = downcast<RenderBlock>(*m_renderer).inlineContinuation()))
364 nextSibling = firstChildConsideringContinuation(*inlineContinuation);
365
366 // Case 2: Anonymous block parent of the start of a continuation - skip all the way to
367 // after the parent of the end, since everything in between will be linked up via the continuation.
368 else if (m_renderer->isAnonymousBlock() && lastChildHasContinuation(downcast<RenderBlock>(*m_renderer))) {
369 RenderElement* lastParent = endOfContinuations(*downcast<RenderBlock>(*m_renderer).lastChild())->parent();
370 ASSERT(lastParent);
371 while (lastChildHasContinuation(*lastParent))
372 lastParent = endOfContinuations(*lastParent->lastChild())->parent();
373 nextSibling = lastParent->nextSibling();
374 }
375
376 // Case 3: node has an actual next sibling
377 else if (RenderObject* ns = m_renderer->nextSibling())
378 nextSibling = ns;
379
380 // Case 4: node is an inline with a continuation. Next sibling is the next sibling of the end
381 // of the continuation chain.
382 else if (isInlineWithContinuation(*m_renderer))
383 nextSibling = endOfContinuations(*m_renderer)->nextSibling();
384
385 // Case 5: node has no next sibling, and its parent is an inline with a continuation.
386 // Case 5.1: After case 4, (the element was inline w/ continuation but had no sibling), then check it's parent.
387 if (!nextSibling && isInlineWithContinuation(*m_renderer->parent())) {
388 auto& continuation = *downcast<RenderInline>(*m_renderer->parent()).continuation();
389
390 // Case 5a: continuation is a block - in this case the block itself is the next sibling.
391 if (is<RenderBlock>(continuation))
392 nextSibling = &continuation;
393 // Case 5b: continuation is an inline - in this case the inline's first child is the next sibling
394 else
395 nextSibling = firstChildConsideringContinuation(continuation);
396
397 // After case 4, there are chances that nextSibling has the same node as the current renderer,
398 // which might lead to adding the same child repeatedly.
399 if (nextSibling && nextSibling->node() == m_renderer->node()) {
400 if (AccessibilityObject* nextObj = axObjectCache()->getOrCreate(nextSibling))
401 return nextObj->nextSibling();
402 }
403 }
404
405 if (!nextSibling)
406 return nullptr;
407
408 // Make sure next sibling has the same parent.
409 AccessibilityObject* nextObj = axObjectCache()->getOrCreate(nextSibling);
410 if (nextObj && nextObj->parentObject() != this->parentObject())
411 return nullptr;
412
413 return nextObj;
414}
415
416static RenderBoxModelObject* nextContinuation(RenderObject& renderer)
417{
418 if (is<RenderInline>(renderer) && !renderer.isReplaced())
419 return downcast<RenderInline>(renderer).continuation();
420 if (is<RenderBlock>(renderer))
421 return downcast<RenderBlock>(renderer).inlineContinuation();
422 return nullptr;
423}
424
425RenderObject* AccessibilityRenderObject::renderParentObject() const
426{
427 if (!m_renderer)
428 return nullptr;
429
430 RenderElement* parent = m_renderer->parent();
431
432 // Case 1: node is a block and is an inline's continuation. Parent
433 // is the start of the continuation chain.
434 RenderInline* startOfConts = nullptr;
435 RenderObject* firstChild = nullptr;
436 if (is<RenderBlock>(*m_renderer) && (startOfConts = startOfContinuations(*m_renderer)))
437 parent = startOfConts;
438
439 // Case 2: node's parent is an inline which is some node's continuation; parent is
440 // the earliest node in the continuation chain.
441 else if (is<RenderInline>(parent) && (startOfConts = startOfContinuations(*parent)))
442 parent = startOfConts;
443
444 // Case 3: The first sibling is the beginning of a continuation chain. Find the origin of that continuation.
445 else if (parent && (firstChild = parent->firstChild()) && firstChild->node()) {
446 // Get the node's renderer and follow that continuation chain until the first child is found
447 RenderObject* nodeRenderFirstChild = firstChild->node()->renderer();
448 while (nodeRenderFirstChild != firstChild) {
449 for (RenderObject* contsTest = nodeRenderFirstChild; contsTest; contsTest = nextContinuation(*contsTest)) {
450 if (contsTest == firstChild) {
451 parent = nodeRenderFirstChild->parent();
452 break;
453 }
454 }
455 RenderObject* parentFirstChild = parent->firstChild();
456 if (firstChild == parentFirstChild)
457 break;
458 firstChild = parentFirstChild;
459 if (!firstChild->node())
460 break;
461 nodeRenderFirstChild = firstChild->node()->renderer();
462 }
463 }
464
465 return parent;
466}
467
468AccessibilityObject* AccessibilityRenderObject::parentObjectIfExists() const
469{
470 AXObjectCache* cache = axObjectCache();
471 if (!cache)
472 return nullptr;
473
474 // WebArea's parent should be the scroll view containing it.
475 if (isWebArea())
476 return cache->get(&m_renderer->view().frameView());
477
478 return cache->get(renderParentObject());
479}
480
481AccessibilityObject* AccessibilityRenderObject::parentObject() const
482{
483 if (!m_renderer)
484 return nullptr;
485
486 if (ariaRoleAttribute() == AccessibilityRole::MenuBar)
487 return axObjectCache()->getOrCreate(m_renderer->parent());
488
489 // menuButton and its corresponding menu are DOM siblings, but Accessibility needs them to be parent/child
490 if (ariaRoleAttribute() == AccessibilityRole::Menu) {
491 AccessibilityObject* parent = menuButtonForMenu();
492 if (parent)
493 return parent;
494 }
495
496 AXObjectCache* cache = axObjectCache();
497 if (!cache)
498 return nullptr;
499
500 RenderObject* parentObj = renderParentObject();
501 if (parentObj)
502 return cache->getOrCreate(parentObj);
503
504 // WebArea's parent should be the scroll view containing it.
505 if (isWebArea())
506 return cache->getOrCreate(&m_renderer->view().frameView());
507
508 return nullptr;
509}
510
511bool AccessibilityRenderObject::isAttachment() const
512{
513 RenderBoxModelObject* renderer = renderBoxModelObject();
514 if (!renderer)
515 return false;
516 // Widgets are the replaced elements that we represent to AX as attachments
517 bool isWidget = renderer->isWidget();
518
519 return isWidget && ariaRoleAttribute() == AccessibilityRole::Unknown;
520}
521
522bool AccessibilityRenderObject::isFileUploadButton() const
523{
524 if (m_renderer && is<HTMLInputElement>(m_renderer->node())) {
525 HTMLInputElement& input = downcast<HTMLInputElement>(*m_renderer->node());
526 return input.isFileUpload();
527 }
528
529 return false;
530}
531
532bool AccessibilityRenderObject::isOffScreen() const
533{
534 if (!m_renderer)
535 return true;
536
537 IntRect contentRect = snappedIntRect(m_renderer->absoluteClippedOverflowRect());
538 // FIXME: unclear if we need LegacyIOSDocumentVisibleRect.
539 IntRect viewRect = m_renderer->view().frameView().visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
540 viewRect.intersect(contentRect);
541 return viewRect.isEmpty();
542}
543
544Element* AccessibilityRenderObject::anchorElement() const
545{
546 if (!m_renderer)
547 return nullptr;
548
549 AXObjectCache* cache = axObjectCache();
550 if (!cache)
551 return nullptr;
552
553 RenderObject* currentRenderer;
554
555 // Search up the render tree for a RenderObject with a DOM node. Defer to an earlier continuation, though.
556 for (currentRenderer = renderer(); currentRenderer && !currentRenderer->node(); currentRenderer = currentRenderer->parent()) {
557 if (currentRenderer->isAnonymousBlock()) {
558 if (RenderObject* continuation = downcast<RenderBlock>(*currentRenderer).continuation())
559 return cache->getOrCreate(continuation)->anchorElement();
560 }
561 }
562
563 // bail if none found
564 if (!currentRenderer)
565 return nullptr;
566
567 // search up the DOM tree for an anchor element
568 // NOTE: this assumes that any non-image with an anchor is an HTMLAnchorElement
569 for (Node* node = currentRenderer->node(); node; node = node->parentNode()) {
570 if (is<HTMLAnchorElement>(*node) || (node->renderer() && cache->getOrCreate(node->renderer())->isLink()))
571 return downcast<Element>(node);
572 }
573
574 return nullptr;
575}
576
577String AccessibilityRenderObject::helpText() const
578{
579 if (!m_renderer)
580 return String();
581
582 const AtomicString& ariaHelp = getAttribute(aria_helpAttr);
583 if (!ariaHelp.isEmpty())
584 return ariaHelp;
585
586 String describedBy = ariaDescribedByAttribute();
587 if (!describedBy.isEmpty())
588 return describedBy;
589
590 String description = accessibilityDescription();
591 for (RenderObject* ancestor = renderer(); ancestor; ancestor = ancestor->parent()) {
592 if (is<HTMLElement>(ancestor->node())) {
593 HTMLElement& element = downcast<HTMLElement>(*ancestor->node());
594 const AtomicString& summary = element.getAttribute(summaryAttr);
595 if (!summary.isEmpty())
596 return summary;
597
598 // The title attribute should be used as help text unless it is already being used as descriptive text.
599 const AtomicString& title = element.getAttribute(titleAttr);
600 if (!title.isEmpty() && description != title)
601 return title;
602 }
603
604 // Only take help text from an ancestor element if its a group or an unknown role. If help was
605 // added to those kinds of elements, it is likely it was meant for a child element.
606 if (AccessibilityObject* axObj = axObjectCache()->getOrCreate(ancestor)) {
607 if (!axObj->isGroup() && axObj->roleValue() != AccessibilityRole::Unknown)
608 break;
609 }
610 }
611
612 return String();
613}
614
615String AccessibilityRenderObject::textUnderElement(AccessibilityTextUnderElementMode mode) const
616{
617 if (!m_renderer)
618 return String();
619
620 if (is<RenderFileUploadControl>(*m_renderer))
621 return downcast<RenderFileUploadControl>(*m_renderer).buttonValue();
622
623 // Reflect when a content author has explicitly marked a line break.
624 if (m_renderer->isBR())
625 return "\n"_s;
626
627 if (shouldGetTextFromNode(mode))
628 return AccessibilityNodeObject::textUnderElement(mode);
629
630 // We use a text iterator for text objects AND for those cases where we are
631 // explicitly asking for the full text under a given element.
632 if (is<RenderText>(*m_renderer) || mode.childrenInclusion == AccessibilityTextUnderElementMode::TextUnderElementModeIncludeAllChildren) {
633 // If possible, use a text iterator to get the text, so that whitespace
634 // is handled consistently.
635 Document* nodeDocument = nullptr;
636 RefPtr<Range> textRange;
637 if (Node* node = m_renderer->node()) {
638 nodeDocument = &node->document();
639 textRange = rangeOfContents(*node);
640 } else {
641 // For anonymous blocks, we work around not having a direct node to create a range from
642 // defining one based in the two external positions defining the boundaries of the subtree.
643 RenderObject* firstChildRenderer = m_renderer->firstChildSlow();
644 RenderObject* lastChildRenderer = m_renderer->lastChildSlow();
645 if (firstChildRenderer && firstChildRenderer->node() && lastChildRenderer && lastChildRenderer->node()) {
646 // We define the start and end positions for the range as the ones right before and after
647 // the first and the last nodes in the DOM tree that is wrapped inside the anonymous block.
648 Node* firstNodeInBlock = firstChildRenderer->node();
649 Position startPosition = positionInParentBeforeNode(firstNodeInBlock);
650 Position endPosition = positionInParentAfterNode(lastChildRenderer->node());
651
652 nodeDocument = &firstNodeInBlock->document();
653 textRange = Range::create(*nodeDocument, startPosition, endPosition);
654 }
655 }
656
657 if (nodeDocument && textRange) {
658 if (Frame* frame = nodeDocument->frame()) {
659 // catch stale WebCoreAXObject (see <rdar://problem/3960196>)
660 if (frame->document() != nodeDocument)
661 return String();
662
663 // Renders referenced by accessibility objects could get destroyed, if TextIterator ends up triggering
664 // style update/layout here. See also AXObjectCache::deferTextChangedIfNeeded().
665 ASSERT_WITH_SECURITY_IMPLICATION(!nodeDocument->childNeedsStyleRecalc());
666 ASSERT_WITH_SECURITY_IMPLICATION(!nodeDocument->view()->layoutContext().isInRenderTreeLayout());
667 return plainText(textRange.get(), textIteratorBehaviorForTextRange());
668 }
669 }
670
671 // Sometimes text fragments don't have Nodes associated with them (like when
672 // CSS content is used to insert text or when a RenderCounter is used.)
673 if (is<RenderText>(*m_renderer)) {
674 RenderText& renderTextObject = downcast<RenderText>(*m_renderer);
675 if (is<RenderTextFragment>(renderTextObject)) {
676 RenderTextFragment& renderTextFragment = downcast<RenderTextFragment>(renderTextObject);
677 // The alt attribute may be set on a text fragment through CSS, which should be honored.
678 const String& altText = renderTextFragment.altText();
679 if (!altText.isEmpty())
680 return altText;
681 return renderTextFragment.contentString();
682 }
683
684 return renderTextObject.text();
685 }
686 }
687
688 return AccessibilityNodeObject::textUnderElement(mode);
689}
690
691bool AccessibilityRenderObject::shouldGetTextFromNode(AccessibilityTextUnderElementMode mode) const
692{
693 if (!m_renderer)
694 return false;
695
696 // AccessibilityRenderObject::textUnderElement() gets the text of anonymous blocks by using
697 // the child nodes to define positions. CSS tables and their anonymous descendants lack
698 // children with nodes.
699 if (m_renderer->isAnonymous() && m_renderer->isTablePart())
700 return mode.childrenInclusion == AccessibilityTextUnderElementMode::TextUnderElementModeIncludeAllChildren;
701
702 // AccessibilityRenderObject::textUnderElement() calls rangeOfContents() to create the text
703 // range. rangeOfContents() does not include CSS-generated content.
704 if (m_renderer->isBeforeOrAfterContent())
705 return true;
706 if (Node* node = m_renderer->node()) {
707 Node* firstChild = node->pseudoAwareFirstChild();
708 Node* lastChild = node->pseudoAwareLastChild();
709 if ((firstChild && firstChild->isPseudoElement()) || (lastChild && lastChild->isPseudoElement()))
710 return true;
711 }
712
713 return false;
714}
715
716Node* AccessibilityRenderObject::node() const
717{
718 if (!m_renderer)
719 return nullptr;
720 if (m_renderer->isRenderView())
721 return &m_renderer->document();
722 return m_renderer->node();
723}
724
725String AccessibilityRenderObject::stringValue() const
726{
727 if (!m_renderer)
728 return String();
729
730 if (isPasswordField())
731 return passwordFieldValue();
732
733 RenderBoxModelObject* cssBox = renderBoxModelObject();
734
735 if (isARIAStaticText()) {
736 String staticText = text();
737 if (!staticText.length())
738 staticText = textUnderElement();
739 return staticText;
740 }
741
742 if (is<RenderText>(*m_renderer))
743 return textUnderElement();
744
745 if (is<RenderMenuList>(cssBox)) {
746 // RenderMenuList will go straight to the text() of its selected item.
747 // This has to be overridden in the case where the selected item has an ARIA label.
748 HTMLSelectElement& selectElement = downcast<HTMLSelectElement>(*m_renderer->node());
749 int selectedIndex = selectElement.selectedIndex();
750 const Vector<HTMLElement*>& listItems = selectElement.listItems();
751 if (selectedIndex >= 0 && static_cast<size_t>(selectedIndex) < listItems.size()) {
752 const AtomicString& overriddenDescription = listItems[selectedIndex]->attributeWithoutSynchronization(aria_labelAttr);
753 if (!overriddenDescription.isNull())
754 return overriddenDescription;
755 }
756 return downcast<RenderMenuList>(*m_renderer).text();
757 }
758
759 if (is<RenderListMarker>(*m_renderer))
760 return downcast<RenderListMarker>(*m_renderer).text();
761
762 if (isWebArea())
763 return String();
764
765 if (isTextControl())
766 return text();
767
768#if PLATFORM(IOS_FAMILY)
769 if (isInputTypePopupButton())
770 return textUnderElement();
771#endif
772
773 if (is<RenderFileUploadControl>(*m_renderer))
774 return downcast<RenderFileUploadControl>(*m_renderer).fileTextValue();
775
776 // FIXME: We might need to implement a value here for more types
777 // FIXME: It would be better not to advertise a value at all for the types for which we don't implement one;
778 // this would require subclassing or making accessibilityAttributeNames do something other than return a
779 // single static array.
780 return String();
781}
782
783bool AccessibilityRenderObject::canHavePlainText() const
784{
785 return isARIAStaticText() || is<RenderText>(*m_renderer) || isTextControl();
786}
787
788HTMLLabelElement* AccessibilityRenderObject::labelElementContainer() const
789{
790 if (!m_renderer)
791 return nullptr;
792
793 // the control element should not be considered part of the label
794 if (isControl())
795 return nullptr;
796
797 // find if this has a parent that is a label
798 for (Node* parentNode = m_renderer->node(); parentNode; parentNode = parentNode->parentNode()) {
799 if (is<HTMLLabelElement>(*parentNode))
800 return downcast<HTMLLabelElement>(parentNode);
801 }
802
803 return nullptr;
804}
805
806// The boundingBox for elements within the remote SVG element needs to be offset by its position
807// within the parent page, otherwise they are in relative coordinates only.
808void AccessibilityRenderObject::offsetBoundingBoxForRemoteSVGElement(LayoutRect& rect) const
809{
810 for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
811 if (parent->isAccessibilitySVGRoot()) {
812 rect.moveBy(parent->parentObject()->boundingBoxRect().location());
813 break;
814 }
815 }
816}
817
818LayoutRect AccessibilityRenderObject::boundingBoxRect() const
819{
820 RenderObject* obj = renderer();
821
822 if (!obj)
823 return LayoutRect();
824
825 if (obj->node()) // If we are a continuation, we want to make sure to use the primary renderer.
826 obj = obj->node()->renderer();
827
828 // absoluteFocusRingQuads will query the hierarchy below this element, which for large webpages can be very slow.
829 // For a web area, which will have the most elements of any element, absoluteQuads should be used.
830 // We should also use absoluteQuads for SVG elements, otherwise transforms won't be applied.
831 Vector<FloatQuad> quads;
832 bool isSVGRoot = false;
833
834 if (obj->isSVGRoot())
835 isSVGRoot = true;
836
837 if (is<RenderText>(*obj))
838 quads = downcast<RenderText>(*obj).absoluteQuadsClippedToEllipsis();
839 else if (isWebArea() || isSVGRoot)
840 obj->absoluteQuads(quads);
841 else
842 obj->absoluteFocusRingQuads(quads);
843
844 LayoutRect result = boundingBoxForQuads(obj, quads);
845
846 Document* document = this->document();
847 if (document && document->isSVGDocument())
848 offsetBoundingBoxForRemoteSVGElement(result);
849
850 // The size of the web area should be the content size, not the clipped size.
851 if (isWebArea())
852 result.setSize(obj->view().frameView().contentsSize());
853
854 return result;
855}
856
857LayoutRect AccessibilityRenderObject::checkboxOrRadioRect() const
858{
859 if (!m_renderer)
860 return LayoutRect();
861
862 HTMLLabelElement* label = labelForElement(downcast<Element>(m_renderer->node()));
863 if (!label || !label->renderer())
864 return boundingBoxRect();
865
866 LayoutRect labelRect = axObjectCache()->getOrCreate(label)->elementRect();
867 labelRect.unite(boundingBoxRect());
868 return labelRect;
869}
870
871LayoutRect AccessibilityRenderObject::elementRect() const
872{
873 // a checkbox or radio button should encompass its label
874 if (isCheckboxOrRadio())
875 return checkboxOrRadioRect();
876
877 return boundingBoxRect();
878}
879
880bool AccessibilityRenderObject::supportsPath() const
881{
882 return is<RenderSVGShape>(renderer());
883}
884
885Path AccessibilityRenderObject::elementPath() const
886{
887 if (is<RenderSVGShape>(renderer()) && downcast<RenderSVGShape>(*m_renderer).hasPath()) {
888 Path path = downcast<RenderSVGShape>(*m_renderer).path();
889
890 // The SVG path is in terms of the parent's bounding box. The path needs to be offset to frame coordinates.
891 if (auto svgRoot = ancestorsOfType<RenderSVGRoot>(*m_renderer).first()) {
892 LayoutPoint parentOffset = axObjectCache()->getOrCreate(&*svgRoot)->elementRect().location();
893 path.transform(AffineTransform().translate(parentOffset.x(), parentOffset.y()));
894 }
895
896 return path;
897 }
898
899 return Path();
900}
901
902IntPoint AccessibilityRenderObject::linkClickPoint()
903{
904 ASSERT(isLink());
905 /* A link bounding rect can contain points that are not part of the link.
906 For instance, a link that starts at the end of a line and finishes at the
907 beginning of the next line will have a bounding rect that includes the
908 entire two lines. In such a case, the middle point of the bounding rect
909 may not belong to the link element and thus may not activate the link.
910 Hence, return the middle point of the first character in the link if exists.
911 */
912 if (RefPtr<Range> range = elementRange()) {
913 VisiblePosition start = range->startPosition();
914 VisiblePosition end = nextVisiblePosition(start);
915 if (start.isNull() || !range->contains(end))
916 return AccessibilityObject::clickPoint();
917
918 RefPtr<Range> charRange = makeRange(start, end);
919 IntRect rect = boundsForRange(charRange);
920 return { rect.x() + rect.width() / 2, rect.y() + rect.height() / 2 };
921 }
922 return AccessibilityObject::clickPoint();
923}
924
925IntPoint AccessibilityRenderObject::clickPoint()
926{
927 // Headings are usually much wider than their textual content. If the mid point is used, often it can be wrong.
928 AccessibilityChildrenVector children = this->children();
929 if (isHeading() && children.size() == 1)
930 return children[0]->clickPoint();
931
932 if (isLink())
933 return linkClickPoint();
934
935 // use the default position unless this is an editable web area, in which case we use the selection bounds.
936 if (!isWebArea() || !canSetValueAttribute())
937 return AccessibilityObject::clickPoint();
938
939 VisibleSelection visSelection = selection();
940 VisiblePositionRange range = VisiblePositionRange(visSelection.visibleStart(), visSelection.visibleEnd());
941 IntRect bounds = boundsForVisiblePositionRange(range);
942 return { bounds.x() + (bounds.width() / 2), bounds.y() + (bounds.height() / 2) };
943}
944
945AccessibilityObject* AccessibilityRenderObject::internalLinkElement() const
946{
947 Element* element = anchorElement();
948 // Right now, we do not support ARIA links as internal link elements
949 if (!is<HTMLAnchorElement>(element))
950 return nullptr;
951 HTMLAnchorElement& anchor = downcast<HTMLAnchorElement>(*element);
952
953 URL linkURL = anchor.href();
954 String fragmentIdentifier = linkURL.fragmentIdentifier();
955 if (fragmentIdentifier.isEmpty())
956 return nullptr;
957
958 // check if URL is the same as current URL
959 URL documentURL = m_renderer->document().url();
960 if (!equalIgnoringFragmentIdentifier(documentURL, linkURL))
961 return nullptr;
962
963 Node* linkedNode = m_renderer->document().findAnchor(fragmentIdentifier);
964 if (!linkedNode)
965 return nullptr;
966
967 // The element we find may not be accessible, so find the first accessible object.
968 return firstAccessibleObjectFromNode(linkedNode);
969}
970
971OptionSet<SpeakAs> AccessibilityRenderObject::speakAsProperty() const
972{
973 if (!m_renderer)
974 return AccessibilityObject::speakAsProperty();
975
976 return m_renderer->style().speakAs();
977}
978
979void AccessibilityRenderObject::addRadioButtonGroupChildren(AccessibilityObject* parent, AccessibilityChildrenVector& linkedUIElements) const
980{
981 for (const auto& child : parent->children()) {
982 if (child->roleValue() == AccessibilityRole::RadioButton)
983 linkedUIElements.append(child);
984 else
985 addRadioButtonGroupChildren(child.get(), linkedUIElements);
986 }
987}
988
989void AccessibilityRenderObject::addRadioButtonGroupMembers(AccessibilityChildrenVector& linkedUIElements) const
990{
991 if (roleValue() != AccessibilityRole::RadioButton)
992 return;
993
994 Node* node = this->node();
995 if (is<HTMLInputElement>(node)) {
996 HTMLInputElement& input = downcast<HTMLInputElement>(*node);
997 for (auto& radioSibling : input.radioButtonGroup()) {
998 if (AccessibilityObject* object = axObjectCache()->getOrCreate(radioSibling))
999 linkedUIElements.append(object);
1000 }
1001 } else {
1002 // If we didn't find any radio button siblings with the traditional naming, lets search for a radio group role and find its children.
1003 for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
1004 if (parent->roleValue() == AccessibilityRole::RadioGroup)
1005 addRadioButtonGroupChildren(parent, linkedUIElements);
1006 }
1007 }
1008}
1009
1010// linked ui elements could be all the related radio buttons in a group
1011// or an internal anchor connection
1012void AccessibilityRenderObject::linkedUIElements(AccessibilityChildrenVector& linkedUIElements) const
1013{
1014 ariaFlowToElements(linkedUIElements);
1015
1016 if (isLink()) {
1017 AccessibilityObject* linkedAXElement = internalLinkElement();
1018 if (linkedAXElement)
1019 linkedUIElements.append(linkedAXElement);
1020 }
1021
1022 if (roleValue() == AccessibilityRole::RadioButton)
1023 addRadioButtonGroupMembers(linkedUIElements);
1024}
1025
1026bool AccessibilityRenderObject::hasTextAlternative() const
1027{
1028 // ARIA: section 2A, bullet #3 says if aria-labeledby or aria-label appears, it should
1029 // override the "label" element association.
1030 return ariaAccessibilityDescription().length();
1031}
1032
1033bool AccessibilityRenderObject::hasPopup() const
1034{
1035 return !equalLettersIgnoringASCIICase(hasPopupValue(), "false");
1036}
1037
1038bool AccessibilityRenderObject::supportsARIADropping() const
1039{
1040 const AtomicString& dropEffect = getAttribute(aria_dropeffectAttr);
1041 return !dropEffect.isEmpty();
1042}
1043
1044bool AccessibilityRenderObject::supportsARIADragging() const
1045{
1046 const AtomicString& grabbed = getAttribute(aria_grabbedAttr);
1047 return equalLettersIgnoringASCIICase(grabbed, "true") || equalLettersIgnoringASCIICase(grabbed, "false");
1048}
1049
1050bool AccessibilityRenderObject::isARIAGrabbed()
1051{
1052 return elementAttributeValue(aria_grabbedAttr);
1053}
1054
1055Vector<String> AccessibilityRenderObject::determineARIADropEffects()
1056{
1057 const AtomicString& dropEffects = getAttribute(aria_dropeffectAttr);
1058 if (dropEffects.isEmpty()) {
1059 return { };
1060 }
1061
1062 String dropEffectsString = dropEffects.string();
1063 dropEffectsString.replace('\n', ' ');
1064 return dropEffectsString.split(' ');
1065}
1066
1067bool AccessibilityRenderObject::exposesTitleUIElement() const
1068{
1069 if (!isControl() && !isFigureElement())
1070 return false;
1071
1072 // If this control is ignored (because it's invisible),
1073 // then the label needs to be exposed so it can be visible to accessibility.
1074 if (accessibilityIsIgnored())
1075 return true;
1076
1077 // When controls have their own descriptions, the title element should be ignored.
1078 if (hasTextAlternative())
1079 return false;
1080
1081 // When <label> element has aria-label or aria-labelledby on it, we shouldn't expose it as the
1082 // titleUIElement, otherwise its inner text will be announced by a screenreader.
1083 if (isLabelable()) {
1084 if (HTMLLabelElement* label = labelForElement(downcast<Element>(node()))) {
1085 if (!label->attributeWithoutSynchronization(aria_labelAttr).isEmpty())
1086 return false;
1087 if (AccessibilityObject* labelObject = axObjectCache()->getOrCreate(label)) {
1088 if (!labelObject->ariaLabeledByAttribute().isEmpty())
1089 return false;
1090 // To simplify instances where the labeling element includes widget descendants
1091 // which it does not label.
1092 if (is<AccessibilityLabel>(*labelObject)
1093 && downcast<AccessibilityLabel>(*labelObject).containsUnrelatedControls())
1094 return false;
1095 }
1096 }
1097 }
1098
1099 return true;
1100}
1101
1102#if ENABLE(APPLE_PAY)
1103String AccessibilityRenderObject::applePayButtonDescription() const
1104{
1105 switch (applePayButtonType()) {
1106 case ApplePayButtonType::Plain:
1107 return AXApplePayPlainLabel();
1108 case ApplePayButtonType::Buy:
1109 return AXApplePayBuyLabel();
1110 case ApplePayButtonType::SetUp:
1111 return AXApplePaySetupLabel();
1112 case ApplePayButtonType::Donate:
1113 return AXApplePayDonateLabel();
1114#if ENABLE(APPLE_PAY_SESSION_V4)
1115 case ApplePayButtonType::CheckOut:
1116 return AXApplePayCheckOutLabel();
1117 case ApplePayButtonType::Book:
1118 return AXApplePayBookLabel();
1119 case ApplePayButtonType::Subscribe:
1120 return AXApplePaySubscribeLabel();
1121#endif
1122 }
1123}
1124#endif
1125
1126void AccessibilityRenderObject::titleElementText(Vector<AccessibilityText>& textOrder) const
1127{
1128#if ENABLE(APPLE_PAY)
1129 if (isApplePayButton()) {
1130 textOrder.append(AccessibilityText(applePayButtonDescription(), AccessibilityTextSource::Alternative));
1131 return;
1132 }
1133#endif
1134
1135 AccessibilityNodeObject::titleElementText(textOrder);
1136}
1137
1138AccessibilityObject* AccessibilityRenderObject::titleUIElement() const
1139{
1140 if (!m_renderer)
1141 return nullptr;
1142
1143 // if isFieldset is true, the renderer is guaranteed to be a RenderFieldset
1144 if (isFieldset())
1145 return axObjectCache()->getOrCreate(downcast<RenderBlock>(*m_renderer).findFieldsetLegend(RenderBlock::FieldsetIncludeFloatingOrOutOfFlow));
1146
1147 if (isFigureElement())
1148 return captionForFigure();
1149
1150 Node* node = m_renderer->node();
1151 if (!is<Element>(node))
1152 return nullptr;
1153 HTMLLabelElement* label = labelForElement(downcast<Element>(node));
1154 if (label && label->renderer())
1155 return axObjectCache()->getOrCreate(label);
1156
1157 return nullptr;
1158}
1159
1160bool AccessibilityRenderObject::isAllowedChildOfTree() const
1161{
1162 // Determine if this is in a tree. If so, we apply special behavior to make it work like an AXOutline.
1163 AccessibilityObject* axObj = parentObject();
1164 bool isInTree = false;
1165 bool isTreeItemDescendant = false;
1166 while (axObj) {
1167 if (axObj->roleValue() == AccessibilityRole::TreeItem)
1168 isTreeItemDescendant = true;
1169 if (axObj->isTree()) {
1170 isInTree = true;
1171 break;
1172 }
1173 axObj = axObj->parentObject();
1174 }
1175
1176 // If the object is in a tree, only tree items should be exposed (and the children of tree items).
1177 if (isInTree) {
1178 AccessibilityRole role = roleValue();
1179 if (role != AccessibilityRole::TreeItem && role != AccessibilityRole::StaticText && !isTreeItemDescendant)
1180 return false;
1181 }
1182 return true;
1183}
1184
1185static AccessibilityObjectInclusion objectInclusionFromAltText(const String& altText)
1186{
1187 // Don't ignore an image that has an alt tag.
1188 if (!altText.isAllSpecialCharacters<isHTMLSpace>())
1189 return AccessibilityObjectInclusion::IncludeObject;
1190
1191 // The informal standard is to ignore images with zero-length alt strings.
1192 if (!altText.isNull())
1193 return AccessibilityObjectInclusion::IgnoreObject;
1194
1195 return AccessibilityObjectInclusion::DefaultBehavior;
1196}
1197
1198AccessibilityObjectInclusion AccessibilityRenderObject::defaultObjectInclusion() const
1199{
1200 // The following cases can apply to any element that's a subclass of AccessibilityRenderObject.
1201
1202 if (!m_renderer)
1203 return AccessibilityObjectInclusion::IgnoreObject;
1204
1205 if (m_renderer->style().visibility() != Visibility::Visible) {
1206 // aria-hidden is meant to override visibility as the determinant in AX hierarchy inclusion.
1207 if (equalLettersIgnoringASCIICase(getAttribute(aria_hiddenAttr), "false"))
1208 return AccessibilityObjectInclusion::DefaultBehavior;
1209
1210 return AccessibilityObjectInclusion::IgnoreObject;
1211 }
1212
1213 return AccessibilityObject::defaultObjectInclusion();
1214}
1215
1216static bool webAreaIsPresentational(RenderObject* renderer)
1217{
1218 if (!renderer || !is<RenderView>(*renderer))
1219 return false;
1220
1221 if (auto ownerElement = renderer->document().ownerElement())
1222 return nodeHasPresentationRole(ownerElement);
1223
1224 return false;
1225}
1226
1227bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const
1228{
1229#ifndef NDEBUG
1230 ASSERT(m_initialized);
1231#endif
1232
1233 if (!m_renderer)
1234 return true;
1235
1236 // Check first if any of the common reasons cause this element to be ignored.
1237 // Then process other use cases that need to be applied to all the various roles
1238 // that AccessibilityRenderObjects take on.
1239 AccessibilityObjectInclusion decision = defaultObjectInclusion();
1240 if (decision == AccessibilityObjectInclusion::IncludeObject)
1241 return false;
1242 if (decision == AccessibilityObjectInclusion::IgnoreObject)
1243 return true;
1244
1245 // If this element is within a parent that cannot have children, it should not be exposed.
1246 if (isDescendantOfBarrenParent())
1247 return true;
1248
1249 if (roleValue() == AccessibilityRole::Ignored)
1250 return true;
1251
1252 if (roleValue() == AccessibilityRole::Presentational || inheritsPresentationalRole())
1253 return true;
1254
1255 // WebAreas should be ignored if their iframe container is marked as presentational.
1256 if (webAreaIsPresentational(renderer()))
1257 return true;
1258
1259 // An ARIA tree can only have tree items and static text as children.
1260 if (!isAllowedChildOfTree())
1261 return true;
1262
1263 // Allow the platform to decide if the attachment is ignored or not.
1264 if (isAttachment())
1265 return accessibilityIgnoreAttachment();
1266
1267 // ignore popup menu items because AppKit does
1268 if (m_renderer && ancestorsOfType<RenderMenuList>(*m_renderer).first())
1269 return true;
1270
1271 // https://webkit.org/b/161276 Getting the controlObject might cause the m_renderer to be nullptr.
1272 if (!m_renderer)
1273 return true;
1274
1275 if (m_renderer->isBR())
1276 return true;
1277
1278 if (is<RenderText>(*m_renderer)) {
1279 // static text beneath MenuItems and MenuButtons are just reported along with the menu item, so it's ignored on an individual level
1280 AccessibilityObject* parent = parentObjectUnignored();
1281 if (parent && (parent->isMenuItem() || parent->ariaRoleAttribute() == AccessibilityRole::MenuButton))
1282 return true;
1283 auto& renderText = downcast<RenderText>(*m_renderer);
1284 if (!renderText.hasRenderedText())
1285 return true;
1286
1287 if (renderText.parent()->isFirstLetter())
1288 return true;
1289
1290 // static text beneath TextControls is reported along with the text control text so it's ignored.
1291 for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
1292 if (parent->roleValue() == AccessibilityRole::TextField)
1293 return true;
1294 }
1295
1296 // Walking up the parent chain might reset the m_renderer.
1297 if (!m_renderer)
1298 return true;
1299
1300 // The alt attribute may be set on a text fragment through CSS, which should be honored.
1301 if (is<RenderTextFragment>(renderText)) {
1302 AccessibilityObjectInclusion altTextInclusion = objectInclusionFromAltText(downcast<RenderTextFragment>(renderText).altText());
1303 if (altTextInclusion == AccessibilityObjectInclusion::IgnoreObject)
1304 return true;
1305 if (altTextInclusion == AccessibilityObjectInclusion::IncludeObject)
1306 return false;
1307 }
1308
1309 // text elements that are just empty whitespace should not be returned
1310 return renderText.text().isAllSpecialCharacters<isHTMLSpace>();
1311 }
1312
1313 if (isHeading())
1314 return false;
1315
1316 if (isLink())
1317 return false;
1318
1319 if (isLandmark())
1320 return false;
1321
1322 // all controls are accessible
1323 if (isControl())
1324 return false;
1325
1326 if (isFigureElement())
1327 return false;
1328
1329 switch (roleValue()) {
1330 case AccessibilityRole::Audio:
1331 case AccessibilityRole::DescriptionListTerm:
1332 case AccessibilityRole::DescriptionListDetail:
1333 case AccessibilityRole::Details:
1334 case AccessibilityRole::DocumentArticle:
1335 case AccessibilityRole::Footer:
1336 case AccessibilityRole::LandmarkRegion:
1337 case AccessibilityRole::ListItem:
1338 case AccessibilityRole::Time:
1339 case AccessibilityRole::Video:
1340 return false;
1341 default:
1342 break;
1343 }
1344
1345 if (ariaRoleAttribute() != AccessibilityRole::Unknown)
1346 return false;
1347
1348 if (roleValue() == AccessibilityRole::HorizontalRule)
1349 return false;
1350
1351 // don't ignore labels, because they serve as TitleUIElements
1352 Node* node = m_renderer->node();
1353 if (is<HTMLLabelElement>(node))
1354 return false;
1355
1356 // Anything that is content editable should not be ignored.
1357 // However, one cannot just call node->hasEditableStyle() since that will ask if its parents
1358 // are also editable. Only the top level content editable region should be exposed.
1359 if (hasContentEditableAttributeSet())
1360 return false;
1361
1362
1363 // if this element has aria attributes on it, it should not be ignored.
1364 if (supportsARIAAttributes())
1365 return false;
1366
1367#if ENABLE(MATHML)
1368 // First check if this is a special case within the math tree that needs to be ignored.
1369 if (isIgnoredElementWithinMathTree())
1370 return true;
1371 // Otherwise all other math elements are in the tree.
1372 if (isMathElement())
1373 return false;
1374#endif
1375
1376 if (is<RenderBlockFlow>(*m_renderer) && m_renderer->childrenInline() && !canSetFocusAttribute())
1377 return !downcast<RenderBlockFlow>(*m_renderer).hasLines() && !mouseButtonListener();
1378
1379 // ignore images seemingly used as spacers
1380 if (isImage()) {
1381
1382 // If the image can take focus, it should not be ignored, lest the user not be able to interact with something important.
1383 if (canSetFocusAttribute())
1384 return false;
1385
1386 // First check the RenderImage's altText (which can be set through a style sheet, or come from the Element).
1387 // However, if this is not a native image, fallback to the attribute on the Element.
1388 AccessibilityObjectInclusion altTextInclusion = AccessibilityObjectInclusion::DefaultBehavior;
1389 bool isRenderImage = is<RenderImage>(renderer());
1390 if (isRenderImage)
1391 altTextInclusion = objectInclusionFromAltText(downcast<RenderImage>(*m_renderer).altText());
1392 else
1393 altTextInclusion = objectInclusionFromAltText(getAttribute(altAttr).string());
1394
1395 if (altTextInclusion == AccessibilityObjectInclusion::IgnoreObject)
1396 return true;
1397 if (altTextInclusion == AccessibilityObjectInclusion::IncludeObject)
1398 return false;
1399
1400 // If an image has a title attribute on it, accessibility should be lenient and allow it to appear in the hierarchy (according to WAI-ARIA).
1401 if (!getAttribute(titleAttr).isEmpty())
1402 return false;
1403
1404 if (isRenderImage) {
1405 // check for one-dimensional image
1406 RenderImage& image = downcast<RenderImage>(*m_renderer);
1407 if (image.height() <= 1 || image.width() <= 1)
1408 return true;
1409
1410 // check whether rendered image was stretched from one-dimensional file image
1411 if (image.cachedImage()) {
1412 LayoutSize imageSize = image.cachedImage()->imageSizeForRenderer(&image, image.view().zoomFactor());
1413 return imageSize.height() <= 1 || imageSize.width() <= 1;
1414 }
1415 }
1416 return false;
1417 }
1418
1419 if (isCanvas()) {
1420 if (canvasHasFallbackContent())
1421 return false;
1422
1423 if (is<RenderBox>(*m_renderer)) {
1424 auto& canvasBox = downcast<RenderBox>(*m_renderer);
1425 if (canvasBox.height() <= 1 || canvasBox.width() <= 1)
1426 return true;
1427 }
1428 // Otherwise fall through; use presence of help text, title, or description to decide.
1429 }
1430
1431 if (m_renderer->isListMarker()) {
1432 AccessibilityObject* parent = parentObjectUnignored();
1433 return parent && !parent->isListItem();
1434 }
1435
1436 if (isWebArea())
1437 return false;
1438
1439#if ENABLE(METER_ELEMENT)
1440 // The render tree of meter includes a RenderBlock (meter) and a RenderMeter (div).
1441 // We expose the latter and thus should ignore the former. However, if the author
1442 // includes a title attribute on the element, hasAttributesRequiredForInclusion()
1443 // will return true, potentially resulting in a redundant accessible object.
1444 if (is<HTMLMeterElement>(node))
1445 return true;
1446#endif
1447
1448 // Using the presence of an accessible name to decide an element's visibility is not
1449 // as definitive as previous checks, so this should remain as one of the last.
1450 if (hasAttributesRequiredForInclusion())
1451 return false;
1452
1453 // Don't ignore generic focusable elements like <div tabindex=0>
1454 // unless they're completely empty, with no children.
1455 if (isGenericFocusableElement() && node->firstChild())
1456 return false;
1457
1458 // <span> tags are inline tags and not meant to convey information if they have no other aria
1459 // information on them. If we don't ignore them, they may emit signals expected to come from
1460 // their parent. In addition, because included spans are AccessibilityRole::Group objects, and AccessibilityRole::Group
1461 // objects are often containers with meaningful information, the inclusion of a span can have
1462 // the side effect of causing the immediate parent accessible to be ignored. This is especially
1463 // problematic for platforms which have distinct roles for textual block elements.
1464 if (node && node->hasTagName(spanTag))
1465 return true;
1466
1467 // Other non-ignored host language elements
1468 if (node && node->hasTagName(dfnTag))
1469 return false;
1470
1471 if (isStyleFormatGroup())
1472 return false;
1473
1474 // Make sure that ruby containers are not ignored.
1475 if (m_renderer->isRubyRun() || m_renderer->isRubyBlock() || m_renderer->isRubyInline())
1476 return false;
1477
1478 // Find out if this element is inside of a label element.
1479 // If so, it may be ignored because it's the label for a checkbox or radio button.
1480 AccessibilityObject* controlObject = correspondingControlForLabelElement();
1481 if (controlObject && !controlObject->exposesTitleUIElement() && controlObject->isCheckboxOrRadio())
1482 return true;
1483
1484 // By default, objects should be ignored so that the AX hierarchy is not
1485 // filled with unnecessary items.
1486 return true;
1487}
1488
1489bool AccessibilityRenderObject::isLoaded() const
1490{
1491 return !m_renderer->document().parser();
1492}
1493
1494double AccessibilityRenderObject::estimatedLoadingProgress() const
1495{
1496 if (!m_renderer)
1497 return 0;
1498
1499 if (isLoaded())
1500 return 1.0;
1501
1502 return m_renderer->page().progress().estimatedProgress();
1503}
1504
1505int AccessibilityRenderObject::layoutCount() const
1506{
1507 if (!m_renderer || !is<RenderView>(*m_renderer))
1508 return 0;
1509 return downcast<RenderView>(*m_renderer).frameView().layoutContext().layoutCount();
1510}
1511
1512String AccessibilityRenderObject::text() const
1513{
1514 if (isPasswordField())
1515 return passwordFieldValue();
1516
1517 return AccessibilityNodeObject::text();
1518}
1519
1520int AccessibilityRenderObject::textLength() const
1521{
1522 ASSERT(isTextControl());
1523
1524 if (isPasswordField())
1525 return passwordFieldValue().length();
1526
1527 return text().length();
1528}
1529
1530PlainTextRange AccessibilityRenderObject::documentBasedSelectedTextRange() const
1531{
1532 Node* node = m_renderer->node();
1533 if (!node)
1534 return PlainTextRange();
1535
1536 VisibleSelection visibleSelection = selection();
1537 RefPtr<Range> currentSelectionRange = visibleSelection.toNormalizedRange();
1538 if (!currentSelectionRange)
1539 return PlainTextRange();
1540 // FIXME: The reason this does the correct thing when the selection is in the
1541 // shadow tree of an input element is that we get an exception below, and we
1542 // choose to interpret all exceptions as "does not intersect". Seems likely
1543 // that does not handle all cases correctly.
1544 auto intersectsResult = currentSelectionRange->intersectsNode(*node);
1545 if (!intersectsResult.hasException() && !intersectsResult.releaseReturnValue())
1546 return PlainTextRange();
1547
1548 int start = indexForVisiblePosition(visibleSelection.start());
1549 int end = indexForVisiblePosition(visibleSelection.end());
1550
1551 return PlainTextRange(start, end - start);
1552}
1553
1554String AccessibilityRenderObject::selectedText() const
1555{
1556 ASSERT(isTextControl());
1557
1558 if (isPasswordField())
1559 return String(); // need to return something distinct from empty string
1560
1561 if (isNativeTextControl()) {
1562 HTMLTextFormControlElement& textControl = downcast<RenderTextControl>(*m_renderer).textFormControlElement();
1563 return textControl.selectedText();
1564 }
1565
1566 return doAXStringForRange(documentBasedSelectedTextRange());
1567}
1568
1569const AtomicString& AccessibilityRenderObject::accessKey() const
1570{
1571 Node* node = m_renderer->node();
1572 if (!is<Element>(node))
1573 return nullAtom();
1574 return downcast<Element>(*node).attributeWithoutSynchronization(accesskeyAttr);
1575}
1576
1577VisibleSelection AccessibilityRenderObject::selection() const
1578{
1579 return m_renderer->frame().selection().selection();
1580}
1581
1582PlainTextRange AccessibilityRenderObject::selectedTextRange() const
1583{
1584 ASSERT(isTextControl());
1585
1586 if (isPasswordField())
1587 return PlainTextRange();
1588
1589 AccessibilityRole ariaRole = ariaRoleAttribute();
1590 // Use the text control native range if it's a native object and it has no ARIA role (or has a text based ARIA role).
1591 if (isNativeTextControl() && (ariaRole == AccessibilityRole::Unknown || isARIATextControl())) {
1592 HTMLTextFormControlElement& textControl = downcast<RenderTextControl>(*m_renderer).textFormControlElement();
1593 return PlainTextRange(textControl.selectionStart(), textControl.selectionEnd() - textControl.selectionStart());
1594 }
1595
1596 return documentBasedSelectedTextRange();
1597}
1598
1599static void setTextSelectionIntent(AXObjectCache* cache, AXTextStateChangeType type)
1600{
1601 if (!cache)
1602 return;
1603 AXTextStateChangeIntent intent(type, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown, false });
1604 cache->setTextSelectionIntent(intent);
1605 cache->setIsSynchronizingSelection(true);
1606}
1607
1608static void clearTextSelectionIntent(AXObjectCache* cache)
1609{
1610 if (!cache)
1611 return;
1612 cache->setTextSelectionIntent(AXTextStateChangeIntent());
1613 cache->setIsSynchronizingSelection(false);
1614}
1615
1616void AccessibilityRenderObject::setSelectedTextRange(const PlainTextRange& range)
1617{
1618 setTextSelectionIntent(axObjectCache(), range.length ? AXTextStateChangeTypeSelectionExtend : AXTextStateChangeTypeSelectionMove);
1619
1620 if (isNativeTextControl()) {
1621 HTMLTextFormControlElement& textControl = downcast<RenderTextControl>(*m_renderer).textFormControlElement();
1622 textControl.setSelectionRange(range.start, range.start + range.length);
1623 } else {
1624 ASSERT(node());
1625 VisiblePosition start = visiblePositionForIndexUsingCharacterIterator(*node(), range.start);
1626 VisiblePosition end = visiblePositionForIndexUsingCharacterIterator(*node(), range.start + range.length);
1627 m_renderer->frame().selection().setSelection(VisibleSelection(start, end), FrameSelection::defaultSetSelectionOptions(UserTriggered));
1628 }
1629
1630 clearTextSelectionIntent(axObjectCache());
1631}
1632
1633URL AccessibilityRenderObject::url() const
1634{
1635 if (isLink() && is<HTMLAnchorElement>(*m_renderer->node())) {
1636 if (HTMLAnchorElement* anchor = downcast<HTMLAnchorElement>(anchorElement()))
1637 return anchor->href();
1638 }
1639
1640 if (isWebArea())
1641 return m_renderer->document().url();
1642
1643 if (isImage() && is<HTMLImageElement>(m_renderer->node()))
1644 return downcast<HTMLImageElement>(*m_renderer->node()).src();
1645
1646 if (isInputImage())
1647 return downcast<HTMLInputElement>(*m_renderer->node()).src();
1648
1649 return URL();
1650}
1651
1652bool AccessibilityRenderObject::isUnvisited() const
1653{
1654 if (!m_renderer)
1655 return true;
1656
1657 // FIXME: Is it a privacy violation to expose unvisited information to accessibility APIs?
1658 return m_renderer->style().isLink() && m_renderer->style().insideLink() == InsideLink::InsideUnvisited;
1659}
1660
1661bool AccessibilityRenderObject::isVisited() const
1662{
1663 if (!m_renderer)
1664 return false;
1665
1666 // FIXME: Is it a privacy violation to expose visited information to accessibility APIs?
1667 return m_renderer->style().isLink() && m_renderer->style().insideLink() == InsideLink::InsideVisited;
1668}
1669
1670void AccessibilityRenderObject::setElementAttributeValue(const QualifiedName& attributeName, bool value)
1671{
1672 if (!m_renderer)
1673 return;
1674
1675 Node* node = m_renderer->node();
1676 if (!is<Element>(node))
1677 return;
1678
1679 downcast<Element>(*node).setAttribute(attributeName, (value) ? "true" : "false");
1680}
1681
1682bool AccessibilityRenderObject::elementAttributeValue(const QualifiedName& attributeName) const
1683{
1684 if (!m_renderer)
1685 return false;
1686
1687 return equalLettersIgnoringASCIICase(getAttribute(attributeName), "true");
1688}
1689
1690bool AccessibilityRenderObject::isSelected() const
1691{
1692 if (!m_renderer)
1693 return false;
1694
1695 if (!m_renderer->node())
1696 return false;
1697
1698 if (equalLettersIgnoringASCIICase(getAttribute(aria_selectedAttr), "true"))
1699 return true;
1700
1701 if (isTabItem() && isTabItemSelected())
1702 return true;
1703
1704 // Menu items are considered selectable by assistive technologies
1705 if (isMenuItem())
1706 return isFocused() || parentObjectUnignored()->activeDescendant() == this;
1707
1708 return false;
1709}
1710
1711bool AccessibilityRenderObject::isTabItemSelected() const
1712{
1713 if (!isTabItem() || !m_renderer)
1714 return false;
1715
1716 Node* node = m_renderer->node();
1717 if (!node || !node->isElementNode())
1718 return false;
1719
1720 // The ARIA spec says a tab item can also be selected if it is aria-labeled by a tabpanel
1721 // that has keyboard focus inside of it, or if a tabpanel in its aria-controls list has KB
1722 // focus inside of it.
1723 AccessibilityObject* focusedElement = static_cast<AccessibilityObject*>(focusedUIElement());
1724 if (!focusedElement)
1725 return false;
1726
1727 Vector<Element*> elements;
1728 elementsFromAttribute(elements, aria_controlsAttr);
1729
1730 AXObjectCache* cache = axObjectCache();
1731 if (!cache)
1732 return false;
1733
1734 for (const auto& element : elements) {
1735 AccessibilityObject* tabPanel = cache->getOrCreate(element);
1736
1737 // A tab item should only control tab panels.
1738 if (!tabPanel || tabPanel->roleValue() != AccessibilityRole::TabPanel)
1739 continue;
1740
1741 AccessibilityObject* checkFocusElement = focusedElement;
1742 // Check if the focused element is a descendant of the element controlled by the tab item.
1743 while (checkFocusElement) {
1744 if (tabPanel == checkFocusElement)
1745 return true;
1746 checkFocusElement = checkFocusElement->parentObject();
1747 }
1748 }
1749
1750 return false;
1751}
1752
1753bool AccessibilityRenderObject::isFocused() const
1754{
1755 if (!m_renderer)
1756 return false;
1757
1758 Document& document = m_renderer->document();
1759
1760 Element* focusedElement = document.focusedElement();
1761 if (!focusedElement)
1762 return false;
1763
1764 // A web area is represented by the Document node in the DOM tree, which isn't focusable.
1765 // Check instead if the frame's selection controller is focused
1766 if (focusedElement == m_renderer->node()
1767 || (roleValue() == AccessibilityRole::WebArea && document.frame()->selection().isFocusedAndActive()))
1768 return true;
1769
1770 return false;
1771}
1772
1773void AccessibilityRenderObject::setFocused(bool on)
1774{
1775 if (!canSetFocusAttribute())
1776 return;
1777
1778 Document* document = this->document();
1779 Node* node = this->node();
1780
1781 if (!on || !is<Element>(node)) {
1782 document->setFocusedElement(nullptr);
1783 return;
1784 }
1785
1786 // When a node is told to set focus, that can cause it to be deallocated, which means that doing
1787 // anything else inside this object will crash. To fix this, we added a RefPtr to protect this object
1788 // long enough for duration.
1789 RefPtr<AccessibilityObject> protectedThis(this);
1790
1791 // If this node is already the currently focused node, then calling focus() won't do anything.
1792 // That is a problem when focus is removed from the webpage to chrome, and then returns.
1793 // In these cases, we need to do what keyboard and mouse focus do, which is reset focus first.
1794 if (document->focusedElement() == node)
1795 document->setFocusedElement(nullptr);
1796
1797 // If we return from setFocusedElement and our element has been removed from a tree, axObjectCache() may be null.
1798 if (AXObjectCache* cache = axObjectCache()) {
1799 cache->setIsSynchronizingSelection(true);
1800 downcast<Element>(*node).focus();
1801 cache->setIsSynchronizingSelection(false);
1802 }
1803}
1804
1805void AccessibilityRenderObject::setSelectedRows(AccessibilityChildrenVector& selectedRows)
1806{
1807 // Setting selected only makes sense in trees and tables (and tree-tables).
1808 AccessibilityRole role = roleValue();
1809 if (role != AccessibilityRole::Tree && role != AccessibilityRole::TreeGrid && role != AccessibilityRole::Table && role != AccessibilityRole::Grid)
1810 return;
1811
1812 bool isMulti = isMultiSelectable();
1813 unsigned count = selectedRows.size();
1814 if (count > 1 && !isMulti)
1815 count = 1;
1816
1817 for (const auto& selectedRow : selectedRows)
1818 selectedRow->setSelected(true);
1819}
1820
1821void AccessibilityRenderObject::setValue(const String& string)
1822{
1823 if (!m_renderer || !is<Element>(m_renderer->node()))
1824 return;
1825
1826 Element& element = downcast<Element>(*m_renderer->node());
1827 RenderObject& renderer = *m_renderer;
1828
1829 // We should use the editor's insertText to mimic typing into the field.
1830 // Also only do this when the field is in editing mode.
1831 if (Frame* frame = renderer.document().frame()) {
1832 Editor& editor = frame->editor();
1833 if (element.shouldUseInputMethod()) {
1834 editor.clearText();
1835 editor.insertText(string, nullptr);
1836 return;
1837 }
1838 }
1839 // FIXME: Do we want to do anything here for ARIA textboxes?
1840 if (renderer.isTextField() && is<HTMLInputElement>(element))
1841 downcast<HTMLInputElement>(element).setValue(string);
1842 else if (renderer.isTextArea() && is<HTMLTextAreaElement>(element))
1843 downcast<HTMLTextAreaElement>(element).setValue(string);
1844}
1845
1846bool AccessibilityRenderObject::supportsARIAOwns() const
1847{
1848 if (!m_renderer)
1849 return false;
1850 const AtomicString& ariaOwns = getAttribute(aria_ownsAttr);
1851
1852 return !ariaOwns.isEmpty();
1853}
1854
1855RenderView* AccessibilityRenderObject::topRenderer() const
1856{
1857 Document* topDoc = topDocument();
1858 if (!topDoc)
1859 return nullptr;
1860
1861 return topDoc->renderView();
1862}
1863
1864Document* AccessibilityRenderObject::document() const
1865{
1866 if (!m_renderer)
1867 return nullptr;
1868 return &m_renderer->document();
1869}
1870
1871Widget* AccessibilityRenderObject::widget() const
1872{
1873 if (!m_renderer || !is<RenderWidget>(*m_renderer))
1874 return nullptr;
1875 return downcast<RenderWidget>(*m_renderer).widget();
1876}
1877
1878AccessibilityObject* AccessibilityRenderObject::accessibilityParentForImageMap(HTMLMapElement* map) const
1879{
1880 // find an image that is using this map
1881 if (!map)
1882 return nullptr;
1883
1884 HTMLImageElement* imageElement = map->imageElement();
1885 if (!imageElement)
1886 return nullptr;
1887
1888 if (AXObjectCache* cache = axObjectCache())
1889 return cache->getOrCreate(imageElement);
1890
1891 return nullptr;
1892}
1893
1894void AccessibilityRenderObject::getDocumentLinks(AccessibilityChildrenVector& result)
1895{
1896 Document& document = m_renderer->document();
1897 Ref<HTMLCollection> links = document.links();
1898 for (unsigned i = 0; auto* current = links->item(i); ++i) {
1899 if (auto* renderer = current->renderer()) {
1900 RefPtr<AccessibilityObject> axObject = document.axObjectCache()->getOrCreate(renderer);
1901 ASSERT(axObject);
1902 if (!axObject->accessibilityIsIgnored() && axObject->isLink())
1903 result.append(axObject);
1904 } else {
1905 auto* parent = current->parentNode();
1906 if (is<HTMLAreaElement>(*current) && is<HTMLMapElement>(parent)) {
1907 auto& areaObject = downcast<AccessibilityImageMapLink>(*axObjectCache()->getOrCreate(AccessibilityRole::ImageMapLink));
1908 HTMLMapElement& map = downcast<HTMLMapElement>(*parent);
1909 areaObject.setHTMLAreaElement(downcast<HTMLAreaElement>(current));
1910 areaObject.setHTMLMapElement(&map);
1911 areaObject.setParent(accessibilityParentForImageMap(&map));
1912
1913 result.append(&areaObject);
1914 }
1915 }
1916 }
1917}
1918
1919FrameView* AccessibilityRenderObject::documentFrameView() const
1920{
1921 if (!m_renderer)
1922 return nullptr;
1923
1924 // this is the RenderObject's Document's Frame's FrameView
1925 return &m_renderer->view().frameView();
1926}
1927
1928Widget* AccessibilityRenderObject::widgetForAttachmentView() const
1929{
1930 if (!isAttachment())
1931 return nullptr;
1932 return downcast<RenderWidget>(*m_renderer).widget();
1933}
1934
1935// This function is like a cross-platform version of - (WebCoreTextMarkerRange*)textMarkerRange. It returns
1936// a Range that we can convert to a WebCoreTextMarkerRange in the Obj-C file
1937VisiblePositionRange AccessibilityRenderObject::visiblePositionRange() const
1938{
1939 if (!m_renderer)
1940 return VisiblePositionRange();
1941
1942 // construct VisiblePositions for start and end
1943 Node* node = m_renderer->node();
1944 if (!node)
1945 return VisiblePositionRange();
1946
1947 VisiblePosition startPos = firstPositionInOrBeforeNode(node);
1948 VisiblePosition endPos = lastPositionInOrAfterNode(node);
1949
1950 // the VisiblePositions are equal for nodes like buttons, so adjust for that
1951 // FIXME: Really? [button, 0] and [button, 1] are distinct (before and after the button)
1952 // I expect this code is only hit for things like empty divs? In which case I don't think
1953 // the behavior is correct here -- eseidel
1954 if (startPos == endPos) {
1955 endPos = endPos.next();
1956 if (endPos.isNull())
1957 endPos = startPos;
1958 }
1959
1960 return VisiblePositionRange(startPos, endPos);
1961}
1962
1963VisiblePositionRange AccessibilityRenderObject::visiblePositionRangeForLine(unsigned lineCount) const
1964{
1965 if (!lineCount || !m_renderer)
1966 return VisiblePositionRange();
1967
1968 // iterate over the lines
1969 // FIXME: this is wrong when lineNumber is lineCount+1, because nextLinePosition takes you to the
1970 // last offset of the last line
1971 VisiblePosition visiblePos = m_renderer->view().positionForPoint(IntPoint(), nullptr);
1972 VisiblePosition savedVisiblePos;
1973 while (--lineCount) {
1974 savedVisiblePos = visiblePos;
1975 visiblePos = nextLinePosition(visiblePos, 0);
1976 if (visiblePos.isNull() || visiblePos == savedVisiblePos)
1977 return VisiblePositionRange();
1978 }
1979
1980 // make a caret selection for the marker position, then extend it to the line
1981 // NOTE: ignores results of sel.modify because it returns false when
1982 // starting at an empty line. The resulting selection in that case
1983 // will be a caret at visiblePos.
1984 FrameSelection selection;
1985 selection.setSelection(VisibleSelection(visiblePos));
1986 selection.modify(FrameSelection::AlterationExtend, DirectionRight, LineBoundary);
1987
1988 return VisiblePositionRange(selection.selection().visibleStart(), selection.selection().visibleEnd());
1989}
1990
1991VisiblePosition AccessibilityRenderObject::visiblePositionForIndex(int index) const
1992{
1993 if (!m_renderer)
1994 return VisiblePosition();
1995
1996 if (isNativeTextControl())
1997 return downcast<RenderTextControl>(*m_renderer).textFormControlElement().visiblePositionForIndex(index);
1998
1999 if (!allowsTextRanges() && !is<RenderText>(*m_renderer))
2000 return VisiblePosition();
2001
2002 Node* node = m_renderer->node();
2003 if (!node)
2004 return VisiblePosition();
2005
2006 return visiblePositionForIndexUsingCharacterIterator(*node, index);
2007}
2008
2009int AccessibilityRenderObject::indexForVisiblePosition(const VisiblePosition& position) const
2010{
2011 if (isNativeTextControl())
2012 return downcast<RenderTextControl>(*m_renderer).textFormControlElement().indexForVisiblePosition(position);
2013
2014 if (!isTextControl())
2015 return 0;
2016
2017 Node* node = m_renderer->node();
2018 if (!node)
2019 return 0;
2020
2021 Position indexPosition = position.deepEquivalent();
2022 if (indexPosition.isNull() || highestEditableRoot(indexPosition, HasEditableAXRole) != node)
2023 return 0;
2024
2025#if PLATFORM(GTK)
2026 // We need to consider replaced elements for GTK, as they will be
2027 // presented with the 'object replacement character' (0xFFFC).
2028 bool forSelectionPreservation = true;
2029#else
2030 bool forSelectionPreservation = false;
2031#endif
2032
2033 return WebCore::indexForVisiblePosition(*node, position, forSelectionPreservation);
2034}
2035
2036Element* AccessibilityRenderObject::rootEditableElementForPosition(const Position& position) const
2037{
2038 // Find the root editable or pseudo-editable (i.e. having an editable ARIA role) element.
2039 Element* result = nullptr;
2040
2041 Element* rootEditableElement = position.rootEditableElement();
2042
2043 for (Element* e = position.element(); e && e != rootEditableElement; e = e->parentElement()) {
2044 if (nodeIsTextControl(e))
2045 result = e;
2046 if (e->hasTagName(bodyTag))
2047 break;
2048 }
2049
2050 if (result)
2051 return result;
2052
2053 return rootEditableElement;
2054}
2055
2056bool AccessibilityRenderObject::nodeIsTextControl(const Node* node) const
2057{
2058 if (!node)
2059 return false;
2060
2061 if (AXObjectCache* cache = axObjectCache()) {
2062 if (AccessibilityObject* axObjectForNode = cache->getOrCreate(const_cast<Node*>(node)))
2063 return axObjectForNode->isTextControl();
2064 }
2065
2066 return false;
2067}
2068
2069IntRect AccessibilityRenderObject::boundsForRects(LayoutRect const& rect1, LayoutRect const& rect2, RefPtr<Range> const& dataRange)
2070{
2071 LayoutRect ourRect = rect1;
2072 ourRect.unite(rect2);
2073
2074 // if the rectangle spans lines and contains multiple text chars, use the range's bounding box intead
2075 if (rect1.maxY() != rect2.maxY()) {
2076 LayoutRect boundingBox = dataRange->absoluteBoundingBox();
2077 String rangeString = plainText(dataRange.get());
2078 if (rangeString.length() > 1 && !boundingBox.isEmpty())
2079 ourRect = boundingBox;
2080 }
2081
2082 return snappedIntRect(ourRect);
2083}
2084
2085IntRect AccessibilityRenderObject::boundsForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
2086{
2087 if (visiblePositionRange.isNull())
2088 return IntRect();
2089
2090 // Create a mutable VisiblePositionRange.
2091 VisiblePositionRange range(visiblePositionRange);
2092 LayoutRect rect1 = range.start.absoluteCaretBounds();
2093 LayoutRect rect2 = range.end.absoluteCaretBounds();
2094
2095 // 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
2096 if (rect2.y() != rect1.y()) {
2097 VisiblePosition endOfFirstLine = endOfLine(range.start);
2098 if (range.start == endOfFirstLine) {
2099 range.start.setAffinity(DOWNSTREAM);
2100 rect1 = range.start.absoluteCaretBounds();
2101 }
2102 if (range.end == endOfFirstLine) {
2103 range.end.setAffinity(UPSTREAM);
2104 rect2 = range.end.absoluteCaretBounds();
2105 }
2106 }
2107
2108 RefPtr<Range> dataRange = makeRange(range.start, range.end);
2109 return boundsForRects(rect1, rect2, dataRange);
2110}
2111
2112IntRect AccessibilityRenderObject::boundsForRange(const RefPtr<Range> range) const
2113{
2114 if (!range)
2115 return IntRect();
2116
2117 AXObjectCache* cache = this->axObjectCache();
2118 if (!cache)
2119 return IntRect();
2120
2121 CharacterOffset start = cache->startOrEndCharacterOffsetForRange(range, true);
2122 CharacterOffset end = cache->startOrEndCharacterOffsetForRange(range, false);
2123
2124 LayoutRect rect1 = cache->absoluteCaretBoundsForCharacterOffset(start);
2125 LayoutRect rect2 = cache->absoluteCaretBoundsForCharacterOffset(end);
2126
2127 // 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.
2128 if (rect2.y() != rect1.y()) {
2129 CharacterOffset endOfFirstLine = cache->endCharacterOffsetOfLine(start);
2130 if (start.isEqual(endOfFirstLine)) {
2131 start = cache->nextCharacterOffset(start, false);
2132 rect1 = cache->absoluteCaretBoundsForCharacterOffset(start);
2133 }
2134 if (end.isEqual(endOfFirstLine)) {
2135 end = cache->previousCharacterOffset(end, false);
2136 rect2 = cache->absoluteCaretBoundsForCharacterOffset(end);
2137 }
2138 }
2139
2140 return boundsForRects(rect1, rect2, range);
2141}
2142
2143bool AccessibilityRenderObject::isVisiblePositionRangeInDifferentDocument(const VisiblePositionRange& range) const
2144{
2145 if (range.start.isNull() || range.end.isNull())
2146 return false;
2147
2148 VisibleSelection newSelection = VisibleSelection(range.start, range.end);
2149 if (Document* newSelectionDocument = newSelection.base().document()) {
2150 if (RefPtr<Frame> newSelectionFrame = newSelectionDocument->frame()) {
2151 Frame* frame = this->frame();
2152 if (!frame || (newSelectionFrame != frame && newSelectionDocument != frame->document()))
2153 return true;
2154 }
2155 }
2156
2157 return false;
2158}
2159
2160void AccessibilityRenderObject::setSelectedVisiblePositionRange(const VisiblePositionRange& range) const
2161{
2162 if (range.start.isNull() || range.end.isNull())
2163 return;
2164
2165 // In WebKit1, when the top web area sets the selection to be an input element in an iframe, the caret will disappear.
2166 // FrameSelection::setSelectionWithoutUpdatingAppearance is setting the selection on the new frame in this case, and causing this behavior.
2167 if (isWebArea() && parentObject() && parentObject()->isAttachment()) {
2168 if (isVisiblePositionRangeInDifferentDocument(range))
2169 return;
2170 }
2171
2172 // make selection and tell the document to use it. if it's zero length, then move to that position
2173 if (range.start == range.end) {
2174 setTextSelectionIntent(axObjectCache(), AXTextStateChangeTypeSelectionMove);
2175 m_renderer->frame().selection().moveTo(range.start, UserTriggered);
2176 clearTextSelectionIntent(axObjectCache());
2177 }
2178 else {
2179 setTextSelectionIntent(axObjectCache(), AXTextStateChangeTypeSelectionExtend);
2180 VisibleSelection newSelection = VisibleSelection(range.start, range.end);
2181 m_renderer->frame().selection().setSelection(newSelection, FrameSelection::defaultSetSelectionOptions());
2182 clearTextSelectionIntent(axObjectCache());
2183 }
2184}
2185
2186VisiblePosition AccessibilityRenderObject::visiblePositionForPoint(const IntPoint& point) const
2187{
2188 if (!m_renderer)
2189 return VisiblePosition();
2190
2191 // convert absolute point to view coordinates
2192 RenderView* renderView = topRenderer();
2193 if (!renderView)
2194 return VisiblePosition();
2195
2196#if PLATFORM(COCOA)
2197 FrameView* frameView = &renderView->frameView();
2198#endif
2199
2200 Node* innerNode = nullptr;
2201
2202 // locate the node containing the point
2203 LayoutPoint pointResult;
2204 while (1) {
2205 LayoutPoint ourpoint;
2206#if PLATFORM(MAC)
2207 ourpoint = frameView->screenToContents(point);
2208#else
2209 ourpoint = point;
2210#endif
2211 HitTestRequest request(HitTestRequest::ReadOnly |
2212 HitTestRequest::Active);
2213 HitTestResult result(ourpoint);
2214 renderView->hitTest(request, result);
2215 innerNode = result.innerNode();
2216 if (!innerNode)
2217 return VisiblePosition();
2218
2219 RenderObject* renderer = innerNode->renderer();
2220 if (!renderer)
2221 return VisiblePosition();
2222
2223 pointResult = result.localPoint();
2224
2225 // done if hit something other than a widget
2226 if (!is<RenderWidget>(*renderer))
2227 break;
2228
2229 // descend into widget (FRAME, IFRAME, OBJECT...)
2230 Widget* widget = downcast<RenderWidget>(*renderer).widget();
2231 if (!is<FrameView>(widget))
2232 break;
2233 Frame& frame = downcast<FrameView>(*widget).frame();
2234 renderView = frame.document()->renderView();
2235#if PLATFORM(COCOA)
2236 frameView = downcast<FrameView>(widget);
2237#endif
2238 }
2239
2240 return innerNode->renderer()->positionForPoint(pointResult, nullptr);
2241}
2242
2243// NOTE: Consider providing this utility method as AX API
2244VisiblePosition AccessibilityRenderObject::visiblePositionForIndex(unsigned indexValue, bool lastIndexOK) const
2245{
2246 if (!isTextControl())
2247 return VisiblePosition();
2248
2249 // lastIndexOK specifies whether the position after the last character is acceptable
2250 if (indexValue >= text().length()) {
2251 if (!lastIndexOK || indexValue > text().length())
2252 return VisiblePosition();
2253 }
2254 VisiblePosition position = visiblePositionForIndex(indexValue);
2255 position.setAffinity(DOWNSTREAM);
2256 return position;
2257}
2258
2259// NOTE: Consider providing this utility method as AX API
2260int AccessibilityRenderObject::index(const VisiblePosition& position) const
2261{
2262 if (position.isNull() || !isTextControl())
2263 return -1;
2264
2265 if (renderObjectContainsPosition(renderer(), position.deepEquivalent()))
2266 return indexForVisiblePosition(position);
2267
2268 return -1;
2269}
2270
2271void AccessibilityRenderObject::lineBreaks(Vector<int>& lineBreaks) const
2272{
2273 if (!isTextControl())
2274 return;
2275
2276 VisiblePosition visiblePos = visiblePositionForIndex(0);
2277 VisiblePosition savedVisiblePos = visiblePos;
2278 visiblePos = nextLinePosition(visiblePos, 0);
2279 while (!visiblePos.isNull() && visiblePos != savedVisiblePos) {
2280 lineBreaks.append(indexForVisiblePosition(visiblePos));
2281 savedVisiblePos = visiblePos;
2282 visiblePos = nextLinePosition(visiblePos, 0);
2283 }
2284}
2285
2286// Given a line number, the range of characters of the text associated with this accessibility
2287// object that contains the line number.
2288PlainTextRange AccessibilityRenderObject::doAXRangeForLine(unsigned lineNumber) const
2289{
2290 if (!isTextControl())
2291 return PlainTextRange();
2292
2293 // iterate to the specified line
2294 VisiblePosition visiblePos = visiblePositionForIndex(0);
2295 VisiblePosition savedVisiblePos;
2296 for (unsigned lineCount = lineNumber; lineCount; lineCount -= 1) {
2297 savedVisiblePos = visiblePos;
2298 visiblePos = nextLinePosition(visiblePos, 0);
2299 if (visiblePos.isNull() || visiblePos == savedVisiblePos)
2300 return PlainTextRange();
2301 }
2302
2303 // Get the end of the line based on the starting position.
2304 VisiblePosition endPosition = endOfLine(visiblePos);
2305
2306 int index1 = indexForVisiblePosition(visiblePos);
2307 int index2 = indexForVisiblePosition(endPosition);
2308
2309 // add one to the end index for a line break not caused by soft line wrap (to match AppKit)
2310 if (endPosition.affinity() == DOWNSTREAM && endPosition.next().isNotNull())
2311 index2 += 1;
2312
2313 // return nil rather than an zero-length range (to match AppKit)
2314 if (index1 == index2)
2315 return PlainTextRange();
2316
2317 return PlainTextRange(index1, index2 - index1);
2318}
2319
2320// The composed character range in the text associated with this accessibility object that
2321// is specified by the given index value. This parameterized attribute returns the complete
2322// range of characters (including surrogate pairs of multi-byte glyphs) at the given index.
2323PlainTextRange AccessibilityRenderObject::doAXRangeForIndex(unsigned index) const
2324{
2325 if (!isTextControl())
2326 return PlainTextRange();
2327
2328 String elementText = text();
2329 if (!elementText.length() || index > elementText.length() - 1)
2330 return PlainTextRange();
2331
2332 return PlainTextRange(index, 1);
2333}
2334
2335// A substring of the text associated with this accessibility object that is
2336// specified by the given character range.
2337String AccessibilityRenderObject::doAXStringForRange(const PlainTextRange& range) const
2338{
2339 if (!range.length)
2340 return String();
2341
2342 if (!isTextControl())
2343 return String();
2344
2345 String elementText = isPasswordField() ? passwordFieldValue() : text();
2346 return elementText.substring(range.start, range.length);
2347}
2348
2349// The bounding rectangle of the text associated with this accessibility object that is
2350// specified by the given range. This is the bounding rectangle a sighted user would see
2351// on the display screen, in pixels.
2352IntRect AccessibilityRenderObject::doAXBoundsForRange(const PlainTextRange& range) const
2353{
2354 if (allowsTextRanges())
2355 return boundsForVisiblePositionRange(visiblePositionRangeForRange(range));
2356 return IntRect();
2357}
2358
2359IntRect AccessibilityRenderObject::doAXBoundsForRangeUsingCharacterOffset(const PlainTextRange& range) const
2360{
2361 if (allowsTextRanges())
2362 return boundsForRange(rangeForPlainTextRange(range));
2363 return IntRect();
2364}
2365
2366AccessibilityObject* AccessibilityRenderObject::accessibilityImageMapHitTest(HTMLAreaElement* area, const IntPoint& point) const
2367{
2368 if (!area)
2369 return nullptr;
2370
2371 AccessibilityObject* parent = nullptr;
2372 for (Element* mapParent = area->parentElement(); mapParent; mapParent = mapParent->parentElement()) {
2373 if (is<HTMLMapElement>(*mapParent)) {
2374 parent = accessibilityParentForImageMap(downcast<HTMLMapElement>(mapParent));
2375 break;
2376 }
2377 }
2378 if (!parent)
2379 return nullptr;
2380
2381 for (const auto& child : parent->children()) {
2382 if (child->elementRect().contains(point))
2383 return child.get();
2384 }
2385
2386 return nullptr;
2387}
2388
2389AccessibilityObjectInterface* AccessibilityRenderObject::remoteSVGElementHitTest(const IntPoint& point) const
2390{
2391 AccessibilityObject* remote = remoteSVGRootElement(Create);
2392 if (!remote)
2393 return nullptr;
2394
2395 IntSize offset = point - roundedIntPoint(boundingBoxRect().location());
2396 return remote->accessibilityHitTest(IntPoint(offset));
2397}
2398
2399AccessibilityObjectInterface* AccessibilityRenderObject::elementAccessibilityHitTest(const IntPoint& point) const
2400{
2401 if (isSVGImage())
2402 return remoteSVGElementHitTest(point);
2403
2404 return AccessibilityObject::elementAccessibilityHitTest(point);
2405}
2406
2407static bool shouldUseShadowHostForHitTesting(Node* shadowHost)
2408{
2409 // We need to allow automation of mouse events on video tags.
2410 return shadowHost && !shadowHost->hasTagName(videoTag);
2411}
2412
2413AccessibilityObjectInterface* AccessibilityRenderObject::accessibilityHitTest(const IntPoint& point) const
2414{
2415 if (!m_renderer || !m_renderer->hasLayer())
2416 return nullptr;
2417
2418 m_renderer->document().updateLayout();
2419
2420 RenderLayer* layer = downcast<RenderBox>(*m_renderer).layer();
2421
2422 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::AccessibilityHitTest);
2423 HitTestResult hitTestResult = HitTestResult(point);
2424 layer->hitTest(request, hitTestResult);
2425 Node* node = hitTestResult.innerNode();
2426 if (!node)
2427 return nullptr;
2428 Node* shadowAncestorNode = node->shadowHost();
2429 if (shouldUseShadowHostForHitTesting(shadowAncestorNode))
2430 node = shadowAncestorNode;
2431 ASSERT(node);
2432
2433 if (is<HTMLAreaElement>(*node))
2434 return accessibilityImageMapHitTest(downcast<HTMLAreaElement>(node), point);
2435
2436 if (is<HTMLOptionElement>(*node))
2437 node = downcast<HTMLOptionElement>(*node).ownerSelectElement();
2438
2439 RenderObject* obj = node->renderer();
2440 if (!obj)
2441 return nullptr;
2442
2443 AccessibilityObject* result = obj->document().axObjectCache()->getOrCreate(obj);
2444 result->updateChildrenIfNecessary();
2445
2446 // Allow the element to perform any hit-testing it might need to do to reach non-render children.
2447 result = static_cast<AccessibilityObject*>(result->elementAccessibilityHitTest(point));
2448
2449 if (result && result->accessibilityIsIgnored()) {
2450 // If this element is the label of a control, a hit test should return the control.
2451 AccessibilityObject* controlObject = result->correspondingControlForLabelElement();
2452 if (controlObject && !controlObject->exposesTitleUIElement())
2453 return controlObject;
2454
2455 result = result->parentObjectUnignored();
2456 }
2457
2458 return result;
2459}
2460
2461bool AccessibilityRenderObject::shouldNotifyActiveDescendant() const
2462{
2463#if PLATFORM(GTK)
2464 // According to the Core AAM spec, ATK expects object:state-changed:focused notifications
2465 // whenever the active descendant changes.
2466 return true;
2467#endif
2468 // We want to notify that the combo box has changed its active descendant,
2469 // but we do not want to change the focus, because focus should remain with the combo box.
2470 if (isComboBox())
2471 return true;
2472
2473 return shouldFocusActiveDescendant();
2474}
2475
2476bool AccessibilityRenderObject::shouldFocusActiveDescendant() const
2477{
2478 switch (ariaRoleAttribute()) {
2479 case AccessibilityRole::ApplicationGroup:
2480 case AccessibilityRole::ListBox:
2481 case AccessibilityRole::Menu:
2482 case AccessibilityRole::MenuBar:
2483 case AccessibilityRole::RadioGroup:
2484 case AccessibilityRole::Row:
2485 case AccessibilityRole::PopUpButton:
2486 case AccessibilityRole::Meter:
2487 case AccessibilityRole::ProgressIndicator:
2488 case AccessibilityRole::Toolbar:
2489 case AccessibilityRole::Outline:
2490 case AccessibilityRole::Tree:
2491 case AccessibilityRole::Grid:
2492 /* FIXME: replace these with actual roles when they are added to AccessibilityRole
2493 composite
2494 alert
2495 alertdialog
2496 status
2497 timer
2498 */
2499 return true;
2500 default:
2501 return false;
2502 }
2503}
2504
2505AccessibilityObject* AccessibilityRenderObject::activeDescendant() const
2506{
2507 if (!m_renderer)
2508 return nullptr;
2509
2510 const AtomicString& activeDescendantAttrStr = getAttribute(aria_activedescendantAttr);
2511 if (activeDescendantAttrStr.isNull() || activeDescendantAttrStr.isEmpty())
2512 return nullptr;
2513 Element* element = this->element();
2514 if (!element)
2515 return nullptr;
2516
2517 Element* target = element->treeScope().getElementById(activeDescendantAttrStr);
2518 if (!target)
2519 return nullptr;
2520
2521 if (AXObjectCache* cache = axObjectCache()) {
2522 AccessibilityObject* obj = cache->getOrCreate(target);
2523 if (obj && obj->isAccessibilityRenderObject())
2524 // an activedescendant is only useful if it has a renderer, because that's what's needed to post the notification
2525 return obj;
2526 }
2527
2528 return nullptr;
2529}
2530
2531void AccessibilityRenderObject::handleAriaExpandedChanged()
2532{
2533 // This object might be deleted under the call to the parentObject() method.
2534 auto protectedThis = makeRef(*this);
2535
2536 // Find if a parent of this object should handle aria-expanded changes.
2537 AccessibilityObject* containerParent = this->parentObject();
2538 while (containerParent) {
2539 bool foundParent = false;
2540
2541 switch (containerParent->roleValue()) {
2542 case AccessibilityRole::Tree:
2543 case AccessibilityRole::TreeGrid:
2544 case AccessibilityRole::Grid:
2545 case AccessibilityRole::Table:
2546 case AccessibilityRole::Browser:
2547 foundParent = true;
2548 break;
2549 default:
2550 break;
2551 }
2552
2553 if (foundParent)
2554 break;
2555
2556 containerParent = containerParent->parentObject();
2557 }
2558
2559 // Post that the row count changed.
2560 AXObjectCache* cache = axObjectCache();
2561 if (!cache)
2562 return;
2563
2564 if (containerParent)
2565 cache->postNotification(containerParent, document(), AXObjectCache::AXRowCountChanged);
2566
2567 // Post that the specific row either collapsed or expanded.
2568 if (roleValue() == AccessibilityRole::Row || roleValue() == AccessibilityRole::TreeItem)
2569 cache->postNotification(this, document(), isExpanded() ? AXObjectCache::AXRowExpanded : AXObjectCache::AXRowCollapsed);
2570 else
2571 cache->postNotification(this, document(), AXObjectCache::AXExpandedChanged);
2572}
2573
2574RenderObject* AccessibilityRenderObject::targetElementForActiveDescendant(const QualifiedName& attributeName, AccessibilityObject* activeDescendant) const
2575{
2576 AccessibilityObject::AccessibilityChildrenVector elements;
2577 ariaElementsFromAttribute(elements, attributeName);
2578 for (const auto& element : elements) {
2579 if (activeDescendant->isDescendantOfObject(element.get()))
2580 return element->renderer();
2581 }
2582
2583 return nullptr;
2584}
2585
2586void AccessibilityRenderObject::handleActiveDescendantChanged()
2587{
2588 Element* element = downcast<Element>(renderer()->node());
2589 if (!element)
2590 return;
2591 if (!renderer()->frame().selection().isFocusedAndActive() || renderer()->document().focusedElement() != element)
2592 return;
2593
2594 auto* activeDescendant = this->activeDescendant();
2595 if (activeDescendant && shouldNotifyActiveDescendant()) {
2596 auto* targetRenderer = renderer();
2597
2598#if PLATFORM(COCOA)
2599 // If the combobox's activeDescendant is inside another object, the target element should be that parent.
2600 if (isComboBox()) {
2601 if (auto* ariaOwner = targetElementForActiveDescendant(aria_ownsAttr, activeDescendant))
2602 targetRenderer = ariaOwner;
2603 else if (auto* ariaController = targetElementForActiveDescendant(aria_controlsAttr, activeDescendant))
2604 targetRenderer = ariaController;
2605 }
2606#endif
2607
2608 renderer()->document().axObjectCache()->postNotification(targetRenderer, AXObjectCache::AXActiveDescendantChanged);
2609 }
2610}
2611
2612AccessibilityObject* AccessibilityRenderObject::correspondingControlForLabelElement() const
2613{
2614 HTMLLabelElement* labelElement = labelElementContainer();
2615 if (!labelElement)
2616 return nullptr;
2617
2618 auto correspondingControl = labelElement->control();
2619 if (!correspondingControl)
2620 return nullptr;
2621
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())
2624 return nullptr;
2625
2626 return axObjectCache()->getOrCreate(correspondingControl.get());
2627}
2628
2629AccessibilityObject* AccessibilityRenderObject::correspondingLabelForControlElement() const
2630{
2631 if (!m_renderer)
2632 return nullptr;
2633
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())
2637 return nullptr;
2638
2639 Node* node = m_renderer->node();
2640 if (is<HTMLElement>(node)) {
2641 if (HTMLLabelElement* label = labelForElement(downcast<HTMLElement>(node)))
2642 return axObjectCache()->getOrCreate(label);
2643 }
2644
2645 return nullptr;
2646}
2647
2648bool AccessibilityRenderObject::renderObjectIsObservable(RenderObject& renderer) const
2649{
2650 // AX clients will listen for AXValueChange on a text control.
2651 if (is<RenderTextControl>(renderer))
2652 return true;
2653
2654 // AX clients will listen for AXSelectedChildrenChanged on listboxes.
2655 Node* node = renderer.node();
2656 if (!node)
2657 return false;
2658
2659 if (nodeHasRole(node, "listbox") || (is<RenderBoxModelObject>(renderer) && downcast<RenderBoxModelObject>(renderer).isListBox()))
2660 return true;
2661
2662 // Textboxes should send out notifications.
2663 if (nodeHasRole(node, "textbox") || (is<Element>(*node) && contentEditableAttributeIsEnabled(downcast<Element>(node))))
2664 return true;
2665
2666 return false;
2667}
2668
2669AccessibilityObject* AccessibilityRenderObject::observableObject() const
2670{
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);
2676 }
2677 }
2678
2679 return nullptr;
2680}
2681
2682bool AccessibilityRenderObject::isDescendantOfElementType(const HashSet<QualifiedName>& tagNames) const
2683{
2684 for (auto& ancestor : ancestorsOfType<RenderElement>(*m_renderer)) {
2685 if (ancestor.element() && tagNames.contains(ancestor.element()->tagQName()))
2686 return true;
2687 }
2688 return false;
2689}
2690
2691bool AccessibilityRenderObject::isDescendantOfElementType(const QualifiedName& tagName) const
2692{
2693 for (auto& ancestor : ancestorsOfType<RenderElement>(*m_renderer)) {
2694 if (ancestor.element() && ancestor.element()->hasTagName(tagName))
2695 return true;
2696 }
2697 return false;
2698}
2699
2700String AccessibilityRenderObject::expandedTextValue() const
2701{
2702 if (AccessibilityObject* parent = parentObject()) {
2703 if (parent->hasTagName(abbrTag) || parent->hasTagName(acronymTag))
2704 return parent->getAttribute(titleAttr);
2705 }
2706
2707 return String();
2708}
2709
2710bool AccessibilityRenderObject::supportsExpandedTextValue() const
2711{
2712 if (roleValue() == AccessibilityRole::StaticText) {
2713 if (AccessibilityObject* parent = parentObject())
2714 return parent->hasTagName(abbrTag) || parent->hasTagName(acronymTag);
2715 }
2716
2717 return false;
2718}
2719
2720AccessibilityRole AccessibilityRenderObject::determineAccessibilityRole()
2721{
2722 if (!m_renderer)
2723 return AccessibilityRole::Unknown;
2724
2725#if ENABLE(APPLE_PAY)
2726 if (isApplePayButton())
2727 return AccessibilityRole::Button;
2728#endif
2729
2730 // Sometimes we need to ignore the attribute role. Like if a tree is malformed,
2731 // we want to ignore the treeitem's attribute role.
2732 if ((m_ariaRole = determineAriaRoleAttribute()) != AccessibilityRole::Unknown && !shouldIgnoreAttributeRole())
2733 return m_ariaRole;
2734
2735 Node* node = m_renderer->node();
2736 RenderBoxModelObject* cssBox = renderBoxModelObject();
2737
2738 if (node && node->isLink())
2739 return AccessibilityRole::WebCoreLink;
2740 if (node && is<HTMLImageElement>(*node) && downcast<HTMLImageElement>(*node).hasAttributeWithoutSynchronization(usemapAttr))
2741 return AccessibilityRole::ImageMap;
2742 if ((cssBox && cssBox->isListItem()) || (node && node->hasTagName(liTag)))
2743 return AccessibilityRole::ListItem;
2744 if (m_renderer->isListMarker())
2745 return AccessibilityRole::ListMarker;
2746 if (node && node->hasTagName(buttonTag))
2747 return buttonRoleType();
2748 if (node && node->hasTagName(legendTag))
2749 return AccessibilityRole::Legend;
2750 if (m_renderer->isText())
2751 return AccessibilityRole::StaticText;
2752 if (cssBox && cssBox->isImage()) {
2753 if (is<HTMLInputElement>(node))
2754 return hasPopup() ? AccessibilityRole::PopUpButton : AccessibilityRole::Button;
2755 if (isSVGImage())
2756 return AccessibilityRole::SVGRoot;
2757 return AccessibilityRole::Image;
2758 }
2759
2760 if (node && node->hasTagName(canvasTag))
2761 return AccessibilityRole::Canvas;
2762
2763 if (cssBox && cssBox->isRenderView())
2764 return AccessibilityRole::WebArea;
2765
2766 if (cssBox && cssBox->isTextField()) {
2767 if (is<HTMLInputElement>(node))
2768 return downcast<HTMLInputElement>(*node).isSearchField() ? AccessibilityRole::SearchField : AccessibilityRole::TextField;
2769 }
2770
2771 if (cssBox && cssBox->isTextArea())
2772 return AccessibilityRole::TextArea;
2773
2774 if (is<HTMLInputElement>(node)) {
2775 HTMLInputElement& input = downcast<HTMLInputElement>(*node);
2776 if (input.isCheckbox())
2777 return AccessibilityRole::CheckBox;
2778 if (input.isRadioButton())
2779 return AccessibilityRole::RadioButton;
2780 if (input.isTextButton())
2781 return buttonRoleType();
2782 // On iOS, the date field and time field are popup buttons. On other platforms they are text fields.
2783#if PLATFORM(IOS_FAMILY)
2784 if (input.isDateField() || input.isTimeField())
2785 return AccessibilityRole::PopUpButton;
2786#endif
2787#if ENABLE(INPUT_TYPE_COLOR)
2788 if (input.isColorControl())
2789 return AccessibilityRole::ColorWell;
2790#endif
2791 }
2792
2793 if (hasContentEditableAttributeSet())
2794 return AccessibilityRole::TextArea;
2795
2796 if (isFileUploadButton())
2797 return AccessibilityRole::Button;
2798
2799 if (cssBox && cssBox->isMenuList())
2800 return AccessibilityRole::PopUpButton;
2801
2802 if (headingLevel())
2803 return AccessibilityRole::Heading;
2804
2805 if (m_renderer->isSVGRoot())
2806 return AccessibilityRole::SVGRoot;
2807
2808 if (isStyleFormatGroup())
2809 return is<RenderInline>(*m_renderer) ? AccessibilityRole::Inline : AccessibilityRole::TextGroup;
2810
2811 if (node && node->hasTagName(ddTag))
2812 return AccessibilityRole::DescriptionListDetail;
2813
2814 if (node && node->hasTagName(dtTag))
2815 return AccessibilityRole::DescriptionListTerm;
2816
2817 if (node && node->hasTagName(dlTag))
2818 return AccessibilityRole::DescriptionList;
2819
2820 if (node && node->hasTagName(fieldsetTag))
2821 return AccessibilityRole::Group;
2822
2823 if (node && node->hasTagName(figureTag))
2824 return AccessibilityRole::Figure;
2825
2826 // Check for Ruby elements
2827 if (m_renderer->isRubyText())
2828 return AccessibilityRole::RubyText;
2829 if (m_renderer->isRubyBase())
2830 return AccessibilityRole::RubyBase;
2831 if (m_renderer->isRubyRun())
2832 return AccessibilityRole::RubyRun;
2833 if (m_renderer->isRubyBlock())
2834 return AccessibilityRole::RubyBlock;
2835 if (m_renderer->isRubyInline())
2836 return AccessibilityRole::RubyInline;
2837
2838 // This return value is what will be used if AccessibilityTableCell determines
2839 // the cell should not be treated as a cell (e.g. because it is a layout table.
2840 if (is<RenderTableCell>(renderer()))
2841 return AccessibilityRole::TextGroup;
2842
2843 // Table sections should be ignored.
2844 if (m_renderer->isTableSection())
2845 return AccessibilityRole::Ignored;
2846
2847 if (m_renderer->isHR())
2848 return AccessibilityRole::HorizontalRule;
2849
2850 if (node && node->hasTagName(pTag))
2851 return AccessibilityRole::Paragraph;
2852
2853 if (is<HTMLLabelElement>(node))
2854 return AccessibilityRole::Label;
2855
2856 if (node && node->hasTagName(dfnTag))
2857 return AccessibilityRole::Definition;
2858
2859 if (node && node->hasTagName(divTag))
2860 return AccessibilityRole::Div;
2861
2862 if (is<HTMLFormElement>(node))
2863 return AccessibilityRole::Form;
2864
2865 if (node && node->hasTagName(articleTag))
2866 return AccessibilityRole::DocumentArticle;
2867
2868 if (node && node->hasTagName(mainTag))
2869 return AccessibilityRole::LandmarkMain;
2870
2871 if (node && node->hasTagName(navTag))
2872 return AccessibilityRole::LandmarkNavigation;
2873
2874 if (node && node->hasTagName(asideTag))
2875 return AccessibilityRole::LandmarkComplementary;
2876
2877 // The default role attribute value for the section element, region, became a landmark in ARIA 1.1.
2878 // The HTML AAM spec says it is "strongly recommended" that ATs only convey and provide navigation
2879 // for section elements which have names.
2880 if (node && node->hasTagName(sectionTag))
2881 return hasAttribute(aria_labelAttr) || hasAttribute(aria_labelledbyAttr) ? AccessibilityRole::LandmarkRegion : AccessibilityRole::TextGroup;
2882
2883 if (node && node->hasTagName(addressTag))
2884 return AccessibilityRole::LandmarkContentInfo;
2885
2886 if (node && node->hasTagName(blockquoteTag))
2887 return AccessibilityRole::Blockquote;
2888
2889 if (node && node->hasTagName(captionTag))
2890 return AccessibilityRole::Caption;
2891
2892 if (node && node->hasTagName(markTag))
2893 return AccessibilityRole::Mark;
2894
2895 if (node && node->hasTagName(preTag))
2896 return AccessibilityRole::Pre;
2897
2898 if (is<HTMLDetailsElement>(node))
2899 return AccessibilityRole::Details;
2900 if (is<HTMLSummaryElement>(node))
2901 return AccessibilityRole::Summary;
2902
2903 // http://rawgit.com/w3c/aria/master/html-aam/html-aam.html
2904 // Output elements should be mapped to status role.
2905 if (isOutput())
2906 return AccessibilityRole::ApplicationStatus;
2907
2908#if ENABLE(VIDEO)
2909 if (is<HTMLVideoElement>(node))
2910 return AccessibilityRole::Video;
2911 if (is<HTMLAudioElement>(node))
2912 return AccessibilityRole::Audio;
2913#endif
2914
2915 // The HTML element should not be exposed as an element. That's what the RenderView element does.
2916 if (node && node->hasTagName(htmlTag))
2917 return AccessibilityRole::Ignored;
2918
2919 // There should only be one banner/contentInfo per page. If header/footer are being used within an article or section
2920 // then it should not be exposed as whole page's banner/contentInfo
2921 if (node && node->hasTagName(headerTag) && !isDescendantOfElementType({ articleTag, sectionTag }))
2922 return AccessibilityRole::LandmarkBanner;
2923
2924 // http://webkit.org/b/190138 Footers should become contentInfo's if scoped to body (and consequently become a landmark).
2925 // It should remain a footer if scoped to main, sectioning elements (article, section) or root sectioning element (blockquote, details, dialog, fieldset, figure, td).
2926 if (node && node->hasTagName(footerTag)) {
2927 if (!isDescendantOfElementType({ articleTag, sectionTag, mainTag, blockquoteTag, detailsTag, fieldsetTag, figureTag, tdTag }))
2928 return AccessibilityRole::LandmarkContentInfo;
2929 return AccessibilityRole::Footer;
2930 }
2931
2932 // menu tags with toolbar type should have Toolbar role.
2933 if (node && node->hasTagName(menuTag) && equalLettersIgnoringASCIICase(getAttribute(typeAttr), "toolbar"))
2934 return AccessibilityRole::Toolbar;
2935
2936 if (node && node->hasTagName(timeTag))
2937 return AccessibilityRole::Time;
2938
2939 // 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.
2940 if (supportsARIAAttributes() || canSetFocusAttribute())
2941 return AccessibilityRole::Group;
2942
2943 if (m_renderer->isRenderBlockFlow())
2944 return m_renderer->isAnonymousBlock() ? AccessibilityRole::TextGroup : AccessibilityRole::Group;
2945
2946 // InlineRole is the final fallback before assigning AccessibilityRole::Unknown to an object. It makes it
2947 // possible to distinguish truly unknown objects from non-focusable inline text elements
2948 // which have an event handler or attribute suggesting possible inclusion by the platform.
2949 if (is<RenderInline>(*m_renderer)
2950 && (hasAttributesRequiredForInclusion()
2951 || (node && node->hasEventListeners())
2952 || (supportsDatetimeAttribute() && !getAttribute(datetimeAttr).isEmpty())))
2953 return AccessibilityRole::Inline;
2954
2955 return AccessibilityRole::Unknown;
2956}
2957
2958AccessibilityOrientation AccessibilityRenderObject::orientation() const
2959{
2960 const AtomicString& ariaOrientation = getAttribute(aria_orientationAttr);
2961 if (equalLettersIgnoringASCIICase(ariaOrientation, "horizontal"))
2962 return AccessibilityOrientation::Horizontal;
2963 if (equalLettersIgnoringASCIICase(ariaOrientation, "vertical"))
2964 return AccessibilityOrientation::Vertical;
2965 if (equalLettersIgnoringASCIICase(ariaOrientation, "undefined"))
2966 return AccessibilityOrientation::Undefined;
2967
2968 // In ARIA 1.1, the implicit value of aria-orientation changed from horizontal
2969 // to undefined on all roles that don't have their own role-specific values. In
2970 // addition, the implicit value of combobox became undefined.
2971 if (isComboBox() || isRadioGroup() || isTreeGrid())
2972 return AccessibilityOrientation::Undefined;
2973
2974 if (isScrollbar() || isListBox() || isMenu() || isTree())
2975 return AccessibilityOrientation::Vertical;
2976
2977 if (isMenuBar() || isSplitter() || isTabList() || isToolbar() || isSlider())
2978 return AccessibilityOrientation::Horizontal;
2979
2980 return AccessibilityObject::orientation();
2981}
2982
2983bool AccessibilityRenderObject::inheritsPresentationalRole() const
2984{
2985 // ARIA states if an item can get focus, it should not be presentational.
2986 if (canSetFocusAttribute())
2987 return false;
2988
2989 // ARIA spec says that when a parent object is presentational, and it has required child elements,
2990 // those child elements are also presentational. For example, <li> becomes presentational from <ul>.
2991 // http://www.w3.org/WAI/PF/aria/complete#presentation
2992
2993 const Vector<const HTMLQualifiedName*>* parentTags;
2994 switch (roleValue()) {
2995 case AccessibilityRole::ListItem:
2996 case AccessibilityRole::ListMarker: {
2997 static const auto listItemParents = makeNeverDestroyed(Vector<const HTMLQualifiedName*> { &dlTag.get(), &olTag.get(), &ulTag.get() });
2998 parentTags = &listItemParents.get();
2999 break;
3000 }
3001 case AccessibilityRole::GridCell:
3002 case AccessibilityRole::Cell: {
3003 static const auto tableCellParents = makeNeverDestroyed(Vector<const HTMLQualifiedName*> { &tableTag.get() });
3004 parentTags = &tableCellParents.get();
3005 break;
3006 }
3007 default:
3008 // Not all elements need to do the following check, only ones that are required children.
3009 return false;
3010 }
3011
3012 for (auto* parent = parentObject(); parent; parent = parent->parentObject()) {
3013 if (!is<AccessibilityRenderObject>(*parent))
3014 continue;
3015
3016 Node* node = downcast<AccessibilityRenderObject>(*parent).node();
3017 if (!is<Element>(node))
3018 continue;
3019
3020 // If native tag of the parent element matches an acceptable name, then return
3021 // based on its presentational status.
3022 auto& name = downcast<Element>(*node).tagQName();
3023 if (std::any_of(parentTags->begin(), parentTags->end(), [&name] (auto* possibleName) { return *possibleName == name; }))
3024 return parent->roleValue() == AccessibilityRole::Presentational;
3025 }
3026
3027 return false;
3028}
3029
3030bool AccessibilityRenderObject::isPresentationalChildOfAriaRole() const
3031{
3032 // Walk the parent chain looking for a parent that has presentational children
3033 AccessibilityObject* parent;
3034 for (parent = parentObject(); parent && !parent->ariaRoleHasPresentationalChildren(); parent = parent->parentObject())
3035 { }
3036
3037 return parent;
3038}
3039
3040bool AccessibilityRenderObject::ariaRoleHasPresentationalChildren() const
3041{
3042 switch (m_ariaRole) {
3043 case AccessibilityRole::Button:
3044 case AccessibilityRole::Slider:
3045 case AccessibilityRole::Image:
3046 case AccessibilityRole::ProgressIndicator:
3047 case AccessibilityRole::SpinButton:
3048 // case SeparatorRole:
3049 return true;
3050 default:
3051 return false;
3052 }
3053}
3054
3055bool AccessibilityRenderObject::canSetExpandedAttribute() const
3056{
3057 if (roleValue() == AccessibilityRole::Details)
3058 return true;
3059
3060 // An object can be expanded if it aria-expanded is true or false.
3061 const AtomicString& expanded = getAttribute(aria_expandedAttr);
3062 if (equalLettersIgnoringASCIICase(expanded, "true") || equalLettersIgnoringASCIICase(expanded, "false"))
3063 return true;
3064 return false;
3065}
3066
3067bool AccessibilityRenderObject::canSetTextRangeAttributes() const
3068{
3069 return isTextControl();
3070}
3071
3072void AccessibilityRenderObject::textChanged()
3073{
3074 // If this element supports ARIA live regions, or is part of a region with an ARIA editable role,
3075 // then notify the AT of changes.
3076 AXObjectCache* cache = axObjectCache();
3077 if (!cache)
3078 return;
3079
3080 for (RenderObject* renderParent = renderer(); renderParent; renderParent = renderParent->parent()) {
3081 AccessibilityObject* parent = cache->get(renderParent);
3082 if (!parent)
3083 continue;
3084
3085 if (parent->supportsLiveRegion())
3086 cache->postLiveRegionChangeNotification(parent);
3087
3088 if (parent->isNonNativeTextControl())
3089 cache->postNotification(renderParent, AXObjectCache::AXValueChanged);
3090 }
3091}
3092
3093void AccessibilityRenderObject::clearChildren()
3094{
3095 AccessibilityObject::clearChildren();
3096 m_childrenDirty = false;
3097}
3098
3099void AccessibilityRenderObject::addImageMapChildren()
3100{
3101 RenderBoxModelObject* cssBox = renderBoxModelObject();
3102 if (!is<RenderImage>(cssBox))
3103 return;
3104
3105 HTMLMapElement* map = downcast<RenderImage>(*cssBox).imageMap();
3106 if (!map)
3107 return;
3108
3109 for (auto& area : descendantsOfType<HTMLAreaElement>(*map)) {
3110 // add an <area> element for this child if it has a link
3111 if (!area.isLink())
3112 continue;
3113 auto& areaObject = downcast<AccessibilityImageMapLink>(*axObjectCache()->getOrCreate(AccessibilityRole::ImageMapLink));
3114 areaObject.setHTMLAreaElement(&area);
3115 areaObject.setHTMLMapElement(map);
3116 areaObject.setParent(this);
3117 if (!areaObject.accessibilityIsIgnored())
3118 m_children.append(&areaObject);
3119 else
3120 axObjectCache()->remove(areaObject.axObjectID());
3121 }
3122}
3123
3124void AccessibilityRenderObject::updateChildrenIfNecessary()
3125{
3126 if (needsToUpdateChildren())
3127 clearChildren();
3128
3129 AccessibilityObject::updateChildrenIfNecessary();
3130}
3131
3132void AccessibilityRenderObject::addTextFieldChildren()
3133{
3134 Node* node = this->node();
3135 if (!is<HTMLInputElement>(node))
3136 return;
3137
3138 HTMLInputElement& input = downcast<HTMLInputElement>(*node);
3139 if (HTMLElement* autoFillElement = input.autoFillButtonElement()) {
3140 if (AccessibilityObject* axAutoFill = axObjectCache()->getOrCreate(autoFillElement))
3141 m_children.append(axAutoFill);
3142 }
3143
3144 HTMLElement* spinButtonElement = input.innerSpinButtonElement();
3145 if (!is<SpinButtonElement>(spinButtonElement))
3146 return;
3147
3148 auto& axSpinButton = downcast<AccessibilitySpinButton>(*axObjectCache()->getOrCreate(AccessibilityRole::SpinButton));
3149 axSpinButton.setSpinButtonElement(downcast<SpinButtonElement>(spinButtonElement));
3150 axSpinButton.setParent(this);
3151 m_children.append(&axSpinButton);
3152}
3153
3154bool AccessibilityRenderObject::isSVGImage() const
3155{
3156 return remoteSVGRootElement(Create);
3157}
3158
3159void AccessibilityRenderObject::detachRemoteSVGRoot()
3160{
3161 if (AccessibilitySVGRoot* root = remoteSVGRootElement(Retrieve))
3162 root->setParent(nullptr);
3163}
3164
3165AccessibilitySVGRoot* AccessibilityRenderObject::remoteSVGRootElement(CreationChoice createIfNecessary) const
3166{
3167 if (!is<RenderImage>(renderer()))
3168 return nullptr;
3169
3170 CachedImage* cachedImage = downcast<RenderImage>(*m_renderer).cachedImage();
3171 if (!cachedImage)
3172 return nullptr;
3173
3174 Image* image = cachedImage->image();
3175 if (!is<SVGImage>(image))
3176 return nullptr;
3177
3178 FrameView* frameView = downcast<SVGImage>(*image).frameView();
3179 if (!frameView)
3180 return nullptr;
3181 Frame& frame = frameView->frame();
3182
3183 Document* document = frame.document();
3184 if (!is<SVGDocument>(document))
3185 return nullptr;
3186
3187 auto rootElement = SVGDocument::rootElement(*document);
3188 if (!rootElement)
3189 return nullptr;
3190 RenderObject* rendererRoot = rootElement->renderer();
3191 if (!rendererRoot)
3192 return nullptr;
3193
3194 AXObjectCache* cache = frame.document()->axObjectCache();
3195 if (!cache)
3196 return nullptr;
3197 AccessibilityObject* rootSVGObject = createIfNecessary == Create ? cache->getOrCreate(rendererRoot) : cache->get(rendererRoot);
3198
3199 // In order to connect the AX hierarchy from the SVG root element from the loaded resource
3200 // the parent must be set, because there's no other way to get back to who created the image.
3201 ASSERT(!createIfNecessary || rootSVGObject);
3202 if (!is<AccessibilitySVGRoot>(rootSVGObject))
3203 return nullptr;
3204
3205 return downcast<AccessibilitySVGRoot>(rootSVGObject);
3206}
3207
3208void AccessibilityRenderObject::addRemoteSVGChildren()
3209{
3210 AccessibilitySVGRoot* root = remoteSVGRootElement(Create);
3211 if (!root)
3212 return;
3213
3214 root->setParent(this);
3215
3216 if (root->accessibilityIsIgnored()) {
3217 for (const auto& child : root->children())
3218 m_children.append(child);
3219 } else
3220 m_children.append(root);
3221}
3222
3223void AccessibilityRenderObject::addCanvasChildren()
3224{
3225 // Add the unrendered canvas children as AX nodes, unless we're not using a canvas renderer
3226 // because JS is disabled for example.
3227 if (!node() || !node()->hasTagName(canvasTag) || (renderer() && !renderer()->isCanvas()))
3228 return;
3229
3230 // If it's a canvas, it won't have rendered children, but it might have accessible fallback content.
3231 // Clear m_haveChildren because AccessibilityNodeObject::addChildren will expect it to be false.
3232 ASSERT(!m_children.size());
3233 m_haveChildren = false;
3234 AccessibilityNodeObject::addChildren();
3235}
3236
3237void AccessibilityRenderObject::addAttachmentChildren()
3238{
3239 if (!isAttachment())
3240 return;
3241
3242 // FrameView's need to be inserted into the AX hierarchy when encountered.
3243 Widget* widget = widgetForAttachmentView();
3244 if (!widget || !widget->isFrameView())
3245 return;
3246
3247 addChild(axObjectCache()->getOrCreate(widget));
3248}
3249
3250#if PLATFORM(COCOA)
3251void AccessibilityRenderObject::updateAttachmentViewParents()
3252{
3253 // Only the unignored parent should set the attachment parent, because that's what is reflected in the AX
3254 // hierarchy to the client.
3255 if (accessibilityIsIgnored())
3256 return;
3257
3258 for (const auto& child : m_children) {
3259 if (child->isAttachment())
3260 child->overrideAttachmentParent(this);
3261 }
3262}
3263#endif
3264
3265// Hidden children are those that are not rendered or visible, but are specifically marked as aria-hidden=false,
3266// meaning that they should be exposed to the AX hierarchy.
3267void AccessibilityRenderObject::addHiddenChildren()
3268{
3269 Node* node = this->node();
3270 if (!node)
3271 return;
3272
3273 // First do a quick run through to determine if we have any hidden nodes (most often we will not).
3274 // If we do have hidden nodes, we need to determine where to insert them so they match DOM order as close as possible.
3275 bool shouldInsertHiddenNodes = false;
3276 for (Node* child = node->firstChild(); child; child = child->nextSibling()) {
3277 if (!child->renderer() && isNodeAriaVisible(child)) {
3278 shouldInsertHiddenNodes = true;
3279 break;
3280 }
3281 }
3282
3283 if (!shouldInsertHiddenNodes)
3284 return;
3285
3286 // Iterate through all of the children, including those that may have already been added, and
3287 // try to insert hidden nodes in the correct place in the DOM order.
3288 unsigned insertionIndex = 0;
3289 for (Node* child = node->firstChild(); child; child = child->nextSibling()) {
3290 if (child->renderer()) {
3291 // Find out where the last render sibling is located within m_children.
3292 AccessibilityObject* childObject = axObjectCache()->get(child->renderer());
3293 if (childObject && childObject->accessibilityIsIgnored()) {
3294 auto& children = childObject->children();
3295 if (children.size())
3296 childObject = children.last().get();
3297 else
3298 childObject = nullptr;
3299 }
3300
3301 if (childObject)
3302 insertionIndex = m_children.find(childObject) + 1;
3303 continue;
3304 }
3305
3306 if (!isNodeAriaVisible(child))
3307 continue;
3308
3309 unsigned previousSize = m_children.size();
3310 if (insertionIndex > previousSize)
3311 insertionIndex = previousSize;
3312
3313 insertChild(axObjectCache()->getOrCreate(child), insertionIndex);
3314 insertionIndex += (m_children.size() - previousSize);
3315 }
3316}
3317
3318void AccessibilityRenderObject::updateRoleAfterChildrenCreation()
3319{
3320 // If a menu does not have valid menuitem children, it should not be exposed as a menu.
3321 auto role = roleValue();
3322 if (role == AccessibilityRole::Menu) {
3323 // Elements marked as menus must have at least one menu item child.
3324 size_t menuItemCount = 0;
3325 for (const auto& child : children()) {
3326 if (child->isMenuItem()) {
3327 menuItemCount++;
3328 break;
3329 }
3330 }
3331
3332 if (!menuItemCount)
3333 m_role = AccessibilityRole::Group;
3334 }
3335 if (role == AccessibilityRole::SVGRoot && !hasChildren())
3336 m_role = AccessibilityRole::Image;
3337}
3338
3339void AccessibilityRenderObject::addChildren()
3340{
3341 // If the need to add more children in addition to existing children arises,
3342 // childrenChanged should have been called, leaving the object with no children.
3343 ASSERT(!m_haveChildren);
3344
3345 m_haveChildren = true;
3346
3347 if (!canHaveChildren())
3348 return;
3349
3350 for (RefPtr<AccessibilityObject> obj = firstChild(); obj; obj = obj->nextSibling())
3351 addChild(obj.get());
3352
3353 m_subtreeDirty = false;
3354
3355 addHiddenChildren();
3356 addAttachmentChildren();
3357 addImageMapChildren();
3358 addTextFieldChildren();
3359 addCanvasChildren();
3360 addRemoteSVGChildren();
3361
3362#if PLATFORM(COCOA)
3363 updateAttachmentViewParents();
3364#endif
3365
3366 updateRoleAfterChildrenCreation();
3367}
3368
3369bool AccessibilityRenderObject::canHaveChildren() const
3370{
3371 if (!m_renderer)
3372 return false;
3373
3374 return AccessibilityNodeObject::canHaveChildren();
3375}
3376
3377const String AccessibilityRenderObject::liveRegionStatus() const
3378{
3379 const AtomicString& liveRegionStatus = getAttribute(aria_liveAttr);
3380 // These roles have implicit live region status.
3381 if (liveRegionStatus.isEmpty())
3382 return defaultLiveRegionStatusForRole(roleValue());
3383
3384 return liveRegionStatus;
3385}
3386
3387const String AccessibilityRenderObject::liveRegionRelevant() const
3388{
3389 static NeverDestroyed<const AtomicString> defaultLiveRegionRelevant("additions text", AtomicString::ConstructFromLiteral);
3390 const AtomicString& relevant = getAttribute(aria_relevantAttr);
3391
3392 // Default aria-relevant = "additions text".
3393 if (relevant.isEmpty())
3394 return "additions text";
3395
3396 return relevant;
3397}
3398
3399bool AccessibilityRenderObject::liveRegionAtomic() const
3400{
3401 const AtomicString& atomic = getAttribute(aria_atomicAttr);
3402 if (equalLettersIgnoringASCIICase(atomic, "true"))
3403 return true;
3404 if (equalLettersIgnoringASCIICase(atomic, "false"))
3405 return false;
3406
3407 // WAI-ARIA "alert" and "status" roles have an implicit aria-atomic value of true.
3408 switch (roleValue()) {
3409 case AccessibilityRole::ApplicationAlert:
3410 case AccessibilityRole::ApplicationStatus:
3411 return true;
3412 default:
3413 return false;
3414 }
3415}
3416
3417bool AccessibilityRenderObject::isBusy() const
3418{
3419 return elementAttributeValue(aria_busyAttr);
3420}
3421
3422bool AccessibilityRenderObject::canHaveSelectedChildren() const
3423{
3424 switch (roleValue()) {
3425 // These roles are containers whose children support aria-selected:
3426 case AccessibilityRole::Grid:
3427 case AccessibilityRole::ListBox:
3428 case AccessibilityRole::TabList:
3429 case AccessibilityRole::Tree:
3430 case AccessibilityRole::TreeGrid:
3431 case AccessibilityRole::List:
3432 // These roles are containers whose children are treated as selected by assistive
3433 // technologies. We can get the "selected" item via aria-activedescendant or the
3434 // focused element.
3435 case AccessibilityRole::Menu:
3436 case AccessibilityRole::MenuBar:
3437 return true;
3438 default:
3439 return false;
3440 }
3441}
3442
3443void AccessibilityRenderObject::ariaSelectedRows(AccessibilityChildrenVector& result)
3444{
3445 // Determine which rows are selected.
3446 bool isMulti = isMultiSelectable();
3447
3448 // Prefer active descendant over aria-selected.
3449 AccessibilityObject* activeDesc = activeDescendant();
3450 if (activeDesc && (activeDesc->isTreeItem() || activeDesc->isTableRow())) {
3451 result.append(activeDesc);
3452 if (!isMulti)
3453 return;
3454 }
3455
3456 // Get all the rows.
3457 auto rowsIteration = [&](auto& rows) {
3458 for (auto& row : rows) {
3459 if (row->isSelected() || row->isActiveDescendantOfFocusedContainer()) {
3460 result.append(row);
3461 if (!isMulti)
3462 break;
3463 }
3464 }
3465 };
3466 if (isTree()) {
3467 AccessibilityChildrenVector allRows;
3468 ariaTreeRows(allRows);
3469 rowsIteration(allRows);
3470 } else if (is<AccessibilityTable>(*this)) {
3471 auto& thisTable = downcast<AccessibilityTable>(*this);
3472 if (thisTable.isExposableThroughAccessibility() && thisTable.supportsSelectedRows())
3473 rowsIteration(thisTable.rows());
3474 }
3475}
3476
3477void AccessibilityRenderObject::ariaListboxSelectedChildren(AccessibilityChildrenVector& result)
3478{
3479 bool isMulti = isMultiSelectable();
3480
3481 for (const auto& child : children()) {
3482 // Every child should have aria-role option, and if so, check for selected attribute/state.
3483 if (child->ariaRoleAttribute() == AccessibilityRole::ListBoxOption && (child->isSelected() || child->isActiveDescendantOfFocusedContainer())) {
3484 result.append(child);
3485 if (!isMulti)
3486 return;
3487 }
3488 }
3489}
3490
3491void AccessibilityRenderObject::selectedChildren(AccessibilityChildrenVector& result)
3492{
3493 ASSERT(result.isEmpty());
3494
3495 if (!canHaveSelectedChildren())
3496 return;
3497
3498 switch (roleValue()) {
3499 case AccessibilityRole::ListBox:
3500 // native list boxes would be AccessibilityListBoxes, so only check for aria list boxes
3501 ariaListboxSelectedChildren(result);
3502 return;
3503 case AccessibilityRole::Grid:
3504 case AccessibilityRole::Tree:
3505 case AccessibilityRole::TreeGrid:
3506 ariaSelectedRows(result);
3507 return;
3508 case AccessibilityRole::TabList:
3509 if (AccessibilityObject* selectedTab = selectedTabItem())
3510 result.append(selectedTab);
3511 return;
3512 case AccessibilityRole::List:
3513 if (auto* selectedListItemChild = selectedListItem())
3514 result.append(selectedListItemChild);
3515 return;
3516 case AccessibilityRole::Menu:
3517 case AccessibilityRole::MenuBar:
3518 if (AccessibilityObject* descendant = activeDescendant()) {
3519 result.append(descendant);
3520 return;
3521 }
3522 if (AccessibilityObject* focusedElement = static_cast<AccessibilityObject*>(focusedUIElement())) {
3523 result.append(focusedElement);
3524 return;
3525 }
3526 return;
3527 default:
3528 ASSERT_NOT_REACHED();
3529 }
3530}
3531
3532void AccessibilityRenderObject::ariaListboxVisibleChildren(AccessibilityChildrenVector& result)
3533{
3534 if (!hasChildren())
3535 addChildren();
3536
3537 for (const auto& child : children()) {
3538 if (child->isOffScreen())
3539 result.append(child);
3540 }
3541}
3542
3543void AccessibilityRenderObject::visibleChildren(AccessibilityChildrenVector& result)
3544{
3545 ASSERT(result.isEmpty());
3546
3547 // only listboxes are asked for their visible children.
3548 if (ariaRoleAttribute() != AccessibilityRole::ListBox) {
3549 // native list boxes would be AccessibilityListBoxes, so only check for aria list boxes
3550 ASSERT_NOT_REACHED();
3551 return;
3552 }
3553 return ariaListboxVisibleChildren(result);
3554}
3555
3556void AccessibilityRenderObject::tabChildren(AccessibilityChildrenVector& result)
3557{
3558 ASSERT(roleValue() == AccessibilityRole::TabList);
3559
3560 for (const auto& child : children()) {
3561 if (child->isTabItem())
3562 result.append(child);
3563 }
3564}
3565
3566const String& AccessibilityRenderObject::actionVerb() const
3567{
3568#if !PLATFORM(IOS_FAMILY)
3569 // FIXME: Need to add verbs for select elements.
3570 static NeverDestroyed<const String> buttonAction(AXButtonActionVerb());
3571 static NeverDestroyed<const String> textFieldAction(AXTextFieldActionVerb());
3572 static NeverDestroyed<const String> radioButtonAction(AXRadioButtonActionVerb());
3573 static NeverDestroyed<const String> checkedCheckBoxAction(AXUncheckedCheckBoxActionVerb());
3574 static NeverDestroyed<const String> uncheckedCheckBoxAction(AXUncheckedCheckBoxActionVerb());
3575 static NeverDestroyed<const String> linkAction(AXLinkActionVerb());
3576
3577 switch (roleValue()) {
3578 case AccessibilityRole::Button:
3579 case AccessibilityRole::ToggleButton:
3580 return buttonAction;
3581 case AccessibilityRole::TextField:
3582 case AccessibilityRole::TextArea:
3583 return textFieldAction;
3584 case AccessibilityRole::RadioButton:
3585 return radioButtonAction;
3586 case AccessibilityRole::CheckBox:
3587 return isChecked() ? checkedCheckBoxAction : uncheckedCheckBoxAction;
3588 case AccessibilityRole::Link:
3589 case AccessibilityRole::WebCoreLink:
3590 return linkAction;
3591 default:
3592 return nullAtom();
3593 }
3594#else
3595 return nullAtom();
3596#endif
3597}
3598
3599void AccessibilityRenderObject::setAccessibleName(const AtomicString& name)
3600{
3601 // Setting the accessible name can store the value in the DOM
3602 if (!m_renderer)
3603 return;
3604
3605 Node* node = nullptr;
3606 // For web areas, set the aria-label on the HTML element.
3607 if (isWebArea())
3608 node = m_renderer->document().documentElement();
3609 else
3610 node = m_renderer->node();
3611
3612 if (is<Element>(node))
3613 downcast<Element>(*node).setAttribute(aria_labelAttr, name);
3614}
3615
3616static bool isLinkable(const AccessibilityRenderObject& object)
3617{
3618 if (!object.renderer())
3619 return false;
3620
3621 // See https://wiki.mozilla.org/Accessibility/AT-Windows-API for the elements
3622 // Mozilla considers linkable.
3623 return object.isLink() || object.isImage() || object.renderer()->isText();
3624}
3625
3626String AccessibilityRenderObject::stringValueForMSAA() const
3627{
3628 if (isLinkable(*this)) {
3629 Element* anchor = anchorElement();
3630 if (is<HTMLAnchorElement>(anchor))
3631 return downcast<HTMLAnchorElement>(*anchor).href();
3632 }
3633
3634 return stringValue();
3635}
3636
3637bool AccessibilityRenderObject::isLinked() const
3638{
3639 if (!isLinkable(*this))
3640 return false;
3641
3642 Element* anchor = anchorElement();
3643 if (!is<HTMLAnchorElement>(anchor))
3644 return false;
3645
3646 return !downcast<HTMLAnchorElement>(*anchor).href().isEmpty();
3647}
3648
3649bool AccessibilityRenderObject::hasBoldFont() const
3650{
3651 if (!m_renderer)
3652 return false;
3653
3654 return isFontWeightBold(m_renderer->style().fontDescription().weight());
3655}
3656
3657bool AccessibilityRenderObject::hasItalicFont() const
3658{
3659 if (!m_renderer)
3660 return false;
3661
3662 return isItalic(m_renderer->style().fontDescription().italic());
3663}
3664
3665bool AccessibilityRenderObject::hasPlainText() const
3666{
3667 if (!m_renderer)
3668 return false;
3669
3670 if (!canHavePlainText())
3671 return false;
3672
3673 const RenderStyle& style = m_renderer->style();
3674 return style.fontDescription().weight() == normalWeightValue()
3675 && !isItalic(style.fontDescription().italic())
3676 && style.textDecorationsInEffect().isEmpty();
3677}
3678
3679bool AccessibilityRenderObject::hasSameFont(RenderObject* renderer) const
3680{
3681 if (!m_renderer || !renderer)
3682 return false;
3683
3684 return m_renderer->style().fontDescription().families() == renderer->style().fontDescription().families();
3685}
3686
3687#if ENABLE(APPLE_PAY)
3688bool AccessibilityRenderObject::isApplePayButton() const
3689{
3690 if (!m_renderer)
3691 return false;
3692 return m_renderer->style().appearance() == ApplePayButtonPart;
3693}
3694
3695ApplePayButtonType AccessibilityRenderObject::applePayButtonType() const
3696{
3697 if (!m_renderer)
3698 return ApplePayButtonType::Plain;
3699 return m_renderer->style().applePayButtonType();
3700}
3701#endif
3702
3703bool AccessibilityRenderObject::hasSameFontColor(RenderObject* renderer) const
3704{
3705 if (!m_renderer || !renderer)
3706 return false;
3707
3708 return m_renderer->style().visitedDependentColor(CSSPropertyColor) == renderer->style().visitedDependentColor(CSSPropertyColor);
3709}
3710
3711bool AccessibilityRenderObject::hasSameStyle(RenderObject* renderer) const
3712{
3713 if (!m_renderer || !renderer)
3714 return false;
3715
3716 return m_renderer->style() == renderer->style();
3717}
3718
3719bool AccessibilityRenderObject::hasUnderline() const
3720{
3721 if (!m_renderer)
3722 return false;
3723
3724 return m_renderer->style().textDecorationsInEffect().contains(TextDecoration::Underline);
3725}
3726
3727String AccessibilityRenderObject::nameForMSAA() const
3728{
3729 if (m_renderer && m_renderer->isText())
3730 return textUnderElement();
3731
3732 return title();
3733}
3734
3735static bool shouldReturnTagNameAsRoleForMSAA(const Element& element)
3736{
3737 return element.hasTagName(abbrTag) || element.hasTagName(acronymTag)
3738 || element.hasTagName(blockquoteTag) || element.hasTagName(ddTag)
3739 || element.hasTagName(dlTag) || element.hasTagName(dtTag)
3740 || element.hasTagName(formTag) || element.hasTagName(frameTag)
3741 || element.hasTagName(h1Tag) || element.hasTagName(h2Tag)
3742 || element.hasTagName(h3Tag) || element.hasTagName(h4Tag)
3743 || element.hasTagName(h5Tag) || element.hasTagName(h6Tag)
3744 || element.hasTagName(iframeTag) || element.hasTagName(qTag)
3745 || element.hasTagName(tbodyTag) || element.hasTagName(tfootTag)
3746 || element.hasTagName(theadTag);
3747}
3748
3749String AccessibilityRenderObject::stringRoleForMSAA() const
3750{
3751 if (!m_renderer)
3752 return String();
3753
3754 Node* node = m_renderer->node();
3755 if (!is<Element>(node))
3756 return String();
3757
3758 Element& element = downcast<Element>(*node);
3759 if (!shouldReturnTagNameAsRoleForMSAA(element))
3760 return String();
3761
3762 return element.tagName();
3763}
3764
3765String AccessibilityRenderObject::positionalDescriptionForMSAA() const
3766{
3767 // See "positional descriptions",
3768 // https://wiki.mozilla.org/Accessibility/AT-Windows-API
3769 if (isHeading())
3770 return makeString('L', headingLevel());
3771
3772 // FIXME: Add positional descriptions for other elements.
3773 return String();
3774}
3775
3776String AccessibilityRenderObject::descriptionForMSAA() const
3777{
3778 String description = positionalDescriptionForMSAA();
3779 if (!description.isEmpty())
3780 return description;
3781
3782 description = accessibilityDescription();
3783 if (!description.isEmpty()) {
3784 // From the Mozilla MSAA implementation:
3785 // "Signal to screen readers that this description is speakable and is not
3786 // a formatted positional information description. Don't localize the
3787 // 'Description: ' part of this string, it will be parsed out by assistive
3788 // technologies."
3789 return "Description: " + description;
3790 }
3791
3792 return String();
3793}
3794
3795static AccessibilityRole msaaRoleForRenderer(const RenderObject* renderer)
3796{
3797 if (!renderer)
3798 return AccessibilityRole::Unknown;
3799
3800 if (is<RenderText>(*renderer))
3801 return AccessibilityRole::EditableText;
3802
3803 if (is<RenderListItem>(*renderer))
3804 return AccessibilityRole::ListItem;
3805
3806 return AccessibilityRole::Unknown;
3807}
3808
3809AccessibilityRole AccessibilityRenderObject::roleValueForMSAA() const
3810{
3811 if (m_roleForMSAA != AccessibilityRole::Unknown)
3812 return m_roleForMSAA;
3813
3814 m_roleForMSAA = msaaRoleForRenderer(renderer());
3815
3816 if (m_roleForMSAA == AccessibilityRole::Unknown)
3817 m_roleForMSAA = roleValue();
3818
3819 return m_roleForMSAA;
3820}
3821
3822String AccessibilityRenderObject::passwordFieldValue() const
3823{
3824 ASSERT(isPasswordField());
3825
3826 // Look for the RenderText object in the RenderObject tree for this input field.
3827 RenderObject* renderer = node()->renderer();
3828 while (renderer && !is<RenderText>(renderer))
3829 renderer = downcast<RenderElement>(*renderer).firstChild();
3830
3831 if (!is<RenderText>(renderer))
3832 return String();
3833
3834 // Return the text that is actually being rendered in the input field.
3835 return downcast<RenderText>(*renderer).textWithoutConvertingBackslashToYenSymbol();
3836}
3837
3838ScrollableArea* AccessibilityRenderObject::getScrollableAreaIfScrollable() const
3839{
3840 // If the parent is a scroll view, then this object isn't really scrollable, the parent ScrollView should handle the scrolling.
3841 if (parentObject() && parentObject()->isAccessibilityScrollView())
3842 return nullptr;
3843
3844 if (!is<RenderBox>(renderer()))
3845 return nullptr;
3846
3847 auto& box = downcast<RenderBox>(*m_renderer);
3848 if (!box.canBeScrolledAndHasScrollableArea())
3849 return nullptr;
3850
3851 return box.layer();
3852}
3853
3854void AccessibilityRenderObject::scrollTo(const IntPoint& point) const
3855{
3856 if (!is<RenderBox>(renderer()))
3857 return;
3858
3859 auto& box = downcast<RenderBox>(*m_renderer);
3860 if (!box.canBeScrolledAndHasScrollableArea())
3861 return;
3862
3863 // FIXME: is point a ScrollOffset or ScrollPosition? Test in RTL overflow.
3864 box.layer()->scrollToOffset(point);
3865}
3866
3867#if ENABLE(MATHML)
3868bool AccessibilityRenderObject::isIgnoredElementWithinMathTree() const
3869{
3870 // We ignore anonymous boxes inserted into RenderMathMLBlocks to honor CSS rules.
3871 // See https://www.w3.org/TR/css3-box/#block-level0
3872 return m_renderer && m_renderer->isAnonymous() && m_renderer->parent() && is<RenderMathMLBlock>(m_renderer->parent());
3873}
3874#endif
3875
3876} // namespace WebCore
3877