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 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 "AccessibilityRenderObject.h" 31 32#include "AXObjectCache.h" 33#include "AccessibilityImageMapLink.h" 34#include "AccessibilityListBox.h" 35#include "AccessibilitySVGRoot.h" 36#include "AccessibilitySpinButton.h" 37#include "AccessibilityTable.h" 38#include "CachedImage.h" 39#include "Chrome.h" 40#include "ElementIterator.h" 41#include "EventNames.h" 42#include "FloatRect.h" 43#include "Frame.h" 44#include "FrameLoader.h" 45#include "FrameSelection.h" 46#include "HTMLAreaElement.h" 47#include "HTMLFormElement.h" 48#include "HTMLFrameElementBase.h" 49#include "HTMLImageElement.h" 50#include "HTMLInputElement.h" 51#include "HTMLLabelElement.h" 52#include "HTMLMapElement.h" 53#include "HTMLNames.h" 54#include "HTMLOptGroupElement.h" 55#include "HTMLOptionElement.h" 56#include "HTMLOptionsCollection.h" 57#include "HTMLSelectElement.h" 58#include "HTMLTableElement.h" 59#include "HTMLTextAreaElement.h" 60#include "HitTestRequest.h" 61#include "HitTestResult.h" 62#include "Image.h" 63#include "LocalizedStrings.h" 64#include "MathMLNames.h" 65#include "NodeList.h" 66#include "Page.h" 67#include "ProgressTracker.h" 68#include "RenderButton.h" 69#include "RenderFieldset.h" 70#include "RenderFileUploadControl.h" 71#include "RenderHTMLCanvas.h" 72#include "RenderImage.h" 73#include "RenderInline.h" 74#include "RenderIterator.h" 75#include "RenderLayer.h" 76#include "RenderLineBreak.h" 77#include "RenderListBox.h" 78#include "RenderListMarker.h" 79#include "RenderMathMLBlock.h" 80#include "RenderMathMLFraction.h" 81#include "RenderMathMLOperator.h" 82#include "RenderMenuList.h" 83#include "RenderSVGRoot.h" 84#include "RenderSVGShape.h" 85#include "RenderText.h" 86#include "RenderTextControl.h" 87#include "RenderTextControlSingleLine.h" 88#include "RenderTextFragment.h" 89#include "RenderTheme.h" 90#include "RenderView.h" 91#include "RenderWidget.h" 92#include "RenderedPosition.h" 93#include "SVGDocument.h" 94#include "SVGImage.h" 95#include "SVGImageChromeClient.h" 96#include "SVGNames.h" 97#include "SVGSVGElement.h" 98#include "Text.h" 99#include "TextControlInnerElements.h" 100#include "TextIterator.h" 101#include "VisibleUnits.h" 102#include "htmlediting.h" 103#include <wtf/NeverDestroyed.h> 104#include <wtf/StdLibExtras.h> 105#include <wtf/text/StringBuilder.h> 106#include <wtf/unicode/CharacterNames.h> 107 108namespace WebCore { 109 110using namespace HTMLNames; 111 112AccessibilityRenderObject::AccessibilityRenderObject(RenderObject* renderer) 113 : AccessibilityNodeObject(renderer->node()) 114 , m_renderer(renderer) 115{ 116#ifndef NDEBUG 117 m_renderer->setHasAXObject(true); 118#endif 119} 120 121AccessibilityRenderObject::~AccessibilityRenderObject() 122{ 123 ASSERT(isDetached()); 124} 125 126void AccessibilityRenderObject::init() 127{ 128 AccessibilityNodeObject::init(); 129} 130 131PassRefPtr<AccessibilityRenderObject> AccessibilityRenderObject::create(RenderObject* renderer) 132{ 133 return adoptRef(new AccessibilityRenderObject(renderer)); 134} 135 136void AccessibilityRenderObject::detach(AccessibilityDetachmentType detachmentType, AXObjectCache* cache) 137{ 138 AccessibilityNodeObject::detach(detachmentType, cache); 139 140 detachRemoteSVGRoot(); 141 142#ifndef NDEBUG 143 if (m_renderer) 144 m_renderer->setHasAXObject(false); 145#endif 146 m_renderer = 0; 147} 148 149RenderBoxModelObject* AccessibilityRenderObject::renderBoxModelObject() const 150{ 151 if (!m_renderer || !m_renderer->isBoxModelObject()) 152 return 0; 153 return toRenderBoxModelObject(m_renderer); 154} 155 156void AccessibilityRenderObject::setRenderer(RenderObject* renderer) 157{ 158 m_renderer = renderer; 159 setNode(renderer->node()); 160} 161 162static inline bool isInlineWithContinuation(RenderObject* object) 163{ 164 if (!object->isBoxModelObject()) 165 return false; 166 167 RenderBoxModelObject* renderer = toRenderBoxModelObject(object); 168 if (!renderer->isRenderInline()) 169 return false; 170 171 return toRenderInline(renderer)->continuation(); 172} 173 174static inline RenderObject* firstChildInContinuation(RenderInline& renderer) 175{ 176 auto continuation = renderer.continuation(); 177 178 while (continuation) { 179 if (continuation->isRenderBlock()) 180 return continuation; 181 if (RenderObject* child = continuation->firstChild()) 182 return child; 183 continuation = toRenderInline(continuation)->continuation(); 184 } 185 186 return nullptr; 187} 188 189static inline RenderObject* firstChildConsideringContinuation(RenderObject* renderer) 190{ 191 RenderObject* firstChild = renderer->firstChildSlow(); 192 193 if (!firstChild && isInlineWithContinuation(renderer)) 194 firstChild = firstChildInContinuation(toRenderInline(*renderer)); 195 196 return firstChild; 197} 198 199 200static inline RenderObject* lastChildConsideringContinuation(RenderObject* renderer) 201{ 202 RenderObject* lastChild = renderer->lastChildSlow(); 203 RenderObject* prev; 204 RenderObject* cur = renderer; 205 206 if (!cur->isRenderInline() && !cur->isRenderBlock()) 207 return renderer; 208 209 while (cur) { 210 prev = cur; 211 212 if (RenderObject* lc = cur->lastChildSlow()) 213 lastChild = lc; 214 215 if (cur->isRenderInline()) { 216 cur = toRenderInline(cur)->inlineElementContinuation(); 217 ASSERT_UNUSED(prev, cur || !toRenderInline(prev)->continuation()); 218 } else 219 cur = toRenderBlock(cur)->inlineElementContinuation(); 220 } 221 222 return lastChild; 223} 224 225AccessibilityObject* AccessibilityRenderObject::firstChild() const 226{ 227 if (!m_renderer) 228 return 0; 229 230 RenderObject* firstChild = firstChildConsideringContinuation(m_renderer); 231 232 // If an object can't have children, then it is using this method to help 233 // calculate some internal property (like its description). 234 // In this case, it should check the Node level for children in case they're 235 // not rendered (like a <meter> element). 236 if (!firstChild && !canHaveChildren()) 237 return AccessibilityNodeObject::firstChild(); 238 239 return axObjectCache()->getOrCreate(firstChild); 240} 241 242AccessibilityObject* AccessibilityRenderObject::lastChild() const 243{ 244 if (!m_renderer) 245 return 0; 246 247 RenderObject* lastChild = lastChildConsideringContinuation(m_renderer); 248 249 if (!lastChild && !canHaveChildren()) 250 return AccessibilityNodeObject::lastChild(); 251 252 return axObjectCache()->getOrCreate(lastChild); 253} 254 255static inline RenderInline* startOfContinuations(RenderObject* r) 256{ 257 if (r->isInlineElementContinuation()) 258 return toRenderInline(r->node()->renderer()); 259 260 // Blocks with a previous continuation always have a next continuation 261 if (r->isRenderBlock() && toRenderBlock(r)->inlineElementContinuation()) 262 return toRenderInline(toRenderBlock(r)->inlineElementContinuation()->element()->renderer()); 263 264 return 0; 265} 266 267static inline RenderObject* endOfContinuations(RenderObject* renderer) 268{ 269 RenderObject* prev = renderer; 270 RenderObject* cur = renderer; 271 272 if (!cur->isRenderInline() && !cur->isRenderBlock()) 273 return renderer; 274 275 while (cur) { 276 prev = cur; 277 if (cur->isRenderInline()) { 278 cur = toRenderInline(cur)->inlineElementContinuation(); 279 ASSERT(cur || !toRenderInline(prev)->continuation()); 280 } else 281 cur = toRenderBlock(cur)->inlineElementContinuation(); 282 } 283 284 return prev; 285} 286 287 288static inline RenderObject* childBeforeConsideringContinuations(RenderInline* r, RenderObject* child) 289{ 290 RenderBoxModelObject* curContainer = r; 291 RenderObject* cur = 0; 292 RenderObject* prev = 0; 293 294 while (curContainer) { 295 if (curContainer->isRenderInline()) { 296 cur = curContainer->firstChild(); 297 while (cur) { 298 if (cur == child) 299 return prev; 300 prev = cur; 301 cur = cur->nextSibling(); 302 } 303 304 curContainer = toRenderInline(curContainer)->continuation(); 305 } else if (curContainer->isRenderBlock()) { 306 if (curContainer == child) 307 return prev; 308 309 prev = curContainer; 310 curContainer = toRenderBlock(curContainer)->inlineElementContinuation(); 311 } 312 } 313 314 ASSERT_NOT_REACHED(); 315 316 return 0; 317} 318 319static inline bool firstChildIsInlineContinuation(RenderObject* renderer) 320{ 321 RenderObject* child = renderer->firstChildSlow(); 322 return child && child->isInlineElementContinuation(); 323} 324 325AccessibilityObject* AccessibilityRenderObject::previousSibling() const 326{ 327 if (!m_renderer) 328 return 0; 329 330 RenderObject* previousSibling = 0; 331 332 // Case 1: The node is a block and is an inline's continuation. In that case, the inline's 333 // last child is our previous sibling (or further back in the continuation chain) 334 RenderInline* startOfConts; 335 if (m_renderer->isRenderBlock() && (startOfConts = startOfContinuations(m_renderer))) 336 previousSibling = childBeforeConsideringContinuations(startOfConts, m_renderer); 337 338 // Case 2: Anonymous block parent of the end of a continuation - skip all the way to before 339 // the parent of the start, since everything in between will be linked up via the continuation. 340 else if (m_renderer->isAnonymousBlock() && firstChildIsInlineContinuation(m_renderer)) { 341 RenderObject* firstParent = startOfContinuations(m_renderer->firstChildSlow())->parent(); 342 while (firstChildIsInlineContinuation(firstParent)) 343 firstParent = startOfContinuations(firstParent->firstChildSlow())->parent(); 344 previousSibling = firstParent->previousSibling(); 345 } 346 347 // Case 3: The node has an actual previous sibling 348 else if (RenderObject* ps = m_renderer->previousSibling()) 349 previousSibling = ps; 350 351 // Case 4: This node has no previous siblings, but its parent is an inline, 352 // and is another node's inline continutation. Follow the continuation chain. 353 else if (m_renderer->parent()->isRenderInline() && (startOfConts = startOfContinuations(m_renderer->parent()))) 354 previousSibling = childBeforeConsideringContinuations(startOfConts, m_renderer->parent()->firstChild()); 355 356 if (!previousSibling) 357 return 0; 358 359 return axObjectCache()->getOrCreate(previousSibling); 360} 361 362static inline bool lastChildHasContinuation(RenderObject* renderer) 363{ 364 RenderObject* child = renderer->lastChildSlow(); 365 return child && isInlineWithContinuation(child); 366} 367 368AccessibilityObject* AccessibilityRenderObject::nextSibling() const 369{ 370 if (!m_renderer) 371 return 0; 372 373 RenderObject* nextSibling = 0; 374 375 // Case 1: node is a block and has an inline continuation. Next sibling is the inline continuation's 376 // first child. 377 RenderInline* inlineContinuation; 378 if (m_renderer->isRenderBlock() && (inlineContinuation = toRenderBlock(m_renderer)->inlineElementContinuation())) 379 nextSibling = firstChildConsideringContinuation(inlineContinuation); 380 381 // Case 2: Anonymous block parent of the start of a continuation - skip all the way to 382 // after the parent of the end, since everything in between will be linked up via the continuation. 383 else if (m_renderer->isAnonymousBlock() && lastChildHasContinuation(m_renderer)) { 384 RenderElement* lastParent = endOfContinuations(toRenderBlock(m_renderer)->lastChild())->parent(); 385 while (lastChildHasContinuation(lastParent)) 386 lastParent = endOfContinuations(lastParent->lastChild())->parent(); 387 nextSibling = lastParent->nextSibling(); 388 } 389 390 // Case 3: node has an actual next sibling 391 else if (RenderObject* ns = m_renderer->nextSibling()) 392 nextSibling = ns; 393 394 // Case 4: node is an inline with a continuation. Next sibling is the next sibling of the end 395 // of the continuation chain. 396 else if (isInlineWithContinuation(m_renderer)) 397 nextSibling = endOfContinuations(m_renderer)->nextSibling(); 398 399 // Case 5: node has no next sibling, and its parent is an inline with a continuation. 400 else if (isInlineWithContinuation(m_renderer->parent())) { 401 auto continuation = toRenderInline(m_renderer->parent())->continuation(); 402 403 // Case 5a: continuation is a block - in this case the block itself is the next sibling. 404 if (continuation->isRenderBlock()) 405 nextSibling = continuation; 406 // Case 5b: continuation is an inline - in this case the inline's first child is the next sibling 407 else 408 nextSibling = firstChildConsideringContinuation(continuation); 409 } 410 411 if (!nextSibling) 412 return 0; 413 414 return axObjectCache()->getOrCreate(nextSibling); 415} 416 417static RenderBoxModelObject* nextContinuation(RenderObject* renderer) 418{ 419 ASSERT(renderer); 420 if (renderer->isRenderInline() && !renderer->isReplaced()) 421 return toRenderInline(renderer)->continuation(); 422 if (renderer->isRenderBlock()) 423 return toRenderBlock(renderer)->inlineElementContinuation(); 424 return 0; 425} 426 427RenderObject* AccessibilityRenderObject::renderParentObject() const 428{ 429 if (!m_renderer) 430 return 0; 431 432 RenderElement* parent = m_renderer->parent(); 433 434 // Case 1: node is a block and is an inline's continuation. Parent 435 // is the start of the continuation chain. 436 RenderInline* startOfConts = nullptr; 437 RenderObject* firstChild = 0; 438 if (m_renderer->isRenderBlock() && (startOfConts = startOfContinuations(m_renderer))) 439 parent = startOfConts; 440 441 // Case 2: node's parent is an inline which is some node's continuation; parent is 442 // the earliest node in the continuation chain. 443 else if (parent && parent->isRenderInline() && (startOfConts = startOfContinuations(parent))) 444 parent = startOfConts; 445 446 // Case 3: The first sibling is the beginning of a continuation chain. Find the origin of that continuation. 447 else if (parent && (firstChild = parent->firstChild()) && firstChild->node()) { 448 // Get the node's renderer and follow that continuation chain until the first child is found 449 RenderObject* nodeRenderFirstChild = firstChild->node()->renderer(); 450 while (nodeRenderFirstChild != firstChild) { 451 for (RenderObject* contsTest = nodeRenderFirstChild; contsTest; contsTest = nextContinuation(contsTest)) { 452 if (contsTest == firstChild) { 453 parent = nodeRenderFirstChild->parent(); 454 break; 455 } 456 } 457 RenderObject* parentFirstChild = parent->firstChild(); 458 if (firstChild == parentFirstChild) 459 break; 460 firstChild = parentFirstChild; 461 if (!firstChild->node()) 462 break; 463 nodeRenderFirstChild = firstChild->node()->renderer(); 464 } 465 } 466 467 return parent; 468} 469 470AccessibilityObject* AccessibilityRenderObject::parentObjectIfExists() const 471{ 472 AXObjectCache* cache = axObjectCache(); 473 if (!cache) 474 return nullptr; 475 476 // WebArea's parent should be the scroll view containing it. 477 if (isWebArea()) 478 return cache->get(&m_renderer->view().frameView()); 479 480 return cache->get(renderParentObject()); 481} 482 483AccessibilityObject* AccessibilityRenderObject::parentObject() const 484{ 485 if (!m_renderer) 486 return 0; 487 488 if (ariaRoleAttribute() == MenuBarRole) 489 return axObjectCache()->getOrCreate(m_renderer->parent()); 490 491 // menuButton and its corresponding menu are DOM siblings, but Accessibility needs them to be parent/child 492 if (ariaRoleAttribute() == MenuRole) { 493 AccessibilityObject* parent = menuButtonForMenu(); 494 if (parent) 495 return parent; 496 } 497 498 AXObjectCache* cache = axObjectCache(); 499 if (!cache) 500 return nullptr; 501 502 RenderObject* parentObj = renderParentObject(); 503 if (parentObj) 504 return cache->getOrCreate(parentObj); 505 506 // WebArea's parent should be the scroll view containing it. 507 if (isWebArea()) 508 return cache->getOrCreate(&m_renderer->view().frameView()); 509 510 return 0; 511} 512 513bool AccessibilityRenderObject::isAttachment() const 514{ 515 RenderBoxModelObject* renderer = renderBoxModelObject(); 516 if (!renderer) 517 return false; 518 // Widgets are the replaced elements that we represent to AX as attachments 519 bool isWidget = renderer->isWidget(); 520 521 return isWidget && ariaRoleAttribute() == UnknownRole; 522} 523 524bool AccessibilityRenderObject::isFileUploadButton() const 525{ 526 if (m_renderer && m_renderer->node() && isHTMLInputElement(m_renderer->node())) { 527 HTMLInputElement* input = toHTMLInputElement(m_renderer->node()); 528 return input->isFileUpload(); 529 } 530 531 return false; 532} 533 534bool AccessibilityRenderObject::isReadOnly() const 535{ 536 ASSERT(m_renderer); 537 538 if (isWebArea()) { 539 if (HTMLElement* body = m_renderer->document().body()) { 540 if (body->hasEditableStyle()) 541 return false; 542 } 543 544 return !m_renderer->document().hasEditableStyle(); 545 } 546 547 return AccessibilityNodeObject::isReadOnly(); 548} 549 550bool AccessibilityRenderObject::isOffScreen() const 551{ 552 ASSERT(m_renderer); 553 IntRect contentRect = pixelSnappedIntRect(m_renderer->absoluteClippedOverflowRect()); 554 // FIXME: unclear if we need LegacyIOSDocumentVisibleRect. 555 IntRect viewRect = m_renderer->view().frameView().visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect); 556 viewRect.intersect(contentRect); 557 return viewRect.isEmpty(); 558} 559 560Element* AccessibilityRenderObject::anchorElement() const 561{ 562 if (!m_renderer) 563 return 0; 564 565 AXObjectCache* cache = axObjectCache(); 566 if (!cache) 567 return nullptr; 568 569 RenderObject* currRenderer; 570 571 // Search up the render tree for a RenderObject with a DOM node. Defer to an earlier continuation, though. 572 for (currRenderer = m_renderer; currRenderer && !currRenderer->node(); currRenderer = currRenderer->parent()) { 573 if (currRenderer->isAnonymousBlock()) { 574 RenderObject* continuation = toRenderBlock(currRenderer)->continuation(); 575 if (continuation) 576 return cache->getOrCreate(continuation)->anchorElement(); 577 } 578 } 579 580 // bail if none found 581 if (!currRenderer) 582 return 0; 583 584 // search up the DOM tree for an anchor element 585 // NOTE: this assumes that any non-image with an anchor is an HTMLAnchorElement 586 Node* node = currRenderer->node(); 587 for ( ; node; node = node->parentNode()) { 588 if (isHTMLAnchorElement(node) || (node->renderer() && cache->getOrCreate(node->renderer())->isAnchor())) 589 return toElement(node); 590 } 591 592 return 0; 593} 594 595String AccessibilityRenderObject::helpText() const 596{ 597 if (!m_renderer) 598 return String(); 599 600 const AtomicString& ariaHelp = getAttribute(aria_helpAttr); 601 if (!ariaHelp.isEmpty()) 602 return ariaHelp; 603 604 String describedBy = ariaDescribedByAttribute(); 605 if (!describedBy.isEmpty()) 606 return describedBy; 607 608 String description = accessibilityDescription(); 609 for (RenderObject* curr = m_renderer; curr; curr = curr->parent()) { 610 if (curr->node() && curr->node()->isHTMLElement()) { 611 const AtomicString& summary = toElement(curr->node())->getAttribute(summaryAttr); 612 if (!summary.isEmpty()) 613 return summary; 614 615 // The title attribute should be used as help text unless it is already being used as descriptive text. 616 const AtomicString& title = toElement(curr->node())->getAttribute(titleAttr); 617 if (!title.isEmpty() && description != title) 618 return title; 619 } 620 621 // Only take help text from an ancestor element if its a group or an unknown role. If help was 622 // added to those kinds of elements, it is likely it was meant for a child element. 623 AccessibilityObject* axObj = axObjectCache()->getOrCreate(curr); 624 if (axObj) { 625 AccessibilityRole role = axObj->roleValue(); 626 if (role != GroupRole && role != UnknownRole) 627 break; 628 } 629 } 630 631 return String(); 632} 633 634String AccessibilityRenderObject::textUnderElement(AccessibilityTextUnderElementMode mode) const 635{ 636 if (!m_renderer) 637 return String(); 638 639 if (m_renderer->isFileUploadControl()) 640 return toRenderFileUploadControl(m_renderer)->buttonValue(); 641 642 // Reflect when a content author has explicitly marked a line break. 643 if (m_renderer->isBR()) 644 return ASCIILiteral("\n"); 645 646#if ENABLE(MATHML) 647 // Math operators create RenderText nodes on the fly that are not tied into the DOM in a reasonable way, 648 // so rangeOfContents does not work for them (nor does regular text selection). 649 if (m_renderer->isText() && m_renderer->isAnonymous() && ancestorsOfType<RenderMathMLOperator>(*m_renderer).first()) 650 return toRenderText(*m_renderer).text(); 651#endif 652 653 // We use a text iterator for text objects AND for those cases where we are 654 // explicitly asking for the full text under a given element. 655 if (m_renderer->isText() || mode.childrenInclusion == AccessibilityTextUnderElementMode::TextUnderElementModeIncludeAllChildren) { 656 // If possible, use a text iterator to get the text, so that whitespace 657 // is handled consistently. 658 Document* nodeDocument = 0; 659 RefPtr<Range> textRange; 660 if (Node* node = m_renderer->node()) { 661 nodeDocument = &node->document(); 662 textRange = rangeOfContents(*node); 663 } else { 664 // For anonymous blocks, we work around not having a direct node to create a range from 665 // defining one based in the two external positions defining the boundaries of the subtree. 666 RenderObject* firstChildRenderer = m_renderer->firstChildSlow(); 667 RenderObject* lastChildRenderer = m_renderer->lastChildSlow(); 668 if (firstChildRenderer && lastChildRenderer) { 669 ASSERT(firstChildRenderer->node()); 670 ASSERT(lastChildRenderer->node()); 671 672 // We define the start and end positions for the range as the ones right before and after 673 // the first and the last nodes in the DOM tree that is wrapped inside the anonymous block. 674 Node* firstNodeInBlock = firstChildRenderer->node(); 675 Position startPosition = positionInParentBeforeNode(firstNodeInBlock); 676 Position endPosition = positionInParentAfterNode(lastChildRenderer->node()); 677 678 nodeDocument = &firstNodeInBlock->document(); 679 textRange = Range::create(*nodeDocument, startPosition, endPosition); 680 } 681 } 682 683 if (nodeDocument && textRange) { 684 if (Frame* frame = nodeDocument->frame()) { 685 // catch stale WebCoreAXObject (see <rdar://problem/3960196>) 686 if (frame->document() != nodeDocument) 687 return String(); 688 return plainText(textRange.get(), textIteratorBehaviorForTextRange()); 689 } 690 } 691 692 // Sometimes text fragments don't have Nodes associated with them (like when 693 // CSS content is used to insert text or when a RenderCounter is used.) 694 if (m_renderer->isText()) { 695 RenderText* renderTextObject = toRenderText(m_renderer); 696 if (renderTextObject->isTextFragment()) { 697 698 // The alt attribute may be set on a text fragment through CSS, which should be honored. 699 const String& altText = toRenderTextFragment(renderTextObject)->altText(); 700 if (!altText.isEmpty()) 701 return altText; 702 return String(static_cast<RenderTextFragment*>(m_renderer)->contentString()); 703 } 704 705 return String(renderTextObject->text()); 706 } 707 } 708 709 return AccessibilityNodeObject::textUnderElement(mode); 710} 711 712Node* AccessibilityRenderObject::node() const 713{ 714 if (!m_renderer) 715 return 0; 716 if (m_renderer->isRenderView()) 717 return &m_renderer->document(); 718 return m_renderer->node(); 719} 720 721String AccessibilityRenderObject::stringValue() const 722{ 723 if (!m_renderer) 724 return String(); 725 726 if (isPasswordField()) 727 return passwordFieldValue(); 728 729 RenderBoxModelObject* cssBox = renderBoxModelObject(); 730 731 if (ariaRoleAttribute() == StaticTextRole) { 732 String staticText = text(); 733 if (!staticText.length()) 734 staticText = textUnderElement(); 735 return staticText; 736 } 737 738 if (m_renderer->isText()) 739 return textUnderElement(); 740 741 if (cssBox && cssBox->isMenuList()) { 742 // RenderMenuList will go straight to the text() of its selected item. 743 // This has to be overridden in the case where the selected item has an ARIA label. 744 HTMLSelectElement* selectElement = toHTMLSelectElement(m_renderer->node()); 745 int selectedIndex = selectElement->selectedIndex(); 746 const Vector<HTMLElement*>& listItems = selectElement->listItems(); 747 if (selectedIndex >= 0 && static_cast<size_t>(selectedIndex) < listItems.size()) { 748 const AtomicString& overriddenDescription = listItems[selectedIndex]->fastGetAttribute(aria_labelAttr); 749 if (!overriddenDescription.isNull()) 750 return overriddenDescription; 751 } 752 return toRenderMenuList(m_renderer)->text(); 753 } 754 755 if (m_renderer->isListMarker()) 756 return toRenderListMarker(*m_renderer).text(); 757 758 if (isWebArea()) 759 return String(); 760 761 if (isTextControl()) 762 return text(); 763 764 if (m_renderer->isFileUploadControl()) 765 return toRenderFileUploadControl(m_renderer)->fileTextValue(); 766 767 // FIXME: We might need to implement a value here for more types 768 // FIXME: It would be better not to advertise a value at all for the types for which we don't implement one; 769 // this would require subclassing or making accessibilityAttributeNames do something other than return a 770 // single static array. 771 return String(); 772} 773 774HTMLLabelElement* AccessibilityRenderObject::labelElementContainer() const 775{ 776 if (!m_renderer) 777 return 0; 778 779 // the control element should not be considered part of the label 780 if (isControl()) 781 return 0; 782 783 // find if this has a parent that is a label 784 for (Node* parentNode = m_renderer->node(); parentNode; parentNode = parentNode->parentNode()) { 785 if (isHTMLLabelElement(parentNode)) 786 return toHTMLLabelElement(parentNode); 787 } 788 789 return 0; 790} 791 792// The boundingBox for elements within the remote SVG element needs to be offset by its position 793// within the parent page, otherwise they are in relative coordinates only. 794void AccessibilityRenderObject::offsetBoundingBoxForRemoteSVGElement(LayoutRect& rect) const 795{ 796 for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) { 797 if (parent->isAccessibilitySVGRoot()) { 798 rect.moveBy(parent->parentObject()->boundingBoxRect().location()); 799 break; 800 } 801 } 802} 803 804LayoutRect AccessibilityRenderObject::boundingBoxRect() const 805{ 806 RenderObject* obj = m_renderer; 807 808 if (!obj) 809 return LayoutRect(); 810 811 if (obj->node()) // If we are a continuation, we want to make sure to use the primary renderer. 812 obj = obj->node()->renderer(); 813 814 // absoluteFocusRingQuads will query the hierarchy below this element, which for large webpages can be very slow. 815 // For a web area, which will have the most elements of any element, absoluteQuads should be used. 816 // We should also use absoluteQuads for SVG elements, otherwise transforms won't be applied. 817 Vector<FloatQuad> quads; 818 bool isSVGRoot = false; 819 820 if (obj->isSVGRoot()) 821 isSVGRoot = true; 822 823 if (obj->isText()) 824 quads = toRenderText(obj)->absoluteQuadsClippedToEllipsis(); 825 else if (isWebArea() || isSVGRoot) 826 obj->absoluteQuads(quads); 827 else 828 obj->absoluteFocusRingQuads(quads); 829 830 LayoutRect result = boundingBoxForQuads(obj, quads); 831 832 Document* document = this->document(); 833 if (document && document->isSVGDocument()) 834 offsetBoundingBoxForRemoteSVGElement(result); 835 836 // The size of the web area should be the content size, not the clipped size. 837 if (isWebArea()) 838 result.setSize(obj->view().frameView().contentsSize()); 839 840 return result; 841} 842 843LayoutRect AccessibilityRenderObject::checkboxOrRadioRect() const 844{ 845 if (!m_renderer) 846 return LayoutRect(); 847 848 HTMLLabelElement* label = labelForElement(toElement(m_renderer->node())); 849 if (!label || !label->renderer()) 850 return boundingBoxRect(); 851 852 LayoutRect labelRect = axObjectCache()->getOrCreate(label)->elementRect(); 853 labelRect.unite(boundingBoxRect()); 854 return labelRect; 855} 856 857LayoutRect AccessibilityRenderObject::elementRect() const 858{ 859 // a checkbox or radio button should encompass its label 860 if (isCheckboxOrRadio()) 861 return checkboxOrRadioRect(); 862 863 return boundingBoxRect(); 864} 865 866bool AccessibilityRenderObject::supportsPath() const 867{ 868 if (m_renderer && m_renderer->isSVGShape()) 869 return true; 870 871 return false; 872} 873 874Path AccessibilityRenderObject::elementPath() const 875{ 876 if (m_renderer && m_renderer->isSVGShape() && toRenderSVGShape(m_renderer)->hasPath()) { 877 Path path = toRenderSVGShape(m_renderer)->path(); 878 879 // The SVG path is in terms of the parent's bounding box. The path needs to be offset to frame coordinates. 880 if (auto svgRoot = ancestorsOfType<RenderSVGRoot>(*m_renderer).first()) { 881 LayoutPoint parentOffset = axObjectCache()->getOrCreate(&*svgRoot)->elementRect().location(); 882 path.transform(AffineTransform().translate(parentOffset.x(), parentOffset.y())); 883 } 884 885 return path; 886 } 887 888 return Path(); 889} 890 891IntPoint AccessibilityRenderObject::clickPoint() 892{ 893 // Headings are usually much wider than their textual content. If the mid point is used, often it can be wrong. 894 if (isHeading() && children().size() == 1) 895 return children()[0]->clickPoint(); 896 897 // use the default position unless this is an editable web area, in which case we use the selection bounds. 898 if (!isWebArea() || isReadOnly()) 899 return AccessibilityObject::clickPoint(); 900 901 VisibleSelection visSelection = selection(); 902 VisiblePositionRange range = VisiblePositionRange(visSelection.visibleStart(), visSelection.visibleEnd()); 903 IntRect bounds = boundsForVisiblePositionRange(range); 904#if PLATFORM(COCOA) 905 bounds.setLocation(m_renderer->view().frameView().screenToContents(bounds.location())); 906#endif 907 return IntPoint(bounds.x() + (bounds.width() / 2), bounds.y() - (bounds.height() / 2)); 908} 909 910AccessibilityObject* AccessibilityRenderObject::internalLinkElement() const 911{ 912 Element* element = anchorElement(); 913 if (!element) 914 return 0; 915 916 // Right now, we do not support ARIA links as internal link elements 917 if (!isHTMLAnchorElement(element)) 918 return 0; 919 HTMLAnchorElement* anchor = toHTMLAnchorElement(element); 920 921 URL linkURL = anchor->href(); 922 String fragmentIdentifier = linkURL.fragmentIdentifier(); 923 if (fragmentIdentifier.isEmpty()) 924 return 0; 925 926 // check if URL is the same as current URL 927 URL documentURL = m_renderer->document().url(); 928 if (!equalIgnoringFragmentIdentifier(documentURL, linkURL)) 929 return 0; 930 931 Node* linkedNode = m_renderer->document().findAnchor(fragmentIdentifier); 932 if (!linkedNode) 933 return 0; 934 935 // The element we find may not be accessible, so find the first accessible object. 936 return firstAccessibleObjectFromNode(linkedNode); 937} 938 939ESpeak AccessibilityRenderObject::speakProperty() const 940{ 941 if (!m_renderer) 942 return AccessibilityObject::speakProperty(); 943 944 return m_renderer->style().speak(); 945} 946 947void AccessibilityRenderObject::addRadioButtonGroupMembers(AccessibilityChildrenVector& linkedUIElements) const 948{ 949 if (!m_renderer || roleValue() != RadioButtonRole) 950 return; 951 952 Node* node = m_renderer->node(); 953 if (!node || !isHTMLInputElement(node)) 954 return; 955 956 HTMLInputElement* input = toHTMLInputElement(node); 957 // if there's a form, then this is easy 958 if (input->form()) { 959 Vector<Ref<Element>> formElements; 960 input->form()->getNamedElements(input->name(), formElements); 961 962 for (auto& associateElement : formElements) { 963 if (AccessibilityObject* object = axObjectCache()->getOrCreate(&associateElement.get())) 964 linkedUIElements.append(object); 965 } 966 } else { 967 RefPtr<NodeList> list = node->document().getElementsByTagName("input"); 968 unsigned len = list->length(); 969 for (unsigned i = 0; i < len; ++i) { 970 if (isHTMLInputElement(list->item(i))) { 971 HTMLInputElement* associateElement = toHTMLInputElement(list->item(i)); 972 if (associateElement->isRadioButton() && associateElement->name() == input->name()) { 973 if (AccessibilityObject* object = axObjectCache()->getOrCreate(associateElement)) 974 linkedUIElements.append(object); 975 } 976 } 977 } 978 } 979} 980 981// linked ui elements could be all the related radio buttons in a group 982// or an internal anchor connection 983void AccessibilityRenderObject::linkedUIElements(AccessibilityChildrenVector& linkedUIElements) const 984{ 985 ariaFlowToElements(linkedUIElements); 986 987 if (isAnchor()) { 988 AccessibilityObject* linkedAXElement = internalLinkElement(); 989 if (linkedAXElement) 990 linkedUIElements.append(linkedAXElement); 991 } 992 993 if (roleValue() == RadioButtonRole) 994 addRadioButtonGroupMembers(linkedUIElements); 995} 996 997bool AccessibilityRenderObject::hasTextAlternative() const 998{ 999 // ARIA: section 2A, bullet #3 says if aria-labeledby or aria-label appears, it should 1000 // override the "label" element association. 1001 return ariaAccessibilityDescription().length(); 1002} 1003 1004bool AccessibilityRenderObject::ariaHasPopup() const 1005{ 1006 return elementAttributeValue(aria_haspopupAttr); 1007} 1008 1009void AccessibilityRenderObject::ariaElementsFromAttribute(AccessibilityChildrenVector& children, const QualifiedName& attributeName) const 1010{ 1011 Vector<Element*> elements; 1012 elementsFromAttribute(elements, attributeName); 1013 AXObjectCache* cache = axObjectCache(); 1014 for (const auto& element : elements) { 1015 if (AccessibilityObject* axObject = cache->getOrCreate(element)) 1016 children.append(axObject); 1017 } 1018} 1019 1020bool AccessibilityRenderObject::supportsARIAFlowTo() const 1021{ 1022 return !getAttribute(aria_flowtoAttr).isEmpty(); 1023} 1024 1025void AccessibilityRenderObject::ariaFlowToElements(AccessibilityChildrenVector& flowTo) const 1026{ 1027 ariaElementsFromAttribute(flowTo, aria_flowtoAttr); 1028} 1029 1030bool AccessibilityRenderObject::supportsARIADescribedBy() const 1031{ 1032 return !getAttribute(aria_describedbyAttr).isEmpty(); 1033} 1034 1035void AccessibilityRenderObject::ariaDescribedByElements(AccessibilityChildrenVector& ariaDescribedBy) const 1036{ 1037 ariaElementsFromAttribute(ariaDescribedBy, aria_describedbyAttr); 1038} 1039 1040bool AccessibilityRenderObject::supportsARIAControls() const 1041{ 1042 return !getAttribute(aria_controlsAttr).isEmpty(); 1043} 1044 1045void AccessibilityRenderObject::ariaControlsElements(AccessibilityChildrenVector& ariaControls) const 1046{ 1047 ariaElementsFromAttribute(ariaControls, aria_controlsAttr); 1048} 1049 1050bool AccessibilityRenderObject::supportsARIADropping() const 1051{ 1052 const AtomicString& dropEffect = getAttribute(aria_dropeffectAttr); 1053 return !dropEffect.isEmpty(); 1054} 1055 1056bool AccessibilityRenderObject::supportsARIADragging() const 1057{ 1058 const AtomicString& grabbed = getAttribute(aria_grabbedAttr); 1059 return equalIgnoringCase(grabbed, "true") || equalIgnoringCase(grabbed, "false"); 1060} 1061 1062bool AccessibilityRenderObject::isARIAGrabbed() 1063{ 1064 return elementAttributeValue(aria_grabbedAttr); 1065} 1066 1067void AccessibilityRenderObject::determineARIADropEffects(Vector<String>& effects) 1068{ 1069 const AtomicString& dropEffects = getAttribute(aria_dropeffectAttr); 1070 if (dropEffects.isEmpty()) { 1071 effects.clear(); 1072 return; 1073 } 1074 1075 String dropEffectsString = dropEffects.string(); 1076 dropEffectsString.replace('\n', ' '); 1077 dropEffectsString.split(' ', effects); 1078} 1079 1080bool AccessibilityRenderObject::exposesTitleUIElement() const 1081{ 1082 if (!isControl()) 1083 return false; 1084 1085 // If this control is ignored (because it's invisible), 1086 // then the label needs to be exposed so it can be visible to accessibility. 1087 if (accessibilityIsIgnored()) 1088 return true; 1089 1090 // When controls have their own descriptions, the title element should be ignored. 1091 if (hasTextAlternative()) 1092 return false; 1093 1094 return true; 1095} 1096 1097AccessibilityObject* AccessibilityRenderObject::titleUIElement() const 1098{ 1099 if (!m_renderer) 1100 return 0; 1101 1102 // if isFieldset is true, the renderer is guaranteed to be a RenderFieldset 1103 if (isFieldset()) 1104 return axObjectCache()->getOrCreate(toRenderFieldset(m_renderer)->findLegend(RenderFieldset::IncludeFloatingOrOutOfFlow)); 1105 1106 Node* node = m_renderer->node(); 1107 if (!node || !node->isElementNode()) 1108 return 0; 1109 HTMLLabelElement* label = labelForElement(toElement(node)); 1110 if (label && label->renderer()) 1111 return axObjectCache()->getOrCreate(label); 1112 1113 return 0; 1114} 1115 1116bool AccessibilityRenderObject::isAllowedChildOfTree() const 1117{ 1118 // Determine if this is in a tree. If so, we apply special behavior to make it work like an AXOutline. 1119 AccessibilityObject* axObj = parentObject(); 1120 bool isInTree = false; 1121 while (axObj) { 1122 if (axObj->isTree()) { 1123 isInTree = true; 1124 break; 1125 } 1126 axObj = axObj->parentObject(); 1127 } 1128 1129 // If the object is in a tree, only tree items should be exposed (and the children of tree items). 1130 if (isInTree) { 1131 AccessibilityRole role = roleValue(); 1132 if (role != TreeItemRole && role != StaticTextRole) 1133 return false; 1134 } 1135 return true; 1136} 1137 1138static AccessibilityObjectInclusion objectInclusionFromAltText(const String& altText) 1139{ 1140 // Don't ignore an image that has an alt tag. 1141 if (!altText.containsOnlyWhitespace()) 1142 return IncludeObject; 1143 1144 // The informal standard is to ignore images with zero-length alt strings. 1145 if (!altText.isNull()) 1146 return IgnoreObject; 1147 1148 return DefaultBehavior; 1149} 1150 1151AccessibilityObjectInclusion AccessibilityRenderObject::defaultObjectInclusion() const 1152{ 1153 // The following cases can apply to any element that's a subclass of AccessibilityRenderObject. 1154 1155 if (!m_renderer) 1156 return IgnoreObject; 1157 1158 if (m_renderer->style().visibility() != VISIBLE) { 1159 // aria-hidden is meant to override visibility as the determinant in AX hierarchy inclusion. 1160 if (equalIgnoringCase(getAttribute(aria_hiddenAttr), "false")) 1161 return DefaultBehavior; 1162 1163 return IgnoreObject; 1164 } 1165 1166 return AccessibilityObject::defaultObjectInclusion(); 1167} 1168 1169bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const 1170{ 1171#ifndef NDEBUG 1172 ASSERT(m_initialized); 1173#endif 1174 1175 // Check first if any of the common reasons cause this element to be ignored. 1176 // Then process other use cases that need to be applied to all the various roles 1177 // that AccessibilityRenderObjects take on. 1178 AccessibilityObjectInclusion decision = defaultObjectInclusion(); 1179 if (decision == IncludeObject) 1180 return false; 1181 if (decision == IgnoreObject) 1182 return true; 1183 1184 // If this element is within a parent that cannot have children, it should not be exposed. 1185 if (isDescendantOfBarrenParent()) 1186 return true; 1187 1188 if (roleValue() == IgnoredRole) 1189 return true; 1190 1191 if (roleValue() == PresentationalRole || inheritsPresentationalRole()) 1192 return true; 1193 1194 // An ARIA tree can only have tree items and static text as children. 1195 if (!isAllowedChildOfTree()) 1196 return true; 1197 1198 // Allow the platform to decide if the attachment is ignored or not. 1199 if (isAttachment()) 1200 return accessibilityIgnoreAttachment(); 1201 1202 // ignore popup menu items because AppKit does 1203 if (ancestorsOfType<RenderMenuList>(*m_renderer).first()) 1204 return true; 1205 1206 // find out if this element is inside of a label element. 1207 // if so, it may be ignored because it's the label for a checkbox or radio button 1208 AccessibilityObject* controlObject = correspondingControlForLabelElement(); 1209 if (controlObject && !controlObject->exposesTitleUIElement() && controlObject->isCheckboxOrRadio()) 1210 return true; 1211 1212 if (m_renderer->isBR()) 1213 return true; 1214 1215 if (m_renderer->isText()) { 1216 // static text beneath MenuItems and MenuButtons are just reported along with the menu item, so it's ignored on an individual level 1217 AccessibilityObject* parent = parentObjectUnignored(); 1218 if (parent && (parent->isMenuItem() || parent->ariaRoleAttribute() == MenuButtonRole)) 1219 return true; 1220 auto& renderText = toRenderText(*m_renderer); 1221 if (!renderText.hasRenderedText()) 1222 return true; 1223 1224 // static text beneath TextControls is reported along with the text control text so it's ignored. 1225 for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) { 1226 if (parent->roleValue() == TextFieldRole) 1227 return true; 1228 } 1229 1230 // The alt attribute may be set on a text fragment through CSS, which should be honored. 1231 if (renderText.isTextFragment()) { 1232 AccessibilityObjectInclusion altTextInclusion = objectInclusionFromAltText(toRenderTextFragment(&renderText)->altText()); 1233 if (altTextInclusion == IgnoreObject) 1234 return true; 1235 if (altTextInclusion == IncludeObject) 1236 return false; 1237 } 1238 1239 // text elements that are just empty whitespace should not be returned 1240 return renderText.text()->containsOnlyWhitespace(); 1241 } 1242 1243 if (isHeading()) 1244 return false; 1245 1246 if (isLink()) 1247 return false; 1248 1249 // all controls are accessible 1250 if (isControl()) 1251 return false; 1252 1253 if (ariaRoleAttribute() != UnknownRole) 1254 return false; 1255 1256 if (roleValue() == HorizontalRuleRole) 1257 return false; 1258 1259 // don't ignore labels, because they serve as TitleUIElements 1260 Node* node = m_renderer->node(); 1261 if (node && isHTMLLabelElement(node)) 1262 return false; 1263 1264 // Anything that is content editable should not be ignored. 1265 // However, one cannot just call node->hasEditableStyle() since that will ask if its parents 1266 // are also editable. Only the top level content editable region should be exposed. 1267 if (hasContentEditableAttributeSet()) 1268 return false; 1269 1270 switch (roleValue()) { 1271 case AudioRole: 1272 case ListItemRole: 1273 case VideoRole: 1274 return false; 1275 default: 1276 break; 1277 } 1278 1279 // if this element has aria attributes on it, it should not be ignored. 1280 if (supportsARIAAttributes()) 1281 return false; 1282 1283#if ENABLE(MATHML) 1284 // First check if this is a special case within the math tree that needs to be ignored. 1285 if (isIgnoredElementWithinMathTree()) 1286 return true; 1287 // Otherwise all other math elements are in the tree. 1288 if (isMathElement()) 1289 return false; 1290#endif 1291 1292 if (m_renderer->isRenderBlockFlow() && m_renderer->childrenInline() && !canSetFocusAttribute()) 1293 return !toRenderBlockFlow(m_renderer)->hasLines() && !mouseButtonListener(); 1294 1295 // ignore images seemingly used as spacers 1296 if (isImage()) { 1297 1298 // If the image can take focus, it should not be ignored, lest the user not be able to interact with something important. 1299 if (canSetFocusAttribute()) 1300 return false; 1301 1302 // First check the RenderImage's altText (which can be set through a style sheet, or come from the Element). 1303 // However, if this is not a native image, fallback to the attribute on the Element. 1304 AccessibilityObjectInclusion altTextInclusion = DefaultBehavior; 1305 bool isRenderImage = m_renderer && m_renderer->isRenderImage(); 1306 if (isRenderImage) 1307 altTextInclusion = objectInclusionFromAltText(toRenderImage(m_renderer)->altText()); 1308 else 1309 altTextInclusion = objectInclusionFromAltText(getAttribute(altAttr).string()); 1310 1311 if (altTextInclusion == IgnoreObject) 1312 return true; 1313 if (altTextInclusion == IncludeObject) 1314 return false; 1315 1316 // If an image has a title attribute on it, accessibility should be lenient and allow it to appear in the hierarchy (according to WAI-ARIA). 1317 if (!getAttribute(titleAttr).isEmpty()) 1318 return false; 1319 1320 if (isRenderImage) { 1321 // check for one-dimensional image 1322 RenderImage* image = toRenderImage(m_renderer); 1323 if (image->height() <= 1 || image->width() <= 1) 1324 return true; 1325 1326 // check whether rendered image was stretched from one-dimensional file image 1327 if (image->cachedImage()) { 1328 LayoutSize imageSize = image->cachedImage()->imageSizeForRenderer(toRenderElement(m_renderer), image->view().zoomFactor()); 1329 return imageSize.height() <= 1 || imageSize.width() <= 1; 1330 } 1331 } 1332 return false; 1333 } 1334 1335 if (isCanvas()) { 1336 if (canvasHasFallbackContent()) 1337 return false; 1338 1339 if (m_renderer->isBox()) { 1340 RenderBox* canvasBox = toRenderBox(m_renderer); 1341 if (canvasBox->height() <= 1 || canvasBox->width() <= 1) 1342 return true; 1343 } 1344 // Otherwise fall through; use presence of help text, title, or description to decide. 1345 } 1346 1347 if (isWebArea() || m_renderer->isListMarker()) 1348 return false; 1349 1350 // Using the presence of an accessible name to decide an element's visibility is not 1351 // as definitive as previous checks, so this should remain as one of the last. 1352 if (hasAttributesRequiredForInclusion()) 1353 return false; 1354 1355 // Don't ignore generic focusable elements like <div tabindex=0> 1356 // unless they're completely empty, with no children. 1357 if (isGenericFocusableElement() && node->firstChild()) 1358 return false; 1359 1360 // <span> tags are inline tags and not meant to convey information if they have no other aria 1361 // information on them. If we don't ignore them, they may emit signals expected to come from 1362 // their parent. In addition, because included spans are GroupRole objects, and GroupRole 1363 // objects are often containers with meaningful information, the inclusion of a span can have 1364 // the side effect of causing the immediate parent accessible to be ignored. This is especially 1365 // problematic for platforms which have distinct roles for textual block elements. 1366 if (node && node->hasTagName(spanTag)) 1367 return true; 1368 1369 // Other non-ignored host language elements 1370 if (node && node->hasTagName(dfnTag)) 1371 return false; 1372 1373 // By default, objects should be ignored so that the AX hierarchy is not 1374 // filled with unnecessary items. 1375 return true; 1376} 1377 1378bool AccessibilityRenderObject::isLoaded() const 1379{ 1380 return !m_renderer->document().parser(); 1381} 1382 1383double AccessibilityRenderObject::estimatedLoadingProgress() const 1384{ 1385 if (!m_renderer) 1386 return 0; 1387 1388 if (isLoaded()) 1389 return 1.0; 1390 1391 Page* page = m_renderer->document().page(); 1392 if (!page) 1393 return 0; 1394 1395 return page->progress().estimatedProgress(); 1396} 1397 1398int AccessibilityRenderObject::layoutCount() const 1399{ 1400 if (!m_renderer->isRenderView()) 1401 return 0; 1402 return toRenderView(*m_renderer).frameView().layoutCount(); 1403} 1404 1405String AccessibilityRenderObject::text() const 1406{ 1407 if (isPasswordField()) 1408 return passwordFieldValue(); 1409 1410 return AccessibilityNodeObject::text(); 1411} 1412 1413int AccessibilityRenderObject::textLength() const 1414{ 1415 ASSERT(isTextControl()); 1416 1417 if (isPasswordField()) 1418 return passwordFieldValue().length(); 1419 1420 return text().length(); 1421} 1422 1423PlainTextRange AccessibilityRenderObject::documentBasedSelectedTextRange() const 1424{ 1425 Node* node = m_renderer->node(); 1426 if (!node) 1427 return PlainTextRange(); 1428 1429 VisibleSelection visibleSelection = selection(); 1430 RefPtr<Range> currentSelectionRange = visibleSelection.toNormalizedRange(); 1431 if (!currentSelectionRange || !currentSelectionRange->intersectsNode(node, IGNORE_EXCEPTION)) 1432 return PlainTextRange(); 1433 1434 int start = indexForVisiblePosition(visibleSelection.start()); 1435 int end = indexForVisiblePosition(visibleSelection.end()); 1436 1437 return PlainTextRange(start, end - start); 1438} 1439 1440String AccessibilityRenderObject::selectedText() const 1441{ 1442 ASSERT(isTextControl()); 1443 1444 if (isPasswordField()) 1445 return String(); // need to return something distinct from empty string 1446 1447 if (isNativeTextControl()) { 1448 HTMLTextFormControlElement& textControl = toRenderTextControl(m_renderer)->textFormControlElement(); 1449 return textControl.selectedText(); 1450 } 1451 1452 return doAXStringForRange(documentBasedSelectedTextRange()); 1453} 1454 1455const AtomicString& AccessibilityRenderObject::accessKey() const 1456{ 1457 Node* node = m_renderer->node(); 1458 if (!node) 1459 return nullAtom; 1460 if (!node->isElementNode()) 1461 return nullAtom; 1462 return toElement(node)->getAttribute(accesskeyAttr); 1463} 1464 1465VisibleSelection AccessibilityRenderObject::selection() const 1466{ 1467 return m_renderer->frame().selection().selection(); 1468} 1469 1470PlainTextRange AccessibilityRenderObject::selectedTextRange() const 1471{ 1472 ASSERT(isTextControl()); 1473 1474 if (isPasswordField()) 1475 return PlainTextRange(); 1476 1477 AccessibilityRole ariaRole = ariaRoleAttribute(); 1478 if (isNativeTextControl() && ariaRole == UnknownRole) { 1479 HTMLTextFormControlElement& textControl = toRenderTextControl(m_renderer)->textFormControlElement(); 1480 return PlainTextRange(textControl.selectionStart(), textControl.selectionEnd() - textControl.selectionStart()); 1481 } 1482 1483 return documentBasedSelectedTextRange(); 1484} 1485 1486void AccessibilityRenderObject::setSelectedTextRange(const PlainTextRange& range) 1487{ 1488 if (isNativeTextControl()) { 1489 HTMLTextFormControlElement& textControl = toRenderTextControl(m_renderer)->textFormControlElement(); 1490 textControl.setSelectionRange(range.start, range.start + range.length); 1491 return; 1492 } 1493 1494 Node* node = m_renderer->node(); 1495 m_renderer->frame().selection().setSelection(VisibleSelection(Position(node, range.start, Position::PositionIsOffsetInAnchor), 1496 Position(node, range.start + range.length, Position::PositionIsOffsetInAnchor), DOWNSTREAM)); 1497} 1498 1499URL AccessibilityRenderObject::url() const 1500{ 1501 if (isAnchor() && isHTMLAnchorElement(m_renderer->node())) { 1502 if (HTMLAnchorElement* anchor = toHTMLAnchorElement(anchorElement())) 1503 return anchor->href(); 1504 } 1505 1506 if (isWebArea()) 1507 return m_renderer->document().url(); 1508 1509 if (isImage() && m_renderer->node() && isHTMLImageElement(m_renderer->node())) 1510 return toHTMLImageElement(m_renderer->node())->src(); 1511 1512 if (isInputImage()) 1513 return toHTMLInputElement(m_renderer->node())->src(); 1514 1515 return URL(); 1516} 1517 1518bool AccessibilityRenderObject::isUnvisited() const 1519{ 1520 // FIXME: Is it a privacy violation to expose unvisited information to accessibility APIs? 1521 return m_renderer->style().isLink() && m_renderer->style().insideLink() == InsideUnvisitedLink; 1522} 1523 1524bool AccessibilityRenderObject::isVisited() const 1525{ 1526 // FIXME: Is it a privacy violation to expose visited information to accessibility APIs? 1527 return m_renderer->style().isLink() && m_renderer->style().insideLink() == InsideVisitedLink; 1528} 1529 1530void AccessibilityRenderObject::setElementAttributeValue(const QualifiedName& attributeName, bool value) 1531{ 1532 if (!m_renderer) 1533 return; 1534 1535 Node* node = m_renderer->node(); 1536 if (!node || !node->isElementNode()) 1537 return; 1538 1539 Element* element = toElement(node); 1540 element->setAttribute(attributeName, (value) ? "true" : "false"); 1541} 1542 1543bool AccessibilityRenderObject::elementAttributeValue(const QualifiedName& attributeName) const 1544{ 1545 if (!m_renderer) 1546 return false; 1547 1548 return equalIgnoringCase(getAttribute(attributeName), "true"); 1549} 1550 1551bool AccessibilityRenderObject::isSelected() const 1552{ 1553 if (!m_renderer) 1554 return false; 1555 1556 Node* node = m_renderer->node(); 1557 if (!node) 1558 return false; 1559 1560 const AtomicString& ariaSelected = getAttribute(aria_selectedAttr); 1561 if (equalIgnoringCase(ariaSelected, "true")) 1562 return true; 1563 1564 if (isTabItem() && isTabItemSelected()) 1565 return true; 1566 1567 return false; 1568} 1569 1570bool AccessibilityRenderObject::isTabItemSelected() const 1571{ 1572 if (!isTabItem() || !m_renderer) 1573 return false; 1574 1575 Node* node = m_renderer->node(); 1576 if (!node || !node->isElementNode()) 1577 return false; 1578 1579 // The ARIA spec says a tab item can also be selected if it is aria-labeled by a tabpanel 1580 // that has keyboard focus inside of it, or if a tabpanel in its aria-controls list has KB 1581 // focus inside of it. 1582 AccessibilityObject* focusedElement = focusedUIElement(); 1583 if (!focusedElement) 1584 return false; 1585 1586 Vector<Element*> elements; 1587 elementsFromAttribute(elements, aria_controlsAttr); 1588 1589 AXObjectCache* cache = axObjectCache(); 1590 if (!cache) 1591 return false; 1592 1593 for (const auto& element : elements) { 1594 AccessibilityObject* tabPanel = cache->getOrCreate(element); 1595 1596 // A tab item should only control tab panels. 1597 if (!tabPanel || tabPanel->roleValue() != TabPanelRole) 1598 continue; 1599 1600 AccessibilityObject* checkFocusElement = focusedElement; 1601 // Check if the focused element is a descendant of the element controlled by the tab item. 1602 while (checkFocusElement) { 1603 if (tabPanel == checkFocusElement) 1604 return true; 1605 checkFocusElement = checkFocusElement->parentObject(); 1606 } 1607 } 1608 1609 return false; 1610} 1611 1612bool AccessibilityRenderObject::isFocused() const 1613{ 1614 if (!m_renderer) 1615 return false; 1616 1617 Document& document = m_renderer->document(); 1618 1619 Element* focusedElement = document.focusedElement(); 1620 if (!focusedElement) 1621 return false; 1622 1623 // A web area is represented by the Document node in the DOM tree, which isn't focusable. 1624 // Check instead if the frame's selection controller is focused 1625 if (focusedElement == m_renderer->node() 1626 || (roleValue() == WebAreaRole && document.frame()->selection().isFocusedAndActive())) 1627 return true; 1628 1629 return false; 1630} 1631 1632void AccessibilityRenderObject::setFocused(bool on) 1633{ 1634 if (!canSetFocusAttribute()) 1635 return; 1636 1637 Document* document = this->document(); 1638 Node* node = this->node(); 1639 1640 if (!on || !node || !node->isElementNode()) { 1641 document->setFocusedElement(0); 1642 return; 1643 } 1644 1645 // If this node is already the currently focused node, then calling focus() won't do anything. 1646 // That is a problem when focus is removed from the webpage to chrome, and then returns. 1647 // In these cases, we need to do what keyboard and mouse focus do, which is reset focus first. 1648 if (document->focusedElement() == node) 1649 document->setFocusedElement(0); 1650 1651 toElement(node)->focus(); 1652} 1653 1654void AccessibilityRenderObject::setSelectedRows(AccessibilityChildrenVector& selectedRows) 1655{ 1656 // Setting selected only makes sense in trees and tables (and tree-tables). 1657 AccessibilityRole role = roleValue(); 1658 if (role != TreeRole && role != TreeGridRole && role != TableRole) 1659 return; 1660 1661 bool isMulti = isMultiSelectable(); 1662 unsigned count = selectedRows.size(); 1663 if (count > 1 && !isMulti) 1664 count = 1; 1665 1666 for (const auto& selectedRow : selectedRows) 1667 selectedRow->setSelected(true); 1668} 1669 1670void AccessibilityRenderObject::setValue(const String& string) 1671{ 1672 if (!m_renderer || !m_renderer->node() || !m_renderer->node()->isElementNode()) 1673 return; 1674 Element* element = toElement(m_renderer->node()); 1675 1676 if (!m_renderer->isBoxModelObject()) 1677 return; 1678 RenderBoxModelObject* renderer = toRenderBoxModelObject(m_renderer); 1679 1680 // FIXME: Do we want to do anything here for ARIA textboxes? 1681 if (renderer->isTextField()) { 1682 // FIXME: This is not safe! Other elements could have a TextField renderer. 1683 toHTMLInputElement(element)->setValue(string); 1684 } else if (renderer->isTextArea()) { 1685 // FIXME: This is not safe! Other elements could have a TextArea renderer. 1686 toHTMLTextAreaElement(element)->setValue(string); 1687 } 1688} 1689 1690void AccessibilityRenderObject::ariaOwnsElements(AccessibilityChildrenVector& axObjects) const 1691{ 1692 ariaElementsFromAttribute(axObjects, aria_ownsAttr); 1693} 1694 1695bool AccessibilityRenderObject::supportsARIAOwns() const 1696{ 1697 if (!m_renderer) 1698 return false; 1699 const AtomicString& ariaOwns = getAttribute(aria_ownsAttr); 1700 1701 return !ariaOwns.isEmpty(); 1702} 1703 1704RenderView* AccessibilityRenderObject::topRenderer() const 1705{ 1706 Document* topDoc = topDocument(); 1707 if (!topDoc) 1708 return 0; 1709 1710 return topDoc->renderView(); 1711} 1712 1713Document* AccessibilityRenderObject::document() const 1714{ 1715 if (!m_renderer) 1716 return 0; 1717 return &m_renderer->document(); 1718} 1719 1720Widget* AccessibilityRenderObject::widget() const 1721{ 1722 if (!m_renderer->isBoxModelObject() || !toRenderBoxModelObject(m_renderer)->isWidget()) 1723 return 0; 1724 return toRenderWidget(m_renderer)->widget(); 1725} 1726 1727AccessibilityObject* AccessibilityRenderObject::accessibilityParentForImageMap(HTMLMapElement* map) const 1728{ 1729 // find an image that is using this map 1730 if (!map) 1731 return 0; 1732 1733 HTMLImageElement* imageElement = map->imageElement(); 1734 if (!imageElement) 1735 return 0; 1736 1737 if (AXObjectCache* cache = axObjectCache()) 1738 return cache->getOrCreate(imageElement); 1739 1740 return nullptr; 1741} 1742 1743void AccessibilityRenderObject::getDocumentLinks(AccessibilityChildrenVector& result) 1744{ 1745 Document& document = m_renderer->document(); 1746 RefPtr<HTMLCollection> links = document.links(); 1747 for (unsigned i = 0; Node* curr = links->item(i); i++) { 1748 RenderObject* obj = curr->renderer(); 1749 if (obj) { 1750 RefPtr<AccessibilityObject> axobj = document.axObjectCache()->getOrCreate(obj); 1751 ASSERT(axobj); 1752 if (!axobj->accessibilityIsIgnored() && axobj->isLink()) 1753 result.append(axobj); 1754 } else { 1755 Node* parent = curr->parentNode(); 1756 if (parent && isHTMLAreaElement(curr) && isHTMLMapElement(parent)) { 1757 AccessibilityImageMapLink* areaObject = toAccessibilityImageMapLink(axObjectCache()->getOrCreate(ImageMapLinkRole)); 1758 HTMLMapElement* map = toHTMLMapElement(parent); 1759 areaObject->setHTMLAreaElement(toHTMLAreaElement(curr)); 1760 areaObject->setHTMLMapElement(map); 1761 areaObject->setParent(accessibilityParentForImageMap(map)); 1762 1763 result.append(areaObject); 1764 } 1765 } 1766 } 1767} 1768 1769FrameView* AccessibilityRenderObject::documentFrameView() const 1770{ 1771 if (!m_renderer) 1772 return 0; 1773 1774 // this is the RenderObject's Document's Frame's FrameView 1775 return &m_renderer->view().frameView(); 1776} 1777 1778Widget* AccessibilityRenderObject::widgetForAttachmentView() const 1779{ 1780 if (!isAttachment()) 1781 return 0; 1782 return toRenderWidget(m_renderer)->widget(); 1783} 1784 1785// This function is like a cross-platform version of - (WebCoreTextMarkerRange*)textMarkerRange. It returns 1786// a Range that we can convert to a WebCoreTextMarkerRange in the Obj-C file 1787VisiblePositionRange AccessibilityRenderObject::visiblePositionRange() const 1788{ 1789 if (!m_renderer) 1790 return VisiblePositionRange(); 1791 1792 // construct VisiblePositions for start and end 1793 Node* node = m_renderer->node(); 1794 if (!node) 1795 return VisiblePositionRange(); 1796 1797 VisiblePosition startPos = firstPositionInOrBeforeNode(node); 1798 VisiblePosition endPos = lastPositionInOrAfterNode(node); 1799 1800 // the VisiblePositions are equal for nodes like buttons, so adjust for that 1801 // FIXME: Really? [button, 0] and [button, 1] are distinct (before and after the button) 1802 // I expect this code is only hit for things like empty divs? In which case I don't think 1803 // the behavior is correct here -- eseidel 1804 if (startPos == endPos) { 1805 endPos = endPos.next(); 1806 if (endPos.isNull()) 1807 endPos = startPos; 1808 } 1809 1810 return VisiblePositionRange(startPos, endPos); 1811} 1812 1813VisiblePositionRange AccessibilityRenderObject::visiblePositionRangeForLine(unsigned lineCount) const 1814{ 1815 if (!lineCount || !m_renderer) 1816 return VisiblePositionRange(); 1817 1818 // iterate over the lines 1819 // FIXME: this is wrong when lineNumber is lineCount+1, because nextLinePosition takes you to the 1820 // last offset of the last line 1821 VisiblePosition visiblePos = m_renderer->view().positionForPoint(IntPoint(), nullptr); 1822 VisiblePosition savedVisiblePos; 1823 while (--lineCount) { 1824 savedVisiblePos = visiblePos; 1825 visiblePos = nextLinePosition(visiblePos, 0); 1826 if (visiblePos.isNull() || visiblePos == savedVisiblePos) 1827 return VisiblePositionRange(); 1828 } 1829 1830 // make a caret selection for the marker position, then extend it to the line 1831 // NOTE: ignores results of sel.modify because it returns false when 1832 // starting at an empty line. The resulting selection in that case 1833 // will be a caret at visiblePos. 1834 FrameSelection selection; 1835 selection.setSelection(VisibleSelection(visiblePos)); 1836 selection.modify(FrameSelection::AlterationExtend, DirectionRight, LineBoundary); 1837 1838 return VisiblePositionRange(selection.selection().visibleStart(), selection.selection().visibleEnd()); 1839} 1840 1841VisiblePosition AccessibilityRenderObject::visiblePositionForIndex(int index) const 1842{ 1843 if (!m_renderer) 1844 return VisiblePosition(); 1845 1846 if (isNativeTextControl()) 1847 return toRenderTextControl(m_renderer)->textFormControlElement().visiblePositionForIndex(index); 1848 1849 if (!allowsTextRanges() && !m_renderer->isText()) 1850 return VisiblePosition(); 1851 1852 Node* node = m_renderer->node(); 1853 if (!node) 1854 return VisiblePosition(); 1855 1856 return visiblePositionForIndexUsingCharacterIterator(node, index); 1857} 1858 1859int AccessibilityRenderObject::indexForVisiblePosition(const VisiblePosition& pos) const 1860{ 1861 if (isNativeTextControl()) 1862 return toRenderTextControl(m_renderer)->textFormControlElement().indexForVisiblePosition(pos); 1863 1864 if (!isTextControl()) 1865 return 0; 1866 1867 Node* node = m_renderer->node(); 1868 if (!node) 1869 return 0; 1870 1871 Position indexPosition = pos.deepEquivalent(); 1872 if (indexPosition.isNull() || highestEditableRoot(indexPosition, HasEditableAXRole) != node) 1873 return 0; 1874 1875#if PLATFORM(GTK) 1876 // We need to consider replaced elements for GTK, as they will be 1877 // presented with the 'object replacement character' (0xFFFC). 1878 bool forSelectionPreservation = true; 1879#else 1880 bool forSelectionPreservation = false; 1881#endif 1882 1883 return WebCore::indexForVisiblePosition(node, pos, forSelectionPreservation); 1884} 1885 1886Element* AccessibilityRenderObject::rootEditableElementForPosition(const Position& position) const 1887{ 1888 // Find the root editable or pseudo-editable (i.e. having an editable ARIA role) element. 1889 Element* result = 0; 1890 1891 Element* rootEditableElement = position.rootEditableElement(); 1892 1893 for (Element* e = position.element(); e && e != rootEditableElement; e = e->parentElement()) { 1894 if (nodeIsTextControl(e)) 1895 result = e; 1896 if (e->hasTagName(bodyTag)) 1897 break; 1898 } 1899 1900 if (result) 1901 return result; 1902 1903 return rootEditableElement; 1904} 1905 1906bool AccessibilityRenderObject::nodeIsTextControl(const Node* node) const 1907{ 1908 if (!node) 1909 return false; 1910 1911 if (AXObjectCache* cache = axObjectCache()) { 1912 if (AccessibilityObject* axObjectForNode = cache->getOrCreate(const_cast<Node*>(node))) 1913 return axObjectForNode->isTextControl(); 1914 } 1915 1916 return false; 1917} 1918 1919IntRect AccessibilityRenderObject::boundsForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const 1920{ 1921 if (visiblePositionRange.isNull()) 1922 return IntRect(); 1923 1924 // Create a mutable VisiblePositionRange. 1925 VisiblePositionRange range(visiblePositionRange); 1926 LayoutRect rect1 = range.start.absoluteCaretBounds(); 1927 LayoutRect rect2 = range.end.absoluteCaretBounds(); 1928 1929 // readjust for position at the edge of a line. This is to exclude line rect that doesn't need to be accounted in the range bounds 1930 if (rect2.y() != rect1.y()) { 1931 VisiblePosition endOfFirstLine = endOfLine(range.start); 1932 if (range.start == endOfFirstLine) { 1933 range.start.setAffinity(DOWNSTREAM); 1934 rect1 = range.start.absoluteCaretBounds(); 1935 } 1936 if (range.end == endOfFirstLine) { 1937 range.end.setAffinity(UPSTREAM); 1938 rect2 = range.end.absoluteCaretBounds(); 1939 } 1940 } 1941 1942 LayoutRect ourrect = rect1; 1943 ourrect.unite(rect2); 1944 1945 // if the rectangle spans lines and contains multiple text chars, use the range's bounding box intead 1946 if (rect1.maxY() != rect2.maxY()) { 1947 RefPtr<Range> dataRange = makeRange(range.start, range.end); 1948 LayoutRect boundingBox = dataRange->boundingBox(); 1949 String rangeString = plainText(dataRange.get()); 1950 if (rangeString.length() > 1 && !boundingBox.isEmpty()) 1951 ourrect = boundingBox; 1952 } 1953 1954#if PLATFORM(MAC) 1955 return m_renderer->view().frameView().contentsToScreen(pixelSnappedIntRect(ourrect)); 1956#else 1957 return pixelSnappedIntRect(ourrect); 1958#endif 1959} 1960 1961void AccessibilityRenderObject::setSelectedVisiblePositionRange(const VisiblePositionRange& range) const 1962{ 1963 if (range.start.isNull() || range.end.isNull()) 1964 return; 1965 1966 // make selection and tell the document to use it. if it's zero length, then move to that position 1967 if (range.start == range.end) 1968 m_renderer->frame().selection().moveTo(range.start, UserTriggered); 1969 else { 1970 VisibleSelection newSelection = VisibleSelection(range.start, range.end); 1971 m_renderer->frame().selection().setSelection(newSelection); 1972 } 1973} 1974 1975VisiblePosition AccessibilityRenderObject::visiblePositionForPoint(const IntPoint& point) const 1976{ 1977 if (!m_renderer) 1978 return VisiblePosition(); 1979 1980 // convert absolute point to view coordinates 1981 RenderView* renderView = topRenderer(); 1982 if (!renderView) 1983 return VisiblePosition(); 1984 1985#if PLATFORM(COCOA) 1986 FrameView* frameView = &renderView->frameView(); 1987#endif 1988 1989 Node* innerNode = 0; 1990 1991 // locate the node containing the point 1992 LayoutPoint pointResult; 1993 while (1) { 1994 LayoutPoint ourpoint; 1995#if PLATFORM(MAC) 1996 ourpoint = frameView->screenToContents(point); 1997#else 1998 ourpoint = point; 1999#endif 2000 HitTestRequest request(HitTestRequest::ReadOnly | 2001 HitTestRequest::Active); 2002 HitTestResult result(ourpoint); 2003 renderView->hitTest(request, result); 2004 innerNode = result.innerNode(); 2005 if (!innerNode) 2006 return VisiblePosition(); 2007 2008 RenderObject* renderer = innerNode->renderer(); 2009 if (!renderer) 2010 return VisiblePosition(); 2011 2012 pointResult = result.localPoint(); 2013 2014 // done if hit something other than a widget 2015 if (!renderer->isWidget()) 2016 break; 2017 2018 // descend into widget (FRAME, IFRAME, OBJECT...) 2019 Widget* widget = toRenderWidget(renderer)->widget(); 2020 if (!widget || !widget->isFrameView()) 2021 break; 2022 Frame& frame = toFrameView(widget)->frame(); 2023 renderView = frame.document()->renderView(); 2024#if PLATFORM(COCOA) 2025 frameView = toFrameView(widget); 2026#endif 2027 } 2028 2029 return innerNode->renderer()->positionForPoint(pointResult, nullptr); 2030} 2031 2032// NOTE: Consider providing this utility method as AX API 2033VisiblePosition AccessibilityRenderObject::visiblePositionForIndex(unsigned indexValue, bool lastIndexOK) const 2034{ 2035 if (!isTextControl()) 2036 return VisiblePosition(); 2037 2038 // lastIndexOK specifies whether the position after the last character is acceptable 2039 if (indexValue >= text().length()) { 2040 if (!lastIndexOK || indexValue > text().length()) 2041 return VisiblePosition(); 2042 } 2043 VisiblePosition position = visiblePositionForIndex(indexValue); 2044 position.setAffinity(DOWNSTREAM); 2045 return position; 2046} 2047 2048// NOTE: Consider providing this utility method as AX API 2049int AccessibilityRenderObject::index(const VisiblePosition& position) const 2050{ 2051 if (position.isNull() || !isTextControl()) 2052 return -1; 2053 2054 if (renderObjectContainsPosition(m_renderer, position.deepEquivalent())) 2055 return indexForVisiblePosition(position); 2056 2057 return -1; 2058} 2059 2060void AccessibilityRenderObject::lineBreaks(Vector<int>& lineBreaks) const 2061{ 2062 if (!isTextControl()) 2063 return; 2064 2065 VisiblePosition visiblePos = visiblePositionForIndex(0); 2066 VisiblePosition savedVisiblePos = visiblePos; 2067 visiblePos = nextLinePosition(visiblePos, 0); 2068 while (!visiblePos.isNull() && visiblePos != savedVisiblePos) { 2069 lineBreaks.append(indexForVisiblePosition(visiblePos)); 2070 savedVisiblePos = visiblePos; 2071 visiblePos = nextLinePosition(visiblePos, 0); 2072 } 2073} 2074 2075// Given a line number, the range of characters of the text associated with this accessibility 2076// object that contains the line number. 2077PlainTextRange AccessibilityRenderObject::doAXRangeForLine(unsigned lineNumber) const 2078{ 2079 if (!isTextControl()) 2080 return PlainTextRange(); 2081 2082 // iterate to the specified line 2083 VisiblePosition visiblePos = visiblePositionForIndex(0); 2084 VisiblePosition savedVisiblePos; 2085 for (unsigned lineCount = lineNumber; lineCount; lineCount -= 1) { 2086 savedVisiblePos = visiblePos; 2087 visiblePos = nextLinePosition(visiblePos, 0); 2088 if (visiblePos.isNull() || visiblePos == savedVisiblePos) 2089 return PlainTextRange(); 2090 } 2091 2092 // Get the end of the line based on the starting position. 2093 VisiblePosition endPosition = endOfLine(visiblePos); 2094 2095 int index1 = indexForVisiblePosition(visiblePos); 2096 int index2 = indexForVisiblePosition(endPosition); 2097 2098 // add one to the end index for a line break not caused by soft line wrap (to match AppKit) 2099 if (endPosition.affinity() == DOWNSTREAM && endPosition.next().isNotNull()) 2100 index2 += 1; 2101 2102 // return nil rather than an zero-length range (to match AppKit) 2103 if (index1 == index2) 2104 return PlainTextRange(); 2105 2106 return PlainTextRange(index1, index2 - index1); 2107} 2108 2109// The composed character range in the text associated with this accessibility object that 2110// is specified by the given index value. This parameterized attribute returns the complete 2111// range of characters (including surrogate pairs of multi-byte glyphs) at the given index. 2112PlainTextRange AccessibilityRenderObject::doAXRangeForIndex(unsigned index) const 2113{ 2114 if (!isTextControl()) 2115 return PlainTextRange(); 2116 2117 String elementText = text(); 2118 if (!elementText.length() || index > elementText.length() - 1) 2119 return PlainTextRange(); 2120 2121 return PlainTextRange(index, 1); 2122} 2123 2124// A substring of the text associated with this accessibility object that is 2125// specified by the given character range. 2126String AccessibilityRenderObject::doAXStringForRange(const PlainTextRange& range) const 2127{ 2128 if (!range.length) 2129 return String(); 2130 2131 if (!isTextControl()) 2132 return String(); 2133 2134 String elementText = isPasswordField() ? passwordFieldValue() : text(); 2135 return elementText.substring(range.start, range.length); 2136} 2137 2138// The bounding rectangle of the text associated with this accessibility object that is 2139// specified by the given range. This is the bounding rectangle a sighted user would see 2140// on the display screen, in pixels. 2141IntRect AccessibilityRenderObject::doAXBoundsForRange(const PlainTextRange& range) const 2142{ 2143 if (allowsTextRanges()) 2144 return boundsForVisiblePositionRange(visiblePositionRangeForRange(range)); 2145 return IntRect(); 2146} 2147 2148AccessibilityObject* AccessibilityRenderObject::accessibilityImageMapHitTest(HTMLAreaElement* area, const IntPoint& point) const 2149{ 2150 if (!area) 2151 return 0; 2152 2153 AccessibilityObject* parent = 0; 2154 for (Element* mapParent = area->parentElement(); mapParent; mapParent = mapParent->parentElement()) { 2155 if (isHTMLMapElement(mapParent)) { 2156 parent = accessibilityParentForImageMap(toHTMLMapElement(mapParent)); 2157 break; 2158 } 2159 } 2160 if (!parent) 2161 return 0; 2162 2163 for (const auto& child : parent->children()) { 2164 if (child->elementRect().contains(point)) 2165 return child.get(); 2166 } 2167 2168 return 0; 2169} 2170 2171AccessibilityObject* AccessibilityRenderObject::remoteSVGElementHitTest(const IntPoint& point) const 2172{ 2173 AccessibilityObject* remote = remoteSVGRootElement(); 2174 if (!remote) 2175 return 0; 2176 2177 IntSize offset = point - roundedIntPoint(boundingBoxRect().location()); 2178 return remote->accessibilityHitTest(IntPoint(offset)); 2179} 2180 2181AccessibilityObject* AccessibilityRenderObject::elementAccessibilityHitTest(const IntPoint& point) const 2182{ 2183 if (isSVGImage()) 2184 return remoteSVGElementHitTest(point); 2185 2186 return AccessibilityObject::elementAccessibilityHitTest(point); 2187} 2188 2189AccessibilityObject* AccessibilityRenderObject::accessibilityHitTest(const IntPoint& point) const 2190{ 2191 if (!m_renderer || !m_renderer->hasLayer()) 2192 return 0; 2193 2194 RenderLayer* layer = toRenderBox(m_renderer)->layer(); 2195 2196 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::AccessibilityHitTest); 2197 HitTestResult hitTestResult = HitTestResult(point); 2198 layer->hitTest(request, hitTestResult); 2199 if (!hitTestResult.innerNode()) 2200 return 0; 2201 Node* node = hitTestResult.innerNode()->deprecatedShadowAncestorNode(); 2202 2203 if (isHTMLAreaElement(node)) 2204 return accessibilityImageMapHitTest(toHTMLAreaElement(node), point); 2205 2206 if (isHTMLOptionElement(node)) 2207 node = toHTMLOptionElement(node)->ownerSelectElement(); 2208 2209 RenderObject* obj = node->renderer(); 2210 if (!obj) 2211 return 0; 2212 2213 AccessibilityObject* result = obj->document().axObjectCache()->getOrCreate(obj); 2214 result->updateChildrenIfNecessary(); 2215 2216 // Allow the element to perform any hit-testing it might need to do to reach non-render children. 2217 result = result->elementAccessibilityHitTest(point); 2218 2219 if (result && result->accessibilityIsIgnored()) { 2220 // If this element is the label of a control, a hit test should return the control. 2221 AccessibilityObject* controlObject = result->correspondingControlForLabelElement(); 2222 if (controlObject && !controlObject->exposesTitleUIElement()) 2223 return controlObject; 2224 2225 result = result->parentObjectUnignored(); 2226 } 2227 2228 return result; 2229} 2230 2231bool AccessibilityRenderObject::shouldNotifyActiveDescendant() const 2232{ 2233 // We want to notify that the combo box has changed its active descendant, 2234 // but we do not want to change the focus, because focus should remain with the combo box. 2235 if (isComboBox()) 2236 return true; 2237 2238 return shouldFocusActiveDescendant(); 2239} 2240 2241bool AccessibilityRenderObject::shouldFocusActiveDescendant() const 2242{ 2243 switch (ariaRoleAttribute()) { 2244 case GroupRole: 2245 case ListBoxRole: 2246 case MenuRole: 2247 case MenuBarRole: 2248 case RadioGroupRole: 2249 case RowRole: 2250 case PopUpButtonRole: 2251 case ProgressIndicatorRole: 2252 case ToolbarRole: 2253 case OutlineRole: 2254 case TreeRole: 2255 case GridRole: 2256 /* FIXME: replace these with actual roles when they are added to AccessibilityRole 2257 composite 2258 alert 2259 alertdialog 2260 status 2261 timer 2262 */ 2263 return true; 2264 default: 2265 return false; 2266 } 2267} 2268 2269AccessibilityObject* AccessibilityRenderObject::activeDescendant() const 2270{ 2271 if (!m_renderer) 2272 return nullptr; 2273 2274 const AtomicString& activeDescendantAttrStr = getAttribute(aria_activedescendantAttr); 2275 if (activeDescendantAttrStr.isNull() || activeDescendantAttrStr.isEmpty()) 2276 return nullptr; 2277 2278 Element* element = this->element(); 2279 if (!element) 2280 return nullptr; 2281 2282 Element* target = element->treeScope().getElementById(activeDescendantAttrStr); 2283 if (!target) 2284 return nullptr; 2285 2286 if (AXObjectCache* cache = axObjectCache()) { 2287 AccessibilityObject* obj = cache->getOrCreate(target); 2288 if (obj && obj->isAccessibilityRenderObject()) 2289 // an activedescendant is only useful if it has a renderer, because that's what's needed to post the notification 2290 return obj; 2291 } 2292 2293 return nullptr; 2294} 2295 2296void AccessibilityRenderObject::handleAriaExpandedChanged() 2297{ 2298 // Find if a parent of this object should handle aria-expanded changes. 2299 AccessibilityObject* containerParent = this->parentObject(); 2300 while (containerParent) { 2301 bool foundParent = false; 2302 2303 switch (containerParent->roleValue()) { 2304 case TreeRole: 2305 case TreeGridRole: 2306 case GridRole: 2307 case TableRole: 2308 case BrowserRole: 2309 foundParent = true; 2310 break; 2311 default: 2312 break; 2313 } 2314 2315 if (foundParent) 2316 break; 2317 2318 containerParent = containerParent->parentObject(); 2319 } 2320 2321 // Post that the row count changed. 2322 AXObjectCache* cache = axObjectCache(); 2323 if (!cache) 2324 return; 2325 2326 if (containerParent) 2327 cache->postNotification(containerParent, document(), AXObjectCache::AXRowCountChanged); 2328 2329 // Post that the specific row either collapsed or expanded. 2330 if (roleValue() == RowRole || roleValue() == TreeItemRole) 2331 cache->postNotification(this, document(), isExpanded() ? AXObjectCache::AXRowExpanded : AXObjectCache::AXRowCollapsed); 2332 else 2333 cache->postNotification(this, document(), AXObjectCache::AXExpandedChanged); 2334} 2335 2336void AccessibilityRenderObject::handleActiveDescendantChanged() 2337{ 2338 Element* element = toElement(renderer()->node()); 2339 if (!element) 2340 return; 2341 if (!renderer()->frame().selection().isFocusedAndActive() || renderer()->document().focusedElement() != element) 2342 return; 2343 2344 if (toAccessibilityRenderObject(activeDescendant()) && shouldNotifyActiveDescendant()) 2345 renderer()->document().axObjectCache()->postNotification(m_renderer, AXObjectCache::AXActiveDescendantChanged); 2346} 2347 2348AccessibilityObject* AccessibilityRenderObject::correspondingControlForLabelElement() const 2349{ 2350 HTMLLabelElement* labelElement = labelElementContainer(); 2351 if (!labelElement) 2352 return 0; 2353 2354 HTMLElement* correspondingControl = labelElement->control(); 2355 if (!correspondingControl) 2356 return 0; 2357 2358 // Make sure the corresponding control isn't a descendant of this label that's in the middle of being destroyed. 2359 if (correspondingControl->renderer() && !correspondingControl->renderer()->parent()) 2360 return 0; 2361 2362 return axObjectCache()->getOrCreate(correspondingControl); 2363} 2364 2365AccessibilityObject* AccessibilityRenderObject::correspondingLabelForControlElement() const 2366{ 2367 if (!m_renderer) 2368 return 0; 2369 2370 // ARIA: section 2A, bullet #3 says if aria-labeledby or aria-label appears, it should 2371 // override the "label" element association. 2372 if (hasTextAlternative()) 2373 return 0; 2374 2375 Node* node = m_renderer->node(); 2376 if (node && node->isHTMLElement()) { 2377 HTMLLabelElement* label = labelForElement(toElement(node)); 2378 if (label) 2379 return axObjectCache()->getOrCreate(label); 2380 } 2381 2382 return 0; 2383} 2384 2385bool AccessibilityRenderObject::renderObjectIsObservable(RenderObject* renderer) const 2386{ 2387 // AX clients will listen for AXValueChange on a text control. 2388 if (renderer->isTextControl()) 2389 return true; 2390 2391 // AX clients will listen for AXSelectedChildrenChanged on listboxes. 2392 Node* node = renderer->node(); 2393 if (!node) 2394 return false; 2395 2396 if (nodeHasRole(node, "listbox") || (renderer->isBoxModelObject() && toRenderBoxModelObject(renderer)->isListBox())) 2397 return true; 2398 2399 // Textboxes should send out notifications. 2400 if (nodeHasRole(node, "textbox") || (node->isElementNode() && contentEditableAttributeIsEnabled(toElement(node)))) 2401 return true; 2402 2403 return false; 2404} 2405 2406AccessibilityObject* AccessibilityRenderObject::observableObject() const 2407{ 2408 // Find the object going up the parent chain that is used in accessibility to monitor certain notifications. 2409 for (RenderObject* renderer = m_renderer; renderer && renderer->node(); renderer = renderer->parent()) { 2410 if (renderObjectIsObservable(renderer)) { 2411 if (AXObjectCache* cache = axObjectCache()) 2412 return cache->getOrCreate(renderer); 2413 } 2414 } 2415 2416 return 0; 2417} 2418 2419bool AccessibilityRenderObject::isDescendantOfElementType(const QualifiedName& tagName) const 2420{ 2421 for (auto& ancestor : ancestorsOfType<RenderElement>(*m_renderer)) { 2422 if (ancestor.element() && ancestor.element()->hasTagName(tagName)) 2423 return true; 2424 } 2425 return false; 2426} 2427 2428String AccessibilityRenderObject::expandedTextValue() const 2429{ 2430 if (AccessibilityObject* parent = parentObject()) { 2431 if (parent->hasTagName(abbrTag) || parent->hasTagName(acronymTag)) 2432 return parent->getAttribute(titleAttr); 2433 } 2434 2435 return String(); 2436} 2437 2438bool AccessibilityRenderObject::supportsExpandedTextValue() const 2439{ 2440 if (roleValue() == StaticTextRole) { 2441 if (AccessibilityObject* parent = parentObject()) 2442 return parent->hasTagName(abbrTag) || parent->hasTagName(acronymTag); 2443 } 2444 2445 return false; 2446} 2447 2448AccessibilityRole AccessibilityRenderObject::determineAccessibilityRole() 2449{ 2450 if (!m_renderer) 2451 return UnknownRole; 2452 2453 m_ariaRole = determineAriaRoleAttribute(); 2454 2455 Node* node = m_renderer->node(); 2456 AccessibilityRole ariaRole = ariaRoleAttribute(); 2457 if (ariaRole != UnknownRole) 2458 return ariaRole; 2459 2460 RenderBoxModelObject* cssBox = renderBoxModelObject(); 2461 2462 if (node && node->isLink()) { 2463 if (cssBox && cssBox->isImage()) 2464 return ImageMapRole; 2465 return WebCoreLinkRole; 2466 } 2467 if ((cssBox && cssBox->isListItem()) || (node && node->hasTagName(liTag))) 2468 return ListItemRole; 2469 if (m_renderer->isListMarker()) 2470 return ListMarkerRole; 2471 if (node && node->hasTagName(buttonTag)) 2472 return buttonRoleType(); 2473 if (node && node->hasTagName(legendTag)) 2474 return LegendRole; 2475 if (m_renderer->isText()) 2476 return StaticTextRole; 2477 if (cssBox && cssBox->isImage()) { 2478 if (node && isHTMLInputElement(node)) 2479 return ariaHasPopup() ? PopUpButtonRole : ButtonRole; 2480 if (isSVGImage()) 2481 return SVGRootRole; 2482 return ImageRole; 2483 } 2484 2485 if (node && node->hasTagName(canvasTag)) 2486 return CanvasRole; 2487 2488 if (cssBox && cssBox->isRenderView()) 2489 return WebAreaRole; 2490 2491 if (cssBox && cssBox->isTextField()) 2492 return TextFieldRole; 2493 2494 if (cssBox && cssBox->isTextArea()) 2495 return TextAreaRole; 2496 2497 if (node && isHTMLInputElement(node)) { 2498 HTMLInputElement* input = toHTMLInputElement(node); 2499 if (input->isCheckbox()) 2500 return CheckBoxRole; 2501 if (input->isRadioButton()) 2502 return RadioButtonRole; 2503 if (input->isTextButton()) 2504 return buttonRoleType(); 2505 // On iOS, the date field is a popup button. On other platforms this is a text field. 2506#if PLATFORM(IOS) 2507 if (input->isDateField()) 2508 return PopUpButtonRole; 2509#endif 2510 2511#if ENABLE(INPUT_TYPE_COLOR) 2512 const AtomicString& type = input->getAttribute(typeAttr); 2513 if (equalIgnoringCase(type, "color")) 2514 return ColorWellRole; 2515#endif 2516 } 2517 2518 if (hasContentEditableAttributeSet()) 2519 return TextAreaRole; 2520 2521 if (isFileUploadButton()) 2522 return ButtonRole; 2523 2524 if (cssBox && cssBox->isMenuList()) 2525 return PopUpButtonRole; 2526 2527 if (headingLevel()) 2528 return HeadingRole; 2529 2530 if (m_renderer->isSVGImage()) 2531 return ImageRole; 2532 if (m_renderer->isSVGRoot()) 2533 return SVGRootRole; 2534 if (node && node->hasTagName(SVGNames::gTag)) 2535 return GroupRole; 2536 2537#if ENABLE(MATHML) 2538 if (node && node->hasTagName(MathMLNames::mathTag)) 2539 return DocumentMathRole; 2540#endif 2541 // It's not clear which role a platform should choose for a math element. 2542 // Declaring a math element role should give flexibility to platforms to choose. 2543 if (isMathElement()) 2544 return MathElementRole; 2545 2546 if (node && node->hasTagName(ddTag)) 2547 return DescriptionListDetailRole; 2548 2549 if (node && node->hasTagName(dtTag)) 2550 return DescriptionListTermRole; 2551 2552 if (node && node->hasTagName(dlTag)) 2553 return DescriptionListRole; 2554 2555 if (node && (node->hasTagName(rpTag) || node->hasTagName(rtTag))) 2556 return AnnotationRole; 2557 2558#if PLATFORM(GTK) 2559 // Gtk ATs expect all tables, data and layout, to be exposed as tables. 2560 if (node && (node->hasTagName(tdTag) || node->hasTagName(thTag))) 2561 return CellRole; 2562 2563 if (node && node->hasTagName(trTag)) 2564 return RowRole; 2565 2566 if (node && isHTMLTableElement(node)) 2567 return TableRole; 2568#endif 2569 2570 // Table sections should be ignored. 2571 if (m_renderer->isTableSection()) 2572 return IgnoredRole; 2573 2574 if (m_renderer->isHR()) 2575 return HorizontalRuleRole; 2576 2577 if (node && node->hasTagName(pTag)) 2578 return ParagraphRole; 2579 2580 if (node && isHTMLLabelElement(node)) 2581 return LabelRole; 2582 2583 if (node && node->hasTagName(dfnTag)) 2584 return DefinitionRole; 2585 2586 if (node && node->hasTagName(divTag)) 2587 return DivRole; 2588 2589 if (node && isHTMLFormElement(node)) 2590 return FormRole; 2591 2592 if (node && node->hasTagName(articleTag)) 2593 return DocumentArticleRole; 2594 2595 if (node && node->hasTagName(mainTag)) 2596 return LandmarkMainRole; 2597 2598 if (node && node->hasTagName(navTag)) 2599 return LandmarkNavigationRole; 2600 2601 if (node && node->hasTagName(asideTag)) 2602 return LandmarkComplementaryRole; 2603 2604 if (node && node->hasTagName(sectionTag)) 2605 return DocumentRegionRole; 2606 2607 if (node && node->hasTagName(addressTag)) 2608 return LandmarkContentInfoRole; 2609 2610#if ENABLE(VIDEO) 2611 if (node && isHTMLVideoElement(node)) 2612 return VideoRole; 2613 if (node && isHTMLAudioElement(node)) 2614 return AudioRole; 2615#endif 2616 2617 // The HTML element should not be exposed as an element. That's what the RenderView element does. 2618 if (node && node->hasTagName(htmlTag)) 2619 return IgnoredRole; 2620 2621 // There should only be one banner/contentInfo per page. If header/footer are being used within an article or section 2622 // then it should not be exposed as whole page's banner/contentInfo 2623 if (node && node->hasTagName(headerTag) && !isDescendantOfElementType(articleTag) && !isDescendantOfElementType(sectionTag)) 2624 return LandmarkBannerRole; 2625 if (node && node->hasTagName(footerTag) && !isDescendantOfElementType(articleTag) && !isDescendantOfElementType(sectionTag)) 2626 return FooterRole; 2627 2628 if (m_renderer->isRenderBlockFlow()) 2629 return GroupRole; 2630 2631 // If the element does not have role, but it has ARIA attributes, or accepts tab focus, accessibility should fallback to exposing it as a group. 2632 if (supportsARIAAttributes() || canSetFocusAttribute()) 2633 return GroupRole; 2634 2635 return UnknownRole; 2636} 2637 2638AccessibilityOrientation AccessibilityRenderObject::orientation() const 2639{ 2640 const AtomicString& ariaOrientation = getAttribute(aria_orientationAttr); 2641 if (equalIgnoringCase(ariaOrientation, "horizontal")) 2642 return AccessibilityOrientationHorizontal; 2643 if (equalIgnoringCase(ariaOrientation, "vertical")) 2644 return AccessibilityOrientationVertical; 2645 2646 return AccessibilityObject::orientation(); 2647} 2648 2649bool AccessibilityRenderObject::inheritsPresentationalRole() const 2650{ 2651 // ARIA states if an item can get focus, it should not be presentational. 2652 if (canSetFocusAttribute()) 2653 return false; 2654 2655 // ARIA spec says that when a parent object is presentational, and it has required child elements, 2656 // those child elements are also presentational. For example, <li> becomes presentational from <ul>. 2657 // http://www.w3.org/WAI/PF/aria/complete#presentation 2658 static NeverDestroyed<HashSet<QualifiedName>> listItemParents; 2659 static NeverDestroyed<HashSet<QualifiedName>> tableCellParents; 2660 2661 HashSet<QualifiedName>* possibleParentTagNames = 0; 2662 switch (roleValue()) { 2663 case ListItemRole: 2664 case ListMarkerRole: 2665 if (listItemParents.get().isEmpty()) { 2666 listItemParents.get().add(ulTag); 2667 listItemParents.get().add(olTag); 2668 listItemParents.get().add(dlTag); 2669 } 2670 possibleParentTagNames = &listItemParents.get(); 2671 break; 2672 case CellRole: 2673 if (tableCellParents.get().isEmpty()) 2674 tableCellParents.get().add(tableTag); 2675 possibleParentTagNames = &tableCellParents.get(); 2676 break; 2677 default: 2678 break; 2679 } 2680 2681 // Not all elements need to check for this, only ones that are required children. 2682 if (!possibleParentTagNames) 2683 return false; 2684 2685 for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) { 2686 if (!parent->isAccessibilityRenderObject()) 2687 continue; 2688 2689 Node* elementNode = toAccessibilityRenderObject(parent)->node(); 2690 if (!elementNode || !elementNode->isElementNode()) 2691 continue; 2692 2693 // If native tag of the parent element matches an acceptable name, then return 2694 // based on its presentational status. 2695 if (possibleParentTagNames->contains(toElement(elementNode)->tagQName())) 2696 return parent->roleValue() == PresentationalRole; 2697 } 2698 2699 return false; 2700} 2701 2702bool AccessibilityRenderObject::isPresentationalChildOfAriaRole() const 2703{ 2704 // Walk the parent chain looking for a parent that has presentational children 2705 AccessibilityObject* parent; 2706 for (parent = parentObject(); parent && !parent->ariaRoleHasPresentationalChildren(); parent = parent->parentObject()) 2707 { } 2708 2709 return parent; 2710} 2711 2712bool AccessibilityRenderObject::ariaRoleHasPresentationalChildren() const 2713{ 2714 switch (m_ariaRole) { 2715 case ButtonRole: 2716 case SliderRole: 2717 case ImageRole: 2718 case ProgressIndicatorRole: 2719 case SpinButtonRole: 2720 // case SeparatorRole: 2721 return true; 2722 default: 2723 return false; 2724 } 2725} 2726 2727bool AccessibilityRenderObject::canSetExpandedAttribute() const 2728{ 2729 // An object can be expanded if it aria-expanded is true or false. 2730 const AtomicString& ariaExpanded = getAttribute(aria_expandedAttr); 2731 return equalIgnoringCase(ariaExpanded, "true") || equalIgnoringCase(ariaExpanded, "false"); 2732} 2733 2734bool AccessibilityRenderObject::canSetValueAttribute() const 2735{ 2736 2737 // In the event of a (Boolean)@readonly and (True/False/Undefined)@aria-readonly 2738 // value mismatch, the host language native attribute value wins. 2739 if (isNativeTextControl()) 2740 return !isReadOnly(); 2741 2742 if (isMeter()) 2743 return false; 2744 2745 if (equalIgnoringCase(getAttribute(aria_readonlyAttr), "true")) 2746 return false; 2747 2748 if (equalIgnoringCase(getAttribute(aria_readonlyAttr), "false")) 2749 return true; 2750 2751 if (isProgressIndicator() || isSlider()) 2752 return true; 2753 2754 if (isTextControl() && !isNativeTextControl()) 2755 return true; 2756 2757 // Any node could be contenteditable, so isReadOnly should be relied upon 2758 // for this information for all elements. 2759 return !isReadOnly(); 2760} 2761 2762bool AccessibilityRenderObject::canSetTextRangeAttributes() const 2763{ 2764 return isTextControl(); 2765} 2766 2767void AccessibilityRenderObject::textChanged() 2768{ 2769 // If this element supports ARIA live regions, or is part of a region with an ARIA editable role, 2770 // then notify the AT of changes. 2771 AXObjectCache* cache = axObjectCache(); 2772 if (!cache) 2773 return; 2774 2775 for (RenderObject* renderParent = m_renderer; renderParent; renderParent = renderParent->parent()) { 2776 AccessibilityObject* parent = cache->get(renderParent); 2777 if (!parent) 2778 continue; 2779 2780 if (parent->supportsARIALiveRegion()) 2781 cache->postNotification(renderParent, AXObjectCache::AXLiveRegionChanged); 2782 2783 if ((parent->isARIATextControl() || parent->hasContentEditableAttributeSet()) && !parent->isNativeTextControl()) 2784 cache->postNotification(renderParent, AXObjectCache::AXValueChanged); 2785 } 2786} 2787 2788void AccessibilityRenderObject::clearChildren() 2789{ 2790 AccessibilityObject::clearChildren(); 2791 m_childrenDirty = false; 2792} 2793 2794void AccessibilityRenderObject::addImageMapChildren() 2795{ 2796 RenderBoxModelObject* cssBox = renderBoxModelObject(); 2797 if (!cssBox || !cssBox->isRenderImage()) 2798 return; 2799 2800 HTMLMapElement* map = toRenderImage(cssBox)->imageMap(); 2801 if (!map) 2802 return; 2803 2804 for (auto& area : descendantsOfType<HTMLAreaElement>(*map)) { 2805 // add an <area> element for this child if it has a link 2806 if (!area.isLink()) 2807 continue; 2808 AccessibilityImageMapLink* areaObject = toAccessibilityImageMapLink(axObjectCache()->getOrCreate(ImageMapLinkRole)); 2809 areaObject->setHTMLAreaElement(&area); 2810 areaObject->setHTMLMapElement(map); 2811 areaObject->setParent(this); 2812 if (!areaObject->accessibilityIsIgnored()) 2813 m_children.append(areaObject); 2814 else 2815 axObjectCache()->remove(areaObject->axObjectID()); 2816 } 2817} 2818 2819void AccessibilityRenderObject::updateChildrenIfNecessary() 2820{ 2821 if (needsToUpdateChildren()) 2822 clearChildren(); 2823 2824 AccessibilityObject::updateChildrenIfNecessary(); 2825} 2826 2827void AccessibilityRenderObject::addTextFieldChildren() 2828{ 2829 Node* node = this->node(); 2830 if (!node || !isHTMLInputElement(node)) 2831 return; 2832 2833 HTMLInputElement* input = toHTMLInputElement(node); 2834 HTMLElement* spinButtonElement = input->innerSpinButtonElement(); 2835 if (!spinButtonElement || !spinButtonElement->isSpinButtonElement()) 2836 return; 2837 2838 AccessibilitySpinButton* axSpinButton = toAccessibilitySpinButton(axObjectCache()->getOrCreate(SpinButtonRole)); 2839 axSpinButton->setSpinButtonElement(static_cast<SpinButtonElement*>(spinButtonElement)); 2840 axSpinButton->setParent(this); 2841 m_children.append(axSpinButton); 2842} 2843 2844bool AccessibilityRenderObject::isSVGImage() const 2845{ 2846 return remoteSVGRootElement(); 2847} 2848 2849void AccessibilityRenderObject::detachRemoteSVGRoot() 2850{ 2851 if (AccessibilitySVGRoot* root = remoteSVGRootElement()) 2852 root->setParent(0); 2853} 2854 2855AccessibilitySVGRoot* AccessibilityRenderObject::remoteSVGRootElement() const 2856{ 2857 if (!m_renderer || !m_renderer->isRenderImage()) 2858 return nullptr; 2859 2860 CachedImage* cachedImage = toRenderImage(m_renderer)->cachedImage(); 2861 if (!cachedImage) 2862 return nullptr; 2863 2864 Image* image = cachedImage->image(); 2865 if (!image || !image->isSVGImage()) 2866 return nullptr; 2867 2868 SVGImage* svgImage = static_cast<SVGImage*>(image); 2869 FrameView* frameView = svgImage->frameView(); 2870 if (!frameView) 2871 return nullptr; 2872 Frame& frame = frameView->frame(); 2873 2874 Document* doc = frame.document(); 2875 if (!doc || !doc->isSVGDocument()) 2876 return nullptr; 2877 2878 SVGSVGElement* rootElement = toSVGDocument(doc)->rootElement(); 2879 if (!rootElement) 2880 return nullptr; 2881 RenderObject* rendererRoot = rootElement->renderer(); 2882 if (!rendererRoot) 2883 return nullptr; 2884 2885 AXObjectCache* cache = frame.document()->axObjectCache(); 2886 if (!cache) 2887 return nullptr; 2888 AccessibilityObject* rootSVGObject = cache->getOrCreate(rendererRoot); 2889 2890 // In order to connect the AX hierarchy from the SVG root element from the loaded resource 2891 // the parent must be set, because there's no other way to get back to who created the image. 2892 ASSERT(rootSVGObject && rootSVGObject->isAccessibilitySVGRoot()); 2893 if (!rootSVGObject->isAccessibilitySVGRoot()) 2894 return nullptr; 2895 2896 return toAccessibilitySVGRoot(rootSVGObject); 2897} 2898 2899void AccessibilityRenderObject::addRemoteSVGChildren() 2900{ 2901 AccessibilitySVGRoot* root = remoteSVGRootElement(); 2902 if (!root) 2903 return; 2904 2905 root->setParent(this); 2906 2907 if (root->accessibilityIsIgnored()) { 2908 for (const auto& child : root->children()) 2909 m_children.append(child); 2910 } else 2911 m_children.append(root); 2912} 2913 2914void AccessibilityRenderObject::addCanvasChildren() 2915{ 2916 // Add the unrendered canvas children as AX nodes, unless we're not using a canvas renderer 2917 // because JS is disabled for example. 2918 if (!node() || !node()->hasTagName(canvasTag) || (renderer() && !renderer()->isCanvas())) 2919 return; 2920 2921 // If it's a canvas, it won't have rendered children, but it might have accessible fallback content. 2922 // Clear m_haveChildren because AccessibilityNodeObject::addChildren will expect it to be false. 2923 ASSERT(!m_children.size()); 2924 m_haveChildren = false; 2925 AccessibilityNodeObject::addChildren(); 2926} 2927 2928void AccessibilityRenderObject::addAttachmentChildren() 2929{ 2930 if (!isAttachment()) 2931 return; 2932 2933 // FrameView's need to be inserted into the AX hierarchy when encountered. 2934 Widget* widget = widgetForAttachmentView(); 2935 if (!widget || !widget->isFrameView()) 2936 return; 2937 2938 AccessibilityObject* axWidget = axObjectCache()->getOrCreate(widget); 2939 if (!axWidget->accessibilityIsIgnored()) 2940 m_children.append(axWidget); 2941} 2942 2943#if PLATFORM(COCOA) 2944void AccessibilityRenderObject::updateAttachmentViewParents() 2945{ 2946 // Only the unignored parent should set the attachment parent, because that's what is reflected in the AX 2947 // hierarchy to the client. 2948 if (accessibilityIsIgnored()) 2949 return; 2950 2951 for (const auto& child : m_children) { 2952 if (child->isAttachment()) 2953 child->overrideAttachmentParent(this); 2954 } 2955} 2956#endif 2957 2958// Hidden children are those that are not rendered or visible, but are specifically marked as aria-hidden=false, 2959// meaning that they should be exposed to the AX hierarchy. 2960void AccessibilityRenderObject::addHiddenChildren() 2961{ 2962 Node* node = this->node(); 2963 if (!node) 2964 return; 2965 2966 // First do a quick run through to determine if we have any hidden nodes (most often we will not). 2967 // If we do have hidden nodes, we need to determine where to insert them so they match DOM order as close as possible. 2968 bool shouldInsertHiddenNodes = false; 2969 for (Node* child = node->firstChild(); child; child = child->nextSibling()) { 2970 if (!child->renderer() && isNodeAriaVisible(child)) { 2971 shouldInsertHiddenNodes = true; 2972 break; 2973 } 2974 } 2975 2976 if (!shouldInsertHiddenNodes) 2977 return; 2978 2979 // Iterate through all of the children, including those that may have already been added, and 2980 // try to insert hidden nodes in the correct place in the DOM order. 2981 unsigned insertionIndex = 0; 2982 for (Node* child = node->firstChild(); child; child = child->nextSibling()) { 2983 if (child->renderer()) { 2984 // Find out where the last render sibling is located within m_children. 2985 AccessibilityObject* childObject = axObjectCache()->get(child->renderer()); 2986 if (childObject && childObject->accessibilityIsIgnored()) { 2987 auto& children = childObject->children(); 2988 if (children.size()) 2989 childObject = children.last().get(); 2990 else 2991 childObject = 0; 2992 } 2993 2994 if (childObject) 2995 insertionIndex = m_children.find(childObject) + 1; 2996 continue; 2997 } 2998 2999 if (!isNodeAriaVisible(child)) 3000 continue; 3001 3002 unsigned previousSize = m_children.size(); 3003 if (insertionIndex > previousSize) 3004 insertionIndex = previousSize; 3005 3006 insertChild(axObjectCache()->getOrCreate(child), insertionIndex); 3007 insertionIndex += (m_children.size() - previousSize); 3008 } 3009} 3010 3011void AccessibilityRenderObject::updateRoleAfterChildrenCreation() 3012{ 3013 // If a menu does not have valid menuitem children, it should not be exposed as a menu. 3014 if (roleValue() == MenuRole) { 3015 // Elements marked as menus must have at least one menu item child. 3016 size_t menuItemCount = 0; 3017 for (const auto& child : children()) { 3018 if (child->isMenuItem()) { 3019 menuItemCount++; 3020 break; 3021 } 3022 } 3023 3024 if (!menuItemCount) 3025 m_role = GroupRole; 3026 } 3027} 3028 3029void AccessibilityRenderObject::addChildren() 3030{ 3031 // If the need to add more children in addition to existing children arises, 3032 // childrenChanged should have been called, leaving the object with no children. 3033 ASSERT(!m_haveChildren); 3034 3035 m_haveChildren = true; 3036 3037 if (!canHaveChildren()) 3038 return; 3039 3040 for (RefPtr<AccessibilityObject> obj = firstChild(); obj; obj = obj->nextSibling()) 3041 addChild(obj.get()); 3042 3043 addHiddenChildren(); 3044 addAttachmentChildren(); 3045 addImageMapChildren(); 3046 addTextFieldChildren(); 3047 addCanvasChildren(); 3048 addRemoteSVGChildren(); 3049 3050#if PLATFORM(COCOA) 3051 updateAttachmentViewParents(); 3052#endif 3053 3054 updateRoleAfterChildrenCreation(); 3055} 3056 3057bool AccessibilityRenderObject::canHaveChildren() const 3058{ 3059 if (!m_renderer) 3060 return false; 3061 3062 return AccessibilityNodeObject::canHaveChildren(); 3063} 3064 3065const String AccessibilityRenderObject::ariaLiveRegionStatus() const 3066{ 3067 const AtomicString& liveRegionStatus = getAttribute(aria_liveAttr); 3068 // These roles have implicit live region status. 3069 if (liveRegionStatus.isEmpty()) 3070 return defaultLiveRegionStatusForRole(roleValue()); 3071 3072 return liveRegionStatus; 3073} 3074 3075const AtomicString& AccessibilityRenderObject::ariaLiveRegionRelevant() const 3076{ 3077 static NeverDestroyed<const AtomicString> defaultLiveRegionRelevant("additions text", AtomicString::ConstructFromLiteral); 3078 const AtomicString& relevant = getAttribute(aria_relevantAttr); 3079 3080 // Default aria-relevant = "additions text". 3081 if (relevant.isEmpty()) 3082 return defaultLiveRegionRelevant; 3083 3084 return relevant; 3085} 3086 3087bool AccessibilityRenderObject::ariaLiveRegionAtomic() const 3088{ 3089 const AtomicString& atomic = getAttribute(aria_atomicAttr); 3090 if (equalIgnoringCase(atomic, "true")) 3091 return true; 3092 if (equalIgnoringCase(atomic, "false")) 3093 return false; 3094 // WAI-ARIA "alert" and "status" roles have an implicit aria-atomic value of true. 3095 switch (roleValue()) { 3096 case ApplicationAlertRole: 3097 case ApplicationStatusRole: 3098 return true; 3099 default: 3100 return false; 3101 } 3102} 3103 3104bool AccessibilityRenderObject::ariaLiveRegionBusy() const 3105{ 3106 return elementAttributeValue(aria_busyAttr); 3107} 3108 3109void AccessibilityRenderObject::ariaSelectedRows(AccessibilityChildrenVector& result) 3110{ 3111 // Determine which rows are selected. 3112 bool isMulti = isMultiSelectable(); 3113 3114 // Prefer active descendant over aria-selected. 3115 AccessibilityObject* activeDesc = activeDescendant(); 3116 if (activeDesc && (activeDesc->isTreeItem() || activeDesc->isTableRow())) { 3117 result.append(activeDesc); 3118 if (!isMulti) 3119 return; 3120 } 3121 3122 // Get all the rows. 3123 auto rowsIteration = [&](const AccessibilityChildrenVector& rows) { 3124 for (auto& row : rows) { 3125 if (row->isSelected()) { 3126 result.append(row); 3127 if (!isMulti) 3128 break; 3129 } 3130 } 3131 }; 3132 if (isTree()) { 3133 AccessibilityChildrenVector allRows; 3134 ariaTreeRows(allRows); 3135 rowsIteration(allRows); 3136 } else if (isAccessibilityTable() && toAccessibilityTable(this)->supportsSelectedRows()) 3137 rowsIteration(toAccessibilityTable(this)->rows()); 3138} 3139 3140void AccessibilityRenderObject::ariaListboxSelectedChildren(AccessibilityChildrenVector& result) 3141{ 3142 bool isMulti = isMultiSelectable(); 3143 3144 for (const auto& child : children()) { 3145 // Every child should have aria-role option, and if so, check for selected attribute/state. 3146 if (child->isSelected() && child->ariaRoleAttribute() == ListBoxOptionRole) { 3147 result.append(child); 3148 if (!isMulti) 3149 return; 3150 } 3151 } 3152} 3153 3154void AccessibilityRenderObject::selectedChildren(AccessibilityChildrenVector& result) 3155{ 3156 ASSERT(result.isEmpty()); 3157 3158 // only listboxes should be asked for their selected children. 3159 AccessibilityRole role = roleValue(); 3160 if (role == ListBoxRole) // native list boxes would be AccessibilityListBoxes, so only check for aria list boxes 3161 ariaListboxSelectedChildren(result); 3162 else if (role == TreeRole || role == TreeGridRole || role == TableRole) 3163 ariaSelectedRows(result); 3164} 3165 3166void AccessibilityRenderObject::ariaListboxVisibleChildren(AccessibilityChildrenVector& result) 3167{ 3168 if (!hasChildren()) 3169 addChildren(); 3170 3171 for (const auto& child : children()) { 3172 if (child->isOffScreen()) 3173 result.append(child); 3174 } 3175} 3176 3177void AccessibilityRenderObject::visibleChildren(AccessibilityChildrenVector& result) 3178{ 3179 ASSERT(result.isEmpty()); 3180 3181 // only listboxes are asked for their visible children. 3182 if (ariaRoleAttribute() != ListBoxRole) { // native list boxes would be AccessibilityListBoxes, so only check for aria list boxes 3183 ASSERT_NOT_REACHED(); 3184 return; 3185 } 3186 return ariaListboxVisibleChildren(result); 3187} 3188 3189void AccessibilityRenderObject::tabChildren(AccessibilityChildrenVector& result) 3190{ 3191 ASSERT(roleValue() == TabListRole); 3192 3193 for (const auto& child : children()) { 3194 if (child->isTabItem()) 3195 result.append(child); 3196 } 3197} 3198 3199const String& AccessibilityRenderObject::actionVerb() const 3200{ 3201#if !PLATFORM(IOS) 3202 // FIXME: Need to add verbs for select elements. 3203 static NeverDestroyed<const String> buttonAction(AXButtonActionVerb()); 3204 static NeverDestroyed<const String> textFieldAction(AXTextFieldActionVerb()); 3205 static NeverDestroyed<const String> radioButtonAction(AXRadioButtonActionVerb()); 3206 static NeverDestroyed<const String> checkedCheckBoxAction(AXUncheckedCheckBoxActionVerb()); 3207 static NeverDestroyed<const String> uncheckedCheckBoxAction(AXUncheckedCheckBoxActionVerb()); 3208 static NeverDestroyed<const String> linkAction(AXLinkActionVerb()); 3209 3210 switch (roleValue()) { 3211 case ButtonRole: 3212 case ToggleButtonRole: 3213 return buttonAction; 3214 case TextFieldRole: 3215 case TextAreaRole: 3216 return textFieldAction; 3217 case RadioButtonRole: 3218 return radioButtonAction; 3219 case CheckBoxRole: 3220 return isChecked() ? checkedCheckBoxAction : uncheckedCheckBoxAction; 3221 case LinkRole: 3222 case WebCoreLinkRole: 3223 return linkAction; 3224 default: 3225 return nullAtom; 3226 } 3227#else 3228 return nullAtom; 3229#endif 3230} 3231 3232void AccessibilityRenderObject::setAccessibleName(const AtomicString& name) 3233{ 3234 // Setting the accessible name can store the value in the DOM 3235 if (!m_renderer) 3236 return; 3237 3238 Node* domNode = 0; 3239 // For web areas, set the aria-label on the HTML element. 3240 if (isWebArea()) 3241 domNode = m_renderer->document().documentElement(); 3242 else 3243 domNode = m_renderer->node(); 3244 3245 if (domNode && domNode->isElementNode()) 3246 toElement(domNode)->setAttribute(aria_labelAttr, name); 3247} 3248 3249static bool isLinkable(const AccessibilityRenderObject& object) 3250{ 3251 if (!object.renderer()) 3252 return false; 3253 3254 // See https://wiki.mozilla.org/Accessibility/AT-Windows-API for the elements 3255 // Mozilla considers linkable. 3256 return object.isLink() || object.isImage() || object.renderer()->isText(); 3257} 3258 3259String AccessibilityRenderObject::stringValueForMSAA() const 3260{ 3261 if (isLinkable(*this)) { 3262 Element* anchor = anchorElement(); 3263 if (anchor && isHTMLAnchorElement(anchor)) 3264 return toHTMLAnchorElement(anchor)->href(); 3265 } 3266 3267 return stringValue(); 3268} 3269 3270bool AccessibilityRenderObject::isLinked() const 3271{ 3272 if (!isLinkable(*this)) 3273 return false; 3274 3275 Element* anchor = anchorElement(); 3276 if (!anchor || !isHTMLAnchorElement(anchor)) 3277 return false; 3278 3279 return !toHTMLAnchorElement(anchor)->href().isEmpty(); 3280} 3281 3282bool AccessibilityRenderObject::hasBoldFont() const 3283{ 3284 if (!m_renderer) 3285 return false; 3286 3287 return m_renderer->style().fontDescription().weight() >= FontWeightBold; 3288} 3289 3290bool AccessibilityRenderObject::hasItalicFont() const 3291{ 3292 if (!m_renderer) 3293 return false; 3294 3295 return m_renderer->style().fontDescription().italic() == FontItalicOn; 3296} 3297 3298bool AccessibilityRenderObject::hasPlainText() const 3299{ 3300 if (!m_renderer) 3301 return false; 3302 3303 const RenderStyle& style = m_renderer->style(); 3304 3305 return style.fontDescription().weight() == FontWeightNormal 3306 && style.fontDescription().italic() == FontItalicOff 3307 && style.textDecorationsInEffect() == TextDecorationNone; 3308} 3309 3310bool AccessibilityRenderObject::hasSameFont(RenderObject* renderer) const 3311{ 3312 if (!m_renderer || !renderer) 3313 return false; 3314 3315 return m_renderer->style().fontDescription().families() == renderer->style().fontDescription().families(); 3316} 3317 3318bool AccessibilityRenderObject::hasSameFontColor(RenderObject* renderer) const 3319{ 3320 if (!m_renderer || !renderer) 3321 return false; 3322 3323 return m_renderer->style().visitedDependentColor(CSSPropertyColor) == renderer->style().visitedDependentColor(CSSPropertyColor); 3324} 3325 3326bool AccessibilityRenderObject::hasSameStyle(RenderObject* renderer) const 3327{ 3328 if (!m_renderer || !renderer) 3329 return false; 3330 3331 return m_renderer->style() == renderer->style(); 3332} 3333 3334bool AccessibilityRenderObject::hasUnderline() const 3335{ 3336 if (!m_renderer) 3337 return false; 3338 3339 return m_renderer->style().textDecorationsInEffect() & TextDecorationUnderline; 3340} 3341 3342String AccessibilityRenderObject::nameForMSAA() const 3343{ 3344 if (m_renderer && m_renderer->isText()) 3345 return textUnderElement(); 3346 3347 return title(); 3348} 3349 3350static bool shouldReturnTagNameAsRoleForMSAA(const Element& element) 3351{ 3352 // See "document structure", 3353 // https://wiki.mozilla.org/Accessibility/AT-Windows-API 3354 // FIXME: Add the other tag names that should be returned as the role. 3355 return element.hasTagName(h1Tag) || element.hasTagName(h2Tag) 3356 || element.hasTagName(h3Tag) || element.hasTagName(h4Tag) 3357 || element.hasTagName(h5Tag) || element.hasTagName(h6Tag); 3358} 3359 3360String AccessibilityRenderObject::stringRoleForMSAA() const 3361{ 3362 if (!m_renderer) 3363 return String(); 3364 3365 Node* node = m_renderer->node(); 3366 if (!node || !node->isElementNode()) 3367 return String(); 3368 3369 Element* element = toElement(node); 3370 if (!shouldReturnTagNameAsRoleForMSAA(*element)) 3371 return String(); 3372 3373 return element->tagName(); 3374} 3375 3376String AccessibilityRenderObject::positionalDescriptionForMSAA() const 3377{ 3378 // See "positional descriptions", 3379 // https://wiki.mozilla.org/Accessibility/AT-Windows-API 3380 if (isHeading()) 3381 return "L" + String::number(headingLevel()); 3382 3383 // FIXME: Add positional descriptions for other elements. 3384 return String(); 3385} 3386 3387String AccessibilityRenderObject::descriptionForMSAA() const 3388{ 3389 String description = positionalDescriptionForMSAA(); 3390 if (!description.isEmpty()) 3391 return description; 3392 3393 description = accessibilityDescription(); 3394 if (!description.isEmpty()) { 3395 // From the Mozilla MSAA implementation: 3396 // "Signal to screen readers that this description is speakable and is not 3397 // a formatted positional information description. Don't localize the 3398 // 'Description: ' part of this string, it will be parsed out by assistive 3399 // technologies." 3400 return "Description: " + description; 3401 } 3402 3403 return String(); 3404} 3405 3406static AccessibilityRole msaaRoleForRenderer(const RenderObject* renderer) 3407{ 3408 if (!renderer) 3409 return UnknownRole; 3410 3411 if (renderer->isText()) 3412 return EditableTextRole; 3413 3414 if (renderer->isBoxModelObject() && toRenderBoxModelObject(renderer)->isListItem()) 3415 return ListItemRole; 3416 3417 return UnknownRole; 3418} 3419 3420AccessibilityRole AccessibilityRenderObject::roleValueForMSAA() const 3421{ 3422 if (m_roleForMSAA != UnknownRole) 3423 return m_roleForMSAA; 3424 3425 m_roleForMSAA = msaaRoleForRenderer(m_renderer); 3426 3427 if (m_roleForMSAA == UnknownRole) 3428 m_roleForMSAA = roleValue(); 3429 3430 return m_roleForMSAA; 3431} 3432 3433String AccessibilityRenderObject::passwordFieldValue() const 3434{ 3435 ASSERT(isPasswordField()); 3436 3437 // Look for the RenderText object in the RenderObject tree for this input field. 3438 RenderObject* renderer = node()->renderer(); 3439 while (renderer && !renderer->isText()) 3440 renderer = toRenderElement(renderer)->firstChild(); 3441 3442 if (!renderer || !renderer->isText()) 3443 return String(); 3444 3445 // Return the text that is actually being rendered in the input field. 3446 return toRenderText(renderer)->textWithoutConvertingBackslashToYenSymbol(); 3447} 3448 3449ScrollableArea* AccessibilityRenderObject::getScrollableAreaIfScrollable() const 3450{ 3451 // If the parent is a scroll view, then this object isn't really scrollable, the parent ScrollView should handle the scrolling. 3452 if (parentObject() && parentObject()->isAccessibilityScrollView()) 3453 return 0; 3454 3455 if (!m_renderer || !m_renderer->isBox()) 3456 return 0; 3457 3458 RenderBox* box = toRenderBox(m_renderer); 3459 if (!box->canBeScrolledAndHasScrollableArea()) 3460 return 0; 3461 3462 return box->layer(); 3463} 3464 3465void AccessibilityRenderObject::scrollTo(const IntPoint& point) const 3466{ 3467 if (!m_renderer || !m_renderer->isBox()) 3468 return; 3469 3470 RenderBox* box = toRenderBox(m_renderer); 3471 if (!box->canBeScrolledAndHasScrollableArea()) 3472 return; 3473 3474 RenderLayer* layer = box->layer(); 3475 layer->scrollToOffset(toIntSize(point), RenderLayer::ScrollOffsetClamped); 3476} 3477 3478#if ENABLE(MATHML) 3479bool AccessibilityRenderObject::isMathElement() const 3480{ 3481 Node* node = this->node(); 3482 if (!m_renderer || !node) 3483 return false; 3484 3485 return node->isMathMLElement(); 3486} 3487 3488bool AccessibilityRenderObject::isMathFraction() const 3489{ 3490 return m_renderer && m_renderer->isRenderMathMLFraction(); 3491} 3492 3493bool AccessibilityRenderObject::isMathFenced() const 3494{ 3495 return m_renderer && m_renderer->isRenderMathMLFenced(); 3496} 3497 3498bool AccessibilityRenderObject::isMathSubscriptSuperscript() const 3499{ 3500 return m_renderer && m_renderer->isRenderMathMLScripts() && !isMathMultiscript(); 3501} 3502 3503bool AccessibilityRenderObject::isMathRow() const 3504{ 3505 return m_renderer && m_renderer->isRenderMathMLRow(); 3506} 3507 3508bool AccessibilityRenderObject::isMathUnderOver() const 3509{ 3510 return m_renderer && m_renderer->isRenderMathMLUnderOver(); 3511} 3512 3513bool AccessibilityRenderObject::isMathSquareRoot() const 3514{ 3515 return m_renderer && m_renderer->isRenderMathMLSquareRoot(); 3516} 3517 3518bool AccessibilityRenderObject::isMathRoot() const 3519{ 3520 return m_renderer && m_renderer->isRenderMathMLRoot(); 3521} 3522 3523bool AccessibilityRenderObject::isMathOperator() const 3524{ 3525 if (!m_renderer || !m_renderer->isRenderMathMLOperator()) 3526 return false; 3527 3528 return true; 3529} 3530 3531bool AccessibilityRenderObject::isMathFenceOperator() const 3532{ 3533 if (!m_renderer || !m_renderer->isRenderMathMLOperator()) 3534 return false; 3535 3536 return toRenderMathMLOperator(*m_renderer).hasOperatorFlag(MathMLOperatorDictionary::Fence); 3537} 3538 3539bool AccessibilityRenderObject::isMathSeparatorOperator() const 3540{ 3541 if (!m_renderer || !m_renderer->isRenderMathMLOperator()) 3542 return false; 3543 3544 return toRenderMathMLOperator(*m_renderer).hasOperatorFlag(MathMLOperatorDictionary::Separator); 3545} 3546 3547bool AccessibilityRenderObject::isMathText() const 3548{ 3549 return node() && node()->hasTagName(MathMLNames::mtextTag); 3550} 3551 3552bool AccessibilityRenderObject::isMathNumber() const 3553{ 3554 return node() && node()->hasTagName(MathMLNames::mnTag); 3555} 3556 3557bool AccessibilityRenderObject::isMathIdentifier() const 3558{ 3559 return node() && node()->hasTagName(MathMLNames::miTag); 3560} 3561 3562bool AccessibilityRenderObject::isMathMultiscript() const 3563{ 3564 return node() && node()->hasTagName(MathMLNames::mmultiscriptsTag); 3565} 3566 3567bool AccessibilityRenderObject::isMathTable() const 3568{ 3569 return node() && node()->hasTagName(MathMLNames::mtableTag); 3570} 3571 3572bool AccessibilityRenderObject::isMathTableRow() const 3573{ 3574 return node() && node()->hasTagName(MathMLNames::mtrTag); 3575} 3576 3577bool AccessibilityRenderObject::isMathTableCell() const 3578{ 3579 return node() && node()->hasTagName(MathMLNames::mtdTag); 3580} 3581 3582bool AccessibilityRenderObject::isIgnoredElementWithinMathTree() const 3583{ 3584 if (!m_renderer) 3585 return true; 3586 3587 // We ignore anonymous renderers inside math blocks. 3588 // However, we do not exclude anonymous RenderMathMLOperator nodes created by the mfenced element nor RenderText nodes created by math operators so that the text can be exposed by AccessibilityRenderObject::textUnderElement. 3589 if (m_renderer->isAnonymous()) { 3590 if (m_renderer->isRenderMathMLOperator()) 3591 return false; 3592 for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) { 3593 if (parent->isMathElement()) 3594 return !(m_renderer->isText() && ancestorsOfType<RenderMathMLOperator>(*m_renderer).first()); 3595 } 3596 } 3597 3598 // Only math elements that we explicitly recognize should be included 3599 // We don't want things like <mstyle> to appear in the tree. 3600 if (isMathElement()) { 3601 if (isMathFraction() || isMathFenced() || isMathSubscriptSuperscript() || isMathRow() 3602 || isMathUnderOver() || isMathRoot() || isMathText() || isMathNumber() 3603 || isMathOperator() || isMathFenceOperator() || isMathSeparatorOperator() 3604 || isMathIdentifier() || isMathTable() || isMathTableRow() || isMathTableCell() || isMathMultiscript()) 3605 return false; 3606 return true; 3607 } 3608 3609 return false; 3610} 3611 3612AccessibilityObject* AccessibilityRenderObject::mathRadicandObject() 3613{ 3614 if (!isMathRoot()) 3615 return 0; 3616 3617 const auto& children = this->children(); 3618 if (children.size() < 1) 3619 return 0; 3620 3621 // The radicand is the value being rooted and must be listed first. 3622 return children[0].get(); 3623} 3624 3625AccessibilityObject* AccessibilityRenderObject::mathRootIndexObject() 3626{ 3627 if (!isMathRoot()) 3628 return 0; 3629 3630 const auto& children = this->children(); 3631 if (children.size() != 2) 3632 return 0; 3633 3634 // The index in a root is the value which determines if it's a square, cube, etc, root 3635 // and must be listed second. 3636 return children[1].get(); 3637} 3638 3639AccessibilityObject* AccessibilityRenderObject::mathNumeratorObject() 3640{ 3641 if (!isMathFraction()) 3642 return 0; 3643 3644 const auto& children = this->children(); 3645 if (children.size() != 2) 3646 return 0; 3647 3648 return children[0].get(); 3649} 3650 3651AccessibilityObject* AccessibilityRenderObject::mathDenominatorObject() 3652{ 3653 if (!isMathFraction()) 3654 return 0; 3655 3656 const auto& children = this->children(); 3657 if (children.size() != 2) 3658 return 0; 3659 3660 return children[1].get(); 3661} 3662 3663AccessibilityObject* AccessibilityRenderObject::mathUnderObject() 3664{ 3665 if (!isMathUnderOver() || !node()) 3666 return 0; 3667 3668 const auto& children = this->children(); 3669 if (children.size() < 2) 3670 return 0; 3671 3672 if (node()->hasTagName(MathMLNames::munderTag) || node()->hasTagName(MathMLNames::munderoverTag)) 3673 return children[1].get(); 3674 3675 return 0; 3676} 3677 3678AccessibilityObject* AccessibilityRenderObject::mathOverObject() 3679{ 3680 if (!isMathUnderOver() || !node()) 3681 return 0; 3682 3683 const auto& children = this->children(); 3684 if (children.size() < 2) 3685 return 0; 3686 3687 if (node()->hasTagName(MathMLNames::moverTag)) 3688 return children[1].get(); 3689 if (node()->hasTagName(MathMLNames::munderoverTag)) 3690 return children[2].get(); 3691 3692 return 0; 3693} 3694 3695AccessibilityObject* AccessibilityRenderObject::mathBaseObject() 3696{ 3697 if (!isMathSubscriptSuperscript() && !isMathUnderOver() && !isMathMultiscript()) 3698 return 0; 3699 3700 const auto& children = this->children(); 3701 // The base object in question is always the first child. 3702 if (children.size() > 0) 3703 return children[0].get(); 3704 3705 return 0; 3706} 3707 3708AccessibilityObject* AccessibilityRenderObject::mathSubscriptObject() 3709{ 3710 if (!isMathSubscriptSuperscript() || !node()) 3711 return 0; 3712 3713 const auto& children = this->children(); 3714 if (children.size() < 2) 3715 return 0; 3716 3717 if (node()->hasTagName(MathMLNames::msubTag) || node()->hasTagName(MathMLNames::msubsupTag)) 3718 return children[1].get(); 3719 3720 return 0; 3721} 3722 3723AccessibilityObject* AccessibilityRenderObject::mathSuperscriptObject() 3724{ 3725 if (!isMathSubscriptSuperscript() || !node()) 3726 return 0; 3727 3728 const auto& children = this->children(); 3729 unsigned count = children.size(); 3730 3731 if (count >= 2 && node()->hasTagName(MathMLNames::msupTag)) 3732 return children[1].get(); 3733 3734 if (count >= 3 && node()->hasTagName(MathMLNames::msubsupTag)) 3735 return children[2].get(); 3736 3737 return 0; 3738} 3739 3740String AccessibilityRenderObject::mathFencedOpenString() const 3741{ 3742 if (!isMathFenced()) 3743 return String(); 3744 3745 return getAttribute(MathMLNames::openAttr); 3746} 3747 3748String AccessibilityRenderObject::mathFencedCloseString() const 3749{ 3750 if (!isMathFenced()) 3751 return String(); 3752 3753 return getAttribute(MathMLNames::closeAttr); 3754} 3755 3756void AccessibilityRenderObject::mathPrescripts(AccessibilityMathMultiscriptPairs& prescripts) 3757{ 3758 if (!isMathMultiscript() || !node()) 3759 return; 3760 3761 bool foundPrescript = false; 3762 std::pair<AccessibilityObject*, AccessibilityObject*> prescriptPair; 3763 for (Node* child = node()->firstChild(); child; child = child->nextSibling()) { 3764 if (foundPrescript) { 3765 AccessibilityObject* axChild = axObjectCache()->getOrCreate(child); 3766 if (axChild && axChild->isMathElement()) { 3767 if (!prescriptPair.first) 3768 prescriptPair.first = axChild; 3769 else { 3770 prescriptPair.second = axChild; 3771 prescripts.append(prescriptPair); 3772 prescriptPair.first = 0; 3773 prescriptPair.second = 0; 3774 } 3775 } 3776 } else if (child->hasTagName(MathMLNames::mprescriptsTag)) 3777 foundPrescript = true; 3778 } 3779 3780 // Handle the odd number of pre scripts case. 3781 if (prescriptPair.first) 3782 prescripts.append(prescriptPair); 3783} 3784 3785void AccessibilityRenderObject::mathPostscripts(AccessibilityMathMultiscriptPairs& postscripts) 3786{ 3787 if (!isMathMultiscript() || !node()) 3788 return; 3789 3790 // In Multiscripts, the post-script elements start after the first element (which is the base) 3791 // and continue until a <mprescripts> tag is found 3792 std::pair<AccessibilityObject*, AccessibilityObject*> postscriptPair; 3793 bool foundBaseElement = false; 3794 for (Node* child = node()->firstChild(); child; child = child->nextSibling()) { 3795 if (child->hasTagName(MathMLNames::mprescriptsTag)) 3796 break; 3797 3798 AccessibilityObject* axChild = axObjectCache()->getOrCreate(child); 3799 if (axChild && axChild->isMathElement()) { 3800 if (!foundBaseElement) 3801 foundBaseElement = true; 3802 else if (!postscriptPair.first) 3803 postscriptPair.first = axChild; 3804 else { 3805 postscriptPair.second = axChild; 3806 postscripts.append(postscriptPair); 3807 postscriptPair.first = 0; 3808 postscriptPair.second = 0; 3809 } 3810 } 3811 } 3812 3813 // Handle the odd number of post scripts case. 3814 if (postscriptPair.first) 3815 postscripts.append(postscriptPair); 3816} 3817 3818int AccessibilityRenderObject::mathLineThickness() const 3819{ 3820 if (!isMathFraction()) 3821 return -1; 3822 3823 return toRenderMathMLFraction(m_renderer)->lineThickness(); 3824} 3825 3826#endif 3827 3828} // namespace WebCore 3829