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.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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.
32 #include "MathMLElement.h"
34 #include "ElementIterator.h"
36 #include "EventHandler.h"
37 #include "HTMLAnchorElement.h"
38 #include "HTMLElement.h"
39 #include "HTMLHtmlElement.h"
40 #include "HTMLMapElement.h"
41 #include "HTMLNames.h"
42 #include "HTMLParserIdioms.h"
43 #include "MathMLMathElement.h"
44 #include "MathMLNames.h"
45 #include "MathMLSelectElement.h"
46 #include "MouseEvent.h"
47 #include "RenderTableCell.h"
48 #include "SVGElement.h"
50 #include "SVGSVGElement.h"
51 #include "XLinkNames.h"
55 using namespace MathMLNames;
57 MathMLElement::MathMLElement(const QualifiedName& tagName, Document& document)
58 : StyledElement(tagName, document, CreateMathMLElement)
62 Ref<MathMLElement> MathMLElement::create(const QualifiedName& tagName, Document& document)
64 return adoptRef(*new MathMLElement(tagName, document));
67 bool MathMLElement::isPresentationMathML() const
69 return hasTagName(MathMLNames::mtrTag)
70 || hasTagName(MathMLNames::mtdTag)
71 || hasTagName(MathMLNames::maligngroupTag)
72 || hasTagName(MathMLNames::malignmarkTag)
73 || hasTagName(MathMLNames::mencloseTag)
74 || hasTagName(MathMLNames::mglyphTag)
75 || hasTagName(MathMLNames::mlabeledtrTag)
76 || hasTagName(MathMLNames::mlongdivTag)
77 || hasTagName(MathMLNames::mpaddedTag)
78 || hasTagName(MathMLNames::msTag)
79 || hasTagName(MathMLNames::mscarriesTag)
80 || hasTagName(MathMLNames::mscarryTag)
81 || hasTagName(MathMLNames::msgroupTag)
82 || hasTagName(MathMLNames::mslineTag)
83 || hasTagName(MathMLNames::msrowTag)
84 || hasTagName(MathMLNames::mstackTag);
87 bool MathMLElement::isPhrasingContent(const Node& node) const
89 // Phrasing content is described in the HTML 5 specification:
90 // http://www.w3.org/TR/html5/dom.html#phrasing-content.
92 if (!node.isElementNode())
93 return node.isTextNode();
95 if (is<MathMLElement>(node)) {
96 auto& mathmlElement = downcast<MathMLElement>(node);
97 return is<MathMLMathElement>(mathmlElement);
100 if (is<SVGElement>(node)) {
101 auto& svgElement = downcast<SVGElement>(node);
102 return is<SVGSVGElement>(svgElement);
105 if (is<HTMLElement>(node)) {
106 // FIXME: add the <data> and <time> tags when they are implemented.
107 auto& htmlElement = downcast<HTMLElement>(node);
108 return htmlElement.hasTagName(HTMLNames::aTag)
109 || htmlElement.hasTagName(HTMLNames::abbrTag)
110 || (htmlElement.hasTagName(HTMLNames::areaTag) && ancestorsOfType<HTMLMapElement>(htmlElement).first())
111 || htmlElement.hasTagName(HTMLNames::audioTag)
112 || htmlElement.hasTagName(HTMLNames::bTag)
113 || htmlElement.hasTagName(HTMLNames::bdiTag)
114 || htmlElement.hasTagName(HTMLNames::bdoTag)
115 || htmlElement.hasTagName(HTMLNames::brTag)
116 || htmlElement.hasTagName(HTMLNames::buttonTag)
117 || htmlElement.hasTagName(HTMLNames::canvasTag)
118 || htmlElement.hasTagName(HTMLNames::citeTag)
119 || htmlElement.hasTagName(HTMLNames::codeTag)
120 || htmlElement.hasTagName(HTMLNames::datalistTag)
121 || htmlElement.hasTagName(HTMLNames::delTag)
122 || htmlElement.hasTagName(HTMLNames::dfnTag)
123 || htmlElement.hasTagName(HTMLNames::emTag)
124 || htmlElement.hasTagName(HTMLNames::embedTag)
125 || htmlElement.hasTagName(HTMLNames::iTag)
126 || htmlElement.hasTagName(HTMLNames::iframeTag)
127 || htmlElement.hasTagName(HTMLNames::imgTag)
128 || htmlElement.hasTagName(HTMLNames::inputTag)
129 || htmlElement.hasTagName(HTMLNames::insTag)
130 || htmlElement.hasTagName(HTMLNames::kbdTag)
131 || htmlElement.hasTagName(HTMLNames::keygenTag)
132 || htmlElement.hasTagName(HTMLNames::labelTag)
133 || htmlElement.hasTagName(HTMLNames::mapTag)
134 || htmlElement.hasTagName(HTMLNames::markTag)
135 || htmlElement.hasTagName(HTMLNames::meterTag)
136 || htmlElement.hasTagName(HTMLNames::noscriptTag)
137 || htmlElement.hasTagName(HTMLNames::objectTag)
138 || htmlElement.hasTagName(HTMLNames::outputTag)
139 || htmlElement.hasTagName(HTMLNames::progressTag)
140 || htmlElement.hasTagName(HTMLNames::qTag)
141 || htmlElement.hasTagName(HTMLNames::rubyTag)
142 || htmlElement.hasTagName(HTMLNames::sTag)
143 || htmlElement.hasTagName(HTMLNames::sampTag)
144 || htmlElement.hasTagName(HTMLNames::scriptTag)
145 || htmlElement.hasTagName(HTMLNames::selectTag)
146 || htmlElement.hasTagName(HTMLNames::smallTag)
147 || htmlElement.hasTagName(HTMLNames::spanTag)
148 || htmlElement.hasTagName(HTMLNames::strongTag)
149 || htmlElement.hasTagName(HTMLNames::subTag)
150 || htmlElement.hasTagName(HTMLNames::supTag)
151 || htmlElement.hasTagName(HTMLNames::templateTag)
152 || htmlElement.hasTagName(HTMLNames::textareaTag)
153 || htmlElement.hasTagName(HTMLNames::uTag)
154 || htmlElement.hasTagName(HTMLNames::varTag)
155 || htmlElement.hasTagName(HTMLNames::videoTag)
156 || htmlElement.hasTagName(HTMLNames::wbrTag);
162 bool MathMLElement::isFlowContent(const Node& node) const
164 // Flow content is described in the HTML 5 specification:
165 // http://www.w3.org/TR/html5/dom.html#flow-content
167 if (isPhrasingContent(node))
170 if (!is<HTMLElement>(node))
173 auto& htmlElement = downcast<HTMLElement>(node);
174 // FIXME add the <dialog> tag when it is implemented.
175 return htmlElement.hasTagName(HTMLNames::addressTag)
176 || htmlElement.hasTagName(HTMLNames::articleTag)
177 || htmlElement.hasTagName(HTMLNames::asideTag)
178 || htmlElement.hasTagName(HTMLNames::blockquoteTag)
179 || htmlElement.hasTagName(HTMLNames::detailsTag)
180 || htmlElement.hasTagName(HTMLNames::divTag)
181 || htmlElement.hasTagName(HTMLNames::dlTag)
182 || htmlElement.hasTagName(HTMLNames::fieldsetTag)
183 || htmlElement.hasTagName(HTMLNames::figureTag)
184 || htmlElement.hasTagName(HTMLNames::footerTag)
185 || htmlElement.hasTagName(HTMLNames::formTag)
186 || htmlElement.hasTagName(HTMLNames::h1Tag)
187 || htmlElement.hasTagName(HTMLNames::h2Tag)
188 || htmlElement.hasTagName(HTMLNames::h3Tag)
189 || htmlElement.hasTagName(HTMLNames::h4Tag)
190 || htmlElement.hasTagName(HTMLNames::h5Tag)
191 || htmlElement.hasTagName(HTMLNames::h6Tag)
192 || htmlElement.hasTagName(HTMLNames::headerTag)
193 || htmlElement.hasTagName(HTMLNames::hrTag)
194 || htmlElement.hasTagName(HTMLNames::mainTag)
195 || htmlElement.hasTagName(HTMLNames::navTag)
196 || htmlElement.hasTagName(HTMLNames::olTag)
197 || htmlElement.hasTagName(HTMLNames::pTag)
198 || htmlElement.hasTagName(HTMLNames::preTag)
199 || htmlElement.hasTagName(HTMLNames::sectionTag)
200 || (htmlElement.hasTagName(HTMLNames::styleTag) && htmlElement.hasAttribute("scoped"))
201 || htmlElement.hasTagName(HTMLNames::tableTag)
202 || htmlElement.hasTagName(HTMLNames::ulTag);
205 unsigned MathMLElement::colSpan() const
207 if (!hasTagName(mtdTag))
209 const AtomicString& colSpanValue = fastGetAttribute(columnspanAttr);
210 return std::max(1u, limitToOnlyHTMLNonNegative(colSpanValue, 1u));
213 unsigned MathMLElement::rowSpan() const
215 if (!hasTagName(mtdTag))
217 const AtomicString& rowSpanValue = fastGetAttribute(rowspanAttr);
218 static const unsigned maxRowspan = 8190; // This constant comes from HTMLTableCellElement.
219 return std::max(1u, std::min(limitToOnlyHTMLNonNegative(rowSpanValue, 1u), maxRowspan));
222 void MathMLElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
224 if (name == hrefAttr) {
225 bool wasLink = isLink();
226 setIsLink(!value.isNull() && !shouldProhibitLinks(this));
227 if (wasLink != isLink())
228 setNeedsStyleRecalc();
229 } else if (name == rowspanAttr) {
230 if (is<RenderTableCell>(renderer()) && hasTagName(mtdTag))
231 downcast<RenderTableCell>(*renderer()).colSpanOrRowSpanChanged();
232 } else if (name == columnspanAttr) {
233 if (is<RenderTableCell>(renderer()) && hasTagName(mtdTag))
234 downcast<RenderTableCell>(renderer())->colSpanOrRowSpanChanged();
236 StyledElement::parseAttribute(name, value);
239 bool MathMLElement::isPresentationAttribute(const QualifiedName& name) const
241 if (name == backgroundAttr || name == colorAttr || name == dirAttr || name == fontfamilyAttr || name == fontsizeAttr || name == fontstyleAttr || name == fontweightAttr || name == mathbackgroundAttr || name == mathcolorAttr || name == mathsizeAttr)
243 return StyledElement::isPresentationAttribute(name);
246 void MathMLElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStyleProperties& style)
248 if (name == mathbackgroundAttr)
249 addPropertyToPresentationAttributeStyle(style, CSSPropertyBackgroundColor, value);
250 else if (name == mathsizeAttr) {
251 // The following three values of mathsize are handled in WebCore/css/mathml.css
252 if (value != "normal" && value != "small" && value != "big")
253 addPropertyToPresentationAttributeStyle(style, CSSPropertyFontSize, value);
254 } else if (name == mathcolorAttr)
255 addPropertyToPresentationAttributeStyle(style, CSSPropertyColor, value);
256 // FIXME: deprecated attributes that should loose in a conflict with a non deprecated attribute
257 else if (name == fontsizeAttr)
258 addPropertyToPresentationAttributeStyle(style, CSSPropertyFontSize, value);
259 else if (name == backgroundAttr)
260 addPropertyToPresentationAttributeStyle(style, CSSPropertyBackgroundColor, value);
261 else if (name == colorAttr)
262 addPropertyToPresentationAttributeStyle(style, CSSPropertyColor, value);
263 else if (name == fontstyleAttr)
264 addPropertyToPresentationAttributeStyle(style, CSSPropertyFontStyle, value);
265 else if (name == fontweightAttr)
266 addPropertyToPresentationAttributeStyle(style, CSSPropertyFontWeight, value);
267 else if (name == fontfamilyAttr)
268 addPropertyToPresentationAttributeStyle(style, CSSPropertyFontFamily, value);
269 else if (name == dirAttr) {
270 if (hasTagName(mathTag) || hasTagName(mrowTag) || hasTagName(mstyleTag) || isMathMLToken())
271 addPropertyToPresentationAttributeStyle(style, CSSPropertyDirection, value);
273 ASSERT(!isPresentationAttribute(name));
274 StyledElement::collectStyleForPresentationAttribute(name, value
279 bool MathMLElement::childShouldCreateRenderer(const Node& child) const
281 if (hasTagName(annotation_xmlTag)) {
282 const AtomicString& value = fastGetAttribute(MathMLNames::encodingAttr);
284 // See annotation-xml.model.mathml, annotation-xml.model.svg and annotation-xml.model.xhtml in the HTML5 RelaxNG schema.
286 if (is<MathMLElement>(child) && (MathMLSelectElement::isMathMLEncoding(value) || MathMLSelectElement::isHTMLEncoding(value))) {
287 auto& mathmlElement = downcast<MathMLElement>(child);
288 return is<MathMLMathElement>(mathmlElement);
291 if (is<SVGElement>(child) && (MathMLSelectElement::isSVGEncoding(value) || MathMLSelectElement::isHTMLEncoding(value))) {
292 auto& svgElement = downcast<SVGElement>(child);
293 return is<SVGSVGElement>(svgElement);
296 if (is<HTMLElement>(child) && MathMLSelectElement::isHTMLEncoding(value)) {
297 auto& htmlElement = downcast<HTMLElement>(child);
298 return is<HTMLHtmlElement>(htmlElement) || (isFlowContent(htmlElement) && StyledElement::childShouldCreateRenderer(child));
304 // In general, only MathML children are allowed. Text nodes are only visible in token MathML elements.
305 return is<MathMLElement>(child);
308 void MathMLElement::attributeChanged(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason reason)
310 if (isSemanticAnnotation() && (name == MathMLNames::srcAttr || name == MathMLNames::encodingAttr)) {
311 auto* parent = parentElement();
312 if (is<MathMLElement>(parent) && parent->hasTagName(semanticsTag))
313 downcast<MathMLElement>(*parent).updateSelectedChild();
315 StyledElement::attributeChanged(name, oldValue, newValue, reason);
318 bool MathMLElement::willRespondToMouseClickEvents()
320 return isLink() || StyledElement::willRespondToMouseClickEvents();
323 void MathMLElement::defaultEventHandler(Event* event)
326 if (focused() && isEnterKeyKeydownEvent(event)) {
327 event->setDefaultHandled();
328 dispatchSimulatedClick(event);
331 if (MouseEvent::canTriggerActivationBehavior(*event)) {
332 const AtomicString& href = fastGetAttribute(hrefAttr);
333 String url = stripLeadingAndTrailingHTMLSpaces(href);
334 event->setDefaultHandled();
335 if (Frame* frame = document().frame())
336 frame->loader().urlSelected(document().completeURL(url), "_self", event, LockHistory::No, LockBackForwardList::No, MaybeSendReferrer, document().shouldOpenExternalURLsPolicyToPropagate());
341 StyledElement::defaultEventHandler(event);
344 bool MathMLElement::canStartSelection() const
347 return StyledElement::canStartSelection();
349 return hasEditableStyle();
352 bool MathMLElement::isFocusable() const
354 if (renderer() && renderer()->absoluteClippedOverflowRect().isEmpty())
357 return StyledElement::isFocusable();
360 bool MathMLElement::isKeyboardFocusable(KeyboardEvent* event) const
362 if (isFocusable() && StyledElement::supportsFocus())
363 return StyledElement::isKeyboardFocusable(event);
366 return document().frame()->eventHandler().tabsToLinks(event);
368 return StyledElement::isKeyboardFocusable(event);
371 bool MathMLElement::isMouseFocusable() const
373 // Links are focusable by default, but only allow links with tabindex or contenteditable to be mouse focusable.
374 // https://bugs.webkit.org/show_bug.cgi?id=26856
376 return StyledElement::supportsFocus();
378 return StyledElement::isMouseFocusable();
381 bool MathMLElement::isURLAttribute(const Attribute& attribute) const
383 return attribute.name().localName() == hrefAttr || StyledElement::isURLAttribute(attribute);
386 bool MathMLElement::supportsFocus() const
388 if (hasEditableStyle())
389 return StyledElement::supportsFocus();
390 // If not a link we should still be able to focus the element if it has tabIndex.
391 return isLink() || StyledElement::supportsFocus();
394 int MathMLElement::tabIndex() const
396 // Skip the supportsFocus check in StyledElement.
397 return Element::tabIndex();
402 #endif // ENABLE(MATHML)