Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(519)

Side by Side Diff: Source/core/accessibility/AXRenderObject.cpp

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

Powered by Google App Engine
This is Rietveld 408576698