2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include "RenderFlexibleBox.h"
34 #if ENABLE(CSS3_FLEXBOX)
38 class RenderFlexibleBox::FlexibleBoxIterator {
40 explicit FlexibleBoxIterator(RenderFlexibleBox* flexibleBox)
41 : m_flexibleBox(flexibleBox)
54 RenderObject* child = m_currentChild ? m_currentChild->nextSibling() : m_flexibleBox->firstChild();
55 // FIXME: Inline nodes (like <img> or <input>) should also be treated as boxes.
56 while (child && !child->isBox())
57 child = child->nextSibling();
59 m_currentChild = toRenderBox(child);
60 return m_currentChild;
69 RenderFlexibleBox* m_flexibleBox;
70 RenderBox* m_currentChild;
74 RenderFlexibleBox::RenderFlexibleBox(Node* node)
79 RenderFlexibleBox::~RenderFlexibleBox()
83 const char* RenderFlexibleBox::renderName() const
85 return "RenderFlexibleBox";
88 void RenderFlexibleBox::layoutBlock(bool relayoutChildren, int, BlockLayoutPass)
90 ASSERT(needsLayout());
92 if (!relayoutChildren && simplifiedLayout())
95 IntSize previousSize = size();
97 computeLogicalWidth();
98 computeLogicalHeight();
102 // FIXME: Assume horizontal layout until flex-flow is added.
103 layoutHorizontalBlock(relayoutChildren);
105 computeLogicalHeight();
107 if (size() != previousSize)
108 relayoutChildren = true;
110 layoutPositionedObjects(relayoutChildren || isRoot());
112 updateLayerTransform();
114 setNeedsLayout(false);
117 static LayoutUnit preferredFlexItemContentWidth(RenderBox* child)
119 // FIXME: Handle vertical writing modes with horizontal flexing.
120 if (child->style()->width().isAuto())
121 return child->maxPreferredLogicalWidth() - child->borderLeft() - child->borderRight() - child->verticalScrollbarWidth() - child->paddingLeft() - child->paddingRight();
122 return child->contentWidth();
125 void RenderFlexibleBox::layoutHorizontalBlock(bool relayoutChildren)
127 LayoutUnit preferredSize;
128 float totalPositiveFlexibility;
129 float totalNegativeFlexibility;
130 FlexibleBoxIterator iterator(this);
132 computePreferredSizeHorizontal(relayoutChildren, iterator, preferredSize, totalPositiveFlexibility, totalNegativeFlexibility);
133 LayoutUnit availableFreeSpace = contentWidth() - preferredSize;
135 InflexibleFlexItemSize inflexibleItems;
136 WTF::Vector<LayoutUnit> childSizes;
137 while (!runFreeSpaceAllocationAlgorithmHorizontal(availableFreeSpace, totalPositiveFlexibility, totalNegativeFlexibility, inflexibleItems, childSizes)) {
138 ASSERT(totalPositiveFlexibility >= 0 && totalNegativeFlexibility >= 0);
139 ASSERT(inflexibleItems.size() > 0);
142 layoutAndPlaceChildrenHorizontal(childSizes, availableFreeSpace, totalPositiveFlexibility);
144 // FIXME: Handle distribution of vertical space (third distribution round).
147 static LayoutUnit preferredSizeForMarginsAndPadding(Length length, LayoutUnit containerLength)
149 return length.calcMinValue(containerLength);
152 void RenderFlexibleBox::computePreferredSizeHorizontal(bool relayoutChildren, FlexibleBoxIterator& iterator, LayoutUnit& preferredSize, float& totalPositiveFlexibility, float& totalNegativeFlexibility)
155 totalPositiveFlexibility = totalNegativeFlexibility = 0;
157 // FIXME: Handle vertical writing modes with horizontal flexing.
158 LayoutUnit flexboxAvailableLogicalWidth = availableLogicalWidth();
159 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
160 // We always have to lay out flexible objects again, since the flex distribution
161 // may have changed, and we need to reallocate space.
162 child->clearOverrideSize();
163 if (!relayoutChildren)
164 child->setChildNeedsLayout(true);
165 child->layoutIfNeeded();
167 preferredSize += preferredSizeForMarginsAndPadding(child->style()->marginLeft(), flexboxAvailableLogicalWidth);
168 preferredSize += preferredSizeForMarginsAndPadding(child->style()->marginRight(), flexboxAvailableLogicalWidth);
169 preferredSize += preferredSizeForMarginsAndPadding(child->style()->paddingLeft(), flexboxAvailableLogicalWidth);
170 preferredSize += preferredSizeForMarginsAndPadding(child->style()->paddingRight(), flexboxAvailableLogicalWidth);
172 if (child->style()->marginLeft().isAuto())
173 totalPositiveFlexibility += 1;
174 if (child->style()->marginRight().isAuto())
175 totalPositiveFlexibility += 1;
177 preferredSize += child->borderLeft() + child->borderRight();
179 preferredSize += preferredFlexItemContentWidth(child);
181 totalPositiveFlexibility += child->style()->flexboxWidthPositiveFlex();
182 totalNegativeFlexibility += child->style()->flexboxWidthNegativeFlex();
186 // Returns true if we successfully ran the algorithm and sized the flex items.
187 bool RenderFlexibleBox::runFreeSpaceAllocationAlgorithmHorizontal(LayoutUnit& availableFreeSpace, float& totalPositiveFlexibility, float& totalNegativeFlexibility, InflexibleFlexItemSize& inflexibleItems, WTF::Vector<LayoutUnit>& childSizes)
189 FlexibleBoxIterator iterator(this);
192 // FIXME: Handle vertical writing modes with horizontal flexing.
193 LayoutUnit flexboxAvailableLogicalWidth = availableLogicalWidth();
194 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
195 LayoutUnit childPreferredSize;
196 if (inflexibleItems.contains(child))
197 childPreferredSize = inflexibleItems.get(child);
199 childPreferredSize = preferredFlexItemContentWidth(child);
200 if (availableFreeSpace > 0 && totalPositiveFlexibility > 0) {
201 childPreferredSize += lroundf(availableFreeSpace * child->style()->flexboxWidthPositiveFlex() / totalPositiveFlexibility);
203 Length childMaxWidth = child->style()->maxWidth();
204 if (!childMaxWidth.isUndefined() && childMaxWidth.isSpecified() && childPreferredSize > childMaxWidth.calcValue(flexboxAvailableLogicalWidth)) {
205 childPreferredSize = childMaxWidth.calcValue(flexboxAvailableLogicalWidth);
206 availableFreeSpace -= childPreferredSize - preferredFlexItemContentWidth(child);
207 totalPositiveFlexibility -= child->style()->flexboxWidthPositiveFlex();
209 inflexibleItems.set(child, childPreferredSize);
212 } else if (availableFreeSpace < 0 && totalNegativeFlexibility > 0) {
213 childPreferredSize += lroundf(availableFreeSpace * child->style()->flexboxWidthNegativeFlex() / totalNegativeFlexibility);
215 Length childMinWidth = child->style()->minWidth();
216 if (!childMinWidth.isUndefined() && childMinWidth.isSpecified() && childPreferredSize < childMinWidth.calcValue(flexboxAvailableLogicalWidth)) {
217 childPreferredSize = childMinWidth.calcValue(flexboxAvailableLogicalWidth);
218 availableFreeSpace += preferredFlexItemContentWidth(child) - childPreferredSize;
219 totalNegativeFlexibility -= child->style()->flexboxWidthNegativeFlex();
221 inflexibleItems.set(child, childPreferredSize);
226 childSizes.append(childPreferredSize);
231 static bool hasPackingSpace(LayoutUnit availableFreeSpace, float totalPositiveFlexibility)
233 return availableFreeSpace > 0 && !totalPositiveFlexibility;
236 void RenderFlexibleBox::layoutAndPlaceChildrenHorizontal(const WTF::Vector<LayoutUnit>& childSizes, LayoutUnit availableFreeSpace, float totalPositiveFlexibility)
238 FlexibleBoxIterator iterator(this);
239 // Now that we know the sizes, layout and position the flex items.
240 LayoutUnit xOffset = borderLeft() + paddingLeft();
242 if (hasPackingSpace(availableFreeSpace, totalPositiveFlexibility)) {
243 if (style()->flexPack() == PackEnd)
244 xOffset += availableFreeSpace;
245 else if (style()->flexPack() == PackCenter)
246 xOffset += availableFreeSpace / 2;
249 LayoutUnit yOffset = borderTop() + paddingTop();
252 for (RenderBox* child = iterator.first(); child; child = iterator.next(), ++i) {
253 LayoutUnit childPreferredSize = child->borderLeft() + child->paddingLeft() + childSizes[i] + child->paddingRight() + child->borderRight();
254 // FIXME: Handle vertical writing modes with horizontal flexing.
255 child->setOverrideWidth(childPreferredSize);
256 child->setChildNeedsLayout(true);
257 child->layoutIfNeeded();
259 setHeight(std::max(height(), borderTop() + paddingTop() + child->marginTop() + child->height() + child->marginBottom() + paddingBottom() + borderBottom() + horizontalScrollbarHeight()));
261 if (child->style()->marginLeft().isAuto())
262 child->setMarginLeft(availableFreeSpace > 0 ? lroundf(availableFreeSpace / totalPositiveFlexibility) : 0);
263 if (child->style()->marginRight().isAuto())
264 child->setMarginRight(availableFreeSpace > 0 ? lroundf(availableFreeSpace / totalPositiveFlexibility) : 0);
266 xOffset += child->marginLeft();
267 child->setLocation(IntPoint(xOffset, yOffset));
268 xOffset += child->width() + child->marginRight();
270 if (hasPackingSpace(availableFreeSpace, totalPositiveFlexibility) && style()->flexPack() == PackJustify && childSizes.size() > 1)
271 xOffset += availableFreeSpace / (childSizes.size() - 1);
277 #endif // ENABLE(CSS3_FLEXBOX)