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