/* * 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" #include "MathMLOperatorElement.h" #if ENABLE(MATHML) #include "RenderMathMLOperator.h" #include #include namespace WebCore { WTF_MAKE_ISO_ALLOCATED_IMPL(MathMLOperatorElement); using namespace MathMLNames; using namespace MathMLOperatorDictionary; MathMLOperatorElement::MathMLOperatorElement(const QualifiedName& tagName, Document& document) : MathMLTokenElement(tagName, document) { } Ref MathMLOperatorElement::create(const QualifiedName& tagName, Document& document) { return adoptRef(*new MathMLOperatorElement(tagName, document)); } MathMLOperatorElement::OperatorChar MathMLOperatorElement::parseOperatorChar(const String& string) { OperatorChar operatorChar; // FIXME: This operator dictionary does not accept multiple characters (https://webkit.org/b/124828). if (auto codePoint = convertToSingleCodePoint(string)) { auto character = codePoint.value(); // The minus sign renders better than the hyphen sign used in some MathML formulas. if (character == hyphenMinus) character = minusSign; operatorChar.character = character; operatorChar.isVertical = isVertical(operatorChar.character); } return operatorChar; } const MathMLOperatorElement::OperatorChar& MathMLOperatorElement::operatorChar() { if (!m_operatorChar) m_operatorChar = parseOperatorChar(textContent()); return m_operatorChar.value(); } Property MathMLOperatorElement::computeDictionaryProperty() { Property dictionaryProperty; // We first determine the form attribute and use the default spacing and properties. const auto& value = attributeWithoutSynchronization(formAttr); bool explicitForm = true; if (value == "prefix") dictionaryProperty.form = Prefix; else if (value == "infix") dictionaryProperty.form = Infix; else if (value == "postfix") dictionaryProperty.form = Postfix; else { // FIXME: We should use more advanced heuristics indicated in the specification to determine the operator form (https://bugs.webkit.org/show_bug.cgi?id=124829). explicitForm = false; if (!previousSibling() && nextSibling()) dictionaryProperty.form = Prefix; else if (previousSibling() && !nextSibling()) dictionaryProperty.form = Postfix; else dictionaryProperty.form = Infix; } // We then try and find an entry in the operator dictionary to override the default values. if (auto entry = search(operatorChar().character, dictionaryProperty.form, explicitForm)) dictionaryProperty = entry.value(); return dictionaryProperty; } const Property& MathMLOperatorElement::dictionaryProperty() { if (!m_dictionaryProperty) m_dictionaryProperty = computeDictionaryProperty(); return m_dictionaryProperty.value(); } static const QualifiedName& propertyFlagToAttributeName(MathMLOperatorDictionary::Flag flag) { switch (flag) { case Accent: return accentAttr; case Fence: return fenceAttr; case LargeOp: return largeopAttr; case MovableLimits: return movablelimitsAttr; case Separator: return separatorAttr; case Stretchy: return stretchyAttr; case Symmetric: return symmetricAttr; } ASSERT_NOT_REACHED(); return nullQName(); } void MathMLOperatorElement::computeOperatorFlag(MathMLOperatorDictionary::Flag flag) { ASSERT(m_properties.dirtyFlags & flag); std::optional property; const auto& name = propertyFlagToAttributeName(flag); const BooleanValue& value = cachedBooleanAttribute(name, property); switch (value) { case BooleanValue::True: m_properties.flags |= flag; break; case BooleanValue::False: m_properties.flags &= ~flag; break; case BooleanValue::Default: // By default, we use the value specified in the operator dictionary. if (dictionaryProperty().flags & flag) m_properties.flags |= flag; else m_properties.flags &= ~flag; break; } } bool MathMLOperatorElement::hasProperty(MathMLOperatorDictionary::Flag flag) { if (m_properties.dirtyFlags & flag) { computeOperatorFlag(flag); m_properties.dirtyFlags &= ~flag; } return m_properties.flags & flag; } MathMLElement::Length MathMLOperatorElement::defaultLeadingSpace() { Length space; space.type = LengthType::MathUnit; space.value = static_cast(dictionaryProperty().leadingSpaceInMathUnit); return space; } MathMLElement::Length MathMLOperatorElement::defaultTrailingSpace() { Length space; space.type = LengthType::MathUnit; space.value = static_cast(dictionaryProperty().trailingSpaceInMathUnit); return space; } const MathMLElement::Length& MathMLOperatorElement::leadingSpace() { return cachedMathMLLength(MathMLNames::lspaceAttr, m_leadingSpace); } const MathMLElement::Length& MathMLOperatorElement::trailingSpace() { return cachedMathMLLength(MathMLNames::rspaceAttr, m_trailingSpace); } const MathMLElement::Length& MathMLOperatorElement::minSize() { return cachedMathMLLength(MathMLNames::minsizeAttr, m_minSize); } const MathMLElement::Length& MathMLOperatorElement::maxSize() { return cachedMathMLLength(MathMLNames::maxsizeAttr, m_maxSize); } void MathMLOperatorElement::childrenChanged(const ChildChange& change) { m_operatorChar = std::nullopt; m_dictionaryProperty = std::nullopt; m_properties.dirtyFlags = MathMLOperatorDictionary::allFlags; MathMLTokenElement::childrenChanged(change); } static std::optional attributeNameToPropertyFlag(const QualifiedName& name) { if (name == accentAttr) return Accent; if (name == fenceAttr) return Fence; if (name == largeopAttr) return LargeOp; if (name == movablelimitsAttr) return MovableLimits; if (name == separatorAttr) return Separator; if (name == stretchyAttr) return Stretchy; if (name == symmetricAttr) return Symmetric; return std::nullopt; } void MathMLOperatorElement::parseAttribute(const QualifiedName& name, const AtomString& value) { if (name == formAttr) { m_dictionaryProperty = std::nullopt; m_properties.dirtyFlags = MathMLOperatorDictionary::allFlags; } else if (auto flag = attributeNameToPropertyFlag(name)) m_properties.dirtyFlags |= flag.value(); else if (name == lspaceAttr) m_leadingSpace = std::nullopt; else if (name == rspaceAttr) m_trailingSpace = std::nullopt; else if (name == minsizeAttr) m_minSize = std::nullopt; else if (name == maxsizeAttr) m_maxSize = std::nullopt; if ((name == stretchyAttr || name == lspaceAttr || name == rspaceAttr || name == movablelimitsAttr) && renderer()) { downcast(*renderer()).updateFromElement(); return; } MathMLTokenElement::parseAttribute(name, value); } RenderPtr MathMLOperatorElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&) { ASSERT(hasTagName(MathMLNames::moTag)); return createRenderer(*this, WTFMove(style)); } } #endif // ENABLE(MATHML)