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