efc4de319ed992707fcb565ba027639ff170deb5
[WebKit-https.git] / WebCore / khtml / rendering / render_flexbox.cpp
1 /*
2  * This file is part of the render object implementation for KHTML.
3  *
4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
6  * Copyright (C) 2003 Apple Computer, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  *
23  */
24
25 #include "render_flexbox.h"
26
27 using namespace DOM;
28
29 namespace khtml {
30
31 class FlexBoxIterator {
32 public:
33     FlexBoxIterator(RenderFlexibleBox* parent) {
34         box = parent;
35         if (box->style()->boxOrient() == HORIZONTAL && box->style()->direction() == RTL)
36             forward = box->style()->boxDirection() != BNORMAL;
37         else
38             forward = box->style()->boxDirection() == BNORMAL;
39         lastOrdinal = 1; 
40         if (!forward) {
41             // No choice, since we're going backwards, we have to find out the highest ordinal up front.
42             RenderObject* child = box->firstChild();
43             while (child) {
44                 if (child->style()->boxOrdinalGroup() > lastOrdinal)
45                     lastOrdinal = child->style()->boxOrdinalGroup();
46                 child = child->nextSibling();
47             }
48         }
49         
50         reset();
51     }
52
53     void reset() {
54         current = 0;
55         currentOrdinal = forward ? 0 : lastOrdinal+1;
56     }
57
58     RenderObject* first() {
59         reset();
60         return next();
61     }
62     
63     RenderObject* next() {
64
65         do { 
66             if (!current) {
67                 if (forward) {
68                     currentOrdinal++; 
69                     if (currentOrdinal > lastOrdinal)
70                         return 0;
71                     current = box->firstChild();
72                 }
73                 else {
74                     currentOrdinal--;
75                     if (currentOrdinal == 0)
76                         return 0;
77                     current = box->lastChild();
78                 }
79             }
80             else
81                 current = forward ? current->nextSibling() : current->previousSibling();
82             if (current && current->style()->boxOrdinalGroup() > lastOrdinal)
83                 lastOrdinal = current->style()->boxOrdinalGroup();
84         } while (!current || current->style()->boxOrdinalGroup() != currentOrdinal ||
85                  current->style()->visibility() == COLLAPSE);
86         return current;
87     }
88
89 private:
90     RenderFlexibleBox* box;
91     RenderObject* current;
92     bool forward;
93     unsigned int currentOrdinal;
94     unsigned int lastOrdinal;
95 };
96     
97 RenderFlexibleBox::RenderFlexibleBox(DOM::NodeImpl* node)
98 :RenderBlock(node)
99 {
100     setChildrenInline(false); // All of our children must be block-level
101     m_flexingChildren = m_stretchingChildren = false;
102 }
103
104 RenderFlexibleBox::~RenderFlexibleBox()
105 {
106 }
107
108 void RenderFlexibleBox::calcHorizontalMinMaxWidth()
109 {
110     RenderObject *child = firstChild();
111     while (child) {
112         // positioned children don't affect the minmaxwidth
113         if (child->isPositioned() || child->style()->visibility() == COLLAPSE)
114         {
115             child = child->nextSibling();
116             continue;
117         }
118
119         int margin=0;
120         //  auto margins don't affect minwidth
121
122         Length ml = child->style()->marginLeft();
123         Length mr = child->style()->marginRight();
124
125         // Call calcWidth on the child to ensure that our margins are
126         // up to date.  This method can be called before the child has actually
127         // calculated its margins (which are computed inside calcWidth).
128         child->calcWidth();
129
130         if (!(ml.type==Variable) && !(mr.type==Variable))
131         {
132             if (!(child->style()->width().type==Variable))
133             {
134                 if (child->style()->direction()==LTR)
135                     margin = child->marginLeft();
136                 else
137                     margin = child->marginRight();
138             }
139             else
140                 margin = child->marginLeft()+child->marginRight();
141
142         }
143         else if (!(ml.type == Variable))
144             margin = child->marginLeft();
145         else if (!(mr.type == Variable))
146             margin = child->marginRight();
147
148         if (margin < 0) margin = 0;
149
150         m_minWidth += child->minWidth() + margin;
151         m_maxWidth += child->maxWidth() + margin;
152
153         child = child->nextSibling();
154     }    
155 }
156
157 void RenderFlexibleBox::calcVerticalMinMaxWidth()
158 {
159     RenderObject *child = firstChild();
160     while(child != 0)
161     {
162         // Positioned children and collapsed children don't affect the min/max width
163         if (child->isPositioned() || child->style()->visibility() == COLLAPSE) {
164             child = child->nextSibling();
165             continue;
166         }
167
168         Length ml = child->style()->marginLeft();
169         Length mr = child->style()->marginRight();
170
171         // Call calcWidth on the child to ensure that our margins are
172         // up to date.  This method can be called before the child has actually
173         // calculated its margins (which are computed inside calcWidth).
174         if (ml.type == Percent || mr.type == Percent)
175             calcWidth();
176
177         // A margin basically has three types: fixed, percentage, and auto (variable).
178         // Auto margins simply become 0 when computing min/max width.
179         // Fixed margins can be added in as is.
180         // Percentage margins are computed as a percentage of the width we calculated in
181         // the calcWidth call above.  In this case we use the actual cached margin values on
182         // the RenderObject itself.
183         int margin = 0;
184         if (ml.type == Fixed)
185             margin += ml.value;
186         else if (ml.type == Percent)
187             margin += child->marginLeft();
188
189         if (mr.type == Fixed)
190             margin += mr.value;
191         else if (mr.type == Percent)
192             margin += child->marginRight();
193
194         if (margin < 0) margin = 0;
195         
196         int w = child->minWidth() + margin;
197         if(m_minWidth < w) m_minWidth = w;
198         
199         w = child->maxWidth() + margin;
200
201         if(m_maxWidth < w) m_maxWidth = w;
202
203         child = child->nextSibling();
204     }    
205 }
206
207 void RenderFlexibleBox::calcMinMaxWidth()
208 {
209     KHTMLAssert( !minMaxKnown() );
210
211     m_minWidth = 0;
212     m_maxWidth = 0;
213
214     if (hasMultipleLines() || isVertical())
215         calcVerticalMinMaxWidth();
216     else
217         calcHorizontalMinMaxWidth();
218
219     if(m_maxWidth < m_minWidth) m_maxWidth = m_minWidth;
220
221     if (style()->width().isFixed() && style()->width().value > 0)
222         m_minWidth = m_maxWidth = style()->width().value;
223    
224     if (style()->minWidth().isFixed() && style()->minWidth().value > 0) {
225         m_maxWidth = KMAX(m_maxWidth, style()->minWidth().value);
226         m_minWidth = KMAX(m_minWidth, style()->minWidth().value);
227     }
228     
229     if (style()->maxWidth().isFixed() && style()->maxWidth().value != UNDEFINED) {
230         m_maxWidth = KMIN(m_maxWidth, style()->maxWidth().value);
231         m_minWidth = KMIN(m_minWidth, style()->maxWidth().value);
232     }
233
234     int toAdd = borderLeft() + borderRight() + paddingLeft() + paddingRight();
235     m_minWidth += toAdd;
236     m_maxWidth += toAdd;
237
238     setMinMaxKnown();
239 }
240
241 void RenderFlexibleBox::layoutBlock(bool relayoutChildren)
242 {
243     KHTMLAssert(needsLayout());
244     KHTMLAssert(minMaxKnown());
245
246     if (!relayoutChildren && posChildNeedsLayout() && !normalChildNeedsLayout() && !selfNeedsLayout()) {
247         // All we have to is lay out our positioned objects.
248         layoutPositionedObjects(relayoutChildren);
249         if (hasOverflowClip())
250             m_layer->updateScrollInfoAfterLayout();
251         setNeedsLayout(false);
252         return;
253     }
254
255     QRect oldBounds;
256     bool checkForRepaint = checkForRepaintDuringLayout();
257     if (checkForRepaint)
258         oldBounds = getAbsoluteRepaintRect();
259     
260     int oldWidth = m_width;
261     int oldHeight = m_height;
262     
263     calcWidth();
264     calcHeight();
265     m_overflowWidth = m_width;
266
267     if (oldWidth != m_width || oldHeight != m_height || parent()->isFlexingChildren())
268         relayoutChildren = true;
269
270     m_height = 0;
271     m_overflowHeight = 0;
272     m_flexingChildren = m_stretchingChildren = false;
273
274     initMaxMarginValues();
275
276     if (scrollsOverflow()) {
277         // For overflow:scroll blocks, ensure we have both scrollbars in place always.
278         if (style()->overflow() == OSCROLL) {
279             m_layer->setHasHorizontalScrollbar(true);
280             m_layer->setHasVerticalScrollbar(true);
281         }
282
283         // Move the scrollbars aside during layout.  The layer will move them back when it
284         // does painting or event handling.
285         m_layer->moveScrollbarsAside();
286     }
287
288     if (isHorizontal())
289         layoutHorizontalBox(relayoutChildren);
290     else
291         layoutVerticalBox(relayoutChildren);
292     
293     oldHeight = m_height;
294     calcHeight();
295     if (oldHeight != m_height) {
296         relayoutChildren = true;
297
298         // If the block got expanded in size, then increase our overflowheight to match.
299         if (m_overflowHeight > m_height)
300             m_overflowHeight -= (borderBottom()+paddingBottom());
301         if (m_overflowHeight < m_height)
302             m_overflowHeight = m_height;
303     }
304
305     layoutPositionedObjects( relayoutChildren );
306
307     //kdDebug() << renderName() << " layout width=" << m_width << " height=" << m_height << endl;
308
309     if (!isFloatingOrPositioned() && m_height == 0) {
310         // We are a block with no border and padding and a computed height
311         // of 0.  The CSS spec states that zero-height blocks collapse their margins
312         // together.
313         // When blocks are self-collapsing, we just use the top margin values and set the
314         // bottom margin max values to 0.  This way we don't factor in the values
315         // twice when we collapse with our previous vertically adjacent and
316         // following vertically adjacent blocks.
317         if (m_maxBottomPosMargin > m_maxTopPosMargin)
318             m_maxTopPosMargin = m_maxBottomPosMargin;
319         if (m_maxBottomNegMargin > m_maxTopNegMargin)
320             m_maxTopNegMargin = m_maxBottomNegMargin;
321         m_maxBottomNegMargin = m_maxBottomPosMargin = 0;
322     }
323
324     // Always ensure our overflow width is at least as large as our width.
325     if (m_overflowWidth < m_width)
326         m_overflowWidth = m_width;
327
328     // Update our scrollbars if we're overflow:auto/scroll/hidden now that we know if
329     // we overflow or not.
330     if (hasOverflowClip())
331         m_layer->updateScrollInfoAfterLayout();
332
333     // Repaint with our new bounds if they are different from our old bounds.
334     if (checkForRepaint)
335         repaintAfterLayoutIfNeeded(oldBounds, oldBounds);
336     
337     setNeedsLayout(false);
338 }
339
340 void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren)
341 {
342     int toAdd = borderBottom() + paddingBottom();
343     int yPos = borderTop() + paddingTop();
344     int xPos = borderLeft() + paddingLeft();
345     bool heightSpecified = false;
346     int oldHeight = 0;
347     
348     unsigned int highestFlexGroup = 0;
349     unsigned int lowestFlexGroup = 0;
350     bool haveFlex = false;
351     int remainingSpace = 0;
352     m_overflowHeight = m_height;
353
354     // The first walk over our kids is to find out if we have any flexible children.
355     FlexBoxIterator iterator(this);
356     RenderObject* child = iterator.next();
357     while (child) {
358         // Check to see if this child flexes.
359         if (!child->isPositioned() && child->style()->boxFlex() > 0.0f) {
360             // We always have to lay out flexible objects again, since the flex distribution
361             // may have changed, and we need to reallocate space.
362             if (!relayoutChildren)
363                 child->setChildNeedsLayout(true);
364             haveFlex = true;
365             unsigned int flexGroup = child->style()->boxFlexGroup();
366             if (lowestFlexGroup == 0)
367                 lowestFlexGroup = flexGroup;
368             if (flexGroup < lowestFlexGroup)
369                 lowestFlexGroup = flexGroup;
370             if (flexGroup > highestFlexGroup)
371                 highestFlexGroup = flexGroup;
372         }
373         child = iterator.next();
374         continue;
375     }
376     
377     // We do 2 passes.  The first pass is simply to lay everyone out at
378     // their preferred widths.  The second pass handles flexing the children.
379     do {
380         // Reset our height.
381         m_height = yPos;
382         m_overflowHeight = m_height;
383         xPos = borderLeft() + paddingLeft();
384                 
385         // Our first pass is done without flexing.  We simply lay the children
386         // out within the box.  We have to do a layout first in order to determine
387         // our box's intrinsic height.
388         child = iterator.first();
389         while (child) {
390             // make sure we relayout children if we need it.
391             if ( relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())))
392                 child->setChildNeedsLayout(true);
393             
394             if (child->isPositioned()) {
395                 child = iterator.next();
396                 continue;
397             }
398     
399             // Compute the child's vertical margins.
400             child->calcVerticalMargins();
401     
402             // Now do the layout.
403             child->layoutIfNeeded();
404     
405             // Update our height and overflow height.
406             m_height = QMAX(m_height, yPos+child->marginTop()+child->height()+child->marginBottom());
407             m_overflowHeight = QMAX(m_overflowHeight, yPos+child->marginTop()+child->overflowHeight());
408
409             child = iterator.next();
410         }
411         m_height += toAdd;
412
413         // Always make sure our overflowheight is at least our height.
414         if (m_overflowHeight < m_height)
415             m_overflowHeight = m_height;
416         
417         oldHeight = m_height;
418         calcHeight();
419
420         relayoutChildren = false;
421         if (oldHeight != m_height) {
422             heightSpecified = true;
423     
424             // If the block got expanded in size, then increase our overflowheight to match.
425             if (m_overflowHeight > m_height)
426                 m_overflowHeight -= (borderBottom() + paddingBottom());
427             if (m_overflowHeight < m_height)
428                 m_overflowHeight = m_height;
429         }
430         
431         // Now that our height is actually known, we can place our boxes.
432         m_stretchingChildren = (style()->boxAlign() == BSTRETCH);
433         child = iterator.first();
434         while (child)
435         {
436             if (child->isPositioned())
437             {
438                 child->containingBlock()->insertPositionedObject(child);
439                 if (child->hasStaticX()) {
440                     if (style()->direction() == LTR)
441                         child->setStaticX(xPos);
442                     else child->setStaticX(width() - xPos);
443                 }
444                 if (child->hasStaticY())
445                     child->setStaticY(yPos);
446                 child = iterator.next();
447                 continue;
448             }
449     
450             // We need to see if this child's height has changed, since we make block elements
451             // fill the height of a containing box by default.
452             // Now do a layout.
453             int oldChildHeight = child->height();
454             static_cast<RenderBox*>(child)->calcHeight();
455             if (oldChildHeight != child->height())
456                 child->setChildNeedsLayout(true);
457             child->layoutIfNeeded();
458     
459             // We can place the child now, using our value of box-align.
460             xPos += child->marginLeft();
461             int childY = yPos;
462             switch (style()->boxAlign()) {
463                 case BCENTER:
464                 case BBASELINE: // FIXME: Implement support for baseline
465                     childY += (contentHeight() - (child->height() + child->marginTop() + child->marginBottom()))/2;
466                     break;
467                 case BEND:
468                     childY += contentHeight() - child->marginBottom() - child->height();
469                     break;
470                 default: // BSTART
471                     childY += child->marginTop();
472                     break;
473             }
474
475             placeChild(child, xPos, childY);
476             
477             xPos += child->width() + child->marginRight();
478     
479             child = iterator.next();
480         }
481
482         remainingSpace = borderLeft() + paddingLeft() + contentWidth() - xPos;
483         
484         m_stretchingChildren = false;
485         if (m_flexingChildren)
486             haveFlex = false; // We're done.
487         else if (haveFlex) {
488             // We have some flexible objects.  See if we need to grow/shrink them at all.
489             if (!remainingSpace)
490                 break;
491
492             // Allocate the remaining space among the flexible objects.  If we are trying to
493             // grow, then we go from the lowest flex group to the highest flex group.  For shrinking,
494             // we go from the highest flex group to the lowest group.
495             unsigned int start = remainingSpace > 0 ? lowestFlexGroup : highestFlexGroup;
496             unsigned int end = remainingSpace > 0 ? highestFlexGroup : lowestFlexGroup;
497             for (unsigned int i = start; i <= end; i++) {
498                 float totalFlex = 0.0f;
499                 child = iterator.first();
500                 while (child) {
501                     if (child->isPositioned() || child->style()->boxFlex() == 0.0f ||
502                         child->style()->boxFlexGroup() != i) {
503                         child = iterator.next();
504                         continue;
505                     }
506
507                     // Add together the flexes so we can normalize.
508                     totalFlex += child->style()->boxFlex();
509                     
510                     child = iterator.next();
511                     continue;
512                 }
513
514                 // The flex group may not have any flexible objects. 
515                 if (totalFlex == 0.0f)
516                     continue;
517
518                 // Now distribute the space to objects.
519                 child = iterator.first();
520                 while (child && remainingSpace && totalFlex) {
521                     if (child->isPositioned() || child->style()->boxFlex() == 0.0f ||
522                         child->style()->boxFlexGroup() != i) {
523                         child = iterator.next();
524                         continue;
525                     }
526
527                     int spaceAdd = (int)(remainingSpace * (child->style()->boxFlex()/totalFlex));
528                     if (remainingSpace > 0) {
529                         // FIXME: For now just handle fixed values.
530                         if (child->style()->maxWidth().value != UNDEFINED &&
531                             child->style()->maxWidth().isFixed()) {
532                             int maxW = child->style()->maxWidth().value;
533                             int w = child->contentWidth();
534                             int allowedGrowth = QMAX(0, maxW - w);
535                             spaceAdd = QMIN(spaceAdd, allowedGrowth);
536                         }
537                     } else {
538                         // FIXME: For now just handle fixed values.
539                         if (child->style()->minWidth().isFixed()) {
540                             int minW = child->style()->minWidth().value;
541                             int w = child->contentWidth();
542                             int allowedShrinkage = QMIN(0, minW - w);
543                             spaceAdd = QMAX(spaceAdd, allowedShrinkage);
544                         }
545                     }
546
547                     if (spaceAdd) {
548                         child->setWidth(child->width()+spaceAdd);
549                         m_flexingChildren = true;
550                         relayoutChildren = true;
551                     }
552
553                     remainingSpace -= spaceAdd;
554                     totalFlex -= child->style()->boxFlex();
555                     
556                     child = iterator.next();
557                 }
558             }
559
560             // We didn't find any children that could grow.
561             if (haveFlex && !m_flexingChildren)
562                 haveFlex = false;
563         }
564     } while (haveFlex);
565
566     m_flexingChildren = false;
567     
568     if (xPos > m_overflowWidth)
569         m_overflowWidth = xPos;
570
571     if (remainingSpace > 0 && ((style()->direction() == LTR && style()->boxPack() != BSTART) ||
572                                (style()->direction() == RTL && style()->boxPack() != BEND))) {
573         // Children must be repositioned.
574         int offset = 0;
575         if (style()->boxPack() == BJUSTIFY) {
576             // Determine the total number of children.
577             int totalChildren = 0;
578             child = iterator.first();
579             while (child) {
580                 if (child->isPositioned()) {
581                     child = iterator.next();
582                     continue;
583                 }
584                 totalChildren++;
585                 child = iterator.next();
586             }
587
588             // Iterate over the children and space them out according to the
589             // justification level.
590             if (totalChildren > 1) {
591                 totalChildren--;
592                 bool firstChild = true;
593                 child = iterator.first();
594                 while (child) {
595                     if (child->isPositioned()) {
596                         child = iterator.next();
597                         continue;
598                     }
599
600                     if (firstChild) {
601                         firstChild = false;
602                         child = iterator.next();
603                         continue;
604                     }
605
606                     offset += remainingSpace/totalChildren;
607                     remainingSpace -= (remainingSpace/totalChildren);
608                     totalChildren--;
609
610                     placeChild(child, child->xPos()+offset, child->yPos());
611                     child = iterator.next();
612                 }
613             }
614         }
615         else {
616             if (style()->boxPack() == BCENTER)
617                 offset += remainingSpace/2;
618             else // END for LTR, START for RTL
619                 offset += remainingSpace;
620             child = iterator.first();
621             while (child) {
622                 if (child->isPositioned()) {
623                     child = iterator.next();
624                     continue;
625                 }
626                 placeChild(child, child->xPos()+offset, child->yPos());
627                 child = iterator.next();
628             }
629         }
630     }
631     
632     // So that the calcHeight in layoutBlock() knows to relayout positioned objects because of
633     // a height change, we revert our height back to the intrinsic height before returning.
634     if (heightSpecified)
635         m_height = oldHeight;
636 }
637
638 void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren)
639 {
640     int xPos = borderLeft() + paddingLeft();
641     int yPos = borderTop() + paddingTop();
642     if( style()->direction() == RTL )
643         xPos = m_width - paddingRight() - borderRight();
644     int toAdd = borderBottom() + paddingBottom();
645     bool heightSpecified = false;
646     int oldHeight = 0;
647     
648     unsigned int highestFlexGroup = 0;
649     unsigned int lowestFlexGroup = 0;
650     bool haveFlex = false;
651     int remainingSpace = 0;
652     
653     // The first walk over our kids is to find out if we have any flexible children.
654     FlexBoxIterator iterator(this);
655     RenderObject *child = iterator.next();
656     while (child) {
657         // Check to see if this child flexes.
658         if (!child->isPositioned() && child->style()->boxFlex() > 0.0f) {
659             // We always have to lay out flexible objects again, since the flex distribution
660             // may have changed, and we need to reallocate space.
661             if (!relayoutChildren)
662                 child->setChildNeedsLayout(true);
663             haveFlex = true;
664             unsigned int flexGroup = child->style()->boxFlexGroup();
665             if (lowestFlexGroup == 0)
666                 lowestFlexGroup = flexGroup;
667             if (flexGroup < lowestFlexGroup)
668                 lowestFlexGroup = flexGroup;
669             if (flexGroup > highestFlexGroup)
670                 highestFlexGroup = flexGroup;
671         }
672         child = iterator.next();
673         continue;
674     }
675
676 #if APPLE_CHANGES
677     // We confine the line clamp ugliness to vertical flexible boxes (thus keeping it out of
678     // mainstream block layout) and put it all inside APPLE_CHANGES to denote that this is not
679     // really part of the XUL box model.
680     bool haveLineClamp = style()->lineClamp() >= 0 && style()->lineClamp() <= 100;
681     if (haveLineClamp) {
682         int maxLineCount = 1;
683         child = iterator.first();
684         while (child) {
685             if (!child->isPositioned()) {
686                 if (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())) ||
687                     (child->style()->height().isVariable() && child->isBlockFlow() && !child->needsLayout())) {
688                     child->setChildNeedsLayout(true);
689                     
690                     // Dirty all the positioned objects.
691                     static_cast<RenderBlock*>(child)->markPositionedObjectsForLayout();
692                 }
693                 child->layoutIfNeeded();
694                 if (child->style()->height().isVariable() && child->isBlockFlow())
695                     maxLineCount = kMax(maxLineCount, static_cast<RenderBlock*>(child)->lineCount());
696             }
697             child = iterator.next();
698         }
699         
700         // Get the # of lines and then alter all block flow children with auto height to use the
701         // specified height.
702         int numLines = int(maxLineCount*style()->lineClamp()/100.0 + 1.0);
703         if (numLines < maxLineCount) {
704             child = iterator.first();
705             while (child) {
706                 if (!child->isPositioned() && child->style()->height().isVariable() && child->isBlockFlow() &&
707                     static_cast<RenderBlock*>(child)->lineCount() > numLines) {
708                     int newHeight = static_cast<RenderBlock*>(child)->heightForLineCount(numLines);
709                     if (newHeight != child->height()) {
710                         child->setChildNeedsLayout(true);
711                         child->style()->setBoxFlexedHeight(newHeight);
712                         m_flexingChildren = true;
713                         child->layoutIfNeeded();
714                         m_flexingChildren = false;
715                         child->style()->setBoxFlexedHeight(-1);
716                     }
717                 }
718                 child = iterator.next();
719             }
720         }
721     }
722 #endif
723
724     // We do 2 passes.  The first pass is simply to lay everyone out at
725     // their preferred widths.  The second pass handles flexing the children.
726     // Our first pass is done without flexing.  We simply lay the children
727     // out within the box.
728     do {
729         m_height = borderTop() + paddingTop();
730         int minHeight = m_height + toAdd;
731         m_overflowHeight = m_height;
732
733         child = iterator.first();
734         while (child) {
735             // make sure we relayout children if we need it.
736             if (!haveLineClamp && (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent()))))
737                 child->setChildNeedsLayout(true);
738     
739             if (child->isPositioned())
740             {
741                 child->containingBlock()->insertPositionedObject(child);
742                 if (child->hasStaticX()) {
743                     if (style()->direction() == LTR)
744                         child->setStaticX(borderLeft()+paddingLeft());
745                     else
746                         child->setStaticX(borderRight()+paddingRight());
747                 }
748                 if (child->hasStaticY())
749                     child->setStaticY(m_height);
750                 child = iterator.next();
751                 continue;
752             } 
753     
754             // Compute the child's vertical margins.
755             child->calcVerticalMargins();
756     
757             // Add in the child's marginTop to our height.
758             m_height += child->marginTop();
759     
760             // Now do a layout.
761             child->layoutIfNeeded();
762     
763             // We can place the child now, using our value of box-align.
764             int childX = borderLeft() + paddingLeft();
765             switch (style()->boxAlign()) {
766                 case BCENTER:
767                 case BBASELINE: // Baseline just maps to center for vertical boxes
768                     childX += (contentWidth() - (child->width() + child->marginLeft() + child->marginRight()))/2;
769                     break;
770                 case BEND:
771                     if (style()->direction() == RTL)
772                         childX += child->marginLeft();
773                     else
774                         childX += contentWidth() - child->marginRight() - child->width();
775                     break;
776                 default: // BSTART/BSTRETCH
777                     if (style()->direction() == LTR)
778                         childX += child->marginLeft();
779                     else
780                         childX += contentWidth() - child->marginRight() - child->width();
781                     break;
782             }
783     
784             // Place the child.
785             placeChild(child, childX, m_height);
786             m_height += child->height() + child->marginBottom();
787     
788             // See if this child has made our overflow need to grow.
789             // XXXdwh Work with left overflow as well as right overflow.
790             int rightChildPos = child->xPos() + QMAX(child->overflowWidth(false), child->width());
791             if (rightChildPos > m_overflowWidth)
792                 m_overflowWidth = rightChildPos;
793             
794             child = iterator.next();
795         }
796
797         yPos = m_height;
798         m_height += toAdd;
799
800         // Negative margins can cause our height to shrink below our minimal height (border/padding).
801         // If this happens, ensure that the computed height is increased to the minimal height.
802         if (m_height < minHeight)
803             m_height = minHeight;
804
805         // Always make sure our overflowheight is at least our height.
806         if (m_overflowHeight < m_height)
807             m_overflowHeight = m_height;
808
809         // Now we have to calc our height, so we know how much space we have remaining.
810         oldHeight = m_height;
811         calcHeight();
812         if (oldHeight != m_height)
813             heightSpecified = true;
814
815         remainingSpace = borderTop() + paddingTop() + contentHeight() - yPos;
816         
817         if (m_flexingChildren)
818             haveFlex = false; // We're done.
819         else if (haveFlex) {
820             // We have some flexible objects.  See if we need to grow/shrink them at all.
821             if (!remainingSpace)
822                 break;
823
824             // Allocate the remaining space among the flexible objects.  If we are trying to
825             // grow, then we go from the lowest flex group to the highest flex group.  For shrinking,
826             // we go from the highest flex group to the lowest group.
827             unsigned int start = remainingSpace > 0 ? lowestFlexGroup : highestFlexGroup;
828             unsigned int end = remainingSpace > 0 ? highestFlexGroup : lowestFlexGroup;
829             for (unsigned int i = start; i <= end; i++) {
830                 float totalFlex = 0.0f;
831                 child = iterator.first();
832                 while (child) {
833                     if (child->isPositioned() || child->style()->boxFlex() == 0.0f ||
834                         child->style()->boxFlexGroup() != i) {
835                         child = iterator.next();
836                         continue;
837                     }
838
839                     // Add together the flexes so we can normalize.
840                     totalFlex += child->style()->boxFlex();
841
842                     child = iterator.next();
843                 }
844
845                 // The flex group may not have any flexible objects.
846                 if (totalFlex == 0.0f)
847                     continue;
848
849                 // Now distribute the space to objects.
850                 child = iterator.first();
851                 while (child && remainingSpace && totalFlex) {
852                     if (child->isPositioned() || child->style()->boxFlex() == 0.0f ||
853                         child->style()->boxFlexGroup() != i) {
854                         child = iterator.next();
855                         continue;
856                     }
857
858                     int spaceAdd = (int)(remainingSpace * (child->style()->boxFlex()/totalFlex));
859                     if (remainingSpace > 0) {
860                         // FIXME: For now just handle fixed values.
861                         if (child->style()->maxHeight().value != UNDEFINED &&
862                             child->style()->maxHeight().isFixed()) {
863                             int maxH = child->style()->maxHeight().value;
864                             int h = child->contentHeight();
865                             int allowedGrowth = QMAX(0, maxH - h);
866                             spaceAdd = QMIN(spaceAdd, allowedGrowth);
867                         }
868                     } else {
869                         // FIXME: For now just handle fixed values.
870                         if (child->style()->minHeight().isFixed()) {
871                             int minH = child->style()->minHeight().value;
872                             int h = child->contentHeight();
873                             int allowedShrinkage = QMIN(0, minH - h);
874                             spaceAdd = QMAX(spaceAdd, allowedShrinkage);
875                         }
876                     }
877
878                     if (spaceAdd) {
879                         child->style()->setBoxFlexedHeight(child->height()+spaceAdd);
880                         m_flexingChildren = true;
881                         child->setNeedsLayout(true);
882                     }
883
884                     remainingSpace -= spaceAdd;
885                     totalFlex -= child->style()->boxFlex();
886
887                     child = iterator.next();
888                 }
889             }
890
891             // We didn't find any children that could grow.
892             if (haveFlex && !m_flexingChildren)
893                 haveFlex = false;
894         }        
895     } while (haveFlex);
896
897     if (style()->boxPack() != BSTART && remainingSpace > 0) {
898         // Children must be repositioned.
899         int offset = 0;
900         if (style()->boxPack() == BJUSTIFY) {
901             // Determine the total number of children.
902             int totalChildren = 0;
903             child = iterator.first();
904             while (child) {
905                 if (child->isPositioned()) {
906                     child = iterator.next();
907                     continue;
908                 }
909                 totalChildren++;
910                 child = iterator.next();
911             }
912             
913             // Iterate over the children and space them out according to the
914             // justification level.
915             if (totalChildren > 1) {
916                 totalChildren--;
917                 bool firstChild = true;
918                 child = iterator.first();
919                 while (child) {
920                     if (child->isPositioned()) {
921                         child = iterator.next();
922                         continue;
923                     }
924                     
925                     if (firstChild) {
926                         firstChild = false;
927                         child = iterator.next();
928                         continue;
929                     }
930
931                     offset += remainingSpace/totalChildren;
932                     remainingSpace -= (remainingSpace/totalChildren);
933                     totalChildren--;
934                     placeChild(child, child->xPos(), child->yPos()+offset);
935                     child = iterator.next();
936                 }
937             }
938         }
939         else {
940             if (style()->boxPack() == BCENTER)
941                 offset += remainingSpace/2;
942             else // END
943                 offset += remainingSpace;
944             child = iterator.first();
945             while (child) {
946                 if (child->isPositioned()) {
947                     child = iterator.next();
948                     continue;
949                 }
950                 placeChild(child, child->xPos(), child->yPos()+offset);
951                 child = iterator.next();
952             }
953         }
954     }
955     
956     // So that the calcHeight in layoutBlock() knows to relayout positioned objects because of
957     // a height change, we revert our height back to the intrinsic height before returning.
958     if (heightSpecified)
959         m_height = oldHeight;    
960 }
961
962 void RenderFlexibleBox::placeChild(RenderObject* child, int x, int y)
963 {
964     int oldChildX = child->xPos();
965     int oldChildY = child->yPos();
966
967     // Place the child.
968     child->setPos(x, y);
969
970     // If the child moved, we have to repaint it as well as any floating/positioned
971     // descendants.  An exception is if we need a layout.  In this case, we know we're going to
972     // repaint ourselves (and the child) anyway.
973     if (!selfNeedsLayout() && child->checkForRepaintDuringLayout())
974         child->repaintDuringLayoutIfMoved(oldChildX, oldChildY);
975 }
976
977 const char *RenderFlexibleBox::renderName() const
978 {
979     if (isFloating())
980         return "RenderFlexibleBox (floating)";
981     if (isPositioned())
982         return "RenderFlexibleBox (positioned)";
983     if (isRelPositioned())
984         return "RenderFlexibleBox (relative positioned)";
985     return "RenderFlexibleBox";
986 }
987
988
989 } // namespace khtml
990