Move some code from MathMLElement to MathMLPresentationElement
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 30 Aug 2016 22:54:41 +0000 (22:54 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 30 Aug 2016 22:54:41 +0000 (22:54 +0000)
https://bugs.webkit.org/show_bug.cgi?id=161377

Patch by Frederic Wang <fwang@igalia.com> on 2016-08-30
Reviewed by Darin Adler.

The following code is only used in presentation MathML classes. We then move it from
MathMLElement to MathMLPresentationElement:
- testing whether a child is a phrasing/flow element.
- parsing of length attributes.
- parsing of boolean attributes.
- parsing of mathvariant attributes.

No new tests, already covered by existing tests.

* mathml/MathMLElement.cpp:
(WebCore::MathMLElement::isPhrasingContent): Deleted.
(WebCore::MathMLElement::isFlowContent): Deleted.
(WebCore::MathMLElement::parseNumberAndUnit): Deleted.
(WebCore::MathMLElement::parseNamedSpace): Deleted.
(WebCore::MathMLElement::parseMathMLLength): Deleted.
(WebCore::MathMLElement::cachedMathMLLength): Deleted.
(WebCore::MathMLElement::cachedBooleanAttribute): Deleted.
(WebCore::MathMLElement::parseMathVariantAttribute): Deleted.
(WebCore::MathMLElement::specifiedDisplayStyle): Deleted.
(WebCore::MathMLElement::specifiedMathVariant): Deleted.
* mathml/MathMLElement.h:
(WebCore::MathMLElement::specifiedDisplayStyle):
(WebCore::MathMLElement::specifiedMathVariant):
(WebCore::MathMLElement::acceptsDisplayStyleAttribute): Deleted.
(WebCore::MathMLElement::acceptsMathVariantAttribute): Deleted.
(WebCore::MathMLElement::toOptionalBool): Deleted.
* mathml/MathMLPresentationElement.cpp:
(WebCore::MathMLPresentationElement::isPhrasingContent):
(WebCore::MathMLPresentationElement::isFlowContent):
(WebCore::MathMLPresentationElement::cachedBooleanAttribute):
(WebCore::MathMLPresentationElement::parseNumberAndUnit):
(WebCore::MathMLPresentationElement::parseNamedSpace):
(WebCore::MathMLPresentationElement::parseMathMLLength):
(WebCore::MathMLPresentationElement::cachedMathMLLength):
(WebCore::MathMLPresentationElement::specifiedDisplayStyle):
(WebCore::MathMLPresentationElement::parseMathVariantAttribute):
(WebCore::MathMLPresentationElement::specifiedMathVariant):
* mathml/MathMLPresentationElement.h:
(WebCore::MathMLPresentationElement::toOptionalBool):
(WebCore::MathMLPresentationElement::acceptsMathVariantAttribute):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@205206 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/mathml/MathMLElement.cpp
Source/WebCore/mathml/MathMLElement.h
Source/WebCore/mathml/MathMLPresentationElement.cpp
Source/WebCore/mathml/MathMLPresentationElement.h

index 2c28966..43fbab3 100644 (file)
@@ -1,5 +1,53 @@
 2016-08-30  Frederic Wang  <fwang@igalia.com>
 
+        Move some code from MathMLElement to MathMLPresentationElement
+        https://bugs.webkit.org/show_bug.cgi?id=161377
+
+        Reviewed by Darin Adler.
+
+        The following code is only used in presentation MathML classes. We then move it from
+        MathMLElement to MathMLPresentationElement:
+        - testing whether a child is a phrasing/flow element.
+        - parsing of length attributes.
+        - parsing of boolean attributes.
+        - parsing of mathvariant attributes.
+
+        No new tests, already covered by existing tests.
+
+        * mathml/MathMLElement.cpp:
+        (WebCore::MathMLElement::isPhrasingContent): Deleted.
+        (WebCore::MathMLElement::isFlowContent): Deleted.
+        (WebCore::MathMLElement::parseNumberAndUnit): Deleted.
+        (WebCore::MathMLElement::parseNamedSpace): Deleted.
+        (WebCore::MathMLElement::parseMathMLLength): Deleted.
+        (WebCore::MathMLElement::cachedMathMLLength): Deleted.
+        (WebCore::MathMLElement::cachedBooleanAttribute): Deleted.
+        (WebCore::MathMLElement::parseMathVariantAttribute): Deleted.
+        (WebCore::MathMLElement::specifiedDisplayStyle): Deleted.
+        (WebCore::MathMLElement::specifiedMathVariant): Deleted.
+        * mathml/MathMLElement.h:
+        (WebCore::MathMLElement::specifiedDisplayStyle):
+        (WebCore::MathMLElement::specifiedMathVariant):
+        (WebCore::MathMLElement::acceptsDisplayStyleAttribute): Deleted.
+        (WebCore::MathMLElement::acceptsMathVariantAttribute): Deleted.
+        (WebCore::MathMLElement::toOptionalBool): Deleted.
+        * mathml/MathMLPresentationElement.cpp:
+        (WebCore::MathMLPresentationElement::isPhrasingContent):
+        (WebCore::MathMLPresentationElement::isFlowContent):
+        (WebCore::MathMLPresentationElement::cachedBooleanAttribute):
+        (WebCore::MathMLPresentationElement::parseNumberAndUnit):
+        (WebCore::MathMLPresentationElement::parseNamedSpace):
+        (WebCore::MathMLPresentationElement::parseMathMLLength):
+        (WebCore::MathMLPresentationElement::cachedMathMLLength):
+        (WebCore::MathMLPresentationElement::specifiedDisplayStyle):
+        (WebCore::MathMLPresentationElement::parseMathVariantAttribute):
+        (WebCore::MathMLPresentationElement::specifiedMathVariant):
+        * mathml/MathMLPresentationElement.h:
+        (WebCore::MathMLPresentationElement::toOptionalBool):
+        (WebCore::MathMLPresentationElement::acceptsMathVariantAttribute):
+
+2016-08-30  Frederic Wang  <fwang@igalia.com>
+
         Introduce a MathMLUnknownElement class
         https://bugs.webkit.org/show_bug.cgi?id=161298
 
index 09d3364..bcc2784 100644 (file)
 
 #if ENABLE(MATHML)
 
-#include "ElementIterator.h"
-#include "Event.h"
 #include "EventHandler.h"
 #include "HTMLAnchorElement.h"
-#include "HTMLElement.h"
-#include "HTMLHtmlElement.h"
-#include "HTMLMapElement.h"
-#include "HTMLNames.h"
 #include "HTMLParserIdioms.h"
-#include "MathMLMathElement.h"
 #include "MathMLNames.h"
-#include "MathMLSelectElement.h"
 #include "MouseEvent.h"
 #include "RenderTableCell.h"
-#include "SVGElement.h"
-#include "SVGNames.h"
-#include "SVGSVGElement.h"
-#include "XLinkNames.h"
 
 namespace WebCore {
 
@@ -64,124 +52,6 @@ Ref<MathMLElement> MathMLElement::create(const QualifiedName& tagName, Document&
     return adoptRef(*new MathMLElement(tagName, document));
 }
 
-bool MathMLElement::isPhrasingContent(const Node& node) const
-{
-    // Phrasing content is described in the HTML 5 specification:
-    // http://www.w3.org/TR/html5/dom.html#phrasing-content.
-
-    if (!node.isElementNode())
-        return node.isTextNode();
-
-    if (is<MathMLElement>(node)) {
-        auto& mathmlElement = downcast<MathMLElement>(node);
-        return is<MathMLMathElement>(mathmlElement);
-    }
-
-    if (is<SVGElement>(node)) {
-        auto& svgElement = downcast<SVGElement>(node);
-        return is<SVGSVGElement>(svgElement);
-    }
-
-    if (is<HTMLElement>(node)) {
-        // FIXME: add the <data> and <time> tags when they are implemented.
-        auto& htmlElement = downcast<HTMLElement>(node);
-        return htmlElement.hasTagName(HTMLNames::aTag)
-            || htmlElement.hasTagName(HTMLNames::abbrTag)
-            || (htmlElement.hasTagName(HTMLNames::areaTag) && ancestorsOfType<HTMLMapElement>(htmlElement).first())
-            || htmlElement.hasTagName(HTMLNames::audioTag)
-            || htmlElement.hasTagName(HTMLNames::bTag)
-            || htmlElement.hasTagName(HTMLNames::bdiTag)
-            || htmlElement.hasTagName(HTMLNames::bdoTag)
-            || htmlElement.hasTagName(HTMLNames::brTag)
-            || htmlElement.hasTagName(HTMLNames::buttonTag)
-            || htmlElement.hasTagName(HTMLNames::canvasTag)
-            || htmlElement.hasTagName(HTMLNames::citeTag)
-            || htmlElement.hasTagName(HTMLNames::codeTag)
-            || htmlElement.hasTagName(HTMLNames::datalistTag)
-            || htmlElement.hasTagName(HTMLNames::delTag)
-            || htmlElement.hasTagName(HTMLNames::dfnTag)
-            || htmlElement.hasTagName(HTMLNames::emTag)
-            || htmlElement.hasTagName(HTMLNames::embedTag)
-            || htmlElement.hasTagName(HTMLNames::iTag)
-            || htmlElement.hasTagName(HTMLNames::iframeTag)
-            || htmlElement.hasTagName(HTMLNames::imgTag)
-            || htmlElement.hasTagName(HTMLNames::inputTag)
-            || htmlElement.hasTagName(HTMLNames::insTag)
-            || htmlElement.hasTagName(HTMLNames::kbdTag)
-            || htmlElement.hasTagName(HTMLNames::keygenTag)
-            || htmlElement.hasTagName(HTMLNames::labelTag)
-            || htmlElement.hasTagName(HTMLNames::mapTag)
-            || htmlElement.hasTagName(HTMLNames::markTag)
-            || htmlElement.hasTagName(HTMLNames::meterTag)
-            || htmlElement.hasTagName(HTMLNames::noscriptTag)
-            || htmlElement.hasTagName(HTMLNames::objectTag)
-            || htmlElement.hasTagName(HTMLNames::outputTag)
-            || htmlElement.hasTagName(HTMLNames::progressTag)
-            || htmlElement.hasTagName(HTMLNames::qTag)
-            || htmlElement.hasTagName(HTMLNames::rubyTag)
-            || htmlElement.hasTagName(HTMLNames::sTag)
-            || htmlElement.hasTagName(HTMLNames::sampTag)
-            || htmlElement.hasTagName(HTMLNames::scriptTag)
-            || htmlElement.hasTagName(HTMLNames::selectTag)
-            || htmlElement.hasTagName(HTMLNames::smallTag)
-            || htmlElement.hasTagName(HTMLNames::spanTag)
-            || htmlElement.hasTagName(HTMLNames::strongTag)
-            || htmlElement.hasTagName(HTMLNames::subTag)
-            || htmlElement.hasTagName(HTMLNames::supTag)
-            || htmlElement.hasTagName(HTMLNames::templateTag)
-            || htmlElement.hasTagName(HTMLNames::textareaTag)
-            || htmlElement.hasTagName(HTMLNames::uTag)
-            || htmlElement.hasTagName(HTMLNames::varTag)
-            || htmlElement.hasTagName(HTMLNames::videoTag)
-            || htmlElement.hasTagName(HTMLNames::wbrTag);
-    }
-
-    return false;
-}
-
-bool MathMLElement::isFlowContent(const Node& node) const
-{
-    // Flow content is described in the HTML 5 specification:
-    // http://www.w3.org/TR/html5/dom.html#flow-content
-
-    if (isPhrasingContent(node))
-        return true;
-
-    if (!is<HTMLElement>(node))
-        return false;
-
-    auto& htmlElement = downcast<HTMLElement>(node);
-    // FIXME add the <dialog> tag when it is implemented.
-    return htmlElement.hasTagName(HTMLNames::addressTag)
-        || htmlElement.hasTagName(HTMLNames::articleTag)
-        || htmlElement.hasTagName(HTMLNames::asideTag)
-        || htmlElement.hasTagName(HTMLNames::blockquoteTag)
-        || htmlElement.hasTagName(HTMLNames::detailsTag)
-        || htmlElement.hasTagName(HTMLNames::divTag)
-        || htmlElement.hasTagName(HTMLNames::dlTag)
-        || htmlElement.hasTagName(HTMLNames::fieldsetTag)
-        || htmlElement.hasTagName(HTMLNames::figureTag)
-        || htmlElement.hasTagName(HTMLNames::footerTag)
-        || htmlElement.hasTagName(HTMLNames::formTag)
-        || htmlElement.hasTagName(HTMLNames::h1Tag)
-        || htmlElement.hasTagName(HTMLNames::h2Tag)
-        || htmlElement.hasTagName(HTMLNames::h3Tag)
-        || htmlElement.hasTagName(HTMLNames::h4Tag)
-        || htmlElement.hasTagName(HTMLNames::h5Tag)
-        || htmlElement.hasTagName(HTMLNames::h6Tag)
-        || htmlElement.hasTagName(HTMLNames::headerTag)
-        || htmlElement.hasTagName(HTMLNames::hrTag)
-        || htmlElement.hasTagName(HTMLNames::mainTag)
-        || htmlElement.hasTagName(HTMLNames::navTag)
-        || htmlElement.hasTagName(HTMLNames::olTag)
-        || htmlElement.hasTagName(HTMLNames::pTag)
-        || htmlElement.hasTagName(HTMLNames::preTag)
-        || htmlElement.hasTagName(HTMLNames::sectionTag)
-        || (htmlElement.hasTagName(HTMLNames::styleTag) && htmlElement.hasAttribute("scoped"))
-        || htmlElement.hasTagName(HTMLNames::tableTag)
-        || htmlElement.hasTagName(HTMLNames::ulTag);
-}
-
 unsigned MathMLElement::colSpan() const
 {
     if (!hasTagName(mtdTag))
@@ -356,197 +226,6 @@ StringView MathMLElement::stripLeadingAndTrailingWhitespace(const StringView& st
     return stringView.substring(start, stringLength);
 }
 
-MathMLElement::Length MathMLElement::parseNumberAndUnit(const StringView& string)
-{
-    LengthType lengthType = LengthType::UnitLess;
-    unsigned stringLength = string.length();
-    UChar lastChar = string[stringLength - 1];
-    if (lastChar == '%') {
-        lengthType = LengthType::Percentage;
-        stringLength--;
-    } else if (stringLength >= 2) {
-        UChar penultimateChar = string[stringLength - 2];
-        if (penultimateChar == 'c' && lastChar == 'm')
-            lengthType = LengthType::Cm;
-        if (penultimateChar == 'e' && lastChar == 'm')
-            lengthType = LengthType::Em;
-        else if (penultimateChar == 'e' && lastChar == 'x')
-            lengthType = LengthType::Ex;
-        else if (penultimateChar == 'i' && lastChar == 'n')
-            lengthType = LengthType::In;
-        else if (penultimateChar == 'm' && lastChar == 'm')
-            lengthType = LengthType::Mm;
-        else if (penultimateChar == 'p' && lastChar == 'c')
-            lengthType = LengthType::Pc;
-        else if (penultimateChar == 'p' && lastChar == 't')
-            lengthType = LengthType::Pt;
-        else if (penultimateChar == 'p' && lastChar == 'x')
-            lengthType = LengthType::Px;
-
-        if (lengthType != LengthType::UnitLess)
-            stringLength -= 2;
-    }
-
-    bool ok;
-    float lengthValue = string.substring(0, stringLength).toFloat(ok);
-    if (!ok)
-        return Length();
-
-    Length length;
-    length.type = lengthType;
-    length.value = lengthValue;
-    return length;
-}
-
-MathMLElement::Length MathMLElement::parseNamedSpace(const StringView& string)
-{
-    // Named space values are case-sensitive.
-    int namedSpaceValue;
-    if (string == "veryverythinmathspace")
-        namedSpaceValue = 1;
-    else if (string == "verythinmathspace")
-        namedSpaceValue = 2;
-    else if (string == "thinmathspace")
-        namedSpaceValue = 3;
-    else if (string == "mediummathspace")
-        namedSpaceValue = 4;
-    else if (string == "thickmathspace")
-        namedSpaceValue = 5;
-    else if (string == "verythickmathspace")
-        namedSpaceValue = 6;
-    else if (string == "veryverythickmathspace")
-        namedSpaceValue = 7;
-    else if (string == "negativeveryverythinmathspace")
-        namedSpaceValue = -1;
-    else if (string == "negativeverythinmathspace")
-        namedSpaceValue = -2;
-    else if (string == "negativethinmathspace")
-        namedSpaceValue = -3;
-    else if (string == "negativemediummathspace")
-        namedSpaceValue = -4;
-    else if (string == "negativethickmathspace")
-        namedSpaceValue = -5;
-    else if (string == "negativeverythickmathspace")
-        namedSpaceValue = -6;
-    else if (string == "negativeveryverythickmathspace")
-        namedSpaceValue = -7;
-    else
-        return Length();
-
-    Length length;
-    length.type = LengthType::MathUnit;
-    length.value = namedSpaceValue;
-    return length;
-}
-
-MathMLElement::Length MathMLElement::parseMathMLLength(const String& string)
-{
-    // The regular expression from the MathML Relax NG schema is as follows:
-    //
-    //   pattern = '\s*((-?[0-9]*([0-9]\.?|\.[0-9])[0-9]*(e[mx]|in|cm|mm|p[xtc]|%)?)|(negative)?((very){0,2}thi(n|ck)|medium)mathspace)\s*'
-    //
-    // We do not perform a strict verification of the syntax of whitespaces and number.
-    // Instead, we just use isHTMLSpace and toFloat to parse these parts.
-
-    // We first skip whitespace from both ends of the string.
-    StringView stringView = stripLeadingAndTrailingWhitespace(string);
-
-    if (stringView.isEmpty())
-        return Length();
-
-    // We consider the most typical case: a number followed by an optional unit.
-    UChar firstChar = stringView[0];
-    if (isASCIIDigit(firstChar) || firstChar == '-' || firstChar == '.')
-        return parseNumberAndUnit(stringView);
-
-    // Otherwise, we try and parse a named space.
-    return parseNamedSpace(stringView);
-}
-
-const MathMLElement::Length& MathMLElement::cachedMathMLLength(const QualifiedName& name, Optional<Length>& length)
-{
-    if (length)
-        return length.value();
-    length = parseMathMLLength(attributeWithoutSynchronization(name));
-    return length.value();
-}
-
-const MathMLElement::BooleanValue& MathMLElement::cachedBooleanAttribute(const QualifiedName& name, Optional<BooleanValue>& attribute)
-{
-    if (attribute)
-        return attribute.value();
-
-    // In MathML, attribute values are case-sensitive.
-    const AtomicString& value = attributeWithoutSynchronization(name);
-    if (value == "true")
-        attribute = BooleanValue::True;
-    else if (value == "false")
-        attribute = BooleanValue::False;
-    else
-        attribute = BooleanValue::Default;
-
-    return attribute.value();
-}
-
-MathMLElement::MathVariant MathMLElement::parseMathVariantAttribute(const AtomicString& attributeValue)
-{
-    // The mathvariant attribute values is case-sensitive.
-    if (attributeValue == "normal")
-        return MathVariant::Normal;
-    if (attributeValue == "bold")
-        return MathVariant::Bold;
-    if (attributeValue == "italic")
-        return MathVariant::Italic;
-    if (attributeValue == "bold-italic")
-        return MathVariant::BoldItalic;
-    if (attributeValue == "double-struck")
-        return MathVariant::DoubleStruck;
-    if (attributeValue == "bold-fraktur")
-        return MathVariant::BoldFraktur;
-    if (attributeValue == "script")
-        return MathVariant::Script;
-    if (attributeValue == "bold-script")
-        return MathVariant::BoldScript;
-    if (attributeValue == "fraktur")
-        return MathVariant::Fraktur;
-    if (attributeValue == "sans-serif")
-        return MathVariant::SansSerif;
-    if (attributeValue == "bold-sans-serif")
-        return MathVariant::BoldSansSerif;
-    if (attributeValue == "sans-serif-italic")
-        return MathVariant::SansSerifItalic;
-    if (attributeValue == "sans-serif-bold-italic")
-        return MathVariant::SansSerifBoldItalic;
-    if (attributeValue == "monospace")
-        return MathVariant::Monospace;
-    if (attributeValue == "initial")
-        return MathVariant::Initial;
-    if (attributeValue == "tailed")
-        return MathVariant::Tailed;
-    if (attributeValue == "looped")
-        return MathVariant::Looped;
-    if (attributeValue == "stretched")
-        return MathVariant::Stretched;
-    return MathVariant::None;
-}
-
-Optional<bool> MathMLElement::specifiedDisplayStyle()
-{
-    if (!acceptsDisplayStyleAttribute())
-        return Nullopt;
-    const MathMLElement::BooleanValue& specifiedDisplayStyle = cachedBooleanAttribute(displaystyleAttr, m_displayStyle);
-    return toOptionalBool(specifiedDisplayStyle);
-}
-
-Optional<MathMLElement::MathVariant> MathMLElement::specifiedMathVariant()
-{
-    if (!acceptsMathVariantAttribute())
-        return Nullopt;
-    if (!m_mathVariant)
-        m_mathVariant = parseMathVariantAttribute(attributeWithoutSynchronization(mathvariantAttr));
-    return m_mathVariant.value() == MathVariant::None ? Nullopt : m_mathVariant;
-}
-
 }
 
 #endif // ENABLE(MATHML)
index 2590c33..d8863c6 100644 (file)
@@ -56,7 +56,6 @@ public:
         LengthType type { LengthType::ParsingFailed };
         float value { 0 };
     };
-    static Length parseMathMLLength(const String&);
 
     enum class BooleanValue { True, False, Default };
 
@@ -85,8 +84,8 @@ public:
         Stretched = 18
     };
 
