summaryrefslogtreecommitdiff
path: root/Source/WebCore/mathml/MathMLPresentationElement.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/mathml/MathMLPresentationElement.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/mathml/MathMLPresentationElement.cpp')
-rw-r--r--Source/WebCore/mathml/MathMLPresentationElement.cpp396
1 files changed, 396 insertions, 0 deletions
diff --git a/Source/WebCore/mathml/MathMLPresentationElement.cpp b/Source/WebCore/mathml/MathMLPresentationElement.cpp
new file mode 100644
index 000000000..e2c7d7945
--- /dev/null
+++ b/Source/WebCore/mathml/MathMLPresentationElement.cpp
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2009 Alex Milowski (alex@milowski.com). All rights reserved.
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2016 Igalia S.L.
+ *
+ * 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 "MathMLPresentationElement.h"
+
+#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 "SVGSVGElement.h"
+
+namespace WebCore {
+
+using namespace MathMLNames;
+
+MathMLPresentationElement::MathMLPresentationElement(const QualifiedName& tagName, Document& document)
+ : MathMLElement(tagName, document)
+{
+}
+
+Ref<MathMLPresentationElement> MathMLPresentationElement::create(const QualifiedName& tagName, Document& document)
+{
+ return adoptRef(*new MathMLPresentationElement(tagName, document));
+}
+
+RenderPtr<RenderElement> MathMLPresentationElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition& insertionPosition)
+{
+ if (hasTagName(mtableTag))
+ return createRenderer<RenderMathMLTable>(*this, WTFMove(style));
+
+ 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, std::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, std::optional<Length>& length)
+{
+ if (length)
+ return length.value();
+ length = parseMathMLLength(attributeWithoutSynchronization(name));
+ return length.value();
+}
+
+bool MathMLPresentationElement::acceptsDisplayStyleAttribute()
+{
+ return hasTagName(mtableTag);
+}
+
+std::optional<bool> MathMLPresentationElement::specifiedDisplayStyle()
+{
+ if (!acceptsDisplayStyleAttribute())
+ return std::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;
+}
+
+std::optional<MathMLElement::MathVariant> MathMLPresentationElement::specifiedMathVariant()
+{
+ if (!acceptsMathVariantAttribute())
+ return std::nullopt;
+ if (!m_mathVariant)
+ m_mathVariant = parseMathVariantAttribute(attributeWithoutSynchronization(mathvariantAttr));
+ return m_mathVariant.value() == MathVariant::None ? std::nullopt : m_mathVariant;
+}
+
+void MathMLPresentationElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
+{
+ bool displayStyleAttribute = name == displaystyleAttr && acceptsDisplayStyleAttribute();
+ bool mathVariantAttribute = name == mathvariantAttr && acceptsMathVariantAttribute();
+ if (displayStyleAttribute)
+ m_displayStyle = std::nullopt;
+ if (mathVariantAttribute)
+ m_mathVariant = std::nullopt;
+ if ((displayStyleAttribute || mathVariantAttribute) && renderer())
+ MathMLStyle::resolveMathMLStyleTree(renderer());
+
+ MathMLElement::parseAttribute(name, value);
+}
+
+}
+
+#endif // ENABLE(MATHML)