• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 Computer, 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 "core/accessibility/AXRenderObject.h"
31 
32 #include "bindings/v8/ExceptionStatePlaceholder.h"
33 #include "core/accessibility/AXImageMapLink.h"
34 #include "core/accessibility/AXInlineTextBox.h"
35 #include "core/accessibility/AXObjectCache.h"
36 #include "core/accessibility/AXSVGRoot.h"
37 #include "core/accessibility/AXSpinButton.h"
38 #include "core/accessibility/AXTable.h"
39 #include "core/dom/ElementTraversal.h"
40 #include "core/dom/shadow/ShadowRoot.h"
41 #include "core/editing/FrameSelection.h"
42 #include "core/editing/RenderedPosition.h"
43 #include "core/editing/TextIterator.h"
44 #include "core/editing/VisibleUnits.h"
45 #include "core/editing/htmlediting.h"
46 #include "core/frame/LocalFrame.h"
47 #include "core/html/HTMLImageElement.h"
48 #include "core/html/HTMLLabelElement.h"
49 #include "core/html/HTMLOptionElement.h"
50 #include "core/html/HTMLSelectElement.h"
51 #include "core/html/HTMLTextAreaElement.h"
52 #include "core/html/shadow/ShadowElementNames.h"
53 #include "core/loader/ProgressTracker.h"
54 #include "core/page/Page.h"
55 #include "core/rendering/HitTestResult.h"
56 #include "core/rendering/RenderFieldset.h"
57 #include "core/rendering/RenderFileUploadControl.h"
58 #include "core/rendering/RenderHTMLCanvas.h"
59 #include "core/rendering/RenderImage.h"
60 #include "core/rendering/RenderInline.h"
61 #include "core/rendering/RenderLayer.h"
62 #include "core/rendering/RenderListMarker.h"
63 #include "core/rendering/RenderMenuList.h"
64 #include "core/rendering/RenderTextControlSingleLine.h"
65 #include "core/rendering/RenderTextFragment.h"
66 #include "core/rendering/RenderView.h"
67 #include "core/rendering/RenderWidget.h"
68 #include "core/svg/SVGDocumentExtensions.h"
69 #include "core/svg/SVGSVGElement.h"
70 #include "core/svg/graphics/SVGImage.h"
71 #include "platform/text/PlatformLocale.h"
72 #include "wtf/StdLibExtras.h"
73 
74 using blink::WebLocalizedString;
75 
76 namespace WebCore {
77 
78 using namespace HTMLNames;
79 
firstChildInContinuation(const RenderInline & renderer)80 static inline RenderObject* firstChildInContinuation(const RenderInline& renderer)
81 {
82     RenderBoxModelObject* r = renderer.continuation();
83 
84     while (r) {
85         if (r->isRenderBlock())
86             return r;
87         if (RenderObject* child = r->slowFirstChild())
88             return child;
89         r = toRenderInline(r)->continuation();
90     }
91 
92     return 0;
93 }
94 
isInlineWithContinuation(RenderObject * object)95 static inline bool isInlineWithContinuation(RenderObject* object)
96 {
97     if (!object->isBoxModelObject())
98         return false;
99 
100     RenderBoxModelObject* renderer = toRenderBoxModelObject(object);
101     if (!renderer->isRenderInline())
102         return false;
103 
104     return toRenderInline(renderer)->continuation();
105 }
106 
firstChildConsideringContinuation(RenderObject * renderer)107 static inline RenderObject* firstChildConsideringContinuation(RenderObject* renderer)
108 {
109     RenderObject* firstChild = renderer->slowFirstChild();
110 
111     if (!firstChild && isInlineWithContinuation(renderer))
112         firstChild = firstChildInContinuation(toRenderInline(*renderer));
113 
114     return firstChild;
115 }
116 
startOfContinuations(RenderObject * r)117 static inline RenderInline* startOfContinuations(RenderObject* r)
118 {
119     if (r->isInlineElementContinuation()) {
120         return toRenderInline(r->node()->renderer());
121     }
122 
123     // Blocks with a previous continuation always have a next continuation
124     if (r->isRenderBlock() && toRenderBlock(r)->inlineElementContinuation())
125         return toRenderInline(toRenderBlock(r)->inlineElementContinuation()->node()->renderer());
126 
127     return 0;
128 }
129 
endOfContinuations(RenderObject * renderer)130 static inline RenderObject* endOfContinuations(RenderObject* renderer)
131 {
132     RenderObject* prev = renderer;
133     RenderObject* cur = renderer;
134 
135     if (!cur->isRenderInline() && !cur->isRenderBlock())
136         return renderer;
137 
138     while (cur) {
139         prev = cur;
140         if (cur->isRenderInline()) {
141             cur = toRenderInline(cur)->inlineElementContinuation();
142             ASSERT(cur || !toRenderInline(prev)->continuation());
143         } else {
144             cur = toRenderBlock(cur)->inlineElementContinuation();
145         }
146     }
147 
148     return prev;
149 }
150 
lastChildHasContinuation(RenderObject * renderer)151 static inline bool lastChildHasContinuation(RenderObject* renderer)
152 {
153     RenderObject* lastChild = renderer->slowLastChild();
154     return lastChild && isInlineWithContinuation(lastChild);
155 }
156 
nextContinuation(RenderObject * renderer)157 static RenderBoxModelObject* nextContinuation(RenderObject* renderer)
158 {
159     ASSERT(renderer);
160     if (renderer->isRenderInline() && !renderer->isReplaced())
161         return toRenderInline(renderer)->continuation();
162     if (renderer->isRenderBlock())
163         return toRenderBlock(renderer)->inlineElementContinuation();
164     return 0;
165 }
166 
AXRenderObject(RenderObject * renderer)167 AXRenderObject::AXRenderObject(RenderObject* renderer)
168     : AXNodeObject(renderer->node())
169     , m_renderer(renderer)
170     , m_cachedElementRectDirty(true)
171 {
172 #ifndef NDEBUG
173     m_renderer->setHasAXObject(true);
174 #endif
175 }
176 
create(RenderObject * renderer)177 PassRefPtr<AXRenderObject> AXRenderObject::create(RenderObject* renderer)
178 {
179     return adoptRef(new AXRenderObject(renderer));
180 }
181 
~AXRenderObject()182 AXRenderObject::~AXRenderObject()
183 {
184     ASSERT(isDetached());
185 }
186 
elementRect() const187 LayoutRect AXRenderObject::elementRect() const
188 {
189     if (!m_explicitElementRect.isEmpty())
190         return m_explicitElementRect;
191     if (!m_renderer)
192         return LayoutRect();
193     if (!m_renderer->isBox())
194         return computeElementRect();
195 
196     for (const AXObject* obj = this; obj; obj = obj->parentObject()) {
197         if (obj->isAXRenderObject())
198             toAXRenderObject(obj)->checkCachedElementRect();
199     }
200     for (const AXObject* obj = this; obj; obj = obj->parentObject()) {
201         if (obj->isAXRenderObject())
202             toAXRenderObject(obj)->updateCachedElementRect();
203     }
204 
205     return m_cachedElementRect;
206 }
207 
setRenderer(RenderObject * renderer)208 void AXRenderObject::setRenderer(RenderObject* renderer)
209 {
210     m_renderer = renderer;
211     setNode(renderer->node());
212 }
213 
renderBoxModelObject() const214 RenderBoxModelObject* AXRenderObject::renderBoxModelObject() const
215 {
216     if (!m_renderer || !m_renderer->isBoxModelObject())
217         return 0;
218     return toRenderBoxModelObject(m_renderer);
219 }
220 
topDocument() const221 Document* AXRenderObject::topDocument() const
222 {
223     if (!document())
224         return 0;
225     return &document()->topDocument();
226 }
227 
shouldNotifyActiveDescendant() const228 bool AXRenderObject::shouldNotifyActiveDescendant() const
229 {
230     // We want to notify that the combo box has changed its active descendant,
231     // but we do not want to change the focus, because focus should remain with the combo box.
232     if (isComboBox())
233         return true;
234 
235     return shouldFocusActiveDescendant();
236 }
237 
getScrollableAreaIfScrollable() const238 ScrollableArea* AXRenderObject::getScrollableAreaIfScrollable() const
239 {
240     // If the parent is a scroll view, then this object isn't really scrollable, the parent ScrollView should handle the scrolling.
241     if (parentObject() && parentObject()->isAXScrollView())
242         return 0;
243 
244     if (!m_renderer || !m_renderer->isBox())
245         return 0;
246 
247     RenderBox* box = toRenderBox(m_renderer);
248     if (!box->canBeScrolledAndHasScrollableArea())
249         return 0;
250 
251     return box->scrollableArea();
252 }
253 
determineAccessibilityRole()254 AccessibilityRole AXRenderObject::determineAccessibilityRole()
255 {
256     if (!m_renderer)
257         return UnknownRole;
258 
259     m_ariaRole = determineAriaRoleAttribute();
260 
261     Node* node = m_renderer->node();
262     AccessibilityRole ariaRole = ariaRoleAttribute();
263     if (ariaRole != UnknownRole)
264         return ariaRole;
265 
266     RenderBoxModelObject* cssBox = renderBoxModelObject();
267 
268     if (node && node->isLink()) {
269         if (cssBox && cssBox->isImage())
270             return ImageMapRole;
271         return LinkRole;
272     }
273     if (cssBox && cssBox->isListItem())
274         return ListItemRole;
275     if (m_renderer->isListMarker())
276         return ListMarkerRole;
277     if (isHTMLButtonElement(node))
278         return buttonRoleType();
279     if (isHTMLLegendElement(node))
280         return LegendRole;
281     if (m_renderer->isText())
282         return StaticTextRole;
283     if (cssBox && cssBox->isImage()) {
284         if (isHTMLInputElement(node))
285             return ariaHasPopup() ? PopUpButtonRole : ButtonRole;
286         if (isSVGImage())
287             return SVGRootRole;
288         return ImageRole;
289     }
290 
291     // Note: if JavaScript is disabled, the renderer won't be a RenderHTMLCanvas.
292     if (isHTMLCanvasElement(node) && m_renderer->isCanvas())
293         return CanvasRole;
294 
295     if (cssBox && cssBox->isRenderView())
296         return WebAreaRole;
297 
298     if (cssBox && cssBox->isTextField())
299         return TextFieldRole;
300 
301     if (cssBox && cssBox->isTextArea())
302         return TextAreaRole;
303 
304     if (isHTMLInputElement(node)) {
305         HTMLInputElement& input = toHTMLInputElement(*node);
306         if (input.isCheckbox())
307             return CheckBoxRole;
308         if (input.isRadioButton())
309             return RadioButtonRole;
310         if (input.isTextButton())
311             return buttonRoleType();
312 
313         const AtomicString& type = input.getAttribute(typeAttr);
314         if (equalIgnoringCase(type, "color"))
315             return ColorWellRole;
316     }
317 
318     if (isFileUploadButton())
319         return ButtonRole;
320 
321     if (cssBox && cssBox->isMenuList())
322         return PopUpButtonRole;
323 
324     if (headingLevel())
325         return HeadingRole;
326 
327     if (m_renderer->isSVGImage())
328         return ImageRole;
329     if (m_renderer->isSVGRoot())
330         return SVGRootRole;
331 
332     if (node && node->hasTagName(ddTag))
333         return DescriptionListDetailRole;
334 
335     if (node && node->hasTagName(dtTag))
336         return DescriptionListTermRole;
337 
338     if (node && (node->hasTagName(rpTag) || node->hasTagName(rtTag)))
339         return AnnotationRole;
340 
341     // Table sections should be ignored.
342     if (m_renderer->isTableSection())
343         return IgnoredRole;
344 
345     if (m_renderer->isHR())
346         return HorizontalRuleRole;
347 
348     if (isHTMLParagraphElement(node))
349         return ParagraphRole;
350 
351     if (isHTMLLabelElement(node))
352         return LabelRole;
353 
354     if (isHTMLDivElement(node))
355         return DivRole;
356 
357     if (isHTMLFormElement(node))
358         return FormRole;
359 
360     if (node && node->hasTagName(articleTag))
361         return ArticleRole;
362 
363     if (node && node->hasTagName(mainTag))
364         return MainRole;
365 
366     if (node && node->hasTagName(navTag))
367         return NavigationRole;
368 
369     if (node && node->hasTagName(asideTag))
370         return ComplementaryRole;
371 
372     if (node && node->hasTagName(sectionTag))
373         return RegionRole;
374 
375     if (node && node->hasTagName(addressTag))
376         return ContentInfoRole;
377 
378     if (node && node->hasTagName(dialogTag))
379         return DialogRole;
380 
381     // The HTML element should not be exposed as an element. That's what the RenderView element does.
382     if (isHTMLHtmlElement(node))
383         return IgnoredRole;
384 
385     if (node && node->hasTagName(iframeTag))
386         return IframeRole;
387 
388     if (isEmbeddedObject())
389         return EmbeddedObjectRole;
390 
391     // There should only be one banner/contentInfo per page. If header/footer are being used within an article or section
392     // then it should not be exposed as whole page's banner/contentInfo
393     if (node && node->hasTagName(headerTag) && !isDescendantOfElementType(articleTag) && !isDescendantOfElementType(sectionTag))
394         return BannerRole;
395     if (node && node->hasTagName(footerTag) && !isDescendantOfElementType(articleTag) && !isDescendantOfElementType(sectionTag))
396         return FooterRole;
397 
398     if (isHTMLAnchorElement(node) && isClickable())
399         return LinkRole;
400 
401     if (m_renderer->isRenderBlockFlow())
402         return GroupRole;
403 
404     // If the element does not have role, but it has ARIA attributes, accessibility should fallback to exposing it as a group.
405     if (supportsARIAAttributes())
406         return GroupRole;
407 
408     return UnknownRole;
409 }
410 
init()411 void AXRenderObject::init()
412 {
413     AXNodeObject::init();
414 }
415 
detach()416 void AXRenderObject::detach()
417 {
418     AXNodeObject::detach();
419 
420     detachRemoteSVGRoot();
421 
422 #ifndef NDEBUG
423     if (m_renderer)
424         m_renderer->setHasAXObject(false);
425 #endif
426     m_renderer = 0;
427 }
428 
429 //
430 // Check object role or purpose.
431 //
432 
isAttachment() const433 bool AXRenderObject::isAttachment() const
434 {
435     RenderBoxModelObject* renderer = renderBoxModelObject();
436     if (!renderer)
437         return false;
438     // Widgets are the replaced elements that we represent to AX as attachments
439     bool isWidget = renderer->isWidget();
440     ASSERT(!isWidget || (renderer->isReplaced() && !isImage()));
441     return isWidget;
442 }
443 
isFileUploadButton() const444 bool AXRenderObject::isFileUploadButton() const
445 {
446     if (m_renderer && isHTMLInputElement(m_renderer->node())) {
447         HTMLInputElement& input = toHTMLInputElement(*m_renderer->node());
448         return input.isFileUpload();
449     }
450 
451     return false;
452 }
453 
isLinkable(const AXObject & object)454 static bool isLinkable(const AXObject& object)
455 {
456     if (!object.renderer())
457         return false;
458 
459     // See https://wiki.mozilla.org/Accessibility/AT-Windows-API for the elements
460     // Mozilla considers linkable.
461     return object.isLink() || object.isImage() || object.renderer()->isText();
462 }
463 
isLinked() const464 bool AXRenderObject::isLinked() const
465 {
466     if (!isLinkable(*this))
467         return false;
468 
469     Element* anchor = anchorElement();
470     if (!isHTMLAnchorElement(anchor))
471         return false;
472 
473     return !toHTMLAnchorElement(*anchor).href().isEmpty();
474 }
475 
isLoaded() const476 bool AXRenderObject::isLoaded() const
477 {
478     return !m_renderer->document().parser();
479 }
480 
isOffScreen() const481 bool AXRenderObject::isOffScreen() const
482 {
483     ASSERT(m_renderer);
484     IntRect contentRect = pixelSnappedIntRect(m_renderer->absoluteClippedOverflowRect());
485     FrameView* view = m_renderer->frame()->view();
486     IntRect viewRect = view->visibleContentRect();
487     viewRect.intersect(contentRect);
488     return viewRect.isEmpty();
489 }
490 
isReadOnly() const491 bool AXRenderObject::isReadOnly() const
492 {
493     ASSERT(m_renderer);
494 
495     if (isWebArea()) {
496         Document& document = m_renderer->document();
497         HTMLElement* body = document.body();
498         if (body && body->rendererIsEditable())
499             return false;
500 
501         return !document.rendererIsEditable();
502     }
503 
504     return AXNodeObject::isReadOnly();
505 }
506 
isVisited() const507 bool AXRenderObject::isVisited() const
508 {
509     // FIXME: Is it a privacy violation to expose visited information to accessibility APIs?
510     return m_renderer->style()->isLink() && m_renderer->style()->insideLink() == InsideVisitedLink;
511 }
512 
513 //
514 // Check object state.
515 //
516 
isFocused() const517 bool AXRenderObject::isFocused() const
518 {
519     if (!m_renderer)
520         return false;
521 
522     Document& document = m_renderer->document();
523     Element* focusedElement = document.focusedElement();
524     if (!focusedElement)
525         return false;
526 
527     // A web area is represented by the Document node in the DOM tree, which isn't focusable.
528     // Check instead if the frame's selection controller is focused
529     if (focusedElement == m_renderer->node()
530         || (roleValue() == WebAreaRole && document.frame()->selection().isFocusedAndActive()))
531         return true;
532 
533     return false;
534 }
535 
isSelected() const536 bool AXRenderObject::isSelected() const
537 {
538     if (!m_renderer)
539         return false;
540 
541     Node* node = m_renderer->node();
542     if (!node)
543         return false;
544 
545     const AtomicString& ariaSelected = getAttribute(aria_selectedAttr);
546     if (equalIgnoringCase(ariaSelected, "true"))
547         return true;
548 
549     if (isTabItem() && isTabItemSelected())
550         return true;
551 
552     return false;
553 }
554 
555 //
556 // Whether objects are ignored, i.e. not included in the tree.
557 //
558 
defaultObjectInclusion() const559 AXObjectInclusion AXRenderObject::defaultObjectInclusion() const
560 {
561     // The following cases can apply to any element that's a subclass of AXRenderObject.
562 
563     if (!m_renderer)
564         return IgnoreObject;
565 
566     if (m_renderer->style()->visibility() != VISIBLE) {
567         // aria-hidden is meant to override visibility as the determinant in AX hierarchy inclusion.
568         if (equalIgnoringCase(getAttribute(aria_hiddenAttr), "false"))
569             return DefaultBehavior;
570 
571         return IgnoreObject;
572     }
573 
574     return AXObject::defaultObjectInclusion();
575 }
576 
computeAccessibilityIsIgnored() const577 bool AXRenderObject::computeAccessibilityIsIgnored() const
578 {
579 #ifndef NDEBUG
580     ASSERT(m_initialized);
581 #endif
582 
583     // Check first if any of the common reasons cause this element to be ignored.
584     // Then process other use cases that need to be applied to all the various roles
585     // that AXRenderObjects take on.
586     AXObjectInclusion decision = defaultObjectInclusion();
587     if (decision == IncludeObject)
588         return false;
589     if (decision == IgnoreObject)
590         return true;
591 
592     // If this element is within a parent that cannot have children, it should not be exposed.
593     if (isDescendantOfBarrenParent())
594         return true;
595 
596     if (roleValue() == IgnoredRole)
597         return true;
598 
599     if (roleValue() == PresentationalRole || inheritsPresentationalRole())
600         return true;
601 
602     // An ARIA tree can only have tree items and static text as children.
603     if (!isAllowedChildOfTree())
604         return true;
605 
606     // TODO: we should refactor this - but right now this is necessary to make
607     // sure scroll areas stay in the tree.
608     if (isAttachment())
609         return false;
610 
611     // ignore popup menu items because AppKit does
612     for (RenderObject* parent = m_renderer->parent(); parent; parent = parent->parent()) {
613         if (parent->isBoxModelObject() && toRenderBoxModelObject(parent)->isMenuList())
614             return true;
615     }
616 
617     // find out if this element is inside of a label element.
618     // if so, it may be ignored because it's the label for a checkbox or radio button
619     AXObject* controlObject = correspondingControlForLabelElement();
620     if (controlObject && !controlObject->exposesTitleUIElement() && controlObject->isCheckboxOrRadio())
621         return true;
622 
623     // NOTE: BRs always have text boxes now, so the text box check here can be removed
624     if (m_renderer->isText()) {
625         // static text beneath MenuItems and MenuButtons are just reported along with the menu item, so it's ignored on an individual level
626         AXObject* parent = parentObjectUnignored();
627         if (parent && (parent->ariaRoleAttribute() == MenuItemRole || parent->ariaRoleAttribute() == MenuButtonRole))
628             return true;
629         RenderText* renderText = toRenderText(m_renderer);
630         if (m_renderer->isBR() || !renderText->firstTextBox())
631             return true;
632 
633         // Don't ignore static text in editable text controls.
634         for (AXObject* parent = parentObject(); parent; parent = parent->parentObject()) {
635             if (parent->roleValue() == TextFieldRole || parent->roleValue() == TextAreaRole)
636                 return false;
637         }
638 
639         // text elements that are just empty whitespace should not be returned
640         // FIXME(dmazzoni): we probably shouldn't ignore this if the style is 'pre', or similar...
641         return renderText->text().impl()->containsOnlyWhitespace();
642     }
643 
644     if (isHeading())
645         return false;
646 
647     if (isLandmarkRelated())
648         return false;
649 
650     if (isLink())
651         return false;
652 
653     // all controls are accessible
654     if (isControl())
655         return false;
656 
657     if (ariaRoleAttribute() != UnknownRole)
658         return false;
659 
660     // don't ignore labels, because they serve as TitleUIElements
661     Node* node = m_renderer->node();
662     if (isHTMLLabelElement(node))
663         return false;
664 
665     // Anything that is content editable should not be ignored.
666     // However, one cannot just call node->rendererIsEditable() since that will ask if its parents
667     // are also editable. Only the top level content editable region should be exposed.
668     if (hasContentEditableAttributeSet())
669         return false;
670 
671     // List items play an important role in defining the structure of lists. They should not be ignored.
672     if (roleValue() == ListItemRole)
673         return false;
674 
675     if (roleValue() == DialogRole)
676         return false;
677 
678     // if this element has aria attributes on it, it should not be ignored.
679     if (supportsARIAAttributes())
680         return false;
681 
682     // <span> tags are inline tags and not meant to convey information if they have no other aria
683     // information on them. If we don't ignore them, they may emit signals expected to come from
684     // their parent. In addition, because included spans are GroupRole objects, and GroupRole
685     // objects are often containers with meaningful information, the inclusion of a span can have
686     // the side effect of causing the immediate parent accessible to be ignored. This is especially
687     // problematic for platforms which have distinct roles for textual block elements.
688     if (isHTMLSpanElement(node))
689         return true;
690 
691     if (m_renderer->isRenderBlockFlow() && m_renderer->childrenInline() && !canSetFocusAttribute())
692         return !toRenderBlock(m_renderer)->firstLineBox() && !mouseButtonListener();
693 
694     // ignore images seemingly used as spacers
695     if (isImage()) {
696 
697         // If the image can take focus, it should not be ignored, lest the user not be able to interact with something important.
698         if (canSetFocusAttribute())
699             return false;
700 
701         if (node && node->isElementNode()) {
702             Element* elt = toElement(node);
703             const AtomicString& alt = elt->getAttribute(altAttr);
704             // don't ignore an image that has an alt tag
705             if (!alt.string().containsOnlyWhitespace())
706                 return false;
707             // informal standard is to ignore images with zero-length alt strings
708             if (!alt.isNull())
709                 return true;
710         }
711 
712         if (isNativeImage() && m_renderer->isImage()) {
713             // check for one-dimensional image
714             RenderImage* image = toRenderImage(m_renderer);
715             if (image->height() <= 1 || image->width() <= 1)
716                 return true;
717 
718             // check whether rendered image was stretched from one-dimensional file image
719             if (image->cachedImage()) {
720                 LayoutSize imageSize = image->cachedImage()->imageSizeForRenderer(m_renderer, image->view()->zoomFactor());
721                 return imageSize.height() <= 1 || imageSize.width() <= 1;
722             }
723         }
724         return false;
725     }
726 
727     if (isCanvas()) {
728         if (canvasHasFallbackContent())
729             return false;
730         RenderHTMLCanvas* canvas = toRenderHTMLCanvas(m_renderer);
731         if (canvas->height() <= 1 || canvas->width() <= 1)
732             return true;
733         // Otherwise fall through; use presence of help text, title, or description to decide.
734     }
735 
736     if (isWebArea() || m_renderer->isListMarker())
737         return false;
738 
739     // Using the help text, title or accessibility description (so we
740     // check if there's some kind of accessible name for the element)
741     // to decide an element's visibility is not as definitive as
742     // previous checks, so this should remain as one of the last.
743     //
744     // These checks are simplified in the interest of execution speed;
745     // for example, any element having an alt attribute will make it
746     // not ignored, rather than just images.
747     if (!getAttribute(aria_helpAttr).isEmpty() || !getAttribute(aria_describedbyAttr).isEmpty() || !getAttribute(altAttr).isEmpty() || !getAttribute(titleAttr).isEmpty())
748         return false;
749 
750     // Don't ignore generic focusable elements like <div tabindex=0>
751     // unless they're completely empty, with no children.
752     if (isGenericFocusableElement() && node->firstChild())
753         return false;
754 
755     if (!ariaAccessibilityDescription().isEmpty())
756         return false;
757 
758     // By default, objects should be ignored so that the AX hierarchy is not
759     // filled with unnecessary items.
760     return true;
761 }
762 
763 //
764 // Properties of static elements.
765 //
766 
accessKey() const767 const AtomicString& AXRenderObject::accessKey() const
768 {
769     Node* node = m_renderer->node();
770     if (!node)
771         return nullAtom;
772     if (!node->isElementNode())
773         return nullAtom;
774     return toElement(node)->getAttribute(accesskeyAttr);
775 }
776 
orientation() const777 AccessibilityOrientation AXRenderObject::orientation() const
778 {
779     const AtomicString& ariaOrientation = getAttribute(aria_orientationAttr);
780     if (equalIgnoringCase(ariaOrientation, "horizontal"))
781         return AccessibilityOrientationHorizontal;
782     if (equalIgnoringCase(ariaOrientation, "vertical"))
783         return AccessibilityOrientationVertical;
784 
785     return AXObject::orientation();
786 }
787 
text() const788 String AXRenderObject::text() const
789 {
790     if (isPasswordField())
791         return String();
792 
793     return AXNodeObject::text();
794 }
795 
textLength() const796 int AXRenderObject::textLength() const
797 {
798     if (!isTextControl())
799         return -1;
800 
801     if (isPasswordField())
802         return -1; // need to return something distinct from 0
803 
804     return text().length();
805 }
806 
url() const807 KURL AXRenderObject::url() const
808 {
809     if (isAnchor() && isHTMLAnchorElement(m_renderer->node())) {
810         if (HTMLAnchorElement* anchor = toHTMLAnchorElement(anchorElement()))
811             return anchor->href();
812     }
813 
814     if (isWebArea())
815         return m_renderer->document().url();
816 
817     if (isImage() && isHTMLImageElement(m_renderer->node()))
818         return toHTMLImageElement(*m_renderer->node()).src();
819 
820     if (isInputImage())
821         return toHTMLInputElement(m_renderer->node())->src();
822 
823     return KURL();
824 }
825 
826 //
827 // Properties of interactive elements.
828 //
829 
queryString(WebLocalizedString::Name name)830 static String queryString(WebLocalizedString::Name name)
831 {
832     return Locale::defaultLocale().queryString(name);
833 }
834 
actionVerb() const835 String AXRenderObject::actionVerb() const
836 {
837     switch (roleValue()) {
838     case ButtonRole:
839     case ToggleButtonRole:
840         return queryString(WebLocalizedString::AXButtonActionVerb);
841     case TextFieldRole:
842     case TextAreaRole:
843         return queryString(WebLocalizedString::AXTextFieldActionVerb);
844     case RadioButtonRole:
845         return queryString(WebLocalizedString::AXRadioButtonActionVerb);
846     case CheckBoxRole:
847         return queryString(isChecked() ? WebLocalizedString::AXCheckedCheckBoxActionVerb : WebLocalizedString::AXUncheckedCheckBoxActionVerb);
848     case LinkRole:
849         return queryString(WebLocalizedString::AXLinkActionVerb);
850     default:
851         return emptyString();
852     }
853 }
854 
stringValue() const855 String AXRenderObject::stringValue() const
856 {
857     if (!m_renderer)
858         return String();
859 
860     if (isPasswordField())
861         return String();
862 
863     RenderBoxModelObject* cssBox = renderBoxModelObject();
864 
865     if (ariaRoleAttribute() == StaticTextRole) {
866         String staticText = text();
867         if (!staticText.length())
868             staticText = textUnderElement();
869         return staticText;
870     }
871 
872     if (m_renderer->isText())
873         return textUnderElement();
874 
875     if (cssBox && cssBox->isMenuList()) {
876         // RenderMenuList will go straight to the text() of its selected item.
877         // This has to be overridden in the case where the selected item has an ARIA label.
878         HTMLSelectElement* selectElement = toHTMLSelectElement(m_renderer->node());
879         int selectedIndex = selectElement->selectedIndex();
880         const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& listItems = selectElement->listItems();
881         if (selectedIndex >= 0 && static_cast<size_t>(selectedIndex) < listItems.size()) {
882             const AtomicString& overriddenDescription = listItems[selectedIndex]->fastGetAttribute(aria_labelAttr);
883             if (!overriddenDescription.isNull())
884                 return overriddenDescription;
885         }
886         return toRenderMenuList(m_renderer)->text();
887     }
888 
889     if (m_renderer->isListMarker())
890         return toRenderListMarker(m_renderer)->text();
891 
892     if (isWebArea()) {
893         // FIXME: Why would a renderer exist when the Document isn't attached to a frame?
894         if (m_renderer->frame())
895             return String();
896 
897         ASSERT_NOT_REACHED();
898     }
899 
900     if (isTextControl())
901         return text();
902 
903     if (m_renderer->isFileUploadControl())
904         return toRenderFileUploadControl(m_renderer)->fileTextValue();
905 
906     // FIXME: We might need to implement a value here for more types
907     // FIXME: It would be better not to advertise a value at all for the types for which we don't implement one;
908     // this would require subclassing or making accessibilityAttributeNames do something other than return a
909     // single static array.
910     return String();
911 }
912 
913 //
914 // ARIA attributes.
915 //
916 
activeDescendant() const917 AXObject* AXRenderObject::activeDescendant() const
918 {
919     if (!m_renderer)
920         return 0;
921 
922     if (m_renderer->node() && !m_renderer->node()->isElementNode())
923         return 0;
924 
925     Element* element = toElement(m_renderer->node());
926     if (!element)
927         return 0;
928 
929     const AtomicString& activeDescendantAttrStr = element->getAttribute(aria_activedescendantAttr);
930     if (activeDescendantAttrStr.isNull() || activeDescendantAttrStr.isEmpty())
931         return 0;
932 
933     Element* target = element->treeScope().getElementById(activeDescendantAttrStr);
934     if (!target)
935         return 0;
936 
937     AXObject* obj = axObjectCache()->getOrCreate(target);
938 
939     // An activedescendant is only useful if it has a renderer, because that's what's needed to post the notification.
940     if (obj && obj->isAXRenderObject())
941         return obj;
942 
943     return 0;
944 }
945 
accessibilityChildrenFromAttribute(QualifiedName attr,AccessibilityChildrenVector & children) const946 void AXRenderObject::accessibilityChildrenFromAttribute(QualifiedName attr, AccessibilityChildrenVector& children) const
947 {
948     Vector<Element*> elements;
949     elementsFromAttribute(elements, attr);
950 
951     AXObjectCache* cache = axObjectCache();
952     unsigned count = elements.size();
953     for (unsigned k = 0; k < count; ++k) {
954         Element* element = elements[k];
955         AXObject* child = cache->getOrCreate(element);
956         if (child)
957             children.append(child);
958     }
959 }
960 
ariaFlowToElements(AccessibilityChildrenVector & flowTo) const961 void AXRenderObject::ariaFlowToElements(AccessibilityChildrenVector& flowTo) const
962 {
963     accessibilityChildrenFromAttribute(aria_flowtoAttr, flowTo);
964 }
965 
ariaControlsElements(AccessibilityChildrenVector & controls) const966 void AXRenderObject::ariaControlsElements(AccessibilityChildrenVector& controls) const
967 {
968     accessibilityChildrenFromAttribute(aria_controlsAttr, controls);
969 }
970 
ariaDescribedbyElements(AccessibilityChildrenVector & describedby) const971 void AXRenderObject::ariaDescribedbyElements(AccessibilityChildrenVector& describedby) const
972 {
973     accessibilityChildrenFromAttribute(aria_describedbyAttr, describedby);
974 }
975 
ariaLabelledbyElements(AccessibilityChildrenVector & labelledby) const976 void AXRenderObject::ariaLabelledbyElements(AccessibilityChildrenVector& labelledby) const
977 {
978     accessibilityChildrenFromAttribute(aria_labelledbyAttr, labelledby);
979 }
980 
ariaOwnsElements(AccessibilityChildrenVector & owns) const981 void AXRenderObject::ariaOwnsElements(AccessibilityChildrenVector& owns) const
982 {
983     accessibilityChildrenFromAttribute(aria_ownsAttr, owns);
984 }
985 
ariaHasPopup() const986 bool AXRenderObject::ariaHasPopup() const
987 {
988     return elementAttributeValue(aria_haspopupAttr);
989 }
990 
ariaRoleHasPresentationalChildren() const991 bool AXRenderObject::ariaRoleHasPresentationalChildren() const
992 {
993     switch (m_ariaRole) {
994     case ButtonRole:
995     case SliderRole:
996     case ImageRole:
997     case ProgressIndicatorRole:
998     case SpinButtonRole:
999     // case SeparatorRole:
1000         return true;
1001     default:
1002         return false;
1003     }
1004 }
1005 
isPresentationalChildOfAriaRole() const1006 bool AXRenderObject::isPresentationalChildOfAriaRole() const
1007 {
1008     // Walk the parent chain looking for a parent that has presentational children
1009     AXObject* parent;
1010     for (parent = parentObject(); parent && !parent->ariaRoleHasPresentationalChildren(); parent = parent->parentObject())
1011     { }
1012 
1013     return parent;
1014 }
1015 
shouldFocusActiveDescendant() const1016 bool AXRenderObject::shouldFocusActiveDescendant() const
1017 {
1018     switch (ariaRoleAttribute()) {
1019     case ComboBoxRole:
1020     case GridRole:
1021     case GroupRole:
1022     case ListBoxRole:
1023     case MenuRole:
1024     case MenuBarRole:
1025     case OutlineRole:
1026     case PopUpButtonRole:
1027     case ProgressIndicatorRole:
1028     case RadioGroupRole:
1029     case RowRole:
1030     case TabListRole:
1031     case ToolbarRole:
1032     case TreeRole:
1033     case TreeGridRole:
1034         return true;
1035     default:
1036         return false;
1037     }
1038 }
1039 
supportsARIADragging() const1040 bool AXRenderObject::supportsARIADragging() const
1041 {
1042     const AtomicString& grabbed = getAttribute(aria_grabbedAttr);
1043     return equalIgnoringCase(grabbed, "true") || equalIgnoringCase(grabbed, "false");
1044 }
1045 
supportsARIADropping() const1046 bool AXRenderObject::supportsARIADropping() const
1047 {
1048     const AtomicString& dropEffect = getAttribute(aria_dropeffectAttr);
1049     return !dropEffect.isEmpty();
1050 }
1051 
supportsARIAFlowTo() const1052 bool AXRenderObject::supportsARIAFlowTo() const
1053 {
1054     return !getAttribute(aria_flowtoAttr).isEmpty();
1055 }
1056 
supportsARIAOwns() const1057 bool AXRenderObject::supportsARIAOwns() const
1058 {
1059     if (!m_renderer)
1060         return false;
1061     const AtomicString& ariaOwns = getAttribute(aria_ownsAttr);
1062 
1063     return !ariaOwns.isEmpty();
1064 }
1065 
1066 //
1067 // ARIA live-region features.
1068 //
1069 
ariaLiveRegionStatus() const1070 const AtomicString& AXRenderObject::ariaLiveRegionStatus() const
1071 {
1072     DEFINE_STATIC_LOCAL(const AtomicString, liveRegionStatusAssertive, ("assertive", AtomicString::ConstructFromLiteral));
1073     DEFINE_STATIC_LOCAL(const AtomicString, liveRegionStatusPolite, ("polite", AtomicString::ConstructFromLiteral));
1074     DEFINE_STATIC_LOCAL(const AtomicString, liveRegionStatusOff, ("off", AtomicString::ConstructFromLiteral));
1075 
1076     const AtomicString& liveRegionStatus = getAttribute(aria_liveAttr);
1077     // These roles have implicit live region status.
1078     if (liveRegionStatus.isEmpty()) {
1079         switch (roleValue()) {
1080         case AlertDialogRole:
1081         case AlertRole:
1082             return liveRegionStatusAssertive;
1083         case LogRole:
1084         case StatusRole:
1085             return liveRegionStatusPolite;
1086         case TimerRole:
1087         case MarqueeRole:
1088             return liveRegionStatusOff;
1089         default:
1090             break;
1091         }
1092     }
1093 
1094     return liveRegionStatus;
1095 }
1096 
ariaLiveRegionRelevant() const1097 const AtomicString& AXRenderObject::ariaLiveRegionRelevant() const
1098 {
1099     DEFINE_STATIC_LOCAL(const AtomicString, defaultLiveRegionRelevant, ("additions text", AtomicString::ConstructFromLiteral));
1100     const AtomicString& relevant = getAttribute(aria_relevantAttr);
1101 
1102     // Default aria-relevant = "additions text".
1103     if (relevant.isEmpty())
1104         return defaultLiveRegionRelevant;
1105 
1106     return relevant;
1107 }
1108 
ariaLiveRegionAtomic() const1109 bool AXRenderObject::ariaLiveRegionAtomic() const
1110 {
1111     return elementAttributeValue(aria_atomicAttr);
1112 }
1113 
ariaLiveRegionBusy() const1114 bool AXRenderObject::ariaLiveRegionBusy() const
1115 {
1116     return elementAttributeValue(aria_busyAttr);
1117 }
1118 
1119 //
1120 // Accessibility Text.
1121 //
1122 
textUnderElement() const1123 String AXRenderObject::textUnderElement() const
1124 {
1125     if (!m_renderer)
1126         return String();
1127 
1128     if (m_renderer->isFileUploadControl())
1129         return toRenderFileUploadControl(m_renderer)->buttonValue();
1130 
1131     if (m_renderer->isText())
1132         return toRenderText(m_renderer)->plainText();
1133 
1134     return AXNodeObject::textUnderElement();
1135 }
1136 
1137 //
1138 // Accessibility Text - (To be deprecated).
1139 //
1140 
helpText() const1141 String AXRenderObject::helpText() const
1142 {
1143     if (!m_renderer)
1144         return String();
1145 
1146     const AtomicString& ariaHelp = getAttribute(aria_helpAttr);
1147     if (!ariaHelp.isEmpty())
1148         return ariaHelp;
1149 
1150     String describedBy = ariaDescribedByAttribute();
1151     if (!describedBy.isEmpty())
1152         return describedBy;
1153 
1154     String description = accessibilityDescription();
1155     for (RenderObject* curr = m_renderer; curr; curr = curr->parent()) {
1156         if (curr->node() && curr->node()->isHTMLElement()) {
1157             const AtomicString& summary = toElement(curr->node())->getAttribute(summaryAttr);
1158             if (!summary.isEmpty())
1159                 return summary;
1160 
1161             // The title attribute should be used as help text unless it is already being used as descriptive text.
1162             const AtomicString& title = toElement(curr->node())->getAttribute(titleAttr);
1163             if (!title.isEmpty() && description != title)
1164                 return title;
1165         }
1166 
1167         // Only take help text from an ancestor element if its a group or an unknown role. If help was
1168         // added to those kinds of elements, it is likely it was meant for a child element.
1169         AXObject* axObj = axObjectCache()->getOrCreate(curr);
1170         if (axObj) {
1171             AccessibilityRole role = axObj->roleValue();
1172             if (role != GroupRole && role != UnknownRole)
1173                 break;
1174         }
1175     }
1176 
1177     return String();
1178 }
1179 
1180 //
1181 // Position and size.
1182 //
1183 
checkCachedElementRect() const1184 void AXRenderObject::checkCachedElementRect() const
1185 {
1186     if (m_cachedElementRectDirty)
1187         return;
1188 
1189     if (!m_renderer)
1190         return;
1191 
1192     if (!m_renderer->isBox())
1193         return;
1194 
1195     bool dirty = false;
1196     RenderBox* box = toRenderBox(m_renderer);
1197     if (box->frameRect() != m_cachedFrameRect)
1198         dirty = true;
1199 
1200     if (box->canBeScrolledAndHasScrollableArea()) {
1201         ScrollableArea* scrollableArea = box->scrollableArea();
1202         if (scrollableArea && scrollableArea->scrollPosition() != m_cachedScrollPosition)
1203             dirty = true;
1204     }
1205 
1206     if (dirty)
1207         markCachedElementRectDirty();
1208 }
1209 
updateCachedElementRect() const1210 void AXRenderObject::updateCachedElementRect() const
1211 {
1212     if (!m_cachedElementRectDirty)
1213         return;
1214 
1215     if (!m_renderer)
1216         return;
1217 
1218     if (!m_renderer->isBox())
1219         return;
1220 
1221     RenderBox* box = toRenderBox(m_renderer);
1222     m_cachedFrameRect = box->frameRect();
1223 
1224     if (box->canBeScrolledAndHasScrollableArea()) {
1225         ScrollableArea* scrollableArea = box->scrollableArea();
1226         if (scrollableArea)
1227             m_cachedScrollPosition = scrollableArea->scrollPosition();
1228     }
1229 
1230     m_cachedElementRect = computeElementRect();
1231     m_cachedElementRectDirty = false;
1232 }
1233 
markCachedElementRectDirty() const1234 void AXRenderObject::markCachedElementRectDirty() const
1235 {
1236     if (m_cachedElementRectDirty)
1237         return;
1238 
1239     // Marks children recursively, if this element changed.
1240     m_cachedElementRectDirty = true;
1241     for (AXObject* child = firstChild(); child; child = child->nextSibling())
1242         child->markCachedElementRectDirty();
1243 }
1244 
clickPoint()1245 IntPoint AXRenderObject::clickPoint()
1246 {
1247     // Headings are usually much wider than their textual content. If the mid point is used, often it can be wrong.
1248     if (isHeading() && children().size() == 1)
1249         return children()[0]->clickPoint();
1250 
1251     // use the default position unless this is an editable web area, in which case we use the selection bounds.
1252     if (!isWebArea() || isReadOnly())
1253         return AXObject::clickPoint();
1254 
1255     IntRect bounds = pixelSnappedIntRect(elementRect());
1256     return IntPoint(bounds.x() + (bounds.width() / 2), bounds.y() - (bounds.height() / 2));
1257 }
1258 
1259 //
1260 // Hit testing.
1261 //
1262 
accessibilityHitTest(const IntPoint & point) const1263 AXObject* AXRenderObject::accessibilityHitTest(const IntPoint& point) const
1264 {
1265     if (!m_renderer || !m_renderer->hasLayer())
1266         return 0;
1267 
1268     RenderLayer* layer = toRenderBox(m_renderer)->layer();
1269 
1270     HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active);
1271     HitTestResult hitTestResult = HitTestResult(point);
1272     layer->hitTest(request, hitTestResult);
1273     if (!hitTestResult.innerNode())
1274         return 0;
1275     Node* node = hitTestResult.innerNode()->deprecatedShadowAncestorNode();
1276 
1277     if (isHTMLAreaElement(node))
1278         return accessibilityImageMapHitTest(toHTMLAreaElement(node), point);
1279 
1280     if (isHTMLOptionElement(node))
1281         node = toHTMLOptionElement(*node).ownerSelectElement();
1282 
1283     RenderObject* obj = node->renderer();
1284     if (!obj)
1285         return 0;
1286 
1287     AXObject* result = obj->document().axObjectCache()->getOrCreate(obj);
1288     result->updateChildrenIfNecessary();
1289 
1290     // Allow the element to perform any hit-testing it might need to do to reach non-render children.
1291     result = result->elementAccessibilityHitTest(point);
1292 
1293     if (result && result->accessibilityIsIgnored()) {
1294         // If this element is the label of a control, a hit test should return the control.
1295         if (result->isAXRenderObject()) {
1296             AXObject* controlObject = toAXRenderObject(result)->correspondingControlForLabelElement();
1297             if (controlObject && !controlObject->exposesTitleUIElement())
1298                 return controlObject;
1299         }
1300 
1301         result = result->parentObjectUnignored();
1302     }
1303 
1304     return result;
1305 }
1306 
elementAccessibilityHitTest(const IntPoint & point) const1307 AXObject* AXRenderObject::elementAccessibilityHitTest(const IntPoint& point) const
1308 {
1309     if (isSVGImage())
1310         return remoteSVGElementHitTest(point);
1311 
1312     return AXObject::elementAccessibilityHitTest(point);
1313 }
1314 
1315 //
1316 // High-level accessibility tree access.
1317 //
1318 
parentObject() const1319 AXObject* AXRenderObject::parentObject() const
1320 {
1321     if (!m_renderer)
1322         return 0;
1323 
1324     if (ariaRoleAttribute() == MenuBarRole)
1325         return axObjectCache()->getOrCreate(m_renderer->parent());
1326 
1327     // menuButton and its corresponding menu are DOM siblings, but Accessibility needs them to be parent/child
1328     if (ariaRoleAttribute() == MenuRole) {
1329         AXObject* parent = menuButtonForMenu();
1330         if (parent)
1331             return parent;
1332     }
1333 
1334     RenderObject* parentObj = renderParentObject();
1335     if (parentObj)
1336         return axObjectCache()->getOrCreate(parentObj);
1337 
1338     // WebArea's parent should be the scroll view containing it.
1339     if (isWebArea())
1340         return axObjectCache()->getOrCreate(m_renderer->frame()->view());
1341 
1342     return 0;
1343 }
1344 
parentObjectIfExists() const1345 AXObject* AXRenderObject::parentObjectIfExists() const
1346 {
1347     // WebArea's parent should be the scroll view containing it.
1348     if (isWebArea())
1349         return axObjectCache()->get(m_renderer->frame()->view());
1350 
1351     return axObjectCache()->get(renderParentObject());
1352 }
1353 
1354 //
1355 // Low-level accessibility tree exploration, only for use within the accessibility module.
1356 //
1357 
firstChild() const1358 AXObject* AXRenderObject::firstChild() const
1359 {
1360     if (!m_renderer)
1361         return 0;
1362 
1363     RenderObject* firstChild = firstChildConsideringContinuation(m_renderer);
1364 
1365     if (!firstChild)
1366         return 0;
1367 
1368     return axObjectCache()->getOrCreate(firstChild);
1369 }
1370 
nextSibling() const1371 AXObject* AXRenderObject::nextSibling() const
1372 {
1373     if (!m_renderer)
1374         return 0;
1375 
1376     RenderObject* nextSibling = 0;
1377 
1378     RenderInline* inlineContinuation;
1379     if (m_renderer->isRenderBlock() && (inlineContinuation = toRenderBlock(m_renderer)->inlineElementContinuation())) {
1380         // Case 1: node is a block and has an inline continuation. Next sibling is the inline continuation's first child.
1381         nextSibling = firstChildConsideringContinuation(inlineContinuation);
1382     } else if (m_renderer->isAnonymousBlock() && lastChildHasContinuation(m_renderer)) {
1383         // Case 2: Anonymous block parent of the start of a continuation - skip all the way to
1384         // after the parent of the end, since everything in between will be linked up via the continuation.
1385         RenderObject* lastParent = endOfContinuations(toRenderBlock(m_renderer)->lastChild())->parent();
1386         while (lastChildHasContinuation(lastParent))
1387             lastParent = endOfContinuations(lastParent->slowLastChild())->parent();
1388         nextSibling = lastParent->nextSibling();
1389     } else if (RenderObject* ns = m_renderer->nextSibling()) {
1390         // Case 3: node has an actual next sibling
1391         nextSibling = ns;
1392     } else if (isInlineWithContinuation(m_renderer)) {
1393         // Case 4: node is an inline with a continuation. Next sibling is the next sibling of the end
1394         // of the continuation chain.
1395         nextSibling = endOfContinuations(m_renderer)->nextSibling();
1396     } else if (isInlineWithContinuation(m_renderer->parent())) {
1397         // Case 5: node has no next sibling, and its parent is an inline with a continuation.
1398         RenderObject* continuation = toRenderInline(m_renderer->parent())->continuation();
1399 
1400         if (continuation->isRenderBlock()) {
1401             // Case 5a: continuation is a block - in this case the block itself is the next sibling.
1402             nextSibling = continuation;
1403         } else {
1404             // Case 5b: continuation is an inline - in this case the inline's first child is the next sibling.
1405             nextSibling = firstChildConsideringContinuation(continuation);
1406         }
1407     }
1408 
1409     if (!nextSibling)
1410         return 0;
1411 
1412     return axObjectCache()->getOrCreate(nextSibling);
1413 }
1414 
addChildren()1415 void AXRenderObject::addChildren()
1416 {
1417     // If the need to add more children in addition to existing children arises,
1418     // childrenChanged should have been called, leaving the object with no children.
1419     ASSERT(!m_haveChildren);
1420 
1421     m_haveChildren = true;
1422 
1423     if (!canHaveChildren())
1424         return;
1425 
1426     for (RefPtr<AXObject> obj = firstChild(); obj; obj = obj->nextSibling())
1427         addChild(obj.get());
1428 
1429     addHiddenChildren();
1430     addAttachmentChildren();
1431     addImageMapChildren();
1432     addTextFieldChildren();
1433     addCanvasChildren();
1434     addRemoteSVGChildren();
1435     addInlineTextBoxChildren();
1436 }
1437 
canHaveChildren() const1438 bool AXRenderObject::canHaveChildren() const
1439 {
1440     if (!m_renderer)
1441         return false;
1442 
1443     return AXNodeObject::canHaveChildren();
1444 }
1445 
updateChildrenIfNecessary()1446 void AXRenderObject::updateChildrenIfNecessary()
1447 {
1448     if (needsToUpdateChildren())
1449         clearChildren();
1450 
1451     AXObject::updateChildrenIfNecessary();
1452 }
1453 
clearChildren()1454 void AXRenderObject::clearChildren()
1455 {
1456     AXObject::clearChildren();
1457     m_childrenDirty = false;
1458 }
1459 
observableObject() const1460 AXObject* AXRenderObject::observableObject() const
1461 {
1462     // Find the object going up the parent chain that is used in accessibility to monitor certain notifications.
1463     for (RenderObject* renderer = m_renderer; renderer && renderer->node(); renderer = renderer->parent()) {
1464         if (renderObjectIsObservable(renderer))
1465             return axObjectCache()->getOrCreate(renderer);
1466     }
1467 
1468     return 0;
1469 }
1470 
1471 //
1472 // Properties of the object's owning document or page.
1473 //
1474 
estimatedLoadingProgress() const1475 double AXRenderObject::estimatedLoadingProgress() const
1476 {
1477     if (!m_renderer)
1478         return 0;
1479 
1480     if (isLoaded())
1481         return 1.0;
1482 
1483     if (LocalFrame* frame = m_renderer->document().frame())
1484         return frame->loader().progress().estimatedProgress();
1485     return 0;
1486 }
1487 
1488 //
1489 // DOM and Render tree access.
1490 //
1491 
node() const1492 Node* AXRenderObject::node() const
1493 {
1494     return m_renderer ? m_renderer->node() : 0;
1495 }
1496 
document() const1497 Document* AXRenderObject::document() const
1498 {
1499     if (!m_renderer)
1500         return 0;
1501     return &m_renderer->document();
1502 }
1503 
documentFrameView() const1504 FrameView* AXRenderObject::documentFrameView() const
1505 {
1506     if (!m_renderer)
1507         return 0;
1508 
1509     // this is the RenderObject's Document's LocalFrame's FrameView
1510     return m_renderer->document().view();
1511 }
1512 
anchorElement() const1513 Element* AXRenderObject::anchorElement() const
1514 {
1515     if (!m_renderer)
1516         return 0;
1517 
1518     AXObjectCache* cache = axObjectCache();
1519     RenderObject* currRenderer;
1520 
1521     // Search up the render tree for a RenderObject with a DOM node. Defer to an earlier continuation, though.
1522     for (currRenderer = m_renderer; currRenderer && !currRenderer->node(); currRenderer = currRenderer->parent()) {
1523         if (currRenderer->isAnonymousBlock()) {
1524             RenderObject* continuation = toRenderBlock(currRenderer)->continuation();
1525             if (continuation)
1526                 return cache->getOrCreate(continuation)->anchorElement();
1527         }
1528     }
1529 
1530     // bail if none found
1531     if (!currRenderer)
1532         return 0;
1533 
1534     // search up the DOM tree for an anchor element
1535     // NOTE: this assumes that any non-image with an anchor is an HTMLAnchorElement
1536     Node* node = currRenderer->node();
1537     for ( ; node; node = node->parentNode()) {
1538         if (isHTMLAnchorElement(*node) || (node->renderer() && cache->getOrCreate(node->renderer())->isAnchor()))
1539             return toElement(node);
1540     }
1541 
1542     return 0;
1543 }
1544 
widgetForAttachmentView() const1545 Widget* AXRenderObject::widgetForAttachmentView() const
1546 {
1547     if (!isAttachment())
1548         return 0;
1549     return toRenderWidget(m_renderer)->widget();
1550 }
1551 
1552 //
1553 // Selected text.
1554 //
1555 
selectedTextRange() const1556 AXObject::PlainTextRange AXRenderObject::selectedTextRange() const
1557 {
1558     if (!isTextControl())
1559         return PlainTextRange();
1560 
1561     if (isPasswordField())
1562         return PlainTextRange();
1563 
1564     AccessibilityRole ariaRole = ariaRoleAttribute();
1565     if (isNativeTextControl() && ariaRole == UnknownRole && m_renderer->isTextControl()) {
1566         HTMLTextFormControlElement* textControl = toRenderTextControl(m_renderer)->textFormControlElement();
1567         return PlainTextRange(textControl->selectionStart(), textControl->selectionEnd() - textControl->selectionStart());
1568     }
1569 
1570     if (ariaRole == UnknownRole)
1571         return PlainTextRange();
1572 
1573     return ariaSelectedTextRange();
1574 }
1575 
selection() const1576 VisibleSelection AXRenderObject::selection() const
1577 {
1578     return m_renderer->frame()->selection().selection();
1579 }
1580 
1581 //
1582 // Modify or take an action on an object.
1583 //
1584 
setSelectedTextRange(const PlainTextRange & range)1585 void AXRenderObject::setSelectedTextRange(const PlainTextRange& range)
1586 {
1587     if (isNativeTextControl() && m_renderer->isTextControl()) {
1588         HTMLTextFormControlElement* textControl = toRenderTextControl(m_renderer)->textFormControlElement();
1589         textControl->setSelectionRange(range.start, range.start + range.length);
1590         return;
1591     }
1592 
1593     Document& document = m_renderer->document();
1594     LocalFrame* frame = document.frame();
1595     if (!frame)
1596         return;
1597     Node* node = m_renderer->node();
1598     frame->selection().setSelection(VisibleSelection(Position(node, range.start, Position::PositionIsOffsetInAnchor),
1599         Position(node, range.start + range.length, Position::PositionIsOffsetInAnchor), DOWNSTREAM));
1600 }
1601 
setValue(const String & string)1602 void AXRenderObject::setValue(const String& string)
1603 {
1604     if (!node() || !node()->isElementNode())
1605         return;
1606     if (!m_renderer || !m_renderer->isBoxModelObject())
1607         return;
1608 
1609     RenderBoxModelObject* renderer = toRenderBoxModelObject(m_renderer);
1610     if (renderer->isTextField() && isHTMLInputElement(*node()))
1611         toHTMLInputElement(*node()).setValue(string);
1612     else if (renderer->isTextArea() && isHTMLTextAreaElement(*node()))
1613         toHTMLTextAreaElement(*node()).setValue(string);
1614 }
1615 
1616 // FIXME: This function should use an IntSize to avoid the conversion below.
scrollTo(const IntPoint & point) const1617 void AXRenderObject::scrollTo(const IntPoint& point) const
1618 {
1619     if (!m_renderer || !m_renderer->isBox())
1620         return;
1621 
1622     RenderBox* box = toRenderBox(m_renderer);
1623     if (!box->canBeScrolledAndHasScrollableArea())
1624         return;
1625 
1626     box->scrollToOffset(IntSize(point.x(), point.y()));
1627 }
1628 
1629 //
1630 // Notifications that this object may have changed.
1631 //
1632 
handleActiveDescendantChanged()1633 void AXRenderObject::handleActiveDescendantChanged()
1634 {
1635     Element* element = toElement(renderer()->node());
1636     if (!element)
1637         return;
1638     Document& doc = renderer()->document();
1639     if (!doc.frame()->selection().isFocusedAndActive() || doc.focusedElement() != element)
1640         return;
1641     AXRenderObject* activedescendant = toAXRenderObject(activeDescendant());
1642 
1643     if (activedescendant && shouldNotifyActiveDescendant())
1644         doc.axObjectCache()->postNotification(m_renderer, AXObjectCache::AXActiveDescendantChanged, true);
1645 }
1646 
handleAriaExpandedChanged()1647 void AXRenderObject::handleAriaExpandedChanged()
1648 {
1649     // Find if a parent of this object should handle aria-expanded changes.
1650     AXObject* containerParent = this->parentObject();
1651     while (containerParent) {
1652         bool foundParent = false;
1653 
1654         switch (containerParent->roleValue()) {
1655         case TreeRole:
1656         case TreeGridRole:
1657         case GridRole:
1658         case TableRole:
1659         case BrowserRole:
1660             foundParent = true;
1661             break;
1662         default:
1663             break;
1664         }
1665 
1666         if (foundParent)
1667             break;
1668 
1669         containerParent = containerParent->parentObject();
1670     }
1671 
1672     // Post that the row count changed.
1673     if (containerParent)
1674         axObjectCache()->postNotification(containerParent, document(), AXObjectCache::AXRowCountChanged, true);
1675 
1676     // Post that the specific row either collapsed or expanded.
1677     if (roleValue() == RowRole || roleValue() == TreeItemRole)
1678         axObjectCache()->postNotification(this, document(), isExpanded() ? AXObjectCache::AXRowExpanded : AXObjectCache::AXRowCollapsed, true);
1679 }
1680 
textChanged()1681 void AXRenderObject::textChanged()
1682 {
1683     if (!m_renderer)
1684         return;
1685 
1686     if (AXObjectCache::inlineTextBoxAccessibility() && roleValue() == StaticTextRole)
1687         childrenChanged();
1688 
1689     // Do this last - AXNodeObject::textChanged posts live region announcements,
1690     // and we should update the inline text boxes first.
1691     AXNodeObject::textChanged();
1692 }
1693 
1694 //
1695 // Text metrics. Most of these should be deprecated, needs major cleanup.
1696 //
1697 
1698 // NOTE: Consider providing this utility method as AX API
index(const VisiblePosition & position) const1699 int AXRenderObject::index(const VisiblePosition& position) const
1700 {
1701     if (position.isNull() || !isTextControl())
1702         return -1;
1703 
1704     if (renderObjectContainsPosition(m_renderer, position.deepEquivalent()))
1705         return indexForVisiblePosition(position);
1706 
1707     return -1;
1708 }
1709 
visiblePositionForIndex(int index) const1710 VisiblePosition AXRenderObject::visiblePositionForIndex(int index) const
1711 {
1712     if (!m_renderer)
1713         return VisiblePosition();
1714 
1715     if (isNativeTextControl() && m_renderer->isTextControl())
1716         return toRenderTextControl(m_renderer)->textFormControlElement()->visiblePositionForIndex(index);
1717 
1718     if (!allowsTextRanges() && !m_renderer->isText())
1719         return VisiblePosition();
1720 
1721     Node* node = m_renderer->node();
1722     if (!node)
1723         return VisiblePosition();
1724 
1725     if (index <= 0)
1726         return VisiblePosition(firstPositionInOrBeforeNode(node), DOWNSTREAM);
1727 
1728     RefPtrWillBeRawPtr<Range> range = Range::create(m_renderer->document());
1729     range->selectNodeContents(node, IGNORE_EXCEPTION);
1730     CharacterIterator it(range.get());
1731     it.advance(index - 1);
1732     return VisiblePosition(Position(it.range()->endContainer(), it.range()->endOffset(), Position::PositionIsOffsetInAnch\
1733 or), UPSTREAM);
1734 }
1735 
indexForVisiblePosition(const VisiblePosition & pos) const1736 int AXRenderObject::indexForVisiblePosition(const VisiblePosition& pos) const
1737 {
1738     if (isNativeTextControl() && m_renderer->isTextControl()) {
1739         HTMLTextFormControlElement* textControl = toRenderTextControl(m_renderer)->textFormControlElement();
1740         return textControl->indexForVisiblePosition(pos);
1741     }
1742 
1743     if (!isTextControl())
1744         return 0;
1745 
1746     Node* node = m_renderer->node();
1747     if (!node)
1748         return 0;
1749 
1750     Position indexPosition = pos.deepEquivalent();
1751     if (indexPosition.isNull() || highestEditableRoot(indexPosition, HasEditableAXRole) != node)
1752         return 0;
1753 
1754     RefPtrWillBeRawPtr<Range> range = Range::create(m_renderer->document());
1755     range->setStart(node, 0, IGNORE_EXCEPTION);
1756     range->setEnd(indexPosition, IGNORE_EXCEPTION);
1757 
1758     return TextIterator::rangeLength(range.get());
1759 }
1760 
addInlineTextBoxChildren()1761 void AXRenderObject::addInlineTextBoxChildren()
1762 {
1763     if (!axObjectCache()->inlineTextBoxAccessibility())
1764         return;
1765 
1766     if (!renderer() || !renderer()->isText())
1767         return;
1768 
1769     if (renderer()->needsLayout()) {
1770         // If a RenderText needs layout, its inline text boxes are either
1771         // nonexistent or invalid, so defer until the layout happens and
1772         // the renderer calls AXObjectCache::inlineTextBoxesUpdated.
1773         return;
1774     }
1775 
1776     RenderText* renderText = toRenderText(renderer());
1777     for (RefPtr<AbstractInlineTextBox> box = renderText->firstAbstractInlineTextBox(); box.get(); box = box->nextInlineTextBox()) {
1778         AXObject* axObject = axObjectCache()->getOrCreate(box.get());
1779         if (!axObject->accessibilityIsIgnored())
1780             m_children.append(axObject);
1781     }
1782 }
1783 
lineBreaks(Vector<int> & lineBreaks) const1784 void AXRenderObject::lineBreaks(Vector<int>& lineBreaks) const
1785 {
1786     if (!isTextControl())
1787         return;
1788 
1789     VisiblePosition visiblePos = visiblePositionForIndex(0);
1790     VisiblePosition savedVisiblePos = visiblePos;
1791     visiblePos = nextLinePosition(visiblePos, 0);
1792     while (!visiblePos.isNull() && visiblePos != savedVisiblePos) {
1793         lineBreaks.append(indexForVisiblePosition(visiblePos));
1794         savedVisiblePos = visiblePos;
1795         visiblePos = nextLinePosition(visiblePos, 0);
1796     }
1797 }
1798 
1799 //
1800 // Private.
1801 //
1802 
isAllowedChildOfTree() const1803 bool AXRenderObject::isAllowedChildOfTree() const
1804 {
1805     // Determine if this is in a tree. If so, we apply special behavior to make it work like an AXOutline.
1806     AXObject* axObj = parentObject();
1807     bool isInTree = false;
1808     while (axObj) {
1809         if (axObj->isTree()) {
1810             isInTree = true;
1811             break;
1812         }
1813         axObj = axObj->parentObject();
1814     }
1815 
1816     // If the object is in a tree, only tree items should be exposed (and the children of tree items).
1817     if (isInTree) {
1818         AccessibilityRole role = roleValue();
1819         if (role != TreeItemRole && role != StaticTextRole)
1820             return false;
1821     }
1822     return true;
1823 }
1824 
ariaListboxSelectedChildren(AccessibilityChildrenVector & result)1825 void AXRenderObject::ariaListboxSelectedChildren(AccessibilityChildrenVector& result)
1826 {
1827     bool isMulti = isMultiSelectable();
1828 
1829     AccessibilityChildrenVector childObjects = children();
1830     unsigned childrenSize = childObjects.size();
1831     for (unsigned k = 0; k < childrenSize; ++k) {
1832         // Every child should have aria-role option, and if so, check for selected attribute/state.
1833         AXObject* child = childObjects[k].get();
1834         if (child->isSelected() && child->ariaRoleAttribute() == ListBoxOptionRole) {
1835             result.append(child);
1836             if (!isMulti)
1837                 return;
1838         }
1839     }
1840 }
1841 
ariaSelectedTextRange() const1842 AXObject::PlainTextRange AXRenderObject::ariaSelectedTextRange() const
1843 {
1844     Node* node = m_renderer->node();
1845     if (!node)
1846         return PlainTextRange();
1847 
1848     VisibleSelection visibleSelection = selection();
1849     RefPtrWillBeRawPtr<Range> currentSelectionRange = visibleSelection.toNormalizedRange();
1850     if (!currentSelectionRange || !currentSelectionRange->intersectsNode(node, IGNORE_EXCEPTION))
1851         return PlainTextRange();
1852 
1853     int start = indexForVisiblePosition(visibleSelection.visibleStart());
1854     int end = indexForVisiblePosition(visibleSelection.visibleEnd());
1855 
1856     return PlainTextRange(start, end - start);
1857 }
1858 
nodeIsTextControl(const Node * node) const1859 bool AXRenderObject::nodeIsTextControl(const Node* node) const
1860 {
1861     if (!node)
1862         return false;
1863 
1864     const AXObject* axObjectForNode = axObjectCache()->getOrCreate(const_cast<Node*>(node));
1865     if (!axObjectForNode)
1866         return false;
1867 
1868     return axObjectForNode->isTextControl();
1869 }
1870 
isTabItemSelected() const1871 bool AXRenderObject::isTabItemSelected() const
1872 {
1873     if (!isTabItem() || !m_renderer)
1874         return false;
1875 
1876     Node* node = m_renderer->node();
1877     if (!node || !node->isElementNode())
1878         return false;
1879 
1880     // The ARIA spec says a tab item can also be selected if it is aria-labeled by a tabpanel
1881     // that has keyboard focus inside of it, or if a tabpanel in its aria-controls list has KB
1882     // focus inside of it.
1883     AXObject* focusedElement = focusedUIElement();
1884     if (!focusedElement)
1885         return false;
1886 
1887     Vector<Element*> elements;
1888     elementsFromAttribute(elements, aria_controlsAttr);
1889 
1890     unsigned count = elements.size();
1891     for (unsigned k = 0; k < count; ++k) {
1892         Element* element = elements[k];
1893         AXObject* tabPanel = axObjectCache()->getOrCreate(element);
1894 
1895         // A tab item should only control tab panels.
1896         if (!tabPanel || tabPanel->roleValue() != TabPanelRole)
1897             continue;
1898 
1899         AXObject* checkFocusElement = focusedElement;
1900         // Check if the focused element is a descendant of the element controlled by the tab item.
1901         while (checkFocusElement) {
1902             if (tabPanel == checkFocusElement)
1903                 return true;
1904             checkFocusElement = checkFocusElement->parentObject();
1905         }
1906     }
1907 
1908     return false;
1909 }
1910 
accessibilityImageMapHitTest(HTMLAreaElement * area,const IntPoint & point) const1911 AXObject* AXRenderObject::accessibilityImageMapHitTest(HTMLAreaElement* area, const IntPoint& point) const
1912 {
1913     if (!area)
1914         return 0;
1915 
1916     AXObject* parent = axObjectCache()->getOrCreate(area->imageElement());
1917     if (!parent)
1918         return 0;
1919 
1920     AXObject::AccessibilityChildrenVector children = parent->children();
1921     unsigned count = children.size();
1922     for (unsigned k = 0; k < count; ++k) {
1923         if (children[k]->elementRect().contains(point))
1924             return children[k].get();
1925     }
1926 
1927     return 0;
1928 }
1929 
renderObjectIsObservable(RenderObject * renderer) const1930 bool AXRenderObject::renderObjectIsObservable(RenderObject* renderer) const
1931 {
1932     // AX clients will listen for AXValueChange on a text control.
1933     if (renderer->isTextControl())
1934         return true;
1935 
1936     // AX clients will listen for AXSelectedChildrenChanged on listboxes.
1937     Node* node = renderer->node();
1938     if (nodeHasRole(node, "listbox") || (renderer->isBoxModelObject() && toRenderBoxModelObject(renderer)->isListBox()))
1939         return true;
1940 
1941     // Textboxes should send out notifications.
1942     if (nodeHasRole(node, "textbox"))
1943         return true;
1944 
1945     return false;
1946 }
1947 
renderParentObject() const1948 RenderObject* AXRenderObject::renderParentObject() const
1949 {
1950     if (!m_renderer)
1951         return 0;
1952 
1953     RenderObject* parent = m_renderer->parent();
1954 
1955     RenderObject* startOfConts = 0;
1956     RenderObject* firstChild = 0;
1957     if (m_renderer->isRenderBlock() && (startOfConts = startOfContinuations(m_renderer))) {
1958         // Case 1: node is a block and is an inline's continuation. Parent
1959         // is the start of the continuation chain.
1960         parent = startOfConts;
1961     } else if (parent && parent->isRenderInline() && (startOfConts = startOfContinuations(parent))) {
1962         // Case 2: node's parent is an inline which is some node's continuation; parent is
1963         // the earliest node in the continuation chain.
1964         parent = startOfConts;
1965     } else if (parent && (firstChild = parent->slowFirstChild()) && firstChild->node()) {
1966         // Case 3: The first sibling is the beginning of a continuation chain. Find the origin of that continuation.
1967         // Get the node's renderer and follow that continuation chain until the first child is found.
1968         RenderObject* nodeRenderFirstChild = firstChild->node()->renderer();
1969         while (nodeRenderFirstChild != firstChild) {
1970             for (RenderObject* contsTest = nodeRenderFirstChild; contsTest; contsTest = nextContinuation(contsTest)) {
1971                 if (contsTest == firstChild) {
1972                     parent = nodeRenderFirstChild->parent();
1973                     break;
1974                 }
1975             }
1976             RenderObject* newFirstChild = parent->slowFirstChild();
1977             if (firstChild == newFirstChild)
1978                 break;
1979             firstChild = newFirstChild;
1980             if (!firstChild->node())
1981                 break;
1982             nodeRenderFirstChild = firstChild->node()->renderer();
1983         }
1984     }
1985 
1986     return parent;
1987 }
1988 
isDescendantOfElementType(const QualifiedName & tagName) const1989 bool AXRenderObject::isDescendantOfElementType(const QualifiedName& tagName) const
1990 {
1991     for (RenderObject* parent = m_renderer->parent(); parent; parent = parent->parent()) {
1992         if (parent->node() && parent->node()->hasTagName(tagName))
1993             return true;
1994     }
1995     return false;
1996 }
1997 
isSVGImage() const1998 bool AXRenderObject::isSVGImage() const
1999 {
2000     return remoteSVGRootElement();
2001 }
2002 
detachRemoteSVGRoot()2003 void AXRenderObject::detachRemoteSVGRoot()
2004 {
2005     if (AXSVGRoot* root = remoteSVGRootElement())
2006         root->setParent(0);
2007 }
2008 
remoteSVGRootElement() const2009 AXSVGRoot* AXRenderObject::remoteSVGRootElement() const
2010 {
2011     if (!m_renderer || !m_renderer->isRenderImage())
2012         return 0;
2013 
2014     ImageResource* cachedImage = toRenderImage(m_renderer)->cachedImage();
2015     if (!cachedImage)
2016         return 0;
2017 
2018     Image* image = cachedImage->image();
2019     if (!image || !image->isSVGImage())
2020         return 0;
2021 
2022     FrameView* frameView = toSVGImage(image)->frameView();
2023     if (!frameView)
2024         return 0;
2025     Document* doc = frameView->frame().document();
2026     if (!doc || !doc->isSVGDocument())
2027         return 0;
2028 
2029     SVGSVGElement* rootElement = doc->accessSVGExtensions().rootElement();
2030     if (!rootElement)
2031         return 0;
2032     RenderObject* rendererRoot = rootElement->renderer();
2033     if (!rendererRoot)
2034         return 0;
2035 
2036     AXObject* rootSVGObject = doc->axObjectCache()->getOrCreate(rendererRoot);
2037 
2038     // In order to connect the AX hierarchy from the SVG root element from the loaded resource
2039     // the parent must be set, because there's no other way to get back to who created the image.
2040     ASSERT(rootSVGObject && rootSVGObject->isAXSVGRoot());
2041     if (!rootSVGObject->isAXSVGRoot())
2042         return 0;
2043 
2044     return toAXSVGRoot(rootSVGObject);
2045 }
2046 
remoteSVGElementHitTest(const IntPoint & point) const2047 AXObject* AXRenderObject::remoteSVGElementHitTest(const IntPoint& point) const
2048 {
2049     AXObject* remote = remoteSVGRootElement();
2050     if (!remote)
2051         return 0;
2052 
2053     IntSize offset = point - roundedIntPoint(elementRect().location());
2054     return remote->accessibilityHitTest(IntPoint(offset));
2055 }
2056 
2057 // The boundingBox for elements within the remote SVG element needs to be offset by its position
2058 // within the parent page, otherwise they are in relative coordinates only.
offsetBoundingBoxForRemoteSVGElement(LayoutRect & rect) const2059 void AXRenderObject::offsetBoundingBoxForRemoteSVGElement(LayoutRect& rect) const
2060 {
2061     for (AXObject* parent = parentObject(); parent; parent = parent->parentObject()) {
2062         if (parent->isAXSVGRoot()) {
2063             rect.moveBy(parent->parentObject()->elementRect().location());
2064             break;
2065         }
2066     }
2067 }
2068 
2069 // Hidden children are those that are not rendered or visible, but are specifically marked as aria-hidden=false,
2070 // meaning that they should be exposed to the AX hierarchy.
addHiddenChildren()2071 void AXRenderObject::addHiddenChildren()
2072 {
2073     Node* node = this->node();
2074     if (!node)
2075         return;
2076 
2077     // First do a quick run through to determine if we have any hidden nodes (most often we will not).
2078     // If we do have hidden nodes, we need to determine where to insert them so they match DOM order as close as possible.
2079     bool shouldInsertHiddenNodes = false;
2080     for (Node* child = node->firstChild(); child; child = child->nextSibling()) {
2081         if (!child->renderer() && isNodeAriaVisible(child)) {
2082             shouldInsertHiddenNodes = true;
2083             break;
2084         }
2085     }
2086 
2087     if (!shouldInsertHiddenNodes)
2088         return;
2089 
2090     // Iterate through all of the children, including those that may have already been added, and
2091     // try to insert hidden nodes in the correct place in the DOM order.
2092     unsigned insertionIndex = 0;
2093     for (Node* child = node->firstChild(); child; child = child->nextSibling()) {
2094         if (child->renderer()) {
2095             // Find out where the last render sibling is located within m_children.
2096             AXObject* childObject = axObjectCache()->get(child->renderer());
2097             if (childObject && childObject->accessibilityIsIgnored()) {
2098                 AccessibilityChildrenVector children = childObject->children();
2099                 if (children.size())
2100                     childObject = children.last().get();
2101                 else
2102                     childObject = 0;
2103             }
2104 
2105             if (childObject)
2106                 insertionIndex = m_children.find(childObject) + 1;
2107             continue;
2108         }
2109 
2110         if (!isNodeAriaVisible(child))
2111             continue;
2112 
2113         unsigned previousSize = m_children.size();
2114         if (insertionIndex > previousSize)
2115             insertionIndex = previousSize;
2116 
2117         insertChild(axObjectCache()->getOrCreate(child), insertionIndex);
2118         insertionIndex += (m_children.size() - previousSize);
2119     }
2120 }
2121 
addTextFieldChildren()2122 void AXRenderObject::addTextFieldChildren()
2123 {
2124     Node* node = this->node();
2125     if (!isHTMLInputElement(node))
2126         return;
2127 
2128     HTMLInputElement& input = toHTMLInputElement(*node);
2129     Element* spinButtonElement = input.userAgentShadowRoot()->getElementById(ShadowElementNames::spinButton());
2130     if (!spinButtonElement || !spinButtonElement->isSpinButtonElement())
2131         return;
2132 
2133     AXSpinButton* axSpinButton = toAXSpinButton(axObjectCache()->getOrCreate(SpinButtonRole));
2134     axSpinButton->setSpinButtonElement(toSpinButtonElement(spinButtonElement));
2135     axSpinButton->setParent(this);
2136     m_children.append(axSpinButton);
2137 }
2138 
addImageMapChildren()2139 void AXRenderObject::addImageMapChildren()
2140 {
2141     RenderBoxModelObject* cssBox = renderBoxModelObject();
2142     if (!cssBox || !cssBox->isRenderImage())
2143         return;
2144 
2145     HTMLMapElement* map = toRenderImage(cssBox)->imageMap();
2146     if (!map)
2147         return;
2148 
2149     for (HTMLAreaElement* area = Traversal<HTMLAreaElement>::firstWithin(*map); area; area = Traversal<HTMLAreaElement>::next(*area, map)) {
2150         // add an <area> element for this child if it has a link
2151         if (area->isLink()) {
2152             AXImageMapLink* areaObject = toAXImageMapLink(axObjectCache()->getOrCreate(ImageMapLinkRole));
2153             areaObject->setHTMLAreaElement(area);
2154             areaObject->setHTMLMapElement(map);
2155             areaObject->setParent(this);
2156             if (!areaObject->accessibilityIsIgnored())
2157                 m_children.append(areaObject);
2158             else
2159                 axObjectCache()->remove(areaObject->axObjectID());
2160         }
2161     }
2162 }
2163 
addCanvasChildren()2164 void AXRenderObject::addCanvasChildren()
2165 {
2166     if (!isHTMLCanvasElement(node()))
2167         return;
2168 
2169     // If it's a canvas, it won't have rendered children, but it might have accessible fallback content.
2170     // Clear m_haveChildren because AXNodeObject::addChildren will expect it to be false.
2171     ASSERT(!m_children.size());
2172     m_haveChildren = false;
2173     AXNodeObject::addChildren();
2174 }
2175 
addAttachmentChildren()2176 void AXRenderObject::addAttachmentChildren()
2177 {
2178     if (!isAttachment())
2179         return;
2180 
2181     // FrameView's need to be inserted into the AX hierarchy when encountered.
2182     Widget* widget = widgetForAttachmentView();
2183     if (!widget || !widget->isFrameView())
2184         return;
2185 
2186     AXObject* axWidget = axObjectCache()->getOrCreate(widget);
2187     if (!axWidget->accessibilityIsIgnored())
2188         m_children.append(axWidget);
2189 }
2190 
addRemoteSVGChildren()2191 void AXRenderObject::addRemoteSVGChildren()
2192 {
2193     AXSVGRoot* root = remoteSVGRootElement();
2194     if (!root)
2195         return;
2196 
2197     root->setParent(this);
2198 
2199     if (root->accessibilityIsIgnored()) {
2200         AccessibilityChildrenVector children = root->children();
2201         unsigned length = children.size();
2202         for (unsigned i = 0; i < length; ++i)
2203             m_children.append(children[i]);
2204     } else {
2205         m_children.append(root);
2206     }
2207 }
2208 
ariaSelectedRows(AccessibilityChildrenVector & result)2209 void AXRenderObject::ariaSelectedRows(AccessibilityChildrenVector& result)
2210 {
2211     // Get all the rows.
2212     AccessibilityChildrenVector allRows;
2213     if (isTree())
2214         ariaTreeRows(allRows);
2215     else if (isAXTable() && toAXTable(this)->supportsSelectedRows())
2216         allRows = toAXTable(this)->rows();
2217 
2218     // Determine which rows are selected.
2219     bool isMulti = isMultiSelectable();
2220 
2221     // Prefer active descendant over aria-selected.
2222     AXObject* activeDesc = activeDescendant();
2223     if (activeDesc && (activeDesc->isTreeItem() || activeDesc->isTableRow())) {
2224         result.append(activeDesc);
2225         if (!isMulti)
2226             return;
2227     }
2228 
2229     unsigned count = allRows.size();
2230     for (unsigned k = 0; k < count; ++k) {
2231         if (allRows[k]->isSelected()) {
2232             result.append(allRows[k]);
2233             if (!isMulti)
2234                 break;
2235         }
2236     }
2237 }
2238 
elementAttributeValue(const QualifiedName & attributeName) const2239 bool AXRenderObject::elementAttributeValue(const QualifiedName& attributeName) const
2240 {
2241     if (!m_renderer)
2242         return false;
2243 
2244     return equalIgnoringCase(getAttribute(attributeName), "true");
2245 }
2246 
inheritsPresentationalRole() const2247 bool AXRenderObject::inheritsPresentationalRole() const
2248 {
2249     // ARIA states if an item can get focus, it should not be presentational.
2250     if (canSetFocusAttribute())
2251         return false;
2252 
2253     // ARIA spec says that when a parent object is presentational, and it has required child elements,
2254     // those child elements are also presentational. For example, <li> becomes presentational from <ul>.
2255     // http://www.w3.org/WAI/PF/aria/complete#presentation
2256     if (roleValue() != ListItemRole && roleValue() != ListMarkerRole)
2257         return false;
2258 
2259     AXObject* parent = parentObject();
2260     if (!parent->isAXRenderObject())
2261         return false;
2262 
2263     Node* elementNode = toAXRenderObject(parent)->node();
2264     if (!elementNode || !elementNode->isElementNode())
2265         return false;
2266 
2267     QualifiedName tagName = toElement(elementNode)->tagQName();
2268     if (tagName == ulTag || tagName == olTag || tagName == dlTag)
2269         return parent->roleValue() == PresentationalRole;
2270 
2271     return false;
2272 }
2273 
computeElementRect() const2274 LayoutRect AXRenderObject::computeElementRect() const
2275 {
2276     RenderObject* obj = m_renderer;
2277 
2278     if (!obj)
2279         return LayoutRect();
2280 
2281     if (obj->node()) // If we are a continuation, we want to make sure to use the primary renderer.
2282         obj = obj->node()->renderer();
2283 
2284     // absoluteFocusRingQuads will query the hierarchy below this element, which for large webpages can be very slow.
2285     // For a web area, which will have the most elements of any element, absoluteQuads should be used.
2286     // We should also use absoluteQuads for SVG elements, otherwise transforms won't be applied.
2287     Vector<FloatQuad> quads;
2288 
2289     if (obj->isText())
2290         toRenderText(obj)->absoluteQuads(quads, 0, RenderText::ClipToEllipsis);
2291     else if (isWebArea() || obj->isSVGRoot())
2292         obj->absoluteQuads(quads);
2293     else
2294         obj->absoluteFocusRingQuads(quads);
2295 
2296     LayoutRect result = boundingBoxForQuads(obj, quads);
2297 
2298     Document* document = this->document();
2299     if (document && document->isSVGDocument())
2300         offsetBoundingBoxForRemoteSVGElement(result);
2301 
2302     // The size of the web area should be the content size, not the clipped size.
2303     if (isWebArea() && obj->frame()->view())
2304         result.setSize(obj->frame()->view()->contentsSize());
2305 
2306     // Checkboxes and radio buttons include their label as part of their rect.
2307     if (isCheckboxOrRadio()) {
2308         HTMLLabelElement* label = labelForElement(toElement(m_renderer->node()));
2309         if (label && label->renderer()) {
2310             LayoutRect labelRect = axObjectCache()->getOrCreate(label)->elementRect();
2311             result.unite(labelRect);
2312         }
2313     }
2314 
2315     return result;
2316 }
2317 
2318 } // namespace WebCore
2319