Minor refactoring in RenderMathMLOperator
[WebKit-https.git] / Source / WebCore / rendering / mathml / RenderMathMLRow.cpp
1 /*
2  * Copyright (C) 2010 Alex Milowski (alex@milowski.com). All rights reserved.
3  * Copyright (C) 2016 Igalia S.L.
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 "RenderMathMLRow.h"
32
33 #include "MathMLNames.h"
34 #include "RenderIterator.h"
35 #include "RenderMathMLOperator.h"
36 #include "RenderMathMLRoot.h"
37
38 namespace WebCore {
39
40 using namespace MathMLNames;
41
42 RenderMathMLRow::RenderMathMLRow(Element& element, std::unique_ptr<RenderStyle> style)
43     : RenderMathMLBlock(element, WTFMove(style))
44 {
45 }
46
47 RenderMathMLRow::RenderMathMLRow(Document& document, std::unique_ptr<RenderStyle> style)
48     : RenderMathMLBlock(document, WTFMove(style))
49 {
50 }
51
52 void RenderMathMLRow::updateOperatorProperties()
53 {
54     for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
55         if (is<RenderMathMLBlock>(*child)) {
56             if (auto* renderOperator = downcast<RenderMathMLBlock>(*child).unembellishedOperator())
57                 renderOperator->updateOperatorProperties();
58         }
59     }
60     setNeedsLayoutAndPrefWidthsRecalc();
61 }
62
63
64 Optional<int> RenderMathMLRow::firstLineBaseline() const
65 {
66     RenderBox* baselineChild = firstChildBox();
67     if (!baselineChild)
68         return Optional<int>();
69
70     return Optional<int>(static_cast<int>(lroundf(ascentForChild(*baselineChild) + baselineChild->logicalTop())));
71 }
72
73 void RenderMathMLRow::computeLineVerticalStretch(int& stretchHeightAboveBaseline, int& stretchDepthBelowBaseline)
74 {
75     for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
76         if (is<RenderMathMLBlock>(child)) {
77             auto* renderOperator = downcast<RenderMathMLBlock>(child)->unembellishedOperator();
78             if (renderOperator && renderOperator->hasOperatorFlag(MathMLOperatorDictionary::Stretchy))
79                 continue;
80         }
81
82         child->layoutIfNeeded();
83
84         LayoutUnit childHeightAboveBaseline = ascentForChild(*child);
85         LayoutUnit childDepthBelowBaseline = child->logicalHeight() - childHeightAboveBaseline;
86
87         stretchHeightAboveBaseline = std::max<LayoutUnit>(stretchHeightAboveBaseline, childHeightAboveBaseline);
88         stretchDepthBelowBaseline = std::max<LayoutUnit>(stretchDepthBelowBaseline, childDepthBelowBaseline);
89     }
90
91     // We ensure a minimal stretch size.
92     if (stretchHeightAboveBaseline + stretchDepthBelowBaseline <= 0) {
93         stretchHeightAboveBaseline = style().fontSize();
94         stretchDepthBelowBaseline = 0;
95     }
96 }
97
98 void RenderMathMLRow::computePreferredLogicalWidths()
99 {
100     ASSERT(preferredLogicalWidthsDirty());
101
102     m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = 0;
103
104     LayoutUnit preferredWidth = 0;
105     for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox())
106         preferredWidth += child->maxPreferredLogicalWidth() + child->marginLogicalWidth();
107
108     m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = preferredWidth + borderAndPaddingLogicalWidth();
109
110     setPreferredLogicalWidthsDirty(false);
111 }
112
113 void RenderMathMLRow::layoutRowItems(int stretchHeightAboveBaseline, int stretchDepthBelowBaseline)
114 {
115     // We first stretch the vertical operators.
116     // For inline formulas, we can then calculate the logical width.
117     LayoutUnit width = borderAndPaddingStart();
118     for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
119         if (child->isOutOfFlowPositioned())
120             continue;
121
122         if (is<RenderMathMLBlock>(child)) {
123             auto renderOperator = downcast<RenderMathMLBlock>(child)->unembellishedOperator();
124             if (renderOperator && renderOperator->hasOperatorFlag(MathMLOperatorDictionary::Stretchy) && renderOperator->isVertical())
125                 renderOperator->stretchTo(stretchHeightAboveBaseline, stretchDepthBelowBaseline);
126         }
127
128         child->layoutIfNeeded();
129
130         width += child->marginStart() + child->logicalWidth() + child->marginEnd();
131     }
132
133     width += borderEnd() + paddingEnd();
134     // FIXME: RenderMathMLRoot and RenderMathMLEnclose classes should also recalculate the exact logical width instead of using the preferred width.
135     // See https://bugs.webkit.org/show_bug.cgi?id=130326
136     if ((!isRenderMathMLMath() || style().display() == INLINE) && !isRenderMathMLRoot() && !isRenderMathMLMenclose())
137         setLogicalWidth(width);
138
139     LayoutUnit verticalOffset = borderTop() + paddingTop();
140     LayoutUnit maxAscent = 0, maxDescent = 0; // Used baseline alignment.
141     LayoutUnit horizontalOffset = borderAndPaddingStart();
142     bool shouldFlipHorizontal = !style().isLeftToRightDirection();
143     for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
144         if (child->isOutOfFlowPositioned()) {
145             child->containingBlock()->insertPositionedObject(*child);
146             continue;
147         }
148         LayoutUnit childHorizontalExtent = child->logicalWidth();
149         LayoutUnit ascent = ascentForChild(*child);
150         LayoutUnit descent = child->verticalMarginExtent() + child->logicalHeight() - ascent;
151         maxAscent = std::max(maxAscent, ascent);
152         maxDescent = std::max(maxDescent, descent);
153         LayoutUnit childVerticalMarginBoxExtent = maxAscent + maxDescent;
154
155         horizontalOffset += child->marginStart();
156
157         setLogicalHeight(std::max(logicalHeight(), verticalOffset + borderBottom() + paddingBottom() + childVerticalMarginBoxExtent + horizontalScrollbarHeight()));
158
159         LayoutPoint childLocation(shouldFlipHorizontal ? logicalWidth() - horizontalOffset - childHorizontalExtent : horizontalOffset, verticalOffset + child->marginTop());
160         child->setLocation(childLocation);
161
162         horizontalOffset += childHorizontalExtent + child->marginEnd();
163     }
164
165     LayoutUnit centerBlockOffset = 0;
166     // FIXME: Remove the FLEX when it is not required by the css.
167     if (style().display() == BLOCK || style().display() == FLEX)
168         centerBlockOffset = std::max<LayoutUnit>(0, (logicalWidth() - (horizontalOffset + borderEnd() + paddingEnd())) / 2);
169
170     if (shouldFlipHorizontal && centerBlockOffset > 0)
171         centerBlockOffset = -centerBlockOffset;
172
173     for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
174         LayoutUnit ascent = ascentForChild(*child);
175         LayoutUnit startOffset = maxAscent - ascent;
176         child->setLocation(child->location() + LayoutPoint(centerBlockOffset, startOffset));
177     }
178 }
179
180 void RenderMathMLRow::layoutBlock(bool relayoutChildren, LayoutUnit)
181 {
182     ASSERT(needsLayout());
183
184     if (!relayoutChildren && simplifiedLayout())
185         return;
186
187     int stretchHeightAboveBaseline = 0;
188     int stretchDepthBelowBaseline = 0;
189     computeLineVerticalStretch(stretchHeightAboveBaseline, stretchDepthBelowBaseline);
190
191     recomputeLogicalWidth();
192
193     setLogicalHeight(borderAndPaddingLogicalHeight() + scrollbarLogicalHeight());
194
195     layoutRowItems(stretchHeightAboveBaseline, stretchDepthBelowBaseline);
196
197     updateLogicalHeight();
198
199     clearNeedsLayout();
200 }
201
202 void RenderMathMLRow::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect)
203 {
204     for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
205         if (!paintChild(*child, paintInfo, paintOffset, paintInfoForChild, usePrintRect, PaintAsInlineBlock))
206             return;
207     }
208 }
209
210 }
211
212 #endif // ENABLE(MATHML)