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