bcc27840286048720c0419f876b3ca5469a86fba
[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  * Copyright (C) 2016 Igalia S.L.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "MathMLElement.h"
31
32 #if ENABLE(MATHML)
33
34 #include "EventHandler.h"
35 #include "HTMLAnchorElement.h"
36 #include "HTMLParserIdioms.h"
37 #include "MathMLNames.h"
38 #include "MouseEvent.h"
39 #include "RenderTableCell.h"
40
41 namespace WebCore {
42
43 using namespace MathMLNames;
44
45 MathMLElement::MathMLElement(const QualifiedName& tagName, Document& document)
46     : StyledElement(tagName, document, CreateMathMLElement)
47 {
48 }
49
50 Ref<MathMLElement> MathMLElement::create(const QualifiedName& tagName, Document& document)
51 {
52     return adoptRef(*new MathMLElement(tagName, document));
53 }
54
55 unsigned MathMLElement::colSpan() const
56 {
57     if (!hasTagName(mtdTag))
58         return 1u;
59     auto& colSpanValue = attributeWithoutSynchronization(columnspanAttr);
60     return std::max(1u, limitToOnlyHTMLNonNegative(colSpanValue, 1u));
61 }
62
63 unsigned MathMLElement::rowSpan() const
64 {
65     if (!hasTagName(mtdTag))
66         return 1u;
67     auto& rowSpanValue = attributeWithoutSynchronization(rowspanAttr);
68     static const unsigned maxRowspan = 8190; // This constant comes from HTMLTableCellElement.
69     return std::max(1u, std::min(limitToOnlyHTMLNonNegative(rowSpanValue, 1u), maxRowspan));
70 }
71
72 void MathMLElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
73 {
74     if (name == hrefAttr) {
75         bool wasLink = isLink();
76         setIsLink(!value.isNull() && !shouldProhibitLinks(this));
77         if (wasLink != isLink())
78             setNeedsStyleRecalc();
79     } else if (name == rowspanAttr) {
80         if (is<RenderTableCell>(renderer()) && hasTagName(mtdTag))
81             downcast<RenderTableCell>(*renderer()).colSpanOrRowSpanChanged();
82     } else if (name == columnspanAttr) {
83         if (is<RenderTableCell>(renderer()) && hasTagName(mtdTag))
84             downcast<RenderTableCell>(renderer())->colSpanOrRowSpanChanged();
85     } else
86         StyledElement::parseAttribute(name, value);
87 }
88
89 bool MathMLElement::isPresentationAttribute(const QualifiedName& name) const
90 {
91     if (name == backgroundAttr || name == colorAttr || name == dirAttr || name == fontfamilyAttr || name == fontsizeAttr || name == fontstyleAttr || name == fontweightAttr || name == mathbackgroundAttr || name == mathcolorAttr || name == mathsizeAttr)
92         return true;
93     return StyledElement::isPresentationAttribute(name);
94 }
95
96 void MathMLElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStyleProperties& style)
97 {
98     if (name == mathbackgroundAttr)
99         addPropertyToPresentationAttributeStyle(style, CSSPropertyBackgroundColor, value);
100     else if (name == mathsizeAttr) {
101         // The following three values of mathsize are handled in WebCore/css/mathml.css
102         if (value != "normal" && value != "small" && value != "big")
103             addPropertyToPresentationAttributeStyle(style, CSSPropertyFontSize, value);
104     } else if (name == mathcolorAttr)
105         addPropertyToPresentationAttributeStyle(style, CSSPropertyColor, value);
106     // FIXME: deprecated attributes that should loose in a conflict with a non deprecated attribute
107     else if (name == fontsizeAttr)
108         addPropertyToPresentationAttributeStyle(style, CSSPropertyFontSize, value);
109     else if (name == backgroundAttr)
110         addPropertyToPresentationAttributeStyle(style, CSSPropertyBackgroundColor, value);
111     else if (name == colorAttr)
112         addPropertyToPresentationAttributeStyle(style, CSSPropertyColor, value);
113     else if (name == fontstyleAttr)
114         addPropertyToPresentationAttributeStyle(style, CSSPropertyFontStyle, value);
115     else if (name == fontweightAttr)
116         addPropertyToPresentationAttributeStyle(style, CSSPropertyFontWeight, value);
117     else if (name == fontfamilyAttr)
118         addPropertyToPresentationAttributeStyle(style, CSSPropertyFontFamily, value);
119     else if (name == dirAttr) {
120         if (hasTagName(mathTag) || hasTagName(mrowTag) || hasTagName(mstyleTag) || isMathMLToken())
121             addPropertyToPresentationAttributeStyle(style, CSSPropertyDirection, value);
122     }  else {
123         ASSERT(!isPresentationAttribute(name));
124         StyledElement::collectStyleForPresentationAttribute(name, value
125         , style);
126     }
127 }
128
129 bool MathMLElement::childShouldCreateRenderer(const Node& child) const
130 {
131     // In general, only MathML children are allowed. Text nodes are only visible in token MathML elements.
132     return is<MathMLElement>(child);
133 }
134
135 bool MathMLElement::willRespondToMouseClickEvents()
136 {
137     return isLink() || StyledElement::willRespondToMouseClickEvents();
138 }
139
140 void MathMLElement::defaultEventHandler(Event* event)
141 {
142     if (isLink()) {
143         if (focused() && isEnterKeyKeydownEvent(event)) {
144             event->setDefaultHandled();
145             dispatchSimulatedClick(event);
146             return;
147         }
148         if (MouseEvent::canTriggerActivationBehavior(*event)) {
149             auto& href = attributeWithoutSynchronization(hrefAttr);
150             const auto& url = stripLeadingAndTrailingHTMLSpaces(href);
151             event->setDefaultHandled();
152             if (auto* frame = document().frame())
153                 frame->loader().urlSelected(document().completeURL(url), "_self", event, LockHistory::No, LockBackForwardList::No, MaybeSendReferrer, document().shouldOpenExternalURLsPolicyToPropagate());
154             return;
155         }
156     }
157
158     StyledElement::defaultEventHandler(event);
159 }
160
161 bool MathMLElement::canStartSelection() const
162 {
163     if (!isLink())
164         return StyledElement::canStartSelection();
165
166     return hasEditableStyle();
167 }
168
169 bool MathMLElement::isFocusable() const
170 {
171     if (renderer() && renderer()->absoluteClippedOverflowRect().isEmpty())
172         return false;
173
174     return StyledElement::isFocusable();
175 }
176
177 bool MathMLElement::isKeyboardFocusable(KeyboardEvent* event) const
178 {
179     if (isFocusable() && StyledElement::supportsFocus())
180         return StyledElement::isKeyboardFocusable(event);
181
182     if (isLink())
183         return document().frame()->eventHandler().tabsToLinks(event);
184
185     return StyledElement::isKeyboardFocusable(event);
186 }
187
188 bool MathMLElement::isMouseFocusable() const
189 {
190     // Links are focusable by default, but only allow links with tabindex or contenteditable to be mouse focusable.
191     // https://bugs.webkit.org/show_bug.cgi?id=26856
192     if (isLink())
193         return StyledElement::supportsFocus();
194
195     return StyledElement::isMouseFocusable();
196 }
197
198 bool MathMLElement::isURLAttribute(const Attribute& attribute) const
199 {
200     return attribute.name().localName() == hrefAttr || StyledElement::isURLAttribute(attribute);
201 }
202
203 bool MathMLElement::supportsFocus() const
204 {
205     if (hasEditableStyle())
206         return StyledElement::supportsFocus();
207     // If not a link we should still be able to focus the element if it has tabIndex.
208     return isLink() || StyledElement::supportsFocus();
209 }
210
211 int MathMLElement::tabIndex() const
212 {
213     // Skip the supportsFocus check in StyledElement.
214     return Element::tabIndex();
215 }
216
217 StringView MathMLElement::stripLeadingAndTrailingWhitespace(const StringView& stringView)
218 {
219     unsigned start = 0, stringLength = stringView.length();
220     while (stringLength > 0 && isHTMLSpace(stringView[start])) {
221         start++;
222         stringLength--;
223     }
224     while (stringLength > 0 && isHTMLSpace(stringView[start + stringLength - 1]))
225         stringLength--;
226     return stringView.substring(start, stringLength);
227 }
228
229 }
230
231 #endif // ENABLE(MATHML)