2 * This file is part of the render object implementation for KHTML.
4 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5 * (C) 1999 Antti Koivisto (koivisto@kde.org)
6 * Copyright (C) 2003 Apple Computer, Inc.
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.
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.
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.
26 #include "core/rendering/RenderDeprecatedFlexibleBox.h"
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"
40 class FlexBoxIterator {
42 FlexBoxIterator(RenderDeprecatedFlexibleBox* parent)
46 if (m_box->style()->boxOrient() == HORIZONTAL && !m_box->style()->isLeftToRightDirection())
47 m_forward = m_box->style()->boxDirection() != BNORMAL;
49 m_forward = m_box->style()->boxDirection() == BNORMAL;
51 // No choice, since we're going backwards, we have to find out the highest ordinal up front.
52 RenderBox* child = m_box->firstChildBox();
54 if (child->style()->boxOrdinalGroup() > m_largestOrdinal)
55 m_largestOrdinal = child->style()->boxOrdinalGroup();
56 child = child->nextSiblingBox();
66 m_ordinalIteration = -1;
78 if (!m_currentChild) {
81 if (!m_ordinalIteration)
82 m_currentOrdinal = m_forward ? 1 : m_largestOrdinal;
84 if (static_cast<size_t>(m_ordinalIteration) >= m_ordinalValues.size() + 1)
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());
92 m_currentOrdinal = m_forward ? m_sortedOrdinalValues[m_ordinalIteration - 1] : m_sortedOrdinalValues[m_sortedOrdinalValues.size() - m_ordinalIteration];
95 m_currentChild = m_forward ? m_box->firstChildBox() : m_box->lastChildBox();
97 m_currentChild = m_forward ? m_currentChild->nextSiblingBox() : m_currentChild->previousSiblingBox();
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;
107 bool notFirstOrdinalValue()
109 unsigned int firstOrdinalValue = m_forward ? 1 : m_largestOrdinal;
110 return m_currentOrdinal == firstOrdinalValue && m_currentChild->style()->boxOrdinalGroup() != firstOrdinalValue;
113 RenderDeprecatedFlexibleBox* m_box;
114 RenderBox* m_currentChild;
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;
123 RenderDeprecatedFlexibleBox::RenderDeprecatedFlexibleBox(Element* element)
124 : RenderBlock(element)
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);
135 UseCounter::count(document(), UseCounter::DeprecatedFlexboxWebContent);
139 RenderDeprecatedFlexibleBox::~RenderDeprecatedFlexibleBox()
143 RenderDeprecatedFlexibleBox* RenderDeprecatedFlexibleBox::createAnonymous(Document* document)
145 RenderDeprecatedFlexibleBox* renderer = new RenderDeprecatedFlexibleBox(0);
146 renderer->setDocumentForAnonymous(document);
150 static LayoutUnit marginWidthForChild(RenderBox* child)
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();
165 static bool childDoesNotAffectWidthOrFlexing(RenderObject* child)
167 // Positioned children and collapsed children don't affect the min/max width.
168 return child->isOutOfFlowPositioned() || child->style()->visibility() == COLLAPSE;
171 static LayoutUnit contentWidthForChild(RenderBox* child)
173 if (child->hasOverrideWidth())
174 return child->overrideLogicalContentWidth();
175 return child->logicalWidth() - child->borderAndPaddingLogicalWidth();
178 static LayoutUnit contentHeightForChild(RenderBox* child)
180 if (child->hasOverrideHeight())
181 return child->overrideLogicalContentHeight();
182 return child->logicalHeight() - child->borderAndPaddingLogicalHeight();
185 void RenderDeprecatedFlexibleBox::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
187 RenderStyle* oldStyle = style();
188 if (oldStyle && !oldStyle->lineClamp().isNone() && newStyle->lineClamp().isNone())
191 RenderBlock::styleWillChange(diff, newStyle);
194 void RenderDeprecatedFlexibleBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
196 if (hasMultipleLines() || isVertical()) {
197 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
198 if (childDoesNotAffectWidthOrFlexing(child))
201 LayoutUnit margin = marginWidthForChild(child);
202 LayoutUnit width = child->minPreferredLogicalWidth() + margin;
203 minLogicalWidth = max(width, minLogicalWidth);
205 width = child->maxPreferredLogicalWidth() + margin;
206 maxLogicalWidth = max(width, maxLogicalWidth);
209 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
210 if (childDoesNotAffectWidthOrFlexing(child))
213 LayoutUnit margin = marginWidthForChild(child);
214 minLogicalWidth += child->minPreferredLogicalWidth() + margin;
215 maxLogicalWidth += child->maxPreferredLogicalWidth() + margin;
219 maxLogicalWidth = max(minLogicalWidth, maxLogicalWidth);
221 LayoutUnit scrollbarWidth = instrinsicScrollbarLogicalWidth();
222 maxLogicalWidth += scrollbarWidth;
223 minLogicalWidth += scrollbarWidth;
226 void RenderDeprecatedFlexibleBox::computePreferredLogicalWidths()
228 ASSERT(preferredLogicalWidthsDirty());
230 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = 0;
231 if (style()->width().isFixed() && style()->width().value() > 0)
232 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(style()->width().value());
234 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
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()));
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()));
246 LayoutUnit borderAndPadding = borderAndPaddingLogicalWidth();
247 m_minPreferredLogicalWidth += borderAndPadding;
248 m_maxPreferredLogicalWidth += borderAndPadding;
250 clearPreferredLogicalWidthsDirty();
253 void RenderDeprecatedFlexibleBox::layoutBlock(bool relayoutChildren)
255 ASSERT(needsLayout());
257 if (!relayoutChildren && simplifiedLayout())
260 LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
261 LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode());
263 RenderFlowThread* flowThread = flowThreadContainingBlock();
264 if (updateRegionsAndShapesLogicalSize(flowThread))
265 relayoutChildren = true;
267 LayoutSize previousSize = size();
269 updateLogicalWidth();
270 updateLogicalHeight();
272 if (previousSize != size()
273 || (parent()->isDeprecatedFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL
274 && parent()->style()->boxAlign() == BSTRETCH))
275 relayoutChildren = true;
279 m_stretchingChildren = false;
282 layoutHorizontalBox(relayoutChildren);
284 layoutVerticalBox(relayoutChildren);
286 LayoutUnit oldClientAfterEdge = clientLogicalBottom();
287 updateLogicalHeight();
289 if (previousSize.height() != height())
290 relayoutChildren = true;
292 layoutPositionedObjects(relayoutChildren || isRoot());
294 computeRegionRangeForBlock(flowThread);
296 computeOverflow(oldClientAfterEdge);
300 updateLayerTransform();
302 if (view()->layoutState()->pageLogicalHeight())
303 setPageLogicalOffset(view()->layoutState()->pageLogicalOffset(this, logicalTop()));
305 // Update our scrollbars if we're overflow:auto/scroll/hidden now that we know if
306 // we overflow or not.
307 if (hasOverflowClip())
308 layer()->scrollableArea()->updateAfterLayout();
310 // Repaint with our new bounds if they are different from our old bounds.
311 repainter.repaintAfterLayout();
316 // The first walk over our kids is to find out if we have any flexible children.
317 static void gatherFlexChildrenInfo(FlexBoxIterator& iterator, bool relayoutChildren, unsigned int& highestFlexGroup, unsigned int& lowestFlexGroup, bool& haveFlex)
319 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
320 // Check to see if this child flexes.
321 if (!childDoesNotAffectWidthOrFlexing(child) && child->style()->boxFlex() > 0.0f) {
322 // We always have to lay out flexible objects again, since the flex distribution
323 // may have changed, and we need to reallocate space.
324 child->clearOverrideSize();
325 if (!relayoutChildren)
326 child->setChildNeedsLayout(MarkOnlyThis);
328 unsigned int flexGroup = child->style()->boxFlexGroup();
329 if (lowestFlexGroup == 0)
330 lowestFlexGroup = flexGroup;
331 if (flexGroup < lowestFlexGroup)
332 lowestFlexGroup = flexGroup;
333 if (flexGroup > highestFlexGroup)
334 highestFlexGroup = flexGroup;
339 void RenderDeprecatedFlexibleBox::layoutHorizontalBox(bool relayoutChildren)
341 LayoutUnit toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight();
342 LayoutUnit yPos = borderTop() + paddingTop();
343 LayoutUnit xPos = borderLeft() + paddingLeft();
344 bool heightSpecified = false;
345 LayoutUnit oldHeight = 0;
347 LayoutUnit remainingSpace = 0;
350 FlexBoxIterator iterator(this);
351 unsigned int highestFlexGroup = 0;
352 unsigned int lowestFlexGroup = 0;
353 bool haveFlex = false, flexingChildren = false;
354 gatherFlexChildrenInfo(iterator, relayoutChildren, highestFlexGroup, lowestFlexGroup, haveFlex);
356 RenderBlock::startDelayUpdateScrollInfo();
358 // We do 2 passes. The first pass is simply to lay everyone out at
359 // their preferred widths. The second pass handles flexing the children.
364 xPos = borderLeft() + paddingLeft();
366 // Our first pass is done without flexing. We simply lay the children
367 // out within the box. We have to do a layout first in order to determine
368 // our box's intrinsic height.
369 LayoutUnit maxAscent = 0, maxDescent = 0;
370 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
371 if (child->isOutOfFlowPositioned())
374 SubtreeLayoutScope layoutScope(child);
375 if (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())))
376 layoutScope.setChildNeedsLayout(child);
378 // Compute the child's vertical margins.
379 child->computeAndSetBlockDirectionMargins(this);
381 if (!child->needsLayout())
382 child->markForPaginationRelayoutIfNeeded(layoutScope);
384 // Now do the layout.
385 child->layoutIfNeeded();
387 // Update our height and overflow height.
388 if (style()->boxAlign() == BBASELINE) {
389 LayoutUnit ascent = child->firstLineBoxBaseline();
391 ascent = child->height() + child->marginBottom();
392 ascent += child->marginTop();
393 LayoutUnit descent = (child->height() + child->marginHeight()) - ascent;
395 // Update our maximum ascent.
396 maxAscent = max(maxAscent, ascent);
398 // Update our maximum descent.
399 maxDescent = max(maxDescent, descent);
401 // Now update our height.
402 setHeight(max(yPos + maxAscent + maxDescent, height()));
405 setHeight(max(height(), yPos + child->height() + child->marginHeight()));
408 if (!iterator.first() && hasLineIfEmpty())
409 setHeight(height() + lineHeight(true, style()->isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes));
411 setHeight(height() + toAdd);
413 oldHeight = height();
414 updateLogicalHeight();
416 relayoutChildren = false;
417 if (oldHeight != height())
418 heightSpecified = true;
420 // Now that our height is actually known, we can place our boxes.
421 m_stretchingChildren = (style()->boxAlign() == BSTRETCH);
422 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
423 if (child->isOutOfFlowPositioned()) {
424 child->containingBlock()->insertPositionedObject(child);
425 RenderLayer* childLayer = child->layer();
426 childLayer->setStaticInlinePosition(xPos);
427 if (childLayer->staticBlockPosition() != yPos) {
428 childLayer->setStaticBlockPosition(yPos);
429 if (child->style()->hasStaticBlockPosition(style()->isHorizontalWritingMode()))
430 child->setChildNeedsLayout(MarkOnlyThis);
435 if (child->style()->visibility() == COLLAPSE) {
436 // visibility: collapsed children do not participate in our positioning.
437 // But we need to lay them down.
438 child->layoutIfNeeded();
442 SubtreeLayoutScope layoutScope(child);
444 // We need to see if this child's height has changed, since we make block elements
445 // fill the height of a containing box by default.
447 LayoutUnit oldChildHeight = child->height();
448 child->updateLogicalHeight();
449 if (oldChildHeight != child->height())
450 layoutScope.setChildNeedsLayout(child);
452 if (!child->needsLayout())
453 child->markForPaginationRelayoutIfNeeded(layoutScope);
455 child->layoutIfNeeded();
457 // We can place the child now, using our value of box-align.
458 xPos += child->marginLeft();
459 LayoutUnit childY = yPos;
460 switch (style()->boxAlign()) {
462 childY += child->marginTop() + max<LayoutUnit>(0, (contentHeight() - (child->height() + child->marginHeight())) / 2);
465 LayoutUnit ascent = child->firstLineBoxBaseline();
467 ascent = child->height() + child->marginBottom();
468 ascent += child->marginTop();
469 childY += child->marginTop() + (maxAscent - ascent);
473 childY += contentHeight() - child->marginBottom() - child->height();
476 childY += child->marginTop();
480 placeChild(child, LayoutPoint(xPos, childY));
482 xPos += child->width() + child->marginRight();
485 remainingSpace = borderLeft() + paddingLeft() + contentWidth() - xPos;
487 m_stretchingChildren = false;
489 haveFlex = false; // We're done.
491 // We have some flexible objects. See if we need to grow/shrink them at all.
495 // Allocate the remaining space among the flexible objects. If we are trying to
496 // grow, then we go from the lowest flex group to the highest flex group. For shrinking,
497 // we go from the highest flex group to the lowest group.
498 bool expanding = remainingSpace > 0;
499 unsigned int start = expanding ? lowestFlexGroup : highestFlexGroup;
500 unsigned int end = expanding? highestFlexGroup : lowestFlexGroup;
501 for (unsigned int i = start; i <= end && remainingSpace; i++) {
502 // Always start off by assuming the group can get all the remaining space.
503 LayoutUnit groupRemainingSpace = remainingSpace;
505 // Flexing consists of multiple passes, since we have to change ratios every time an object hits its max/min-width
506 // For a given pass, we always start off by computing the totalFlex of all objects that can grow/shrink at all, and
507 // computing the allowed growth before an object hits its min/max width (and thus
508 // forces a totalFlex recomputation).
509 LayoutUnit groupRemainingSpaceAtBeginning = groupRemainingSpace;
510 float totalFlex = 0.0f;
511 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
512 if (allowedChildFlex(child, expanding, i))
513 totalFlex += child->style()->boxFlex();
515 LayoutUnit spaceAvailableThisPass = groupRemainingSpace;
516 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
517 LayoutUnit allowedFlex = allowedChildFlex(child, expanding, i);
519 LayoutUnit projectedFlex = (allowedFlex == LayoutUnit::max()) ? allowedFlex : LayoutUnit(allowedFlex * (totalFlex / child->style()->boxFlex()));
520 spaceAvailableThisPass = expanding ? min(spaceAvailableThisPass, projectedFlex) : max(spaceAvailableThisPass, projectedFlex);
524 // The flex groups may not have any flexible objects this time around.
525 if (!spaceAvailableThisPass || totalFlex == 0.0f) {
526 // If we just couldn't grow/shrink any more, then it's time to transition to the next flex group.
527 groupRemainingSpace = 0;
531 // Now distribute the space to objects.
532 for (RenderBox* child = iterator.first(); child && spaceAvailableThisPass && totalFlex; child = iterator.next()) {
533 if (child->style()->visibility() == COLLAPSE)
536 if (allowedChildFlex(child, expanding, i)) {
537 LayoutUnit spaceAdd = LayoutUnit(spaceAvailableThisPass * (child->style()->boxFlex() / totalFlex));
539 child->setOverrideLogicalContentWidth(contentWidthForChild(child) + spaceAdd);
540 flexingChildren = true;
541 relayoutChildren = true;
544 spaceAvailableThisPass -= spaceAdd;
545 remainingSpace -= spaceAdd;
546 groupRemainingSpace -= spaceAdd;
548 totalFlex -= child->style()->boxFlex();
551 if (groupRemainingSpace == groupRemainingSpaceAtBeginning) {
552 // This is not advancing, avoid getting stuck by distributing the remaining pixels.
553 LayoutUnit spaceAdd = groupRemainingSpace > 0 ? 1 : -1;
554 for (RenderBox* child = iterator.first(); child && groupRemainingSpace; child = iterator.next()) {
555 if (allowedChildFlex(child, expanding, i)) {
556 child->setOverrideLogicalContentWidth(contentWidthForChild(child) + spaceAdd);
557 flexingChildren = true;
558 relayoutChildren = true;
559 remainingSpace -= spaceAdd;
560 groupRemainingSpace -= spaceAdd;
564 } while (absoluteValue(groupRemainingSpace) >= 1);
567 // We didn't find any children that could grow.
568 if (haveFlex && !flexingChildren)
573 RenderBlock::finishDelayUpdateScrollInfo();
575 if (remainingSpace > 0 && ((style()->isLeftToRightDirection() && style()->boxPack() != Start)
576 || (!style()->isLeftToRightDirection() && style()->boxPack() != End))) {
577 // Children must be repositioned.
578 LayoutUnit offset = 0;
579 if (style()->boxPack() == Justify) {
580 // Determine the total number of children.
581 int totalChildren = 0;
582 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
583 if (childDoesNotAffectWidthOrFlexing(child))
588 // Iterate over the children and space them out according to the
589 // justification level.
590 if (totalChildren > 1) {
592 bool firstChild = true;
593 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
594 if (childDoesNotAffectWidthOrFlexing(child))
602 offset += remainingSpace/totalChildren;
603 remainingSpace -= (remainingSpace/totalChildren);
606 placeChild(child, child->location() + LayoutSize(offset, 0));
610 if (style()->boxPack() == Center)
611 offset += remainingSpace / 2;
612 else // END for LTR, START for RTL
613 offset += remainingSpace;
614 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
615 if (childDoesNotAffectWidthOrFlexing(child))
618 placeChild(child, child->location() + LayoutSize(offset, 0));
623 // So that the computeLogicalHeight in layoutBlock() knows to relayout positioned objects because of
624 // a height change, we revert our height back to the intrinsic height before returning.
626 setHeight(oldHeight);
629 void RenderDeprecatedFlexibleBox::layoutVerticalBox(bool relayoutChildren)
631 LayoutUnit yPos = borderTop() + paddingTop();
632 LayoutUnit toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight();
633 bool heightSpecified = false;
634 LayoutUnit oldHeight = 0;
636 LayoutUnit remainingSpace = 0;
638 FlexBoxIterator iterator(this);
639 unsigned int highestFlexGroup = 0;
640 unsigned int lowestFlexGroup = 0;
641 bool haveFlex = false, flexingChildren = false;
642 gatherFlexChildrenInfo(iterator, relayoutChildren, highestFlexGroup, lowestFlexGroup, haveFlex);
644 // We confine the line clamp ugliness to vertical flexible boxes (thus keeping it out of
645 // mainstream block layout); this is not really part of the XUL box model.
646 bool haveLineClamp = !style()->lineClamp().isNone();
648 applyLineClamp(iterator, relayoutChildren);
650 RenderBlock::startDelayUpdateScrollInfo();
652 // We do 2 passes. The first pass is simply to lay everyone out at
653 // their preferred widths. The second pass handles flexing the children.
654 // Our first pass is done without flexing. We simply lay the children
655 // out within the box.
657 setHeight(borderTop() + paddingTop());
658 LayoutUnit minHeight = height() + toAdd;
660 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
661 if (child->isOutOfFlowPositioned()) {
662 child->containingBlock()->insertPositionedObject(child);
663 RenderLayer* childLayer = child->layer();
664 childLayer->setStaticInlinePosition(borderStart() + paddingStart());
665 if (childLayer->staticBlockPosition() != height()) {
666 childLayer->setStaticBlockPosition(height());
667 if (child->style()->hasStaticBlockPosition(style()->isHorizontalWritingMode()))
668 child->setChildNeedsLayout(MarkOnlyThis);
673 SubtreeLayoutScope layoutScope(child);
674 if (!haveLineClamp && (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent()))))
675 layoutScope.setChildNeedsLayout(child);
677 if (child->style()->visibility() == COLLAPSE) {
678 // visibility: collapsed children do not participate in our positioning.
679 // But we need to lay them down.
680 child->layoutIfNeeded();
684 // Compute the child's vertical margins.
685 child->computeAndSetBlockDirectionMargins(this);
687 // Add in the child's marginTop to our height.
688 setHeight(height() + child->marginTop());
690 if (!child->needsLayout())
691 child->markForPaginationRelayoutIfNeeded(layoutScope);
694 child->layoutIfNeeded();
696 // We can place the child now, using our value of box-align.
697 LayoutUnit childX = borderLeft() + paddingLeft();
698 switch (style()->boxAlign()) {
700 case BBASELINE: // Baseline just maps to center for vertical boxes
701 childX += child->marginLeft() + max<LayoutUnit>(0, (contentWidth() - (child->width() + child->marginWidth())) / 2);
704 if (!style()->isLeftToRightDirection())
705 childX += child->marginLeft();
707 childX += contentWidth() - child->marginRight() - child->width();
709 default: // BSTART/BSTRETCH
710 if (style()->isLeftToRightDirection())
711 childX += child->marginLeft();
713 childX += contentWidth() - child->marginRight() - child->width();
718 placeChild(child, LayoutPoint(childX, height()));
719 setHeight(height() + child->height() + child->marginBottom());
724 if (!iterator.first() && hasLineIfEmpty())
725 setHeight(height() + lineHeight(true, style()->isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes));
727 setHeight(height() + toAdd);
729 // Negative margins can cause our height to shrink below our minimal height (border/padding).
730 // If this happens, ensure that the computed height is increased to the minimal height.
731 if (height() < minHeight)
732 setHeight(minHeight);
734 // Now we have to calc our height, so we know how much space we have remaining.
735 oldHeight = height();
736 updateLogicalHeight();
737 if (oldHeight != height())
738 heightSpecified = true;
740 remainingSpace = borderTop() + paddingTop() + contentHeight() - yPos;
743 haveFlex = false; // We're done.
745 // We have some flexible objects. See if we need to grow/shrink them at all.
749 // Allocate the remaining space among the flexible objects. If we are trying to
750 // grow, then we go from the lowest flex group to the highest flex group. For shrinking,
751 // we go from the highest flex group to the lowest group.
752 bool expanding = remainingSpace > 0;
753 unsigned int start = expanding ? lowestFlexGroup : highestFlexGroup;
754 unsigned int end = expanding? highestFlexGroup : lowestFlexGroup;
755 for (unsigned int i = start; i <= end && remainingSpace; i++) {
756 // Always start off by assuming the group can get all the remaining space.
757 LayoutUnit groupRemainingSpace = remainingSpace;
759 // Flexing consists of multiple passes, since we have to change ratios every time an object hits its max/min-width
760 // For a given pass, we always start off by computing the totalFlex of all objects that can grow/shrink at all, and
761 // computing the allowed growth before an object hits its min/max width (and thus
762 // forces a totalFlex recomputation).
763 LayoutUnit groupRemainingSpaceAtBeginning = groupRemainingSpace;
764 float totalFlex = 0.0f;
765 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
766 if (allowedChildFlex(child, expanding, i))
767 totalFlex += child->style()->boxFlex();
769 LayoutUnit spaceAvailableThisPass = groupRemainingSpace;
770 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
771 LayoutUnit allowedFlex = allowedChildFlex(child, expanding, i);
773 LayoutUnit projectedFlex = (allowedFlex == LayoutUnit::max()) ? allowedFlex : static_cast<LayoutUnit>(allowedFlex * (totalFlex / child->style()->boxFlex()));
774 spaceAvailableThisPass = expanding ? min(spaceAvailableThisPass, projectedFlex) : max(spaceAvailableThisPass, projectedFlex);
778 // The flex groups may not have any flexible objects this time around.
779 if (!spaceAvailableThisPass || totalFlex == 0.0f) {
780 // If we just couldn't grow/shrink any more, then it's time to transition to the next flex group.
781 groupRemainingSpace = 0;
785 // Now distribute the space to objects.
786 for (RenderBox* child = iterator.first(); child && spaceAvailableThisPass && totalFlex; child = iterator.next()) {
787 if (allowedChildFlex(child, expanding, i)) {
788 LayoutUnit spaceAdd = static_cast<LayoutUnit>(spaceAvailableThisPass * (child->style()->boxFlex() / totalFlex));
790 child->setOverrideLogicalContentHeight(contentHeightForChild(child) + spaceAdd);
791 flexingChildren = true;
792 relayoutChildren = true;
795 spaceAvailableThisPass -= spaceAdd;
796 remainingSpace -= spaceAdd;
797 groupRemainingSpace -= spaceAdd;
799 totalFlex -= child->style()->boxFlex();
802 if (groupRemainingSpace == groupRemainingSpaceAtBeginning) {
803 // This is not advancing, avoid getting stuck by distributing the remaining pixels.
804 LayoutUnit spaceAdd = groupRemainingSpace > 0 ? 1 : -1;
805 for (RenderBox* child = iterator.first(); child && groupRemainingSpace; child = iterator.next()) {
806 if (allowedChildFlex(child, expanding, i)) {
807 child->setOverrideLogicalContentHeight(contentHeightForChild(child) + spaceAdd);
808 flexingChildren = true;
809 relayoutChildren = true;
810 remainingSpace -= spaceAdd;
811 groupRemainingSpace -= spaceAdd;
815 } while (absoluteValue(groupRemainingSpace) >= 1);
818 // We didn't find any children that could grow.
819 if (haveFlex && !flexingChildren)
824 RenderBlock::finishDelayUpdateScrollInfo();
826 if (style()->boxPack() != Start && remainingSpace > 0) {
827 // Children must be repositioned.
828 LayoutUnit offset = 0;
829 if (style()->boxPack() == Justify) {
830 // Determine the total number of children.
831 int totalChildren = 0;
832 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
833 if (childDoesNotAffectWidthOrFlexing(child))
839 // Iterate over the children and space them out according to the
840 // justification level.
841 if (totalChildren > 1) {
843 bool firstChild = true;
844 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
845 if (childDoesNotAffectWidthOrFlexing(child))
853 offset += remainingSpace/totalChildren;
854 remainingSpace -= (remainingSpace/totalChildren);
856 placeChild(child, child->location() + LayoutSize(0, offset));
860 if (style()->boxPack() == Center)
861 offset += remainingSpace / 2;
863 offset += remainingSpace;
864 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
865 if (childDoesNotAffectWidthOrFlexing(child))
867 placeChild(child, child->location() + LayoutSize(0, offset));
872 // So that the computeLogicalHeight in layoutBlock() knows to relayout positioned objects because of
873 // a height change, we revert our height back to the intrinsic height before returning.
875 setHeight(oldHeight);
878 void RenderDeprecatedFlexibleBox::applyLineClamp(FlexBoxIterator& iterator, bool relayoutChildren)
880 UseCounter::count(document(), UseCounter::LineClamp);
882 int maxLineCount = 0;
883 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
884 if (childDoesNotAffectWidthOrFlexing(child))
887 child->clearOverrideSize();
888 if (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent()))
889 || (child->style()->height().isAuto() && child->isRenderBlock())) {
890 child->setChildNeedsLayout(MarkOnlyThis);
892 // Dirty all the positioned objects.
893 if (child->isRenderBlock()) {
894 toRenderBlock(child)->markPositionedObjectsForLayout();
895 toRenderBlock(child)->clearTruncation();
898 child->layoutIfNeeded();
899 if (child->style()->height().isAuto() && child->isRenderBlock())
900 maxLineCount = max(maxLineCount, toRenderBlock(child)->lineCount());
903 // Get the number of lines and then alter all block flow children with auto height to use the
904 // specified height. We always try to leave room for at least one line.
905 LineClampValue lineClamp = style()->lineClamp();
906 int numVisibleLines = lineClamp.isPercentage() ? max(1, (maxLineCount + 1) * lineClamp.value() / 100) : lineClamp.value();
907 if (numVisibleLines >= maxLineCount)
910 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
911 if (childDoesNotAffectWidthOrFlexing(child) || !child->style()->height().isAuto() || !child->isRenderBlock())
914 RenderBlock* blockChild = toRenderBlock(child);
915 int lineCount = blockChild->lineCount();
916 if (lineCount <= numVisibleLines)
919 LayoutUnit newHeight = blockChild->heightForLineCount(numVisibleLines);
920 if (newHeight == child->height())
923 child->setOverrideLogicalContentHeight(newHeight - child->borderAndPaddingHeight());
924 child->forceChildLayout();
926 // FIXME: For now don't support RTL.
927 if (style()->direction() != LTR)
931 RootInlineBox* lastLine = blockChild->lineAtIndex(lineCount - 1);
935 RootInlineBox* lastVisibleLine = blockChild->lineAtIndex(numVisibleLines - 1);
936 if (!lastVisibleLine)
939 const UChar ellipsisAndSpace[2] = { horizontalEllipsis, ' ' };
940 DEFINE_STATIC_LOCAL(AtomicString, ellipsisAndSpaceStr, (ellipsisAndSpace, 2));
941 DEFINE_STATIC_LOCAL(AtomicString, ellipsisStr, (&horizontalEllipsis, 1));
942 const Font& font = style(numVisibleLines == 1)->font();
944 // 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
945 LayoutUnit totalWidth;
946 InlineBox* anchorBox = lastLine->lastChild();
947 if (anchorBox && anchorBox->renderer()->style()->isLink())
948 totalWidth = anchorBox->logicalWidth() + font.width(RenderBlockFlow::constructTextRun(this, font, ellipsisAndSpace, 2, style(), style()->direction()));
951 totalWidth = font.width(RenderBlockFlow::constructTextRun(this, font, &horizontalEllipsis, 1, style(), style()->direction()));
954 // See if this width can be accommodated on the last visible line
955 RenderBlockFlow* destBlock = lastVisibleLine->block();
956 RenderBlockFlow* srcBlock = lastLine->block();
958 // FIXME: Directions of src/destBlock could be different from our direction and from one another.
959 if (!srcBlock->style()->isLeftToRightDirection())
962 bool leftToRight = destBlock->style()->isLeftToRightDirection();
966 LayoutUnit blockRightEdge = destBlock->logicalRightOffsetForLine(lastVisibleLine->y(), false);
967 if (!lastVisibleLine->lineCanAccommodateEllipsis(leftToRight, blockRightEdge, lastVisibleLine->x() + lastVisibleLine->logicalWidth(), totalWidth))
970 // Let the truncation code kick in.
971 // FIXME: the text alignment should be recomputed after the width changes due to truncation.
972 LayoutUnit blockLeftEdge = destBlock->logicalLeftOffsetForLine(lastVisibleLine->y(), false);
973 lastVisibleLine->placeEllipsis(anchorBox ? ellipsisAndSpaceStr : ellipsisStr, leftToRight, blockLeftEdge, blockRightEdge, totalWidth, anchorBox);
974 destBlock->setHasMarkupTruncation(true);
978 void RenderDeprecatedFlexibleBox::clearLineClamp()
980 FlexBoxIterator iterator(this);
981 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
982 if (childDoesNotAffectWidthOrFlexing(child))
985 child->clearOverrideSize();
986 if ((child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent()))
987 || (child->style()->height().isAuto() && child->isRenderBlock())) {
988 child->setChildNeedsLayout();
990 if (child->isRenderBlock()) {
991 toRenderBlock(child)->markPositionedObjectsForLayout();
992 toRenderBlock(child)->clearTruncation();
998 void RenderDeprecatedFlexibleBox::placeChild(RenderBox* child, const LayoutPoint& location)
1000 LayoutRect oldRect = child->frameRect();
1003 child->setLocation(location);
1005 // If the child moved, we have to repaint it as well as any floating/positioned
1006 // descendants. An exception is if we need a layout. In this case, we know we're going to
1007 // repaint ourselves (and the child) anyway.
1008 if (!selfNeedsLayout() && child->checkForRepaintDuringLayout())
1009 child->repaintDuringLayoutIfMoved(oldRect);
1012 LayoutUnit RenderDeprecatedFlexibleBox::allowedChildFlex(RenderBox* child, bool expanding, unsigned int group)
1014 if (childDoesNotAffectWidthOrFlexing(child) || child->style()->boxFlex() == 0.0f || child->style()->boxFlexGroup() != group)
1018 if (isHorizontal()) {
1019 // FIXME: For now just handle fixed values.
1020 LayoutUnit maxWidth = LayoutUnit::max();
1021 LayoutUnit width = contentWidthForChild(child);
1022 if (!child->style()->maxWidth().isUndefined() && child->style()->maxWidth().isFixed())
1023 maxWidth = child->style()->maxWidth().value();
1024 else if (child->style()->maxWidth().type() == Intrinsic)
1025 maxWidth = child->maxPreferredLogicalWidth();
1026 else if (child->style()->maxWidth().type() == MinIntrinsic)
1027 maxWidth = child->minPreferredLogicalWidth();
1028 if (maxWidth == LayoutUnit::max())
1030 return max<LayoutUnit>(0, maxWidth - width);
1032 // FIXME: For now just handle fixed values.
1033 LayoutUnit maxHeight = LayoutUnit::max();
1034 LayoutUnit height = contentHeightForChild(child);
1035 if (!child->style()->maxHeight().isUndefined() && child->style()->maxHeight().isFixed())
1036 maxHeight = child->style()->maxHeight().value();
1037 if (maxHeight == LayoutUnit::max())
1039 return max<LayoutUnit>(0, maxHeight - height);
1043 // FIXME: For now just handle fixed values.
1044 if (isHorizontal()) {
1045 LayoutUnit minWidth = child->minPreferredLogicalWidth();
1046 LayoutUnit width = contentWidthForChild(child);
1047 if (child->style()->minWidth().isFixed())
1048 minWidth = child->style()->minWidth().value();
1049 else if (child->style()->minWidth().type() == Intrinsic)
1050 minWidth = child->maxPreferredLogicalWidth();
1051 else if (child->style()->minWidth().type() == MinIntrinsic)
1052 minWidth = child->minPreferredLogicalWidth();
1053 else if (child->style()->minWidth().type() == Auto)
1056 LayoutUnit allowedShrinkage = min<LayoutUnit>(0, minWidth - width);
1057 return allowedShrinkage;
1059 Length minHeight = child->style()->minHeight();
1060 if (minHeight.isFixed() || minHeight.isAuto()) {
1061 LayoutUnit minHeight = child->style()->minHeight().value();
1062 LayoutUnit height = contentHeightForChild(child);
1063 LayoutUnit allowedShrinkage = min<LayoutUnit>(0, minHeight - height);
1064 return allowedShrinkage;
1071 const char* RenderDeprecatedFlexibleBox::renderName() const
1074 return "RenderDeprecatedFlexibleBox (floating)";
1075 if (isOutOfFlowPositioned())
1076 return "RenderDeprecatedFlexibleBox (positioned)";
1077 // FIXME: Temporary hack while the new generated content system is being implemented.
1078 if (isPseudoElement())
1079 return "RenderDeprecatedFlexibleBox (generated)";
1081 return "RenderDeprecatedFlexibleBox (generated)";
1082 if (isRelPositioned())
1083 return "RenderDeprecatedFlexibleBox (relative positioned)";
1084 return "RenderDeprecatedFlexibleBox";
1087 } // namespace WebCore