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