blob: 14a6cd12d1e0678341d6b47a0c32611dab0237bf [file] [log] [blame]
/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2007 David Smith (catfish.man@gmail.com)
* Copyright (C) 2003-2013 Apple Inc. All rights reserved.
* Copyright (C) Research In Motion Limited 2010. All rights reserved.
* Copyright (C) 2013 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef LayoutBlockFlow_h
#define LayoutBlockFlow_h
#include "core/CoreExport.h"
#include "core/layout/FloatingObjects.h"
#include "core/layout/LayoutBlock.h"
#include "core/layout/api/LineLayoutItem.h"
#include "core/layout/line/LineBoxList.h"
#include "core/layout/line/RootInlineBox.h"
#include "core/layout/line/TrailingObjects.h"
#include <memory>
namespace blink {
class BlockChildrenLayoutInfo;
class MarginInfo;
class LayoutInline;
class LineInfo;
class LineLayoutState;
class LineWidth;
class LayoutMultiColumnFlowThread;
class LayoutMultiColumnSpannerPlaceholder;
class LayoutRubyRun;
template <class Run>
class BidiRunList;
enum IndentTextOrNot { DoNotIndentText, IndentText };
// LayoutBlockFlow is the class that implements a block container in CSS 2.1.
// http://www.w3.org/TR/CSS21/visuren.html#block-boxes
//
// LayoutBlockFlows are the only LayoutObject allowed to own floating objects
// (aka floats): http://www.w3.org/TR/CSS21/visuren.html#floats .
//
// Floats are inserted into |m_floatingObjects| (see FloatingObjects for more
// information on how floats are modelled) during layout. This happens either as
// part of laying out blocks (layoutBlockChildren) or line layout (LineBreaker
// class). This is because floats can be part of an inline or a block context.
//
// An interesting feature of floats is that they can intrude into the next
// block(s). This means that |m_floatingObjects| can potentially contain
// pointers to a previous sibling LayoutBlockFlow's float.
//
// LayoutBlockFlow is also the only LayoutObject to own a line box tree and
// perform inline layout. See LayoutBlockFlowLine.cpp for these parts.
//
// TODO(jchaffraix): We need some float and line box expert to expand on this.
//
// LayoutBlockFlow enforces the following invariant:
//
// All in-flow children (ie excluding floating and out-of-flow positioned) are
// either all blocks or all inline boxes.
//
// This is suggested by CSS to correctly the layout mixed inlines and blocks
// lines (http://www.w3.org/TR/CSS21/visuren.html#anonymous-block-level). See
// LayoutBlock::addChild about how the invariant is enforced.
class CORE_EXPORT LayoutBlockFlow : public LayoutBlock {
public:
explicit LayoutBlockFlow(ContainerNode*);
~LayoutBlockFlow() override;
static LayoutBlockFlow* createAnonymous(Document*);
bool beingDestroyed() const { return m_beingDestroyed; }
bool isLayoutBlockFlow() const final { return true; }
void layoutBlock(bool relayoutChildren) override;
void computeOverflow(LayoutUnit oldClientAfterEdge,
bool recomputeFloats = false) override;
void deleteLineBoxTree();
LayoutUnit availableLogicalWidthForLine(
LayoutUnit position,
IndentTextOrNot indentText,
LayoutUnit logicalHeight = LayoutUnit()) const {
return (logicalRightOffsetForLine(position, indentText, logicalHeight) -
logicalLeftOffsetForLine(position, indentText, logicalHeight))
.clampNegativeToZero();
}
LayoutUnit logicalRightOffsetForLine(
LayoutUnit position,
IndentTextOrNot indentText,
LayoutUnit logicalHeight = LayoutUnit()) const {
return logicalRightOffsetForLine(position, logicalRightOffsetForContent(),
indentText, logicalHeight);
}
LayoutUnit logicalLeftOffsetForLine(
LayoutUnit position,
IndentTextOrNot indentText,
LayoutUnit logicalHeight = LayoutUnit()) const {
return logicalLeftOffsetForLine(position, logicalLeftOffsetForContent(),
indentText, logicalHeight);
}
LayoutUnit startOffsetForLine(LayoutUnit position,
IndentTextOrNot indentText,
LayoutUnit logicalHeight = LayoutUnit()) const {
return style()->isLeftToRightDirection()
? logicalLeftOffsetForLine(position, indentText, logicalHeight)
: logicalWidth() - logicalRightOffsetForLine(
position, indentText, logicalHeight);
}
LayoutUnit endOffsetForLine(LayoutUnit position,
IndentTextOrNot indentText,
LayoutUnit logicalHeight = LayoutUnit()) const {
return !style()->isLeftToRightDirection()
? logicalLeftOffsetForLine(position, indentText, logicalHeight)
: logicalWidth() - logicalRightOffsetForLine(
position, indentText, logicalHeight);
}
const LineBoxList& lineBoxes() const { return m_lineBoxes; }
LineBoxList* lineBoxes() { return &m_lineBoxes; }
InlineFlowBox* firstLineBox() const { return m_lineBoxes.firstLineBox(); }
InlineFlowBox* lastLineBox() const { return m_lineBoxes.lastLineBox(); }
RootInlineBox* firstRootBox() const {
return static_cast<RootInlineBox*>(firstLineBox());
}
RootInlineBox* lastRootBox() const {
return static_cast<RootInlineBox*>(lastLineBox());
}
LayoutUnit logicalLeftSelectionOffset(const LayoutBlock* rootBlock,
LayoutUnit position) const override;
LayoutUnit logicalRightSelectionOffset(const LayoutBlock* rootBlock,
LayoutUnit position) const override;
RootInlineBox* createAndAppendRootInlineBox();
// Return the number of lines in *this* block flow. Does not recurse into
// block flow children.
// Will start counting from the first line, and stop counting right after
// |stopRootInlineBox|, if specified.
int lineCount(const RootInlineBox* stopRootInlineBox = nullptr) const;
int firstLineBoxBaseline() const override;
int inlineBlockBaseline(LineDirectionMode) const override;
void removeFloatingObjectsFromDescendants();
void markAllDescendantsWithFloatsForLayout(LayoutBox* floatToRemove = nullptr,
bool inLayout = true);
void markSiblingsWithFloatsForLayout(LayoutBox* floatToRemove = nullptr);
bool containsFloats() const {
return m_floatingObjects && !m_floatingObjects->set().isEmpty();
}
bool containsFloat(LayoutBox*) const;
void removeFloatingObjects();
LayoutBoxModelObject* virtualContinuation() const final {
return continuation();
}
bool isAnonymousBlockContinuation() const {
return continuation() && isAnonymousBlock();
}
using LayoutBoxModelObject::continuation;
using LayoutBoxModelObject::setContinuation;
LayoutInline* inlineElementContinuation() const;
void addChild(LayoutObject* newChild,
LayoutObject* beforeChild = nullptr) override;
void removeChild(LayoutObject*) override;
void moveAllChildrenIncludingFloatsTo(LayoutBlock* toBlock,
bool fullRemoveInsert);
void childBecameFloatingOrOutOfFlow(LayoutBox* child);
void collapseAnonymousBlockChild(LayoutBlockFlow* child);
bool generatesLineBoxesForInlineChild(LayoutObject*);
LayoutUnit logicalTopForFloat(const FloatingObject& floatingObject) const {
return isHorizontalWritingMode() ? floatingObject.y() : floatingObject.x();
}
LayoutUnit logicalBottomForFloat(const FloatingObject& floatingObject) const {
return isHorizontalWritingMode() ? floatingObject.maxY()
: floatingObject.maxX();
}
LayoutUnit logicalLeftForFloat(const FloatingObject& floatingObject) const {
return isHorizontalWritingMode() ? floatingObject.x() : floatingObject.y();
}
LayoutUnit logicalRightForFloat(const FloatingObject& floatingObject) const {
return isHorizontalWritingMode() ? floatingObject.maxX()
: floatingObject.maxY();
}
LayoutUnit logicalWidthForFloat(const FloatingObject& floatingObject) const {
return isHorizontalWritingMode() ? floatingObject.width()
: floatingObject.height();
}
void setLogicalTopForFloat(FloatingObject& floatingObject,
LayoutUnit logicalTop) {
if (isHorizontalWritingMode())
floatingObject.setY(logicalTop);
else
floatingObject.setX(logicalTop);
}
void setLogicalLeftForFloat(FloatingObject& floatingObject,
LayoutUnit logicalLeft) {
if (isHorizontalWritingMode())
floatingObject.setX(logicalLeft);
else
floatingObject.setY(logicalLeft);
}
void setLogicalHeightForFloat(FloatingObject& floatingObject,
LayoutUnit logicalHeight) {
if (isHorizontalWritingMode())
floatingObject.setHeight(logicalHeight);
else
floatingObject.setWidth(logicalHeight);
}
void setLogicalWidthForFloat(FloatingObject& floatingObject,
LayoutUnit logicalWidth) {
if (isHorizontalWritingMode())
floatingObject.setWidth(logicalWidth);
else
floatingObject.setHeight(logicalWidth);
}
LayoutUnit startAlignedOffsetForLine(LayoutUnit position, IndentTextOrNot);
void setStaticInlinePositionForChild(LayoutBox&, LayoutUnit inlinePosition);
void updateStaticInlinePositionForChild(LayoutBox&,
LayoutUnit logicalTop,
IndentTextOrNot = DoNotIndentText);
static bool shouldSkipCreatingRunsForObject(LineLayoutItem obj) {
return obj.isFloating() || (obj.isOutOfFlowPositioned() &&
!obj.style()->isOriginalDisplayInlineType() &&
!obj.container().isLayoutInline());
}
LayoutMultiColumnFlowThread* multiColumnFlowThread() const {
return m_rareData ? m_rareData->m_multiColumnFlowThread : 0;
}
void resetMultiColumnFlowThread() {
if (m_rareData)
m_rareData->m_multiColumnFlowThread = nullptr;
}
void addOverflowFromInlineChildren();
// FIXME: This should be const to avoid a const_cast, but can modify child
// dirty bits and LayoutTextCombine.
void computeInlinePreferredLogicalWidths(LayoutUnit& minLogicalWidth,
LayoutUnit& maxLogicalWidth);
bool allowsPaginationStrut() const;
// Pagination strut caused by the first line or child block inside this
// block-level object.
//
// When the first piece of content (first child block or line) inside an
// object wants to insert a soft page or column break, rather than setting a
// pagination strut on itself it normally propagates the strut to its
// containing block (|this|), as long as our implementation can handle it.
// The idea is that we want to push the entire object to the next page or
// column along with the child content that caused the break, instead of
// leaving unusable space at the beginning of the object at the end of one
// column or page and just push the first line or block to the next column or
// page. That would waste space in the container for no good reason, and it
// would also be a spec violation, since there is no break opportunity defined
// between the content logical top of an object and its first child or line
// (only *between* blocks or lines).
LayoutUnit paginationStrutPropagatedFromChild() const {
return m_rareData ? m_rareData->m_paginationStrutPropagatedFromChild
: LayoutUnit();
}
void setPaginationStrutPropagatedFromChild(LayoutUnit);
void positionSpannerDescendant(LayoutMultiColumnSpannerPlaceholder& child);
bool avoidsFloats() const override;
using LayoutBoxModelObject::moveChildrenTo;
void moveChildrenTo(LayoutBoxModelObject* toBoxModelObject,
LayoutObject* startChild,
LayoutObject* endChild,
LayoutObject* beforeChild,
bool fullRemoveInsert = false) override;
LayoutUnit xPositionForFloatIncludingMargin(
const FloatingObject& child) const {
if (isHorizontalWritingMode())
return child.x() + child.layoutObject()->marginLeft();
return child.x() + marginBeforeForChild(*child.layoutObject());
}
DISABLE_CFI_PERF
LayoutUnit yPositionForFloatIncludingMargin(
const FloatingObject& child) const {
if (isHorizontalWritingMode())
return child.y() + marginBeforeForChild(*child.layoutObject());
return child.y() + child.layoutObject()->marginTop();
}
LayoutPoint flipFloatForWritingModeForChild(const FloatingObject&,
const LayoutPoint&) const;
const char* name() const override { return "LayoutBlockFlow"; }
FloatingObject* insertFloatingObject(LayoutBox&);
// Called from lineWidth, to position the floats added in the last line.
// Returns true if and only if it has positioned any floats.
bool positionNewFloats(LineWidth* = nullptr);
LayoutUnit nextFloatLogicalBottomBelow(LayoutUnit) const;
LayoutUnit nextFloatLogicalBottomBelowForBlock(LayoutUnit) const;
FloatingObject* lastFloatFromPreviousLine() const {
return containsFloats() ? m_floatingObjects->set().last().get() : nullptr;
}
void setShouldDoFullPaintInvalidationForFirstLine();
void simplifiedNormalFlowInlineLayout();
bool recalcInlineChildrenOverflowAfterStyleChange();
PositionWithAffinity positionForPoint(const LayoutPoint&) override;
LayoutUnit lowestFloatLogicalBottom(
FloatingObject::Type = FloatingObject::FloatLeftRight) const;
bool hasOverhangingFloats() const {
return parent() && containsFloats() &&
lowestFloatLogicalBottom() > logicalHeight();
}
bool isOverhangingFloat(const FloatingObject& floatObject) const {
return logicalBottomForFloat(floatObject) > logicalHeight();
}
// This function is only public so we can call it from NGBox while we're
// still working on LayoutNG.
void updateIsSelfCollapsing() {
m_isSelfCollapsing = checkIfIsSelfCollapsingBlock();
}
#ifndef NDEBUG
void showLineTreeAndMark(const InlineBox* = nullptr,
const char* = nullptr,
const InlineBox* = nullptr,
const char* = nullptr,
const LayoutObject* = nullptr) const;
#endif
protected:
void rebuildFloatsFromIntruding();
void layoutInlineChildren(bool relayoutChildren, LayoutUnit afterEdge);
void addLowestFloatFromChildren(LayoutBlockFlow*);
void createFloatingObjects();
void willBeDestroyed() override;
void styleWillChange(StyleDifference, const ComputedStyle& newStyle) override;
void styleDidChange(StyleDifference, const ComputedStyle* oldStyle) override;
void updateBlockChildDirtyBitsBeforeLayout(bool relayoutChildren, LayoutBox&);
void addOverflowFromFloats();
void computeSelfHitTestRects(Vector<LayoutRect>&,
const LayoutPoint& layerOffset) const override;
void absoluteRects(Vector<IntRect>&,
const LayoutPoint& accumulatedOffset) const override;
void absoluteQuads(Vector<FloatQuad>&) const override;
LayoutObject* hoverAncestor() const final;
LayoutUnit logicalRightOffsetForLine(
LayoutUnit logicalTop,
LayoutUnit fixedOffset,
IndentTextOrNot applyTextIndent,
LayoutUnit logicalHeight = LayoutUnit()) const {
return adjustLogicalRightOffsetForLine(
logicalRightFloatOffsetForLine(logicalTop, fixedOffset, logicalHeight),
applyTextIndent);
}
LayoutUnit logicalLeftOffsetForLine(
LayoutUnit logicalTop,
LayoutUnit fixedOffset,
IndentTextOrNot applyTextIndent,
LayoutUnit logicalHeight = LayoutUnit()) const {
return adjustLogicalLeftOffsetForLine(
logicalLeftFloatOffsetForLine(logicalTop, fixedOffset, logicalHeight),
applyTextIndent);
}
virtual LayoutObject* layoutSpecialExcludedChild(bool /*relayoutChildren*/,
SubtreeLayoutScope&);
bool updateLogicalWidthAndColumnWidth() override;
void setLogicalLeftForChild(LayoutBox& child, LayoutUnit logicalLeft);
void setLogicalTopForChild(LayoutBox& child, LayoutUnit logicalTop);
void determineLogicalLeftPositionForChild(LayoutBox& child);
void addOutlineRects(Vector<LayoutRect>&,
const LayoutPoint& additionalOffset,
IncludeBlockVisualOverflowOrNot) const override;
bool paintedOutputOfObjectHasNoEffectRegardlessOfSize() const override;
PaintInvalidationReason invalidatePaintIfNeeded(
const PaintInvalidationState&) override;
void invalidateDisplayItemClients(PaintInvalidationReason) const override;
Node* nodeForHitTest() const final;
bool hitTestChildren(HitTestResult&,
const HitTestLocation& locationInContainer,
const LayoutPoint& accumulatedOffset,
HitTestAction) override;
LayoutSize accumulateInFlowPositionOffsets() const override;
private:
bool layoutBlockFlow(bool relayoutChildren,
LayoutUnit& pageLogicalHeight,
SubtreeLayoutScope&);
void layoutBlockChildren(bool relayoutChildren,
SubtreeLayoutScope&,
LayoutUnit beforeEdge,
LayoutUnit afterEdge);
void markDescendantsWithFloatsForLayoutIfNeeded(
LayoutBlockFlow& child,
LayoutUnit newLogicalTop,
LayoutUnit previousFloatLogicalBottom);
bool positionAndLayoutOnceIfNeeded(LayoutBox& child,
LayoutUnit newLogicalTop,
BlockChildrenLayoutInfo&);
// Handle breaking policy before the child, and insert a forced break in front
// of it if needed.
void insertForcedBreakBeforeChildIfNeeded(LayoutBox& child,
BlockChildrenLayoutInfo&);
void layoutBlockChild(LayoutBox& child, BlockChildrenLayoutInfo&);
void adjustPositionedBlock(LayoutBox& child, const BlockChildrenLayoutInfo&);
void adjustFloatingBlock(const MarginInfo&);
LayoutPoint computeLogicalLocationForFloat(const FloatingObject&,
LayoutUnit logicalTopOffset) const;
void removeFloatingObject(LayoutBox*);
void removeFloatingObjectsBelow(FloatingObject*, int logicalOffset);
LayoutUnit getClearDelta(LayoutBox* child, LayoutUnit yPos);
bool hasOverhangingFloat(LayoutBox*);
void addIntrudingFloats(LayoutBlockFlow* prev,
LayoutUnit xoffset,
LayoutUnit yoffset);
void addOverhangingFloats(LayoutBlockFlow* child,
bool makeChildPaintOtherFloats);
bool hitTestFloats(HitTestResult&,
const HitTestLocation& locationInContainer,
const LayoutPoint& accumulatedOffset);
void clearFloats(EClear);
LayoutUnit logicalRightFloatOffsetForLine(LayoutUnit logicalTop,
LayoutUnit fixedOffset,
LayoutUnit logicalHeight) const;
LayoutUnit logicalLeftFloatOffsetForLine(LayoutUnit logicalTop,
LayoutUnit fixedOffset,
LayoutUnit logicalHeight) const;
LayoutUnit logicalRightOffsetForPositioningFloat(
LayoutUnit logicalTop,
LayoutUnit fixedOffset,
LayoutUnit* heightRemaining) const;
LayoutUnit logicalLeftOffsetForPositioningFloat(
LayoutUnit logicalTop,
LayoutUnit fixedOffset,
LayoutUnit* heightRemaining) const;
LayoutUnit adjustLogicalRightOffsetForLine(
LayoutUnit offsetFromFloats,
IndentTextOrNot applyTextIndent) const;
LayoutUnit adjustLogicalLeftOffsetForLine(
LayoutUnit offsetFromFloats,
IndentTextOrNot applyTextIndent) const;
virtual RootInlineBox* createRootInlineBox(); // Subclassed by SVG
void dirtyLinesFromChangedChild(
LayoutObject* child,
MarkingBehavior markingBehaviour = MarkContainerChain) final {
m_lineBoxes.dirtyLinesFromChangedChild(
LineLayoutItem(this), LineLayoutItem(child),
markingBehaviour == MarkContainerChain);
}
bool isPagedOverflow(const ComputedStyle&);
enum FlowThreadType { NoFlowThread, MultiColumnFlowThread, PagedFlowThread };
FlowThreadType getFlowThreadType(const ComputedStyle&);
LayoutMultiColumnFlowThread* createMultiColumnFlowThread(FlowThreadType);
void createOrDestroyMultiColumnFlowThreadIfNeeded(
const ComputedStyle* oldStyle);
// Merge children of |siblingThatMayBeDeleted| into this object if possible,
// and delete |siblingThatMayBeDeleted|. Returns true if we were able to
// merge. In that case, |siblingThatMayBeDeleted| will be dead. We'll only be
// able to merge if both blocks are anonymous.
bool mergeSiblingContiguousAnonymousBlock(
LayoutBlockFlow* siblingThatMayBeDeleted);
// Reparent subsequent or preceding adjacent floating or out-of-flow siblings
// into this object.
void reparentSubsequentFloatingOrOutOfFlowSiblings();
void reparentPrecedingFloatingOrOutOfFlowSiblings();
void makeChildrenInlineIfPossible();
void makeChildrenNonInline(LayoutObject* insertionPoint = nullptr);
void childBecameNonInline(LayoutObject* child) final;
void updateLogicalWidthForAlignment(const ETextAlign&,
const RootInlineBox*,
BidiRun* trailingSpaceRun,
LayoutUnit& logicalLeft,
LayoutUnit& totalLogicalWidth,
LayoutUnit& availableLogicalWidth,
unsigned expansionOpportunityCount);
void checkForPaginationLogicalHeightChange(
LayoutUnit& pageLogicalHeight,
bool& pageLogicalHeightChanged,
bool& hasSpecifiedPageLogicalHeight);
bool shouldBreakAtLineToAvoidWidow() const {
return m_rareData && m_rareData->m_lineBreakToAvoidWidow >= 0;
}
void clearShouldBreakAtLineToAvoidWidow() const;
int lineBreakToAvoidWidow() const {
return m_rareData ? m_rareData->m_lineBreakToAvoidWidow : -1;
}
void setBreakAtLineToAvoidWidow(int);
void clearDidBreakAtLineToAvoidWidow();
void setDidBreakAtLineToAvoidWidow();
bool didBreakAtLineToAvoidWidow() const {
return m_rareData && m_rareData->m_didBreakAtLineToAvoidWidow;
}
public:
struct FloatWithRect {
DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
FloatWithRect(LayoutBox* f)
: object(f), rect(f->frameRect()), everHadLayout(f->everHadLayout()) {
rect.expand(f->marginBoxOutsets());
}
LayoutBox* object;
LayoutRect rect;
bool everHadLayout;
};
// MarginValues holds the margins in the block direction
// used during collapsing margins computation.
// CSS mandates to keep track of both positive and negative margins:
// "When two or more margins collapse, the resulting margin width is the
// maximum of the collapsing margins' widths. In the case of negative
// margins, the maximum of the absolute values of the negative adjoining
// margins is deducted from the maximum of the positive adjoining margins.
// If there are no positive margins, the maximum of the absolute values of
// the adjoining margins is deducted from zero."
// https://drafts.csswg.org/css2/box.html#collapsing-margins
class MarginValues {
DISALLOW_NEW();
public:
MarginValues(LayoutUnit beforePos,
LayoutUnit beforeNeg,
LayoutUnit afterPos,
LayoutUnit afterNeg)
: m_positiveMarginBefore(beforePos),
m_negativeMarginBefore(beforeNeg),
m_positiveMarginAfter(afterPos),
m_negativeMarginAfter(afterNeg) {}
LayoutUnit positiveMarginBefore() const { return m_positiveMarginBefore; }
LayoutUnit negativeMarginBefore() const { return m_negativeMarginBefore; }
LayoutUnit positiveMarginAfter() const { return m_positiveMarginAfter; }
LayoutUnit negativeMarginAfter() const { return m_negativeMarginAfter; }
void setPositiveMarginBefore(LayoutUnit pos) {
m_positiveMarginBefore = pos;
}
void setNegativeMarginBefore(LayoutUnit neg) {
m_negativeMarginBefore = neg;
}
void setPositiveMarginAfter(LayoutUnit pos) { m_positiveMarginAfter = pos; }
void setNegativeMarginAfter(LayoutUnit neg) { m_negativeMarginAfter = neg; }
private:
LayoutUnit m_positiveMarginBefore;
LayoutUnit m_negativeMarginBefore;
LayoutUnit m_positiveMarginAfter;
LayoutUnit m_negativeMarginAfter;
};
MarginValues marginValuesForChild(LayoutBox& child) const;
// Allocated only when some of these fields have non-default values
struct LayoutBlockFlowRareData {
WTF_MAKE_NONCOPYABLE(LayoutBlockFlowRareData);
USING_FAST_MALLOC(LayoutBlockFlowRareData);
public:
LayoutBlockFlowRareData(const LayoutBlockFlow* block)
: m_margins(positiveMarginBeforeDefault(block),
negativeMarginBeforeDefault(block),
positiveMarginAfterDefault(block),
negativeMarginAfterDefault(block)),
m_multiColumnFlowThread(nullptr),
m_breakBefore(BreakAuto),
m_breakAfter(BreakAuto),
m_lineBreakToAvoidWidow(-1),
m_didBreakAtLineToAvoidWidow(false),
m_discardMarginBefore(false),
m_discardMarginAfter(false) {}
static LayoutUnit positiveMarginBeforeDefault(
const LayoutBlockFlow* block) {
return block->marginBefore().clampNegativeToZero();
}
static LayoutUnit negativeMarginBeforeDefault(
const LayoutBlockFlow* block) {
return (-block->marginBefore()).clampNegativeToZero();
}
static LayoutUnit positiveMarginAfterDefault(const LayoutBlockFlow* block) {
return block->marginAfter().clampNegativeToZero();
}
static LayoutUnit negativeMarginAfterDefault(const LayoutBlockFlow* block) {
return (-block->marginAfter()).clampNegativeToZero();
}
MarginValues m_margins;
LayoutUnit m_paginationStrutPropagatedFromChild;
LayoutMultiColumnFlowThread* m_multiColumnFlowThread;
unsigned m_breakBefore : 4;
unsigned m_breakAfter : 4;
int m_lineBreakToAvoidWidow;
bool m_didBreakAtLineToAvoidWidow : 1;
bool m_discardMarginBefore : 1;
bool m_discardMarginAfter : 1;
};
const FloatingObjects* floatingObjects() const {
return m_floatingObjects.get();
}
static void setAncestorShouldPaintFloatingObject(const LayoutBox& floatBox);
protected:
LayoutUnit maxPositiveMarginBefore() const {
return m_rareData
? m_rareData->m_margins.positiveMarginBefore()
: LayoutBlockFlowRareData::positiveMarginBeforeDefault(this);
}
LayoutUnit maxNegativeMarginBefore() const {
return m_rareData
? m_rareData->m_margins.negativeMarginBefore()
: LayoutBlockFlowRareData::negativeMarginBeforeDefault(this);
}
LayoutUnit maxPositiveMarginAfter() const {
return m_rareData
? m_rareData->m_margins.positiveMarginAfter()
: LayoutBlockFlowRareData::positiveMarginAfterDefault(this);
}
LayoutUnit maxNegativeMarginAfter() const {
return m_rareData
? m_rareData->m_margins.negativeMarginAfter()
: LayoutBlockFlowRareData::negativeMarginAfterDefault(this);
}
void setMaxMarginBeforeValues(LayoutUnit pos, LayoutUnit neg);
void setMaxMarginAfterValues(LayoutUnit pos, LayoutUnit neg);
void setMustDiscardMarginBefore(bool = true);
void setMustDiscardMarginAfter(bool = true);
bool mustDiscardMarginBefore() const;
bool mustDiscardMarginAfter() const;
bool mustDiscardMarginBeforeForChild(const LayoutBox&) const;
bool mustDiscardMarginAfterForChild(const LayoutBox&) const;
bool mustSeparateMarginBeforeForChild(const LayoutBox&) const;
bool mustSeparateMarginAfterForChild(const LayoutBox&) const;
void initMaxMarginValues() {
if (m_rareData) {
m_rareData->m_margins = MarginValues(
LayoutBlockFlowRareData::positiveMarginBeforeDefault(this),
LayoutBlockFlowRareData::negativeMarginBeforeDefault(this),
LayoutBlockFlowRareData::positiveMarginAfterDefault(this),
LayoutBlockFlowRareData::negativeMarginAfterDefault(this));
m_rareData->m_discardMarginBefore = false;
m_rareData->m_discardMarginAfter = false;
}
}
virtual ETextAlign textAlignmentForLine(bool endsWithSoftBreak) const;
private:
LayoutUnit collapsedMarginBefore() const final {
return maxPositiveMarginBefore() - maxNegativeMarginBefore();
}
LayoutUnit collapsedMarginAfter() const final {
return maxPositiveMarginAfter() - maxNegativeMarginAfter();
}
// Floats' margins do not collapse with page or column boundaries, and we
// therefore need to treat them specially in some cases.
LayoutUnit marginBeforeIfFloating() const {
return isFloating() ? marginBefore() : LayoutUnit();
}
LayoutUnit collapseMargins(LayoutBox& child,
MarginInfo&,
bool childIsSelfCollapsing,
bool childDiscardMarginBefore,
bool childDiscardMarginAfter);
LayoutUnit clearFloatsIfNeeded(LayoutBox& child,
MarginInfo&,
LayoutUnit oldTopPosMargin,
LayoutUnit oldTopNegMargin,
LayoutUnit yPos,
bool childIsSelfCollapsing,
bool childDiscardMargin);
LayoutUnit estimateLogicalTopPosition(LayoutBox& child,
const BlockChildrenLayoutInfo&,
LayoutUnit& estimateWithoutPagination);
void marginBeforeEstimateForChild(LayoutBox&,
LayoutUnit&,
LayoutUnit&,
bool&) const;
void handleAfterSideOfBlock(LayoutBox* lastChild,
LayoutUnit top,
LayoutUnit bottom,
MarginInfo&);
void setCollapsedBottomMargin(const MarginInfo&);
// Apply any forced fragmentainer break that's set on the current class A
// break point.
LayoutUnit applyForcedBreak(LayoutUnit logicalOffset, EBreak);
void setBreakBefore(EBreak);
void setBreakAfter(EBreak);
EBreak breakBefore() const override;
EBreak breakAfter() const override;
LayoutUnit adjustBlockChildForPagination(LayoutUnit logicalTop,
LayoutBox& child,
BlockChildrenLayoutInfo&,
bool atBeforeSideOfBlock);
// Computes a deltaOffset value that put a line at the top of the next page if
// it doesn't fit on the current page.
void adjustLinePositionForPagination(RootInlineBox&, LayoutUnit& deltaOffset);
// If the child is unsplittable and can't fit on the current page, return the
// top of the next page/column.
LayoutUnit adjustForUnsplittableChild(LayoutBox&,
LayoutUnit logicalOffset) const;
// Used to store state between styleWillChange and styleDidChange
static bool s_canPropagateFloatIntoSibling;
LineBoxList m_lineBoxes; // All of the root line boxes created for this block
// flow. For example, <div>Hello<br>world.</div>
// will have two total lines for the <div>.
LayoutBlockFlowRareData& ensureRareData();
bool isSelfCollapsingBlock() const override;
bool checkIfIsSelfCollapsingBlock() const;
protected:
std::unique_ptr<LayoutBlockFlowRareData> m_rareData;
std::unique_ptr<FloatingObjects> m_floatingObjects;
friend class MarginInfo;
friend class LineWidth; // needs to know FloatingObject
// LayoutRubyBase objects need to be able to split and merge, moving their
// children around (calling makeChildrenNonInline).
// TODO(mstensho): Try to get rid of this friendship.
friend class LayoutRubyBase;
// FIXME-BLOCKFLOW: These methods have implementations in
// LayoutBlockFlowLine. They should be moved to the proper header once the
// line layout code is separated from LayoutBlock and LayoutBlockFlow.
// START METHODS DEFINED IN LayoutBlockFlowLine
private:
InlineFlowBox* createLineBoxes(LineLayoutItem,
const LineInfo&,
InlineBox* childBox);
RootInlineBox* constructLine(BidiRunList<BidiRun>&, const LineInfo&);
void setMarginsForRubyRun(BidiRun*,
LayoutRubyRun*,
LayoutObject*,
const LineInfo&);
void computeInlineDirectionPositionsForLine(RootInlineBox*,
const LineInfo&,
BidiRun* firstRun,
BidiRun* trailingSpaceRun,
bool reachedEnd,
GlyphOverflowAndFallbackFontsMap&,
VerticalPositionCache&,
WordMeasurements&);
BidiRun* computeInlineDirectionPositionsForSegment(
RootInlineBox*,
const LineInfo&,
ETextAlign,
LayoutUnit& logicalLeft,
LayoutUnit& availableLogicalWidth,
BidiRun* firstRun,
BidiRun* trailingSpaceRun,
GlyphOverflowAndFallbackFontsMap& textBoxDataMap,
VerticalPositionCache&,
WordMeasurements&);
void computeBlockDirectionPositionsForLine(RootInlineBox*,
BidiRun*,
GlyphOverflowAndFallbackFontsMap&,
VerticalPositionCache&);
void appendFloatingObjectToLastLine(FloatingObject&);
void appendFloatsToLastLine(LineLayoutState&,
const InlineIterator& cleanLineStart,
const InlineBidiResolver&,
const BidiStatus& cleanLineBidiStatus);
// Helper function for layoutInlineChildren()
RootInlineBox* createLineBoxesFromBidiRuns(unsigned bidiLevel,
BidiRunList<BidiRun>&,
const InlineIterator& end,
LineInfo&,
VerticalPositionCache&,
BidiRun* trailingSpaceRun,
WordMeasurements&);
void layoutRunsAndFloats(LineLayoutState&);
const InlineIterator& restartLayoutRunsAndFloatsInRange(
LayoutUnit oldLogicalHeight,
LayoutUnit newLogicalHeight,
FloatingObject* lastFloatFromPreviousLine,
InlineBidiResolver&,
const InlineIterator&);
void layoutRunsAndFloatsInRange(LineLayoutState&,
InlineBidiResolver&,
const InlineIterator& cleanLineStart,
const BidiStatus& cleanLineBidiStatus);
void linkToEndLineIfNeeded(LineLayoutState&);
void markDirtyFloatsForPaintInvalidation(Vector<FloatWithRect>& floats);
RootInlineBox* determineStartPosition(LineLayoutState&, InlineBidiResolver&);
void determineEndPosition(LineLayoutState&,
RootInlineBox* startBox,
InlineIterator& cleanLineStart,
BidiStatus& cleanLineBidiStatus);
bool lineBoxHasBRWithClearance(RootInlineBox*);
bool checkPaginationAndFloatsAtEndLine(LineLayoutState&);
bool matchedEndLine(LineLayoutState&,
const InlineBidiResolver&,
const InlineIterator& endLineStart,
const BidiStatus& endLineStatus);
void deleteEllipsisLineBoxes();
void checkLinesForTextOverflow();
void markLinesDirtyInBlockRange(LayoutUnit logicalTop,
LayoutUnit logicalBottom,
RootInlineBox* highest = nullptr);
// Positions new floats and also adjust all floats encountered on the line if
// any of them have to move to the next page/column.
void positionDialog();
// END METHODS DEFINED IN LayoutBlockFlowLine
};
DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutBlockFlow, isLayoutBlockFlow());
} // namespace blink
#endif // LayoutBlockFlow_h