initial import
[vuplus_webkit] / Source / WebCore / rendering / RenderFlexibleBox.cpp
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
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
13  * distribution.
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.
17  *
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.
29  */
30
31 #include "config.h"
32 #include "RenderFlexibleBox.h"
33
34 #if ENABLE(CSS3_FLEXBOX)
35
36 namespace WebCore {
37
38 class RenderFlexibleBox::FlexibleBoxIterator {
39 public:
40     explicit FlexibleBoxIterator(RenderFlexibleBox* flexibleBox)
41         : m_flexibleBox(flexibleBox)
42         , m_currentChild(0)
43     {
44     }
45
46     RenderBox* first()
47     {
48         reset();
49         return next();
50     }
51
52     RenderBox* next()
53     {
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();
58
59         m_currentChild = toRenderBox(child);
60         return m_currentChild;
61     }
62
63     void reset()
64     {
65         m_currentChild = 0;
66     }
67
68 private:
69     RenderFlexibleBox* m_flexibleBox;
70     RenderBox* m_currentChild;
71 };
72
73
74 RenderFlexibleBox::RenderFlexibleBox(Node* node)
75     : RenderBlock(node)
76 {
77 }
78
79 RenderFlexibleBox::~RenderFlexibleBox()
80 {
81 }
82
83 const char* RenderFlexibleBox::renderName() const
84 {
85     return "RenderFlexibleBox";
86 }
87
88 void RenderFlexibleBox::layoutBlock(bool relayoutChildren, int, BlockLayoutPass)
89 {
90     ASSERT(needsLayout());
91
92     if (!relayoutChildren && simplifiedLayout())
93         return;
94
95     IntSize previousSize = size();
96
97     computeLogicalWidth();
98     computeLogicalHeight();
99
100     m_overflow.clear();
101
102     // FIXME: Assume horizontal layout until flex-flow is added.
103     layoutHorizontalBlock(relayoutChildren);
104
105     computeLogicalHeight();
106
107     if (size() != previousSize)
108         relayoutChildren = true;
109
110     layoutPositionedObjects(relayoutChildren || isRoot());
111
112     updateLayerTransform();
113
114     setNeedsLayout(false);
115 }
116
117 static LayoutUnit preferredFlexItemContentWidth(RenderBox* child)
118 {
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();
123 }
124
125 void RenderFlexibleBox::layoutHorizontalBlock(bool relayoutChildren)
126 {
127     LayoutUnit preferredSize;
128     float totalPositiveFlexibility;
129     float totalNegativeFlexibility;
130     FlexibleBoxIterator iterator(this);
131
132     computePreferredSizeHorizontal(relayoutChildren, iterator, preferredSize, totalPositiveFlexibility, totalNegativeFlexibility);
133     LayoutUnit availableFreeSpace = contentWidth() - preferredSize;
134
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);
140     }
141
142     layoutAndPlaceChildrenHorizontal(childSizes, availableFreeSpace, totalPositiveFlexibility);
143
144     // FIXME: Handle distribution of vertical space (third distribution round).
145 }
146
147 static LayoutUnit preferredSizeForMarginsAndPadding(Length length, LayoutUnit containerLength)
148 {
149     return length.calcMinValue(containerLength);
150 }
151
152 void RenderFlexibleBox::computePreferredSizeHorizontal(bool relayoutChildren, FlexibleBoxIterator& iterator, LayoutUnit& preferredSize, float& totalPositiveFlexibility, float& totalNegativeFlexibility)
153 {
154     preferredSize = 0;
155     totalPositiveFlexibility = totalNegativeFlexibility = 0;
156
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();
166
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);
171
172         if (child->style()->marginLeft().isAuto())
173             totalPositiveFlexibility += 1;
174         if (child->style()->marginRight().isAuto())
175             totalPositiveFlexibility += 1;
176
177         preferredSize += child->borderLeft() + child->borderRight();
178
179         preferredSize += preferredFlexItemContentWidth(child);
180
181         totalPositiveFlexibility += child->style()->flexboxWidthPositiveFlex();
182         totalNegativeFlexibility += child->style()->flexboxWidthNegativeFlex();
183     }
184 }
185
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)
188 {
189     FlexibleBoxIterator iterator(this);
190     childSizes.clear();
191
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);
198         else {
199             childPreferredSize = preferredFlexItemContentWidth(child);
200             if (availableFreeSpace > 0 && totalPositiveFlexibility > 0) {
201                 childPreferredSize += lroundf(availableFreeSpace * child->style()->flexboxWidthPositiveFlex() / totalPositiveFlexibility);
202
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();
208
209                     inflexibleItems.set(child, childPreferredSize);
210                     return false;
211                 }
212             } else if (availableFreeSpace < 0 && totalNegativeFlexibility > 0) {
213                 childPreferredSize += lroundf(availableFreeSpace * child->style()->flexboxWidthNegativeFlex() / totalNegativeFlexibility);
214
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();
220
221                     inflexibleItems.set(child, childPreferredSize);
222                     return false;
223                 }
224             }
225         }
226         childSizes.append(childPreferredSize);
227     }
228     return true;
229 }
230
231 static bool hasPackingSpace(LayoutUnit availableFreeSpace, float totalPositiveFlexibility)
232 {
233     return availableFreeSpace > 0 && !totalPositiveFlexibility;
234 }
235
236 void RenderFlexibleBox::layoutAndPlaceChildrenHorizontal(const WTF::Vector<LayoutUnit>& childSizes, LayoutUnit availableFreeSpace, float totalPositiveFlexibility)
237 {
238     FlexibleBoxIterator iterator(this);
239     // Now that we know the sizes, layout and position the flex items.
240     LayoutUnit xOffset = borderLeft() + paddingLeft();
241
242     if (hasPackingSpace(availableFreeSpace, totalPositiveFlexibility)) {
243         if (style()->flexPack() == PackEnd)
244             xOffset += availableFreeSpace;
245         else if (style()->flexPack() == PackCenter)
246             xOffset += availableFreeSpace / 2;
247     }
248
249     LayoutUnit yOffset = borderTop() + paddingTop();
250     setHeight(0);
251     size_t i = 0;
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();
258
259         setHeight(std::max(height(), borderTop() + paddingTop() + child->marginTop() + child->height() + child->marginBottom() + paddingBottom() + borderBottom() + horizontalScrollbarHeight()));
260
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);
265
266         xOffset += child->marginLeft();
267         child->setLocation(IntPoint(xOffset, yOffset));
268         xOffset += child->width() + child->marginRight();
269
270         if (hasPackingSpace(availableFreeSpace, totalPositiveFlexibility) && style()->flexPack() == PackJustify && childSizes.size() > 1)
271             xOffset += availableFreeSpace / (childSizes.size() - 1);
272     }
273 }
274
275 }
276
277 #endif // ENABLE(CSS3_FLEXBOX)