[CSS Grid Layout] Upgrade align-self and align-items parsing to CSS 3
[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 #include "RenderMathMLSquareRoot.h"
39
40 namespace WebCore {
41     
42 // RenderMathMLRoot implements drawing of radicals via the <mroot> and <msqrt> elements. For valid MathML elements, the DOM is
43 //
44 // <mroot> Base Index </mroot>
45 // <msqrt> Child1 Child2 ... ChildN </msqrt>
46 //
47 // and the structure of the render tree will be
48 //
49 // IndexWrapper RadicalWrapper BaseWrapper
50 //
51 // where RadicalWrapper contains an <mo>&#x221A;</mo>.
52 // For <mroot>, the IndexWrapper and BaseWrapper should contain exactly one child (Index and Base respectively).
53 // For <msqrt>, the IndexWrapper should be empty and the BaseWrapper can contain any number of children (Child1, ... ChildN).
54 //
55 // 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.
56 // 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.
57
58 RenderMathMLRoot::RenderMathMLRoot(Element& element, Ref<RenderStyle>&& style)
59     : RenderMathMLBlock(element, WTF::move(style))
60 {
61 }
62
63 RenderMathMLRoot::RenderMathMLRoot(Document& document, Ref<RenderStyle>&& style)
64     : RenderMathMLBlock(document, WTF::move(style))
65 {
66 }
67
68 RenderMathMLRootWrapper* RenderMathMLRoot::baseWrapper() const
69 {
70     ASSERT(!isEmpty());
71     return downcast<RenderMathMLRootWrapper>(lastChild());
72 }
73
74 RenderMathMLBlock* RenderMathMLRoot::radicalWrapper() const
75 {
76     ASSERT(!isEmpty());
77     return downcast<RenderMathMLBlock>(lastChild()->previousSibling());
78 }
79
80 RenderMathMLRootWrapper* RenderMathMLRoot::indexWrapper() const
81 {
82     ASSERT(!isEmpty());
83     return is<RenderMathMLSquareRoot>(*this) ? nullptr : downcast<RenderMathMLRootWrapper>(firstChild());
84 }
85
86 RenderMathMLRadicalOperator* RenderMathMLRoot::radicalOperator() const
87 {
88     ASSERT(!isEmpty());
89     return downcast<RenderMathMLRadicalOperator>(radicalWrapper()->firstChild());
90 }
91
92 void RenderMathMLRoot::restructureWrappers()
93 {
94     ASSERT(!isEmpty());
95
96     auto base = baseWrapper();
97     auto index = indexWrapper();
98     auto radical = radicalWrapper();
99
100     // For visual consistency with the initial state, we remove the radical when the base/index wrappers become empty.
101     if (base->isEmpty() && (!index || index->isEmpty())) {
102         if (!radical->isEmpty()) {
103             auto child = radicalOperator();
104             radical->removeChild(*child);
105             child->destroy();
106         }
107         // FIXME: early return!!!
108     }
109
110     if (radical->isEmpty()) {
111         // We create the radical operator.
112         RenderPtr<RenderMathMLRadicalOperator> radicalOperator = createRenderer<RenderMathMLRadicalOperator>(document(), RenderStyle::createAnonymousStyleWithDisplay(&style(), FLEX));
113         radicalOperator->initializeStyle();
114         radical->addChild(radicalOperator.leakPtr());
115     }
116
117     if (isRenderMathMLSquareRoot())
118         return;
119
120     if (auto childToMove = base->lastChild()) {
121         // 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.
122         if (childToMove->previousSibling() && index->isEmpty()) {
123             base->removeChildWithoutRestructuring(*childToMove);
124             index->addChild(childToMove);
125         }
126     }
127
128     if (auto childToMove = index->firstChild()) {
129         // We move the first child of the index wrapper into the base wrapper if:
130         // - either the index wrapper has at least two children.
131         // - or the base wrapper is empty but the index wrapper is not.
132         if (childToMove->nextSibling() || base->isEmpty()) {
133             index->removeChildWithoutRestructuring(*childToMove);
134             base->addChild(childToMove);
135         }
136     }
137 }
138
139 void RenderMathMLRoot::addChild(RenderObject* newChild, RenderObject* beforeChild)
140 {
141     if (isEmpty()) {
142         if (!isRenderMathMLSquareRoot()) {
143             // We add the IndexWrapper.
144             RenderMathMLBlock::addChild(RenderMathMLRootWrapper::createAnonymousWrapper(this).leakPtr());
145         }
146
147         // We create the radicalWrapper
148         RenderMathMLBlock::addChild(RenderMathMLBlock::createAnonymousMathMLBlock().leakPtr());
149
150         // We create the BaseWrapper.
151         RenderMathMLBlock::addChild(RenderMathMLRootWrapper::createAnonymousWrapper(this).leakPtr());
152
153         updateStyle();
154     }
155
156     // We insert the child.
157     auto base = baseWrapper();
158     auto index = indexWrapper();
159     RenderElement* actualParent;
160     RenderElement* actualBeforeChild;
161     if (is<RenderMathMLSquareRoot>(*this)) {
162         // For square root, we always insert the child into the base wrapper.
163         actualParent = base;
164         if (beforeChild && beforeChild->parent() == base)
165             actualBeforeChild = downcast<RenderElement>(beforeChild);
166         else
167             actualBeforeChild = nullptr;
168     } else {
169         // For mroot, we insert the child into the parent of beforeChild, or at the end of the index. The wrapper structure is reorganize below.
170         actualParent = beforeChild ? beforeChild->parent() : nullptr;
171         if (actualParent == base || actualParent == index)
172             actualBeforeChild = downcast<RenderElement>(beforeChild);
173         else {
174             actualParent = index;
175             actualBeforeChild = nullptr;
176         }
177     }
178     actualParent->addChild(newChild, actualBeforeChild);
179     restructureWrappers();
180 }
181
182 void RenderMathMLRoot::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
183 {
184     RenderMathMLBlock::styleDidChange(diff, oldStyle);
185     if (!isEmpty())
186         updateStyle();
187 }
188
189 void RenderMathMLRoot::updateFromElement()
190 {
191     RenderMathMLBlock::updateFromElement();
192     if (!isEmpty())
193         updateStyle();
194 }
195
196 void RenderMathMLRoot::updateStyle()
197 {
198     ASSERT(!isEmpty());
199
200     // We set some constants to draw the radical, as defined in the OpenType MATH tables.
201
202     m_ruleThickness = 0.05f * style().fontCascade().size();
203
204     // 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).
205     m_verticalGap = 11 * m_ruleThickness / 4;
206     m_extraAscender = m_ruleThickness;
207     LayoutUnit kernBeforeDegree = 5 * style().fontCascade().size() / 18;
208     LayoutUnit kernAfterDegree = -10 * style().fontCascade().size() / 18;
209     m_degreeBottomRaisePercent = 0.6f;
210
211     const auto& primaryFont = style().fontCascade().primaryFont();
212     if (auto* mathData = style().fontCascade().primaryFont().mathData()) {
213         // FIXME: m_verticalGap should use RadicalDisplayStyleVertical in display mode (https://bugs.webkit.org/show_bug.cgi?id=118737).
214         m_verticalGap = mathData->getMathConstant(primaryFont, OpenTypeMathData::RadicalVerticalGap);
215         m_ruleThickness = mathData->getMathConstant(primaryFont, OpenTypeMathData::RadicalRuleThickness);
216         m_extraAscender = mathData->getMathConstant(primaryFont, OpenTypeMathData::RadicalExtraAscender);
217
218         if (!isRenderMathMLSquareRoot()) {
219             kernBeforeDegree = mathData->getMathConstant(primaryFont, OpenTypeMathData::RadicalKernBeforeDegree);
220             kernAfterDegree = mathData->getMathConstant(primaryFont, OpenTypeMathData::RadicalKernAfterDegree);
221             m_degreeBottomRaisePercent = mathData->getMathConstant(primaryFont, OpenTypeMathData::RadicalDegreeBottomRaisePercent);
222         }
223     }
224
225     // We set the style of the anonymous wrappers.
226
227     auto radical = radicalWrapper();
228     auto radicalStyle = RenderStyle::createAnonymousStyleWithDisplay(&style(), FLEX);
229     radicalStyle.get().setMarginTop(Length(0, Fixed)); // This will be updated in RenderMathMLRoot::layout().
230     radical->setStyle(WTF::move(radicalStyle));
231     radical->setNeedsLayoutAndPrefWidthsRecalc();
232
233     auto base = baseWrapper();
234     auto baseStyle = RenderStyle::createAnonymousStyleWithDisplay(&style(), FLEX);
235     baseStyle.get().setMarginTop(Length(0, Fixed)); // This will be updated in RenderMathMLRoot::layout().
236     baseStyle.get().setAlignItems(ItemPositionBaseline);
237     base->setStyle(WTF::move(baseStyle));
238     base->setNeedsLayoutAndPrefWidthsRecalc();
239
240     if (!isRenderMathMLSquareRoot()) {
241         // For mroot, we also set the style of the index wrapper.
242         auto index = indexWrapper();
243         auto indexStyle = RenderStyle::createAnonymousStyleWithDisplay(&style(), FLEX);
244         indexStyle.get().setMarginTop(Length(0, Fixed)); // This will be updated in RenderMathMLRoot::layout().
245         indexStyle.get().setMarginStart(Length(kernBeforeDegree, Fixed));
246         indexStyle.get().setMarginEnd(Length(kernAfterDegree, Fixed));
247         indexStyle.get().setAlignItems(ItemPositionBaseline);
248         index->setStyle(WTF::move(indexStyle));
249         index->setNeedsLayoutAndPrefWidthsRecalc();
250     }
251 }
252
253 Optional<int> RenderMathMLRoot::firstLineBaseline() const
254 {
255     if (!isEmpty()) {
256         auto base = baseWrapper();
257         return static_cast<int>(lroundf(base->firstLineBaseline().valueOr(-1) + base->marginTop()));
258     }
259
260     return RenderMathMLBlock::firstLineBaseline();
261 }
262
263 void RenderMathMLRoot::layout()
264 {
265     if (isEmpty()) {
266         RenderMathMLBlock::layout();
267         return;
268     }
269
270     // FIXME: It seems that changing the top margin of the base below modifies its logical height and leads to reftest failures.
271     // For now, we workaround that by avoiding to recompute the child margins if they were not reset in updateStyle().
272     auto base = baseWrapper();
273     if (base->marginTop() > 0) {
274         RenderMathMLBlock::layout();
275         return;
276     }
277
278     // We layout the children.
279     for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
280         if (child->needsLayout())
281             downcast<RenderElement>(*child).layout();
282     }
283
284     auto radical = radicalOperator();
285     if (radical) {
286         // We stretch the radical sign to cover the height of the base wrapper.
287         float baseHeight = base->logicalHeight();
288         float baseHeightAboveBaseline = base->firstLineBaseline().valueOr(baseHeight);
289         float baseDepthBelowBaseline = baseHeight - baseHeightAboveBaseline;
290         baseHeightAboveBaseline += m_verticalGap;
291         radical->stretchTo(baseHeightAboveBaseline, baseDepthBelowBaseline);
292
293         // We modify the top margins to adjust the vertical positions of wrappers.
294         float radicalTopMargin = m_extraAscender;
295         float baseTopMargin = m_verticalGap + m_ruleThickness + m_extraAscender;
296         if (!isRenderMathMLSquareRoot()) {
297             // For mroot, we try to place the index so the space below its baseline is m_degreeBottomRaisePercent times the height of the radical.
298             auto index = indexWrapper();
299             float indexHeight = 0;
300             if (!index->isEmpty())
301                 indexHeight = downcast<RenderBlock>(*index->firstChild()).logicalHeight();
302             float indexTopMargin = (1.0 - m_degreeBottomRaisePercent) * radical->stretchSize() + radicalTopMargin - indexHeight;
303             if (indexTopMargin < 0) {
304                 // If the index is too tall, we must add space at the top of renderer.
305                 radicalTopMargin -= indexTopMargin;
306                 baseTopMargin -= indexTopMargin;
307                 indexTopMargin = 0;
308             }
309             index->style().setMarginTop(Length(indexTopMargin, Fixed));
310         }
311         radical->style().setMarginTop(Length(radicalTopMargin, Fixed));
312         base->style().setMarginTop(Length(baseTopMargin, Fixed));
313     }
314
315     RenderMathMLBlock::layout();
316 }
317
318 void RenderMathMLRoot::paint(PaintInfo& info, const LayoutPoint& paintOffset)
319 {
320     RenderMathMLBlock::paint(info, paintOffset);
321     
322     if (isEmpty() || info.context->paintingDisabled() || style().visibility() != VISIBLE)
323         return;
324
325     auto base = baseWrapper();
326     auto radical = radicalOperator();
327     if (!base || !radical || !m_ruleThickness)
328         return;
329
330     // We draw the radical line.
331     GraphicsContextStateSaver stateSaver(*info.context);
332
333     info.context->setStrokeThickness(m_ruleThickness);
334     info.context->setStrokeStyle(SolidStroke);
335     info.context->setStrokeColor(style().visitedDependentColor(CSSPropertyColor), ColorSpaceDeviceRGB);
336
337     // 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).
338     LayoutUnit sizeError = radical->trailingSpaceError();
339     IntPoint adjustedPaintOffset = roundedIntPoint(paintOffset + location() + base->location() + LayoutPoint(-sizeError, -(m_verticalGap + m_ruleThickness / 2)));
340     info.context->drawLine(adjustedPaintOffset, IntPoint(adjustedPaintOffset.x() + base->pixelSnappedOffsetWidth() + sizeError, adjustedPaintOffset.y()));
341 }
342
343 RenderPtr<RenderMathMLRootWrapper> RenderMathMLRootWrapper::createAnonymousWrapper(RenderMathMLRoot* renderObject)
344 {
345     RenderPtr<RenderMathMLRootWrapper> newBlock = createRenderer<RenderMathMLRootWrapper>(renderObject->document(), RenderStyle::createAnonymousStyleWithDisplay(&renderObject->style(), FLEX));
346     newBlock->initializeStyle();
347     return newBlock;
348 }
349
350 void RenderMathMLRootWrapper::removeChildWithoutRestructuring(RenderObject& child)
351 {
352     RenderMathMLBlock::removeChild(child);
353 }
354
355 void RenderMathMLRootWrapper::removeChild(RenderObject& child)
356 {
357     RenderMathMLBlock::removeChild(child);
358
359     if (!(beingDestroyed() || documentBeingDestroyed()))
360         downcast<RenderMathMLRoot>(*parent()).restructureWrappers();
361 }
362
363 }
364
365 #endif // ENABLE(MATHML)