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