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