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