Add WTF::move()
[WebKit-https.git] / Source / WebCore / rendering / mathml / RenderMathMLRoot.cpp
1 /*
2  * Copyright (C) 2009 Alex Milowski (alex@milowski.com). All rights reserved.
3  * Copyright (C) 2010 Fran├žois Sausset (sausset@gmail.com). All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
18  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28
29 #if ENABLE(MATHML)
30
31 #include "RenderMathMLRoot.h"
32
33 #include "FontCache.h"
34 #include "GraphicsContext.h"
35 #include "PaintInfo.h"
36 #include "RenderIterator.h"
37 #include "RenderMathMLRadicalOperator.h"
38
39 namespace WebCore {
40     
41 // RenderMathMLRoot implements drawing of radicals via the <mroot> and <msqrt> elements. For valid MathML elements, the DOM is
42 //
43 // <mroot> Base Index </mroot>
44 // <msqrt> Child1 Child2 ... ChildN </msqrt>
45 //
46 // and the structure of the render tree will be
47 //
48 // IndexWrapper RadicalWrapper BaseWrapper
49 //
50 // where RadicalWrapper contains an <mo>&#x221A;</mo>.
51 // For <mroot>, the IndexWrapper and BaseWrapper should contain exactly one child (Index and Base respectively).
52 // For <msqrt>, the IndexWrapper should be empty and the BaseWrapper can contain any number of children (Child1, ... ChildN).
53 //
54 // In order to accept invalid markup and to handle <mroot> and <msqrt> consistently, we will allow any number of children in the BaseWrapper of <mroot> too.
55 // We will allow the IndexWrapper to be empty and it will always contain the last child of the <mroot> if there are at least 2 elements.
56
57 RenderMathMLRoot::RenderMathMLRoot(Element& element, PassRef<RenderStyle> style)
58     : RenderMathMLBlock(element, WTF::move(style))
59 {
60 }
61
62 RenderMathMLRoot::RenderMathMLRoot(Document& document, PassRef<RenderStyle> style)
63     : RenderMathMLBlock(document, WTF::move(style))
64 {
65 }
66
67 RenderMathMLRootWrapper* RenderMathMLRoot::baseWrapper() const
68 {
69     ASSERT(!isEmpty());
70     return toRenderMathMLRootWrapper(lastChild());
71 }
72
73 RenderMathMLBlock* RenderMathMLRoot::radicalWrapper() const
74 {
75     ASSERT(!isEmpty());
76     return toRenderMathMLBlock(lastChild()->previousSibling());
77 }
78
79 RenderMathMLRootWrapper* RenderMathMLRoot::indexWrapper() const
80 {
81     ASSERT(!isEmpty());
82     return isRenderMathMLSquareRoot() ? nullptr : toRenderMathMLRootWrapper(firstChild());
83 }
84
85 RenderMathMLRadicalOperator* RenderMathMLRoot::radicalOperator() const
86 {
87     ASSERT(!isEmpty());
88     return toRenderMathMLRadicalOperator(radicalWrapper()->firstChild());
89 }
90
91 void RenderMathMLRoot::restructureWrappers()
92 {
93     ASSERT(!isEmpty());
94
95     auto base = baseWrapper();
96     auto index = indexWrapper();
97     auto radical = radicalWrapper();
98
99     // For visual consistency with the initial state, we remove the radical when the base/index wrappers become empty.
100     if (base->isEmpty() && (!index || index->isEmpty())) {
101         if (!radical->isEmpty()) {
102             auto child = radicalOperator();
103             radical->removeChild(*child);
104             child->destroy();
105         }
106         // FIXME: early return!!!
107     }
108
109     if (radical->isEmpty()) {
110         // We create the radical operator.
111         RenderPtr<RenderMathMLRadicalOperator> radicalOperator = createRenderer<RenderMathMLRadicalOperator>(document(), RenderStyle::createAnonymousStyleWithDisplay(&style(), FLEX));
112         radicalOperator->initializeStyle();
113         radical->addChild(radicalOperator.leakPtr());
114     }
115
116     if (isRenderMathMLSquareRoot())
117         return;
118
119     if (auto childToMove = base->lastChild()) {
120         // We move the last child of the base wrapper into the index wrapper if the index wrapper is empty and the base wrapper has at least two children.
121         if (childToMove->previousSibling() && index->isEmpty()) {
122             base->removeChildWithoutRestructuring(*childToMove);
123             index->addChild(childToMove);
124         }
125     }
126
127     if (auto childToMove = index->firstChild()) {
128         // We move the first child of the index wrapper into the base wrapper if:
129         // - either the index wrapper has at least two children.
130         // - or the base wrapper is empty but the index wrapper is not.
131         if (childToMove->nextSibling() || base->isEmpty()) {
132             index->removeChildWithoutRestructuring(*childToMove);
133             base->addChild(childToMove);
134         }
135     }
136 }
137
138 void RenderMathMLRoot::addChild(RenderObject* newChild, RenderObject* beforeChild)
139 {
140     if (isEmpty()) {
141         if (!isRenderMathMLSquareRoot()) {
142             // We add the IndexWrapper.
143             RenderMathMLBlock::addChild(RenderMathMLRootWrapper::createAnonymousWrapper(this).leakPtr());
144         }
145
146         // We create the radicalWrapper
147         RenderMathMLBlock::addChild(RenderMathMLBlock::createAnonymousMathMLBlock().leakPtr());
148
149         // We create the BaseWrapper.
150         RenderMathMLBlock::addChild(RenderMathMLRootWrapper::createAnonymousWrapper(this).leakPtr());
151
152         updateStyle();
153     }
154
155     // We insert the child.
156     auto base = baseWrapper();
157     auto index = indexWrapper();
158     RenderElement* actualParent;
159     RenderElement* actualBeforeChild;
160     if (isRenderMathMLSquareRoot()) {
161         // For square root, we always insert the child into the base wrapper.
162         actualParent = base;
163         if (beforeChild && beforeChild->parent() == base)
164             actualBeforeChild = toRenderElement(beforeChild);
165         else
166             actualBeforeChild = nullptr;
167     } else {
168         // For mroot, we insert the child into the parent of beforeChild, or at the end of the index. The wrapper structure is reorganize below.
169         actualParent = beforeChild ? beforeChild->parent() : nullptr;
170         if (actualParent == base || actualParent == index)
171             actualBeforeChild = toRenderElement(beforeChild);
172         else {
173             actualParent = index;
174             actualBeforeChild = nullptr;
175         }
176     }
177     actualParent->addChild(newChild, actualBeforeChild);
178     restructureWrappers();
179 }
180
181 void RenderMathMLRoot::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
182 {
183     RenderMathMLBlock::styleDidChange(diff, oldStyle);
184     if (!isEmpty())
185         updateStyle();
186 }
187
188 void RenderMathMLRoot::updateFromElement()
189 {
190     RenderMathMLBlock::updateFromElement();
191     if (!isEmpty())
192         updateStyle();
193 }
194
195 void RenderMathMLRoot::updateStyle()
196 {
197     ASSERT(!isEmpty());
198
199     // We set some constants to draw the radical, as defined in the OpenType MATH tables.
200
201     m_ruleThickness = 0.05f * style().font().size();
202
203     // FIXME: The recommended default for m_verticalGap in displaystyle is rule thickness + 1/4 x-height (https://bugs.webkit.org/show_bug.cgi?id=118737).
204     m_verticalGap = 11 * m_ruleThickness / 4;
205     m_extraAscender = m_ruleThickness;
206     LayoutUnit kernBeforeDegree = 5 * style().font().size() / 18;
207     LayoutUnit kernAfterDegree = -10 * style().font().size() / 18;
208     m_degreeBottomRaisePercent = 0.6f;
209
210     const auto& primaryFontData = style().font().primaryFont();
211     if (primaryFontData && primaryFontData->mathData()) {
212         // FIXME: m_verticalGap should use RadicalDisplayStyleVertical in display mode (https://bugs.webkit.org/show_bug.cgi?id=118737).
213         m_verticalGap = primaryFontData->mathData()->getMathConstant(primaryFontData, OpenTypeMathData::RadicalVerticalGap);
214         m_ruleThickness = primaryFontData->mathData()->getMathConstant(primaryFontData, OpenTypeMathData::RadicalRuleThickness);
215         m_extraAscender = primaryFontData->mathData()->getMathConstant(primaryFontData, OpenTypeMathData::RadicalExtraAscender);
216
217         if (!isRenderMathMLSquareRoot()) {
218             kernBeforeDegree = primaryFontData->mathData()->getMathConstant(primaryFontData, OpenTypeMathData::RadicalKernBeforeDegree);
219             kernAfterDegree = primaryFontData->mathData()->getMathConstant(primaryFontData, OpenTypeMathData::RadicalKernAfterDegree);
220             m_degreeBottomRaisePercent = primaryFontData->mathData()->getMathConstant(primaryFontData, OpenTypeMathData::RadicalDegreeBottomRaisePercent);
221         }
222     }
223
224     // We set the style of the anonymous wrappers.
225
226     auto radical = radicalWrapper();
227     auto radicalStyle = RenderStyle::createAnonymousStyleWithDisplay(&style(), FLEX);
228     radicalStyle.get().setMarginTop(Length(0, Fixed)); // This will be updated in RenderMathMLRoot::layout().
229     radical->setStyle(WTF::move(radicalStyle));
230     radical->setNeedsLayoutAndPrefWidthsRecalc();
231
232     auto base = baseWrapper();
233     auto baseStyle = RenderStyle::createAnonymousStyleWithDisplay(&style(), FLEX);
234     baseStyle.get().setMarginTop(Length(0, Fixed)); // This will be updated in RenderMathMLRoot::layout().
235     baseStyle.get().setAlignItems(AlignBaseline);
236     base->setStyle(WTF::move(baseStyle));
237     base->setNeedsLayoutAndPrefWidthsRecalc();
238
239     if (!isRenderMathMLSquareRoot()) {
240         // For mroot, we also set the style of the index wrapper.
241         auto index = indexWrapper();
242         auto indexStyle = RenderStyle::createAnonymousStyleWithDisplay(&style(), FLEX);
243         indexStyle.get().setMarginTop(Length(0, Fixed)); // This will be updated in RenderMathMLRoot::layout().
244         indexStyle.get().setMarginStart(Length(kernBeforeDegree, Fixed));
245         indexStyle.get().setMarginEnd(Length(kernAfterDegree, Fixed));
246         indexStyle.get().setAlignItems(AlignBaseline);
247         index->setStyle(WTF::move(indexStyle));
248         index->setNeedsLayoutAndPrefWidthsRecalc();
249     }
250 }
251
252 int RenderMathMLRoot::firstLineBaseline() const
253 {
254     if (!isEmpty()) {
255         auto base = baseWrapper();
256         return static_cast<int>(lroundf(base->firstLineBaseline() + base->marginTop()));
257     }
258
259     return RenderMathMLBlock::firstLineBaseline();
260 }
261
262 void RenderMathMLRoot::layout()
263 {
264     if (isEmpty()) {
265         RenderMathMLBlock::layout();
266         return;
267     }
268
269     // FIXME: It seems that changing the top margin of the base below modifies its logical height and leads to reftest failures.
270     // For now, we workaround that by avoiding to recompute the child margins if they were not reset in updateStyle().
271     auto base = baseWrapper();
272     if (base->marginTop() > 0) {
273         RenderMathMLBlock::layout();
274         return;
275     }
276
277     // We layout the children.
278     for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
279         if (child->needsLayout())
280             toRenderElement(child)->layout();
281     }
282
283     auto radical = radicalOperator();
284     if (radical) {
285         // We stretch the radical sign to cover the height of the base wrapper.
286         float baseHeight = base->logicalHeight();
287         float baseHeightAboveBaseline = base->firstLineBaseline();
288         if (baseHeightAboveBaseline == -1)
289             baseHeightAboveBaseline = baseHeight;
290         float baseDepthBelowBaseline = baseHeight - baseHeightAboveBaseline;
291         baseHeightAboveBaseline += m_verticalGap;
292         radical->stretchTo(baseHeightAboveBaseline, baseDepthBelowBaseline);
293
294         // We modify the top margins to adjust the vertical positions of wrappers.
295         float radicalTopMargin = m_extraAscender;
296         float baseTopMargin = m_verticalGap + m_ruleThickness + m_extraAscender;
297         if (!isRenderMathMLSquareRoot()) {
298             // For mroot, we try to place the index so the space below its baseline is m_degreeBottomRaisePercent times the height of the radical.
299             auto index = indexWrapper();
300             float indexHeight = 0;
301             if (!index->isEmpty())
302                 indexHeight = toRenderBlock(index->firstChild())->logicalHeight();
303             float indexTopMargin = (1.0 - m_degreeBottomRaisePercent) * radical->stretchSize() + radicalTopMargin - indexHeight;
304             if (indexTopMargin < 0) {
305                 // If the index is too tall, we must add space at the top of renderer.
306                 radicalTopMargin -= indexTopMargin;
307                 baseTopMargin -= indexTopMargin;
308                 indexTopMargin = 0;
309             }
310             index->style().setMarginTop(Length(indexTopMargin, Fixed));
311         }
312         radical->style().setMarginTop(Length(radicalTopMargin, Fixed));
313         base->style().setMarginTop(Length(baseTopMargin, Fixed));
314     }
315
316     RenderMathMLBlock::layout();
317 }
318
319 void RenderMathMLRoot::paint(PaintInfo& info, const LayoutPoint& paintOffset)
320 {
321     RenderMathMLBlock::paint(info, paintOffset);
322     
323     if (isEmpty() || info.context->paintingDisabled() || style().visibility() != VISIBLE)
324         return;
325
326     auto base = baseWrapper();
327     auto radical = radicalOperator();
328     if (!base || !radical || !m_ruleThickness)
329         return;
330
331     // We draw the radical line.
332     GraphicsContextStateSaver stateSaver(*info.context);
333
334     info.context->setStrokeThickness(m_ruleThickness);
335     info.context->setStrokeStyle(SolidStroke);
336     info.context->setStrokeColor(style().visitedDependentColor(CSSPropertyColor), ColorSpaceDeviceRGB);
337
338     // The preferred width of the radical is sometimes incorrect, so we draw a slightly longer line to ensure it touches the radical symbol (https://bugs.webkit.org/show_bug.cgi?id=130326).
339     LayoutUnit sizeError = radical->trailingSpaceError();
340     IntPoint adjustedPaintOffset = roundedIntPoint(paintOffset + location() + base->location() + LayoutPoint(-sizeError, -(m_verticalGap + m_ruleThickness / 2)));
341     info.context->drawLine(adjustedPaintOffset, IntPoint(adjustedPaintOffset.x() + base->pixelSnappedOffsetWidth() + sizeError, adjustedPaintOffset.y()));
342 }
343
344 RenderPtr<RenderMathMLRootWrapper> RenderMathMLRootWrapper::createAnonymousWrapper(RenderMathMLRoot* renderObject)
345 {
346     RenderPtr<RenderMathMLRootWrapper> newBlock = createRenderer<RenderMathMLRootWrapper>(renderObject->document(), RenderStyle::createAnonymousStyleWithDisplay(&renderObject->style(), FLEX));
347     newBlock->initializeStyle();
348     return newBlock;
349 }
350
351 RenderObject* RenderMathMLRootWrapper::removeChildWithoutRestructuring(RenderObject& child)
352 {
353     return RenderMathMLBlock::removeChild(child);
354 }
355
356 RenderObject* RenderMathMLRootWrapper::removeChild(RenderObject& child)
357 {
358     RenderObject* next = RenderMathMLBlock::removeChild(child);
359
360     if (!(beingDestroyed() || documentBeingDestroyed()))
361         toRenderMathMLRoot(parent())->restructureWrappers();
362
363     return next;
364 }
365
366 }
367
368 #endif // ENABLE(MATHML)