Use SPECIALIZE_TYPE_TRAITS_*() macro for MathMLElement
[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 "HTMLMapElement.h"
37 #include "HTMLNames.h"
38 #include "MathMLNames.h"
39 #include "MathMLSelectElement.h"
40 #include "RenderTableCell.h"
41 #include "SVGElement.h"
42 #include "SVGNames.h"
43
44 namespace WebCore {
45     
46 using namespace MathMLNames;
47     
48 MathMLElement::MathMLElement(const QualifiedName& tagName, Document& document)
49     : StyledElement(tagName, document, CreateMathMLElement)
50 {
51 }
52     
53 PassRefPtr<MathMLElement> MathMLElement::create(const QualifiedName& tagName, Document& document)
54 {
55     return adoptRef(new MathMLElement(tagName, document));
56 }
57
58 bool MathMLElement::isPresentationMathML() const
59 {
60     return hasTagName(MathMLNames::mtrTag)
61         || hasTagName(MathMLNames::mtdTag)
62         || hasTagName(MathMLNames::maligngroupTag)
63         || hasTagName(MathMLNames::malignmarkTag)
64         || hasTagName(MathMLNames::mencloseTag)
65         || hasTagName(MathMLNames::mglyphTag)
66         || hasTagName(MathMLNames::mlabeledtrTag)
67         || hasTagName(MathMLNames::mlongdivTag)
68         || hasTagName(MathMLNames::mpaddedTag)
69         || hasTagName(MathMLNames::msTag)
70         || hasTagName(MathMLNames::mscarriesTag)
71         || hasTagName(MathMLNames::mscarryTag)
72         || hasTagName(MathMLNames::msgroupTag)
73         || hasTagName(MathMLNames::mslineTag)
74         || hasTagName(MathMLNames::msrowTag)
75         || hasTagName(MathMLNames::mstackTag);
76 }
77
78 bool MathMLElement::isPhrasingContent(const Node& node) const
79 {
80     // Phrasing content is described in the HTML 5 specification:
81     // http://www.w3.org/TR/html5/dom.html#phrasing-content.
82
83     if (!node.isElementNode())
84         return node.isTextNode();
85
86     if (is<MathMLElement>(node)) {
87         auto& mathmlElement = downcast<MathMLElement>(node);
88         return is<MathMLMathElement>(mathmlElement);
89     }
90
91     if (is<SVGElement>(node)) {
92         auto& svgElement = downcast<SVGElement>(node);
93         return is<SVGSVGElement>(svgElement);
94     }
95
96     if (is<HTMLElement>(node)) {
97         // FIXME: add the <data> and <time> tags when they are implemented.
98         auto& htmlElement = downcast<HTMLElement>(node);
99         return htmlElement.hasTagName(HTMLNames::aTag)
100             || htmlElement.hasTagName(HTMLNames::abbrTag)
101             || (htmlElement.hasTagName(HTMLNames::areaTag) && ancestorsOfType<HTMLMapElement>(htmlElement).first())
102             || htmlElement.hasTagName(HTMLNames::audioTag)
103             || htmlElement.hasTagName(HTMLNames::bTag)
104             || htmlElement.hasTagName(HTMLNames::bdiTag)
105             || htmlElement.hasTagName(HTMLNames::bdoTag)
106             || htmlElement.hasTagName(HTMLNames::brTag)
107             || htmlElement.hasTagName(HTMLNames::buttonTag)
108             || htmlElement.hasTagName(HTMLNames::canvasTag)
109             || htmlElement.hasTagName(HTMLNames::citeTag)
110             || htmlElement.hasTagName(HTMLNames::codeTag)
111             || htmlElement.hasTagName(HTMLNames::datalistTag)
112             || htmlElement.hasTagName(HTMLNames::delTag)
113             || htmlElement.hasTagName(HTMLNames::dfnTag)
114             || htmlElement.hasTagName(HTMLNames::emTag)
115             || htmlElement.hasTagName(HTMLNames::embedTag)
116             || htmlElement.hasTagName(HTMLNames::iTag)
117             || htmlElement.hasTagName(HTMLNames::iframeTag)
118             || htmlElement.hasTagName(HTMLNames::imgTag)
119             || htmlElement.hasTagName(HTMLNames::inputTag)
120             || htmlElement.hasTagName(HTMLNames::insTag)
121             || htmlElement.hasTagName(HTMLNames::kbdTag)
122             || htmlElement.hasTagName(HTMLNames::keygenTag)
123             || htmlElement.hasTagName(HTMLNames::labelTag)
124             || htmlElement.hasTagName(HTMLNames::mapTag)
125             || htmlElement.hasTagName(HTMLNames::markTag)
126             || htmlElement.hasTagName(HTMLNames::meterTag)
127             || htmlElement.hasTagName(HTMLNames::noscriptTag)
128             || htmlElement.hasTagName(HTMLNames::objectTag)
129             || htmlElement.hasTagName(HTMLNames::outputTag)
130             || htmlElement.hasTagName(HTMLNames::progressTag)
131             || htmlElement.hasTagName(HTMLNames::qTag)
132             || htmlElement.hasTagName(HTMLNames::rubyTag)
133             || htmlElement.hasTagName(HTMLNames::sTag)
134             || htmlElement.hasTagName(HTMLNames::sampTag)
135             || htmlElement.hasTagName(HTMLNames::scriptTag)
136             || htmlElement.hasTagName(HTMLNames::selectTag)
137             || htmlElement.hasTagName(HTMLNames::smallTag)
138             || htmlElement.hasTagName(HTMLNames::spanTag)
139             || htmlElement.hasTagName(HTMLNames::strongTag)
140             || htmlElement.hasTagName(HTMLNames::subTag)
141             || htmlElement.hasTagName(HTMLNames::supTag)
142             || htmlElement.hasTagName(HTMLNames::templateTag)
143             || htmlElement.hasTagName(HTMLNames::textareaTag)
144             || htmlElement.hasTagName(HTMLNames::uTag)
145             || htmlElement.hasTagName(HTMLNames::varTag)
146             || htmlElement.hasTagName(HTMLNames::videoTag)
147             || htmlElement.hasTagName(HTMLNames::wbrTag);
148     }
149
150     return false;
151 }
152
153 bool MathMLElement::isFlowContent(const Node& node) const
154 {
155     // Flow content is described in the HTML 5 specification:
156     // http://www.w3.org/TR/html5/dom.html#flow-content
157
158     if (isPhrasingContent(node))
159         return true;
160
161     if (!is<HTMLElement>(node))
162         return false;
163
164     auto& htmlElement = downcast<HTMLElement>(node);
165     // FIXME add the <dialog> tag when it is implemented.
166     return htmlElement.hasTagName(HTMLNames::addressTag)
167         || htmlElement.hasTagName(HTMLNames::articleTag)
168         || htmlElement.hasTagName(HTMLNames::asideTag)
169         || htmlElement.hasTagName(HTMLNames::blockquoteTag)
170         || htmlElement.hasTagName(HTMLNames::detailsTag)
171         || htmlElement.hasTagName(HTMLNames::divTag)
172         || htmlElement.hasTagName(HTMLNames::dlTag)
173         || htmlElement.hasTagName(HTMLNames::fieldsetTag)
174         || htmlElement.hasTagName(HTMLNames::figureTag)
175         || htmlElement.hasTagName(HTMLNames::footerTag)
176         || htmlElement.hasTagName(HTMLNames::formTag)
177         || htmlElement.hasTagName(HTMLNames::h1Tag)
178         || htmlElement.hasTagName(HTMLNames::h2Tag)
179         || htmlElement.hasTagName(HTMLNames::h3Tag)
180         || htmlElement.hasTagName(HTMLNames::h4Tag)
181         || htmlElement.hasTagName(HTMLNames::h5Tag)
182         || htmlElement.hasTagName(HTMLNames::h6Tag)
183         || htmlElement.hasTagName(HTMLNames::headerTag)
184         || htmlElement.hasTagName(HTMLNames::hrTag)
185         || htmlElement.hasTagName(HTMLNames::mainTag)
186         || htmlElement.hasTagName(HTMLNames::navTag)
187         || htmlElement.hasTagName(HTMLNames::olTag)
188         || htmlElement.hasTagName(HTMLNames::pTag)
189         || htmlElement.hasTagName(HTMLNames::preTag)
190         || htmlElement.hasTagName(HTMLNames::sectionTag)
191         || (htmlElement.hasTagName(HTMLNames::styleTag) && htmlElement.hasAttribute("scoped"))
192         || htmlElement.hasTagName(HTMLNames::tableTag)
193         || htmlElement.hasTagName(HTMLNames::ulTag);
194 }
195
196 int MathMLElement::colSpan() const
197 {
198     if (!hasTagName(mtdTag))
199         return 1;
200     const AtomicString& colSpanValue = fastGetAttribute(columnspanAttr);
201     return std::max(1, colSpanValue.toInt());
202 }
203
204 int MathMLElement::rowSpan() const
205 {
206     if (!hasTagName(mtdTag))
207         return 1;
208     const AtomicString& rowSpanValue = fastGetAttribute(rowspanAttr);
209     return std::max(1, rowSpanValue.toInt());
210 }
211
212 void MathMLElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
213 {
214     if (name == rowspanAttr) {
215         if (renderer() && renderer()->isTableCell() && hasTagName(mtdTag))
216             toRenderTableCell(renderer())->colSpanOrRowSpanChanged();
217     } else if (name == columnspanAttr) {
218         if (renderer() && renderer()->isTableCell() && hasTagName(mtdTag))
219             toRenderTableCell(renderer())->colSpanOrRowSpanChanged();
220     } else
221         StyledElement::parseAttribute(name, value);
222 }
223
224 bool MathMLElement::isPresentationAttribute(const QualifiedName& name) const
225 {
226     if (name == backgroundAttr || name == colorAttr || name == dirAttr || name == fontfamilyAttr || name == fontsizeAttr || name == fontstyleAttr || name == fontweightAttr || name == mathbackgroundAttr || name == mathcolorAttr || name == mathsizeAttr)
227         return true;
228     return StyledElement::isPresentationAttribute(name);
229 }
230
231 void MathMLElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStyleProperties& style)
232 {
233     if (name == mathbackgroundAttr)
234         addPropertyToPresentationAttributeStyle(style, CSSPropertyBackgroundColor, value);
235     else if (name == mathsizeAttr) {
236         // The following three values of mathsize are handled in WebCore/css/mathml.css
237         if (value != "normal" && value != "small" && value != "big")
238             addPropertyToPresentationAttributeStyle(style, CSSPropertyFontSize, value);
239     } else if (name == mathcolorAttr)
240         addPropertyToPresentationAttributeStyle(style, CSSPropertyColor, value);
241     // FIXME: deprecated attributes that should loose in a conflict with a non deprecated attribute
242     else if (name == fontsizeAttr)
243         addPropertyToPresentationAttributeStyle(style, CSSPropertyFontSize, value);
244     else if (name == backgroundAttr)
245         addPropertyToPresentationAttributeStyle(style, CSSPropertyBackgroundColor, value);
246     else if (name == colorAttr)
247         addPropertyToPresentationAttributeStyle(style, CSSPropertyColor, value);
248     else if (name == fontstyleAttr)
249         addPropertyToPresentationAttributeStyle(style, CSSPropertyFontStyle, value);
250     else if (name == fontweightAttr)
251         addPropertyToPresentationAttributeStyle(style, CSSPropertyFontWeight, value);
252     else if (name == fontfamilyAttr)
253         addPropertyToPresentationAttributeStyle(style, CSSPropertyFontFamily, value);
254     else if (name == dirAttr) {
255         if (hasTagName(mathTag) || hasTagName(mrowTag) || hasTagName(mstyleTag) || isMathMLToken())
256             addPropertyToPresentationAttributeStyle(style, CSSPropertyDirection, value);
257     }  else {
258         ASSERT(!isPresentationAttribute(name));
259         StyledElement::collectStyleForPresentationAttribute(name, value
260         , style);
261     }
262 }
263
264 bool MathMLElement::childShouldCreateRenderer(const Node& child) const
265 {
266     if (hasTagName(annotation_xmlTag)) {
267         const AtomicString& value = fastGetAttribute(MathMLNames::encodingAttr);
268
269         // See annotation-xml.model.mathml, annotation-xml.model.svg and annotation-xml.model.xhtml in the HTML5 RelaxNG schema.
270
271         if (is<MathMLElement>(child) && (MathMLSelectElement::isMathMLEncoding(value) || MathMLSelectElement::isHTMLEncoding(value))) {
272             auto& mathmlElement = downcast<MathMLElement>(child);
273             return is<MathMLMathElement>(mathmlElement);
274         }
275
276         if (is<SVGElement>(child) && (MathMLSelectElement::isSVGEncoding(value) || MathMLSelectElement::isHTMLEncoding(value))) {
277             auto& svgElement = downcast<SVGElement>(child);
278             return is<SVGSVGElement>(svgElement);
279         }
280
281         if (is<HTMLElement>(child) && MathMLSelectElement::isHTMLEncoding(value)) {
282             auto& htmlElement = downcast<HTMLElement>(child);
283             return is<HTMLHtmlElement>(htmlElement) || (isFlowContent(htmlElement) && StyledElement::childShouldCreateRenderer(child));
284         }
285
286         return false;
287     }
288
289     // In general, only MathML children are allowed. Text nodes are only visible in token MathML elements.
290     return is<MathMLElement>(child);
291 }
292
293 void MathMLElement::attributeChanged(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason reason)
294 {
295     if (isSemanticAnnotation() && (name == MathMLNames::srcAttr || name == MathMLNames::encodingAttr)) {
296         Element* parent = parentElement();
297         if (parent && is<MathMLElement>(parent) && parent->hasTagName(semanticsTag))
298             downcast<MathMLElement>(*parent).updateSelectedChild();
299     }
300     StyledElement::attributeChanged(name, oldValue, newValue, reason);
301 }
302
303 }
304
305 #endif // ENABLE(MATHML)