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