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