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