• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * This file is part of the render object implementation for KHTML.
3  *
4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
6  * Copyright (C) 2003 Apple Computer, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  */
24 
25 #include "config.h"
26 #include "RenderFlexibleBox.h"
27 
28 #include "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:
FlexBoxIterator(RenderFlexibleBox * parent)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 
reset()66     void reset()
67     {
68         m_currentChild = 0;
69         m_currentOrdinal = m_forward ? 0 : m_lastOrdinal + 1;
70     }
71 
first()72     RenderBox* first()
73     {
74         reset();
75         return next();
76     }
77 
next()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 
RenderFlexibleBox(Node * node)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 
~RenderFlexibleBox()118 RenderFlexibleBox::~RenderFlexibleBox()
119 {
120 }
121 
marginWidthForChild(RenderBox * child)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 
calcHorizontalPrefWidths()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 
calcVerticalPrefWidths()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 
computePreferredLogicalWidths()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 
layoutBlock(bool relayoutChildren,int)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.
gatherFlexChildrenInfo(FlexBoxIterator & iterator,bool relayoutChildren,unsigned int & highestFlexGroup,unsigned int & lowestFlexGroup,bool & haveFlex)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 
layoutHorizontalBox(bool relayoutChildren)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 
layoutVerticalBox(bool relayoutChildren)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 
applyLineClamp(FlexBoxIterator & iterator,bool relayoutChildren)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 
placeChild(RenderBox * child,int x,int y)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 
allowedChildFlex(RenderBox * child,bool expanding,unsigned int group)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 
renderName() const1018 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