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
44namespace WebCore {
45
46using namespace MathMLNames;
47
48MathMLElement::MathMLElement(const QualifiedName& tagName, Document& document)
49    : StyledElement(tagName, document, CreateMathMLElement)
50{
51}
52
53PassRefPtr<MathMLElement> MathMLElement::create(const QualifiedName& tagName, Document& document)
54{
55    return adoptRef(new MathMLElement(tagName, document));
56}
57
58bool 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
78bool 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 (node.isMathMLElement()) {
87        auto& mathmlElement = toMathMLElement(node);
88        return mathmlElement.hasTagName(MathMLNames::mathTag);
89    }
90
91    if (node.isSVGElement()) {
92        auto& svgElement = toSVGElement(node);
93        return svgElement.hasTagName(SVGNames::svgTag);
94    }
95
96    if (node.isHTMLElement()) {
97        // FIXME: add the <data> and <time> tags when they are implemented.
98        auto& htmlElement = toHTMLElement(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
153bool 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 (!node.isHTMLElement())
162        return false;
163
164    auto& htmlElement = toHTMLElement(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
196int 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
204int 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
212void 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
224bool 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
231void 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
264bool 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 (child.isMathMLElement() && (MathMLSelectElement::isMathMLEncoding(value) || MathMLSelectElement::isHTMLEncoding(value))) {
272            auto& mathmlElement = toMathMLElement(child);
273            return mathmlElement.hasTagName(MathMLNames::mathTag);
274        }
275
276        if (child.isSVGElement() && (MathMLSelectElement::isSVGEncoding(value) || MathMLSelectElement::isHTMLEncoding(value))) {
277            auto& svgElement = toSVGElement(child);
278            return svgElement.hasTagName(SVGNames::svgTag);
279        }
280
281        if (child.isHTMLElement() && MathMLSelectElement::isHTMLEncoding(value)) {
282            auto& htmlElement = toHTMLElement(child);
283            return htmlElement.hasTagName(HTMLNames::htmlTag) || (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 child.isMathMLElement();
291}
292
293void 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 && parent->isMathMLElement() && parent->hasTagName(semanticsTag))
298            toMathMLElement(parent)->updateSelectedChild();
299    }
300    StyledElement::attributeChanged(name, oldValue, newValue, reason);
301}
302
303}
304
305#endif // ENABLE(MATHML)
306