-    virtual Optional<bool> specifiedDisplayStyle();
-    Optional<MathVariant> specifiedMathVariant();
+    virtual Optional<bool> specifiedDisplayStyle() { return Nullopt; }
+    virtual Optional<MathVariant> specifiedMathVariant() { return Nullopt; }
 
     virtual void updateSelectedChild() { }
 
@@ -101,27 +100,10 @@ protected:
     bool isPresentationAttribute(const QualifiedName&) const override;
     void collectStyleForPresentationAttribute(const QualifiedName&, const AtomicString&, MutableStyleProperties&) override;
 
-    bool isPhrasingContent(const Node&) const;
-    bool isFlowContent(const Node&) const;
-
     bool willRespondToMouseClickEvents() override;
     void defaultEventHandler(Event*) override;
 
-    const Length& cachedMathMLLength(const QualifiedName&, Optional<Length>&);
-    const BooleanValue& cachedBooleanAttribute(const QualifiedName&, Optional<BooleanValue>&);
-
-    virtual bool acceptsDisplayStyleAttribute() { return false; }
-    virtual bool acceptsMathVariantAttribute() { return false; }
-
-    static Optional<bool> toOptionalBool(const BooleanValue& value) { return value == BooleanValue::Default ? Nullopt : Optional<bool>(value == BooleanValue::True); }
-    Optional<BooleanValue> m_displayStyle;
-    Optional<MathVariant> m_mathVariant;
-
 private:
