| /* |
| * This file is part of the render object implementation for KHTML. |
| * |
| * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| * (C) 1999 Antti Koivisto (koivisto@kde.org) |
| * Copyright (C) 2003 Apple Inc. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| * |
| */ |
| |
| #include "config.h" |
| #include "RenderDeprecatedFlexibleBox.h" |
| |
| #include "FontCascade.h" |
| #include "LayoutRepainter.h" |
| #include "RenderIterator.h" |
| #include "RenderLayer.h" |
| #include "RenderLayoutState.h" |
| #include "RenderView.h" |
| #include <wtf/IsoMallocInlines.h> |
| #include <wtf/StdLibExtras.h> |
| #include <wtf/unicode/CharacterNames.h> |
| |
| namespace WebCore { |
| |
| WTF_MAKE_ISO_ALLOCATED_IMPL(RenderDeprecatedFlexibleBox); |
| |
| class FlexBoxIterator { |
| public: |
| FlexBoxIterator(RenderDeprecatedFlexibleBox* parent) |
| : m_box(parent) |
| , m_largestOrdinal(1) |
| { |
| if (m_box->style().boxOrient() == BoxOrient::Horizontal && !m_box->style().isLeftToRightDirection()) |
| m_forward = m_box->style().boxDirection() != BoxDirection::Normal; |
| else |
| m_forward = m_box->style().boxDirection() == BoxDirection::Normal; |
| if (!m_forward) { |
| // No choice, since we're going backwards, we have to find out the highest ordinal up front. |
| RenderBox* child = m_box->firstChildBox(); |
| while (child) { |
| if (child->style().boxOrdinalGroup() > m_largestOrdinal) |
| m_largestOrdinal = child->style().boxOrdinalGroup(); |
| child = child->nextSiblingBox(); |
| } |
| } |
| |
| reset(); |
| } |
| |
| void reset() |
| { |
| m_currentChild = nullptr; |
| m_ordinalIteration = std::numeric_limits<unsigned>::max(); |
| } |
| |
| RenderBox* first() |
| { |
| reset(); |
| return next(); |
| } |
| |
| RenderBox* next() |
| { |
| do { |
| if (!m_currentChild) { |
| ++m_ordinalIteration; |
| |
| if (!m_ordinalIteration) |
| m_currentOrdinal = m_forward ? 1 : m_largestOrdinal; |
| else { |
| if (m_ordinalIteration > m_ordinalValues.size()) |
| return nullptr; |
| |
| // Only copy+sort the values once per layout even if the iterator is reset. |
| if (static_cast<size_t>(m_ordinalValues.size()) != m_sortedOrdinalValues.size()) { |
| m_sortedOrdinalValues = copyToVector(m_ordinalValues); |
| std::sort(m_sortedOrdinalValues.begin(), m_sortedOrdinalValues.end()); |
| } |
| m_currentOrdinal = m_forward ? m_sortedOrdinalValues[m_ordinalIteration - 1] : m_sortedOrdinalValues[m_sortedOrdinalValues.size() - m_ordinalIteration]; |
| } |
| |
| m_currentChild = m_forward ? m_box->firstChildBox() : m_box->lastChildBox(); |
| } else |
| m_currentChild = m_forward ? m_currentChild->nextSiblingBox() : m_currentChild->previousSiblingBox(); |
| |
| if (m_currentChild && notFirstOrdinalValue()) |
| m_ordinalValues.add(m_currentChild->style().boxOrdinalGroup()); |
| } while (!m_currentChild || m_currentChild->isExcludedFromNormalLayout() || (!m_currentChild->isAnonymous() |
| && m_currentChild->style().boxOrdinalGroup() != m_currentOrdinal)); |
| return m_currentChild; |
| } |
| |
| private: |
| bool notFirstOrdinalValue() |
| { |
| unsigned int firstOrdinalValue = m_forward ? 1 : m_largestOrdinal; |
| return m_currentOrdinal == firstOrdinalValue && m_currentChild->style().boxOrdinalGroup() != firstOrdinalValue; |
| } |
| |
| RenderDeprecatedFlexibleBox* m_box; |
| RenderBox* m_currentChild; |
| bool m_forward; |
| unsigned m_currentOrdinal; |
| unsigned m_largestOrdinal; |
| HashSet<unsigned> m_ordinalValues; |
| Vector<unsigned> m_sortedOrdinalValues; |
| unsigned m_ordinalIteration; |
| }; |
| |
| RenderDeprecatedFlexibleBox::RenderDeprecatedFlexibleBox(Element& element, RenderStyle&& style) |
| : RenderBlock(element, WTFMove(style), 0) |
| { |
| setChildrenInline(false); // All of our children must be block-level |
| m_stretchingChildren = false; |
| } |
| |
| RenderDeprecatedFlexibleBox::~RenderDeprecatedFlexibleBox() = default; |
| |
| static LayoutUnit marginWidthForChild(RenderBox* child) |
| { |
| // A margin basically has three types: fixed, percentage, and auto (variable). |
| // Auto and percentage margins simply become 0 when computing min/max width. |
| // Fixed margins can be added in as is. |
| Length marginLeft = child->style().marginLeft(); |
| Length marginRight = child->style().marginRight(); |
| LayoutUnit margin; |
| if (marginLeft.isFixed()) |
| margin += marginLeft.value(); |
| if (marginRight.isFixed()) |
| margin += marginRight.value(); |
| return margin; |
| } |
| |
| static bool childDoesNotAffectWidthOrFlexing(RenderObject* child) |
| { |
| // Positioned children and collapsed children don't affect the min/max width. |
| return child->isOutOfFlowPositioned() || child->style().visibility() == Visibility::Collapse; |
| } |
| |
| static LayoutUnit widthForChild(RenderBox* child) |
| { |
| if (child->hasOverridingLogicalWidth()) |
| return child->overridingLogicalWidth(); |
| return child->logicalWidth(); |
| } |
| |
| static LayoutUnit heightForChild(RenderBox* child) |
| { |
| if (child->hasOverridingLogicalHeight()) |
| return child->overridingLogicalHeight(); |
| return child->logicalHeight(); |
| } |
| |
| static LayoutUnit contentWidthForChild(RenderBox* child) |
| { |
| return std::max<LayoutUnit>(0, widthForChild(child) - child->borderAndPaddingLogicalWidth()); |
| } |
| |
| static LayoutUnit contentHeightForChild(RenderBox* child) |
| { |
| return std::max<LayoutUnit>(0, heightForChild(child) - child->borderAndPaddingLogicalHeight()); |
| } |
| |
| void RenderDeprecatedFlexibleBox::styleWillChange(StyleDifference diff, const RenderStyle& newStyle) |
| { |
| auto* oldStyle = hasInitializedStyle() ? &style() : nullptr; |
| if (oldStyle) { |
| auto hadLineClamp = !oldStyle->lineClamp().isNone(); |
| auto hasLineClamp = !newStyle.lineClamp().isNone(); |
| if (hasLineClamp != hadLineClamp) { |
| for (auto& child : childrenOfType<RenderBlockFlow>(*this)) |
| child.invalidateLineLayoutPath(); |
| |
| if (hadLineClamp) |
| clearLineClamp(); |
| } |
| } |
| RenderBlock::styleWillChange(diff, newStyle); |
| } |
| |
| void RenderDeprecatedFlexibleBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const |
| { |
| auto addScrollbarWidth = [&]() { |
| LayoutUnit scrollbarWidth = intrinsicScrollbarLogicalWidth(); |
| maxLogicalWidth += scrollbarWidth; |
| minLogicalWidth += scrollbarWidth; |
| }; |
| |
| if (shouldApplySizeContainment()) { |
| addScrollbarWidth(); |
| return; |
| } |
| |
| if (hasMultipleLines() || isVertical()) { |
| for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { |
| if (childDoesNotAffectWidthOrFlexing(child)) |
| continue; |
| |
| LayoutUnit margin = marginWidthForChild(child); |
| LayoutUnit width = child->minPreferredLogicalWidth() + margin; |
| minLogicalWidth = std::max(width, minLogicalWidth); |
| |
| width = child->maxPreferredLogicalWidth() + margin; |
| maxLogicalWidth = std::max(width, maxLogicalWidth); |
| } |
| } else { |
| for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { |
| if (childDoesNotAffectWidthOrFlexing(child)) |
| continue; |
| |
| LayoutUnit margin = marginWidthForChild(child); |
| minLogicalWidth += child->minPreferredLogicalWidth() + margin; |
| maxLogicalWidth += child->maxPreferredLogicalWidth() + margin; |
| } |
| } |
| |
| maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth); |
| addScrollbarWidth(); |
| } |
| |
| void RenderDeprecatedFlexibleBox::computePreferredLogicalWidths() |
| { |
| ASSERT(preferredLogicalWidthsDirty()); |
| |
| m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = 0; |
| if (style().width().isFixed() && style().width().value() > 0) |
| m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(style().width()); |
| else |
| computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); |
| |
| RenderBox::computePreferredLogicalWidths(style().minWidth(), style().maxWidth(), borderAndPaddingLogicalWidth()); |
| |
| setPreferredLogicalWidthsDirty(false); |
| } |
| |
| // Use an inline capacity of 8, since flexbox containers usually have less than 8 children. |
| typedef Vector<LayoutRect, 8> ChildFrameRects; |
| typedef Vector<LayoutSize, 8> ChildLayoutDeltas; |
| |
| static void appendChildFrameRects(RenderDeprecatedFlexibleBox* box, ChildFrameRects& childFrameRects) |
| { |
| FlexBoxIterator iterator(box); |
| for (RenderBox* child = iterator.first(); child; child = iterator.next()) { |
| if (!child->isOutOfFlowPositioned()) |
| childFrameRects.append(child->frameRect()); |
| } |
| } |
| |
| static void appendChildLayoutDeltas(RenderDeprecatedFlexibleBox* box, ChildLayoutDeltas& childLayoutDeltas) |
| { |
| FlexBoxIterator iterator(box); |
| for (RenderBox* child = iterator.first(); child; child = iterator.next()) { |
| if (!child->isOutOfFlowPositioned()) |
| childLayoutDeltas.append(LayoutSize()); |
| } |
| } |
| |
| static void repaintChildrenDuringLayoutIfMoved(RenderDeprecatedFlexibleBox* box, const ChildFrameRects& oldChildRects) |
| { |
| size_t childIndex = 0; |
| FlexBoxIterator iterator(box); |
| for (RenderBox* child = iterator.first(); child; child = iterator.next()) { |
| if (child->isOutOfFlowPositioned()) |
| continue; |
| |
| // If the child moved, we have to repaint it as well as any floating/positioned |
| // descendants. An exception is if we need a layout. In this case, we know we're going to |
| // repaint ourselves (and the child) anyway. |
| if (!box->selfNeedsLayout() && child->checkForRepaintDuringLayout()) |
| child->repaintDuringLayoutIfMoved(oldChildRects[childIndex]); |
| |
| ++childIndex; |
| } |
| ASSERT(childIndex == oldChildRects.size()); |
| } |
| |
| void RenderDeprecatedFlexibleBox::layoutBlock(bool relayoutChildren, LayoutUnit) |
| { |
| ASSERT(needsLayout()); |
| |
| if (!relayoutChildren && simplifiedLayout()) |
| return; |
| |
| LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); |
| { |
| LayoutStateMaintainer statePusher(*this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode()); |
| |
| resetLogicalHeightBeforeLayoutIfNeeded(); |
| preparePaginationBeforeBlockLayout(relayoutChildren); |
| |
| LayoutSize previousSize = size(); |
| |
| updateLogicalWidth(); |
| updateLogicalHeight(); |
| |
| if (previousSize != size() |
| || (parent()->isDeprecatedFlexibleBox() && parent()->style().boxOrient() == BoxOrient::Horizontal |
| && parent()->style().boxAlign() == BoxAlignment::Stretch)) |
| relayoutChildren = true; |
| |
| setHeight(0); |
| |
| m_stretchingChildren = false; |
| |
| #if ASSERT_ENABLED |
| LayoutSize oldLayoutDelta = view().frameView().layoutContext().layoutDelta(); |
| #endif |
| |
| // Fieldsets need to find their legend and position it inside the border of the object. |
| // The legend then gets skipped during normal layout. The same is true for ruby text. |
| // It doesn't get included in the normal layout process but is instead skipped. |
| layoutExcludedChildren(relayoutChildren); |
| |
| ChildFrameRects oldChildRects; |
| appendChildFrameRects(this, oldChildRects); |
| |
| if (isHorizontal()) |
| layoutHorizontalBox(relayoutChildren); |
| else |
| layoutVerticalBox(relayoutChildren); |
| |
| repaintChildrenDuringLayoutIfMoved(this, oldChildRects); |
| ASSERT(view().frameView().layoutContext().layoutDeltaMatches(oldLayoutDelta)); |
| |
| LayoutUnit oldClientAfterEdge = clientLogicalBottom(); |
| updateLogicalHeight(); |
| |
| if (previousSize.height() != height()) |
| relayoutChildren = true; |
| |
| layoutPositionedObjects(relayoutChildren || isDocumentElementRenderer()); |
| |
| computeOverflow(oldClientAfterEdge); |
| } |
| |
| updateLayerTransform(); |
| |
| auto* layoutState = view().frameView().layoutContext().layoutState(); |
| if (layoutState && layoutState->pageLogicalHeight()) |
| setPageLogicalOffset(layoutState->pageLogicalOffset(this, logicalTop())); |
| |
| // Update our scrollbars if we're overflow:auto/scroll/hidden now that we know if |
| // we overflow or not. |
| updateScrollInfoAfterLayout(); |
| |
| // Repaint with our new bounds if they are different from our old bounds. |
| repainter.repaintAfterLayout(); |
| |
| clearNeedsLayout(); |
| } |
| |
| // The first walk over our kids is to find out if we have any flexible children. |
| static void gatherFlexChildrenInfo(FlexBoxIterator& iterator, bool relayoutChildren, unsigned int& highestFlexGroup, unsigned int& lowestFlexGroup, bool& haveFlex) |
| { |
| for (RenderBox* child = iterator.first(); child; child = iterator.next()) { |
| // Check to see if this child flexes. |
| if (!childDoesNotAffectWidthOrFlexing(child) && child->style().boxFlex() > 0.0f) { |
| // We always have to lay out flexible objects again, since the flex distribution |
| // may have changed, and we need to reallocate space. |
| child->clearOverridingContentSize(); |
| if (!relayoutChildren) |
| child->setChildNeedsLayout(MarkOnlyThis); |
| haveFlex = true; |
| unsigned flexGroup = child->style().boxFlexGroup(); |
| if (lowestFlexGroup == 0) |
| lowestFlexGroup = flexGroup; |
| if (flexGroup < lowestFlexGroup) |
| lowestFlexGroup = flexGroup; |
| if (flexGroup > highestFlexGroup) |
| highestFlexGroup = flexGroup; |
| } |
| } |
| } |
| |
| static void layoutChildIfNeededApplyingDelta(RenderBox* child, const LayoutSize& layoutDelta) |
| { |
| if (!child->needsLayout()) |
| return; |
| |
| child->view().frameView().layoutContext().addLayoutDelta(layoutDelta); |
| child->layoutIfNeeded(); |
| child->view().frameView().layoutContext().addLayoutDelta(-layoutDelta); |
| } |
| |
| void RenderDeprecatedFlexibleBox::layoutHorizontalBox(bool relayoutChildren) |
| { |
| LayoutUnit toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight(); |
| LayoutUnit yPos = borderTop() + paddingTop(); |
| LayoutUnit xPos = borderLeft() + paddingLeft(); |
| bool heightSpecified = false; |
| LayoutUnit oldHeight; |
| |
| LayoutUnit remainingSpace; |
| |
| FlexBoxIterator iterator(this); |
| unsigned int highestFlexGroup = 0; |
| unsigned int lowestFlexGroup = 0; |
| bool haveFlex = false, flexingChildren = false; |
| gatherFlexChildrenInfo(iterator, relayoutChildren, highestFlexGroup, lowestFlexGroup, haveFlex); |
| |
| beginUpdateScrollInfoAfterLayoutTransaction(); |
| |
| ChildLayoutDeltas childLayoutDeltas; |
| appendChildLayoutDeltas(this, childLayoutDeltas); |
| |
| // We do 2 passes. The first pass is simply to lay everyone out at |
| // their preferred widths. The subsequent passes handle flexing the children. |
| // The first pass skips flexible objects completely. |
| do { |
| // Reset our height. |
| setHeight(yPos); |
| |
| xPos = borderLeft() + paddingLeft(); |
| |
| size_t childIndex = 0; |
| |
| // Our first pass is done without flexing. We simply lay the children |
| // out within the box. We have to do a layout first in order to determine |
| // our box's intrinsic height. |
| LayoutUnit maxAscent, maxDescent; |
| for (RenderBox* child = iterator.first(); child; child = iterator.next()) { |
| if (relayoutChildren) |
| child->setChildNeedsLayout(MarkOnlyThis); |
| |
| if (child->isOutOfFlowPositioned()) |
| continue; |
| |
| LayoutSize& childLayoutDelta = childLayoutDeltas[childIndex++]; |
| |
| // Compute the child's vertical margins. |
| child->computeAndSetBlockDirectionMargins(*this); |
| |
| child->markForPaginationRelayoutIfNeeded(); |
| |
| // Apply the child's current layout delta. |
| layoutChildIfNeededApplyingDelta(child, childLayoutDelta); |
| |
| // Update our height and overflow height. |
| if (style().boxAlign() == BoxAlignment::Baseline) { |
| LayoutUnit ascent = child->firstLineBaseline().value_or(child->height() + child->marginBottom()); |
| ascent += child->marginTop(); |
| LayoutUnit descent = (child->height() + child->verticalMarginExtent()) - ascent; |
| |
| // Update our maximum ascent. |
| maxAscent = std::max(maxAscent, ascent); |
| |
| // Update our maximum descent. |
| maxDescent = std::max(maxDescent, descent); |
| |
| // Now update our height. |
| setHeight(std::max(yPos + maxAscent + maxDescent, height())); |
| } |
| else |
| setHeight(std::max(height(), yPos + child->height() + child->verticalMarginExtent())); |
| } |
| ASSERT(childIndex == childLayoutDeltas.size()); |
| |
| if (!iterator.first() && hasLineIfEmpty()) |
| setHeight(height() + lineHeight(true, style().isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); |
| |
| setHeight(height() + toAdd); |
| |
| oldHeight = height(); |
| updateLogicalHeight(); |
| |
| relayoutChildren = false; |
| if (oldHeight != height()) |
| heightSpecified = true; |
| |
| // Now that our height is actually known, we can place our boxes. |
| childIndex = 0; |
| m_stretchingChildren = (style().boxAlign() == BoxAlignment::Stretch); |
| for (RenderBox* child = iterator.first(); child; child = iterator.next()) { |
| if (child->isOutOfFlowPositioned()) { |
| child->containingBlock()->insertPositionedObject(*child); |
| RenderLayer* childLayer = child->layer(); |
| childLayer->setStaticInlinePosition(xPos); // FIXME: Not right for regions. |
| if (childLayer->staticBlockPosition() != yPos) { |
| childLayer->setStaticBlockPosition(yPos); |
| if (child->style().hasStaticBlockPosition(style().isHorizontalWritingMode())) |
| child->setChildNeedsLayout(MarkOnlyThis); |
| } |
| continue; |
| } |
| |
| LayoutSize& childLayoutDelta = childLayoutDeltas[childIndex++]; |
| |
| if (child->style().visibility() == Visibility::Collapse) { |
| // visibility: collapsed children do not participate in our positioning. |
| // But we need to lay them out. |
| layoutChildIfNeededApplyingDelta(child, childLayoutDelta); |
| continue; |
| } |
| |
| // We need to see if this child's height has changed, since we make block elements |
| // fill the height of a containing box by default. |
| // Now do a layout. |
| LayoutUnit oldChildHeight = child->height(); |
| child->updateLogicalHeight(); |
| if (oldChildHeight != child->height()) |
| child->setChildNeedsLayout(MarkOnlyThis); |
| |
| child->markForPaginationRelayoutIfNeeded(); |
| |
| layoutChildIfNeededApplyingDelta(child, childLayoutDelta); |
| |
| // We can place the child now, using our value of box-align. |
| xPos += child->marginLeft(); |
| LayoutUnit childY = yPos; |
| switch (style().boxAlign()) { |
| case BoxAlignment::Center: |
| childY += child->marginTop() + std::max<LayoutUnit>(0, (contentHeight() - (child->height() + child->verticalMarginExtent())) / 2); |
| break; |
| case BoxAlignment::Baseline: { |
| LayoutUnit ascent = child->firstLineBaseline().value_or(child->height() + child->marginBottom()); |
| ascent += child->marginTop(); |
| childY += child->marginTop() + (maxAscent - ascent); |
| break; |
| } |
| case BoxAlignment::End: |
| childY += contentHeight() - child->marginBottom() - child->height(); |
| break; |
| default: // BoxAlignment::Start |
| childY += child->marginTop(); |
| break; |
| } |
| |
| placeChild(child, LayoutPoint(xPos, childY), &childLayoutDelta); |
| |
| xPos += child->width() + child->marginRight(); |
| } |
| ASSERT(childIndex == childLayoutDeltas.size()); |
| |
| remainingSpace = borderLeft() + paddingLeft() + contentWidth() - xPos; |
| |
| m_stretchingChildren = false; |
| if (flexingChildren) |
| haveFlex = false; // We're done. |
| else if (haveFlex) { |
| // We have some flexible objects. See if we need to grow/shrink them at all. |
| if (!remainingSpace) |
| break; |
| |
| // Allocate the remaining space among the flexible objects. If we are trying to |
| // grow, then we go from the lowest flex group to the highest flex group. For shrinking, |
| // we go from the highest flex group to the lowest group. |
| bool expanding = remainingSpace > 0; |
| unsigned int start = expanding ? lowestFlexGroup : highestFlexGroup; |
| unsigned int end = expanding? highestFlexGroup : lowestFlexGroup; |
| for (unsigned int i = start; i <= end && remainingSpace; i++) { |
| // Always start off by assuming the group can get all the remaining space. |
| LayoutUnit groupRemainingSpace = remainingSpace; |
| do { |
| // Flexing consists of multiple passes, since we have to change ratios every time an object hits its max/min-width |
| // For a given pass, we always start off by computing the totalFlex of all objects that can grow/shrink at all, and |
| // computing the allowed growth before an object hits its min/max width (and thus |
| // forces a totalFlex recomputation). |
| LayoutUnit groupRemainingSpaceAtBeginning = groupRemainingSpace; |
| float totalFlex = 0.0f; |
| for (RenderBox* child = iterator.first(); child; child = iterator.next()) { |
| if (allowedChildFlex(child, expanding, i)) |
| totalFlex += child->style().boxFlex(); |
| } |
| LayoutUnit spaceAvailableThisPass = groupRemainingSpace; |
| for (RenderBox* child = iterator.first(); child; child = iterator.next()) { |
| LayoutUnit allowedFlex = allowedChildFlex(child, expanding, i); |
| if (allowedFlex) { |
| LayoutUnit projectedFlex = (allowedFlex == LayoutUnit::max()) ? allowedFlex : LayoutUnit(allowedFlex * (totalFlex / child->style().boxFlex())); |
| spaceAvailableThisPass = expanding ? std::min(spaceAvailableThisPass, projectedFlex) : std::max(spaceAvailableThisPass, projectedFlex); |
| } |
| } |
| |
| // The flex groups may not have any flexible objects this time around. |
| if (!spaceAvailableThisPass || totalFlex == 0.0f) { |
| // If we just couldn't grow/shrink any more, then it's time to transition to the next flex group. |
| groupRemainingSpace = 0; |
| continue; |
| } |
| |
| // Now distribute the space to objects. |
| for (RenderBox* child = iterator.first(); child && spaceAvailableThisPass && totalFlex; child = iterator.next()) { |
| if (child->style().visibility() == Visibility::Collapse) |
| continue; |
| |
| if (allowedChildFlex(child, expanding, i)) { |
| LayoutUnit spaceAdd = LayoutUnit(spaceAvailableThisPass * (child->style().boxFlex() / totalFlex)); |
| if (spaceAdd) { |
| child->setOverridingLogicalWidth(widthForChild(child) + spaceAdd); |
| flexingChildren = true; |
| relayoutChildren = true; |
| } |
| |
| spaceAvailableThisPass -= spaceAdd; |
| remainingSpace -= spaceAdd; |
| groupRemainingSpace -= spaceAdd; |
| |
| totalFlex -= child->style().boxFlex(); |
| } |
| } |
| if (groupRemainingSpace == groupRemainingSpaceAtBeginning) { |
| // This is not advancing, avoid getting stuck by distributing the remaining pixels. |
| LayoutUnit spaceAdd = groupRemainingSpace > 0 ? 1 : -1; |
| for (RenderBox* child = iterator.first(); child && groupRemainingSpace; child = iterator.next()) { |
| if (allowedChildFlex(child, expanding, i)) { |
| child->setOverridingLogicalWidth(widthForChild(child) + spaceAdd); |
| flexingChildren = true; |
| relayoutChildren = true; |
| remainingSpace -= spaceAdd; |
| groupRemainingSpace -= spaceAdd; |
| } |
| } |
| } |
| } while (absoluteValue(groupRemainingSpace) >= 1); |
| } |
| |
| // We didn't find any children that could grow. |
| if (haveFlex && !flexingChildren) |
| haveFlex = false; |
| } |
| } while (haveFlex); |
| |
| endAndCommitUpdateScrollInfoAfterLayoutTransaction(); |
| |
| if (remainingSpace > 0 && ((style().isLeftToRightDirection() && style().boxPack() != BoxPack::Start) |
| || (!style().isLeftToRightDirection() && style().boxPack() != BoxPack::End))) { |
| // Children must be repositioned. |
| LayoutUnit offset; |
| if (style().boxPack() == BoxPack::Justify) { |
| // Determine the total number of children. |
| int totalChildren = 0; |
| for (RenderBox* child = iterator.first(); child; child = iterator.next()) { |
| if (childDoesNotAffectWidthOrFlexing(child)) |
| continue; |
| ++totalChildren; |
| } |
| |
| // Iterate over the children and space them out according to the |
| // justification level. |
| if (totalChildren > 1) { |
| --totalChildren; |
| bool firstChild = true; |
| for (RenderBox* child = iterator.first(); child; child = iterator.next()) { |
| if (childDoesNotAffectWidthOrFlexing(child)) |
| continue; |
| |
| if (firstChild) { |
| firstChild = false; |
| continue; |
| } |
| |
| offset += remainingSpace/totalChildren; |
| remainingSpace -= (remainingSpace/totalChildren); |
| --totalChildren; |
| |
| placeChild(child, child->location() + LayoutSize(offset, 0_lu)); |
| } |
| } |
| } else { |
| if (style().boxPack() == BoxPack::Center) |
| offset += remainingSpace / 2; |
| else // BoxPack::End for LTR, BoxPack::Start for RTL |
| offset += remainingSpace; |
| for (RenderBox* child = iterator.first(); child; child = iterator.next()) { |
| if (childDoesNotAffectWidthOrFlexing(child)) |
| continue; |
| |
| placeChild(child, child->location() + LayoutSize(offset, 0_lu)); |
| } |
| } |
| } |
| |
| // So that the computeLogicalHeight in layoutBlock() knows to relayout positioned objects because of |
| // a height change, we revert our height back to the intrinsic height before returning. |
| if (heightSpecified) |
| setHeight(oldHeight); |
| } |
| |
| void RenderDeprecatedFlexibleBox::layoutVerticalBox(bool relayoutChildren) |
| { |
| LayoutUnit yPos = borderTop() + paddingTop(); |
| LayoutUnit toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight(); |
| bool heightSpecified = false; |
| LayoutUnit oldHeight; |
| |
| LayoutUnit remainingSpace; |
| |
| FlexBoxIterator iterator(this); |
| unsigned int highestFlexGroup = 0; |
| unsigned int lowestFlexGroup = 0; |
| bool haveFlex = false, flexingChildren = false; |
| gatherFlexChildrenInfo(iterator, relayoutChildren, highestFlexGroup, lowestFlexGroup, haveFlex); |
| |
| // We confine the line clamp ugliness to vertical flexible boxes (thus keeping it out of |
| // mainstream block layout); this is not really part of the XUL box model. |
| bool haveLineClamp = !style().lineClamp().isNone(); |
| if (haveLineClamp) |
| applyLineClamp(iterator, relayoutChildren); |
| |
| beginUpdateScrollInfoAfterLayoutTransaction(); |
| |
| ChildLayoutDeltas childLayoutDeltas; |
| appendChildLayoutDeltas(this, childLayoutDeltas); |
| |
| // We do 2 passes. The first pass is simply to lay everyone out at |
| // their preferred widths. The second pass handles flexing the children. |
| // Our first pass is done without flexing. We simply lay the children |
| // out within the box. |
| do { |
| setHeight(borderTop() + paddingTop()); |
| LayoutUnit minHeight = height() + toAdd; |
| |
| size_t childIndex = 0; |
| for (RenderBox* child = iterator.first(); child; child = iterator.next()) { |
| // Make sure we relayout children if we need it. |
| if (!haveLineClamp && relayoutChildren) |
| child->setChildNeedsLayout(MarkOnlyThis); |
| |
| if (child->isOutOfFlowPositioned()) { |
| child->containingBlock()->insertPositionedObject(*child); |
| RenderLayer* childLayer = child->layer(); |
| childLayer->setStaticInlinePosition(borderStart() + paddingStart()); // FIXME: Not right for regions. |
| if (childLayer->staticBlockPosition() != height()) { |
| childLayer->setStaticBlockPosition(height()); |
| if (child->style().hasStaticBlockPosition(style().isHorizontalWritingMode())) |
| child->setChildNeedsLayout(MarkOnlyThis); |
| } |
| continue; |
| } |
| |
| LayoutSize& childLayoutDelta = childLayoutDeltas[childIndex++]; |
| |
| if (child->style().visibility() == Visibility::Collapse) { |
| // visibility: collapsed children do not participate in our positioning. |
| // But we need to lay them down. |
| layoutChildIfNeededApplyingDelta(child, childLayoutDelta); |
| continue; |
| } |
| |
| // Compute the child's vertical margins. |
| child->computeAndSetBlockDirectionMargins(*this); |
| |
| // Add in the child's marginTop to our height. |
| setHeight(height() + child->marginTop()); |
| |
| child->markForPaginationRelayoutIfNeeded(); |
| |
| // Now do a layout. |
| layoutChildIfNeededApplyingDelta(child, childLayoutDelta); |
| |
| // We can place the child now, using our value of box-align. |
| LayoutUnit childX = borderLeft() + paddingLeft(); |
| switch (style().boxAlign()) { |
| case BoxAlignment::Center: |
| case BoxAlignment::Baseline: // Baseline just maps to center for vertical boxes |
| childX += child->marginLeft() + std::max<LayoutUnit>(0, (contentWidth() - (child->width() + child->horizontalMarginExtent())) / 2); |
| break; |
| case BoxAlignment::End: |
| if (!style().isLeftToRightDirection()) |
| childX += child->marginLeft(); |
| else |
| childX += contentWidth() - child->marginRight() - child->width(); |
| break; |
| default: // BoxAlignment::Start/BoxAlignment::Stretch |
| if (style().isLeftToRightDirection()) |
| childX += child->marginLeft(); |
| else |
| childX += contentWidth() - child->marginRight() - child->width(); |
| break; |
| } |
| |
| // Place the child. |
| placeChild(child, LayoutPoint(childX, height()), &childLayoutDelta); |
| setHeight(height() + child->height() + child->marginBottom()); |
| } |
| ASSERT(childIndex == childLayoutDeltas.size()); |
| |
| yPos = height(); |
| |
| if (!iterator.first() && hasLineIfEmpty()) |
| setHeight(height() + lineHeight(true, style().isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); |
| |
| setHeight(height() + toAdd); |
| |
| // Negative margins can cause our height to shrink below our minimal height (border/padding). |
| // If this happens, ensure that the computed height is increased to the minimal height. |
| if (height() < minHeight) |
| setHeight(minHeight); |
| |
| // Now we have to calc our height, so we know how much space we have remaining. |
| oldHeight = height(); |
| updateLogicalHeight(); |
| if (oldHeight != height()) |
| heightSpecified = true; |
| |
| remainingSpace = borderTop() + paddingTop() + contentHeight() - yPos; |
| |
| if (flexingChildren) |
| haveFlex = false; // We're done. |
| else if (haveFlex) { |
| // We have some flexible objects. See if we need to grow/shrink them at all. |
| if (!remainingSpace) |
| break; |
| |
| // Allocate the remaining space among the flexible objects. If we are trying to |
| // grow, then we go from the lowest flex group to the highest flex group. For shrinking, |
| // we go from the highest flex group to the lowest group. |
| bool expanding = remainingSpace > 0; |
| unsigned int start = expanding ? lowestFlexGroup : highestFlexGroup; |
| unsigned int end = expanding? highestFlexGroup : lowestFlexGroup; |
| for (unsigned int i = start; i <= end && remainingSpace; i++) { |
| // Always start off by assuming the group can get all the remaining space. |
| LayoutUnit groupRemainingSpace = remainingSpace; |
| do { |
| // Flexing consists of multiple passes, since we have to change ratios every time an object hits its max/min-width |
| // For a given pass, we always start off by computing the totalFlex of all objects that can grow/shrink at all, and |
| // computing the allowed growth before an object hits its min/max width (and thus |
| // forces a totalFlex recomputation). |
| LayoutUnit groupRemainingSpaceAtBeginning = groupRemainingSpace; |
| float totalFlex = 0.0f; |
| for (RenderBox* child = iterator.first(); child; child = iterator.next()) { |
| if (allowedChildFlex(child, expanding, i)) |
| totalFlex += child->style().boxFlex(); |
| } |
| LayoutUnit spaceAvailableThisPass = groupRemainingSpace; |
| for (RenderBox* child = iterator.first(); child; child = iterator.next()) { |
| LayoutUnit allowedFlex = allowedChildFlex(child, expanding, i); |
| if (allowedFlex) { |
| LayoutUnit projectedFlex = (allowedFlex == LayoutUnit::max()) ? allowedFlex : LayoutUnit(allowedFlex * (totalFlex / child->style().boxFlex())); |
| spaceAvailableThisPass = expanding ? std::min(spaceAvailableThisPass, projectedFlex) : std::max(spaceAvailableThisPass, projectedFlex); |
| } |
| } |
| |
| // The flex groups may not have any flexible objects this time around. |
| if (!spaceAvailableThisPass || totalFlex == 0.0f) { |
| // If we just couldn't grow/shrink any more, then it's time to transition to the next flex group. |
| groupRemainingSpace = 0; |
| continue; |
| } |
| |
| // Now distribute the space to objects. |
| for (RenderBox* child = iterator.first(); child && spaceAvailableThisPass && totalFlex; child = iterator.next()) { |
| if (allowedChildFlex(child, expanding, i)) { |
| LayoutUnit spaceAdd { spaceAvailableThisPass * (child->style().boxFlex() / totalFlex) }; |
| if (spaceAdd) { |
| child->setOverridingLogicalHeight(heightForChild(child) + spaceAdd); |
| flexingChildren = true; |
| relayoutChildren = true; |
| } |
| |
| spaceAvailableThisPass -= spaceAdd; |
| remainingSpace -= spaceAdd; |
| groupRemainingSpace -= spaceAdd; |
| |
| totalFlex -= child->style().boxFlex(); |
| } |
| } |
| if (groupRemainingSpace == groupRemainingSpaceAtBeginning) { |
| // This is not advancing, avoid getting stuck by distributing the remaining pixels. |
| LayoutUnit spaceAdd = groupRemainingSpace > 0 ? 1 : -1; |
| for (RenderBox* child = iterator.first(); child && groupRemainingSpace; child = iterator.next()) { |
| if (allowedChildFlex(child, expanding, i)) { |
| child->setOverridingLogicalHeight(heightForChild(child) + spaceAdd); |
| flexingChildren = true; |
| relayoutChildren = true; |
| remainingSpace -= spaceAdd; |
| groupRemainingSpace -= spaceAdd; |
| } |
| } |
| } |
| } while (absoluteValue(groupRemainingSpace) >= 1); |
| } |
| |
| // We didn't find any children that could grow. |
| if (haveFlex && !flexingChildren) |
| haveFlex = false; |
| } |
| } while (haveFlex); |
| |
| endAndCommitUpdateScrollInfoAfterLayoutTransaction(); |
| |
| if (style().boxPack() != BoxPack::Start && remainingSpace > 0) { |
| // Children must be repositioned. |
| LayoutUnit offset; |
| if (style().boxPack() == BoxPack::Justify) { |
| // Determine the total number of children. |
| int totalChildren = 0; |
| for (RenderBox* child = iterator.first(); child; child = iterator.next()) { |
| if (childDoesNotAffectWidthOrFlexing(child)) |
| continue; |
| |
| ++totalChildren; |
| } |
| |
| // Iterate over the children and space them out according to the |
| // justification level. |
| if (totalChildren > 1) { |
| --totalChildren; |
| bool firstChild = true; |
| for (RenderBox* child = iterator.first(); child; child = iterator.next()) { |
| if (childDoesNotAffectWidthOrFlexing(child)) |
| continue; |
| |
| if (firstChild) { |
| firstChild = false; |
| continue; |
| } |
| |
| offset += remainingSpace/totalChildren; |
| remainingSpace -= (remainingSpace/totalChildren); |
| --totalChildren; |
| placeChild(child, child->location() + LayoutSize(0_lu, offset)); |
| } |
| } |
| } else { |
| if (style().boxPack() == BoxPack::Center) |
| offset += remainingSpace / 2; |
| else // BoxPack::End |
| offset += remainingSpace; |
| for (RenderBox* child = iterator.first(); child; child = iterator.next()) { |
| if (childDoesNotAffectWidthOrFlexing(child)) |
| continue; |
| placeChild(child, child->location() + LayoutSize(0_lu, offset)); |
| } |
| } |
| } |
| |
| // So that the computeLogicalHeight in layoutBlock() knows to relayout positioned objects because of |
| // a height change, we revert our height back to the intrinsic height before returning. |
| if (heightSpecified) |
| setHeight(oldHeight); |
| } |
| |
| static bool shouldIncludeLinesForParentLineCount(const RenderBlockFlow& blockFlow) |
| { |
| return !blockFlow.isFloatingOrOutOfFlowPositioned() && blockFlow.style().height().isAuto(); |
| } |
| |
| static void clearTruncation(RenderBlockFlow& blockFlow) |
| { |
| if (blockFlow.style().visibility() != Visibility::Visible) |
| return; |
| |
| if (blockFlow.childrenInline() && blockFlow.hasMarkupTruncation()) { |
| blockFlow.setHasMarkupTruncation(false); |
| for (auto* box = blockFlow.firstRootBox(); box; box = box->nextRootBox()) |
| box->clearTruncation(); |
| return; |
| } |
| |
| for (auto& child : childrenOfType<RenderBlockFlow>(blockFlow)) { |
| if (shouldIncludeLinesForParentLineCount(child)) |
| clearTruncation(child); |
| } |
| } |
| |
| static LegacyRootInlineBox* lineAtIndex(const RenderBlockFlow& flow, int i) |
| { |
| ASSERT(i >= 0); |
| |
| if (flow.style().visibility() != Visibility::Visible) |
| return nullptr; |
| |
| if (flow.childrenInline()) { |
| for (auto* box = flow.firstRootBox(); box; box = box->nextRootBox()) { |
| if (!i--) |
| return box; |
| } |
| return nullptr; |
| } |
| |
| for (auto& blockFlow : childrenOfType<RenderBlockFlow>(flow)) { |
| if (!shouldIncludeLinesForParentLineCount(blockFlow)) |
| continue; |
| if (LegacyRootInlineBox* box = lineAtIndex(blockFlow, i)) |
| return box; |
| } |
| |
| return nullptr; |
| } |
| |
| static std::optional<LayoutUnit> getHeightForLineCount(const RenderBlockFlow& block, size_t lineCount, bool includeBottom, size_t& count) |
| { |
| if (block.style().visibility() != Visibility::Visible) |
| return { }; |
| |
| if (block.childrenInline()) { |
| for (auto* box = block.firstRootBox(); box; box = box->nextRootBox()) { |
| if (++count == lineCount) |
| return box->lineBottom() + (includeBottom ? (block.borderBottom() + block.paddingBottom()) : 0_lu); |
| } |
| } else { |
| RenderBox* normalFlowChildWithoutLines = nullptr; |
| for (auto* obj = block.firstChildBox(); obj; obj = obj->nextSiblingBox()) { |
| if (is<RenderBlockFlow>(*obj) && shouldIncludeLinesForParentLineCount(downcast<RenderBlockFlow>(*obj))) { |
| if (auto height = getHeightForLineCount(downcast<RenderBlockFlow>(*obj), lineCount, false, count)) |
| return *height + obj->y() + (includeBottom ? (block.borderBottom() + block.paddingBottom()) : 0_lu); |
| } else if (!obj->isFloatingOrOutOfFlowPositioned()) |
| normalFlowChildWithoutLines = obj; |
| } |
| if (normalFlowChildWithoutLines && !lineCount) |
| return normalFlowChildWithoutLines->y() + normalFlowChildWithoutLines->height(); |
| } |
| |
| return { }; |
| } |
| |
| static LayoutUnit heightForLineCount(const RenderBlockFlow& flow, size_t lineCount) |
| { |
| size_t count = 0; |
| if (auto height = getHeightForLineCount(flow, lineCount, true, count)) |
| return *height; |
| return { }; |
| } |
| |
| static size_t lineCountFor(const RenderBlockFlow& blockFlow) |
| { |
| if (blockFlow.style().visibility() != Visibility::Visible) |
| return 0; |
| |
| if (blockFlow.childrenInline()) |
| return blockFlow.lineCount(); |
| |
| size_t count = 0; |
| for (auto& child : childrenOfType<RenderBlockFlow>(blockFlow)) { |
| if (blockFlow.isFloatingOrOutOfFlowPositioned() || !blockFlow.style().height().isAuto()) |
| continue; |
| count += lineCountFor(child); |
| } |
| return count; |
| } |
| |
| void RenderDeprecatedFlexibleBox::applyLineClamp(FlexBoxIterator& iterator, bool relayoutChildren) |
| { |
| size_t maxLineCount = 0; |
| for (RenderBox* child = iterator.first(); child; child = iterator.next()) { |
| if (childDoesNotAffectWidthOrFlexing(child)) |
| continue; |
| |
| child->clearOverridingContentSize(); |
| if (relayoutChildren || (child->isReplacedOrInlineBlock() && (child->style().width().isPercentOrCalculated() || child->style().height().isPercentOrCalculated())) |
| || (child->style().height().isAuto() && is<RenderBlockFlow>(*child))) { |
| child->setChildNeedsLayout(MarkOnlyThis); |
| |
| // Dirty all the positioned objects. |
| if (is<RenderBlockFlow>(*child)) { |
| downcast<RenderBlockFlow>(*child).markPositionedObjectsForLayout(); |
| clearTruncation(downcast<RenderBlockFlow>(*child)); |
| } |
| } |
| child->layoutIfNeeded(); |
| if (child->style().height().isAuto() && is<RenderBlockFlow>(*child)) |
| maxLineCount = std::max(maxLineCount, lineCountFor(downcast<RenderBlockFlow>(*child))); |
| } |
| |
| // Get the number of lines and then alter all block flow children with auto height to use the |
| // specified height. We always try to leave room for at least one line. |
| auto lineClamp = style().lineClamp(); |
| ASSERT(!lineClamp.isNone()); |
| |
| size_t numVisibleLines = lineClamp.value(); |
| if (lineClamp.isPercentage()) { |
| float percentValue = lineClamp.value(); |
| numVisibleLines = std::max<size_t>(1, (maxLineCount + 1) * percentValue / 100.f); |
| } |
| if (numVisibleLines >= maxLineCount) |
| return; |
| |
| for (RenderBox* child = iterator.first(); child; child = iterator.next()) { |
| if (childDoesNotAffectWidthOrFlexing(child) || !child->style().height().isAuto() || !is<RenderBlockFlow>(*child)) |
| continue; |
| |
| RenderBlockFlow& blockChild = downcast<RenderBlockFlow>(*child); |
| auto lineCount = lineCountFor(blockChild); |
| if (lineCount <= numVisibleLines) |
| continue; |
| |
| auto newHeight = heightForLineCount(blockChild, numVisibleLines); |
| if (newHeight == child->height()) |
| continue; |
| |
| child->setChildNeedsLayout(MarkOnlyThis); |
| child->setOverridingLogicalHeight(newHeight); |
| child->layoutIfNeeded(); |
| |
| // FIXME: For now don't support RTL. |
| if (style().direction() != TextDirection::LTR) |
| continue; |
| |
| // Get the last line |
| LegacyRootInlineBox* lastLine = lineAtIndex(blockChild, lineCount - 1); |
| if (!lastLine) |
| continue; |
| |
| LegacyRootInlineBox* lastVisibleLine = lineAtIndex(blockChild, numVisibleLines - 1); |
| if (!lastVisibleLine || !lastVisibleLine->firstChild()) |
| continue; |
| |
| const UChar ellipsisAndSpace[2] = { horizontalEllipsis, ' ' }; |
| static MainThreadNeverDestroyed<const AtomString> ellipsisAndSpaceStr(ellipsisAndSpace, 2); |
| static MainThreadNeverDestroyed<const AtomString> ellipsisStr(&horizontalEllipsis, 1); |
| const RenderStyle& lineStyle = numVisibleLines == 1 ? firstLineStyle() : style(); |
| const FontCascade& font = lineStyle.fontCascade(); |
| |
| // 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 |
| LayoutUnit totalWidth; |
| LegacyInlineBox* anchorBox = lastLine->lastChild(); |
| auto& lastVisibleRenderer = lastVisibleLine->firstChild()->renderer(); |
| if (anchorBox && anchorBox->renderer().style().isLink() && &lastVisibleRenderer != &anchorBox->renderer()) |
| totalWidth = anchorBox->logicalWidth() + font.width(constructTextRun(ellipsisAndSpace, 2, style())); |
| else { |
| anchorBox = nullptr; |
| totalWidth = font.width(constructTextRun(&horizontalEllipsis, 1, style())); |
| } |
| |
| // See if this width can be accommodated on the last visible line |
| RenderBlockFlow& destBlock = lastVisibleLine->blockFlow(); |
| RenderBlockFlow& srcBlock = lastLine->blockFlow(); |
| |
| // FIXME: Directions of src/destBlock could be different from our direction and from one another. |
| if (!srcBlock.style().isLeftToRightDirection()) |
| continue; |
| |
| bool leftToRight = destBlock.style().isLeftToRightDirection(); |
| if (!leftToRight) |
| continue; |
| |
| LayoutUnit blockRightEdge = destBlock.logicalRightOffsetForLine(LayoutUnit(lastVisibleLine->y()), DoNotIndentText); |
| if (!lastVisibleLine->lineCanAccommodateEllipsis(leftToRight, blockRightEdge, lastVisibleLine->x() + lastVisibleLine->logicalWidth(), totalWidth)) |
| continue; |
| |
| // Let the truncation code kick in. |
| // FIXME: the text alignment should be recomputed after the width changes due to truncation. |
| LayoutUnit blockLeftEdge = destBlock.logicalLeftOffsetForLine(LayoutUnit(lastVisibleLine->y()), DoNotIndentText); |
| lastVisibleLine->placeEllipsis(anchorBox ? ellipsisAndSpaceStr : ellipsisStr, leftToRight, blockLeftEdge, blockRightEdge, totalWidth, anchorBox); |
| destBlock.setHasMarkupTruncation(true); |
| } |
| } |
| |
| void RenderDeprecatedFlexibleBox::clearLineClamp() |
| { |
| FlexBoxIterator iterator(this); |
| for (RenderBox* child = iterator.first(); child; child = iterator.next()) { |
| if (childDoesNotAffectWidthOrFlexing(child)) |
| continue; |
| |
| child->clearOverridingContentSize(); |
| if ((child->isReplacedOrInlineBlock() && (child->style().width().isPercentOrCalculated() || child->style().height().isPercentOrCalculated())) |
| || (child->style().height().isAuto() && is<RenderBlockFlow>(*child))) { |
| child->setChildNeedsLayout(); |
| |
| if (is<RenderBlockFlow>(*child)) { |
| downcast<RenderBlockFlow>(*child).markPositionedObjectsForLayout(); |
| clearTruncation(downcast<RenderBlockFlow>(*child)); |
| } |
| } |
| } |
| } |
| |
| void RenderDeprecatedFlexibleBox::placeChild(RenderBox* child, const LayoutPoint& location, LayoutSize* childLayoutDelta) |
| { |
| // Place the child and track the layout delta so we can apply it if we do another layout. |
| if (childLayoutDelta) |
| *childLayoutDelta += LayoutSize(child->x() - location.x(), child->y() - location.y()); |
| child->setLocation(location); |
| } |
| |
| LayoutUnit RenderDeprecatedFlexibleBox::allowedChildFlex(RenderBox* child, bool expanding, unsigned int group) |
| { |
| if (childDoesNotAffectWidthOrFlexing(child) || child->style().boxFlex() == 0.0f || child->style().boxFlexGroup() != group) |
| return 0; |
| |
| if (expanding) { |
| if (isHorizontal()) { |
| // FIXME: For now just handle fixed values. |
| LayoutUnit maxWidth = LayoutUnit::max(); |
| LayoutUnit width = contentWidthForChild(child); |
| if (!child->style().maxWidth().isUndefined() && child->style().maxWidth().isFixed()) |
| maxWidth = child->style().maxWidth().value(); |
| else if (child->style().maxWidth().type() == LengthType::Intrinsic) |
| maxWidth = child->maxPreferredLogicalWidth(); |
| else if (child->style().maxWidth().isMinIntrinsic()) |
| maxWidth = child->minPreferredLogicalWidth(); |
| if (maxWidth == LayoutUnit::max()) |
| return maxWidth; |
| return std::max<LayoutUnit>(0, maxWidth - width); |
| } else { |
| // FIXME: For now just handle fixed values. |
| LayoutUnit maxHeight = LayoutUnit::max(); |
| LayoutUnit height = contentHeightForChild(child); |
| if (!child->style().maxHeight().isUndefined() && child->style().maxHeight().isFixed()) |
| maxHeight = child->style().maxHeight().value(); |
| if (maxHeight == LayoutUnit::max()) |
| return maxHeight; |
| return std::max<LayoutUnit>(0, maxHeight - height); |
| } |
| } |
| |
| // FIXME: For now just handle fixed values. |
| if (isHorizontal()) { |
| LayoutUnit minWidth = child->minPreferredLogicalWidth(); |
| LayoutUnit width = contentWidthForChild(child); |
| if (child->style().minWidth().isFixed()) |
| minWidth = child->style().minWidth().value(); |
| else if (child->style().minWidth().type() == LengthType::Intrinsic) |
| minWidth = child->maxPreferredLogicalWidth(); |
| else if (child->style().minWidth().isMinIntrinsic()) |
| minWidth = child->minPreferredLogicalWidth(); |
| else if (child->style().minWidth().isAuto()) |
| minWidth = 0; |
| |
| LayoutUnit allowedShrinkage = std::min<LayoutUnit>(0, minWidth - width); |
| return allowedShrinkage; |
| } else { |
| Length minHeight = child->style().minHeight(); |
| if (minHeight.isFixed() || minHeight.isAuto()) { |
| LayoutUnit minHeight { child->style().minHeight().value() }; |
| LayoutUnit height = contentHeightForChild(child); |
| LayoutUnit allowedShrinkage = std::min<LayoutUnit>(0, minHeight - height); |
| return allowedShrinkage; |
| } |
| } |
| |
| return 0; |
| } |
| |
| ASCIILiteral RenderDeprecatedFlexibleBox::renderName() const |
| { |
| if (isFloating()) |
| return "RenderDeprecatedFlexibleBox (floating)"_s; |
| if (isOutOfFlowPositioned()) |
| return "RenderDeprecatedFlexibleBox (positioned)"_s; |
| // FIXME: Temporary hack while the new generated content system is being implemented. |
| if (isPseudoElement()) |
| return "RenderDeprecatedFlexibleBox (generated)"_s; |
| if (isAnonymous()) |
| return "RenderDeprecatedFlexibleBox (generated)"_s; |
| if (isRelativelyPositioned()) |
| return "RenderDeprecatedFlexibleBox (relative positioned)"_s; |
| return "RenderDeprecatedFlexibleBox"_s; |
| } |
| |
| } // namespace WebCore |