From 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c Mon Sep 17 00:00:00 2001 From: Lorry Tar Creator Date: Tue, 27 Jun 2017 06:07:23 +0000 Subject: webkitgtk-2.16.5 --- .../accessibility/AccessibilityRenderObject.cpp | 2195 ++++++++++---------- 1 file changed, 1069 insertions(+), 1126 deletions(-) (limited to 'Source/WebCore/accessibility/AccessibilityRenderObject.cpp') diff --git a/Source/WebCore/accessibility/AccessibilityRenderObject.cpp b/Source/WebCore/accessibility/AccessibilityRenderObject.cpp index 80e59420e..3b7edcaa9 100644 --- a/Source/WebCore/accessibility/AccessibilityRenderObject.cpp +++ b/Source/WebCore/accessibility/AccessibilityRenderObject.cpp @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. -* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of +* 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -36,32 +36,33 @@ #include "AccessibilitySpinButton.h" #include "AccessibilityTable.h" #include "CachedImage.h" -#include "Chrome.h" #include "ElementIterator.h" -#include "EventNames.h" #include "FloatRect.h" #include "Frame.h" #include "FrameLoader.h" #include "FrameSelection.h" #include "HTMLAreaElement.h" +#include "HTMLAudioElement.h" +#include "HTMLDetailsElement.h" #include "HTMLFormElement.h" #include "HTMLFrameElementBase.h" #include "HTMLImageElement.h" #include "HTMLInputElement.h" #include "HTMLLabelElement.h" #include "HTMLMapElement.h" +#include "HTMLMeterElement.h" #include "HTMLNames.h" -#include "HTMLOptGroupElement.h" #include "HTMLOptionElement.h" #include "HTMLOptionsCollection.h" #include "HTMLSelectElement.h" +#include "HTMLSummaryElement.h" #include "HTMLTableElement.h" #include "HTMLTextAreaElement.h" +#include "HTMLVideoElement.h" #include "HitTestRequest.h" #include "HitTestResult.h" #include "Image.h" #include "LocalizedStrings.h" -#include "MathMLNames.h" #include "NodeList.h" #include "Page.h" #include "ProgressTracker.h" @@ -75,13 +76,13 @@ #include "RenderLayer.h" #include "RenderLineBreak.h" #include "RenderListBox.h" +#include "RenderListItem.h" #include "RenderListMarker.h" #include "RenderMathMLBlock.h" -#include "RenderMathMLFraction.h" -#include "RenderMathMLOperator.h" #include "RenderMenuList.h" #include "RenderSVGRoot.h" #include "RenderSVGShape.h" +#include "RenderTableCell.h" #include "RenderText.h" #include "RenderTextControl.h" #include "RenderTextControlSingleLine.h" @@ -92,15 +93,14 @@ #include "RenderedPosition.h" #include "SVGDocument.h" #include "SVGImage.h" -#include "SVGImageChromeClient.h" -#include "SVGNames.h" #include "SVGSVGElement.h" #include "Text.h" #include "TextControlInnerElements.h" +#include "TextIterator.h" #include "VisibleUnits.h" #include "htmlediting.h" +#include #include -#include #include namespace WebCore { @@ -126,9 +126,9 @@ void AccessibilityRenderObject::init() AccessibilityNodeObject::init(); } -PassRefPtr AccessibilityRenderObject::create(RenderObject* renderer) +Ref AccessibilityRenderObject::create(RenderObject* renderer) { - return adoptRef(new AccessibilityRenderObject(renderer)); + return adoptRef(*new AccessibilityRenderObject(renderer)); } void AccessibilityRenderObject::detach(AccessibilityDetachmentType detachmentType, AXObjectCache* cache) @@ -141,14 +141,14 @@ void AccessibilityRenderObject::detach(AccessibilityDetachmentType detachmentTyp if (m_renderer) m_renderer->setHasAXObject(false); #endif - m_renderer = 0; + m_renderer = nullptr; } RenderBoxModelObject* AccessibilityRenderObject::renderBoxModelObject() const { - if (!m_renderer || !m_renderer->isBoxModelObject()) - return 0; - return toRenderBoxModelObject(m_renderer); + if (!is(m_renderer)) + return nullptr; + return downcast(m_renderer); } void AccessibilityRenderObject::setRenderer(RenderObject* renderer) @@ -157,16 +157,9 @@ void AccessibilityRenderObject::setRenderer(RenderObject* renderer) setNode(renderer->node()); } -static inline bool isInlineWithContinuation(RenderObject* object) +static inline bool isInlineWithContinuation(RenderObject& object) { - if (!object->isBoxModelObject()) - return false; - - RenderBoxModelObject* renderer = toRenderBoxModelObject(object); - if (!renderer->isRenderInline()) - return false; - - return toRenderInline(renderer)->continuation(); + return is(object) && downcast(object).continuation(); } static inline RenderObject* firstChildInContinuation(RenderInline& renderer) @@ -174,47 +167,51 @@ static inline RenderObject* firstChildInContinuation(RenderInline& renderer) auto continuation = renderer.continuation(); while (continuation) { - if (continuation->isRenderBlock()) + if (is(*continuation)) return continuation; if (RenderObject* child = continuation->firstChild()) return child; - continuation = toRenderInline(continuation)->continuation(); + continuation = downcast(*continuation).continuation(); } return nullptr; } -static inline RenderObject* firstChildConsideringContinuation(RenderObject* renderer) +static inline RenderObject* firstChildConsideringContinuation(RenderObject& renderer) { - RenderObject* firstChild = renderer->firstChildSlow(); + RenderObject* firstChild = renderer.firstChildSlow(); + // We don't want to include the end of a continuation as the firstChild of the + // anonymous parent, because everything has already been linked up via continuation. + // CSS first-letter selector is an example of this case. + if (renderer.isAnonymous() && firstChild && firstChild->isInlineElementContinuation()) + firstChild = nullptr; + if (!firstChild && isInlineWithContinuation(renderer)) - firstChild = firstChildInContinuation(toRenderInline(*renderer)); + firstChild = firstChildInContinuation(downcast(renderer)); return firstChild; } -static inline RenderObject* lastChildConsideringContinuation(RenderObject* renderer) +static inline RenderObject* lastChildConsideringContinuation(RenderObject& renderer) { - RenderObject* lastChild = renderer->lastChildSlow(); - RenderObject* prev; - RenderObject* cur = renderer; - - if (!cur->isRenderInline() && !cur->isRenderBlock()) - return renderer; + if (!is(renderer) && !is(renderer)) + return &renderer; - while (cur) { - prev = cur; + RenderObject* lastChild = downcast(renderer).lastChild(); + RenderBoxModelObject* previous; + for (auto* current = &downcast(renderer); current; ) { + previous = current; - if (RenderObject* lc = cur->lastChildSlow()) - lastChild = lc; + if (RenderObject* newLastChild = current->lastChild()) + lastChild = newLastChild; - if (cur->isRenderInline()) { - cur = toRenderInline(cur)->inlineElementContinuation(); - ASSERT_UNUSED(prev, cur || !toRenderInline(prev)->continuation()); + if (is(*current)) { + current = downcast(*current).inlineElementContinuation(); + ASSERT_UNUSED(previous, current || !downcast(*previous).continuation()); } else - cur = toRenderBlock(cur)->inlineElementContinuation(); + current = downcast(*current).inlineElementContinuation(); } return lastChild; @@ -223,9 +220,9 @@ static inline RenderObject* lastChildConsideringContinuation(RenderObject* rende AccessibilityObject* AccessibilityRenderObject::firstChild() const { if (!m_renderer) - return 0; + return nullptr; - RenderObject* firstChild = firstChildConsideringContinuation(m_renderer); + RenderObject* firstChild = firstChildConsideringContinuation(*m_renderer); // If an object can't have children, then it is using this method to help // calculate some internal property (like its description). @@ -240,9 +237,9 @@ AccessibilityObject* AccessibilityRenderObject::firstChild() const AccessibilityObject* AccessibilityRenderObject::lastChild() const { if (!m_renderer) - return 0; + return nullptr; - RenderObject* lastChild = lastChildConsideringContinuation(m_renderer); + RenderObject* lastChild = lastChildConsideringContinuation(*m_renderer); if (!lastChild && !canHaveChildren()) return AccessibilityNodeObject::lastChild(); @@ -250,104 +247,91 @@ AccessibilityObject* AccessibilityRenderObject::lastChild() const return axObjectCache()->getOrCreate(lastChild); } -static inline RenderInline* startOfContinuations(RenderObject* r) +static inline RenderInline* startOfContinuations(RenderObject& renderer) { - if (r->isInlineElementContinuation()) { -#if ENABLE(MATHML) - // MathML elements make anonymous RenderObjects, then set their node to the parent's node. - // This makes it so that the renderer() != renderer()->node()->renderer() - // (which is what isInlineElementContinuation() uses as a determinant). - if (r->node()->isMathMLElement()) - return nullptr; -#endif - - return toRenderInline(r->node()->renderer()); - } + if (renderer.isInlineElementContinuation() && is(renderer.node()->renderer())) + return downcast(renderer.node()->renderer()); // Blocks with a previous continuation always have a next continuation - if (r->isRenderBlock() && toRenderBlock(r)->inlineElementContinuation()) - return toRenderInline(toRenderBlock(r)->inlineElementContinuation()->element()->renderer()); + if (is(renderer) && downcast(renderer).inlineElementContinuation()) + return downcast(downcast(renderer).inlineElementContinuation()->element()->renderer()); - return 0; + return nullptr; } -static inline RenderObject* endOfContinuations(RenderObject* renderer) +static inline RenderObject* endOfContinuations(RenderObject& renderer) { - RenderObject* prev = renderer; - RenderObject* cur = renderer; - - if (!cur->isRenderInline() && !cur->isRenderBlock()) - return renderer; + if (!is(renderer) && !is(renderer)) + return &renderer; - while (cur) { - prev = cur; - if (cur->isRenderInline()) { - cur = toRenderInline(cur)->inlineElementContinuation(); - ASSERT(cur || !toRenderInline(prev)->continuation()); + auto* previous = &downcast(renderer); + for (auto* current = previous; current; ) { + previous = current; + if (is(*current)) { + current = downcast(*current).inlineElementContinuation(); + ASSERT(current || !downcast(*previous).continuation()); } else - cur = toRenderBlock(cur)->inlineElementContinuation(); + current = downcast(*current).inlineElementContinuation(); } - return prev; + return previous; } -static inline RenderObject* childBeforeConsideringContinuations(RenderInline* r, RenderObject* child) +static inline RenderObject* childBeforeConsideringContinuations(RenderInline* renderer, RenderObject* child) { - RenderBoxModelObject* curContainer = r; - RenderObject* cur = 0; - RenderObject* prev = 0; - - while (curContainer) { - if (curContainer->isRenderInline()) { - cur = curContainer->firstChild(); - while (cur) { - if (cur == child) - return prev; - prev = cur; - cur = cur->nextSibling(); + RenderObject* previous = nullptr; + for (RenderBoxModelObject* currentContainer = renderer; currentContainer; ) { + if (is(*currentContainer)) { + auto* current = currentContainer->firstChild(); + while (current) { + if (current == child) + return previous; + previous = current; + current = current->nextSibling(); } - curContainer = toRenderInline(curContainer)->continuation(); - } else if (curContainer->isRenderBlock()) { - if (curContainer == child) - return prev; + currentContainer = downcast(*currentContainer).continuation(); + } else if (is(*currentContainer)) { + if (currentContainer == child) + return previous; - prev = curContainer; - curContainer = toRenderBlock(curContainer)->inlineElementContinuation(); + previous = currentContainer; + currentContainer = downcast(*currentContainer).inlineElementContinuation(); } } ASSERT_NOT_REACHED(); - - return 0; + return nullptr; } -static inline bool firstChildIsInlineContinuation(RenderObject* renderer) +static inline bool firstChildIsInlineContinuation(RenderElement& renderer) { - RenderObject* child = renderer->firstChildSlow(); + RenderObject* child = renderer.firstChild(); return child && child->isInlineElementContinuation(); } AccessibilityObject* AccessibilityRenderObject::previousSibling() const { if (!m_renderer) - return 0; + return nullptr; - RenderObject* previousSibling = 0; + RenderObject* previousSibling = nullptr; // Case 1: The node is a block and is an inline's continuation. In that case, the inline's // last child is our previous sibling (or further back in the continuation chain) RenderInline* startOfConts; - if (m_renderer->isRenderBlock() && (startOfConts = startOfContinuations(m_renderer))) + if (is(*m_renderer) && (startOfConts = startOfContinuations(*m_renderer))) previousSibling = childBeforeConsideringContinuations(startOfConts, m_renderer); // Case 2: Anonymous block parent of the end of a continuation - skip all the way to before // the parent of the start, since everything in between will be linked up via the continuation. - else if (m_renderer->isAnonymousBlock() && firstChildIsInlineContinuation(m_renderer)) { - RenderObject* firstParent = startOfContinuations(m_renderer->firstChildSlow())->parent(); - while (firstChildIsInlineContinuation(firstParent)) - firstParent = startOfContinuations(firstParent->firstChildSlow())->parent(); + else if (m_renderer->isAnonymousBlock() && firstChildIsInlineContinuation(downcast(*m_renderer))) { + RenderBlock& renderBlock = downcast(*m_renderer); + auto* firstParent = startOfContinuations(*renderBlock.firstChild())->parent(); + ASSERT(firstParent); + while (firstChildIsInlineContinuation(*firstParent)) + firstParent = startOfContinuations(*firstParent->firstChild())->parent(); previousSibling = firstParent->previousSibling(); } @@ -357,40 +341,41 @@ AccessibilityObject* AccessibilityRenderObject::previousSibling() const // Case 4: This node has no previous siblings, but its parent is an inline, // and is another node's inline continutation. Follow the continuation chain. - else if (m_renderer->parent()->isRenderInline() && (startOfConts = startOfContinuations(m_renderer->parent()))) + else if (is(*m_renderer->parent()) && (startOfConts = startOfContinuations(*m_renderer->parent()))) previousSibling = childBeforeConsideringContinuations(startOfConts, m_renderer->parent()->firstChild()); if (!previousSibling) - return 0; + return nullptr; return axObjectCache()->getOrCreate(previousSibling); } -static inline bool lastChildHasContinuation(RenderObject* renderer) +static inline bool lastChildHasContinuation(RenderElement& renderer) { - RenderObject* child = renderer->lastChildSlow(); - return child && isInlineWithContinuation(child); + RenderObject* child = renderer.lastChild(); + return child && isInlineWithContinuation(*child); } AccessibilityObject* AccessibilityRenderObject::nextSibling() const { if (!m_renderer) - return 0; + return nullptr; - RenderObject* nextSibling = 0; + RenderObject* nextSibling = nullptr; // Case 1: node is a block and has an inline continuation. Next sibling is the inline continuation's // first child. RenderInline* inlineContinuation; - if (m_renderer->isRenderBlock() && (inlineContinuation = toRenderBlock(m_renderer)->inlineElementContinuation())) - nextSibling = firstChildConsideringContinuation(inlineContinuation); + if (is(*m_renderer) && (inlineContinuation = downcast(*m_renderer).inlineElementContinuation())) + nextSibling = firstChildConsideringContinuation(*inlineContinuation); // Case 2: Anonymous block parent of the start of a continuation - skip all the way to // after the parent of the end, since everything in between will be linked up via the continuation. - else if (m_renderer->isAnonymousBlock() && lastChildHasContinuation(m_renderer)) { - RenderElement* lastParent = endOfContinuations(toRenderBlock(m_renderer)->lastChild())->parent(); - while (lastChildHasContinuation(lastParent)) - lastParent = endOfContinuations(lastParent->lastChild())->parent(); + else if (m_renderer->isAnonymousBlock() && lastChildHasContinuation(downcast(*m_renderer))) { + RenderElement* lastParent = endOfContinuations(*downcast(*m_renderer).lastChild())->parent(); + ASSERT(lastParent); + while (lastChildHasContinuation(*lastParent)) + lastParent = endOfContinuations(*lastParent->lastChild())->parent(); nextSibling = lastParent->nextSibling(); } @@ -400,54 +385,61 @@ AccessibilityObject* AccessibilityRenderObject::nextSibling() const // Case 4: node is an inline with a continuation. Next sibling is the next sibling of the end // of the continuation chain. - else if (isInlineWithContinuation(m_renderer)) - nextSibling = endOfContinuations(m_renderer)->nextSibling(); + else if (isInlineWithContinuation(*m_renderer)) + nextSibling = endOfContinuations(*m_renderer)->nextSibling(); // Case 5: node has no next sibling, and its parent is an inline with a continuation. - else if (isInlineWithContinuation(m_renderer->parent())) { - auto continuation = toRenderInline(m_renderer->parent())->continuation(); + // Case 5.1: After case 4, (the element was inline w/ continuation but had no sibling), then check it's parent. + if (!nextSibling && isInlineWithContinuation(*m_renderer->parent())) { + auto& continuation = *downcast(*m_renderer->parent()).continuation(); // Case 5a: continuation is a block - in this case the block itself is the next sibling. - if (continuation->isRenderBlock()) - nextSibling = continuation; + if (is(continuation)) + nextSibling = &continuation; // Case 5b: continuation is an inline - in this case the inline's first child is the next sibling else nextSibling = firstChildConsideringContinuation(continuation); + + // After case 4, there are chances that nextSibling has the same node as the current renderer, + // which might lead to adding the same child repeatedly. + if (nextSibling && nextSibling->node() == m_renderer->node()) { + if (AccessibilityObject* nextObj = axObjectCache()->getOrCreate(nextSibling)) + return nextObj->nextSibling(); + } } if (!nextSibling) - return 0; + return nullptr; return axObjectCache()->getOrCreate(nextSibling); } -static RenderBoxModelObject* nextContinuation(RenderObject* renderer) +static RenderBoxModelObject* nextContinuation(RenderObject& renderer) { - ASSERT(renderer); - if (renderer->isRenderInline() && !renderer->isReplaced()) - return toRenderInline(renderer)->continuation(); - if (renderer->isRenderBlock()) - return toRenderBlock(renderer)->inlineElementContinuation(); - return 0; + if (is(renderer) && !renderer.isReplaced()) + return downcast(renderer).continuation(); + if (is(renderer)) + return downcast(renderer).inlineElementContinuation(); + return nullptr; } RenderObject* AccessibilityRenderObject::renderParentObject() const { if (!m_renderer) - return 0; + return nullptr; RenderElement* parent = m_renderer->parent(); // Case 1: node is a block and is an inline's continuation. Parent // is the start of the continuation chain. RenderInline* startOfConts = nullptr; - RenderObject* firstChild = 0; - if (m_renderer->isRenderBlock() && (startOfConts = startOfContinuations(m_renderer))) + RenderObject* firstChild = nullptr; + if (is(*m_renderer) && (startOfConts = startOfContinuations(*m_renderer))) parent = startOfConts; // Case 2: node's parent is an inline which is some node's continuation; parent is // the earliest node in the continuation chain. - else if (parent && parent->isRenderInline() && (startOfConts = startOfContinuations(parent))) + else if (is(parent) && (startOfConts = startOfContinuations(*parent))) parent = startOfConts; // Case 3: The first sibling is the beginning of a continuation chain. Find the origin of that continuation. @@ -455,7 +447,7 @@ RenderObject* AccessibilityRenderObject::renderParentObject() const // Get the node's renderer and follow that continuation chain until the first child is found RenderObject* nodeRenderFirstChild = firstChild->node()->renderer(); while (nodeRenderFirstChild != firstChild) { - for (RenderObject* contsTest = nodeRenderFirstChild; contsTest; contsTest = nextContinuation(contsTest)) { + for (RenderObject* contsTest = nodeRenderFirstChild; contsTest; contsTest = nextContinuation(*contsTest)) { if (contsTest == firstChild) { parent = nodeRenderFirstChild->parent(); break; @@ -476,17 +468,21 @@ RenderObject* AccessibilityRenderObject::renderParentObject() const AccessibilityObject* AccessibilityRenderObject::parentObjectIfExists() const { + AXObjectCache* cache = axObjectCache(); + if (!cache) + return nullptr; + // WebArea's parent should be the scroll view containing it. - if (isWebArea() || isSeamlessWebArea()) - return axObjectCache()->get(&m_renderer->view().frameView()); + if (isWebArea()) + return cache->get(&m_renderer->view().frameView()); - return axObjectCache()->get(renderParentObject()); + return cache->get(renderParentObject()); } AccessibilityObject* AccessibilityRenderObject::parentObject() const { if (!m_renderer) - return 0; + return nullptr; if (ariaRoleAttribute() == MenuBarRole) return axObjectCache()->getOrCreate(m_renderer->parent()); @@ -498,15 +494,19 @@ AccessibilityObject* AccessibilityRenderObject::parentObject() const return parent; } + AXObjectCache* cache = axObjectCache(); + if (!cache) + return nullptr; + RenderObject* parentObj = renderParentObject(); if (parentObj) - return axObjectCache()->getOrCreate(parentObj); + return cache->getOrCreate(parentObj); // WebArea's parent should be the scroll view containing it. - if (isWebArea() || isSeamlessWebArea()) - return axObjectCache()->getOrCreate(&m_renderer->view().frameView()); + if (isWebArea()) + return cache->getOrCreate(&m_renderer->view().frameView()); - return 0; + return nullptr; } bool AccessibilityRenderObject::isAttachment() const @@ -522,34 +522,20 @@ bool AccessibilityRenderObject::isAttachment() const bool AccessibilityRenderObject::isFileUploadButton() const { - if (m_renderer && m_renderer->node() && isHTMLInputElement(m_renderer->node())) { - HTMLInputElement* input = toHTMLInputElement(m_renderer->node()); - return input->isFileUpload(); + if (m_renderer && is(m_renderer->node())) { + HTMLInputElement& input = downcast(*m_renderer->node()); + return input.isFileUpload(); } return false; } - -bool AccessibilityRenderObject::isReadOnly() const -{ - ASSERT(m_renderer); - - if (isWebArea()) { - if (HTMLElement* body = m_renderer->document().body()) { - if (body->hasEditableStyle()) - return false; - } - - return !m_renderer->document().hasEditableStyle(); - } - - return AccessibilityNodeObject::isReadOnly(); -} bool AccessibilityRenderObject::isOffScreen() const { - ASSERT(m_renderer); - IntRect contentRect = pixelSnappedIntRect(m_renderer->absoluteClippedOverflowRect()); + if (!m_renderer) + return true; + + IntRect contentRect = snappedIntRect(m_renderer->absoluteClippedOverflowRect()); // FIXME: unclear if we need LegacyIOSDocumentVisibleRect. IntRect viewRect = m_renderer->view().frameView().visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect); viewRect.intersect(contentRect); @@ -559,33 +545,34 @@ bool AccessibilityRenderObject::isOffScreen() const Element* AccessibilityRenderObject::anchorElement() const { if (!m_renderer) - return 0; + return nullptr; AXObjectCache* cache = axObjectCache(); - RenderObject* currRenderer; + if (!cache) + return nullptr; + + RenderObject* currentRenderer; // Search up the render tree for a RenderObject with a DOM node. Defer to an earlier continuation, though. - for (currRenderer = m_renderer; currRenderer && !currRenderer->node(); currRenderer = currRenderer->parent()) { - if (currRenderer->isAnonymousBlock()) { - RenderObject* continuation = toRenderBlock(currRenderer)->continuation(); - if (continuation) + for (currentRenderer = m_renderer; currentRenderer && !currentRenderer->node(); currentRenderer = currentRenderer->parent()) { + if (currentRenderer->isAnonymousBlock()) { + if (RenderObject* continuation = downcast(*currentRenderer).continuation()) return cache->getOrCreate(continuation)->anchorElement(); } } // bail if none found - if (!currRenderer) - return 0; + if (!currentRenderer) + return nullptr; // search up the DOM tree for an anchor element // NOTE: this assumes that any non-image with an anchor is an HTMLAnchorElement - Node* node = currRenderer->node(); - for ( ; node; node = node->parentNode()) { - if (isHTMLAnchorElement(node) || (node->renderer() && cache->getOrCreate(node->renderer())->isAnchor())) - return toElement(node); + for (Node* node = currentRenderer->node(); node; node = node->parentNode()) { + if (is(*node) || (node->renderer() && cache->getOrCreate(node->renderer())->isLink())) + return downcast(node); } - return 0; + return nullptr; } String AccessibilityRenderObject::helpText() const @@ -602,21 +589,22 @@ String AccessibilityRenderObject::helpText() const return describedBy; String description = accessibilityDescription(); - for (RenderObject* curr = m_renderer; curr; curr = curr->parent()) { - if (curr->node() && curr->node()->isHTMLElement()) { - const AtomicString& summary = toElement(curr->node())->getAttribute(summaryAttr); + for (RenderObject* ancestor = m_renderer; ancestor; ancestor = ancestor->parent()) { + if (is(ancestor->node())) { + HTMLElement& element = downcast(*ancestor->node()); + const AtomicString& summary = element.getAttribute(summaryAttr); if (!summary.isEmpty()) return summary; // The title attribute should be used as help text unless it is already being used as descriptive text. - const AtomicString& title = toElement(curr->node())->getAttribute(titleAttr); + const AtomicString& title = element.getAttribute(titleAttr); if (!title.isEmpty() && description != title) return title; } // Only take help text from an ancestor element if its a group or an unknown role. If help was // added to those kinds of elements, it is likely it was meant for a child element. - AccessibilityObject* axObj = axObjectCache()->getOrCreate(curr); + AccessibilityObject* axObj = axObjectCache()->getOrCreate(ancestor); if (axObj) { AccessibilityRole role = axObj->roleValue(); if (role != GroupRole && role != UnknownRole) @@ -632,28 +620,25 @@ String AccessibilityRenderObject::textUnderElement(AccessibilityTextUnderElement if (!m_renderer) return String(); - if (m_renderer->isFileUploadControl()) - return toRenderFileUploadControl(m_renderer)->buttonValue(); + if (is(*m_renderer)) + return downcast(*m_renderer).buttonValue(); // Reflect when a content author has explicitly marked a line break. if (m_renderer->isBR()) return ASCIILiteral("\n"); -#if ENABLE(MATHML) - // Math operators create RenderText nodes on the fly that are not tied into the DOM in a reasonable way, - // so rangeOfContents does not work for them (nor does regular text selection). - if (m_renderer->isText() && isMathElement()) { - if (ancestorsOfType(*m_renderer).first()) - return toRenderText(*m_renderer).text(); - } -#endif + bool isRenderText = is(*m_renderer); + + if (shouldGetTextFromNode(mode)) + return AccessibilityNodeObject::textUnderElement(mode); // We use a text iterator for text objects AND for those cases where we are // explicitly asking for the full text under a given element. - if (m_renderer->isText() || mode.childrenInclusion == AccessibilityTextUnderElementMode::TextUnderElementModeIncludeAllChildren) { + bool shouldIncludeAllChildren = mode.childrenInclusion == AccessibilityTextUnderElementMode::TextUnderElementModeIncludeAllChildren; + if (isRenderText || shouldIncludeAllChildren) { // If possible, use a text iterator to get the text, so that whitespace // is handled consistently. - Document* nodeDocument = 0; + Document* nodeDocument = nullptr; RefPtr textRange; if (Node* node = m_renderer->node()) { nodeDocument = &node->document(); @@ -683,34 +668,65 @@ String AccessibilityRenderObject::textUnderElement(AccessibilityTextUnderElement // catch stale WebCoreAXObject (see ) if (frame->document() != nodeDocument) return String(); + + // The tree should be stable before looking through the children of a non-Render Text object. + // Otherwise, further uses of TextIterator will force a layout update, potentially altering + // the accessibility tree and causing crashes in the loop that computes the result text. + ASSERT((isRenderText || !shouldIncludeAllChildren) || (!nodeDocument->renderView()->layoutState() && !nodeDocument->childNeedsStyleRecalc())); + return plainText(textRange.get(), textIteratorBehaviorForTextRange()); } } // Sometimes text fragments don't have Nodes associated with them (like when // CSS content is used to insert text or when a RenderCounter is used.) - if (m_renderer->isText()) { - RenderText* renderTextObject = toRenderText(m_renderer); - if (renderTextObject->isTextFragment()) { - + if (is(*m_renderer)) { + RenderText& renderTextObject = downcast(*m_renderer); + if (is(renderTextObject)) { + RenderTextFragment& renderTextFragment = downcast(renderTextObject); // The alt attribute may be set on a text fragment through CSS, which should be honored. - const String& altText = toRenderTextFragment(renderTextObject)->altText(); + const String& altText = renderTextFragment.altText(); if (!altText.isEmpty()) return altText; - return String(static_cast(m_renderer)->contentString()); + return renderTextFragment.contentString(); } - return String(renderTextObject->text()); + return renderTextObject.text(); } } return AccessibilityNodeObject::textUnderElement(mode); } +bool AccessibilityRenderObject::shouldGetTextFromNode(AccessibilityTextUnderElementMode mode) const +{ + if (!m_renderer) + return false; + + // AccessibilityRenderObject::textUnderElement() gets the text of anonymous blocks by using + // the child nodes to define positions. CSS tables and their anonymous descendants lack + // children with nodes. + if (m_renderer->isAnonymous() && m_renderer->isTablePart()) + return mode.childrenInclusion == AccessibilityTextUnderElementMode::TextUnderElementModeIncludeAllChildren; + + // AccessibilityRenderObject::textUnderElement() calls rangeOfContents() to create the text + // range. rangeOfContents() does not include CSS-generated content. + if (m_renderer->isBeforeOrAfterContent()) + return true; + if (Node* node = m_renderer->node()) { + Node* firstChild = node->pseudoAwareFirstChild(); + Node* lastChild = node->pseudoAwareLastChild(); + if ((firstChild && firstChild->isPseudoElement()) || (lastChild && lastChild->isPseudoElement())) + return true; + } + + return false; +} + Node* AccessibilityRenderObject::node() const { if (!m_renderer) - return 0; + return nullptr; if (m_renderer->isRenderView()) return &m_renderer->document(); return m_renderer->node(); @@ -733,25 +749,25 @@ String AccessibilityRenderObject::stringValue() const return staticText; } - if (m_renderer->isText()) + if (is(*m_renderer)) return textUnderElement(); - - if (cssBox && cssBox->isMenuList()) { + + if (is(cssBox)) { // RenderMenuList will go straight to the text() of its selected item. // This has to be overridden in the case where the selected item has an ARIA label. - HTMLSelectElement* selectElement = toHTMLSelectElement(m_renderer->node()); - int selectedIndex = selectElement->selectedIndex(); - const Vector listItems = selectElement->listItems(); + HTMLSelectElement& selectElement = downcast(*m_renderer->node()); + int selectedIndex = selectElement.selectedIndex(); + const Vector& listItems = selectElement.listItems(); if (selectedIndex >= 0 && static_cast(selectedIndex) < listItems.size()) { - const AtomicString& overriddenDescription = listItems[selectedIndex]->fastGetAttribute(aria_labelAttr); + const AtomicString& overriddenDescription = listItems[selectedIndex]->attributeWithoutSynchronization(aria_labelAttr); if (!overriddenDescription.isNull()) return overriddenDescription; } - return toRenderMenuList(m_renderer)->text(); + return downcast(*m_renderer).text(); } - if (m_renderer->isListMarker()) - return toRenderListMarker(*m_renderer).text(); + if (is(*m_renderer)) + return downcast(*m_renderer).text(); if (isWebArea()) return String(); @@ -759,8 +775,13 @@ String AccessibilityRenderObject::stringValue() const if (isTextControl()) return text(); - if (m_renderer->isFileUploadControl()) - return toRenderFileUploadControl(m_renderer)->fileTextValue(); +#if PLATFORM(IOS) + if (isInputTypePopupButton()) + return textUnderElement(); +#endif + + if (is(*m_renderer)) + return downcast(*m_renderer).fileTextValue(); // FIXME: We might need to implement a value here for more types // FIXME: It would be better not to advertise a value at all for the types for which we don't implement one; @@ -772,19 +793,19 @@ String AccessibilityRenderObject::stringValue() const HTMLLabelElement* AccessibilityRenderObject::labelElementContainer() const { if (!m_renderer) - return 0; + return nullptr; // the control element should not be considered part of the label if (isControl()) - return 0; + return nullptr; // find if this has a parent that is a label for (Node* parentNode = m_renderer->node(); parentNode; parentNode = parentNode->parentNode()) { - if (isHTMLLabelElement(parentNode)) - return toHTMLLabelElement(parentNode); + if (is(*parentNode)) + return downcast(parentNode); } - return 0; + return nullptr; } // The boundingBox for elements within the remote SVG element needs to be offset by its position @@ -814,27 +835,25 @@ LayoutRect AccessibilityRenderObject::boundingBoxRect() const // We should also use absoluteQuads for SVG elements, otherwise transforms won't be applied. Vector quads; bool isSVGRoot = false; -#if ENABLE(SVG) + if (obj->isSVGRoot()) isSVGRoot = true; -#endif - if (obj->isText()) - quads = toRenderText(obj)->absoluteQuadsClippedToEllipsis(); - else if (isWebArea() || isSeamlessWebArea() || isSVGRoot) + + if (is(*obj)) + quads = downcast(*obj).absoluteQuadsClippedToEllipsis(); + else if (isWebArea() || isSVGRoot) obj->absoluteQuads(quads); else obj->absoluteFocusRingQuads(quads); LayoutRect result = boundingBoxForQuads(obj, quads); -#if ENABLE(SVG) Document* document = this->document(); if (document && document->isSVGDocument()) offsetBoundingBoxForRemoteSVGElement(result); -#endif // The size of the web area should be the content size, not the clipped size. - if (isWebArea() || isSeamlessWebArea()) + if (isWebArea()) result.setSize(obj->view().frameView().contentsSize()); return result; @@ -845,7 +864,7 @@ LayoutRect AccessibilityRenderObject::checkboxOrRadioRect() const if (!m_renderer) return LayoutRect(); - HTMLLabelElement* label = labelForElement(toElement(m_renderer->node())); + HTMLLabelElement* label = labelForElement(downcast(m_renderer->node())); if (!label || !label->renderer()) return boundingBoxRect(); @@ -865,19 +884,13 @@ LayoutRect AccessibilityRenderObject::elementRect() const bool AccessibilityRenderObject::supportsPath() const { -#if ENABLE(SVG) - if (m_renderer && m_renderer->isSVGShape()) - return true; -#endif - - return false; + return is(m_renderer); } Path AccessibilityRenderObject::elementPath() const { -#if ENABLE(SVG) - if (m_renderer && m_renderer->isSVGShape() && toRenderSVGShape(m_renderer)->hasPath()) { - Path path = toRenderSVGShape(m_renderer)->path(); + if (is(m_renderer) && downcast(*m_renderer).hasPath()) { + Path path = downcast(*m_renderer).path(); // The SVG path is in terms of the parent's bounding box. The path needs to be offset to frame coordinates. if (auto svgRoot = ancestorsOfType(*m_renderer).first()) { @@ -887,7 +900,6 @@ Path AccessibilityRenderObject::elementPath() const return path; } -#endif return Path(); } @@ -899,13 +911,13 @@ IntPoint AccessibilityRenderObject::clickPoint() return children()[0]->clickPoint(); // use the default position unless this is an editable web area, in which case we use the selection bounds. - if (!isWebArea() || isReadOnly()) + if (!isWebArea() || !canSetValueAttribute()) return AccessibilityObject::clickPoint(); VisibleSelection visSelection = selection(); VisiblePositionRange range = VisiblePositionRange(visSelection.visibleStart(), visSelection.visibleEnd()); IntRect bounds = boundsForVisiblePositionRange(range); -#if PLATFORM(MAC) +#if PLATFORM(COCOA) bounds.setLocation(m_renderer->view().frameView().screenToContents(bounds.location())); #endif return IntPoint(bounds.x() + (bounds.width() / 2), bounds.y() - (bounds.height() / 2)); @@ -914,27 +926,24 @@ IntPoint AccessibilityRenderObject::clickPoint() AccessibilityObject* AccessibilityRenderObject::internalLinkElement() const { Element* element = anchorElement(); - if (!element) - return 0; - // Right now, we do not support ARIA links as internal link elements - if (!isHTMLAnchorElement(element)) - return 0; - HTMLAnchorElement* anchor = toHTMLAnchorElement(element); + if (!is(element)) + return nullptr; + HTMLAnchorElement& anchor = downcast(*element); - URL linkURL = anchor->href(); + URL linkURL = anchor.href(); String fragmentIdentifier = linkURL.fragmentIdentifier(); if (fragmentIdentifier.isEmpty()) - return 0; + return nullptr; // check if URL is the same as current URL URL documentURL = m_renderer->document().url(); if (!equalIgnoringFragmentIdentifier(documentURL, linkURL)) - return 0; + return nullptr; Node* linkedNode = m_renderer->document().findAnchor(fragmentIdentifier); if (!linkedNode) - return 0; + return nullptr; // The element we find may not be accessible, so find the first accessible object. return firstAccessibleObjectFromNode(linkedNode); @@ -948,36 +957,33 @@ ESpeak AccessibilityRenderObject::speakProperty() const return m_renderer->style().speak(); } -void AccessibilityRenderObject::addRadioButtonGroupMembers(AccessibilityChildrenVector& linkedUIElements) const +void AccessibilityRenderObject::addRadioButtonGroupChildren(AccessibilityObject* parent, AccessibilityChildrenVector& linkedUIElements) const { - if (!m_renderer || roleValue() != RadioButtonRole) - return; + for (const auto& child : parent->children()) { + if (child->roleValue() == RadioButtonRole) + linkedUIElements.append(child); + else + addRadioButtonGroupChildren(child.get(), linkedUIElements); + } +} - Node* node = m_renderer->node(); - if (!node || !isHTMLInputElement(node)) +void AccessibilityRenderObject::addRadioButtonGroupMembers(AccessibilityChildrenVector& linkedUIElements) const +{ + if (roleValue() != RadioButtonRole) return; - HTMLInputElement* input = toHTMLInputElement(node); - // if there's a form, then this is easy - if (input->form()) { - Vector> formElements; - input->form()->getNamedElements(input->name(), formElements); - - for (auto& associateElement : formElements) { - if (AccessibilityObject* object = axObjectCache()->getOrCreate(&associateElement.get())) - linkedUIElements.append(object); - } + Node* node = this->node(); + if (is(node)) { + HTMLInputElement& input = downcast(*node); + for (auto& radioSibling : input.radioButtonGroup()) { + if (AccessibilityObject* object = axObjectCache()->getOrCreate(radioSibling)) + linkedUIElements.append(object); + } } else { - RefPtr list = node->document().getElementsByTagName("input"); - unsigned len = list->length(); - for (unsigned i = 0; i < len; ++i) { - if (isHTMLInputElement(list->item(i))) { - HTMLInputElement* associateElement = toHTMLInputElement(list->item(i)); - if (associateElement->isRadioButton() && associateElement->name() == input->name()) { - if (AccessibilityObject* object = axObjectCache()->getOrCreate(associateElement)) - linkedUIElements.append(object); - } - } + // If we didn't find any radio button siblings with the traditional naming, lets search for a radio group role and find its children. + for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) { + if (parent->roleValue() == RadioGroupRole) + addRadioButtonGroupChildren(parent, linkedUIElements); } } } @@ -988,7 +994,7 @@ void AccessibilityRenderObject::linkedUIElements(AccessibilityChildrenVector& li { ariaFlowToElements(linkedUIElements); - if (isAnchor()) { + if (isLink()) { AccessibilityObject* linkedAXElement = internalLinkElement(); if (linkedAXElement) linkedUIElements.append(linkedAXElement); @@ -1010,42 +1016,6 @@ bool AccessibilityRenderObject::ariaHasPopup() const return elementAttributeValue(aria_haspopupAttr); } -bool AccessibilityRenderObject::supportsARIAFlowTo() const -{ - return !getAttribute(aria_flowtoAttr).isEmpty(); -} - -void AccessibilityRenderObject::ariaFlowToElements(AccessibilityChildrenVector& flowTo) const -{ - Vector elements; - elementsFromAttribute(elements, aria_flowtoAttr); - - AXObjectCache* cache = axObjectCache(); - for (const auto& element : elements) { - AccessibilityObject* flowToElement = cache->getOrCreate(element); - if (flowToElement) - flowTo.append(flowToElement); - } - -} - -bool AccessibilityRenderObject::supportsARIADescribedBy() const -{ - return !getAttribute(aria_describedbyAttr).isEmpty(); -} - -void AccessibilityRenderObject::ariaDescribedByElements(AccessibilityChildrenVector& ariaDescribedBy) const -{ - Vector elements; - elementsFromAttribute(elements, aria_describedbyAttr); - - AXObjectCache* cache = axObjectCache(); - for (const auto& element : elements) { - if (AccessibilityObject* describedByElement = cache->getOrCreate(element)) - ariaDescribedBy.append(describedByElement); - } -} - bool AccessibilityRenderObject::supportsARIADropping() const { const AtomicString& dropEffect = getAttribute(aria_dropeffectAttr); @@ -1055,7 +1025,7 @@ bool AccessibilityRenderObject::supportsARIADropping() const bool AccessibilityRenderObject::supportsARIADragging() const { const AtomicString& grabbed = getAttribute(aria_grabbedAttr); - return equalIgnoringCase(grabbed, "true") || equalIgnoringCase(grabbed, "false"); + return equalLettersIgnoringASCIICase(grabbed, "true") || equalLettersIgnoringASCIICase(grabbed, "false"); } bool AccessibilityRenderObject::isARIAGrabbed() @@ -1078,7 +1048,7 @@ void AccessibilityRenderObject::determineARIADropEffects(Vector& effects bool AccessibilityRenderObject::exposesTitleUIElement() const { - if (!isControl()) + if (!isControl() && !isFigure()) return false; // If this control is ignored (because it's invisible), @@ -1090,26 +1060,42 @@ bool AccessibilityRenderObject::exposesTitleUIElement() const if (hasTextAlternative()) return false; + // When