blob: 9a000f4cbaa58d9957013cd5584b9726b84db19b [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 THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_BLOCK_FLOW_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_BLOCK_FLOW_H_
#include <memory>
#include "base/macros.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/layout/api/line_layout_item.h"
#include "third_party/blink/renderer/core/layout/floating_objects.h"
#include "third_party/blink/renderer/core/layout/layout_block.h"
#include "third_party/blink/renderer/core/layout/line/line_box_list.h"
#include "third_party/blink/renderer/core/layout/line/root_inline_box.h"
#include "third_party/blink/renderer/core/layout/line/trailing_objects.h"
namespace blink {
template <class Run>
class BidiRunList;
class BlockChildrenLayoutInfo;
class LayoutInline;
class LineInfo;
class LineLayoutState;
class LineWidth;
class LayoutMultiColumnFlowThread;
class LayoutMultiColumnSpannerPlaceholder;
class LayoutRubyRun;
class MarginInfo;
class NGBreakToken;
class NGConstraintSpace;
class NGLayoutResult;
class NGPaintFragment;
class NGPhysicalFragment;
struct NGInlineNodeData;
struct NGPhysicalOffset;
enum IndentTextOrNot { kDoNotIndentText, kIndentText };
// 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*,
scoped_refptr<ComputedStyle>);
bool BeingDestroyed() const { return being_destroyed_; }
bool IsLayoutBlockFlow() const final { return true; }
void UpdateBlockLayout(bool relayout_children) override;
void ComputeVisualOverflow(const LayoutRect&, bool recompute_floats) override;
void ComputeLayoutOverflow(LayoutUnit old_client_after_edge,
bool recompute_floats) override;
void DeleteLineBoxTree();
bool CanContainFirstFormattedLine() const;
LayoutUnit AvailableLogicalWidthForLine(
LayoutUnit position,
IndentTextOrNot indent_text,
LayoutUnit logical_height = LayoutUnit()) const {
return (LogicalRightOffsetForLine(position, indent_text, logical_height) -
LogicalLeftOffsetForLine(position, indent_text, logical_height))
.ClampNegativeToZero();
}
LayoutUnit LogicalRightOffsetForLine(
LayoutUnit position,
IndentTextOrNot indent_text,
LayoutUnit logical_height = LayoutUnit()) const {
return LogicalRightOffsetForLine(position, LogicalRightOffsetForContent(),
indent_text, logical_height);
}
LayoutUnit LogicalLeftOffsetForLine(
LayoutUnit position,
IndentTextOrNot indent_text,
LayoutUnit logical_height = LayoutUnit()) const {
return LogicalLeftOffsetForLine(position, LogicalLeftOffsetForContent(),
indent_text, logical_height);
}
LayoutUnit StartOffsetForLine(
LayoutUnit position,
IndentTextOrNot indent_text,
LayoutUnit logical_height = LayoutUnit()) const {
return StyleRef().IsLeftToRightDirection()
? LogicalLeftOffsetForLine(position, indent_text, logical_height)
: LogicalWidth() - LogicalRightOffsetForLine(
position, indent_text, logical_height);
}
LayoutUnit AvailableLogicalWidthForAvoidingFloats(
LayoutUnit position,
LayoutUnit logical_height = LayoutUnit()) const {
return (LogicalRightOffsetForAvoidingFloats(position, logical_height) -
LogicalLeftOffsetForAvoidingFloats(position, logical_height))
.ClampNegativeToZero();
}
LayoutUnit LogicalLeftOffsetForAvoidingFloats(
LayoutUnit position,
LayoutUnit logical_height = LayoutUnit()) const {
return LogicalLeftFloatOffsetForAvoidingFloats(
position, LogicalLeftOffsetForContent(), logical_height);
}
LayoutUnit LogicalRightOffsetForAvoidingFloats(
LayoutUnit position,
LayoutUnit logical_height = LayoutUnit()) const {
return LogicalRightFloatOffsetForAvoidingFloats(
position, LogicalRightOffsetForContent(), logical_height);
}
LayoutUnit StartOffsetForAvoidingFloats(
LayoutUnit position,
LayoutUnit logical_height = LayoutUnit()) const {
return StyleRef().IsLeftToRightDirection()
? LogicalLeftOffsetForAvoidingFloats(position, logical_height)
: LogicalWidth() - LogicalRightOffsetForAvoidingFloats(
position, logical_height);
}
LayoutUnit EndOffsetForAvoidingFloats(
LayoutUnit position,
LayoutUnit logical_height = LayoutUnit()) const {
return !StyleRef().IsLeftToRightDirection()
? LogicalLeftOffsetForAvoidingFloats(position, logical_height)
: LogicalWidth() - LogicalRightOffsetForAvoidingFloats(
position, logical_height);
}
const LineBoxList& LineBoxes() const { return line_boxes_; }
LineBoxList* LineBoxes() { return &line_boxes_; }
InlineFlowBox* FirstLineBox() const { return line_boxes_.First(); }
InlineFlowBox* LastLineBox() const { return line_boxes_.Last(); }
RootInlineBox* FirstRootBox() const {
return static_cast<RootInlineBox*>(FirstLineBox());
}
RootInlineBox* LastRootBox() const {
return static_cast<RootInlineBox*>(LastLineBox());
}
RootInlineBox* CreateAndAppendRootInlineBox();
RootInlineBox* ConstructLine(BidiRunList<BidiRun>&, const LineInfo&);
// 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* stop_root_inline_box = nullptr) const;
LayoutUnit FirstLineBoxBaseline() const override;
LayoutUnit InlineBlockBaseline(LineDirectionMode) const override;
void RemoveFloatingObjectsFromDescendants();
void MarkAllDescendantsWithFloatsForLayout(
LayoutBox* float_to_remove = nullptr,
bool in_layout = true);
void MarkSiblingsWithFloatsForLayout(LayoutBox* float_to_remove = nullptr);
bool ContainsFloats() const {
return floating_objects_ && !floating_objects_->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* new_child,
LayoutObject* before_child = nullptr) override;
void RemoveChild(LayoutObject*) override;
void MoveAllChildrenIncludingFloatsTo(LayoutBlock* to_block,
bool full_remove_insert);
void ChildBecameFloatingOrOutOfFlow(LayoutBox* child);
void CollapseAnonymousBlockChild(LayoutBlockFlow* child);
bool GeneratesLineBoxesForInlineChild(LayoutObject*);
LayoutUnit LogicalTopForFloat(const FloatingObject& floating_object) const {
return IsHorizontalWritingMode() ? floating_object.Y()
: floating_object.X();
}
LayoutUnit LogicalBottomForFloat(
const FloatingObject& floating_object) const {
return IsHorizontalWritingMode() ? floating_object.MaxY()
: floating_object.MaxX();
}
LayoutUnit LogicalLeftForFloat(const FloatingObject& floating_object) const {
return IsHorizontalWritingMode() ? floating_object.X()
: floating_object.Y();
}
LayoutUnit LogicalRightForFloat(const FloatingObject& floating_object) const {
return IsHorizontalWritingMode() ? floating_object.MaxX()
: floating_object.MaxY();
}
LayoutUnit LogicalWidthForFloat(const FloatingObject& floating_object) const {
return IsHorizontalWritingMode() ? floating_object.Width()
: floating_object.Height();
}
void SetLogicalTopForFloat(FloatingObject& floating_object,
LayoutUnit logical_top) {
if (IsHorizontalWritingMode())
floating_object.SetY(logical_top);
else
floating_object.SetX(logical_top);
}
void SetLogicalLeftForFloat(FloatingObject& floating_object,
LayoutUnit logical_left) {
if (IsHorizontalWritingMode())
floating_object.SetX(logical_left);
else
floating_object.SetY(logical_left);
}
void SetLogicalHeightForFloat(FloatingObject& floating_object,
LayoutUnit logical_height) {
if (IsHorizontalWritingMode())
floating_object.SetHeight(logical_height);
else
floating_object.SetWidth(logical_height);
}
void SetLogicalWidthForFloat(FloatingObject& floating_object,
LayoutUnit logical_width) {
if (IsHorizontalWritingMode())
floating_object.SetWidth(logical_width);
else
floating_object.SetHeight(logical_width);
}
LayoutUnit StartAlignedOffsetForLine(LayoutUnit position, IndentTextOrNot);
void SetStaticInlinePositionForChild(LayoutBox&, LayoutUnit inline_position);
void UpdateStaticInlinePositionForChild(LayoutBox&,
LayoutUnit logical_top,
IndentTextOrNot = kDoNotIndentText);
static bool ShouldSkipCreatingRunsForObject(LineLayoutItem obj) {
return obj.IsFloating() || (obj.IsOutOfFlowPositioned() &&
!obj.StyleRef().IsOriginalDisplayInlineType() &&
!obj.Container().IsLayoutInline());
}
LayoutMultiColumnFlowThread* MultiColumnFlowThread() const {
return rare_data_ ? rare_data_->multi_column_flow_thread_ : nullptr;
}
void ResetMultiColumnFlowThread() {
if (rare_data_)
rare_data_->multi_column_flow_thread_ = nullptr;
}
void AddVisualOverflowFromInlineChildren();
void AddLayoutOverflowFromInlineChildren();
// FIXME: This should be const to avoid a const_cast, but can modify child
// dirty bits and LayoutTextCombine.
void ComputeInlinePreferredLogicalWidths(LayoutUnit& min_logical_width,
LayoutUnit& max_logical_width);
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 rare_data_ ? rare_data_->pagination_strut_propagated_from_child_
: LayoutUnit();
}
void SetPaginationStrutPropagatedFromChild(LayoutUnit);
LayoutUnit FirstForcedBreakOffset() const {
if (!rare_data_)
return LayoutUnit();
return rare_data_->first_forced_break_offset_;
}
void SetFirstForcedBreakOffset(LayoutUnit);
void PositionSpannerDescendant(LayoutMultiColumnSpannerPlaceholder& child);
bool CreatesNewFormattingContext() const override;
bool AvoidsFloats() const final;
using LayoutBoxModelObject::MoveChildrenTo;
void MoveChildrenTo(LayoutBoxModelObject* to_box_model_object,
LayoutObject* start_child,
LayoutObject* end_child,
LayoutObject* before_child,
bool full_remove_insert = false) override;
LayoutUnit XPositionForFloatIncludingMargin(
const FloatingObject& child) const {
LayoutUnit scrollbar_adjustment(OriginAdjustmentForScrollbars().Width());
if (IsHorizontalWritingMode()) {
return child.X() + child.GetLayoutObject()->MarginLeft() +
scrollbar_adjustment;
}
return child.X() + MarginBeforeForChild(*child.GetLayoutObject());
}
DISABLE_CFI_PERF
LayoutUnit YPositionForFloatIncludingMargin(
const FloatingObject& child) const {
if (IsHorizontalWritingMode())
return child.Y() + MarginBeforeForChild(*child.GetLayoutObject());
return child.Y() + child.GetLayoutObject()->MarginTop();
}
LayoutPoint FlipFloatForWritingModeForChild(const FloatingObject&,
const LayoutPoint&) const;
const char* GetName() const override { return "LayoutBlockFlow"; }
FloatingObject* InsertFloatingObject(LayoutBox&);
// Position and lay out all floats that have not yet been positioned.
//
// This will mark them as "placed", which means that they have found their
// final location in this layout pass.
//
// |logicalTopMarginEdge| is the minimum logical top for the floats. The
// final logical top of the floats will also be affected by clearance and
// space available after having positioned earlier floats.
//
// Returns true if and only if it has placed any floats.
bool PlaceNewFloats(LayoutUnit logical_top_margin_edge, LineWidth* = nullptr);
// Position and lay out the float, if it needs layout.
// |logicalTopMarginEdge| is the minimum logical top offset for the float.
// The value returned is the minimum logical top offset for subsequent
// floats.
LayoutUnit PositionAndLayoutFloat(FloatingObject&,
LayoutUnit logical_top_margin_edge);
LayoutUnit NextFloatLogicalBottomBelow(LayoutUnit) const;
LayoutUnit NextFloatLogicalBottomBelowForBlock(LayoutUnit) const;
FloatingObject* LastFloatFromPreviousLine() const {
return ContainsFloats() ? floating_objects_->Set().back().get() : nullptr;
}
void SetShouldDoFullPaintInvalidationForFirstLine();
void SimplifiedNormalFlowInlineLayout();
bool RecalcInlineChildrenOverflow();
PositionWithAffinity PositionForPoint(const LayoutPoint&) const override;
LayoutUnit LowestFloatLogicalBottom(EClear = EClear::kBoth) const;
bool HasOverhangingFloats() const {
return Parent() && ContainsFloats() &&
LowestFloatLogicalBottom() > LogicalHeight();
}
bool IsOverhangingFloat(const FloatingObject& float_object) const {
return LogicalBottomForFloat(float_object) > LogicalHeight();
}
LayoutUnit LogicalHeightWithVisibleOverflow() const final;
// This function is only public so we can call it from NGBlockNode while we're
// still working on LayoutNG.
void UpdateIsSelfCollapsing() {
is_self_collapsing_ = CheckIfIsSelfCollapsingBlock();
}
// These functions are only public so we can call it from NGBlockNode while
// we're still working on LayoutNG.
void AddVisualOverflowFromFloats();
void AddLayoutOverflowFromFloats();
virtual NGInlineNodeData* TakeNGInlineNodeData() { return nullptr; }
virtual NGInlineNodeData* GetNGInlineNodeData() const { return nullptr; }
virtual void ResetNGInlineNodeData() {}
virtual bool HasNGInlineNodeData() const { return false; }
virtual NGPaintFragment* PaintFragment() const { return nullptr; }
virtual scoped_refptr<NGLayoutResult> CachedLayoutResult(
const NGConstraintSpace&,
const NGBreakToken*);
virtual scoped_refptr<const NGLayoutResult> CachedLayoutResultForTesting();
virtual void SetCachedLayoutResult(const NGConstraintSpace&,
const NGBreakToken*,
const NGLayoutResult&);
virtual void ClearCachedLayoutResult();
virtual bool AreCachedLinesValidFor(const NGConstraintSpace&) const;
virtual void WillCollectInlines() {}
virtual void SetPaintFragment(const NGBreakToken*,
scoped_refptr<const NGPhysicalFragment>,
NGPhysicalOffset);
virtual void UpdatePaintFragmentFromCachedLayoutResult(
const NGBreakToken*,
scoped_refptr<const NGPhysicalFragment>,
NGPhysicalOffset);
virtual const NGPhysicalBoxFragment* CurrentFragment() const {
return nullptr;
}
#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 relayout_children, LayoutUnit after_edge);
void AddLowestFloatFromChildren(LayoutBlockFlow*);
void CreateFloatingObjects();
void WillBeDestroyed() override;
void StyleWillChange(StyleDifference,
const ComputedStyle& new_style) override;
void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override;
void UpdateBlockChildDirtyBitsBeforeLayout(bool relayout_children,
LayoutBox&);
void ComputeSelfHitTestRects(Vector<LayoutRect>&,
const LayoutPoint& layer_offset) const override;
void AbsoluteRects(Vector<IntRect>&,
const LayoutPoint& accumulated_offset) const override;
void AbsoluteQuads(Vector<FloatQuad>&,
MapCoordinatesFlags mode = 0) const override;
void AbsoluteQuadsForSelf(Vector<FloatQuad>& quads,
MapCoordinatesFlags mode = 0) const override;
LayoutObject* HoverAncestor() const final;
LayoutUnit LogicalRightOffsetForLine(
LayoutUnit logical_top,
LayoutUnit fixed_offset,
IndentTextOrNot apply_text_indent,
LayoutUnit logical_height = LayoutUnit()) const {
return AdjustLogicalRightOffsetForLine(
LogicalRightFloatOffsetForLine(logical_top, fixed_offset,
logical_height),
apply_text_indent);
}
LayoutUnit LogicalLeftOffsetForLine(
LayoutUnit logical_top,
LayoutUnit fixed_offset,
IndentTextOrNot apply_text_indent,
LayoutUnit logical_height = LayoutUnit()) const {
return AdjustLogicalLeftOffsetForLine(
LogicalLeftFloatOffsetForLine(logical_top, fixed_offset,
logical_height),
apply_text_indent);
}
virtual LayoutObject* LayoutSpecialExcludedChild(bool /*relayoutChildren*/,
SubtreeLayoutScope&);
bool UpdateLogicalWidthAndColumnWidth() override;
void SetLogicalLeftForChild(LayoutBox& child, LayoutUnit logical_left);
void SetLogicalTopForChild(LayoutBox& child, LayoutUnit logical_top);
void DetermineLogicalLeftPositionForChild(LayoutBox& child);
void AddOutlineRects(Vector<LayoutRect>&,
const LayoutPoint& additional_offset,
NGOutlineType) const override;
bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const override;
void InvalidateDisplayItemClients(PaintInvalidationReason) const override;
Node* NodeForHitTest() const final;
bool HitTestChildren(HitTestResult&,
const HitTestLocation& location_in_container,
const LayoutPoint& accumulated_offset,
HitTestAction) override;
LayoutSize AccumulateInFlowPositionOffsets() const override;
private:
void ResetLayout();
void LayoutChildren(bool relayout_children, SubtreeLayoutScope&);
void AddOverhangingFloatsFromChildren(LayoutUnit unconstrained_height);
void LayoutBlockChildren(bool relayout_children,
SubtreeLayoutScope&,
LayoutUnit before_edge,
LayoutUnit after_edge);
void MarkDescendantsWithFloatsForLayoutIfNeeded(
LayoutBlockFlow& child,
LayoutUnit new_logical_top,
LayoutUnit previous_float_logical_bottom);
bool PositionAndLayoutOnceIfNeeded(LayoutBox& child,
LayoutUnit new_logical_top,
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 logical_top_offset) const;
void RemoveFloatingObject(LayoutBox*);
void RemoveFloatingObjectsBelow(FloatingObject*, LayoutUnit logical_offset);
LayoutUnit GetClearDelta(LayoutBox* child, LayoutUnit y_pos);
bool HasOverhangingFloat(LayoutBox*);
void AddIntrudingFloats(LayoutBlockFlow* prev,
LayoutUnit xoffset,
LayoutUnit yoffset);
void AddOverhangingFloats(LayoutBlockFlow* child,
bool make_child_paint_other_floats);
bool HitTestFloats(HitTestResult&,
const HitTestLocation& location_in_container,
const LayoutPoint& accumulated_offset);
void ClearFloats(EClear);
LayoutUnit LogicalRightFloatOffsetForLine(LayoutUnit logical_top,
LayoutUnit fixed_offset,
LayoutUnit logical_height) const;
LayoutUnit LogicalLeftFloatOffsetForLine(LayoutUnit logical_top,
LayoutUnit fixed_offset,
LayoutUnit logical_height) const;
LayoutUnit LogicalLeftFloatOffsetForAvoidingFloats(
LayoutUnit logical_top,
LayoutUnit fixed_offset,
LayoutUnit logical_height) const;
LayoutUnit LogicalRightFloatOffsetForAvoidingFloats(
LayoutUnit logical_top,
LayoutUnit fixed_offset,
LayoutUnit logical_height) const;
LayoutUnit LogicalRightOffsetForPositioningFloat(
LayoutUnit logical_top,
LayoutUnit fixed_offset,
LayoutUnit* height_remaining) const;
LayoutUnit LogicalLeftOffsetForPositioningFloat(
LayoutUnit logical_top,
LayoutUnit fixed_offset,
LayoutUnit* height_remaining) const;
LayoutUnit AdjustLogicalRightOffsetForLine(
LayoutUnit offset_from_floats,
IndentTextOrNot apply_text_indent) const;
LayoutUnit AdjustLogicalLeftOffsetForLine(
LayoutUnit offset_from_floats,
IndentTextOrNot apply_text_indent) const;
virtual RootInlineBox* CreateRootInlineBox(); // Subclassed by SVG
void DirtyLinesFromChangedChild(
LayoutObject* child,
MarkingBehavior marking_behaviour = kMarkContainerChain) override {
line_boxes_.DirtyLinesFromChangedChild(
LineLayoutItem(this), LineLayoutItem(child),
marking_behaviour == kMarkContainerChain);
}
bool IsPagedOverflow(const ComputedStyle&);
enum FlowThreadType {
kNoFlowThread,
kMultiColumnFlowThread,
kPagedFlowThread
};
FlowThreadType GetFlowThreadType(const ComputedStyle&);
LayoutMultiColumnFlowThread* CreateMultiColumnFlowThread(FlowThreadType);
void CreateOrDestroyMultiColumnFlowThreadIfNeeded(
const ComputedStyle* old_style);
// 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* sibling_that_may_be_deleted);
// Reparent subsequent or preceding adjacent floating or out-of-flow siblings
// into this object.
void ReparentSubsequentFloatingOrOutOfFlowSiblings();
void ReparentPrecedingFloatingOrOutOfFlowSiblings();
bool NeedsAnonymousInlineWrapper() const;
void MakeChildrenInlineIfPossible();
void MakeChildrenNonInline(LayoutObject* insertion_point = nullptr);
void ChildBecameNonInline(LayoutObject* child) final;
void UpdateLogicalWidthForAlignment(const ETextAlign&,
const RootInlineBox*,
BidiRun* trailing_space_run,
LayoutUnit& logical_left,
LayoutUnit& total_logical_width,
LayoutUnit& available_logical_width,
unsigned expansion_opportunity_count);
bool ShouldBreakAtLineToAvoidWidow() const {
return rare_data_ && rare_data_->line_break_to_avoid_widow_ >= 0;
}
void ClearShouldBreakAtLineToAvoidWidow() const;
int LineBreakToAvoidWidow() const {
return rare_data_ ? rare_data_->line_break_to_avoid_widow_ : -1;
}
void SetBreakAtLineToAvoidWidow(int);
void ClearDidBreakAtLineToAvoidWidow();
void SetDidBreakAtLineToAvoidWidow();
bool DidBreakAtLineToAvoidWidow() const {
return rare_data_ && rare_data_->did_break_at_line_to_avoid_widow_;
}
public:
struct FloatWithRect {
DISALLOW_NEW();
FloatWithRect(LayoutBox* f)
: object(f), rect(f->FrameRect()), ever_had_layout(f->EverHadLayout()) {
rect.Expand(f->MarginBoxOutsets());
}
LayoutBox* object;
LayoutRect rect;
bool ever_had_layout;
};
// 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 before_pos,
LayoutUnit before_neg,
LayoutUnit after_pos,
LayoutUnit after_neg)
: positive_margin_before_(before_pos),
negative_margin_before_(before_neg),
positive_margin_after_(after_pos),
negative_margin_after_(after_neg) {}
LayoutUnit PositiveMarginBefore() const { return positive_margin_before_; }
LayoutUnit NegativeMarginBefore() const { return negative_margin_before_; }
LayoutUnit PositiveMarginAfter() const { return positive_margin_after_; }
LayoutUnit NegativeMarginAfter() const { return negative_margin_after_; }
void SetPositiveMarginBefore(LayoutUnit pos) {
positive_margin_before_ = pos;
}
void SetNegativeMarginBefore(LayoutUnit neg) {
negative_margin_before_ = neg;
}
void SetPositiveMarginAfter(LayoutUnit pos) {
positive_margin_after_ = pos;
}
void SetNegativeMarginAfter(LayoutUnit neg) {
negative_margin_after_ = neg;
}
private:
LayoutUnit positive_margin_before_;
LayoutUnit negative_margin_before_;
LayoutUnit positive_margin_after_;
LayoutUnit negative_margin_after_;
};
MarginValues MarginValuesForChild(LayoutBox& child) const;
// Allocated only when some of these fields have non-default values
struct LayoutBlockFlowRareData {
USING_FAST_MALLOC(LayoutBlockFlowRareData);
public:
LayoutBlockFlowRareData(const LayoutBlockFlow* block)
: margins_(PositiveMarginBeforeDefault(block),
NegativeMarginBeforeDefault(block),
PositiveMarginAfterDefault(block),
NegativeMarginAfterDefault(block)),
multi_column_flow_thread_(nullptr),
break_before_(static_cast<unsigned>(EBreakBetween::kAuto)),
break_after_(static_cast<unsigned>(EBreakBetween::kAuto)),
line_break_to_avoid_widow_(-1),
did_break_at_line_to_avoid_widow_(false),
discard_margin_before_(false),
discard_margin_after_(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 margins_;
LayoutUnit pagination_strut_propagated_from_child_;
LayoutUnit first_forced_break_offset_;
LayoutMultiColumnFlowThread* multi_column_flow_thread_;
unsigned break_before_ : 4;
unsigned break_after_ : 4;
int line_break_to_avoid_widow_;
bool did_break_at_line_to_avoid_widow_ : 1;
bool discard_margin_before_ : 1;
bool discard_margin_after_ : 1;
DISALLOW_COPY_AND_ASSIGN(LayoutBlockFlowRareData);
};
const FloatingObjects* GetFloatingObjects() const {
return floating_objects_.get();
}
static void UpdateAncestorShouldPaintFloatingObject(
const LayoutBox& float_box);
bool ShouldTruncateOverflowingText() const;
int GetLayoutPassCountForTesting();
// This is public only for use by LayoutNG, so that NGBlockNode can call it.
void IncrementLayoutPassCount();
protected:
LayoutUnit MaxPositiveMarginBefore() const {
return rare_data_
? rare_data_->margins_.PositiveMarginBefore()
: LayoutBlockFlowRareData::PositiveMarginBeforeDefault(this);
}
LayoutUnit MaxNegativeMarginBefore() const {
return rare_data_
? rare_data_->margins_.NegativeMarginBefore()
: LayoutBlockFlowRareData::NegativeMarginBeforeDefault(this);
}
LayoutUnit MaxPositiveMarginAfter() const {
return rare_data_
? rare_data_->margins_.PositiveMarginAfter()
: LayoutBlockFlowRareData::PositiveMarginAfterDefault(this);
}
LayoutUnit MaxNegativeMarginAfter() const {
return rare_data_
? rare_data_->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 (rare_data_) {
rare_data_->margins_ = MarginValues(
LayoutBlockFlowRareData::PositiveMarginBeforeDefault(this),
LayoutBlockFlowRareData::NegativeMarginBeforeDefault(this),
LayoutBlockFlowRareData::PositiveMarginAfterDefault(this),
LayoutBlockFlowRareData::NegativeMarginAfterDefault(this));
rare_data_->discard_margin_before_ = false;
rare_data_->discard_margin_after_ = false;
}
}
virtual ETextAlign TextAlignmentForLine(bool ends_with_soft_break) const;
private:
LayoutUnit CollapsedMarginBefore() const final {
return MaxPositiveMarginBefore() - MaxNegativeMarginBefore();
}
LayoutUnit CollapsedMarginAfter() const final {
return MaxPositiveMarginAfter() - MaxNegativeMarginAfter();
}
LayoutUnit AdjustedMarginBeforeForPagination(
const LayoutBox&,
LayoutUnit logical_top_margin_edge,
LayoutUnit logical_top_border_edge,
const BlockChildrenLayoutInfo&) const;
LayoutUnit CollapseMargins(LayoutBox& child,
BlockChildrenLayoutInfo&,
bool child_is_self_collapsing,
bool child_discard_margin_before,
bool child_discard_margin_after);
LayoutUnit ClearFloatsIfNeeded(LayoutBox& child,
MarginInfo&,
LayoutUnit old_top_pos_margin,
LayoutUnit old_top_neg_margin,
LayoutUnit y_pos,
bool child_is_self_collapsing,
bool child_discard_margin);
LayoutUnit EstimateLogicalTopPosition(
LayoutBox& child,
const BlockChildrenLayoutInfo&,
LayoutUnit& estimate_without_pagination);
void MarginBeforeEstimateForChild(LayoutBox&,
LayoutUnit&,
LayoutUnit&,
bool&) const;
void HandleAfterSideOfBlock(LayoutBox* last_child,
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 logical_offset, EBreakBetween);
void SetBreakBefore(EBreakBetween);
void SetBreakAfter(EBreakBetween);
EBreakBetween BreakBefore() const override;
EBreakBetween BreakAfter() const override;
LayoutUnit AdjustBlockChildForPagination(LayoutUnit logical_top,
LayoutBox& child,
BlockChildrenLayoutInfo&,
bool at_before_side_of_block);
// If a float cannot fit in the current fragmentainer, return the logical top
// margin edge that the float needs to have in order to be pushed to the top
// of the next fragmentainer. Otherwise, just return |logicalTopMarginEdge|.
LayoutUnit AdjustFloatLogicalTopForPagination(
LayoutBox&,
LayoutUnit logical_top_margin_edge);
// 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& delta_offset);
// 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 logical_offset) const;
// Used to store state between styleWillChange and styleDidChange
static bool can_propagate_float_into_sibling_;
LineBoxList line_boxes_; // 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> rare_data_;
std::unique_ptr<FloatingObjects> floating_objects_;
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* child_box);
void SetMarginsForRubyRun(BidiRun*,
LayoutRubyRun*,
LayoutObject*,
const LineInfo&);
void ComputeInlineDirectionPositionsForLine(RootInlineBox*,
const LineInfo&,
BidiRun* first_run,
BidiRun* trailing_space_run,
bool reached_end,
GlyphOverflowAndFallbackFontsMap&,
VerticalPositionCache&,
const WordMeasurements&);
BidiRun* ComputeInlineDirectionPositionsForSegment(
RootInlineBox*,
const LineInfo&,
LayoutUnit& logical_left,
LayoutUnit& available_logical_width,
BidiRun* first_run,
BidiRun* trailing_space_run,
GlyphOverflowAndFallbackFontsMap& text_box_data_map,
VerticalPositionCache&,
const WordMeasurements&);
void ComputeBlockDirectionPositionsForLine(RootInlineBox*,
BidiRun*,
GlyphOverflowAndFallbackFontsMap&,
VerticalPositionCache&);
void AppendFloatingObjectToLastLine(FloatingObject&);
void AppendFloatsToLastLine(LineLayoutState&,
const InlineIterator& clean_line_start,
const InlineBidiResolver&,
const BidiStatus& clean_line_bidi_status);
// Helper function for layoutInlineChildren()
RootInlineBox* CreateLineBoxesFromBidiRuns(unsigned bidi_level,
BidiRunList<BidiRun>&,
const InlineIterator& end,
LineInfo&,
VerticalPositionCache&,
BidiRun* trailing_space_run,
const WordMeasurements&);
void LayoutRunsAndFloats(LineLayoutState&);
const InlineIterator& RestartLayoutRunsAndFloatsInRange(
LayoutUnit old_logical_height,
LayoutUnit new_logical_height,
FloatingObject* last_float_from_previous_line,
InlineBidiResolver&,
const InlineIterator&);
void LayoutRunsAndFloatsInRange(LineLayoutState&,
InlineBidiResolver&,
const InlineIterator& clean_line_start,
const BidiStatus& clean_line_bidi_status);
void LinkToEndLineIfNeeded(LineLayoutState&);
void MarkDirtyFloatsForPaintInvalidation(Vector<FloatWithRect>& floats);
RootInlineBox* DetermineStartPosition(LineLayoutState&, InlineBidiResolver&);
void DetermineEndPosition(LineLayoutState&,
RootInlineBox* start_box,
InlineIterator& clean_line_start,
BidiStatus& clean_line_bidi_status);
bool LineBoxHasBRWithClearance(RootInlineBox*);
bool CheckPaginationAndFloatsAtEndLine(LineLayoutState&);
bool MatchedEndLine(LineLayoutState&,
const InlineBidiResolver&,
const InlineIterator& end_line_start,
const BidiStatus& end_line_status);
void DeleteEllipsisLineBoxes();
void CheckLinesForTextOverflow();
void TryPlacingEllipsisOnAtomicInlines(RootInlineBox*,
LayoutUnit block_right_edge,
LayoutUnit block_left_edge,
LayoutUnit width,
const AtomicString&,
InlineBox*);
void ClearTruncationOnAtomicInlines(RootInlineBox*);
void MarkLinesDirtyInBlockRange(LayoutUnit logical_top,
LayoutUnit logical_bottom,
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 // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_BLOCK_FLOW_H_