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