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