Unreviewed, rolling out r104972.
[WebKit-https.git] / 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 #include "LayoutRepainter.h"
35 #include "RenderLayer.h"
36 #include "RenderView.h"
37
38 namespace WebCore {
39
40 // Normally, -1 and 0 are not valid in a HashSet, but these are relatively likely flex-order values. Instead,
41 // we make the two smallest int values invalid flex-order values (in the css parser code we clamp them to
42 // int min + 2).
43 struct FlexOrderHashTraits : WTF::GenericHashTraits<int> {
44     static const bool emptyValueIsZero = false;
45     static int emptyValue() { return std::numeric_limits<int>::min(); }
46     static void constructDeletedValue(int& slot) { slot = std::numeric_limits<int>::min() + 1; }
47     static bool isDeletedValue(int value) { return value == std::numeric_limits<int>::min() + 1; }
48 };
49
50 typedef HashSet<int, DefaultHash<int>::Hash, FlexOrderHashTraits> FlexOrderHashSet;
51
52 class RenderFlexibleBox::TreeOrderIterator {
53 public:
54     explicit TreeOrderIterator(RenderFlexibleBox* flexibleBox)
55         : m_flexibleBox(flexibleBox)
56         , m_currentChild(0)
57     {
58     }
59
60     RenderBox* first()
61     {
62         reset();
63         return next();
64     }
65
66     RenderBox* next()
67     {
68         m_currentChild = m_currentChild ? m_currentChild->nextSiblingBox() : m_flexibleBox->firstChildBox();
69
70         if (m_currentChild)
71             m_flexOrderValues.add(m_currentChild->style()->flexOrder());
72
73         return m_currentChild;
74     }
75
76     void reset()
77     {
78         m_currentChild = 0;
79     }
80
81     const FlexOrderHashSet& flexOrderValues()
82     {
83         return m_flexOrderValues;
84     }
85
86 private:
87     RenderFlexibleBox* m_flexibleBox;
88     RenderBox* m_currentChild;
89     FlexOrderHashSet m_flexOrderValues;
90 };
91
92 class RenderFlexibleBox::FlexOrderIterator {
93 public:
94     FlexOrderIterator(RenderFlexibleBox* flexibleBox, const FlexOrderHashSet& flexOrderValues)
95         : m_flexibleBox(flexibleBox)
96         , m_currentChild(0)
97         , m_orderValuesIterator(0)
98     {
99         copyToVector(flexOrderValues, m_orderValues);
100         std::sort(m_orderValues.begin(), m_orderValues.end());
101     }
102
103     RenderBox* first()
104     {
105         reset();
106         return next();
107     }
108
109     RenderBox* next()
110     {
111         do {
112             if (!m_currentChild) {
113                 if (m_orderValuesIterator == m_orderValues.end())
114                     return 0;
115                 if (m_orderValuesIterator) {
116                     ++m_orderValuesIterator;
117                     if (m_orderValuesIterator == m_orderValues.end())
118                         return 0;
119                 } else
120                     m_orderValuesIterator = m_orderValues.begin();
121
122                 m_currentChild = m_flexibleBox->firstChildBox();
123             } else
124                 m_currentChild = m_currentChild->nextSiblingBox();
125         } while (!m_currentChild || m_currentChild->style()->flexOrder() != *m_orderValuesIterator);
126
127         return m_currentChild;
128     }
129
130     void reset()
131     {
132         m_currentChild = 0;
133         m_orderValuesIterator = 0;
134     }
135
136 private:
137     RenderFlexibleBox* m_flexibleBox;
138     RenderBox* m_currentChild;
139     Vector<int> m_orderValues;
140     Vector<int>::const_iterator m_orderValuesIterator;
141 };
142
143
144 RenderFlexibleBox::RenderFlexibleBox(Node* node)
145     : RenderBlock(node)
146 {
147     setChildrenInline(false); // All of our children must be block-level.
148 }
149
150 RenderFlexibleBox::~RenderFlexibleBox()
151 {
152 }
153
154 const char* RenderFlexibleBox::renderName() const
155 {
156     return "RenderFlexibleBox";
157 }
158
159 void RenderFlexibleBox::layoutBlock(bool relayoutChildren, int, BlockLayoutPass)
160 {
161     ASSERT(needsLayout());
162
163     if (!relayoutChildren && simplifiedLayout())
164         return;
165
166     LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
167     LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode());
168
169     IntSize previousSize = size();
170
171     setLogicalHeight(0);
172     // We need to call both of these because we grab both crossAxisExtent and mainAxisExtent in layoutFlexItems.
173     computeLogicalWidth();
174     computeLogicalHeight();
175
176     m_overflow.clear();
177
178     layoutFlexItems(relayoutChildren);
179
180     LayoutUnit oldClientAfterEdge = clientLogicalBottom();
181     computeLogicalHeight();
182
183     if (size() != previousSize)
184         relayoutChildren = true;
185
186     layoutPositionedObjects(relayoutChildren || isRoot());
187
188     // FIXME: css3/flexbox/repaint-rtl-column.html seems to repaint more overflow than it needs to.
189     computeOverflow(oldClientAfterEdge);
190     statePusher.pop();
191
192     updateLayerTransform();
193
194     repainter.repaintAfterLayout();
195
196     setNeedsLayout(false);
197 }
198
199 bool RenderFlexibleBox::hasOrthogonalFlow(RenderBox* child) const
200 {
201     // FIXME: If the child is a flexbox, then we need to check isHorizontalFlow.
202     return isHorizontalFlow() != child->isHorizontalWritingMode();
203 }
204
205 bool RenderFlexibleBox::isColumnFlow() const
206 {
207     return style()->isColumnFlexDirection();
208 }
209
210 bool RenderFlexibleBox::isHorizontalFlow() const
211 {
212     if (isHorizontalWritingMode())
213         return !isColumnFlow();
214     return isColumnFlow();
215 }
216
217 bool RenderFlexibleBox::isLeftToRightFlow() const
218 {
219     if (isColumnFlow())
220         return style()->writingMode() == TopToBottomWritingMode || style()->writingMode() == LeftToRightWritingMode;
221     return style()->isLeftToRightDirection() ^ (style()->flexDirection() == FlowRowReverse);
222 }
223
224 Length RenderFlexibleBox::mainAxisLengthForChild(RenderBox* child) const
225 {
226     return isHorizontalFlow() ? child->style()->width() : child->style()->height();
227 }
228
229 Length RenderFlexibleBox::crossAxisLength() const
230 {
231     return isHorizontalFlow() ? style()->height() : style()->width();
232 }
233
234 void RenderFlexibleBox::setCrossAxisExtent(LayoutUnit extent)
235 {
236     if (isHorizontalFlow())
237         setHeight(extent);
238     else
239         setWidth(extent);
240 }
241
242 LayoutUnit RenderFlexibleBox::crossAxisExtentForChild(RenderBox* child)
243 {
244     return isHorizontalFlow() ? child->height() : child->width();
245 }
246
247 LayoutUnit RenderFlexibleBox::mainAxisExtentForChild(RenderBox* child)
248 {
249     return isHorizontalFlow() ? child->width() : child->height();
250 }
251
252 LayoutUnit RenderFlexibleBox::crossAxisExtent() const
253 {
254     return isHorizontalFlow() ? height() : width();
255 }
256
257 LayoutUnit RenderFlexibleBox::mainAxisExtent() const
258 {
259     return isHorizontalFlow() ? width() : height();
260 }
261
262 LayoutUnit RenderFlexibleBox::crossAxisContentExtent() const
263 {
264     return isHorizontalFlow() ? contentHeight() : contentWidth();
265 }
266
267 LayoutUnit RenderFlexibleBox::mainAxisContentExtent() const
268 {
269     return isHorizontalFlow() ? contentWidth() : contentHeight();
270 }
271
272 WritingMode RenderFlexibleBox::transformedWritingMode() const
273 {
274     WritingMode mode = style()->writingMode();
275     if (!isColumnFlow())
276         return mode;
277
278     switch (mode) {
279     case TopToBottomWritingMode:
280     case BottomToTopWritingMode:
281         return style()->isLeftToRightDirection() ? LeftToRightWritingMode : RightToLeftWritingMode;
282     case LeftToRightWritingMode:
283     case RightToLeftWritingMode:
284         return style()->isLeftToRightDirection() ? TopToBottomWritingMode : BottomToTopWritingMode;
285     }
286     ASSERT_NOT_REACHED();
287     return TopToBottomWritingMode;
288 }
289
290 LayoutUnit RenderFlexibleBox::flowAwareBorderStart() const
291 {
292     if (isHorizontalFlow())
293         return isLeftToRightFlow() ? borderLeft() : borderRight();
294     return isLeftToRightFlow() ? borderTop() : borderBottom();
295 }
296
297 LayoutUnit RenderFlexibleBox::flowAwareBorderEnd() const
298 {
299     if (isHorizontalFlow())
300         return isLeftToRightFlow() ? borderRight() : borderLeft();
301     return isLeftToRightFlow() ? borderBottom() : borderTop();
302 }
303
304 LayoutUnit RenderFlexibleBox::flowAwareBorderBefore() const
305 {
306     switch (transformedWritingMode()) {
307     case TopToBottomWritingMode:
308         return borderTop();
309     case BottomToTopWritingMode:
310         return borderBottom();
311     case LeftToRightWritingMode:
312         return borderLeft();
313     case RightToLeftWritingMode:
314         return borderRight();
315     }
316     ASSERT_NOT_REACHED();
317     return borderTop();
318 }
319
320 LayoutUnit RenderFlexibleBox::crossAxisBorderAndPaddingExtent() const
321 {
322     return isHorizontalFlow() ? borderAndPaddingHeight() : borderAndPaddingWidth();
323 }
324
325 LayoutUnit RenderFlexibleBox::flowAwarePaddingStart() const
326 {
327     if (isHorizontalFlow())
328         return isLeftToRightFlow() ? paddingLeft() : paddingRight();
329     return isLeftToRightFlow() ? paddingTop() : paddingBottom();
330 }
331
332 LayoutUnit RenderFlexibleBox::flowAwarePaddingEnd() const
333 {
334     if (isHorizontalFlow())
335         return isLeftToRightFlow() ? paddingRight() : paddingLeft();
336     return isLeftToRightFlow() ? paddingBottom() : paddingTop();
337 }
338
339 LayoutUnit RenderFlexibleBox::flowAwarePaddingBefore() const
340 {
341     switch (transformedWritingMode()) {
342     case TopToBottomWritingMode:
343         return paddingTop();
344     case BottomToTopWritingMode:
345         return paddingBottom();
346     case LeftToRightWritingMode:
347         return paddingLeft();
348     case RightToLeftWritingMode:
349         return paddingRight();
350     }
351     ASSERT_NOT_REACHED();
352     return paddingTop();
353 }
354
355 LayoutUnit RenderFlexibleBox::flowAwareMarginStartForChild(RenderBox* child) const
356 {
357     if (isHorizontalFlow())
358         return isLeftToRightFlow() ? child->marginLeft() : child->marginRight();
359     return isLeftToRightFlow() ? child->marginTop() : child->marginBottom();
360 }
361
362 LayoutUnit RenderFlexibleBox::flowAwareMarginEndForChild(RenderBox* child) const
363 {
364     if (isHorizontalFlow())
365         return isLeftToRightFlow() ? child->marginRight() : child->marginLeft();
366     return isLeftToRightFlow() ? child->marginBottom() : child->marginTop();
367 }
368
369 LayoutUnit RenderFlexibleBox::flowAwareMarginBeforeForChild(RenderBox* child) const
370 {
371     switch (transformedWritingMode()) {
372     case TopToBottomWritingMode:
373         return child->marginTop();
374     case BottomToTopWritingMode:
375         return child->marginBottom();
376     case LeftToRightWritingMode:
377         return child->marginLeft();
378     case RightToLeftWritingMode:
379         return child->marginRight();
380     }
381     ASSERT_NOT_REACHED();
382     return marginTop();
383 }
384
385 LayoutUnit RenderFlexibleBox::flowAwareMarginAfterForChild(RenderBox* child) const
386 {
387     switch (transformedWritingMode()) {
388     case TopToBottomWritingMode:
389         return child->marginBottom();
390     case BottomToTopWritingMode:
391         return child->marginTop();
392     case LeftToRightWritingMode:
393         return child->marginRight();
394     case RightToLeftWritingMode:
395         return child->marginLeft();
396     }
397     ASSERT_NOT_REACHED();
398     return marginBottom();
399 }
400
401 LayoutUnit RenderFlexibleBox::crossAxisMarginExtentForChild(RenderBox* child) const
402 {
403     return isHorizontalFlow() ? child->marginTop() + child->marginBottom() : child->marginLeft() + child->marginRight();
404 }
405
406 LayoutPoint RenderFlexibleBox::flowAwareLocationForChild(RenderBox* child) const
407 {
408     return isHorizontalFlow() ? child->location() : child->location().transposedPoint();
409 }
410
411 void RenderFlexibleBox::setFlowAwareLocationForChild(RenderBox* child, const LayoutPoint& location)
412 {
413     if (isHorizontalFlow())
414         child->setLocation(location);
415     else
416         child->setLocation(location.transposedPoint());
417 }
418
419 LayoutUnit RenderFlexibleBox::mainAxisBorderAndPaddingExtentForChild(RenderBox* child) const
420 {
421     return isHorizontalFlow() ? child->borderAndPaddingWidth() : child->borderAndPaddingHeight();
422 }
423
424 LayoutUnit RenderFlexibleBox::mainAxisScrollbarExtentForChild(RenderBox* child) const
425 {
426     return isHorizontalFlow() ? child->verticalScrollbarWidth() : child->horizontalScrollbarHeight();
427 }
428
429 LayoutUnit RenderFlexibleBox::preferredMainAxisContentExtentForFlexItem(RenderBox* child) const
430 {
431     Length mainAxisLength = mainAxisLengthForChild(child);
432     if (mainAxisLength.isAuto()) {
433         LayoutUnit mainAxisExtent = hasOrthogonalFlow(child) ? child->logicalHeight() : child->maxPreferredLogicalWidth();
434         return mainAxisExtent - mainAxisBorderAndPaddingExtentForChild(child) - mainAxisScrollbarExtentForChild(child);
435     }
436     return mainAxisLength.calcMinValue(mainAxisContentExtent());
437 }
438
439 void RenderFlexibleBox::layoutFlexItems(bool relayoutChildren)
440 {
441     LayoutUnit preferredMainAxisExtent;
442     float totalPositiveFlexibility;
443     float totalNegativeFlexibility;
444     TreeOrderIterator treeIterator(this);
445
446     computePreferredMainAxisExtent(relayoutChildren, treeIterator, preferredMainAxisExtent, totalPositiveFlexibility, totalNegativeFlexibility);
447     LayoutUnit availableFreeSpace = mainAxisContentExtent() - preferredMainAxisExtent;
448
449     FlexOrderIterator flexIterator(this, treeIterator.flexOrderValues());
450     InflexibleFlexItemSize inflexibleItems;
451     WTF::Vector<LayoutUnit> childSizes;
452     while (!runFreeSpaceAllocationAlgorithm(flexIterator, availableFreeSpace, totalPositiveFlexibility, totalNegativeFlexibility, inflexibleItems, childSizes)) {
453         ASSERT(totalPositiveFlexibility >= 0 && totalNegativeFlexibility >= 0);
454         ASSERT(inflexibleItems.size() > 0);
455     }
456
457     layoutAndPlaceChildren(flexIterator, childSizes, availableFreeSpace, totalPositiveFlexibility);
458 }
459
460 float RenderFlexibleBox::positiveFlexForChild(RenderBox* child) const
461 {
462     return isHorizontalFlow() ? child->style()->flexboxWidthPositiveFlex() : child->style()->flexboxHeightPositiveFlex();
463 }
464
465 float RenderFlexibleBox::negativeFlexForChild(RenderBox* child) const
466 {
467     return isHorizontalFlow() ? child->style()->flexboxWidthNegativeFlex() : child->style()->flexboxHeightNegativeFlex();
468 }
469
470 LayoutUnit RenderFlexibleBox::availableAlignmentSpaceForChild(RenderBox* child)
471 {
472     LayoutUnit crossContentExtent = crossAxisContentExtent();
473     LayoutUnit childCrossExtent = crossAxisMarginExtentForChild(child) + crossAxisExtentForChild(child);
474     return crossContentExtent - childCrossExtent;
475 }
476
477 LayoutUnit RenderFlexibleBox::marginBoxAscent(RenderBox* child)
478 {
479     LayoutUnit ascent = child->firstLineBoxBaseline();
480     if (ascent == -1)
481         ascent = crossAxisExtentForChild(child) + flowAwareMarginAfterForChild(child);
482     return ascent + flowAwareMarginBeforeForChild(child);
483 }
484
485 void RenderFlexibleBox::computePreferredMainAxisExtent(bool relayoutChildren, TreeOrderIterator& iterator, LayoutUnit& preferredMainAxisExtent, float& totalPositiveFlexibility, float& totalNegativeFlexibility)
486 {
487     preferredMainAxisExtent = 0;
488     totalPositiveFlexibility = totalNegativeFlexibility = 0;
489
490     LayoutUnit flexboxAvailableContentExtent = mainAxisContentExtent();
491     for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
492         if (child->isPositioned())
493             continue;
494
495         child->clearOverrideSize();
496         if (mainAxisLengthForChild(child).isAuto()) {
497             if (!relayoutChildren)
498                 child->setChildNeedsLayout(true);
499             child->layoutIfNeeded();
500         }
501
502         // We set the margins because we want to make sure 'auto' has a margin
503         // of 0 and because if we're not auto sizing, we don't do a layout that
504         // computes the start/end margins.
505         if (isHorizontalFlow()) {
506             child->setMarginLeft(child->style()->marginLeft().calcMinValue(flexboxAvailableContentExtent));
507             child->setMarginRight(child->style()->marginRight().calcMinValue(flexboxAvailableContentExtent));
508             preferredMainAxisExtent += child->marginLeft() + child->marginRight();
509         } else {
510             child->setMarginTop(child->style()->marginTop().calcMinValue(flexboxAvailableContentExtent));
511             child->setMarginBottom(child->style()->marginBottom().calcMinValue(flexboxAvailableContentExtent));
512             preferredMainAxisExtent += child->marginTop() + child->marginBottom();
513         }
514
515         preferredMainAxisExtent += mainAxisBorderAndPaddingExtentForChild(child);
516         preferredMainAxisExtent += preferredMainAxisContentExtentForFlexItem(child);
517
518         totalPositiveFlexibility += positiveFlexForChild(child);
519         totalNegativeFlexibility += negativeFlexForChild(child);
520     }
521 }
522
523 // Returns true if we successfully ran the algorithm and sized the flex items.
524 bool RenderFlexibleBox::runFreeSpaceAllocationAlgorithm(FlexOrderIterator& iterator, LayoutUnit& availableFreeSpace, float& totalPositiveFlexibility, float& totalNegativeFlexibility, InflexibleFlexItemSize& inflexibleItems, WTF::Vector<LayoutUnit>& childSizes)
525 {
526     childSizes.clear();
527
528     LayoutUnit flexboxAvailableContentExtent = mainAxisContentExtent();
529     for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
530         if (child->isPositioned()) {
531             childSizes.append(0);
532             continue;
533         }
534
535         LayoutUnit childPreferredSize;
536         if (inflexibleItems.contains(child))
537             childPreferredSize = inflexibleItems.get(child);
538         else {
539             childPreferredSize = preferredMainAxisContentExtentForFlexItem(child);
540             if (availableFreeSpace > 0 && totalPositiveFlexibility > 0) {
541                 childPreferredSize += lroundf(availableFreeSpace * positiveFlexForChild(child) / totalPositiveFlexibility);
542
543                 Length childLogicalMaxWidth = isHorizontalFlow() ? child->style()->maxWidth() : child->style()->maxHeight();
544                 if (!childLogicalMaxWidth.isUndefined() && childLogicalMaxWidth.isSpecified() && childPreferredSize > childLogicalMaxWidth.calcValue(flexboxAvailableContentExtent)) {
545                     childPreferredSize = childLogicalMaxWidth.calcValue(flexboxAvailableContentExtent);
546                     availableFreeSpace -= childPreferredSize - preferredMainAxisContentExtentForFlexItem(child);
547                     totalPositiveFlexibility -= positiveFlexForChild(child);
548
549                     inflexibleItems.set(child, childPreferredSize);
550                     return false;
551                 }
552             } else if (availableFreeSpace < 0 && totalNegativeFlexibility > 0) {
553                 childPreferredSize += lroundf(availableFreeSpace * negativeFlexForChild(child) / totalNegativeFlexibility);
554
555                 Length childLogicalMinWidth = isHorizontalFlow() ? child->style()->minWidth() : child->style()->minHeight();
556                 if (!childLogicalMinWidth.isUndefined() && childLogicalMinWidth.isSpecified() && childPreferredSize < childLogicalMinWidth.calcValue(flexboxAvailableContentExtent)) {
557                     childPreferredSize = childLogicalMinWidth.calcValue(flexboxAvailableContentExtent);
558                     availableFreeSpace += preferredMainAxisContentExtentForFlexItem(child) - childPreferredSize;
559                     totalNegativeFlexibility -= negativeFlexForChild(child);
560
561                     inflexibleItems.set(child, childPreferredSize);
562                     return false;
563                 }
564             }
565         }
566         childSizes.append(childPreferredSize);
567     }
568     return true;
569 }
570
571 static bool hasPackingSpace(LayoutUnit availableFreeSpace, float totalPositiveFlexibility)
572 {
573     return availableFreeSpace > 0 && !totalPositiveFlexibility;
574 }
575
576 static LayoutUnit initialPackingOffset(LayoutUnit availableFreeSpace, float totalPositiveFlexibility, EFlexPack flexPack)
577 {
578     if (hasPackingSpace(availableFreeSpace, totalPositiveFlexibility)) {
579         if (flexPack == PackEnd)
580             return availableFreeSpace;
581         if (flexPack == PackCenter)
582             return availableFreeSpace / 2;
583     }
584     return 0;
585 }
586
587 static LayoutUnit packingSpaceBetweenChildren(LayoutUnit availableFreeSpace, float totalPositiveFlexibility, EFlexPack flexPack, size_t numberOfChildren)
588 {
589     if (hasPackingSpace(availableFreeSpace, totalPositiveFlexibility) && flexPack == PackJustify && numberOfChildren > 1)
590         return availableFreeSpace / (numberOfChildren - 1);
591     return 0;
592 }
593
594 void RenderFlexibleBox::setLogicalOverrideSize(RenderBox* child, LayoutUnit childPreferredSize)
595 {
596     // FIXME: Rename setOverrideWidth/setOverrideHeight to setOverrideLogicalWidth/setOverrideLogicalHeight.
597     if (hasOrthogonalFlow(child))
598         child->setOverrideHeight(childPreferredSize);
599     else
600         child->setOverrideWidth(childPreferredSize);
601 }
602
603 void RenderFlexibleBox::prepareChildForPositionedLayout(RenderBox* child, LayoutUnit mainAxisOffset, LayoutUnit crossAxisOffset)
604 {
605     ASSERT(child->isPositioned());
606     child->containingBlock()->insertPositionedObject(child);
607     RenderLayer* childLayer = child->layer();
608     LayoutUnit inlinePosition = isColumnFlow() ? crossAxisOffset : mainAxisOffset;
609     if (style()->flexDirection() == FlowRowReverse)
610         inlinePosition = mainAxisExtent() - mainAxisOffset;
611     childLayer->setStaticInlinePosition(inlinePosition); // FIXME: Not right for regions.
612
613     LayoutUnit staticBlockPosition = isColumnFlow() ? mainAxisOffset : crossAxisOffset;
614     if (childLayer->staticBlockPosition() != staticBlockPosition) {
615         childLayer->setStaticBlockPosition(staticBlockPosition);
616         if (child->style()->hasStaticBlockPosition(style()->isHorizontalWritingMode()))
617             child->setChildNeedsLayout(true, false);
618     }
619 }
620
621 void RenderFlexibleBox::layoutAndPlaceChildren(FlexOrderIterator& iterator, const WTF::Vector<LayoutUnit>& childSizes, LayoutUnit availableFreeSpace, float totalPositiveFlexibility)
622 {
623     LayoutUnit mainAxisOffset = flowAwareBorderStart() + flowAwarePaddingStart();
624     mainAxisOffset += initialPackingOffset(availableFreeSpace, totalPositiveFlexibility, style()->flexPack());
625
626     LayoutUnit crossAxisOffset = flowAwareBorderBefore() + flowAwarePaddingBefore();
627     LayoutUnit totalMainExtent = mainAxisExtent();
628     LayoutUnit maxAscent = 0, maxDescent = 0; // Used when flex-align: baseline.
629     bool shouldFlipMainAxis = !isColumnFlow() && !isLeftToRightFlow();
630     size_t i = 0;
631     for (RenderBox* child = iterator.first(); child; child = iterator.next(), ++i) {
632         if (child->isPositioned()) {
633             prepareChildForPositionedLayout(child, mainAxisOffset, crossAxisOffset);
634             mainAxisOffset += packingSpaceBetweenChildren(availableFreeSpace, totalPositiveFlexibility, style()->flexPack(), childSizes.size());
635             continue;
636         }
637         LayoutUnit childPreferredSize = childSizes[i] + mainAxisBorderAndPaddingExtentForChild(child);
638         setLogicalOverrideSize(child, childPreferredSize);
639         child->setChildNeedsLayout(true);
640         child->layoutIfNeeded();
641
642         if (child->style()->flexItemAlign() == AlignBaseline) {
643             LayoutUnit ascent = marginBoxAscent(child);
644             LayoutUnit descent = (crossAxisMarginExtentForChild(child) + crossAxisExtentForChild(child)) - ascent;
645
646             maxAscent = std::max(maxAscent, ascent);
647             maxDescent = std::max(maxDescent, descent);
648
649             // FIXME: add flowAwareScrollbarLogicalHeight.
650             if (crossAxisLength().isAuto())
651                 setCrossAxisExtent(std::max(crossAxisExtent(), crossAxisBorderAndPaddingExtent() + crossAxisMarginExtentForChild(child) + maxAscent + maxDescent + scrollbarLogicalHeight()));
652         } else if (crossAxisLength().isAuto())
653             setCrossAxisExtent(std::max(crossAxisExtent(), crossAxisBorderAndPaddingExtent() + crossAxisMarginExtentForChild(child) + crossAxisExtentForChild(child) + scrollbarLogicalHeight()));
654
655         mainAxisOffset += flowAwareMarginStartForChild(child);
656
657         LayoutUnit childMainExtent = mainAxisExtentForChild(child);
658         IntPoint childLocation(shouldFlipMainAxis ? totalMainExtent - mainAxisOffset - childMainExtent : mainAxisOffset,
659             crossAxisOffset + flowAwareMarginBeforeForChild(child));
660
661         // FIXME: Supporting layout deltas.
662         setFlowAwareLocationForChild(child, childLocation);
663         mainAxisOffset += childMainExtent + flowAwareMarginEndForChild(child);
664
665         mainAxisOffset += packingSpaceBetweenChildren(availableFreeSpace, totalPositiveFlexibility, style()->flexPack(), childSizes.size());
666
667         if (isColumnFlow())
668             setLogicalHeight(mainAxisOffset);
669     }
670
671     if (style()->flexDirection() == FlowColumnReverse) {
672         // We have to do an extra pass for column-reverse to reposition the flex items since the start depends
673         // on the height of the flexbox, which we only know after we've positioned all the flex items.
674         computeLogicalHeight();
675         layoutColumnReverse(iterator, childSizes, availableFreeSpace, totalPositiveFlexibility);
676     }
677
678     alignChildren(iterator, maxAscent);
679 }
680
681 void RenderFlexibleBox::layoutColumnReverse(FlexOrderIterator& iterator, const WTF::Vector<LayoutUnit>& childSizes, LayoutUnit availableFreeSpace, float totalPositiveFlexibility)
682 {
683     // This is similar to the logic in layoutAndPlaceChildren, except we place the children
684     // starting from the end of the flexbox. We also don't need to layout anything since we're
685     // just moving the children to a new position.
686     LayoutUnit mainAxisOffset = logicalHeight() - flowAwareBorderEnd() - flowAwarePaddingEnd();
687     mainAxisOffset -= initialPackingOffset(availableFreeSpace, totalPositiveFlexibility, style()->flexPack());
688
689     LayoutUnit crossAxisOffset = flowAwareBorderBefore() + flowAwarePaddingBefore();
690     size_t i = 0;
691     for (RenderBox* child = iterator.first(); child; child = iterator.next(), ++i) {
692         if (child->isPositioned()) {
693             child->layer()->setStaticBlockPosition(mainAxisOffset);
694             mainAxisOffset -= packingSpaceBetweenChildren(availableFreeSpace, totalPositiveFlexibility, style()->flexPack(), childSizes.size());
695             continue;
696         }
697         mainAxisOffset -= mainAxisExtentForChild(child) + flowAwareMarginEndForChild(child);
698
699         LayoutRect oldRect = child->frameRect();
700         setFlowAwareLocationForChild(child, IntPoint(mainAxisOffset, crossAxisOffset + flowAwareMarginBeforeForChild(child)));
701         if (!selfNeedsLayout() && child->checkForRepaintDuringLayout())
702             child->repaintDuringLayoutIfMoved(oldRect);
703
704         mainAxisOffset -= flowAwareMarginStartForChild(child);
705         mainAxisOffset -= packingSpaceBetweenChildren(availableFreeSpace, totalPositiveFlexibility, style()->flexPack(), childSizes.size());
706     }
707 }
708
709 void RenderFlexibleBox::adjustAlignmentForChild(RenderBox* child, LayoutUnit delta)
710 {
711     LayoutRect oldRect = child->frameRect();
712
713     setFlowAwareLocationForChild(child, flowAwareLocationForChild(child) + LayoutSize(0, delta));
714
715     // If the child moved, we have to repaint it as well as any floating/positioned
716     // descendants. An exception is if we need a layout. In this case, we know we're going to
717     // repaint ourselves (and the child) anyway.
718     if (!selfNeedsLayout() && child->checkForRepaintDuringLayout())
719         child->repaintDuringLayoutIfMoved(oldRect);
720 }
721
722 void RenderFlexibleBox::alignChildren(FlexOrderIterator& iterator, LayoutUnit maxAscent)
723 {
724     LayoutUnit crossExtent = crossAxisExtent();
725
726     for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
727         // direction:rtl + flex-direction:column means the cross-axis direction is flipped.
728         if (!style()->isLeftToRightDirection() && isColumnFlow()) {
729             LayoutPoint location = flowAwareLocationForChild(child);
730             location.setY(crossExtent - crossAxisExtentForChild(child) - location.y());
731             setFlowAwareLocationForChild(child, location);
732         }
733
734         // FIXME: Make sure this does the right thing with column flows.
735         switch (child->style()->flexItemAlign()) {
736         case AlignAuto:
737             // FIXME: Handle this once we add flex-align.
738             break;
739         case AlignStretch: {
740             if (!isColumnFlow() && child->style()->logicalHeight().isAuto()) {
741                 LayoutUnit logicalHeightBefore = child->logicalHeight();
742                 LayoutUnit stretchedLogicalHeight = child->logicalHeight() + RenderFlexibleBox::availableAlignmentSpaceForChild(child);
743                 child->setLogicalHeight(stretchedLogicalHeight);
744                 child->computeLogicalHeight();
745
746                 if (child->logicalHeight() != logicalHeightBefore) {
747                     child->setOverrideHeight(child->logicalHeight());
748                     child->setLogicalHeight(0);
749                     child->setChildNeedsLayout(true);
750                     child->layoutIfNeeded();
751                 }
752             }
753             break;
754         }
755         case AlignStart:
756             break;
757         case AlignEnd:
758             adjustAlignmentForChild(child, RenderFlexibleBox::availableAlignmentSpaceForChild(child));
759             break;
760         case AlignCenter:
761             adjustAlignmentForChild(child, RenderFlexibleBox::availableAlignmentSpaceForChild(child) / 2);
762             break;
763         case AlignBaseline: {
764             LayoutUnit ascent = marginBoxAscent(child);
765             adjustAlignmentForChild(child, maxAscent - ascent);
766             break;
767         }
768         }
769     }
770 }
771
772 }