-    static Length parseNumberAndUnit(const StringView&);
-    static Length parseNamedSpace(const StringView&);
-    static MathVariant parseMathVariantAttribute(const AtomicString& attributeValue);
-
     bool canStartSelection() const final;
     bool isFocusable() const final;
     bool isKeyboardFocusable(KeyboardEvent*) const final;
index 00739cd..9355d38 100644 (file)
 
 #if ENABLE(MATHML)
 
+#include "ElementIterator.h"
+#include "HTMLHtmlElement.h"
+#include "HTMLMapElement.h"
+#include "HTMLNames.h"
+#include "HTMLParserIdioms.h"
+#include "MathMLMathElement.h"
 #include "MathMLNames.h"
 #include "RenderMathMLBlock.h"
+#include "RenderTableCell.h"
+#include "SVGNames.h"
+#include "SVGSVGElement.h"
 
 namespace WebCore {
 
@@ -55,11 +64,320 @@ RenderPtr<RenderElement> MathMLPresentationElement::createElementRenderer(Render
     return MathMLElement::createElementRenderer(WTFMove(style), insertionPosition);
 }
 
+bool MathMLPresentationElement::isPhrasingContent(const Node& node)
+{
+    // Phrasing content is described in the HTML 5 specification:
+    // http://www.w3.org/TR/html5/dom.html#phrasing-content.
+
+    if (!node.isElementNode())
+        return node.isTextNode();
+
+    if (is<MathMLElement>(node)) {
+        auto& mathmlElement = downcast<MathMLElement>(node);
+        return is<MathMLMathElement>(mathmlElement);
+    }
+
+    if (is<SVGElement>(node)) {
+        auto& svgElement = downcast<SVGElement>(node);
+        return is<SVGSVGElement>(svgElement);
+    }
+
+    if (is<HTMLElement>(node)) {
+        // FIXME: add the <data> and <time> tags when they are implemented.
+        auto& htmlElement = downcast<HTMLElement>(node);
+        return htmlElement.hasTagName(HTMLNames::aTag)
+            || htmlElement.hasTagName(HTMLNames::abbrTag)
+            || (htmlElement.hasTagName(HTMLNames::areaTag) && ancestorsOfType<HTMLMapElement>(htmlElement).first())
+            || htmlElement.hasTagName(HTMLNames::audioTag)
+            || htmlElement.hasTagName(HTMLNames::bTag)
+            || htmlElement.hasTagName(HTMLNames::bdiTag)
+            || htmlElement.hasTagName(HTMLNames::bdoTag)
+            || htmlElement.hasTagName(HTMLNames::brTag)
+            || htmlElement.hasTagName(HTMLNames::buttonTag)
+            || htmlElement.hasTagName(HTMLNames::canvasTag)
+            || htmlElement.hasTagName(HTMLNames::citeTag)
+            || htmlElement.hasTagName(HTMLNames::codeTag)
+            || htmlElement.hasTagName(HTMLNames::datalistTag)
+            || htmlElement.hasTagName(HTMLNames::delTag)
+            || htmlElement.hasTagName(HTMLNames::dfnTag)
+            || htmlElement.hasTagName(HTMLNames::emTag)
+            || htmlElement.hasTagName(HTMLNames::embedTag)
+            || htmlElement.hasTagName(HTMLNames::iTag)
+            || htmlElement.hasTagName(HTMLNames::iframeTag)
+            || htmlElement.hasTagName(HTMLNames::imgTag)
+            || htmlElement.hasTagName(HTMLNames::inputTag)
+            || htmlElement.hasTagName(HTMLNames::insTag)
+            || htmlElement.hasTagName(HTMLNames::kbdTag)
+            || htmlElement.hasTagName(HTMLNames::keygenTag)
+            || htmlElement.hasTagName(HTMLNames::labelTag)
+            || htmlElement.hasTagName(HTMLNames::mapTag)
+            || htmlElement.hasTagName(HTMLNames::markTag)
+            || htmlElement.hasTagName(HTMLNames::meterTag)
+            || htmlElement.hasTagName(HTMLNames::noscriptTag)
+            || htmlElement.hasTagName(HTMLNames::objectTag)
+            || htmlElement.hasTagName(HTMLNames::outputTag)
+            || htmlElement.hasTagName(HTMLNames::progressTag)
+            || htmlElement.hasTagName(HTMLNames::qTag)
+            || htmlElement.hasTagName(HTMLNames::rubyTag)
+            || htmlElement.hasTagName(HTMLNames::sTag)
+            || htmlElement.hasTagName(HTMLNames::sampTag)
+            || htmlElement.hasTagName(HTMLNames::scriptTag)
+            || htmlElement.hasTagName(HTMLNames::selectTag)
+            || htmlElement.hasTagName(HTMLNames::smallTag)
+            || htmlElement.hasTagName(HTMLNames::spanTag)
+            || htmlElement.hasTagName(HTMLNames::strongTag)
+            || htmlElement.hasTagName(HTMLNames::subTag)
+            || htmlElement.hasTagName(HTMLNames::supTag)
+            || htmlElement.hasTagName(HTMLNames::templateTag)
+            || htmlElement.hasTagName(HTMLNames::textareaTag)
+            || htmlElement.hasTagName(HTMLNames::uTag)
+            || htmlElement.hasTagName(HTMLNames::varTag)
+            || htmlElement.hasTagName(HTMLNames::videoTag)
+            || htmlElement.hasTagName(HTMLNames::wbrTag);
+    }
+
+    return false;
+}
+
+bool MathMLPresentationElement::isFlowContent(const Node& node)
+{
+    // Flow content is described in the HTML 5 specification:
+    // http://www.w3.org/TR/html5/dom.html#flow-content
+
+    if (isPhrasingContent(node))
+        return true;
+
+    if (!is<HTMLElement>(node))
+        return false;
+
+    auto& htmlElement = downcast<HTMLElement>(node);
+    // FIXME add the <dialog> tag when it is implemented.
+    return htmlElement.hasTagName(HTMLNames::addressTag)
+        || htmlElement.hasTagName(HTMLNames::articleTag)
+        || htmlElement.hasTagName(HTMLNames::asideTag)
+        || htmlElement.hasTagName(HTMLNames::blockquoteTag)
+        || htmlElement.hasTagName(HTMLNames::detailsTag)
+        || htmlElement.hasTagName(HTMLNames::divTag)
+        || htmlElement.hasTagName(HTMLNames::dlTag)
+        || htmlElement.hasTagName(HTMLNames::fieldsetTag)
+        || htmlElement.hasTagName(HTMLNames::figureTag)
+        || htmlElement.hasTagName(HTMLNames::footerTag)
+        || htmlElement.hasTagName(HTMLNames::formTag)
+        || htmlElement.hasTagName(HTMLNames::h1Tag)
+        || htmlElement.hasTagName(HTMLNames::h2Tag)
+        || htmlElement.hasTagName(HTMLNames::h3Tag)
+        || htmlElement.hasTagName(HTMLNames::h4Tag)
+        || htmlElement.hasTagName(HTMLNames::h5Tag)
+        || htmlElement.hasTagName(HTMLNames::h6Tag)
+        || htmlElement.hasTagName(HTMLNames::headerTag)
+        || htmlElement.hasTagName(HTMLNames::hrTag)
+        || htmlElement.hasTagName(HTMLNames::mainTag)
+        || htmlElement.hasTagName(HTMLNames::navTag)
+        || htmlElement.hasTagName(HTMLNames::olTag)
+        || htmlElement.hasTagName(HTMLNames::pTag)
+        || htmlElement.hasTagName(HTMLNames::preTag)
+        || htmlElement.hasTagName(HTMLNames::sectionTag)
+        || (htmlElement.hasTagName(HTMLNames::styleTag) && htmlElement.hasAttribute("scoped"))
+        || htmlElement.hasTagName(HTMLNames::tableTag)
+        || htmlElement.hasTagName(HTMLNames::ulTag);
+}
+
+const MathMLElement::BooleanValue& MathMLPresentationElement::cachedBooleanAttribute(const QualifiedName& name, Optional<BooleanValue>& attribute)
+{
+    if (attribute)
+        return attribute.value();
+
+    // In MathML, attribute values are case-sensitive.
+    const AtomicString& value = attributeWithoutSynchronization(name);
+    if (value == "true")
+        attribute = BooleanValue::True;
+    else if (value == "false")
+        attribute = BooleanValue::False;
+    else
+        attribute = BooleanValue::Default;
+
+    return attribute.value();
+}
+
+MathMLElement::Length MathMLPresentationElement::parseNumberAndUnit(const StringView& string)
+{
+    LengthType lengthType = LengthType::UnitLess;
+    unsigned stringLength = string.length();
+    UChar lastChar = string[stringLength - 1];
+    if (lastChar == '%') {
+        lengthType = LengthType::Percentage;
+        stringLength--;
+    } else if (stringLength >= 2) {
+        UChar penultimateChar = string[stringLength - 2];
+        if (penultimateChar == 'c' && lastChar == 'm')
+            lengthType = LengthType::Cm;
+        if (penultimateChar == 'e' && lastChar == 'm')
+            lengthType = LengthType::Em;
+        else if (penultimateChar == 'e' && lastChar == 'x')
+            lengthType = LengthType::Ex;
+        else if (penultimateChar == 'i' && lastChar == 'n')
+            lengthType = LengthType::In;
+        else if (penultimateChar == 'm' && lastChar == 'm')
+            lengthType = LengthType::Mm;
+        else if (penultimateChar == 'p' && lastChar == 'c')
+            lengthType = LengthType::Pc;
+        else if (penultimateChar == 'p' && lastChar == 't')
+            lengthType = LengthType::Pt;
+        else if (penultimateChar == 'p' && lastChar == 'x')
+            lengthType = LengthType::Px;
+
+        if (lengthType != LengthType::UnitLess)
+            stringLength -= 2;
+    }
+
+    bool ok;
+    float lengthValue = string.substring(0, stringLength).toFloat(ok);
+    if (!ok)
+        return Length();
+
+    Length length;
+    length.type = lengthType;
+    length.value = lengthValue;
+    return length;
+}
+
+MathMLElement::Length MathMLPresentationElement::parseNamedSpace(const StringView& string)
+{
+    // Named space values are case-sensitive.
+    int namedSpaceValue;
+    if (string == "veryverythinmathspace")
+        namedSpaceValue = 1;
+    else if (string == "verythinmathspace")
+        namedSpaceValue = 2;
+    else if (string == "thinmathspace")
+        namedSpaceValue = 3;
+    else if (string == "mediummathspace")
+        namedSpaceValue = 4;
+    else if (string == "thickmathspace")
+        namedSpaceValue = 5;
+    else if (string == "verythickmathspace")
+        namedSpaceValue = 6;
+    else if (string == "veryverythickmathspace")
+        namedSpaceValue = 7;
+    else if (string == "negativeveryverythinmathspace")
+        namedSpaceValue = -1;
+    else if (string == "negativeverythinmathspace")
+        namedSpaceValue = -2;
+    else if (string == "negativethinmathspace")
+        namedSpaceValue = -3;
+    else if (string == "negativemediummathspace")
+        namedSpaceValue = -4;
+    else if (string == "negativethickmathspace")
+        namedSpaceValue = -5;
+    else if (string == "negativeverythickmathspace")
+        namedSpaceValue = -6;
+    else if (string == "negativeveryverythickmathspace")
+        namedSpaceValue = -7;
+    else
+        return Length();
+
+    Length length;
+    length.type = LengthType::MathUnit;
+    length.value = namedSpaceValue;
+    return length;
+}
+
+MathMLElement::Length MathMLPresentationElement::parseMathMLLength(const String& string)
+{
+    // The regular expression from the MathML Relax NG schema is as follows:
+    //
+    //   pattern = '\s*((-?[0-9]*([0-9]\.?|\.[0-9])[0-9]*(e[mx]|in|cm|mm|p[xtc]|%)?)|(negative)?((very){0,2}thi(n|ck)|medium)mathspace)\s*'
+    //
+    // We do not perform a strict verification of the syntax of whitespaces and number.
+    // Instead, we just use isHTMLSpace and toFloat to parse these parts.
+
+    // We first skip whitespace from both ends of the string.
+    StringView stringView = stripLeadingAndTrailingWhitespace(string);
+
+    if (stringView.isEmpty())
+        return Length();
+
+    // We consider the most typical case: a number followed by an optional unit.
+    UChar firstChar = stringView[0];
+    if (isASCIIDigit(firstChar) || firstChar == '-' || firstChar == '.')
+        return parseNumberAndUnit(stringView);
+
+    // Otherwise, we try and parse a named space.
+    return parseNamedSpace(stringView);
+}
+
+const MathMLElement::Length& MathMLPresentationElement::cachedMathMLLength(const QualifiedName& name, Optional<Length>& length)
+{
+    if (length)
+        return length.value();
+    length = parseMathMLLength(attributeWithoutSynchronization(name));
+    return length.value();
+}
+
 bool MathMLPresentationElement::acceptsDisplayStyleAttribute()
 {
     return hasTagName(mtableTag);
 }
 
+Optional<bool> MathMLPresentationElement::specifiedDisplayStyle()
+{
+    if (!acceptsDisplayStyleAttribute())
+        return Nullopt;
+    const MathMLElement::BooleanValue& specifiedDisplayStyle = cachedBooleanAttribute(displaystyleAttr, m_displayStyle);
+    return toOptionalBool(specifiedDisplayStyle);
+}
+
+MathMLElement::MathVariant MathMLPresentationElement::parseMathVariantAttribute(const AtomicString& attributeValue)
+{
+    // The mathvariant attribute values is case-sensitive.
+    if (attributeValue == "normal")
+        return MathVariant::Normal;
+    if (attributeValue == "bold")
+        return MathVariant::Bold;
+    if (attributeValue == "italic")
+        return MathVariant::Italic;
+    if (attributeValue == "bold-italic")
+        return MathVariant::BoldItalic;
+    if (attributeValue == "double-struck")
+        return MathVariant::DoubleStruck;
+    if (attributeValue == "bold-fraktur")
+        return MathVariant::BoldFraktur;
+    if (attributeValue == "script")
+        return MathVariant::Script;
+    if (attributeValue == "bold-script")
+        return MathVariant::BoldScript;
+    if (attributeValue == "fraktur")
+        return MathVariant::Fraktur;
+    if (attributeValue == "sans-serif")
+        return MathVariant::SansSerif;
+    if (attributeValue == "bold-sans-serif")
+        return MathVariant::BoldSansSerif;
+    if (attributeValue == "sans-serif-italic")
+        return MathVariant::SansSerifItalic;
+    if (attributeValue == "sans-serif-bold-italic")
+        return MathVariant::SansSerifBoldItalic;
+    if (attributeValue == "monospace")
+        return MathVariant::Monospace;
+    if (attributeValue == "initial")
+        return MathVariant::Initial;
+    if (attributeValue == "tailed")
+        return MathVariant::Tailed;
+    if (attributeValue == "looped")
+        return MathVariant::Looped;
+    if (attributeValue == "stretched")
+        return MathVariant::Stretched;
+    return MathVariant::None;
+}
+
+Optional<MathMLElement::MathVariant> MathMLPresentationElement::specifiedMathVariant()
+{
+    if (!acceptsMathVariantAttribute())
+        return Nullopt;
+    if (!m_mathVariant)
+        m_mathVariant = parseMathVariantAttribute(attributeWithoutSynchronization(mathvariantAttr));
+    return m_mathVariant.value() == MathVariant::None ? Nullopt : m_mathVariant;
+}
+
 void MathMLPresentationElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
 {
     bool displayStyleAttribute = name == displaystyleAttr && acceptsDisplayStyleAttribute();
index b9f3300..1af8b81 100644 (file)
@@ -41,11 +41,31 @@ protected:
     MathMLPresentationElement(const QualifiedName& tagName, Document&);
     void parseAttribute(const QualifiedName&, const AtomicString&) override;
 
-    bool acceptsDisplayStyleAttribute() override;
+    static bool isPhrasingContent(const Node&);
+    static bool isFlowContent(const Node&);
+
+    static Optional<bool> toOptionalBool(const BooleanValue& value) { return value == BooleanValue::Default ? Nullopt : Optional<bool>(value == BooleanValue::True); }
+    const BooleanValue& cachedBooleanAttribute(const QualifiedName&, Optional<BooleanValue>&);
+
+    static Length parseMathMLLength(const String&);
+    const Length& cachedMathMLLength(const QualifiedName&, Optional<Length>&);
+
+    virtual bool acceptsDisplayStyleAttribute();
+    Optional<bool> specifiedDisplayStyle() override;
+
+    virtual bool acceptsMathVariantAttribute() { return false; }
+    Optional<MathVariant> specifiedMathVariant() final;
+
+    Optional<BooleanValue> m_displayStyle;
+    Optional<MathVariant> m_mathVariant;
 
 private:
     RenderPtr<RenderElement> createElementRenderer(RenderStyle&&, const RenderTreePosition&) override;
     bool isPresentationMathML() const final { return true; }
+
+    static Length parseNumberAndUnit(const StringView&);
+    static Length parseNamedSpace(const StringView&);
+    static MathVariant parseMathVariantAttribute(const AtomicString& attributeValue);
 };
 
 }