2 * Copyright (C) 2009 Alex Milowski (alex@milowski.com). All rights reserved.
3 * Copyright (C) 2016 Igalia S.L.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
31 #include "RenderMathMLUnderOver.h"
33 #include "MathMLElement.h"
34 #include "MathMLNames.h"
35 #include "RenderIterator.h"
36 #include "RenderMathMLOperator.h"
40 using namespace MathMLNames;
42 RenderMathMLUnderOver::RenderMathMLUnderOver(Element& element, Ref<RenderStyle>&& style)
43 : RenderMathMLBlock(element, WTFMove(style))
45 // Determine what kind of under/over expression we have by element name
46 if (element.hasTagName(MathMLNames::munderTag))
48 else if (element.hasTagName(MathMLNames::moverTag))
51 ASSERT(element.hasTagName(MathMLNames::munderoverTag));
52 m_scriptType = UnderOver;
56 RenderMathMLOperator* RenderMathMLUnderOver::unembellishedOperator()
58 RenderObject* base = firstChild();
59 if (!is<RenderMathMLBlock>(base))
61 return downcast<RenderMathMLBlock>(*base).unembellishedOperator();
64 Optional<int> RenderMathMLUnderOver::firstLineBaseline() const
66 RenderBox* base = firstChildBox();
68 return Optional<int>();
70 return Optional<int>(static_cast<int>(lroundf(ascentForChild(*base) + base->logicalTop())));
73 void RenderMathMLUnderOver::computeOperatorsHorizontalStretch()
75 LayoutUnit stretchWidth = 0;
76 Vector<RenderMathMLOperator*, 2> renderOperators;
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);
89 downcast<RenderElement>(*child).layout();
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());
98 // Set the sizes of (possibly embellished) stretchy operator children.
99 for (auto& renderOperator : renderOperators)
100 renderOperator->stretchTo(stretchWidth);
103 bool RenderMathMLUnderOver::isValid() const
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();
112 child = child->nextSiblingBox();
115 child = child->nextSiblingBox();
116 switch (m_scriptType) {
121 return child && !child->nextSiblingBox();
123 ASSERT_NOT_REACHED();
128 RenderBox& RenderMathMLUnderOver::base() const
131 return *firstChildBox();
134 RenderBox& RenderMathMLUnderOver::under() const
137 ASSERT(m_scriptType == Under || m_scriptType == UnderOver);
138 return *firstChildBox()->nextSiblingBox();
141 RenderBox& RenderMathMLUnderOver::over() const
144 ASSERT(m_scriptType == Over || m_scriptType == UnderOver);
145 RenderBox* secondChild = firstChildBox()->nextSiblingBox();
146 return m_scriptType == Over ? *secondChild : *secondChild->nextSiblingBox();
150 void RenderMathMLUnderOver::computePreferredLogicalWidths()
152 ASSERT(preferredLogicalWidthsDirty());
155 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = 0;
156 setPreferredLogicalWidthsDirty(false);
160 LayoutUnit preferredWidth = base().maxPreferredLogicalWidth();
162 if (m_scriptType == Under || m_scriptType == UnderOver)
163 preferredWidth = std::max(preferredWidth, under().maxPreferredLogicalWidth());
165 if (m_scriptType == Over || m_scriptType == UnderOver)
166 preferredWidth = std::max(preferredWidth, over().maxPreferredLogicalWidth());
168 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = preferredWidth;
170 setPreferredLogicalWidthsDirty(false);
173 LayoutUnit RenderMathMLUnderOver::horizontalOffset(const RenderBox& child) const
175 return (logicalWidth() - child.logicalWidth()) / 2;
178 void RenderMathMLUnderOver::layoutBlock(bool relayoutChildren, LayoutUnit)
180 ASSERT(needsLayout());
182 if (!relayoutChildren && simplifiedLayout())
192 recomputeLogicalWidth();
194 computeOperatorsHorizontalStretch();
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();
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);
209 LayoutUnit verticalOffset = 0;
210 if (m_scriptType == Over || m_scriptType == UnderOver) {
211 over().setLocation(LayoutPoint(horizontalOffset(over()), 0));
212 verticalOffset += over().logicalHeight();
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();
221 setLogicalHeight(verticalOffset);
226 void RenderMathMLUnderOver::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect)
228 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
229 if (!paintChild(*child, paintInfo, paintOffset, paintInfoForChild, usePrintRect, PaintAsInlineBlock))
236 #endif // ENABLE(MATHML)