Replace reftest mathml/presentation/fractions-positions.html with script tests
[WebKit-https.git] / Source / WebCore / mathml / MathMLElement.cpp
1 /*
2  * Copyright (C) 2009 Alex Milowski (alex@milowski.com). All rights reserved.
3  * Copyright (C) 2010 Apple Inc. All rights reserved.
4  * Copyright (C) 2010 Fran├žois Sausset (sausset@gmail.com). All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "config.h"
29
30 #if ENABLE(MATHML)
31
32 #include "MathMLElement.h"
33
34 #include "ElementIterator.h"
35 #include "HTMLElement.h"
36 #include "HTMLHtmlElement.h"
37 #include "HTMLMapElement.h"
38 #include "HTMLNames.h"
39 #include "HTMLParserIdioms.h"
40 #include "MathMLMathElement.h"
41 #include "MathMLNames.h"
42 #include "MathMLSelectElement.h"
43 #include "RenderTableCell.h"
44 #include "SVGElement.h"
45 #include "SVGNames.h"
46 #include "SVGSVGElement.h"
47
48 namespace WebCore {
49
50 using namespace MathMLNames;
51
52 MathMLElement::MathMLElement(const QualifiedName& tagName, Document& document)
53     : StyledElement(tagName, document, CreateMathMLElement)
54 {
55 }
56
57 Ref<MathMLElement> MathMLElement::create(const QualifiedName& tagName, Document& document)
58 {
59     return adoptRef(*new MathMLElement(tagName, document));
60 }
61
62 bool MathMLElement::isPresentationMathML() const
63 {
64     return hasTagName(MathMLNames::mtrTag)
65         || hasTagName(MathMLNames::mtdTag)
66         || hasTagName(MathMLNames::maligngroupTag)
67         || hasTagName(MathMLNames::malignmarkTag)
68         || hasTagName(MathMLNames::mencloseTag)
69         || hasTagName(MathMLNames::mglyphTag)
70         || hasTagName(MathMLNames::mlabeledtrTag)
71         || hasTagName(MathMLNames::mlongdivTag)
72         || hasTagName(MathMLNames::mpaddedTag)
73         || hasTagName(MathMLNames::msTag)
74         || hasTagName(MathMLNames::mscarriesTag)
75         || hasTagName(MathMLNames::mscarryTag)
76         || hasTagName(MathMLNames::msgroupTag)
77         || hasTagName(MathMLNames::mslineTag)
78         || hasTagName(MathMLNames::msrowTag)
79         || hasTagName(MathMLNames::mstackTag);
80 }
81
82 bool MathMLElement::isPhrasingContent(const Node& node) const
83 {
84     // Phrasing content is described in the HTML 5 specification:
85     // http://www.w3.org/TR/html5/dom.html#phrasing-content.
86
87     if (!node.isElementNode())
88         return node.isTextNode();
89
90     if (is<MathMLElement>(node)) {
91         auto& mathmlElement = downcast<MathMLElement>(node);
92         return is<MathMLMathElement>(mathmlElement);
93     }
94
95     if (is<SVGElement>(node)) {
96         auto& svgElement = downcast<SVGElement>(node);
97         return is<SVGSVGElement>(svgElement);
98     }
99
100     if (is<HTMLElement>(node)) {
101         // FIXME: add the <data> and <time> tags when they are implemented.
102         auto& htmlElement = downcast<HTMLElement>(node);
103         return htmlElement.hasTagName(HTMLNames::aTag)
104             || htmlElement.hasTagName(HTMLNames::abbrTag)
105             || (htmlElement.hasTagName(HTMLNames::areaTag) && ancestorsOfType<HTMLMapElement>(htmlElement).first())
106             || htmlElement.hasTagName(HTMLNames::audioTag)
107             || htmlElement.hasTagName(HTMLNames::bTag)
108             || htmlElement.hasTagName(HTMLNames::bdiTag)
109             || htmlElement.hasTagName(HTMLNames::bdoTag)
110             || htmlElement.hasTagName(HTMLNames::brTag)
111             || htmlElement.hasTagName(HTMLNames::buttonTag)
112             || htmlElement.hasTagName(HTMLNames::canvasTag)
113             || htmlElement.hasTagName(HTMLNames::citeTag)
114             || htmlElement.hasTagName(HTMLNames::codeTag)
115             || htmlElement.hasTagName(HTMLNames::datalistTag)
116             || htmlElement.hasTagName(HTMLNames::delTag)
117             || htmlElement.hasTagName(HTMLNames::dfnTag)
118             || htmlElement.hasTagName(HTMLNames::emTag)
119             || htmlElement.hasTagName(HTMLNames::embedTag)
120             || htmlElement.hasTagName(HTMLNames::iTag)
121             || htmlElement.hasTagName(HTMLNames::iframeTag)
122             || htmlElement.hasTagName(HTMLNames::imgTag)
123             || htmlElement.hasTagName(HTMLNames::inputTag)
124             || htmlElement.hasTagName(HTMLNames::insTag)
125             || htmlElement.hasTagName(HTMLNames::kbdTag)
126             || htmlElement.hasTagName(HTMLNames::keygenTag)
127             || htmlElement.hasTagName(HTMLNames::labelTag)
128             || htmlElement.hasTagName(HTMLNames::mapTag)
129             || htmlElement.hasTagName(HTMLNames::markTag)
130             || htmlElement.hasTagName(HTMLNames::meterTag)
131             || htmlElement.hasTagName(HTMLNames::noscriptTag)
132             || htmlElement.hasTagName(HTMLNames::objectTag)
133             || htmlElement.hasTagName(HTMLNames::outputTag)
134             || htmlElement.hasTagName(HTMLNames::progressTag)
135             || htmlElement.hasTagName(HTMLNames::qTag)
136             || htmlElement.hasTagName(HTMLNames::rubyTag)
137             || htmlElement.hasTagName(HTMLNames::sTag)
138             || htmlElement.hasTagName(HTMLNames::sampTag)
139             || htmlElement.hasTagName(HTMLNames::scriptTag)
140             || htmlElement.hasTagName(HTMLNames::selectTag)
141             || htmlElement.hasTagName(HTMLNames::smallTag)
142             || htmlElement.hasTagName(HTMLNames::spanTag)
143             || htmlElement.hasTagName(HTMLNames::strongTag)
144             || htmlElement.hasTagName(HTMLNames::subTag)
145             || htmlElement.hasTagName(HTMLNames::supTag)
146             || htmlElement.hasTagName(HTMLNames::templateTag)
147             || htmlElement.hasTagName(HTMLNames::textareaTag)
148             || htmlElement.hasTagName(HTMLNames::uTag)
149             || htmlElement.hasTagName(HTMLNames::varTag)
150             || htmlElement.hasTagName(HTMLNames::videoTag)
151             || htmlElement.hasTagName(HTMLNames::wbrTag);
152     }
153
154     return false;
155 }
156
157 bool MathMLElement::isFlowContent(const Node& node) const
158 {
159     // Flow content is described in the HTML 5 specification:
160     // http://www.w3.org/TR/html5/dom.html#flow-content
161
162     if (isPhrasingContent(node))
163         return true;
164
165     if (!is<HTMLElement>(node))
166         return false;
167
168     auto& htmlElement = downcast<HTMLElement>(node);
169     // FIXME add the <dialog> tag when it is implemented.
170     return htmlElement.hasTagName(HTMLNames::addressTag)
171         || htmlElement.hasTagName(HTMLNames::articleTag)
172         || htmlElement.hasTagName(HTMLNames::asideTag)
173         || htmlElement.hasTagName(HTMLNames::blockquoteTag)
174         || htmlElement.hasTagName(HTMLNames::detailsTag)
175         || htmlElement.hasTagName(HTMLNames::divTag)
176         || htmlElement.hasTagName(HTMLNames::dlTag)
177         || htmlElement.hasTagName(HTMLNames::fieldsetTag)
178         || htmlElement.hasTagName(HTMLNames::figureTag)
179         || htmlElement.hasTagName(HTMLNames::footerTag)
180         || htmlElement.hasTagName(HTMLNames::formTag)
181         || htmlElement.hasTagName(HTMLNames::h1Tag)
182         || htmlElement.hasTagName(HTMLNames::h2Tag)
183         || htmlElement.hasTagName(HTMLNames::h3Tag)
184         || htmlElement.hasTagName(HTMLNames::h4Tag)
185         || htmlElement.hasTagName(HTMLNames::h5Tag)
186         || htmlElement.hasTagName(HTMLNames::h6Tag)
187         || htmlElement.hasTagName(HTMLNames::headerTag)
188         || htmlElement.hasTagName(HTMLNames::hrTag)
189         || htmlElement.hasTagName(HTMLNames::mainTag)
190         || htmlElement.hasTagName(HTMLNames::navTag)
191         || htmlElement.hasTagName(HTMLNames::olTag)
192         || htmlElement.hasTagName(HTMLNames::pTag)
193         || htmlElement.hasTagName(HTMLNames::preTag)
194         || htmlElement.hasTagName(HTMLNames::sectionTag)
195         || (htmlElement.hasTagName(HTMLNames::styleTag) && htmlElement.hasAttribute("scoped"))
196         || htmlElement.hasTagName(HTMLNames::tableTag)
197         || htmlElement.hasTagName(HTMLNames::ulTag);
198 }
199
200 unsigned MathMLElement::colSpan() const
201 {
202     if (!hasTagName(mtdTag))
203         return 1u;
204     const AtomicString& colSpanValue = fastGetAttribute(columnspanAttr);
205     return std::max(1u, limitToOnlyHTMLNonNegative(colSpanValue, 1u));
206 }
207
208 unsigned MathMLElement::rowSpan() const
209 {
210     if (!hasTagName(mtdTag))
211         return 1u;
212     const AtomicString& rowSpanValue = fastGetAttribute(rowspanAttr);
213     static const unsigned maxRowspan = 8190; // This constant comes from HTMLTableCellElement.
214     return std::max(1u, std::min(limitToOnlyHTMLNonNegative(rowSpanValue, 1u), maxRowspan));
215 }
216
217 void MathMLElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
218 {
219     if (name == rowspanAttr) {
220         if (is<RenderTableCell>(renderer()) && hasTagName(mtdTag))
221             downcast<RenderTableCell>(*renderer()).colSpanOrRowSpanChanged();
222     } else if (name == columnspanAttr) {
223         if (is<RenderTableCell>(renderer()) && hasTagName(mtdTag))
224             downcast<RenderTableCell>(renderer())->colSpanOrRowSpanChanged();
225     } else
226         StyledElement::parseAttribute(name, value);
227 }
228
229 bool MathMLElement::isPresentationAttribute(const QualifiedName& name) const
230 {
231     if (name == backgroundAttr || name == colorAttr || name == dirAttr || name == fontfamilyAttr || name == fontsizeAttr || name == fontstyleAttr || name == fontweightAttr || name == mathbackgroundAttr || name == mathcolorAttr || name == mathsizeAttr)
232         return true;
233     return StyledElement::isPresentationAttribute(name);
234 }
235
236 void MathMLElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStyleProperties& style)
237 {
238     if (name == mathbackgroundAttr)
239         addPropertyToPresentationAttributeStyle(style, CSSPropertyBackgroundColor, value);
240     else if (name == mathsizeAttr) {
241         // The following three values of mathsize are handled in WebCore/css/mathml.css
242         if (value != "normal" && value != "small" && value != "big")
243             addPropertyToPresentationAttributeStyle(style, CSSPropertyFontSize, value);
244     } else if (name == mathcolorAttr)
245         addPropertyToPresentationAttributeStyle(style, CSSPropertyColor, value);
246     // FIXME: deprecated attributes that should loose in a conflict with a non deprecated attribute
247     else if (name == fontsizeAttr)
248         addPropertyToPresentationAttributeStyle(style, CSSPropertyFontSize, value);
249     else if (name == backgroundAttr)
250         addPropertyToPresentationAttributeStyle(style, CSSPropertyBackgroundColor, value);
251     else if (name == colorAttr)
252         addPropertyToPresentationAttributeStyle(style, CSSPropertyColor, value);
253     else if (name == fontstyleAttr)
254         addPropertyToPresentationAttributeStyle(style, CSSPropertyFontStyle, value);
255     else if (name == fontweightAttr)
256         addPropertyToPresentationAttributeStyle(style, CSSPropertyFontWeight, value);
257     else if (name == fontfamilyAttr)
258         addPropertyToPresentationAttributeStyle(style, CSSPropertyFontFamily, value);
259     else if (name == dirAttr) {
260         if (hasTagName(mathTag) || hasTagName(mrowTag) || hasTagName(mstyleTag) || isMathMLToken())
261             addPropertyToPresentationAttributeStyle(style, CSSPropertyDirection, value);
262     }  else {
263         ASSERT(!isPresentationAttribute(name));
264         StyledElement::collectStyleForPresentationAttribute(name, value
265         , style);
266     }
267 }
268
269 bool MathMLElement::childShouldCreateRenderer(const Node& child) const
270 {
271     if (hasTagName(annotation_xmlTag)) {
272         const AtomicString& value = fastGetAttribute(MathMLNames::encodingAttr);
273
274         // See annotation-xml.model.mathml, annotation-xml.model.svg and annotation-xml.model.xhtml in the HTML5 RelaxNG schema.
275
276         if (is<MathMLElement>(child) && (MathMLSelectElement::isMathMLEncoding(value) || MathMLSelectElement::isHTMLEncoding(value))) {
277             auto& mathmlElement = downcast<MathMLElement>(child);
278             return is<MathMLMathElement>(mathmlElement);
279         }
280
281         if (is<SVGElement>(child) && (MathMLSelectElement::isSVGEncoding(value) || MathMLSelectElement::isHTMLEncoding(value))) {
282             auto& svgElement = downcast<SVGElement>(child);
283             return is<SVGSVGElement>(svgElement);
284         }
285
286         if (is<HTMLElement>(child) && MathMLSelectElement::isHTMLEncoding(value)) {
287             auto& htmlElement = downcast<HTMLElement>(child);
288             return is<HTMLHtmlElement>(htmlElement) || (isFlowContent(htmlElement) && StyledElement::childShouldCreateRenderer(child));
289         }
290
291         return false;
292     }
293
294     // In general, only MathML children are allowed. Text nodes are only visible in token MathML elements.
295     return is<MathMLElement>(child);
296 }
297
298 void MathMLElement::attributeChanged(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason reason)
299 {
300     if (isSemanticAnnotation() && (name == MathMLNames::srcAttr || name == MathMLNames::encodingAttr)) {
301         auto* parent = parentElement();
302         if (is<MathMLElement>(parent) && parent->hasTagName(semanticsTag))
303             downcast<MathMLElement>(*parent).updateSelectedChild();
304     }
305     StyledElement::attributeChanged(name, oldValue, newValue, reason);
306 }
307
308 }
309
310 #endif // ENABLE(MATHML)