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