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