Use SPECIALIZE_TYPE_TRAITS_*() macro for MathMLElement
[WebKit-https.git] / Source / WebCore / mathml / MathMLSelectElement.cpp
1 /*
2  * Copyright (C) 2013 The MathJax Consortium. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
14  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
15  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
16  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
17  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "MathMLSelectElement.h"
28
29 #if ENABLE(MATHML)
30
31 #include "Event.h"
32 #include "HTMLElement.h"
33 #include "HTMLNames.h"
34 #include "MathMLNames.h"
35 #include "RenderMathMLRow.h"
36 #include "SVGElement.h"
37 #include "SVGNames.h"
38
39 namespace WebCore {
40
41 using namespace MathMLNames;
42
43 MathMLSelectElement::MathMLSelectElement(const QualifiedName& tagName, Document& document)
44     : MathMLInlineContainerElement(tagName, document)
45     , m_selectedChild(nullptr)
46 {
47 }
48
49 PassRefPtr<MathMLSelectElement> MathMLSelectElement::create(const QualifiedName& tagName, Document& document)
50 {
51     return adoptRef(new MathMLSelectElement(tagName, document));
52 }
53
54 RenderPtr<RenderElement> MathMLSelectElement::createElementRenderer(PassRef<RenderStyle> style)
55 {
56     return createRenderer<RenderMathMLRow>(*this, WTF::move(style));
57 }
58
59 //  We recognize the following values for the encoding attribute of the <semantics> element:
60 //
61 // - "MathML-Presentation", which is mentioned in the MathML 3 recommendation.
62 // - "SVG1.1" which is mentioned in the W3C note.
63 //   http://www.w3.org/Math/Documents/Notes/graphics.xml
64 // - Other MIME Content-Types for MathML, SVG and HTML.
65 //
66 // We exclude "application/mathml+xml" which is ambiguous about whether it is Presentation or Content MathML. Authors must use a more explicit encoding value.
67 bool MathMLSelectElement::isMathMLEncoding(const AtomicString& value)
68 {
69     return value == "application/mathml-presentation+xml" || value == "MathML-Presentation";
70 }
71
72 bool MathMLSelectElement::isSVGEncoding(const AtomicString& value)
73 {
74     return value == "image/svg+xml" || value == "SVG1.1";
75 }
76
77 bool MathMLSelectElement::isHTMLEncoding(const AtomicString& value)
78 {
79     return value == "application/xhtml+xml" || value == "text/html";
80 }
81
82 bool MathMLSelectElement::childShouldCreateRenderer(const Node& child) const
83 {
84     return MathMLElement::childShouldCreateRenderer(child) && m_selectedChild == &child;
85 }
86
87 void MathMLSelectElement::finishParsingChildren()
88 {
89     updateSelectedChild();
90     MathMLInlineContainerElement::finishParsingChildren();
91 }
92
93 void MathMLSelectElement::childrenChanged(const ChildChange& change)
94 {
95     updateSelectedChild();
96     MathMLInlineContainerElement::childrenChanged(change);
97 }
98
99 void MathMLSelectElement::attributeChanged(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason reason)
100 {
101     if (hasTagName(mactionTag) && (name == MathMLNames::actiontypeAttr || name == MathMLNames::selectionAttr))
102         updateSelectedChild();
103
104     MathMLInlineContainerElement::attributeChanged(name, oldValue, newValue, reason);
105 }
106
107 int MathMLSelectElement::getSelectedActionChildAndIndex(Element*& selectedChild)
108 {
109     ASSERT(hasTagName(mactionTag));
110
111     // We "round up or down to the closest allowable value" of the selection attribute, as suggested by the MathML specification.
112     selectedChild = firstElementChild();
113     if (!selectedChild)
114         return 1;
115
116     int selection = fastGetAttribute(MathMLNames::selectionAttr).toInt();
117     int i;
118     for (i = 1; i < selection; i++) {
119         Element* nextChild = selectedChild->nextElementSibling();
120         if (!nextChild)
121             break;
122         selectedChild = nextChild;
123     }
124
125     return i;
126 }
127
128 Element* MathMLSelectElement::getSelectedActionChild()
129 {
130     ASSERT(hasTagName(mactionTag));
131
132     Element* child = firstElementChild();
133     if (!child)
134         return child;
135
136     // The value of the actiontype attribute is case-sensitive.
137     const AtomicString& actiontype = fastGetAttribute(MathMLNames::actiontypeAttr);
138     if (actiontype == "statusline")
139         // FIXME: implement user interaction for the "statusline" action type (http://wkbug/124922).
140         { }
141     else if (actiontype == "tooltip")
142         // FIXME: implement user interaction for the "tooltip" action type (http://wkbug/124921).
143         { }
144     else {
145         // For the "toggle" action type or any unknown action type, we rely on the value of the selection attribute to determine the visible child.
146         getSelectedActionChildAndIndex(child);
147     }
148
149     return child;
150 }
151
152 Element* MathMLSelectElement::getSelectedSemanticsChild()
153 {
154     ASSERT(hasTagName(semanticsTag));
155
156     Element* child = firstElementChild();
157     if (!child)
158         return nullptr;
159
160     if (!is<MathMLElement>(child) || !downcast<MathMLElement>(*child).isPresentationMathML()) {
161         // The first child is not a presentation MathML element. Hence we move to the second child and start searching an annotation child that could be displayed.
162         child = child->nextElementSibling();
163     } else if (!downcast<MathMLElement>(*child).isSemanticAnnotation()) {
164         // The first child is a presentation MathML but not an annotation, so we can just display it.
165         return child;
166     }
167     // Otherwise, the first child is an <annotation> or <annotation-xml> element. This is invalid, but some people use this syntax so we take care of this case too and start the search from this first child.
168
169     for ( ; child; child = child->nextElementSibling()) {
170         if (!is<MathMLElement>(child))
171             continue;
172
173         if (child->hasTagName(MathMLNames::annotationTag)) {
174             // If the <annotation> element has an src attribute then it is a reference to arbitrary binary data and it is not clear whether we can display it. Hence we just ignore the annotation.
175             if (child->hasAttribute(MathMLNames::srcAttr))
176                 continue;
177             // Otherwise, we assume it is a text annotation that can always be displayed and we stop here.
178             return child;
179         }
180
181         if (child->hasTagName(MathMLNames::annotation_xmlTag)) {
182             // If the <annotation-xml> element has an src attribute then it is a reference to arbitrary binary data and it is not clear whether we can display it. Hence we just ignore the annotation.
183             if (child->hasAttribute(MathMLNames::srcAttr))
184                 continue;
185             // If the <annotation-xml> element has an encoding attribute describing presentation MathML, SVG or HTML we assume the content can be displayed and we stop here.
186             const AtomicString& value = child->fastGetAttribute(MathMLNames::encodingAttr);
187             if (isMathMLEncoding(value) || isSVGEncoding(value) || isHTMLEncoding(value))
188                 return child;
189         }
190     }
191
192     // We fallback to the first child.
193     return firstElementChild();
194 }
195
196 void MathMLSelectElement::updateSelectedChild()
197 {
198     Element* newSelectedChild = hasTagName(mactionTag) ? getSelectedActionChild() : getSelectedSemanticsChild();
199
200     if (m_selectedChild == newSelectedChild)
201         return;
202
203     if (m_selectedChild && m_selectedChild->renderer())
204         Style::detachRenderTree(*m_selectedChild);
205
206     m_selectedChild = newSelectedChild;
207     setNeedsStyleRecalc();
208 }
209
210 void MathMLSelectElement::defaultEventHandler(Event* event)
211 {
212     if (event->type() == eventNames().clickEvent) {
213         if (fastGetAttribute(MathMLNames::actiontypeAttr) == "toggle") {
214             toggle();
215             event->setDefaultHandled();
216             return;
217         }
218     }
219
220     MathMLInlineContainerElement::defaultEventHandler(event);
221 }
222
223 bool MathMLSelectElement::willRespondToMouseClickEvents()
224 {
225     return fastGetAttribute(MathMLNames::actiontypeAttr) == "toggle";
226 }
227
228 void MathMLSelectElement::toggle()
229 {
230     // Select the successor of the currently selected child
231     // or the first child if the currently selected child is the last.
232     Element* selectedChild;
233     int newSelectedChildIndex = getSelectedActionChildAndIndex(selectedChild) + 1;
234     if (!selectedChild || !selectedChild->nextElementSibling())
235         newSelectedChildIndex = 1;
236
237     // We update the attribute value of the selection attribute.
238     // This will also call MathMLSelectElement::attributeChanged to update the selected child.
239     setAttribute(MathMLNames::selectionAttr, AtomicString::number(newSelectedChildIndex));
240 }
241
242 }
243
244 #endif // ENABLE(MATHML)