Refactor RenderMathMLUnderOver layout functions to avoid using flexbox
[WebKit-https.git] / Source / WebCore / rendering / mathml / RenderMathMLUnderOver.cpp
1 /*
2  * Copyright (C) 2009 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 "RenderMathMLUnderOver.h"
32
33 #include "MathMLElement.h"
34 #include "MathMLNames.h"
35 #include "RenderIterator.h"
36 #include "RenderMathMLOperator.h"
37
38 namespace WebCore {
39
40 using namespace MathMLNames;
41     
42 RenderMathMLUnderOver::RenderMathMLUnderOver(Element& element, Ref<RenderStyle>&& style)
43     : RenderMathMLBlock(element, WTFMove(style))
44 {
45     // Determine what kind of under/over expression we have by element name
46     if (element.hasTagName(MathMLNames::munderTag))
47         m_scriptType = Under;
48     else if (element.hasTagName(MathMLNames::moverTag))
49         m_scriptType = Over;
50     else {
51         ASSERT(element.hasTagName(MathMLNames::munderoverTag));
52         m_scriptType = UnderOver;
53     }
54 }
55
56 RenderMathMLOperator* RenderMathMLUnderOver::unembellishedOperator()
57 {
58     RenderObject* base = firstChild();
59     if (!is<RenderMathMLBlock>(base))
60         return nullptr;
61     return downcast<RenderMathMLBlock>(*base).unembellishedOperator();
62 }
63
64 Optional<int> RenderMathMLUnderOver::firstLineBaseline() const
65 {
66     RenderBox* base = firstChildBox();
67     if (!base)
68         return Optional<int>();
69
70     return Optional<int>(static_cast<int>(lroundf(ascentForChild(*base) + base->logicalTop())));
71 }
72
73 void RenderMathMLUnderOver::computeOperatorsHorizontalStretch()
74 {
75     LayoutUnit stretchWidth = 0;
76     Vector<RenderMathMLOperator*, 2> renderOperators;
77
78     for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
79         if (child->needsLayout()) {
80             if (is<RenderMathMLBlock>(child)) {
81                 if (auto renderOperator = downcast<RenderMathMLBlock>(*child).unembellishedOperator()) {
82                     if (renderOperator->hasOperatorFlag(MathMLOperatorDictionary::Stretchy) && !renderOperator->isVertical()) {
83                         renderOperator->resetStretchSize();
84                         renderOperators.append(renderOperator);
85                     }
86                 }
87             }
88
89             downcast<RenderElement>(*child).layout();
90         }
91
92         // Skipping the embellished op does not work for nested structures like
93         // <munder><mover><mo>_</mo>...</mover> <mo>_</mo></munder>.
94         if (is<RenderBox>(*child))
95             stretchWidth = std::max<LayoutUnit>(stretchWidth, downcast<RenderBox>(*child).logicalWidth());
96     }
97
98     // Set the sizes of (possibly embellished) stretchy operator children.
99     for (auto& renderOperator : renderOperators)
100         renderOperator->stretchTo(stretchWidth);
101 }
102
103 bool RenderMathMLUnderOver::isValid() const
104 {
105     // Verify whether the list of children is valid:
106     // <munder> base under </munder>
107     // <mover> base over </mover>
108     // <munderover> base under over </munderover>
109     RenderBox* child = firstChildBox();
110     if (!child)
111         return false;
112     child = child->nextSiblingBox();
113     if (!child)
114         return false;
115     child = child->nextSiblingBox();
116     switch (m_scriptType) {
117     case Over:
118     case Under:
119         return !child;
120     case UnderOver:
121         return child && !child->nextSiblingBox();
122     default:
123         ASSERT_NOT_REACHED();
124         return false;
125     }
126 }
127
128 RenderBox& RenderMathMLUnderOver::base() const
129 {
130     ASSERT(isValid());
131     return *firstChildBox();
132 }
133
134 RenderBox& RenderMathMLUnderOver::under() const
135 {
136     ASSERT(isValid());
137     ASSERT(m_scriptType == Under || m_scriptType == UnderOver);
138     return *firstChildBox()->nextSiblingBox();
139 }
140
141 RenderBox& RenderMathMLUnderOver::over() const
142 {
143     ASSERT(isValid());
144     ASSERT(m_scriptType == Over || m_scriptType == UnderOver);
145     RenderBox* secondChild = firstChildBox()->nextSiblingBox();
146     return m_scriptType == Over ? *secondChild : *secondChild->nextSiblingBox();
147 }
148
149
150 void RenderMathMLUnderOver::computePreferredLogicalWidths()
151 {
152     ASSERT(preferredLogicalWidthsDirty());
153
154     if (!isValid()) {
155         m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = 0;
156         setPreferredLogicalWidthsDirty(false);
157         return;
158     }
159
160     LayoutUnit preferredWidth = base().maxPreferredLogicalWidth();
161
162     if (m_scriptType == Under || m_scriptType == UnderOver)
163         preferredWidth = std::max(preferredWidth, under().maxPreferredLogicalWidth());
164
165     if (m_scriptType == Over || m_scriptType == UnderOver)
166         preferredWidth = std::max(preferredWidth, over().maxPreferredLogicalWidth());
167
168     m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = preferredWidth;
169
170     setPreferredLogicalWidthsDirty(false);
171 }
172
173 LayoutUnit RenderMathMLUnderOver::horizontalOffset(const RenderBox& child) const
174 {
175     return (logicalWidth() - child.logicalWidth()) / 2;
176 }
177
178 void RenderMathMLUnderOver::layoutBlock(bool relayoutChildren, LayoutUnit)
179 {
180     ASSERT(needsLayout());
181
182     if (!relayoutChildren && simplifiedLayout())
183         return;
184
185     if (!isValid()) {
186         setLogicalWidth(0);
187         setLogicalHeight(0);
188         clearNeedsLayout();
189         return;
190     }
191
192     recomputeLogicalWidth();
193
194     computeOperatorsHorizontalStretch();
195
196     base().layoutIfNeeded();
197     if (m_scriptType == Under || m_scriptType == UnderOver)
198         under().layoutIfNeeded();
199     if (m_scriptType == Over || m_scriptType == UnderOver)
200         over().layoutIfNeeded();
201
202     LayoutUnit logicalWidth = base().logicalWidth();
203     if (m_scriptType == Under || m_scriptType == UnderOver)
204         logicalWidth = std::max(logicalWidth, under().logicalWidth());
205     if (m_scriptType == Over || m_scriptType == UnderOver)
206         logicalWidth = std::max(logicalWidth, over().logicalWidth());
207     setLogicalWidth(logicalWidth);
208
209     LayoutUnit verticalOffset = 0;
210     if (m_scriptType == Over || m_scriptType == UnderOver) {
211         over().setLocation(LayoutPoint(horizontalOffset(over()), 0));
212         verticalOffset += over().logicalHeight();
213     }
214     base().setLocation(LayoutPoint(horizontalOffset(base()), verticalOffset));
215     verticalOffset += base().logicalHeight();
216     if (m_scriptType == Under || m_scriptType == UnderOver) {
217         under().setLocation(LayoutPoint(horizontalOffset(under()), verticalOffset));
218         verticalOffset += under().logicalHeight();
219     }
220
221     setLogicalHeight(verticalOffset);
222
223     clearNeedsLayout();
224 }
225
226 void RenderMathMLUnderOver::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect)
227 {
228     for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
229         if (!paintChild(*child, paintInfo, paintOffset, paintInfoForChild, usePrintRect, PaintAsInlineBlock))
230             return;
231     }
232 }
233
234 }
235
236 #endif // ENABLE(MATHML)