Fix for 3678031, implement better flexing for Emerson headers. This involved adding...
[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 ||
268         (parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL &&
269          parent()->style()->boxAlign() == BSTRETCH))
270         relayoutChildren = true;
271
272     m_height = 0;
273     m_overflowHeight = 0;
274     m_flexingChildren = m_stretchingChildren = false;
275
276     initMaxMarginValues();
277
278     if (scrollsOverflow()) {
279         // For overflow:scroll blocks, ensure we have both scrollbars in place always.
280         if (style()->overflow() == OSCROLL) {
281             m_layer->setHasHorizontalScrollbar(true);
282             m_layer->setHasVerticalScrollbar(true);
283         }
284
285         // Move the scrollbars aside during layout.  The layer will move them back when it
286         // does painting or event handling.
287         m_layer->moveScrollbarsAside();
288     }
289
290     if (isHorizontal())
291         layoutHorizontalBox(relayoutChildren);
292     else
293         layoutVerticalBox(relayoutChildren);
294     
295     oldHeight = m_height;
296     calcHeight();
297     if (oldHeight != m_height) {
298         relayoutChildren = true;
299
300         // If the block got expanded in size, then increase our overflowheight to match.
301         if (m_overflowHeight > m_height)
302             m_overflowHeight -= (borderBottom()+paddingBottom());
303         if (m_overflowHeight < m_height)
304             m_overflowHeight = m_height;
305     }
306
307     layoutPositionedObjects( relayoutChildren );
308
309     //kdDebug() << renderName() << " layout width=" << m_width << " height=" << m_height << endl;
310
311     if (!isFloatingOrPositioned() && m_height == 0) {
312         // We are a block with no border and padding and a computed height
313         // of 0.  The CSS spec states that zero-height blocks collapse their margins
314         // together.
315         // When blocks are self-collapsing, we just use the top margin values and set the
316         // bottom margin max values to 0.  This way we don't factor in the values
317         // twice when we collapse with our previous vertically adjacent and
318         // following vertically adjacent blocks.
319         if (m_maxBottomPosMargin > m_maxTopPosMargin)
320             m_maxTopPosMargin = m_maxBottomPosMargin;
321         if (m_maxBottomNegMargin > m_maxTopNegMargin)
322             m_maxTopNegMargin = m_maxBottomNegMargin;
323         m_maxBottomNegMargin = m_maxBottomPosMargin = 0;
324     }
325
326     // Always ensure our overflow width is at least as large as our width.
327     if (m_overflowWidth < m_width)
328         m_overflowWidth = m_width;
329
330     // Update our scrollbars if we're overflow:auto/scroll/hidden now that we know if
331     // we overflow or not.
332     if (hasOverflowClip())
333         m_layer->updateScrollInfoAfterLayout();
334
335     // Repaint with our new bounds if they are different from our old bounds.
336     if (checkForRepaint)
337         repaintAfterLayoutIfNeeded(oldBounds, oldBounds);
338     
339     setNeedsLayout(false);
340 }
341
342 void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren)
343 {
344     int toAdd = borderBottom() + paddingBottom();
345     int yPos = borderTop() + paddingTop();
346     int xPos = borderLeft() + paddingLeft();
347     bool heightSpecified = false;
348     int oldHeight = 0;
349     
350     unsigned int highestFlexGroup = 0;
351     unsigned int lowestFlexGroup = 0;
352     bool haveFlex = false;
353     int remainingSpace = 0;
354     m_overflowHeight = m_height;
355
356     // The first walk over our kids is to find out if we have any flexible children.
357     FlexBoxIterator iterator(this);
358     RenderObject* child = iterator.next();
359     while (child) {
360         // Check to see if this child flexes.
361         if (!child->isPositioned() && child->style()->boxFlex() > 0.0f) {
362             // We always have to lay out flexible objects again, since the flex distribution
363             // may have changed, and we need to reallocate space.
364             child->setOverrideSize(-1);
365             if (!relayoutChildren)
366                 child->setChildNeedsLayout(true, false);
367             haveFlex = true;
368             unsigned int flexGroup = child->style()->boxFlexGroup();
369             if (lowestFlexGroup == 0)
370                 lowestFlexGroup = flexGroup;
371             if (flexGroup < lowestFlexGroup)
372                 lowestFlexGroup = flexGroup;
373             if (flexGroup > highestFlexGroup)
374                 highestFlexGroup = flexGroup;
375         }
376         child = iterator.next();
377     }
378     
379     // We do 2 passes.  The first pass is simply to lay everyone out at
380     // their preferred widths.  The second pass handles flexing the children.
381     do {
382         // Reset our height.
383         m_height = yPos;
384         m_overflowHeight = m_height;
385         xPos = borderLeft() + paddingLeft();
386                 
387         // Our first pass is done without flexing.  We simply lay the children
388         // out within the box.  We have to do a layout first in order to determine
389         // our box's intrinsic height.
390         int maxAscent = 0, maxDescent = 0;
391         child = iterator.first();
392         while (child) {
393             // make sure we relayout children if we need it.
394             if (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())))
395                 child->setChildNeedsLayout(true, false);
396             
397             if (child->isPositioned()) {
398                 child = iterator.next();
399                 continue;
400             }
401     
402             // Compute the child's vertical margins.
403             child->calcVerticalMargins();
404     
405             // Now do the layout.
406             child->layoutIfNeeded();
407     
408             // Update our height and overflow height.
409             if (style()->boxAlign() == BBASELINE) {
410                 int ascent = child->marginTop() + child->getBaselineOfFirstLineBox();
411                 if (ascent == -1)
412                     ascent = child->marginTop() + child->height() + child->marginBottom();
413                 int descent = (child->marginTop() + child->height() + child->marginBottom()) - ascent;
414                 
415                 // Update our maximum ascent.
416                 maxAscent = kMax(maxAscent, ascent);
417                 
418                 // Update our maximum descent.
419                 maxDescent = kMax(maxDescent, descent);
420                 
421                 // Now update our height.
422                 m_height = kMax(yPos + maxAscent + maxDescent, m_height);
423             }
424             else
425                 m_height = kMax(m_height, yPos + child->marginTop() + child->height() + child->marginBottom());
426
427             child = iterator.next();
428         }
429         m_height += toAdd;
430
431         // Always make sure our overflowheight is at least our height.
432         if (m_overflowHeight < m_height)
433             m_overflowHeight = m_height;
434         
435         oldHeight = m_height;
436         calcHeight();
437
438         relayoutChildren = false;
439         if (oldHeight != m_height) {
440             heightSpecified = true;
441     
442             // If the block got expanded in size, then increase our overflowheight to match.
443             if (m_overflowHeight > m_height)
444                 m_overflowHeight -= (borderBottom() + paddingBottom());
445             if (m_overflowHeight < m_height)
446                 m_overflowHeight = m_height;
447         }
448         
449         // Now that our height is actually known, we can place our boxes.
450         m_stretchingChildren = (style()->boxAlign() == BSTRETCH);
451         child = iterator.first();
452         while (child) {
453             if (child->isPositioned()) {
454                 child->containingBlock()->insertPositionedObject(child);
455                 if (child->hasStaticX()) {
456                     if (style()->direction() == LTR)
457                         child->setStaticX(xPos);
458                     else child->setStaticX(width() - xPos);
459                 }
460                 if (child->hasStaticY())
461                     child->setStaticY(yPos);
462                 child = iterator.next();
463                 continue;
464             }
465     
466             // We need to see if this child's height has changed, since we make block elements
467             // fill the height of a containing box by default.
468             // Now do a layout.
469             int oldChildHeight = child->height();
470             static_cast<RenderBox*>(child)->calcHeight();
471             if (oldChildHeight != child->height())
472                 child->setChildNeedsLayout(true, false);
473             child->layoutIfNeeded();
474     
475             // We can place the child now, using our value of box-align.
476             xPos += child->marginLeft();
477             int childY = yPos;
478             switch (style()->boxAlign()) {
479                 case BCENTER:
480                     childY += child->marginTop() + (contentHeight() - (child->height() + child->marginTop() + child->marginBottom()))/2;
481                     break;
482                 case BBASELINE: {
483                     int ascent = child->marginTop() + child->getBaselineOfFirstLineBox();
484                     if (ascent == -1)
485                         ascent = child->marginTop() + child->height() + child->marginBottom();
486                     childY += child->marginTop() + (maxAscent - ascent);
487                     break;
488                 }
489                 case BEND:
490                     childY += contentHeight() - child->marginBottom() - child->height();
491                     break;
492                 default: // BSTART
493                     childY += child->marginTop();
494                     break;
495             }
496
497             placeChild(child, xPos, childY);
498             m_overflowHeight = kMax(m_overflowHeight, childY + child->overflowHeight(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 ? kMin(spaceAvailableThisPass, projectedFlex) : kMax(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 (xPos > m_overflowWidth)
585         m_overflowWidth = xPos;
586
587     if (remainingSpace > 0 && ((style()->direction() == LTR && style()->boxPack() != BSTART) ||
588                                (style()->direction() == RTL && style()->boxPack() != BEND))) {
589         // Children must be repositioned.
590         int offset = 0;
591         if (style()->boxPack() == BJUSTIFY) {
592             // Determine the total number of children.
593             int totalChildren = 0;
594             child = iterator.first();
595             while (child) {
596                 if (child->isPositioned()) {
597                     child = iterator.next();
598                     continue;
599                 }
600                 totalChildren++;
601                 child = iterator.next();
602             }
603
604             // Iterate over the children and space them out according to the
605             // justification level.
606             if (totalChildren > 1) {
607                 totalChildren--;
608                 bool firstChild = true;
609                 child = iterator.first();
610                 while (child) {
611                     if (child->isPositioned()) {
612                         child = iterator.next();
613                         continue;
614                     }
615
616                     if (firstChild) {
617                         firstChild = false;
618                         child = iterator.next();
619                         continue;
620                     }
621
622                     offset += remainingSpace/totalChildren;
623                     remainingSpace -= (remainingSpace/totalChildren);
624                     totalChildren--;
625
626                     placeChild(child, child->xPos()+offset, child->yPos());
627                     child = iterator.next();
628                 }
629             }
630         }
631         else {
632             if (style()->boxPack() == BCENTER)
633                 offset += remainingSpace/2;
634             else // END for LTR, START for RTL
635                 offset += remainingSpace;
636             child = iterator.first();
637             while (child) {
638                 if (child->isPositioned()) {
639                     child = iterator.next();
640                     continue;
641                 }
642                 placeChild(child, child->xPos()+offset, child->yPos());
643                 child = iterator.next();
644             }
645         }
646     }
647     
648     // So that the calcHeight in layoutBlock() knows to relayout positioned objects because of
649     // a height change, we revert our height back to the intrinsic height before returning.
650     if (heightSpecified)
651         m_height = oldHeight;
652 }
653
654 void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren)
655 {
656     int xPos = borderLeft() + paddingLeft();
657     int yPos = borderTop() + paddingTop();
658     if( style()->direction() == RTL )
659         xPos = m_width - paddingRight() - borderRight();
660     int toAdd = borderBottom() + paddingBottom();
661     bool heightSpecified = false;
662     int oldHeight = 0;
663     
664     unsigned int highestFlexGroup = 0;
665     unsigned int lowestFlexGroup = 0;
666     bool haveFlex = false;
667     int remainingSpace = 0;
668     
669     // The first walk over our kids is to find out if we have any flexible children.
670     FlexBoxIterator iterator(this);
671     RenderObject *child = iterator.next();
672     while (child) {
673         // Check to see if this child flexes.
674         if (!child->isPositioned() && child->style()->boxFlex() > 0.0f) {
675             // We always have to lay out flexible objects again, since the flex distribution
676             // may have changed, and we need to reallocate space.
677             child->setOverrideSize(-1);
678             if (!relayoutChildren)
679                 child->setChildNeedsLayout(true);
680             haveFlex = true;
681             unsigned int flexGroup = child->style()->boxFlexGroup();
682             if (lowestFlexGroup == 0)
683                 lowestFlexGroup = flexGroup;
684             if (flexGroup < lowestFlexGroup)
685                 lowestFlexGroup = flexGroup;
686             if (flexGroup > highestFlexGroup)
687                 highestFlexGroup = flexGroup;
688         }
689         child = iterator.next();
690     }
691
692 #if APPLE_CHANGES
693     // We confine the line clamp ugliness to vertical flexible boxes (thus keeping it out of
694     // mainstream block layout) and put it all inside APPLE_CHANGES to denote that this is not
695     // really part of the XUL box model.
696     bool haveLineClamp = style()->lineClamp() >= 0 && style()->lineClamp() <= 100;
697     if (haveLineClamp) {
698         int maxLineCount = 1;
699         child = iterator.first();
700         while (child) {
701             if (!child->isPositioned()) {
702                 if (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())) ||
703                     (child->style()->height().isVariable() && child->isBlockFlow() && !child->needsLayout())) {
704                     child->setChildNeedsLayout(true);
705                     
706                     // Dirty all the positioned objects.
707                     static_cast<RenderBlock*>(child)->markPositionedObjectsForLayout();
708                 }
709                 child->layoutIfNeeded();
710                 if (child->style()->height().isVariable() && child->isBlockFlow())
711                     maxLineCount = kMax(maxLineCount, static_cast<RenderBlock*>(child)->lineCount());
712             }
713             child = iterator.next();
714         }
715         
716         // Get the # of lines and then alter all block flow children with auto height to use the
717         // specified height.
718         int numLines = int(maxLineCount*style()->lineClamp()/100.0 + 1.0);
719         if (numLines < maxLineCount) {
720             child = iterator.first();
721             while (child) {
722                 if (!child->isPositioned() && child->style()->height().isVariable() && child->isBlockFlow() &&
723                     static_cast<RenderBlock*>(child)->lineCount() > numLines) {
724                     int newHeight = static_cast<RenderBlock*>(child)->heightForLineCount(numLines);
725                     if (newHeight != child->height()) {
726                         child->setChildNeedsLayout(true);
727                         child->setOverrideSize(newHeight);
728                         m_flexingChildren = true;
729                         child->layoutIfNeeded();
730                         m_flexingChildren = false;
731                         child->setOverrideSize(-1);
732                     }
733                 }
734                 child = iterator.next();
735             }
736         }
737     }
738 #endif
739
740     // We do 2 passes.  The first pass is simply to lay everyone out at
741     // their preferred widths.  The second pass handles flexing the children.
742     // Our first pass is done without flexing.  We simply lay the children
743     // out within the box.
744     do {
745         m_height = borderTop() + paddingTop();
746         int minHeight = m_height + toAdd;
747         m_overflowHeight = m_height;
748
749         child = iterator.first();
750         while (child) {
751             // make sure we relayout children if we need it.
752             if (!haveLineClamp && (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent()))))
753                 child->setChildNeedsLayout(true);
754     
755             if (child->isPositioned())
756             {
757                 child->containingBlock()->insertPositionedObject(child);
758                 if (child->hasStaticX()) {
759                     if (style()->direction() == LTR)
760                         child->setStaticX(borderLeft()+paddingLeft());
761                     else
762                         child->setStaticX(borderRight()+paddingRight());
763                 }
764                 if (child->hasStaticY())
765                     child->setStaticY(m_height);
766                 child = iterator.next();
767                 continue;
768             } 
769     
770             // Compute the child's vertical margins.
771             child->calcVerticalMargins();
772     
773             // Add in the child's marginTop to our height.
774             m_height += child->marginTop();
775     
776             // Now do a layout.
777             child->layoutIfNeeded();
778     
779             // We can place the child now, using our value of box-align.
780             int childX = borderLeft() + paddingLeft();
781             switch (style()->boxAlign()) {
782                 case BCENTER:
783                 case BBASELINE: // Baseline just maps to center for vertical boxes
784                     childX += child->marginLeft() + (contentWidth() - (child->width() + child->marginLeft() + child->marginRight()))/2;
785                     break;
786                 case BEND:
787                     if (style()->direction() == RTL)
788                         childX += child->marginLeft();
789                     else
790                         childX += contentWidth() - child->marginRight() - child->width();
791                     break;
792                 default: // BSTART/BSTRETCH
793                     if (style()->direction() == LTR)
794                         childX += child->marginLeft();
795                     else
796                         childX += contentWidth() - child->marginRight() - child->width();
797                     break;
798             }
799     
800             // Place the child.
801             placeChild(child, childX, m_height);
802             m_height += child->height() + child->marginBottom();
803     
804             // See if this child has made our overflow need to grow.
805             // XXXdwh Work with left overflow as well as right overflow.
806             int rightChildPos = child->xPos() + kMax(child->overflowWidth(false), child->width());
807             if (rightChildPos > m_overflowWidth)
808                 m_overflowWidth = rightChildPos;
809             
810             child = iterator.next();
811         }
812
813         yPos = m_height;
814         m_height += toAdd;
815
816         // Negative margins can cause our height to shrink below our minimal height (border/padding).
817         // If this happens, ensure that the computed height is increased to the minimal height.
818         if (m_height < minHeight)
819             m_height = minHeight;
820
821         // Always make sure our overflowheight is at least our height.
822         if (m_overflowHeight < m_height)
823             m_overflowHeight = m_height;
824
825         // Now we have to calc our height, so we know how much space we have remaining.
826         oldHeight = m_height;
827         calcHeight();
828         if (oldHeight != m_height)
829             heightSpecified = true;
830
831         remainingSpace = borderTop() + paddingTop() + contentHeight() - yPos;
832         
833         if (m_flexingChildren)
834             haveFlex = false; // We're done.
835         else if (haveFlex) {
836             // We have some flexible objects.  See if we need to grow/shrink them at all.
837             if (!remainingSpace)
838                 break;
839
840             // Allocate the remaining space among the flexible objects.  If we are trying to
841             // grow, then we go from the lowest flex group to the highest flex group.  For shrinking,
842             // we go from the highest flex group to the lowest group.
843             bool expanding = remainingSpace > 0;
844             unsigned int start = expanding ? lowestFlexGroup : highestFlexGroup;
845             unsigned int end = expanding? highestFlexGroup : lowestFlexGroup;
846             for (unsigned int i = start; i <= end && remainingSpace; i++) {
847                 // Always start off by assuming the group can get all the remaining space.
848                 int groupRemainingSpace = remainingSpace;
849                 do {
850                     // Flexing consists of multiple passes, since we have to change ratios every time an object hits its max/min-width
851                     // For a given pass, we always start off by computing the totalFlex of all objects that can grow/shrink at all, and
852                     // computing the allowed growth before an object hits its min/max width (and thus
853                     // forces a totalFlex recomputation).
854                     float totalFlex = 0.0f;
855                     child = iterator.first();
856                     while (child) {
857                         if (allowedChildFlex(child, expanding, i))
858                             totalFlex += child->style()->boxFlex();
859                         child = iterator.next();
860                     }
861                     child = iterator.first();
862                     int spaceAvailableThisPass = groupRemainingSpace;
863                     while (child) {
864                         int allowedFlex = allowedChildFlex(child, expanding, i);
865                         if (allowedFlex) {
866                             int projectedFlex = (allowedFlex == INT_MAX) ? allowedFlex : (int)(allowedFlex * (totalFlex / child->style()->boxFlex()));
867                             spaceAvailableThisPass = expanding ? kMin(spaceAvailableThisPass, projectedFlex) : kMax(spaceAvailableThisPass, projectedFlex);
868                         }
869                         child = iterator.next();
870                     }
871
872                     // The flex groups may not have any flexible objects this time around. 
873                     if (!spaceAvailableThisPass || totalFlex == 0.0f) {
874                         // If we just couldn't grow/shrink any more, then it's time to transition to the next flex group.
875                         groupRemainingSpace = 0;
876                         continue;
877                     }
878             
879                     // Now distribute the space to objects.
880                     child = iterator.first();
881                     while (child && spaceAvailableThisPass && totalFlex) {
882                         if (allowedChildFlex(child, expanding, i)) {
883                             int spaceAdd = (int)(spaceAvailableThisPass * (child->style()->boxFlex()/totalFlex));
884                             if (spaceAdd) {
885                                 child->setOverrideSize(child->overrideHeight() + spaceAdd);
886                                 m_flexingChildren = true;
887                                 relayoutChildren = true;
888                             }
889
890                             spaceAvailableThisPass -= spaceAdd;
891                             remainingSpace -= spaceAdd;
892                             groupRemainingSpace -= spaceAdd;
893                             
894                             totalFlex -= child->style()->boxFlex();
895                         }
896                         child = iterator.next();
897                     }
898                 } while (groupRemainingSpace);
899             }
900
901             // We didn't find any children that could grow.
902             if (haveFlex && !m_flexingChildren)
903                 haveFlex = false;
904         }        
905     } while (haveFlex);
906
907     if (style()->boxPack() != BSTART && remainingSpace > 0) {
908         // Children must be repositioned.
909         int offset = 0;
910         if (style()->boxPack() == BJUSTIFY) {
911             // Determine the total number of children.
912             int totalChildren = 0;
913             child = iterator.first();
914             while (child) {
915                 if (child->isPositioned()) {
916                     child = iterator.next();
917                     continue;
918                 }
919                 totalChildren++;
920                 child = iterator.next();
921             }
922             
923             // Iterate over the children and space them out according to the
924             // justification level.
925             if (totalChildren > 1) {
926                 totalChildren--;
927                 bool firstChild = true;
928                 child = iterator.first();
929                 while (child) {
930                     if (child->isPositioned()) {
931                         child = iterator.next();
932                         continue;
933                     }
934                     
935                     if (firstChild) {
936                         firstChild = false;
937                         child = iterator.next();
938                         continue;
939                     }
940
941                     offset += remainingSpace/totalChildren;
942                     remainingSpace -= (remainingSpace/totalChildren);
943                     totalChildren--;
944                     placeChild(child, child->xPos(), child->yPos()+offset);
945                     child = iterator.next();
946                 }
947             }
948         }
949         else {
950             if (style()->boxPack() == BCENTER)
951                 offset += remainingSpace/2;
952             else // END
953                 offset += remainingSpace;
954             child = iterator.first();
955             while (child) {
956                 if (child->isPositioned()) {
957                     child = iterator.next();
958                     continue;
959                 }
960                 placeChild(child, child->xPos(), child->yPos()+offset);
961                 child = iterator.next();
962             }
963         }
964     }
965     
966     // So that the calcHeight in layoutBlock() knows to relayout positioned objects because of
967     // a height change, we revert our height back to the intrinsic height before returning.
968     if (heightSpecified)
969         m_height = oldHeight;    
970 }
971
972 void RenderFlexibleBox::placeChild(RenderObject* child, int x, int y)
973 {
974     int oldChildX = child->xPos();
975     int oldChildY = child->yPos();
976
977     // Place the child.
978     child->setPos(x, y);
979
980     // If the child moved, we have to repaint it as well as any floating/positioned
981     // descendants.  An exception is if we need a layout.  In this case, we know we're going to
982     // repaint ourselves (and the child) anyway.
983     if (!selfNeedsLayout() && child->checkForRepaintDuringLayout())
984         child->repaintDuringLayoutIfMoved(oldChildX, oldChildY);
985 }
986
987 int RenderFlexibleBox::allowedChildFlex(RenderObject* child, bool expanding, unsigned int group)
988 {
989     if (child->isPositioned() || child->style()->boxFlex() == 0.0f || child->style()->boxFlexGroup() != group)
990         return 0;
991                         
992     if (expanding) {
993         if (isHorizontal()) {
994             // FIXME: For now just handle fixed values.
995             int maxW = INT_MAX;
996             int w = child->overrideWidth() - (child->borderLeft() + child->borderRight() + child->paddingLeft() + child->paddingRight());
997             if (child->style()->maxWidth().value != UNDEFINED &&
998                 child->style()->maxWidth().isFixed())
999                 maxW = child->style()->maxWidth().value;
1000             else if (child->style()->maxWidth().type == Intrinsic)
1001                 maxW = child->maxWidth();
1002             else if (child->style()->maxWidth().type == MinIntrinsic)
1003                 maxW = child->minWidth();
1004             int allowedGrowth = kMax(0, maxW - w);
1005             return allowedGrowth;
1006         }
1007         else {
1008             // FIXME: For now just handle fixed values.
1009             int maxH = INT_MAX;
1010             int h = child->overrideHeight() - (child->borderTop() + child->borderBottom() + child->paddingTop() + child->paddingBottom());
1011             if (child->style()->maxHeight().value != UNDEFINED &&
1012                 child->style()->maxHeight().isFixed())
1013                 maxH = child->style()->maxHeight().value;
1014             int allowedGrowth = kMax(0, maxH - h);
1015             return allowedGrowth;
1016         }
1017     }
1018
1019     // FIXME: For now just handle fixed values.
1020     if (isHorizontal()) {
1021         int minW = child->minWidth();
1022         int w = child->contentWidth();
1023         if (child->style()->minWidth().isFixed())
1024             minW = child->style()->minWidth().value;
1025         else if (child->style()->minWidth().type == Intrinsic)
1026             minW = child->maxWidth();
1027         else if (child->style()->minWidth().type == MinIntrinsic)
1028             minW = child->minWidth();
1029             
1030         int allowedShrinkage = kMin(0, minW - w);
1031         return allowedShrinkage;
1032     }
1033     else {
1034         if (child->style()->minHeight().isFixed()) {
1035             int minH = child->style()->minHeight().value;
1036             int h = child->contentHeight();
1037             int allowedShrinkage = kMin(0, minH - h);
1038             return allowedShrinkage;
1039         }
1040     }
1041     
1042     return 0;
1043 }
1044
1045 const char *RenderFlexibleBox::renderName() const
1046 {
1047     if (isFloating())
1048         return "RenderFlexibleBox (floating)";
1049     if (isPositioned())
1050         return "RenderFlexibleBox (positioned)";
1051     if (isRelPositioned())
1052         return "RenderFlexibleBox (relative positioned)";
1053     return "RenderFlexibleBox";
1054 }
1055
1056
1057 } // namespace khtml
1058