| /* |
| * Copyright (C) 2016 Igalia, S.L. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 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. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| |
| #if ENABLE(MATHML) |
| #include "AccessibilityMathMLElement.h" |
| |
| #include "AXObjectCache.h" |
| #include "MathMLNames.h" |
| |
| namespace WebCore { |
| |
| AccessibilityMathMLElement::AccessibilityMathMLElement(RenderObject* renderer, bool isAnonymousOperator) |
| : AccessibilityRenderObject(renderer) |
| , m_isAnonymousOperator(isAnonymousOperator) |
| { |
| } |
| |
| AccessibilityMathMLElement::~AccessibilityMathMLElement() = default; |
| |
| Ref<AccessibilityMathMLElement> AccessibilityMathMLElement::create(RenderObject* renderer, bool isAnonymousOperator) |
| { |
| return adoptRef(*new AccessibilityMathMLElement(renderer, isAnonymousOperator)); |
| } |
| |
| AccessibilityRole AccessibilityMathMLElement::determineAccessibilityRole() |
| { |
| if (!m_renderer) |
| return AccessibilityRole::Unknown; |
| |
| if ((m_ariaRole = determineAriaRoleAttribute()) != AccessibilityRole::Unknown) |
| return m_ariaRole; |
| |
| Node* node = m_renderer->node(); |
| if (node && node->hasTagName(MathMLNames::mathTag)) |
| return AccessibilityRole::DocumentMath; |
| |
| // It's not clear which role a platform should choose for a math element. |
| // Declaring a math element role should give flexibility to platforms to choose. |
| return AccessibilityRole::MathElement; |
| } |
| |
| String AccessibilityMathMLElement::textUnderElement(AccessibilityTextUnderElementMode mode) const |
| { |
| if (m_isAnonymousOperator) { |
| UChar operatorChar = downcast<RenderMathMLOperator>(*m_renderer).textContent(); |
| return operatorChar ? String(&operatorChar, 1) : String(); |
| } |
| |
| return AccessibilityRenderObject::textUnderElement(mode); |
| } |
| |
| String AccessibilityMathMLElement::stringValue() const |
| { |
| if (m_isAnonymousOperator) |
| return textUnderElement(); |
| |
| return AccessibilityRenderObject::stringValue(); |
| } |
| |
| bool AccessibilityMathMLElement::isIgnoredElementWithinMathTree() const |
| { |
| if (m_isAnonymousOperator) |
| return false; |
| |
| // Only math elements that we explicitly recognize should be included |
| // We don't want things like <mstyle> to appear in the tree. |
| if (isMathFraction() || isMathFenced() || isMathSubscriptSuperscript() || isMathRow() |
| || isMathUnderOver() || isMathRoot() || isMathText() || isMathNumber() |
| || isMathOperator() || isMathFenceOperator() || isMathSeparatorOperator() |
| || isMathIdentifier() || isMathTable() || isMathTableRow() || isMathTableCell() || isMathMultiscript()) |
| return false; |
| |
| return true; |
| } |
| |
| bool AccessibilityMathMLElement::isMathFraction() const |
| { |
| return m_renderer && m_renderer->isRenderMathMLFraction(); |
| } |
| |
| bool AccessibilityMathMLElement::isMathFenced() const |
| { |
| return m_renderer && m_renderer->isRenderMathMLFenced(); |
| } |
| |
| bool AccessibilityMathMLElement::isMathSubscriptSuperscript() const |
| { |
| return m_renderer && m_renderer->isRenderMathMLScripts() && !isMathMultiscript(); |
| } |
| |
| bool AccessibilityMathMLElement::isMathRow() const |
| { |
| return m_renderer && m_renderer->isRenderMathMLRow() && !isMathRoot(); |
| } |
| |
| bool AccessibilityMathMLElement::isMathUnderOver() const |
| { |
| return m_renderer && m_renderer->isRenderMathMLUnderOver(); |
| } |
| |
| bool AccessibilityMathMLElement::isMathSquareRoot() const |
| { |
| return m_renderer && m_renderer->isRenderMathMLSquareRoot(); |
| } |
| |
| bool AccessibilityMathMLElement::isMathToken() const |
| { |
| return m_renderer && m_renderer->isRenderMathMLToken(); |
| } |
| |
| bool AccessibilityMathMLElement::isMathRoot() const |
| { |
| return m_renderer && m_renderer->isRenderMathMLRoot(); |
| } |
| |
| bool AccessibilityMathMLElement::isMathOperator() const |
| { |
| return m_renderer && m_renderer->isRenderMathMLOperator(); |
| } |
| |
| bool AccessibilityMathMLElement::isAnonymousMathOperator() const |
| { |
| return m_isAnonymousOperator; |
| } |
| |
| bool AccessibilityMathMLElement::isMathFenceOperator() const |
| { |
| if (!is<RenderMathMLOperator>(renderer())) |
| return false; |
| |
| return downcast<RenderMathMLOperator>(*m_renderer).hasOperatorFlag(MathMLOperatorDictionary::Fence); |
| } |
| |
| bool AccessibilityMathMLElement::isMathSeparatorOperator() const |
| { |
| if (!is<RenderMathMLOperator>(renderer())) |
| return false; |
| |
| return downcast<RenderMathMLOperator>(*m_renderer).hasOperatorFlag(MathMLOperatorDictionary::Separator); |
| } |
| |
| bool AccessibilityMathMLElement::isMathText() const |
| { |
| return node() && (node()->hasTagName(MathMLNames::mtextTag) || hasTagName(MathMLNames::msTag)); |
| } |
| |
| bool AccessibilityMathMLElement::isMathNumber() const |
| { |
| return node() && node()->hasTagName(MathMLNames::mnTag); |
| } |
| |
| bool AccessibilityMathMLElement::isMathIdentifier() const |
| { |
| return node() && node()->hasTagName(MathMLNames::miTag); |
| } |
| |
| bool AccessibilityMathMLElement::isMathMultiscript() const |
| { |
| return node() && node()->hasTagName(MathMLNames::mmultiscriptsTag); |
| } |
| |
| bool AccessibilityMathMLElement::isMathTable() const |
| { |
| return node() && node()->hasTagName(MathMLNames::mtableTag); |
| } |
| |
| bool AccessibilityMathMLElement::isMathTableRow() const |
| { |
| return node() && (node()->hasTagName(MathMLNames::mtrTag) || hasTagName(MathMLNames::mlabeledtrTag)); |
| } |
| |
| bool AccessibilityMathMLElement::isMathTableCell() const |
| { |
| return node() && node()->hasTagName(MathMLNames::mtdTag); |
| } |
| |
| bool AccessibilityMathMLElement::isMathScriptObject(AccessibilityMathScriptObjectType type) const |
| { |
| AXCoreObject* parent = parentObjectUnignored(); |
| if (!parent) |
| return false; |
| |
| return type == AccessibilityMathScriptObjectType::Subscript ? this == parent->mathSubscriptObject() : this == parent->mathSuperscriptObject(); |
| } |
| |
| bool AccessibilityMathMLElement::isMathMultiscriptObject(AccessibilityMathMultiscriptObjectType type) const |
| { |
| AXCoreObject* parent = parentObjectUnignored(); |
| if (!parent || !parent->isMathMultiscript()) |
| return false; |
| |
| // The scripts in a MathML <mmultiscripts> element consist of one or more |
| // subscript, superscript pairs. In order to determine if this object is |
| // a scripted token, we need to examine each set of pairs to see if the |
| // this token is present and in the position corresponding with the type. |
| |
| AccessibilityMathMultiscriptPairs pairs; |
| if (type == AccessibilityMathMultiscriptObjectType::PreSubscript || type == AccessibilityMathMultiscriptObjectType::PreSuperscript) |
| parent->mathPrescripts(pairs); |
| else |
| parent->mathPostscripts(pairs); |
| |
| for (const auto& pair : pairs) { |
| if (this == pair.first) |
| return (type == AccessibilityMathMultiscriptObjectType::PreSubscript || type == AccessibilityMathMultiscriptObjectType::PostSubscript); |
| if (this == pair.second) |
| return (type == AccessibilityMathMultiscriptObjectType::PreSuperscript || type == AccessibilityMathMultiscriptObjectType::PostSuperscript); |
| } |
| |
| return false; |
| } |
| |
| AXCoreObject* AccessibilityMathMLElement::mathRadicandObject() |
| { |
| if (!isMathRoot()) |
| return nullptr; |
| |
| // For MathSquareRoot, we actually return the first child of the base. |
| // See also https://webkit.org/b/146452 |
| const auto& children = this->children(); |
| if (children.size() < 1) |
| return nullptr; |
| |
| return children[0].get(); |
| } |
| |
| AXCoreObject* AccessibilityMathMLElement::mathRootIndexObject() |
| { |
| if (!isMathRoot() || isMathSquareRoot()) |
| return nullptr; |
| |
| const auto& children = this->children(); |
| if (children.size() < 2) |
| return nullptr; |
| |
| return children[1].get(); |
| } |
| |
| AXCoreObject* AccessibilityMathMLElement::mathNumeratorObject() |
| { |
| if (!isMathFraction()) |
| return nullptr; |
| |
| const auto& children = this->children(); |
| if (children.size() != 2) |
| return nullptr; |
| |
| return children[0].get(); |
| } |
| |
| AXCoreObject* AccessibilityMathMLElement::mathDenominatorObject() |
| { |
| if (!isMathFraction()) |
| return nullptr; |
| |
| const auto& children = this->children(); |
| if (children.size() != 2) |
| return nullptr; |
| |
| return children[1].get(); |
| } |
| |
| AXCoreObject* AccessibilityMathMLElement::mathUnderObject() |
| { |
| if (!isMathUnderOver() || !node()) |
| return nullptr; |
| |
| const auto& children = this->children(); |
| if (children.size() < 2) |
| return nullptr; |
| |
| if (node()->hasTagName(MathMLNames::munderTag) || node()->hasTagName(MathMLNames::munderoverTag)) |
| return children[1].get(); |
| |
| return nullptr; |
| } |
| |
| AXCoreObject* AccessibilityMathMLElement::mathOverObject() |
| { |
| if (!isMathUnderOver() || !node()) |
| return nullptr; |
| |
| const auto& children = this->children(); |
| if (children.size() < 2) |
| return nullptr; |
| |
| if (node()->hasTagName(MathMLNames::moverTag)) |
| return children[1].get(); |
| if (node()->hasTagName(MathMLNames::munderoverTag)) |
| return children[2].get(); |
| |
| return nullptr; |
| } |
| |
| AXCoreObject* AccessibilityMathMLElement::mathBaseObject() |
| { |
| if (!isMathSubscriptSuperscript() && !isMathUnderOver() && !isMathMultiscript()) |
| return nullptr; |
| |
| const auto& children = this->children(); |
| // The base object in question is always the first child. |
| if (children.size() > 0) |
| return children[0].get(); |
| |
| return nullptr; |
| } |
| |
| AXCoreObject* AccessibilityMathMLElement::mathSubscriptObject() |
| { |
| if (!isMathSubscriptSuperscript() || !node()) |
| return nullptr; |
| |
| const auto& children = this->children(); |
| if (children.size() < 2) |
| return nullptr; |
| |
| if (node()->hasTagName(MathMLNames::msubTag) || node()->hasTagName(MathMLNames::msubsupTag)) |
| return children[1].get(); |
| |
| return nullptr; |
| } |
| |
| AXCoreObject* AccessibilityMathMLElement::mathSuperscriptObject() |
| { |
| if (!isMathSubscriptSuperscript() || !node()) |
| return nullptr; |
| |
| const auto& children = this->children(); |
| unsigned count = children.size(); |
| |
| if (count >= 2 && node()->hasTagName(MathMLNames::msupTag)) |
| return children[1].get(); |
| |
| if (count >= 3 && node()->hasTagName(MathMLNames::msubsupTag)) |
| return children[2].get(); |
| |
| return nullptr; |
| } |
| |
| String AccessibilityMathMLElement::mathFencedOpenString() const |
| { |
| if (!isMathFenced()) |
| return String(); |
| |
| return getAttribute(MathMLNames::openAttr); |
| } |
| |
| String AccessibilityMathMLElement::mathFencedCloseString() const |
| { |
| if (!isMathFenced()) |
| return String(); |
| |
| return getAttribute(MathMLNames::closeAttr); |
| } |
| |
| void AccessibilityMathMLElement::mathPrescripts(AccessibilityMathMultiscriptPairs& prescripts) |
| { |
| if (!isMathMultiscript() || !node()) |
| return; |
| |
| bool foundPrescript = false; |
| std::pair<AccessibilityObject*, AccessibilityObject*> prescriptPair; |
| for (Node* child = node()->firstChild(); child; child = child->nextSibling()) { |
| if (foundPrescript) { |
| AccessibilityObject* axChild = axObjectCache()->getOrCreate(child); |
| if (axChild && axChild->isMathElement()) { |
| if (!prescriptPair.first) |
| prescriptPair.first = axChild; |
| else { |
| prescriptPair.second = axChild; |
| prescripts.append(prescriptPair); |
| prescriptPair.first = nullptr; |
| prescriptPair.second = nullptr; |
| } |
| } |
| } else if (child->hasTagName(MathMLNames::mprescriptsTag)) |
| foundPrescript = true; |
| } |
| |
| // Handle the odd number of pre scripts case. |
| if (prescriptPair.first) |
| prescripts.append(prescriptPair); |
| } |
| |
| void AccessibilityMathMLElement::mathPostscripts(AccessibilityMathMultiscriptPairs& postscripts) |
| { |
| if (!isMathMultiscript() || !node()) |
| return; |
| |
| // In Multiscripts, the post-script elements start after the first element (which is the base) |
| // and continue until a <mprescripts> tag is found |
| std::pair<AccessibilityObject*, AccessibilityObject*> postscriptPair; |
| bool foundBaseElement = false; |
| for (Node* child = node()->firstChild(); child; child = child->nextSibling()) { |
| if (child->hasTagName(MathMLNames::mprescriptsTag)) |
| break; |
| |
| AccessibilityObject* axChild = axObjectCache()->getOrCreate(child); |
| if (axChild && axChild->isMathElement()) { |
| if (!foundBaseElement) |
| foundBaseElement = true; |
| else if (!postscriptPair.first) |
| postscriptPair.first = axChild; |
| else { |
| postscriptPair.second = axChild; |
| postscripts.append(postscriptPair); |
| postscriptPair.first = nullptr; |
| postscriptPair.second = nullptr; |
| } |
| } |
| } |
| |
| // Handle the odd number of post scripts case. |
| if (postscriptPair.first) |
| postscripts.append(postscriptPair); |
| } |
| |
| int AccessibilityMathMLElement::mathLineThickness() const |
| { |
| if (!is<RenderMathMLFraction>(renderer())) |
| return -1; |
| |
| return downcast<RenderMathMLFraction>(*m_renderer).relativeLineThickness(); |
| } |
| |
| } // namespace WebCore |
| |
| #endif // ENABLE(MATHML) |