Fix build. This one was not supposed to be checked in.
[WebKit-https.git] / WebCore / rendering / RenderFlexibleBox.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 "config.h"
26 #include "RenderFlexibleBox.h"
27
28 #include "CharacterNames.h"
29 #include "RenderLayer.h"
30 #include "RenderView.h"
31
32 using namespace std;
33
34 namespace WebCore {
35
36 class FlexBoxIterator {
37 public:
38     FlexBoxIterator(RenderFlexibleBox* parent) {
39         box = parent;
40         if (box->style()->boxOrient() == HORIZONTAL && box->style()->direction() == RTL)
41             forward = box->style()->boxDirection() != BNORMAL;
42         else
43             forward = box->style()->boxDirection() == BNORMAL;
44         lastOrdinal = 1; 
45         if (!forward) {
46             // No choice, since we're going backwards, we have to find out the highest ordinal up front.
47             RenderObject* child = box->firstChild();
48             while (child) {
49                 if (child->style()->boxOrdinalGroup() > lastOrdinal)
50                     lastOrdinal = child->style()->boxOrdinalGroup();
51                 child = child->nextSibling();
52             }
53         }
54         
55         reset();
56     }
57
58     void reset() {
59         current = 0;
60         currentOrdinal = forward ? 0 : lastOrdinal+1;
61     }
62
63     RenderObject* first() {
64         reset();
65         return next();
66     }
67     
68     RenderObject* next() {
69
70         do { 
71             if (!current) {
72                 if (forward) {
73                     currentOrdinal++; 
74                     if (currentOrdinal > lastOrdinal)
75                         return 0;
76                     current = box->firstChild();
77                 } else {
78                     currentOrdinal--;
79                     if (currentOrdinal == 0)
80                         return 0;
81                     current = box->lastChild();
82                 }
83             }
84             else
85                 current = forward ? current->nextSibling() : current->previousSibling();
86             if (current && current->style()->boxOrdinalGroup() > lastOrdinal)
87                 lastOrdinal = current->style()->boxOrdinalGroup();
88         } while (!current || current->style()->boxOrdinalGroup() != currentOrdinal ||
89                  current->style()->visibility() == COLLAPSE);
90         return current;
91     }
92
93 private:
94     RenderFlexibleBox* box;
95     RenderObject* current;
96     bool forward;
97     unsigned int currentOrdinal;
98     unsigned int lastOrdinal;
99 };
100     
101 RenderFlexibleBox::RenderFlexibleBox(Node* node)
102 :RenderBlock(node)
103 {
104     setChildrenInline(false); // All of our children must be block-level
105     m_flexingChildren = m_stretchingChildren = false;
106 }
107
108 RenderFlexibleBox::~RenderFlexibleBox()
109 {
110 }
111
112 void RenderFlexibleBox::calcHorizontalMinMaxWidth()
113 {
114     RenderObject *child = firstChild();
115     while (child) {
116         // positioned children don't affect the minmaxwidth
117         if (child->isPositioned() || child->style()->visibility() == COLLAPSE)
118         {
119             child = child->nextSibling();
120             continue;
121         }
122
123         int margin=0;
124         //  auto margins don't affect minwidth
125
126         Length ml = child->style()->marginLeft();
127         Length mr = child->style()->marginRight();
128
129         // Call calcWidth on the child to ensure that our margins are
130         // up to date.  This method can be called before the child has actually
131         // calculated its margins (which are computed inside calcWidth).
132         child->calcWidth();
133
134         if (!ml.isAuto() && !mr.isAuto()) {
135             if (!child->style()->width().isAuto()) {
136                 if (child->style()->direction()==LTR)
137                     margin = child->marginLeft();
138                 else
139                     margin = child->marginRight();
140             } else
141                 margin = child->marginLeft()+child->marginRight();
142         } else if (!ml.isAuto())
143             margin = child->marginLeft();
144         else if (!mr.isAuto())
145             margin = child->marginRight();
146
147         if (margin < 0) margin = 0;
148
149         m_minWidth += child->minWidth() + margin;
150         m_maxWidth += child->maxWidth() + margin;
151
152         child = child->nextSibling();
153     }    
154 }
155
156 void RenderFlexibleBox::calcVerticalMinMaxWidth()
157 {
158     RenderObject *child = firstChild();
159     while(child != 0)
160     {
161         // Positioned children and collapsed children don't affect the min/max width
162         if (child->isPositioned() || child->style()->visibility() == COLLAPSE) {
163             child = child->nextSibling();
164             continue;
165         }
166
167         Length ml = child->style()->marginLeft();
168         Length mr = child->style()->marginRight();
169
170         // Call calcWidth on the child to ensure that our margins are
171         // up to date.  This method can be called before the child has actually
172         // calculated its margins (which are computed inside calcWidth).
173         if (ml.isPercent() || mr.isPercent())
174             calcWidth();
175
176         // A margin basically has three types: fixed, percentage, and auto (variable).
177         // Auto margins simply become 0 when computing min/max width.
178         // Fixed margins can be added in as is.
179         // Percentage margins are computed as a percentage of the width we calculated in
180         // the calcWidth call above.  In this case we use the actual cached margin values on
181         // the RenderObject itself.
182         int margin = 0;
183         if (ml.isFixed())
184             margin += ml.value();
185         else if (ml.isPercent())
186             margin += child->marginLeft();
187
188         if (mr.isFixed())
189             margin += mr.value();
190         else if (mr.isAuto())
191             margin += child->marginRight();
192
193         if (margin < 0) margin = 0;
194         
195         int w = child->minWidth() + margin;
196         if(m_minWidth < w) m_minWidth = w;
197         
198         w = child->maxWidth() + margin;
199
200         if(m_maxWidth < w) m_maxWidth = w;
201
202         child = child->nextSibling();
203     }    
204 }
205
206 void RenderFlexibleBox::calcMinMaxWidth()
207 {
208     ASSERT( !minMaxKnown() );
209
210     m_minWidth = 0;
211     m_maxWidth = 0;
212
213     if (hasMultipleLines() || isVertical())
214         calcVerticalMinMaxWidth();
215     else
216         calcHorizontalMinMaxWidth();
217
218     if(m_maxWidth < m_minWidth) m_maxWidth = m_minWidth;
219
220     if (style()->width().isFixed() && style()->width().value() > 0)
221         m_minWidth = m_maxWidth = calcContentBoxWidth(style()->width().value());
222    
223     if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
224         m_maxWidth = max(m_maxWidth, calcContentBoxWidth(style()->minWidth().value()));
225         m_minWidth = max(m_minWidth, calcContentBoxWidth(style()->minWidth().value()));
226     }
227     
228     if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) {
229         m_maxWidth = min(m_maxWidth, calcContentBoxWidth(style()->maxWidth().value()));
230         m_minWidth = min(m_minWidth, calcContentBoxWidth(style()->maxWidth().value()));
231     }
232
233     int toAdd = borderLeft() + borderRight() + paddingLeft() + paddingRight();
234     m_minWidth += toAdd;
235     m_maxWidth += toAdd;
236
237     setMinMaxKnown();
238 }
239
240 void RenderFlexibleBox::layoutBlock(bool relayoutChildren)
241 {
242     ASSERT(needsLayout());
243     ASSERT(minMaxKnown());
244
245     if (!relayoutChildren && posChildNeedsLayout() && !normalChildNeedsLayout() && !selfNeedsLayout()) {
246         // All we have to is lay out our positioned objects.
247         layoutPositionedObjects(false);
248         if (hasOverflowClip())
249             m_layer->updateScrollInfoAfterLayout();
250         setNeedsLayout(false);
251         return;
252     }
253
254     IntRect oldBounds;
255     IntRect oldOutlineBox;
256     bool checkForRepaint = checkForRepaintDuringLayout();
257     if (checkForRepaint) {
258         oldBounds = absoluteClippedOverflowRect();
259         oldBounds.move(view()->layoutDelta());
260         oldOutlineBox = absoluteOutlineBox();
261         oldOutlineBox.move(view()->layoutDelta());
262     }
263
264     int previousWidth = m_width;
265     int previousHeight = m_height;
266     
267     calcWidth();
268     calcHeight();
269     m_overflowWidth = m_width;
270
271     if (previousWidth != m_width || previousHeight != m_height ||
272         (parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL &&
273          parent()->style()->boxAlign() == BSTRETCH))
274         relayoutChildren = true;
275
276     m_height = 0;
277     m_overflowHeight = 0;
278     m_flexingChildren = m_stretchingChildren = false;
279
280     initMaxMarginValues();
281
282     // For overflow:scroll blocks, ensure we have both scrollbars in place always.
283     if (scrollsOverflow()) {
284         if (style()->overflowX() == OSCROLL)
285             m_layer->setHasHorizontalScrollbar(true);
286         if (style()->overflowY() == OSCROLL)
287             m_layer->setHasVerticalScrollbar(true);
288     }
289
290     if (isHorizontal())
291         layoutHorizontalBox(relayoutChildren);
292     else
293         layoutVerticalBox(relayoutChildren);
294
295     int oldHeight = m_height;
296     calcHeight();
297     if (oldHeight != m_height) {
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() + horizontalScrollbarHeight());
301         if (m_overflowHeight < m_height)
302             m_overflowHeight = m_height;
303     }
304     if (previousHeight != m_height)
305         relayoutChildren = true;
306
307     layoutPositionedObjects(relayoutChildren || isRoot());
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, oldOutlineBox);
336     
337     setNeedsLayout(false);
338 }
339
340 void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren)
341 {
342     int toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight();
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             child->setOverrideSize(-1);
363             if (!relayoutChildren)
364                 child->setChildNeedsLayout(true, false);
365             haveFlex = true;
366             unsigned int flexGroup = child->style()->boxFlexGroup();
367             if (lowestFlexGroup == 0)
368                 lowestFlexGroup = flexGroup;
369             if (flexGroup < lowestFlexGroup)
370                 lowestFlexGroup = flexGroup;
371             if (flexGroup > highestFlexGroup)
372                 highestFlexGroup = flexGroup;
373         }
374         child = iterator.next();
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         int maxAscent = 0, maxDescent = 0;
389         child = iterator.first();
390         while (child) {
391             // make sure we relayout children if we need it.
392             if (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())))
393                 child->setChildNeedsLayout(true, false);
394             
395             if (child->isPositioned()) {
396                 child = iterator.next();
397                 continue;
398             }
399     
400             // Compute the child's vertical margins.
401             child->calcVerticalMargins();
402     
403             // Now do the layout.
404             child->layoutIfNeeded();
405     
406             // Update our height and overflow height.
407             if (style()->boxAlign() == BBASELINE) {
408                 int ascent = child->marginTop() + child->getBaselineOfFirstLineBox();
409                 if (ascent == -1)
410                     ascent = child->marginTop() + child->height() + child->marginBottom();
411                 int descent = (child->marginTop() + child->height() + child->marginBottom()) - ascent;
412                 
413                 // Update our maximum ascent.
414                 maxAscent = max(maxAscent, ascent);
415                 
416                 // Update our maximum descent.
417                 maxDescent = max(maxDescent, descent);
418                 
419                 // Now update our height.
420                 m_height = max(yPos + maxAscent + maxDescent, m_height);
421             }
422             else
423                 m_height = max(m_height, yPos + child->marginTop() + child->height() + child->marginBottom());
424
425             child = iterator.next();
426         }
427         
428         if (!iterator.first() && hasLineIfEmpty())
429             m_height += lineHeight(true, true);
430         
431         m_height += toAdd;
432
433         // Always make sure our overflowheight is at least our height.
434         if (m_overflowHeight < m_height)
435             m_overflowHeight = m_height;
436         
437         oldHeight = m_height;
438         calcHeight();
439
440         relayoutChildren = false;
441         if (oldHeight != m_height)
442             heightSpecified = true;
443         
444         // Now that our height is actually known, we can place our boxes.
445         m_stretchingChildren = (style()->boxAlign() == BSTRETCH);
446         child = iterator.first();
447         while (child) {
448             if (child->isPositioned()) {
449                 child->containingBlock()->insertPositionedObject(child);
450                 if (child->hasStaticX()) {
451                     if (style()->direction() == LTR)
452                         child->setStaticX(xPos);
453                     else child->setStaticX(width() - xPos);
454                 }
455                 if (child->hasStaticY())
456                     child->setStaticY(yPos);
457                 child = iterator.next();
458                 continue;
459             }
460     
461             // We need to see if this child's height has changed, since we make block elements
462             // fill the height of a containing box by default.
463             // Now do a layout.
464             int oldChildHeight = child->height();
465             static_cast<RenderBox*>(child)->calcHeight();
466             if (oldChildHeight != child->height())
467                 child->setChildNeedsLayout(true, false);
468             child->layoutIfNeeded();
469     
470             // We can place the child now, using our value of box-align.
471             xPos += child->marginLeft();
472             int childY = yPos;
473             switch (style()->boxAlign()) {
474                 case BCENTER:
475                     childY += child->marginTop() + max(0, (contentHeight() - (child->height() + child->marginTop() + child->marginBottom()))/2);
476                     break;
477                 case BBASELINE: {
478                     int ascent = child->marginTop() + child->getBaselineOfFirstLineBox();
479                     if (ascent == -1)
480                         ascent = child->marginTop() + child->height() + child->marginBottom();
481                     childY += child->marginTop() + (maxAscent - ascent);
482                     break;
483                 }
484                 case BEND:
485                     childY += contentHeight() - child->marginBottom() - child->height();
486                     break;
487                 default: // BSTART
488                     childY += child->marginTop();
489                     break;
490             }
491
492             placeChild(child, xPos, childY);
493
494             if (child->isRenderBlock())
495                 static_cast<RenderBlock*>(child)->addVisualOverflow(static_cast<RenderBlock*>(child)->floatRect());
496
497             m_overflowHeight = max(m_overflowHeight, childY + child->overflowHeight(false));
498             m_overflowTop = min(m_overflowTop, child->yPos() + child->overflowTop(false));
499             
500             xPos += child->width() + child->marginRight();
501     
502             child = iterator.next();
503         }
504
505         remainingSpace = borderLeft() + paddingLeft() + contentWidth() - xPos;
506         
507         m_stretchingChildren = false;
508         if (m_flexingChildren)
509             haveFlex = false; // We're done.
510         else if (haveFlex) {
511             // We have some flexible objects.  See if we need to grow/shrink them at all.
512             if (!remainingSpace)
513                 break;
514
515             // Allocate the remaining space among the flexible objects.  If we are trying to
516             // grow, then we go from the lowest flex group to the highest flex group.  For shrinking,
517             // we go from the highest flex group to the lowest group.
518             bool expanding = remainingSpace > 0;
519             unsigned int start = expanding ? lowestFlexGroup : highestFlexGroup;
520             unsigned int end = expanding? highestFlexGroup : lowestFlexGroup;
521             for (unsigned int i = start; i <= end && remainingSpace; i++) {
522                 // Always start off by assuming the group can get all the remaining space.
523                 int groupRemainingSpace = remainingSpace;
524                 do {
525                     // Flexing consists of multiple passes, since we have to change ratios every time an object hits its max/min-width
526                     // For a given pass, we always start off by computing the totalFlex of all objects that can grow/shrink at all, and
527                     // computing the allowed growth before an object hits its min/max width (and thus
528                     // forces a totalFlex recomputation).
529                     float totalFlex = 0.0f;
530                     child = iterator.first();
531                     while (child) {
532                         if (allowedChildFlex(child, expanding, i))
533                             totalFlex += child->style()->boxFlex();
534                         child = iterator.next();
535                     }
536                     child = iterator.first();
537                     int spaceAvailableThisPass = groupRemainingSpace;
538                     while (child) {
539                         int allowedFlex = allowedChildFlex(child, expanding, i);
540                         if (allowedFlex) {
541                             int projectedFlex = (allowedFlex == INT_MAX) ? allowedFlex : (int)(allowedFlex * (totalFlex / child->style()->boxFlex()));
542                             spaceAvailableThisPass = expanding ? min(spaceAvailableThisPass, projectedFlex) : max(spaceAvailableThisPass, projectedFlex);
543                         }
544                         child = iterator.next();
545                     }
546
547                     // The flex groups may not have any flexible objects this time around. 
548                     if (!spaceAvailableThisPass || totalFlex == 0.0f) {
549                         // If we just couldn't grow/shrink any more, then it's time to transition to the next flex group.
550                         groupRemainingSpace = 0;
551                         continue;
552                     }
553
554                     // Now distribute the space to objects.
555                     child = iterator.first();
556                     while (child && spaceAvailableThisPass && totalFlex) {
557                         if (allowedChildFlex(child, expanding, i)) {
558                             int spaceAdd = (int)(spaceAvailableThisPass * (child->style()->boxFlex()/totalFlex));
559                             if (spaceAdd) {
560                                 child->setOverrideSize(child->overrideWidth() + spaceAdd);
561                                 m_flexingChildren = true;
562                                 relayoutChildren = true;
563                             }
564
565                             spaceAvailableThisPass -= spaceAdd;
566                             remainingSpace -= spaceAdd;
567                             groupRemainingSpace -= spaceAdd;
568                             
569                             totalFlex -= child->style()->boxFlex();
570                         }
571                         child = iterator.next();
572                     }
573                 } while (groupRemainingSpace);
574             }
575
576             // We didn't find any children that could grow.
577             if (haveFlex && !m_flexingChildren)
578                 haveFlex = false;
579         }
580     } while (haveFlex);
581
582     m_flexingChildren = false;
583     
584     if (remainingSpace > 0 && ((style()->direction() == LTR && style()->boxPack() != BSTART) ||
585                                (style()->direction() == RTL && style()->boxPack() != BEND))) {
586         // Children must be repositioned.
587         int offset = 0;
588         if (style()->boxPack() == BJUSTIFY) {
589             // Determine the total number of children.
590             int totalChildren = 0;
591             child = iterator.first();
592             while (child) {
593                 if (child->isPositioned()) {
594                     child = iterator.next();
595                     continue;
596                 }
597                 totalChildren++;
598                 child = iterator.next();
599             }
600
601             // Iterate over the children and space them out according to the
602             // justification level.
603             if (totalChildren > 1) {
604                 totalChildren--;
605                 bool firstChild = true;
606                 child = iterator.first();
607                 while (child) {
608                     if (child->isPositioned()) {
609                         child = iterator.next();
610                         continue;
611                     }
612
613                     if (firstChild) {
614                         firstChild = false;
615                         child = iterator.next();
616                         continue;
617                     }
618
619                     offset += remainingSpace/totalChildren;
620                     remainingSpace -= (remainingSpace/totalChildren);
621                     totalChildren--;
622
623                     placeChild(child, child->xPos()+offset, child->yPos());
624                     child = iterator.next();
625                 }
626             }
627         } else {
628             if (style()->boxPack() == BCENTER)
629                 offset += remainingSpace/2;
630             else // END for LTR, START for RTL
631                 offset += remainingSpace;
632             child = iterator.first();
633             while (child) {
634                 if (child->isPositioned()) {
635                     child = iterator.next();
636                     continue;
637                 }
638                 placeChild(child, child->xPos()+offset, child->yPos());
639                 child = iterator.next();
640             }
641         }
642     }
643     
644     child = iterator.first();
645     while (child && child->isPositioned()) {
646         child = iterator.next();
647     }
648     
649     if (child) {
650         m_overflowLeft = min(child->xPos() + child->overflowLeft(false), m_overflowLeft);
651
652         RenderObject* lastChild = child;
653         while ((child = iterator.next())) {
654             if (!child->isPositioned())
655                 lastChild = child;
656         }
657         m_overflowWidth = max(lastChild->xPos() + lastChild->overflowWidth(false), m_overflowWidth);
658     }
659     
660     // So that the calcHeight in layoutBlock() knows to relayout positioned objects because of
661     // a height change, we revert our height back to the intrinsic height before returning.
662     if (heightSpecified)
663         m_height = oldHeight;
664 }
665
666 void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren)
667 {
668     int xPos = borderLeft() + paddingLeft();
669     int yPos = borderTop() + paddingTop();
670     if( style()->direction() == RTL )
671         xPos = m_width - paddingRight() - borderRight();
672     int toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight();
673     bool heightSpecified = false;
674     int oldHeight = 0;
675     
676     unsigned int highestFlexGroup = 0;
677     unsigned int lowestFlexGroup = 0;
678     bool haveFlex = false;
679     int remainingSpace = 0;
680     
681     // The first walk over our kids is to find out if we have any flexible children.
682     FlexBoxIterator iterator(this);
683     RenderObject *child = iterator.next();
684     while (child) {
685         // Check to see if this child flexes.
686         if (!child->isPositioned() && child->style()->boxFlex() > 0.0f) {
687             // We always have to lay out flexible objects again, since the flex distribution
688             // may have changed, and we need to reallocate space.
689             child->setOverrideSize(-1);
690             if (!relayoutChildren)
691                 child->setChildNeedsLayout(true, false);
692             haveFlex = true;
693             unsigned int flexGroup = child->style()->boxFlexGroup();
694             if (lowestFlexGroup == 0)
695                 lowestFlexGroup = flexGroup;
696             if (flexGroup < lowestFlexGroup)
697                 lowestFlexGroup = flexGroup;
698             if (flexGroup > highestFlexGroup)
699                 highestFlexGroup = flexGroup;
700         }
701         child = iterator.next();
702     }
703
704     // We confine the line clamp ugliness to vertical flexible boxes (thus keeping it out of
705     // mainstream block layout); this is not really part of the XUL box model.
706     bool haveLineClamp = style()->lineClamp() >= 0 && style()->lineClamp() <= 100;
707     if (haveLineClamp) {
708         int maxLineCount = 0;
709         child = iterator.first();
710         while (child) {
711             if (!child->isPositioned()) {
712                 if (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())) ||
713                     (child->style()->height().isAuto() && child->isBlockFlow() && !child->needsLayout())) {
714                     child->setChildNeedsLayout(true, false);
715                     
716                     // Dirty all the positioned objects.
717                     static_cast<RenderBlock*>(child)->markPositionedObjectsForLayout();
718                     static_cast<RenderBlock*>(child)->clearTruncation();
719                 }
720                 child->layoutIfNeeded();
721                 if (child->style()->height().isAuto() && child->isBlockFlow())
722                     maxLineCount = max(maxLineCount, static_cast<RenderBlock*>(child)->lineCount());
723             }
724             child = iterator.next();
725         }
726         
727         // Get the # of lines and then alter all block flow children with auto height to use the
728         // specified height.
729         int numVisibleLines = int((maxLineCount+1)*style()->lineClamp()/100.0);
730         if (numVisibleLines < maxLineCount) {
731             for (child = iterator.first(); child; child = iterator.next()) {
732                 if (child->isPositioned() || !child->style()->height().isAuto() || !child->isBlockFlow())
733                     continue;
734                 
735                 RenderBlock* blockChild = static_cast<RenderBlock*>(child);
736                 int lineCount = blockChild->lineCount();
737                 if (lineCount <= numVisibleLines)
738                     continue;
739                 
740                 int newHeight = blockChild->heightForLineCount(numVisibleLines);
741                 if (newHeight == child->height())
742                     continue;
743                 
744                 child->setChildNeedsLayout(true, false);
745                 child->setOverrideSize(newHeight);
746                 m_flexingChildren = true;
747                 child->layoutIfNeeded();
748                 m_flexingChildren = false;
749                 child->setOverrideSize(-1);
750                 
751                 // FIXME: For now don't support RTL.
752                 if (style()->direction() != LTR)
753                     continue;
754                 
755                 // Get the last line
756                 RootInlineBox* lastLine = blockChild->lineAtIndex(lineCount-1);
757                 if (!lastLine)
758                     continue;
759                 
760                 // See if the last item is an anchor
761                 InlineBox* anchorBox = lastLine->lastChild();
762                 if (!anchorBox)
763                     continue;
764                 if (!anchorBox->object()->element())
765                     continue;
766                 if (!anchorBox->object()->element()->isLink())
767                     continue;
768                 
769                 RootInlineBox* lastVisibleLine = blockChild->lineAtIndex(numVisibleLines-1);
770                 if (!lastVisibleLine)
771                     continue;
772
773                 const UChar ellipsisAndSpace[2] = { horizontalEllipsis, ' ' };
774                 static AtomicString ellipsisAndSpaceStr(ellipsisAndSpace, 2);
775
776                 const Font& font = style(numVisibleLines == 1)->font();
777                 int ellipsisAndSpaceWidth = font.width(TextRun(ellipsisAndSpace, 2));
778
779                 // Get ellipsis width + " " + anchor width
780                 int totalWidth = ellipsisAndSpaceWidth + anchorBox->width();
781                 
782                 // See if this width can be accommodated on the last visible line
783                 RenderBlock* destBlock = static_cast<RenderBlock*>(lastVisibleLine->object());
784                 RenderBlock* srcBlock = static_cast<RenderBlock*>(lastLine->object());
785                 
786                 // FIXME: Directions of src/destBlock could be different from our direction and from one another.
787                 if (srcBlock->style()->direction() != LTR)
788                     continue;
789                 if (destBlock->style()->direction() != LTR)
790                     continue;
791
792                 int blockEdge = destBlock->rightOffset(lastVisibleLine->yPos());
793                 if (!lastVisibleLine->canAccommodateEllipsis(true, blockEdge, 
794                                                              lastVisibleLine->xPos() + lastVisibleLine->width(),
795                                                              totalWidth))
796                     continue;
797
798                 // Let the truncation code kick in.
799                 lastVisibleLine->placeEllipsis(ellipsisAndSpaceStr, true, blockEdge, totalWidth, anchorBox);
800                 destBlock->setHasMarkupTruncation(true);
801             }
802         }
803     }
804
805     // We do 2 passes.  The first pass is simply to lay everyone out at
806     // their preferred widths.  The second pass handles flexing the children.
807     // Our first pass is done without flexing.  We simply lay the children
808     // out within the box.
809     do {
810         m_height = borderTop() + paddingTop();
811         int minHeight = m_height + toAdd;
812         m_overflowHeight = m_height;
813
814         child = iterator.first();
815         while (child) {
816             // make sure we relayout children if we need it.
817             if (!haveLineClamp && (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent()))))
818                 child->setChildNeedsLayout(true, false);
819     
820             if (child->isPositioned())
821             {
822                 child->containingBlock()->insertPositionedObject(child);
823                 if (child->hasStaticX()) {
824                     if (style()->direction() == LTR)
825                         child->setStaticX(borderLeft()+paddingLeft());
826                     else
827                         child->setStaticX(borderRight()+paddingRight());
828                 }
829                 if (child->hasStaticY())
830                     child->setStaticY(m_height);
831                 child = iterator.next();
832                 continue;
833             } 
834     
835             // Compute the child's vertical margins.
836             child->calcVerticalMargins();
837     
838             // Add in the child's marginTop to our height.
839             m_height += child->marginTop();
840     
841             // Now do a layout.
842             child->layoutIfNeeded();
843     
844             // We can place the child now, using our value of box-align.
845             int childX = borderLeft() + paddingLeft();
846             switch (style()->boxAlign()) {
847                 case BCENTER:
848                 case BBASELINE: // Baseline just maps to center for vertical boxes
849                     childX += child->marginLeft() + max(0, (contentWidth() - (child->width() + child->marginLeft() + child->marginRight()))/2);
850                     break;
851                 case BEND:
852                     if (style()->direction() == RTL)
853                         childX += child->marginLeft();
854                     else
855                         childX += contentWidth() - child->marginRight() - child->width();
856                     break;
857                 default: // BSTART/BSTRETCH
858                     if (style()->direction() == LTR)
859                         childX += child->marginLeft();
860                     else
861                         childX += contentWidth() - child->marginRight() - child->width();
862                     break;
863             }
864     
865             // Place the child.
866             placeChild(child, childX, m_height);
867             m_height += child->height() + child->marginBottom();
868     
869             if (child->isRenderBlock())
870                 static_cast<RenderBlock*>(child)->addVisualOverflow(static_cast<RenderBlock*>(child)->floatRect());
871
872             // See if this child has made our overflow need to grow.
873             m_overflowWidth = max(child->xPos() + child->overflowWidth(false), m_overflowWidth);
874             m_overflowLeft = min(child->xPos() + child->overflowLeft(false), m_overflowLeft);
875             
876             child = iterator.next();
877         }
878
879         yPos = m_height;
880         
881         if (!iterator.first() && hasLineIfEmpty())
882             m_height += lineHeight(true, true);
883     
884         m_height += toAdd;
885
886         // Negative margins can cause our height to shrink below our minimal height (border/padding).
887         // If this happens, ensure that the computed height is increased to the minimal height.
888         if (m_height < minHeight)
889             m_height = minHeight;
890
891         // Always make sure our overflowheight is at least our height.
892         if (m_overflowHeight < m_height)
893             m_overflowHeight = m_height;
894
895         // Now we have to calc our height, so we know how much space we have remaining.
896         oldHeight = m_height;
897         calcHeight();
898         if (oldHeight != m_height)
899             heightSpecified = true;
900
901         remainingSpace = borderTop() + paddingTop() + contentHeight() - yPos;
902         
903         if (m_flexingChildren)
904             haveFlex = false; // We're done.
905         else if (haveFlex) {
906             // We have some flexible objects.  See if we need to grow/shrink them at all.
907             if (!remainingSpace)
908                 break;
909
910             // Allocate the remaining space among the flexible objects.  If we are trying to
911             // grow, then we go from the lowest flex group to the highest flex group.  For shrinking,
912             // we go from the highest flex group to the lowest group.
913             bool expanding = remainingSpace > 0;
914             unsigned int start = expanding ? lowestFlexGroup : highestFlexGroup;
915             unsigned int end = expanding? highestFlexGroup : lowestFlexGroup;
916             for (unsigned int i = start; i <= end && remainingSpace; i++) {
917                 // Always start off by assuming the group can get all the remaining space.
918                 int groupRemainingSpace = remainingSpace;
919                 do {
920                     // Flexing consists of multiple passes, since we have to change ratios every time an object hits its max/min-width
921                     // For a given pass, we always start off by computing the totalFlex of all objects that can grow/shrink at all, and
922                     // computing the allowed growth before an object hits its min/max width (and thus
923                     // forces a totalFlex recomputation).
924                     float totalFlex = 0.0f;
925                     child = iterator.first();
926                     while (child) {
927                         if (allowedChildFlex(child, expanding, i))
928                             totalFlex += child->style()->boxFlex();
929                         child = iterator.next();
930                     }
931                     child = iterator.first();
932                     int spaceAvailableThisPass = groupRemainingSpace;
933                     while (child) {
934                         int allowedFlex = allowedChildFlex(child, expanding, i);
935                         if (allowedFlex) {
936                             int projectedFlex = (allowedFlex == INT_MAX) ? allowedFlex : (int)(allowedFlex * (totalFlex / child->style()->boxFlex()));
937                             spaceAvailableThisPass = expanding ? min(spaceAvailableThisPass, projectedFlex) : max(spaceAvailableThisPass, projectedFlex);
938                         }
939                         child = iterator.next();
940                     }
941
942                     // The flex groups may not have any flexible objects this time around. 
943                     if (!spaceAvailableThisPass || totalFlex == 0.0f) {
944                         // If we just couldn't grow/shrink any more, then it's time to transition to the next flex group.
945                         groupRemainingSpace = 0;
946                         continue;
947                     }
948             
949                     // Now distribute the space to objects.
950                     child = iterator.first();
951                     while (child && spaceAvailableThisPass && totalFlex) {
952                         if (allowedChildFlex(child, expanding, i)) {
953                             int spaceAdd = (int)(spaceAvailableThisPass * (child->style()->boxFlex()/totalFlex));
954                             if (spaceAdd) {
955                                 child->setOverrideSize(child->overrideHeight() + spaceAdd);
956                                 m_flexingChildren = true;
957                                 relayoutChildren = true;
958                             }
959
960                             spaceAvailableThisPass -= spaceAdd;
961                             remainingSpace -= spaceAdd;
962                             groupRemainingSpace -= spaceAdd;
963                             
964                             totalFlex -= child->style()->boxFlex();
965                         }
966                         child = iterator.next();
967                     }
968                 } while (groupRemainingSpace);
969             }
970
971             // We didn't find any children that could grow.
972             if (haveFlex && !m_flexingChildren)
973                 haveFlex = false;
974         }        
975     } while (haveFlex);
976
977     if (style()->boxPack() != BSTART && remainingSpace > 0) {
978         // Children must be repositioned.
979         int offset = 0;
980         if (style()->boxPack() == BJUSTIFY) {
981             // Determine the total number of children.
982             int totalChildren = 0;
983             child = iterator.first();
984             while (child) {
985                 if (child->isPositioned()) {
986                     child = iterator.next();
987                     continue;
988                 }
989                 totalChildren++;
990                 child = iterator.next();
991             }
992             
993             // Iterate over the children and space them out according to the
994             // justification level.
995             if (totalChildren > 1) {
996                 totalChildren--;
997                 bool firstChild = true;
998                 child = iterator.first();
999                 while (child) {
1000                     if (child->isPositioned()) {
1001                         child = iterator.next();
1002                         continue;
1003                     }
1004                     
1005                     if (firstChild) {
1006                         firstChild = false;
1007                         child = iterator.next();
1008                         continue;
1009                     }
1010
1011                     offset += remainingSpace/totalChildren;
1012                     remainingSpace -= (remainingSpace/totalChildren);
1013                     totalChildren--;
1014                     placeChild(child, child->xPos(), child->yPos()+offset);
1015                     child = iterator.next();
1016                 }
1017             }
1018         } else {
1019             if (style()->boxPack() == BCENTER)
1020                 offset += remainingSpace/2;
1021             else // END
1022                 offset += remainingSpace;
1023             child = iterator.first();
1024             while (child) {
1025                 if (child->isPositioned()) {
1026                     child = iterator.next();
1027                     continue;
1028                 }
1029                 placeChild(child, child->xPos(), child->yPos()+offset);
1030                 child = iterator.next();
1031             }
1032         }
1033     }
1034     
1035     child = iterator.first();
1036     while (child && child->isPositioned()) {
1037         child = iterator.next();
1038     }
1039     
1040     if (child) {
1041         m_overflowTop = min(child->yPos() + child->overflowTop(false), m_overflowTop);
1042
1043         RenderObject* lastChild = child;
1044         while ((child = iterator.next())) {
1045             if (!child->isPositioned())
1046                 lastChild = child;
1047         }
1048         m_overflowHeight = max(lastChild->yPos() + lastChild->overflowHeight(false), m_overflowHeight);
1049     }
1050
1051     // So that the calcHeight in layoutBlock() knows to relayout positioned objects because of
1052     // a height change, we revert our height back to the intrinsic height before returning.
1053     if (heightSpecified)
1054         m_height = oldHeight; 
1055 }
1056
1057 void RenderFlexibleBox::placeChild(RenderObject* child, int x, int y)
1058 {
1059     IntRect oldRect(child->xPos(), child->yPos() , child->width(), child->height());
1060
1061     // Place the child.
1062     child->setPos(x, y);
1063
1064     // If the child moved, we have to repaint it as well as any floating/positioned
1065     // descendants.  An exception is if we need a layout.  In this case, we know we're going to
1066     // repaint ourselves (and the child) anyway.
1067     if (!selfNeedsLayout() && child->checkForRepaintDuringLayout())
1068         child->repaintDuringLayoutIfMoved(oldRect);
1069 }
1070
1071 int RenderFlexibleBox::allowedChildFlex(RenderObject* child, bool expanding, unsigned int group)
1072 {
1073     if (child->isPositioned() || child->style()->boxFlex() == 0.0f || child->style()->boxFlexGroup() != group)
1074         return 0;
1075                         
1076     if (expanding) {
1077         if (isHorizontal()) {
1078             // FIXME: For now just handle fixed values.
1079             int maxW = INT_MAX;
1080             int w = child->overrideWidth() - (child->borderLeft() + child->borderRight() + child->paddingLeft() + child->paddingRight());
1081             if (!child->style()->maxWidth().isUndefined() &&
1082                 child->style()->maxWidth().isFixed())
1083                 maxW = child->style()->maxWidth().value();
1084             else if (child->style()->maxWidth().type() == Intrinsic)
1085                 maxW = child->maxWidth();
1086             else if (child->style()->maxWidth().type() == MinIntrinsic)
1087                 maxW = child->minWidth();
1088             if (maxW == INT_MAX)
1089                 return maxW;
1090             return max(0, maxW - w);
1091         } else {
1092             // FIXME: For now just handle fixed values.
1093             int maxH = INT_MAX;
1094             int h = child->overrideHeight() - (child->borderTop() + child->borderBottom() + child->paddingTop() + child->paddingBottom());
1095             if (!child->style()->maxHeight().isUndefined() &&
1096                 child->style()->maxHeight().isFixed())
1097                 maxH = child->style()->maxHeight().value();
1098             if (maxH == INT_MAX)
1099                 return maxH;
1100             return max(0, maxH - h);
1101         }
1102     }
1103
1104     // FIXME: For now just handle fixed values.
1105     if (isHorizontal()) {
1106         int minW = child->minWidth();
1107         int w = child->overrideWidth() - (child->borderLeft() + child->borderRight() + child->paddingLeft() + child->paddingRight());
1108         if (child->style()->minWidth().isFixed())
1109             minW = child->style()->minWidth().value();
1110         else if (child->style()->minWidth().type() == Intrinsic)
1111             minW = child->maxWidth();
1112         else if (child->style()->minWidth().type() == MinIntrinsic)
1113             minW = child->minWidth();
1114             
1115         int allowedShrinkage = min(0, minW - w);
1116         return allowedShrinkage;
1117     } else {
1118         if (child->style()->minHeight().isFixed()) {
1119             int minH = child->style()->minHeight().value();
1120             int h = child->overrideHeight() - (child->borderLeft() + child->borderRight() + child->paddingLeft() + child->paddingRight());
1121             int allowedShrinkage = min(0, minH - h);
1122             return allowedShrinkage;
1123         }
1124     }
1125     
1126     return 0;
1127 }
1128
1129 const char *RenderFlexibleBox::renderName() const
1130 {
1131     if (isFloating())
1132         return "RenderFlexibleBox (floating)";
1133     if (isPositioned())
1134         return "RenderFlexibleBox (positioned)";
1135     if (isRelPositioned())
1136         return "RenderFlexibleBox (relative positioned)";
1137     return "RenderFlexibleBox";
1138 }
1139
1140 } // namespace WebCore