2 * Copyright (C) 2013 The MathJax Consortium. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "MathMLSelectElement.h"
32 #include "MathMLNames.h"
33 #include "RenderMathMLRow.h"
37 using namespace MathMLNames;
39 MathMLSelectElement::MathMLSelectElement(const QualifiedName& tagName, Document& document)
40 : MathMLInlineContainerElement(tagName, document)
41 , m_selectedChild(nullptr)
45 PassRefPtr<MathMLSelectElement> MathMLSelectElement::create(const QualifiedName& tagName, Document& document)
47 return adoptRef(new MathMLSelectElement(tagName, document));
50 RenderPtr<RenderElement> MathMLSelectElement::createElementRenderer(PassRef<RenderStyle> style)
52 return createRenderer<RenderMathMLRow>(*this, std::move(style));
55 bool MathMLSelectElement::childShouldCreateRenderer(const Node& child) const
57 return MathMLElement::childShouldCreateRenderer(child) && m_selectedChild == &child;
60 void MathMLSelectElement::finishParsingChildren()
62 updateSelectedChild();
63 MathMLInlineContainerElement::finishParsingChildren();
66 void MathMLSelectElement::childrenChanged(const ChildChange& change)
68 updateSelectedChild();
69 MathMLInlineContainerElement::childrenChanged(change);
72 void MathMLSelectElement::attributeChanged(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason reason)
74 if (hasTagName(mactionTag) && (name == MathMLNames::actiontypeAttr || name == MathMLNames::selectionAttr))
75 updateSelectedChild();
77 MathMLInlineContainerElement::attributeChanged(name, oldValue, newValue, reason);
80 int MathMLSelectElement::getSelectedActionChildAndIndex(Element*& selectedChild)
82 ASSERT(hasTagName(mactionTag));
84 // We "round up or down to the closest allowable value" of the selection attribute, as suggested by the MathML specification.
85 selectedChild = firstElementChild();
89 int selection = fastGetAttribute(MathMLNames::selectionAttr).toInt();
91 for (i = 1; i < selection; i++) {
92 Element* nextChild = selectedChild->nextElementSibling();
95 selectedChild = nextChild;
101 Element* MathMLSelectElement::getSelectedActionChild()
103 ASSERT(hasTagName(mactionTag));
105 Element* child = firstElementChild();
109 // The value of the actiontype attribute is case-sensitive.
110 const AtomicString& actiontype = fastGetAttribute(MathMLNames::actiontypeAttr);
111 if (actiontype == "statusline")
112 // FIXME: implement user interaction for the "statusline" action type (http://wkbug/124922).
114 else if (actiontype == "tooltip")
115 // FIXME: implement user interaction for the "tooltip" action type (http://wkbug/124921).
118 // For the "toggle" action type or any unknown action type, we rely on the value of the selection attribute to determine the visible child.
119 getSelectedActionChildAndIndex(child);
125 Element* MathMLSelectElement::getSelectedSemanticsChild()
127 ASSERT(hasTagName(semanticsTag));
129 Element* child = firstElementChild();
133 if (!child->isMathMLElement() || !toMathMLElement(child)->isPresentationMathML()) {
134 // 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.
135 child = child->nextElementSibling();
136 } else if (!toMathMLElement(child)->isSemanticAnnotation()) {
137 // The first child is a presentation MathML but not an annotation, so we can just display it.
140 // 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.
142 for ( ; child; child = child->nextElementSibling()) {
143 if (!child->isMathMLElement())
146 if (child->hasTagName(MathMLNames::annotationTag)) {
147 // 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.
148 if (child->hasAttribute(MathMLNames::srcAttr))
150 // Otherwise, we assume it is a text annotation that can always be displayed and we stop here.
154 if (child->hasTagName(MathMLNames::annotation_xmlTag)) {
155 // 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.
156 if (child->hasAttribute(MathMLNames::srcAttr))
158 // 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. We recognize the following encoding values:
160 // - "MathML-Presentation", which is mentioned in the MathML 3 recommendation.
161 // - "SVG1.1" which is mentioned in the W3C note.
162 // http://www.w3.org/Math/Documents/Notes/graphics.xml
163 // - Other MIME Content-Types for SVG and HTML.
165 // We exclude "application/mathml+xml" which is ambiguous about whether it is Presentation or Content MathML. Authors must use a more explicit encoding value.
166 const AtomicString& value = child->fastGetAttribute(MathMLNames::encodingAttr);
167 if (value == "application/mathml-presentation+xml" || value == "MathML-Presentation" || value == "image/svg+xml" || value == "SVG1.1" || value == "application/xhtml+xml" || value == "text/html")
172 // We fallback to the first child.
173 return firstElementChild();
176 void MathMLSelectElement::updateSelectedChild()
178 Element* newSelectedChild = hasTagName(mactionTag) ? getSelectedActionChild() : getSelectedSemanticsChild();
180 if (m_selectedChild == newSelectedChild)
183 if (m_selectedChild && m_selectedChild->renderer())
184 Style::detachRenderTree(*m_selectedChild);
186 m_selectedChild = newSelectedChild;
187 setNeedsStyleRecalc();
190 void MathMLSelectElement::defaultEventHandler(Event* event)
192 if (event->type() == eventNames().clickEvent) {
193 if (fastGetAttribute(MathMLNames::actiontypeAttr) == "toggle") {
195 event->setDefaultHandled();
200 MathMLInlineContainerElement::defaultEventHandler(event);
203 bool MathMLSelectElement::willRespondToMouseClickEvents()
205 return fastGetAttribute(MathMLNames::actiontypeAttr) == "toggle";
208 void MathMLSelectElement::toggle()
210 // Select the successor of the currently selected child
211 // or the first child if the currently selected child is the last.
212 Element* selectedChild;
213 int newSelectedChildIndex = getSelectedActionChildAndIndex(selectedChild) + 1;
214 if (!selectedChild || !selectedChild->nextElementSibling())
215 newSelectedChildIndex = 1;
217 // We update the attribute value of the selection attribute.
218 // This will also call MathMLSelectElement::attributeChanged to update the selected child.
219 setAttribute(MathMLNames::selectionAttr, AtomicString::number(newSelectedChildIndex));
224 #endif // ENABLE(MATHML)