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