blob: 26855745701fe877aafea79e4a32c93a06a10dab [file] [log] [blame]
* 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.
#include "third_party/blink/renderer/core/layout/layout_block_flow.h"
#include <algorithm>
#include <memory>
#include <utility>
#include "third_party/blink/renderer/core/editing/editing_behavior.h"
#include "third_party/blink/renderer/core/editing/editor.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/use_counter.h"
#include "third_party/blink/renderer/core/html/html_dialog_element.h"
#include "third_party/blink/renderer/core/layout/hit_test_location.h"
#include "third_party/blink/renderer/core/layout/layout_analyzer.h"
#include "third_party/blink/renderer/core/layout/layout_flow_thread.h"
#include "third_party/blink/renderer/core/layout/layout_inline.h"
#include "third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h"
#include "third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h"
#include "third_party/blink/renderer/core/layout/layout_object_factory.h"
#include "third_party/blink/renderer/core/layout/layout_paged_flow_thread.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/layout/line/glyph_overflow.h"
#include "third_party/blink/renderer/core/layout/line/inline_iterator.h"
#include "third_party/blink/renderer/core/layout/line/inline_text_box.h"
#include "third_party/blink/renderer/core/layout/line/line_width.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_line_height_metrics.h"
#include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h"
#include "third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.h"
#include "third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h"
#include "third_party/blink/renderer/core/layout/shapes/shape_outside_info.h"
#include "third_party/blink/renderer/core/layout/text_autosizer.h"
#include "third_party/blink/renderer/core/paint/block_flow_paint_invalidator.h"
#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
namespace blink {
bool LayoutBlockFlow::can_propagate_float_into_sibling_ = false;
struct SameSizeAsLayoutBlockFlow : public LayoutBlock {
LineBoxList line_boxes;
void* pointers[2];
static_assert(sizeof(LayoutBlockFlow) == sizeof(SameSizeAsLayoutBlockFlow),
"LayoutBlockFlow should stay small");
struct SameSizeAsMarginInfo {
uint16_t bitfields;
LayoutUnit margins[2];
static_assert(sizeof(LayoutBlockFlow::MarginValues) == sizeof(LayoutUnit[4]),
"MarginValues should stay small");
typedef HashMap<LayoutBlockFlow*, int> LayoutPassCountMap;
static LayoutPassCountMap& GetLayoutPassCountMap() {
DEFINE_STATIC_LOCAL(LayoutPassCountMap, map, ());
return map;
// Caches all our current margin collapsing state.
class MarginInfo {
// Collapsing flags for whether we can collapse our margins with our
// children's margins.
bool can_collapse_with_children_ : 1;
bool can_collapse_margin_before_with_children_ : 1;
bool can_collapse_margin_after_with_children_ : 1;
bool can_collapse_margin_after_with_last_child_ : 1;
// Whether or not we are a quirky container, i.e., do we collapse away top and
// bottom margins in our container. Table cells and the body are the common
// examples. We also have a custom style property for Safari RSS to deal with
// TypePad blog articles.
bool quirk_container_ : 1;
// This flag tracks whether we are still looking at child margins that can all
// collapse together at the beginning of a block. They may or may not collapse
// with the top margin of the block (|m_canCollapseTopWithChildren| tells us
// that), but they will always be collapsing with one another. This variable
// can remain set to true through multiple iterations as long as we keep
// encountering self-collapsing blocks.
bool at_before_side_of_block_ : 1;
// This flag is set when we know we're examining bottom margins and we know
// we're at the bottom of the block.
bool at_after_side_of_block_ : 1;
// These variables are used to detect quirky margins that we need to collapse
// away (in table cells
// and in the body element).
bool has_margin_before_quirk_ : 1;
bool has_margin_after_quirk_ : 1;
bool determined_margin_before_quirk_ : 1;
bool discard_margin_ : 1;
bool last_child_is_self_collapsing_block_with_clearance_ : 1;
// These flags track the previous maximal positive and negative margins.
LayoutUnit positive_margin_;
LayoutUnit negative_margin_;
LayoutUnit before_border_padding,
LayoutUnit after_border_padding);
void SetAtBeforeSideOfBlock(bool b) { at_before_side_of_block_ = b; }
void SetAtAfterSideOfBlock(bool b) { at_after_side_of_block_ = b; }
void ClearMargin() {
positive_margin_ = LayoutUnit();
negative_margin_ = LayoutUnit();
void SetHasMarginBeforeQuirk(bool b) { has_margin_before_quirk_ = b; }
void SetHasMarginAfterQuirk(bool b) { has_margin_after_quirk_ = b; }
void SetDeterminedMarginBeforeQuirk(bool b) {
determined_margin_before_quirk_ = b;
void SetPositiveMargin(LayoutUnit p) {
positive_margin_ = p;
void SetNegativeMargin(LayoutUnit n) {
negative_margin_ = n;
void SetPositiveMarginIfLarger(LayoutUnit p) {
if (p > positive_margin_)
positive_margin_ = p;
void SetNegativeMarginIfLarger(LayoutUnit n) {
if (n > negative_margin_)
negative_margin_ = n;
void SetMargin(LayoutUnit p, LayoutUnit n) {
positive_margin_ = p;
negative_margin_ = n;
void SetCanCollapseMarginAfterWithChildren(bool collapse) {
can_collapse_margin_after_with_children_ = collapse;
void SetCanCollapseMarginAfterWithLastChild(bool collapse) {
can_collapse_margin_after_with_last_child_ = collapse;
void SetDiscardMargin(bool value) { discard_margin_ = value; }
bool AtBeforeSideOfBlock() const { return at_before_side_of_block_; }
bool CanCollapseWithMarginBefore() const {
return at_before_side_of_block_ &&
bool CanCollapseWithMarginAfter() const {
return at_after_side_of_block_ && can_collapse_margin_after_with_children_;
bool CanCollapseMarginBeforeWithChildren() const {
return can_collapse_margin_before_with_children_;
bool CanCollapseMarginAfterWithChildren() const {
return can_collapse_margin_after_with_children_;
bool CanCollapseMarginAfterWithLastChild() const {
return can_collapse_margin_after_with_last_child_;
bool QuirkContainer() const { return quirk_container_; }
bool DeterminedMarginBeforeQuirk() const {
return determined_margin_before_quirk_;
bool HasMarginBeforeQuirk() const { return has_margin_before_quirk_; }
bool HasMarginAfterQuirk() const { return has_margin_after_quirk_; }
LayoutUnit PositiveMargin() const { return positive_margin_; }
LayoutUnit NegativeMargin() const { return negative_margin_; }
bool DiscardMargin() const { return discard_margin_; }
LayoutUnit Margin() const { return positive_margin_ - negative_margin_; }
void SetLastChildIsSelfCollapsingBlockWithClearance(bool value) {
last_child_is_self_collapsing_block_with_clearance_ = value;
bool LastChildIsSelfCollapsingBlockWithClearance() const {
return last_child_is_self_collapsing_block_with_clearance_;
// Some features, such as floats, margin collapsing and fragmentation, require
// some knowledge about things that happened when laying out previous block
// child siblings. Only looking at the object currently being laid out isn't
// always enough.
class BlockChildrenLayoutInfo {
BlockChildrenLayoutInfo(LayoutBlockFlow* block_flow,
LayoutUnit before_edge,
LayoutUnit after_edge)
: margin_info_(block_flow, before_edge, after_edge),
is_at_first_in_flow_child_(true) {}
// Store multicol layout state before first layout of a block child. The child
// may contain a column spanner. If we need to re-lay out the block child
// because our initial logical top estimate was wrong, we need to roll back to
// how things were before laying out the child.
void StoreMultiColumnLayoutState(const LayoutFlowThread& flow_thread) {
multi_column_layout_state_ = flow_thread.GetMultiColumnLayoutState();
void RollBackToInitialMultiColumnLayoutState(LayoutFlowThread& flow_thread) {
const MarginInfo& GetMarginInfo() const { return margin_info_; }
MarginInfo& GetMarginInfo() { return margin_info_; }
LayoutUnit& PreviousFloatLogicalBottom() {
return previous_float_logical_bottom_;
EBreakBetween PreviousBreakAfterValue() const {
return previous_break_after_value_;
void SetPreviousBreakAfterValue(EBreakBetween value) {
previous_break_after_value_ = value;
bool IsAtFirstInFlowChild() const { return is_at_first_in_flow_child_; }
void ClearIsAtFirstInFlowChild() { is_at_first_in_flow_child_ = false; }
MultiColumnLayoutState multi_column_layout_state_;
MarginInfo margin_info_;
LayoutUnit previous_float_logical_bottom_;
EBreakBetween previous_break_after_value_;
bool is_at_first_in_flow_child_;
LayoutBlockFlow::LayoutBlockFlow(ContainerNode* node) : LayoutBlock(node) {
static_assert(sizeof(MarginInfo) == sizeof(SameSizeAsMarginInfo),
"MarginInfo should stay small");
LayoutBlockFlow::~LayoutBlockFlow() {
LayoutBlockFlow::~LayoutBlockFlow() = default;
LayoutBlockFlow* LayoutBlockFlow::CreateAnonymous(
Document* document,
scoped_refptr<ComputedStyle> style) {
LayoutBlockFlow* layout_block_flow =
LayoutObjectFactory::CreateBlockFlow(*document, *style);
return layout_block_flow;
LayoutObject* LayoutBlockFlow::LayoutSpecialExcludedChild(
bool relayout_children,
SubtreeLayoutScope& layout_scope) {
LayoutMultiColumnFlowThread* flow_thread = MultiColumnFlowThread();
if (!flow_thread)
return nullptr;
SetLogicalTopForChild(*flow_thread, BorderBefore() + PaddingBefore());
return flow_thread;
bool LayoutBlockFlow::UpdateLogicalWidthAndColumnWidth() {
bool relayout_children = LayoutBlock::UpdateLogicalWidthAndColumnWidth();
if (LayoutMultiColumnFlowThread* flow_thread = MultiColumnFlowThread()) {
if (flow_thread->NeedsNewWidth())
return true;
return relayout_children;
void LayoutBlockFlow::SetBreakAtLineToAvoidWidow(int line_to_break) {
DCHECK_GE(line_to_break, 0);
rare_data_->line_break_to_avoid_widow_ = line_to_break;
void LayoutBlockFlow::SetDidBreakAtLineToAvoidWidow() {
// This function should be called only after a break was applied to avoid
// widows so assert |m_rareData| exists.
rare_data_->did_break_at_line_to_avoid_widow_ = true;
void LayoutBlockFlow::ClearDidBreakAtLineToAvoidWidow() {
if (!rare_data_)
rare_data_->did_break_at_line_to_avoid_widow_ = false;
void LayoutBlockFlow::ClearShouldBreakAtLineToAvoidWidow() const {
if (!rare_data_)
rare_data_->line_break_to_avoid_widow_ = -1;
bool LayoutBlockFlow::IsSelfCollapsingBlock() const {
if (NeedsLayout()) {
// Sometimes we don't lay out objects in DOM order (column spanners being
// one such relevant type of object right here). As long as the object in
// question establishes a new formatting context, that's nothing to worry
// about, though.
return false;
DCHECK_EQ(!is_self_collapsing_, !CheckIfIsSelfCollapsingBlock());
return is_self_collapsing_;
bool LayoutBlockFlow::CheckIfIsSelfCollapsingBlock() const {
// We are not self-collapsing if we
// (a) have a non-zero height according to layout (an optimization to avoid
// wasting time)
// (b) have border/padding,
// (c) have a min-height
// (d) have specified that one of our margins can't collapse using a CSS
// extension
// (e) establish a new block formatting context.
// The early exit must be done before we check for clean layout.
// We should be able to give a quick answer if the box is a relayout boundary.
// Being a relayout boundary implies a block formatting context, and also
// our internal layout shouldn't affect our container in any way.
if (CreatesNewFormattingContext())
return false;
// Placeholder elements are not laid out until the dimensions of their parent
// text control are known, so they don't get layout until their parent has had
// layout - this is unique in the layout tree and means when we call
// isSelfCollapsingBlock on them we find that they still need layout.
DCHECK(!NeedsLayout() || (GetNode() && GetNode()->IsElementNode() &&
ToElement(GetNode())->ShadowPseudoId() ==
if (LogicalHeight() > LayoutUnit() || BorderAndPaddingLogicalHeight() ||
StyleRef().LogicalMinHeight().IsPositive() ||
StyleRef().MarginBeforeCollapse() == EMarginCollapse::kSeparate ||
StyleRef().MarginAfterCollapse() == EMarginCollapse::kSeparate)
return false;
Length logical_height_length = StyleRef().LogicalHeight();
bool has_auto_height = logical_height_length.IsAuto();
if (logical_height_length.IsPercentOrCalc() &&
!GetDocument().InQuirksMode()) {
has_auto_height = true;
for (LayoutBlock* cb = ContainingBlock(); !cb->IsLayoutView();
cb = cb->ContainingBlock()) {
if (cb->StyleRef().LogicalHeight().IsFixed() || cb->IsTableCell())
has_auto_height = false;
// If the height is 0 or auto, then whether or not we are a self-collapsing
// block depends on whether we have content that is all self-collapsing.
// TODO(alancutter): Make this work correctly for calc lengths.
if (has_auto_height || ((logical_height_length.IsFixed() ||
logical_height_length.IsPercentOrCalc()) &&
logical_height_length.IsZero())) {
// Marker_container should be a self-collapsing block. Marker_container is a
// zero height anonymous block and marker is its only child.
if (logical_height_length.IsFixed() && logical_height_length.IsZero() &&
IsAnonymous() && Parent() && Parent()->IsListItem()) {
LayoutObject* first_child = FirstChild();
if (first_child && first_child->IsListMarker() &&
return true;
// If the block has inline children, see if we generated any line boxes.
// If we have any line boxes, then we can't be self-collapsing, since we
// have content.
if (ChildrenInline())
return !FirstLineBox();
// Whether or not we collapse is dependent on whether all our normal flow
// children are also self-collapsing.
for (LayoutBox* child = FirstChildBox(); child;
child = child->NextSiblingBox()) {
if (child->IsFloatingOrOutOfFlowPositioned() || child->IsColumnSpanAll())
if (!child->IsSelfCollapsingBlock())
return false;
return true;
return false;
void LayoutBlockFlow::UpdateBlockLayout(bool relayout_children) {
DCHECK(IsInlineBlockOrInlineTable() || !IsInline());
if (RuntimeEnabledFeatures::TrackLayoutPassesPerBlockEnabled())
if (!relayout_children && SimplifiedLayout())
LayoutAnalyzer::BlockScope analyzer(*this);
SubtreeLayoutScope layout_scope(*this);
LayoutUnit previous_height = LogicalHeight();
LayoutUnit old_left = LogicalLeft();
bool logical_width_changed = UpdateLogicalWidthAndColumnWidth();
relayout_children |= logical_width_changed;
TextAutosizer::LayoutScope text_autosizer_layout_scope(this, &layout_scope);
bool pagination_state_changed = pagination_state_changed_;
bool preferred_logical_widths_were_dirty = PreferredLogicalWidthsDirty();
// Multiple passes might be required for column based layout.
// The number of passes could be as high as the number of columns.
LayoutMultiColumnFlowThread* flow_thread = MultiColumnFlowThread();
do {
LayoutState state(*this, logical_width_changed);
if (pagination_state_changed_) {
// We now need a deep layout to clean up struts after pagination, if we
// just ceased to be paginated, or, if we just became paginated on the
// other hand, we now need the deep layout, to insert pagination struts.
pagination_state_changed_ = false;
LayoutChildren(relayout_children, layout_scope);
if (!preferred_logical_widths_were_dirty && PreferredLogicalWidthsDirty()) {
// The only thing that should dirty preferred widths at this point is the
// addition of overflow:auto scrollbars in a descendant. To avoid a
// potential infinite loop, run layout again with auto scrollbars frozen
// in their current state.
PaintLayerScrollableArea::FreezeScrollbarsScope freeze_scrollbars;
relayout_children |= UpdateLogicalWidthAndColumnWidth();
LayoutChildren(relayout_children, layout_scope);
if (flow_thread && !flow_thread->FinishLayout()) {
if (ShouldBreakAtLineToAvoidWidow()) {
} while (true);
LayoutState state(*this, logical_width_changed);
if (pagination_state_changed) {
// We still haven't laid out positioned descendants, and we need to perform
// a deep layout on those too if pagination state changed.
// Remember the automatic logical height we got from laying out the children.
LayoutUnit unconstrained_height = LogicalHeight();
LayoutUnit unconstrained_client_after_edge = ClientLogicalBottom();
// Adjust logical height to satisfy whatever computed style requires.
if (!ChildrenInline())
if (LogicalHeight() != previous_height || IsDocumentElement())
relayout_children = true;
PositionedLayoutBehavior behavior = kDefaultLayout;
if (old_left != LogicalLeft())
behavior = kForcedLayoutAfterContainingBlockMoved;
LayoutPositionedObjects(relayout_children, behavior);
// Add overflow from children.
descendants_with_floats_marked_for_layout_ = false;
if (IsHTMLDialogElement(GetNode()) && IsOutOfFlowPositioned())
void LayoutBlockFlow::ResetLayout() {
if (!FirstChild() && !IsAnonymousBlock())
// Text truncation kicks in if overflow isn't visible and text-overflow isn't
// 'clip'. If this is an anonymous block, we have to examine the parent.
// FIXME: CSS3 says that descendants that are clipped must also know how to
// truncate. This is insanely difficult to figure out in general (especially
// in the middle of doing layout), so we only handle the simple case of an
// anonymous block truncating when its parent is clipped.
// Walk all the lines and delete our ellipsis line boxes if they exist.
if (ChildrenInline() && ShouldTruncateOverflowingText())
// We use four values, maxTopPos, maxTopNeg, maxBottomPos, and maxBottomNeg,
// to track our current maximal positive and negative margins. These values
// are used when we are collapsed with adjacent blocks, so for example, if you
// have block A and B collapsing together, then you'd take the maximal
// positive margin from both A and B and subtract it from the maximal negative
// margin from both A and B to get the true collapsed margin. This algorithm
// is recursive, so when we finish layout() our block knows its current
// maximal positive/negative values.
// Start out by setting our margin values to our current margins. Table cells
// have no margins, so we don't fill in the values for table cells.
if (!IsTableCell()) {
if (View()->GetLayoutState()->IsPaginated()) {
// Start with any applicable computed break-after and break-before values
// for this object. During child layout, breakBefore will be joined with the
// breakBefore value of the first in-flow child, and breakAfter will be
// joined with the breakAfter value of the last in-flow child. This is done
// in order to honor the requirement that a class A break point [1] may only
// exists *between* in-flow siblings (i.e. not before the first child and
// not after the last child).
// [1]
void LayoutBlockFlow::LayoutChildren(bool relayout_children,
SubtreeLayoutScope& layout_scope) {
LayoutUnit before_edge = BorderBefore() + PaddingBefore();
LayoutUnit after_edge = BorderAfter() + PaddingAfter();
if (HasFlippedBlocksWritingMode())
before_edge += ScrollbarLogicalHeight();
after_edge += ScrollbarLogicalHeight();
if (ChildrenInline())
LayoutInlineChildren(relayout_children, after_edge);
LayoutBlockChildren(relayout_children, layout_scope, before_edge,
// Expand our intrinsic height to encompass floats.
if (LowestFloatLogicalBottom() > (LogicalHeight() - after_edge) &&
SetLogicalHeight(LowestFloatLogicalBottom() + after_edge);
void LayoutBlockFlow::AddOverhangingFloatsFromChildren(
LayoutUnit unconstrained_height) {
LayoutBlockFlow* lowest_block = nullptr;
bool added_overhanging_floats = false;
// One of our children's floats may have become an overhanging float for us.
for (LayoutObject* child = LastChild(); child;
child = child->PreviousSibling()) {
// TODO(robhogan): We should exclude blocks that create formatting
// contexts, not just out of flow or floating blocks.
if (child->IsLayoutBlockFlow() &&
!child->IsFloatingOrOutOfFlowPositioned()) {
LayoutBlockFlow* block = ToLayoutBlockFlow(child);
if (!block->ContainsFloats())
lowest_block = block;
if (unconstrained_height <= LogicalHeight())
LayoutUnit logical_bottom =
block->LogicalTop() + block->LowestFloatLogicalBottom();
if (logical_bottom <= LogicalHeight())
AddOverhangingFloats(block, false);
added_overhanging_floats = true;
// If we have no overhanging floats we still pass a record of the lowest
// non-overhanging float up the tree so we can enclose it if we are a
// formatting context and allow siblings to avoid it if they have negative
// margin and find themselves in its vicinity.
if (!added_overhanging_floats)
void LayoutBlockFlow::AddLowestFloatFromChildren(LayoutBlockFlow* block) {
// TODO(robhogan): Make createsNewFormattingContext an ASSERT.
if (!block || !block->ContainsFloats() ||
FloatingObject* floating_object =
if (!floating_object || ContainsFloat(floating_object->GetLayoutObject()))
LayoutSize offset(-block->LogicalLeft(), -block->LogicalTop());
if (!IsHorizontalWritingMode())
offset = offset.TransposedSize();
if (!floating_objects_)
FloatingObject* new_floating_object = floating_objects_->Add(
floating_object->CopyToNewContainer(offset, false, true));
void LayoutBlockFlow::DetermineLogicalLeftPositionForChild(LayoutBox& child) {
LayoutUnit start_position = BorderStart() + PaddingStart();
LayoutUnit initial_start_position = start_position;
start_position -= LogicalLeftScrollbarWidth();
LayoutUnit total_available_logical_width =
BorderAndPaddingLogicalWidth() + AvailableLogicalWidth();
LayoutUnit child_margin_start = MarginStartForChild(child);
LayoutUnit new_position = start_position + child_margin_start;
if (child.AvoidsFloats() && ContainsFloats()) {
LayoutUnit position_to_avoid_floats = StartOffsetForAvoidingFloats(
LogicalTopForChild(child), LogicalHeightForChild(child));
// If the child has an offset from the content edge to avoid floats then use
// that, otherwise let any negative margin pull it back over the content
// edge or any positive margin push it out.
// If the child is being centred then the margin calculated to do that has
// factored in any offset required to avoid floats, so use it if necessary.
if (StyleRef().GetTextAlign() == ETextAlign::kWebkitCenter ||
new_position =
std::max(new_position, position_to_avoid_floats + child_margin_start);
else if (position_to_avoid_floats > initial_start_position)
new_position = std::max(new_position, position_to_avoid_floats);
SetLogicalLeftForChild(child, StyleRef().IsLeftToRightDirection()
? new_position
: total_available_logical_width -
new_position -
void LayoutBlockFlow::SetLogicalLeftForChild(LayoutBox& child,
LayoutUnit logical_left) {
LayoutPoint new_location(child.Location());
if (IsHorizontalWritingMode()) {
} else {
void LayoutBlockFlow::SetLogicalTopForChild(LayoutBox& child,
LayoutUnit logical_top) {
if (IsHorizontalWritingMode()) {
} else {
void LayoutBlockFlow::MarkDescendantsWithFloatsForLayoutIfNeeded(
LayoutBlockFlow& child,
LayoutUnit new_logical_top,
LayoutUnit previous_float_logical_bottom) {
// TODO(mstensho): rework the code to return early when there is no need for
// marking, instead of this |markDescendantsWithFloats| flag.
bool mark_descendants_with_floats = false;
if (new_logical_top != child.LogicalTop() && !child.AvoidsFloats() &&
child.ContainsFloats()) {
mark_descendants_with_floats = true;
} else if (UNLIKELY(new_logical_top.MightBeSaturated())) {
// The logical top might be saturated for very large elements. Comparing
// with the old logical top might then yield a false negative, as adding and
// removing margins, borders etc. from a saturated number might yield
// incorrect results. If this is the case, always mark for layout.
mark_descendants_with_floats = true;
} else if (!child.AvoidsFloats() || child.ShrinkToAvoidFloats()) {
// If an element might be affected by the presence of floats, then always
// mark it for layout.
LayoutUnit lowest_float =
std::max(previous_float_logical_bottom, LowestFloatLogicalBottom());
if (lowest_float > new_logical_top)
mark_descendants_with_floats = true;
if (mark_descendants_with_floats)
bool LayoutBlockFlow::PositionAndLayoutOnceIfNeeded(
LayoutBox& child,
LayoutUnit new_logical_top,
BlockChildrenLayoutInfo& layout_info) {
if (LayoutFlowThread* flow_thread = FlowThreadContainingBlock())
if (child.IsLayoutBlockFlow()) {
LayoutUnit& previous_float_logical_bottom =
LayoutBlockFlow& child_block_flow = ToLayoutBlockFlow(child);
if (child_block_flow.ContainsFloats() || ContainsFloats())
child_block_flow, new_logical_top, previous_float_logical_bottom);
// TODO(mstensho): A writing mode root is one thing, but we should be able
// to skip anything that establishes a new block formatting context here.
// Their floats don't affect us.
if (!child_block_flow.IsWritingModeRoot())
previous_float_logical_bottom =
child_block_flow.LogicalTop() +
LayoutUnit old_logical_top = LogicalTopForChild(child);
SetLogicalTopForChild(child, new_logical_top);
SubtreeLayoutScope layout_scope(child);
if (!child.NeedsLayout()) {
if (new_logical_top != old_logical_top && child.ShrinkToAvoidFloats()) {
// The child's width is affected by adjacent floats. When the child shifts
// to clear an item, its width can change (because it has more available
// width).
} else {
MarkChildForPaginationRelayoutIfNeeded(child, layout_scope);
bool needed_layout = child.NeedsLayout();
if (needed_layout)
if (View()->GetLayoutState()->IsPaginated())
return needed_layout;
void LayoutBlockFlow::InsertForcedBreakBeforeChildIfNeeded(
LayoutBox& child,
BlockChildrenLayoutInfo& layout_info) {
if (layout_info.IsAtFirstInFlowChild()) {
// There's no class A break point before the first child (only *between*
// siblings), so steal its break value and join it with what we already have
// here.
JoinFragmentainerBreakValues(BreakBefore(), child.BreakBefore()));
// Figure out if a forced break should be inserted in front of the child. If
// we insert a forced break, the margins on this child may not collapse with
// those preceding the break.
EBreakBetween class_a_break_point_value =
if (IsForcedFragmentainerBreakValue(class_a_break_point_value)) {
LayoutUnit old_logical_top = LogicalHeight();
LayoutUnit new_logical_top =
ApplyForcedBreak(old_logical_top, class_a_break_point_value);
LayoutUnit pagination_strut = new_logical_top - old_logical_top;
void LayoutBlockFlow::LayoutBlockChild(LayoutBox& child,
BlockChildrenLayoutInfo& layout_info) {
MarginInfo& margin_info = layout_info.GetMarginInfo();
LayoutBlockFlow* child_layout_block_flow =
child.IsLayoutBlockFlow() ? ToLayoutBlockFlow(&child) : nullptr;
LayoutUnit old_pos_margin_before = MaxPositiveMarginBefore();
LayoutUnit old_neg_margin_before = MaxNegativeMarginBefore();
// The child is a normal flow object. Compute the margins we will use for
// collapsing now.
// Try to guess our correct logical top position. In most cases this guess
// will be correct. Only if we're wrong (when we compute the real logical top
// position) will we have to potentially relayout.
LayoutUnit estimate_without_pagination;
LayoutUnit logical_top_estimate = EstimateLogicalTopPosition(
child, layout_info, estimate_without_pagination);
LayoutRect old_rect = child.FrameRect();
if (LayoutFlowThread* flow_thread = FlowThreadContainingBlock())
// Use the estimated block position and lay out the child if needed. After
// child layout, when we have enough information to perform proper margin
// collapsing, float clearing and pagination, we may have to reposition and
// lay out again if the estimate was wrong.
bool child_needed_layout =
PositionAndLayoutOnceIfNeeded(child, logical_top_estimate, layout_info);
// Cache if we are at the top of the block right now.
bool at_before_side_of_block = margin_info.AtBeforeSideOfBlock();
bool child_is_self_collapsing = child.IsSelfCollapsingBlock();
bool child_discard_margin_before = MustDiscardMarginBeforeForChild(child);
bool child_discard_margin_after = MustDiscardMarginAfterForChild(child);
bool paginated = View()->GetLayoutState()->IsPaginated();
// If there should be a forced break before the child, we need to insert it
// before attempting to collapse margins or apply clearance.
if (paginated) {
// We will now insert the strut needed by any forced break. After this
// operation, we will have calculated the offset where we can apply margin
// collapsing and clearance. After having applied those things, we'll be at
// the position where we can honor requirements of unbreakable content,
// which may extend the strut further.
InsertForcedBreakBeforeChildIfNeeded(child, layout_info);
// Now determine the correct ypos based off examination of collapsing margin
// values.
LayoutUnit logical_top_before_clear =
CollapseMargins(child, layout_info, child_is_self_collapsing,
child_discard_margin_before, child_discard_margin_after);
// Now check for clear.
bool child_discard_margin =
child_discard_margin_before || child_discard_margin_after;
LayoutUnit new_logical_top = ClearFloatsIfNeeded(
child, margin_info, old_pos_margin_before, old_neg_margin_before,
logical_top_before_clear, child_is_self_collapsing, child_discard_margin);
// If there's a forced break in front of this child, its final position has
// already been determined. Otherwise, see if there are other reasons for
// breaking before it (break-inside:avoid, or not enough space for the first
// piece of child content to fit in the current fragmentainer), and adjust the
// position accordingly.
if (paginated) {
if (estimate_without_pagination != new_logical_top) {
// We got a new position due to clearance or margin collapsing. Before we
// attempt to paginate (which may result in the position changing again),
// let's try again at the new position (since a new position may result in
// a new logical height).
PositionAndLayoutOnceIfNeeded(child, new_logical_top, layout_info);
// We have now applied forced breaks, margin collapsing and clearance, and
// we're at the position where we can honor requirements of unbreakable
// content.
new_logical_top = AdjustBlockChildForPagination(
new_logical_top, child, layout_info,
at_before_side_of_block && logical_top_before_clear == new_logical_top);
// Clearance, margin collapsing or pagination may have given us a new logical
// top, in which case we may have to reposition and possibly relayout as well.
// If we determined during child layout that we need to insert a break to
// honor widows, we also need to relayout.
if (new_logical_top != logical_top_estimate || child.NeedsLayout() ||
(paginated && child_layout_block_flow &&
child_layout_block_flow->ShouldBreakAtLineToAvoidWidow())) {
PositionAndLayoutOnceIfNeeded(child, new_logical_top, layout_info);
// If we previously encountered a self-collapsing sibling of this child that
// had clearance then we set this bit to ensure we would not collapse the
// child's margins, and those of any subsequent self-collapsing siblings, with
// our parent. If this child is not self-collapsing then it can collapse its
// margins with the parent so reset the bit.
if (!margin_info.CanCollapseMarginAfterWithLastChild() &&
// We are no longer at the top of the block if we encounter a non-empty child.
// This has to be done after checking for clear, so that margins can be reset
// if a clear occurred.
if (margin_info.AtBeforeSideOfBlock() && !child_is_self_collapsing)
// Now place the child in the correct left position
LayoutSize child_offset = child.Location() - old_rect.Location();
// Update our height now that the child has been placed in the correct
// position.
SetLogicalHeight(LogicalHeight() + LogicalHeightForChild(child));
if (MustSeparateMarginAfterForChild(child)) {
SetLogicalHeight(LogicalHeight() + MarginAfterForChild(child));
// If the child has overhanging floats that intrude into following siblings
// (or possibly out of this block), then the parent gets notified of the
// floats now.
if (child_layout_block_flow)
AddOverhangingFloats(child_layout_block_flow, !child_needed_layout);
// If the child moved, we have to invalidate its paint as well as any
// floating/positioned descendants. An exception is if we need a layout.
// In this case, we know we're going to invalidate our paint (and the child)
// anyway.
if (!SelfNeedsLayout() && (child_offset.Width() || child_offset.Height()) &&
if (paginated) {
// Keep track of the break-after value of the child, so that it can be
// joined with the break-before value of the next in-flow object at the next
// class A break point.
if (child_layout_block_flow) {
// If a forced break was inserted inside the child, translate and
// propagate the offset to this object.
if (LayoutUnit offset = child_layout_block_flow->FirstForcedBreakOffset())
SetFirstForcedBreakOffset(offset + new_logical_top);
if (child.IsLayoutMultiColumnSpannerPlaceholder()) {
// The actual column-span:all element is positioned by this placeholder
// child.
LayoutUnit LayoutBlockFlow::AdjustBlockChildForPagination(
LayoutUnit logical_top,
LayoutBox& child,
BlockChildrenLayoutInfo& layout_info,
bool at_before_side_of_block) {
LayoutBlockFlow* child_block_flow =
child.IsLayoutBlockFlow() ? ToLayoutBlockFlow(&child) : nullptr;
// See if we need a soft (unforced) break in front of this child, and set the
// pagination strut in that case. An unforced break may come from two sources:
// 1. The first piece of content inside the child doesn't fit in the current
// page or column
// 2. The child itself has breaking restrictions (break-inside:avoid, replaced
// content, etc.) and doesn't fully fit in the current page or column.
// No matter which source, if we need to insert a strut, it should always take
// us to the exact top of a page or column further ahead, or be zero.
// The first piece of content inside the child may have set a strut during
// layout. Currently, only block flows support strut propagation, but this may
// (and should) change in the future. See
LayoutUnit strut_from_content =
child_block_flow ? child_block_flow->PaginationStrutPropagatedFromChild()
: LayoutUnit();
LayoutUnit logical_top_with_content_strut = logical_top + strut_from_content;
LayoutUnit logical_top_after_unsplittable =
AdjustForUnsplittableChild(child, logical_top);
// Pick the largest offset. Tall unsplittable content may take us to a page or
// column further ahead than the next one.
LayoutUnit logical_top_after_pagination =
std::max(logical_top_with_content_strut, logical_top_after_unsplittable);
LayoutUnit new_logical_top = logical_top;
// Forced breaks may already have caused a strut, and this needs to be added
// together with any strut detected here in this method.
LayoutUnit previous_strut = child.PaginationStrut();
if (LayoutUnit pagination_strut =
logical_top_after_pagination - logical_top + previous_strut) {
DCHECK_GT(pagination_strut, 0);
// If we're not at the first in-flow child, there's a class A break point
// before the child. If we *are* at the first in-flow child, but the child
// isn't flush with the content edge of its container, due to e.g.
// clearance, there's a class C break point before the child. Otherwise we
// should propagate the strut to our parent block, and attempt to break
// there instead. See
bool can_break =
!layout_info.IsAtFirstInFlowChild() || !at_before_side_of_block;
if (!can_break && child.GetPaginationBreakability() == kForbidBreaks &&
!AllowsPaginationStrut()) {
// The child is monolithic content, e.g. an image. It is truly
// unsplittable. Breaking inside it would be bad. Since this block doesn't
// allow pagination struts to be propagated to it, we're left to handle it
// on our own right here. Break before the child, even if we're currently
// at the block start (i.e. there's no class A or C break point here).
can_break = true;
if (can_break) {
// |previousStrut| was already baked into the logical top, so don't add
// it again.
new_logical_top += pagination_strut - previous_strut;
} else {
// No valid break point here. Propagate the strut from the child to this
// block, but only if the block allows it. If the block doesn't allow it,
// we'll just ignore the strut and carry on, without breaking. This
// happens e.g. when a tall break-inside:avoid object with a top margin is
// the first in-flow child in the fragmentation context.
if (AllowsPaginationStrut()) {
pagination_strut += logical_top;
if (child_block_flow)
// Similar to how we apply clearance. Go ahead and boost height() to be the
// place where we're going to position the child.
SetLogicalHeight(LogicalHeight() + (new_logical_top - logical_top));
// Return the final adjusted logical top.
return new_logical_top;
LayoutUnit LayoutBlockFlow::AdjustFloatLogicalTopForPagination(
LayoutBox& child,
LayoutUnit logical_top_margin_edge) {
// The first piece of content inside the child may have set a strut during
// layout.
LayoutUnit strut;
if (child.IsLayoutBlockFlow())
strut = ToLayoutBlockFlow(child).PaginationStrutPropagatedFromChild();
LayoutUnit margin_before = MarginBeforeForChild(child);
if (margin_before > LayoutUnit()) {
// Avoid breaking inside the top margin of a float.
if (strut) {
// If we already had decided to break, just add the margin. The strut so
// far only accounts for pushing the top border edge to the next
// fragmentainer. We need to push the margin over as well, because
// there's no break opportunity between margin and border.
strut += margin_before;
} else {
// Even if we didn't break before the border box to the next
// fragmentainer, we need to check if we can fit the margin before it.
if (IsPageLogicalHeightKnown()) {
LayoutUnit remaining_space = PageRemainingLogicalHeightForOffset(
logical_top_margin_edge, kAssociateWithLatterPage);
if (remaining_space <= margin_before) {
strut += CalculatePaginationStrutToFitContent(logical_top_margin_edge,
if (!strut) {
// If we are unsplittable and don't fit, move to the next page or column
// if that helps the situation.
LayoutUnit new_logical_top_margin_edge =
AdjustForUnsplittableChild(child, logical_top_margin_edge);
strut = new_logical_top_margin_edge - logical_top_margin_edge;
return logical_top_margin_edge + strut;
static bool ShouldSetStrutOnBlock(const LayoutBlockFlow& block,
const RootInlineBox& line_box,
LayoutUnit line_logical_offset,
int line_index,
LayoutUnit page_logical_height) {
if (line_box == block.FirstRootBox()) {
// This is the first line in the block. We can take the whole block with us
// to the next page or column, rather than keeping a content-less portion of
// it in the previous one. Only do this if the line is flush with the
// content edge of the block, though. If it isn't, it means that the line
// was pushed downwards by preceding floats that didn't fit beside the line,
// and we don't want to move all that, since it has already been established
// that it fits nicely where it is. In this case we have a class "C" break
// point [1] in front of this line.
// [1]
if (line_logical_offset > block.BorderAndPaddingBefore())
return false;
LayoutUnit line_height =
line_box.LineBottomWithLeading() - line_box.LineTopWithLeading();
LayoutUnit total_logical_height =
line_height + line_logical_offset.ClampNegativeToZero();
// It's rather pointless to break before the block if the current line isn't
// going to fit in the same column or page, so check that as well.
if (total_logical_height > page_logical_height)
return false;
} else {
if (line_index > block.StyleRef().Orphans())
return false;
// Not enough orphans here. Push the entire block to the next column / page
// as an attempt to better satisfy the orphans requirement.
// Note that we should ideally check if the first line in the block is flush
// with the content edge of the block here, because if it isn't, we should
// break at the class "C" break point in front of the first line, rather
// than before the entire block.
return block.AllowsPaginationStrut();
void LayoutBlockFlow::AdjustLinePositionForPagination(RootInlineBox& line_box,
LayoutUnit& delta) {
// TODO(mstensho): Pay attention to line overflow. It should be painted in the
// same column as the rest of the line, possibly overflowing the column. We
// currently only allow overflow above the first column. We clip at all other
// column boundaries, and that's how it has to be for now. The paint we have
// to do when a column has overflow has to be special.
// We need to exclude content that paints in a previous column (and content
// that paints in the following column).
// FIXME: Another problem with simply moving lines is that the available line
// width may change (because of floats). Technically if the location we move
// the line to has a different line width than our old position, then we need
// to dirty the line and all following lines.
LayoutUnit logical_offset = line_box.LineTopWithLeading();
LayoutUnit line_height = line_box.LineBottomWithLeading() - logical_offset;
logical_offset += delta;
LayoutState* layout_state = View()->GetLayoutState();
if (!layout_state->IsPaginated())
if (!IsPageLogicalHeightKnown())
LayoutUnit page_logical_height = PageLogicalHeightForOffset(logical_offset);
LayoutUnit remaining_logical_height = PageRemainingLogicalHeightForOffset(
logical_offset, kAssociateWithLatterPage);
int line_index = LineCount(&line_box);
if (remaining_logical_height < line_height ||
(ShouldBreakAtLineToAvoidWidow() &&
LineBreakToAvoidWidow() == line_index)) {
LayoutUnit pagination_strut =
CalculatePaginationStrutToFitContent(logical_offset, line_height);
LayoutUnit new_logical_offset = logical_offset + pagination_strut;
// Moving to a different page or column may mean that its height is
// different.
page_logical_height = PageLogicalHeightForOffset(new_logical_offset);
if (line_height > page_logical_height) {
// Too tall to fit in one page / column. Give up. Don't push to the next
// page / column.
// TODO(mstensho): Get rid of this. This is just utter weirdness, but the
// other browsers also do something slightly similar, although in much
// more specific cases than we do here, and printing Google Docs depends
// on it.
PaginatedContentWasLaidOut(logical_offset + line_height);
// We need to insert a break now, either because there's no room for the
// line in the current column / page, or because we have determined that we
// need a break to satisfy widow requirements.
if (ShouldBreakAtLineToAvoidWidow() &&
LineBreakToAvoidWidow() == line_index) {
if (ShouldSetStrutOnBlock(*this, line_box, logical_offset, line_index,
page_logical_height)) {
// Note that when setting the strut on a block, it may be propagated to
// parent blocks later on, if a block's logical top is flush with that of
// its parent. We don't want content-less portions (struts) at the
// beginning of a block before a break, if it can be avoided. After all,
// that's the reason for setting struts on blocks and not lines in the
// first place.
SetPaginationStrutPropagatedFromChild(pagination_strut + logical_offset);
} else {
delta += pagination_strut;
PaginatedContentWasLaidOut(new_logical_offset + line_height);
LayoutUnit strut_to_propagate;
if (remaining_logical_height == page_logical_height) {
// We're at the very top of a page or column.
if (line_box != FirstRootBox())
// If this is the first line in the block, and the block has a top border or
// padding, we may want to set a strut on the block, so that everything ends
// up in the next column or page. Setting a strut on the block is also
// important when it comes to satisfying orphan requirements.
if (ShouldSetStrutOnBlock(*this, line_box, logical_offset, line_index,
page_logical_height)) {
strut_to_propagate =
logical_offset + layout_state->HeightOffsetForTableHeaders();
} else if (LayoutUnit pagination_strut =
layout_state->HeightOffsetForTableHeaders()) {
delta += pagination_strut;
} else if (line_box == FirstRootBox() && AllowsPaginationStrut()) {
// This is the first line in the block. The block may still start in the
// previous column or page, and if that's the case, attempt to pull it over
// to where this line is, so that we don't split the top border or padding.
LayoutUnit strut = remaining_logical_height + logical_offset +
layout_state->HeightOffsetForTableHeaders() -
if (strut > LayoutUnit()) {
// The block starts in a previous column or page. Set a strut on the block
// if there's room for the top border, padding and the line in one column
// or page.
if (logical_offset + line_height <= page_logical_height)
strut_to_propagate = strut;
// If we found that some preceding content (lines, border and padding) belongs
// together with this line, we should pull the entire block with us to the
// fragmentainer we're currently in. We need to avoid this when the block
// precedes the first fragmentainer, though. We shouldn't fragment content
// there, but rather let it appear in the overflow area before the first
// fragmentainer.
if (strut_to_propagate && OffsetFromLogicalTopOfFirstPage() > LayoutUnit())
PaginatedContentWasLaidOut(logical_offset + line_height);
LayoutUnit LayoutBlockFlow::AdjustForUnsplittableChild(
LayoutBox& child,
LayoutUnit logical_offset) const {
if (child.GetPaginationBreakability() == kAllowAnyBreaks)
return logical_offset;
LayoutUnit child_logical_height = LogicalHeightForChild(child);
// Floats' margins do not collapse with page or column boundaries.
if (child.IsFloating())
child_logical_height +=
MarginBeforeForChild(child) + MarginAfterForChild(child);
if (!IsPageLogicalHeightKnown())
return logical_offset;
LayoutUnit remaining_logical_height = PageRemainingLogicalHeightForOffset(
logical_offset, kAssociateWithLatterPage);
if (remaining_logical_height >= child_logical_height)
return logical_offset; // It fits fine where it is. No need to break.
LayoutUnit pagination_strut = CalculatePaginationStrutToFitContent(
logical_offset, child_logical_height);
if (pagination_strut == remaining_logical_height &&
remaining_logical_height == PageLogicalHeightForOffset(logical_offset)) {
// Don't break if we were at the top of a page, and we failed to fit the
// content completely. No point in leaving a page completely blank.
return logical_offset;
if (child.IsLayoutBlockFlow()) {
// If there's a forced break inside this object, figure out if we can fit
// everything before that forced break in the current fragmentainer. If it
// fits, we don't need to insert a break before the child.
const LayoutBlockFlow& block_child = ToLayoutBlockFlow(child);
if (LayoutUnit first_break_offset = block_child.FirstForcedBreakOffset()) {
if (remaining_logical_height >= first_break_offset)
return logical_offset;
return logical_offset + pagination_strut;
void LayoutBlockFlow::RebuildFloatsFromIntruding() {
if (floating_objects_)
HashSet<LayoutBox*> old_intruding_float_set;
if (!ChildrenInline() && floating_objects_) {
const FloatingObjectSet& floating_object_set = floating_objects_->Set();
FloatingObjectSetIterator end = floating_object_set.end();
for (FloatingObjectSetIterator it = floating_object_set.begin(); it != end;
++it) {
const FloatingObject& floating_object = *it->get();
if (!floating_object.IsDescendant())
// Inline blocks are covered by the isAtomicInlineLevel() check in the
// avoidFloats method.
if (AvoidsFloats() || IsDocumentElement() || IsLayoutView() ||
IsFloatingOrOutOfFlowPositioned() || IsTableCell()) {
if (floating_objects_) {
if (!old_intruding_float_set.IsEmpty())
LayoutBoxToFloatInfoMap float_map;
if (floating_objects_) {
if (ChildrenInline())
// We should not process floats if the parent node is not a LayoutBlockFlow.
// Otherwise, we will add floats in an invalid context. This will cause a
// crash arising from a bad cast on the parent.
// See <rdar://problem/8049753>, where float property is applied on a text
// node in a SVG.
if (!Parent() || !Parent()->IsLayoutBlockFlow())
// Attempt to locate a previous sibling with overhanging floats. We skip any
// elements that may have shifted to avoid floats, and any objects whose
// floats cannot interact with objects outside it (i.e. objects that create a
// new block formatting context).
LayoutBlockFlow* parent_block_flow = ToLayoutBlockFlow(Parent());
bool sibling_float_may_intrude = false;
LayoutObject* prev = PreviousSibling();
while (prev && (!prev->IsBox() || !prev->IsLayoutBlock() ||
ToLayoutBlock(prev)->AvoidsFloats() ||
ToLayoutBlock(prev)->CreatesNewFormattingContext())) {
if (prev->IsFloating())
sibling_float_may_intrude = true;
prev = prev->PreviousSibling();
// First add in floats from the parent. Self-collapsing blocks let their
// parent track any floats that intrude into them (as opposed to floats they
// contain themselves) so check for those here too. If margin collapsing has
// moved us up past the top a previous sibling then we need to check for
// floats from the parent too.
bool parent_floats_may_intrude =
!sibling_float_may_intrude &&
(!prev || ToLayoutBlockFlow(prev)->IsSelfCollapsingBlock() ||
ToLayoutBlock(prev)->LogicalTop() > LogicalTop()) &&
parent_block_flow->LowestFloatLogicalBottom() > LogicalTop();
if (sibling_float_may_intrude || parent_floats_may_intrude)
// Add overhanging floats from the previous LayoutBlockFlow, but only if it
// has a float that intrudes into our space.
if (prev) {
LayoutBlockFlow* previous_block_flow = ToLayoutBlockFlow(prev);
if (LogicalTop() < previous_block_flow->LogicalTop() +
AddIntrudingFloats(previous_block_flow, LayoutUnit(),
LogicalTop() - previous_block_flow->LogicalTop());
if (ChildrenInline()) {
LayoutUnit change_logical_top = LayoutUnit::Max();
LayoutUnit change_logical_bottom = LayoutUnit::Min();
if (floating_objects_) {
const FloatingObjectSet& floating_object_set = floating_objects_->Set();
FloatingObjectSetIterator end = floating_object_set.end();
for (FloatingObjectSetIterator it = floating_object_set.begin();
it != end; ++it) {
const FloatingObject& floating_object = *it->get();
FloatingObject* old_floating_object =;
LayoutUnit logical_bottom = LogicalBottomForFloat(floating_object);
if (old_floating_object) {
LayoutUnit old_logical_bottom =
if (LogicalWidthForFloat(floating_object) !=
LogicalWidthForFloat(*old_floating_object) ||
LogicalLeftForFloat(floating_object) !=
LogicalLeftForFloat(*old_floating_object)) {
change_logical_top = LayoutUnit();
change_logical_bottom =
std::max(logical_bottom, old_logical_bottom));
} else {
if (logical_bottom != old_logical_bottom) {
change_logical_top =
std::min(logical_bottom, old_logical_bottom));
change_logical_bottom =
std::max(logical_bottom, old_logical_bottom));
LayoutUnit logical_top = LogicalTopForFloat(floating_object);
LayoutUnit old_logical_top =
if (logical_top != old_logical_top) {
change_logical_top = std::min(
change_logical_top, std::min(logical_top, old_logical_top));
change_logical_bottom =
std::max(logical_top, old_logical_top));
if (old_floating_object->OriginatingLine() && !SelfNeedsLayout()) {
} else {
change_logical_top = LayoutUnit();
change_logical_bottom =
std::max(change_logical_bottom, logical_bottom);
LayoutBoxToFloatInfoMap::iterator end = float_map.end();
for (LayoutBoxToFloatInfoMap::iterator it = float_map.begin(); it != end;
++it) {
std::unique_ptr<FloatingObject>& floating_object = it->value;
if (!floating_object->IsDescendant()) {
change_logical_top = LayoutUnit();
change_logical_bottom = std::max(
change_logical_bottom, LogicalBottomForFloat(*floating_object));
MarkLinesDirtyInBlockRange(change_logical_top, change_logical_bottom);
} else if (!old_intruding_float_set.IsEmpty()) {
// If there are previously intruding floats that no longer intrude, then
// children with floats should also get layout because they might need their
// floating object lists cleared.
if (floating_objects_->Set().size() < old_intruding_float_set.size()) {
} else {
const FloatingObjectSet& floating_object_set = floating_objects_->Set();
FloatingObjectSetIterator end = floating_object_set.end();
for (FloatingObjectSetIterator it = floating_object_set.begin();
it != end && !old_intruding_float_set.IsEmpty(); ++it)
if (!old_intruding_float_set.IsEmpty())
void LayoutBlockFlow::LayoutBlockChildren(bool relayout_children,
SubtreeLayoutScope& layout_scope,
LayoutUnit before_edge,
LayoutUnit after_edge) {
BlockChildrenLayoutInfo layout_info(this, before_edge, after_edge);
MarginInfo& margin_info = layout_info.GetMarginInfo();
// 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
LayoutObject* child_to_exclude =
LayoutSpecialExcludedChild(relayout_children, layout_scope);
// TODO(foolip): Speculative CHECKs to crash if any non-LayoutBox
// children ever appear, the childrenInline() check at the call site
// should make this impossible.
LayoutObject* first_child = FirstChild();
CHECK(!first_child || first_child->IsBox());
LayoutBox* next = ToLayoutBox(first_child);
LayoutBox* last_normal_flow_child = nullptr;
while (next) {
LayoutBox* child = next;
LayoutObject* next_sibling = child->NextSibling();
CHECK(!next_sibling || next_sibling->IsBox());
next = ToLayoutBox(next_sibling);
if (child_to_exclude == child)
continue; // Skip this child, since it will be positioned by the
// specialized subclass (fieldsets and ruby runs).
UpdateBlockChildDirtyBitsBeforeLayout(relayout_children, *child);
if (child->IsOutOfFlowPositioned()) {
AdjustPositionedBlock(*child, layout_info);
if (child->IsFloating()) {
if (child->IsColumnSpanAll()) {
// This is not the containing block of the spanner. The spanner's
// placeholder will lay it out in due course. For now we just need to
// consult our flow thread, so that the columns (if any) preceding and
// following the spanner are laid out correctly. But first we apply the
// pending margin, so that it's taken into consideration and doesn't end
// up on the other side of the spanner.
SetLogicalHeight(LogicalHeight() + margin_info.Margin());
child, OffsetFromLogicalTopOfFirstPage() + LogicalHeight());
// Lay out the child.
LayoutBlockChild(*child, layout_info);
last_normal_flow_child = child;
// Now do the handling of the bottom of the block, adding in our bottom
// border/padding and determining the correct collapsed bottom margin
// information.
HandleAfterSideOfBlock(last_normal_flow_child, before_edge, after_edge,
// Our MarginInfo state used when laying out block children.
MarginInfo::MarginInfo(LayoutBlockFlow* block_flow,
LayoutUnit before_border_padding,
LayoutUnit after_border_padding)
: can_collapse_margin_after_with_last_child_(true),
last_child_is_self_collapsing_block_with_clearance_(false) {
const ComputedStyle& block_style = block_flow->StyleRef();
DCHECK(block_flow->IsLayoutView() || block_flow->Parent());
can_collapse_with_children_ = !block_flow->CreatesNewFormattingContext() &&
!block_flow->IsLayoutFlowThread() &&
can_collapse_margin_before_with_children_ =
can_collapse_with_children_ && !before_border_padding &&
block_style.MarginBeforeCollapse() != EMarginCollapse::kSeparate;
// If any height other than auto is specified in CSS, then we don't collapse
// our bottom margins with our children's margins. To do otherwise would be to
// risk odd visual effects when the children overflow out of the parent block
// and yet still collapse with it. We also don't collapse if we have any
// bottom border/padding.
can_collapse_margin_after_with_children_ =
can_collapse_with_children_ && !after_border_padding &&
(block_style.LogicalHeight().IsAuto() &&
!block_style.LogicalHeight().Value()) &&
block_style.MarginAfterCollapse() != EMarginCollapse::kSeparate;
quirk_container_ = block_flow->IsTableCell() || block_flow->IsBody();
discard_margin_ = can_collapse_margin_before_with_children_ &&
positive_margin_ = (can_collapse_margin_before_with_children_ &&
? block_flow->MaxPositiveMarginBefore()
: LayoutUnit();
negative_margin_ = (can_collapse_margin_before_with_children_ &&
? block_flow->MaxNegativeMarginBefore()
: LayoutUnit();
LayoutBlockFlow::MarginValues LayoutBlockFlow::MarginValuesForChild(
LayoutBox& child) const {
LayoutUnit child_before_positive;
LayoutUnit child_before_negative;
LayoutUnit child_after_positive;
LayoutUnit child_after_negative;
LayoutUnit before_margin;
LayoutUnit after_margin;
LayoutBlockFlow* child_layout_block_flow =
child.IsLayoutBlockFlow() ? ToLayoutBlockFlow(&child) : nullptr;
// If the child has the same directionality as we do, then we can just return
// its margins in the same direction.
if (!child.IsWritingModeRoot()) {
if (child_layout_block_flow) {
child_before_positive =
child_before_negative =
child_after_positive = child_layout_block_flow->MaxPositiveMarginAfter();
child_after_negative = child_layout_block_flow->MaxNegativeMarginAfter();
} else {
before_margin = child.MarginBefore();
after_margin = child.MarginAfter();
} else if (child.IsHorizontalWritingMode() == IsHorizontalWritingMode()) {
// The child has a different directionality. If the child is parallel, then
// it's just flipped relative to us. We can use the margins for the opposite
// edges.
if (child_layout_block_flow) {
child_before_positive = child_layout_block_flow->MaxPositiveMarginAfter();
child_before_negative = child_layout_block_flow->MaxNegativeMarginAfter();
child_after_positive = child_layout_block_flow->MaxPositiveMarginBefore();
child_after_negative = child_layout_block_flow->MaxNegativeMarginBefore();
} else {
before_margin = child.MarginAfter();
after_margin = child.MarginBefore();
} else {
// The child is perpendicular to us, which means its margins don't collapse
// but are on the "logical left/right" sides of the child box. We can just
// return the raw margin in this case.
before_margin = MarginBeforeForChild(child);
after_margin = MarginAfterForChild(child);
// Resolve uncollapsing margins into their positive/negative buckets.
if (before_margin) {
if (before_margin > 0)
child_before_positive = before_margin;
child_before_negative = -before_margin;
if (after_margin) {
if (after_margin > 0)
child_after_positive = after_margin;
child_after_negative = -after_margin;
return LayoutBlockFlow::MarginValues(
child_before_positive, child_before_negative, child_after_positive,
LayoutUnit LayoutBlockFlow::AdjustedMarginBeforeForPagination(
const LayoutBox& child,
LayoutUnit logical_top_margin_edge,
LayoutUnit logical_top_border_edge,
const BlockChildrenLayoutInfo& layout_info) const {
LayoutUnit effective_margin =
logical_top_border_edge - logical_top_margin_edge;
if (effective_margin <= LayoutUnit())
return effective_margin;
// If margins would pull us past the top of the next fragmentainer, then we
// need to pull back and let the margins collapse into the fragmentainer
// boundary. If we're at a fragmentainer boundary, and there's no forced break
// involved, collapse the margin with the boundary we're at. Otherwise,
// preserve the margin at the top of the fragmentainer, but collapse it with
// the next fragmentainer boundary, since no margin should ever live in more
// than one fragmentainer.
PageBoundaryRule rule = kAssociateWithLatterPage;
if (!child.NeedsForcedBreakBefore(layout_info.PreviousBreakAfterValue()) &&
OffsetFromLogicalTopOfFirstPage() + logical_top_margin_edge >
rule = kAssociateWithFormerPage;
LayoutUnit remaining_space =
PageRemainingLogicalHeightForOffset(logical_top_margin_edge, rule);
return std::min(effective_margin, remaining_space);
static LayoutBlockFlow* PreviousBlockFlowInFormattingContext(
const LayoutBox& child) {
LayoutObject* prev = child.PreviousSibling();
while (prev && (!prev->IsLayoutBlockFlow() ||
ToLayoutBlockFlow(prev)->CreatesNewFormattingContext())) {
prev = prev->PreviousSibling();
if (prev)
return ToLayoutBlockFlow(prev);
return nullptr;
LayoutUnit LayoutBlockFlow::CollapseMargins(
LayoutBox& child,
BlockChildrenLayoutInfo& layout_info,
bool child_is_self_collapsing,
bool child_discard_margin_before,
bool child_discard_margin_after) {
MarginInfo& margin_info = layout_info.GetMarginInfo();
// The child discards the before margin when the the after margin has discard
// in the case of a self collapsing block.
child_discard_margin_before =
child_discard_margin_before ||
(child_discard_margin_after && child_is_self_collapsing);
// Get the four margin values for the child and cache them.
const LayoutBlockFlow::MarginValues child_margins =
// Get our max pos and neg top margins.
LayoutUnit pos_top = child_margins.PositiveMarginBefore();
LayoutUnit neg_top = child_margins.NegativeMarginBefore();
// For self-collapsing blocks, collapse our bottom margins into our
// top to get new posTop and negTop values.
if (child_is_self_collapsing) {
pos_top = std::max(pos_top, child_margins.PositiveMarginAfter());
neg_top = std::max(neg_top, child_margins.NegativeMarginAfter());
// See if the top margin is quirky. We only care if this child has
// margins that will collapse with us.
bool top_quirk = HasMarginBeforeQuirk(&child);
if (margin_info.CanCollapseWithMarginBefore()) {
if (!child_discard_margin_before && !margin_info.DiscardMargin()) {
// This child is collapsing with the top of the
// block. If it has larger margin values, then we need to update
// our own maximal values.
if (!GetDocument().InQuirksMode() || !margin_info.QuirkContainer() ||
SetMaxMarginBeforeValues(std::max(pos_top, MaxPositiveMarginBefore()),
std::max(neg_top, MaxNegativeMarginBefore()));
// The minute any of the margins involved isn't a quirk, don't
// collapse it away, even if the margin is smaller (
// has an example of this, a <dt> with 0.8em author-specified inside
// a <dl> inside a <td>.
if (!margin_info.DeterminedMarginBeforeQuirk() && !top_quirk &&
(pos_top - neg_top)) {
if (!margin_info.DeterminedMarginBeforeQuirk() && top_quirk &&
!MarginBefore()) {
// We have no top margin and our top child has a quirky margin.
// We will pick up this quirky margin and pass it through.
// This deals with the <td><div><p> case.
// Don't do this for a block that split two inlines though. You do
// still apply margins in this case.
} else {
// The before margin of the container will also discard all the margins it
// is collapsing with.
// Once we find a child with discardMarginBefore all the margins collapsing
// with us must also discard.
if (child_discard_margin_before) {
if (margin_info.QuirkContainer() && margin_info.AtBeforeSideOfBlock() &&
(pos_top - neg_top))
LayoutUnit before_collapse_logical_top = LogicalHeight();
LayoutUnit logical_top = before_collapse_logical_top;
LayoutObject* prev = child.PreviousSibling();
LayoutBlockFlow* previous_block_flow =
prev && prev->IsLayoutBlockFlow() ? ToLayoutBlockFlow(prev) : nullptr;
bool previous_block_flow_can_self_collapse =
previous_block_flow &&
// If the child's previous sibling is a self-collapsing block that cleared a
// float then its top border edge has been set at the bottom border edge of
// the float. Since we want to collapse the child's top margin with the self-
// collapsing block's top and bottom margins we need to adjust our parent's
// height to match the margin top of the self-collapsing block. If the
// resulting collapsed margin leaves the child still intruding into the float
// then we will want to clear it.
if (!margin_info.CanCollapseWithMarginBefore() &&
previous_block_flow_can_self_collapse &&
LogicalHeight() -
if (child_is_self_collapsing) {
// For a self collapsing block both the before and after margins get
// discarded. The block doesn't contribute anything to the height of the
// block. Also, the child's top position equals the logical height of the
// container.
if (!child_discard_margin_before && !margin_info.DiscardMargin()) {
// This child has no height. We need to compute our
// position before we collapse the child's margins together,
// so that we can get an accurate position for the zero-height block.
LayoutUnit collapsed_before_pos = std::max(
margin_info.PositiveMargin(), child_margins.PositiveMarginBefore());
LayoutUnit collapsed_before_neg = std::max(
margin_info.NegativeMargin(), child_margins.NegativeMarginBefore());
margin_info.SetMargin(collapsed_before_pos, collapsed_before_neg);
// Now collapse the child's margins together, which means examining our
// bottom margin values as well.
if (!margin_info.CanCollapseWithMarginBefore()) {
// We need to make sure that the position of the self-collapsing block
// is correct, since it could have overflowing content
// that needs to be positioned correctly (e.g., a block that
// had a specified height of 0 but that actually had subcontent).
logical_top =
LogicalHeight() + collapsed_before_pos - collapsed_before_neg;
} else {
if (MustSeparateMarginBeforeForChild(child)) {
DCHECK(!margin_info.DiscardMargin() ||
(margin_info.DiscardMargin() && !margin_info.Margin()));
// If we are at the before side of the block and we collapse, ignore the
// computed margin and just add the child margin to the container height.
// This will correctly position the child inside the container.
LayoutUnit separate_margin = !margin_info.CanCollapseWithMarginBefore()
? margin_info.Margin()
: LayoutUnit();
SetLogicalHeight(LogicalHeight() + separate_margin +
logical_top = LogicalHeight();
} else if (!margin_info.DiscardMargin() &&
(!margin_info.AtBeforeSideOfBlock() ||
(!margin_info.CanCollapseMarginBeforeWithChildren() &&
(!GetDocument().InQuirksMode() ||
!margin_info.QuirkContainer() ||
!margin_info.HasMarginBeforeQuirk())))) {
// We're collapsing with a previous sibling's margins and not
// with the top of the block.
SetLogicalHeight(LogicalHeight() +
std::max(margin_info.PositiveMargin(), pos_top) -
std::max(margin_info.NegativeMargin(), neg_top));
logical_top = LogicalHeight();
if (!margin_info.DiscardMargin()) {
} else {
if (margin_info.Margin())
if (View()->GetLayoutState()->IsPaginated() && IsPageLogicalHeightKnown()) {
LayoutUnit old_logical_top = logical_top;
LayoutUnit margin = AdjustedMarginBeforeForPagination(
child, before_collapse_logical_top, logical_top, layout_info);
logical_top = before_collapse_logical_top + margin;
SetLogicalHeight(LogicalHeight() + (logical_top - old_logical_top));
// If |child| has moved up into previous siblings it needs to avoid or clear
// any floats they contain.
if (logical_top < before_collapse_logical_top) {
LayoutUnit old_logical_height = LogicalHeight();
LayoutBlockFlow* previous_block_flow =
while (previous_block_flow) {
auto lowest_float = previous_block_flow->LogicalTop() +
if (lowest_float <= logical_top)
AddOverhangingFloats(previous_block_flow, false);
previous_block_flow =
if (previous_block_flow_can_self_collapse) {
// If |child|'s previous sibling is or contains a self-collapsing block that
// cleared a float and margin collapsing resulted in |child| moving up
// into the margin area of the self-collapsing block then the float it
// clears is now intruding into |child|. Layout again so that we can look
// for floats in the parent that overhang |child|'s new logical top.
bool logical_top_intrudes_into_float =
logical_top < before_collapse_logical_top;
if (logical_top_intrudes_into_float && ContainsFloats() &&
!child.AvoidsFloats() && LowestFloatLogicalBottom() > logical_top)
return logical_top;
void LayoutBlockFlow::AdjustPositionedBlock(
LayoutBox& child,
const BlockChildrenLayoutInfo& layout_info) {
LayoutUnit logical_top = LogicalHeight();
// Forced breaks are only specified on in-flow objects, but auto-positioned
// out-of-flow objects may be affected by a break-after value of the previous
// in-flow object.
if (View()->GetLayoutState()->IsPaginated())
logical_top =
ApplyForcedBreak(logical_top, layout_info.PreviousBreakAfterValue());
UpdateStaticInlinePositionForChild(child, logical_top);
const MarginInfo& margin_info = layout_info.GetMarginInfo();
if (!margin_info.CanCollapseWithMarginBefore()) {
// Positioned blocks don't collapse margins, so add the margin provided by
// the container now. The child's own margin is added later when calculating
// its logical top.
LayoutUnit collapsed_before_pos = margin_info.PositiveMargin();
LayoutUnit collapsed_before_neg = margin_info.NegativeMargin();
logical_top += collapsed_before_pos - collapsed_before_neg;
PaintLayer* child_layer = child.Layer();
if (child_layer->StaticBlockPosition() != logical_top)
LayoutUnit LayoutBlockFlow::ClearFloatsIfNeeded(LayoutBox& child,
MarginInfo& margin_info,
LayoutUnit old_top_pos_margin,
LayoutUnit old_top_neg_margin,
LayoutUnit y_pos,
bool child_is_self_collapsing,
bool child_discard_margin) {
LayoutUnit height_increase = GetClearDelta(&child, y_pos);
if (!height_increase)
return y_pos;
if (child_is_self_collapsing) {
// For self-collapsing blocks that clear, they can still collapse their
// margins with following siblings. Reset the current margins to represent
// the self-collapsing block's margins only.
// If DISCARD is specified for -webkit-margin-collapse, reset the margin
// values.
LayoutBlockFlow::MarginValues child_margins = MarginValuesForChild(child);
if (!child_discard_margin) {
} else {
// CSS2.1 states:
// "If the top and bottom margins of an element with clearance are
// adjoining, its margins collapse with the adjoining margins of following
// siblings but that resulting margin does not collapse with the bottom
// margin of the parent block."
// So the parent's bottom margin cannot collapse through this block or any
// subsequent self-collapsing blocks. Set a bit to ensure this happens; it
// will get reset if we encounter an in-flow sibling that is not
// self-collapsing.
// For now set the border-top of |child| flush with the bottom border-edge
// of the float so it can layout any floating or positioned children of its
// own at the correct vertical position. If subsequent siblings attempt to
// collapse with |child|'s margins in |collapseMargins| we will adjust the
// height of the parent to |child|'s margin top (which if it is positive
// sits up 'inside' the float it's clearing) so that all three margins can
// collapse at the correct vertical position.
// Per CSS2.1 we need to ensure that any negative margin-top clears |child|
// beyond the bottom border-edge of the float so that the top border edge of
// the child (i.e. its clearance) is at a position that satisfies the
// equation: "the amount of clearance is set so that:
// clearance + margin-top = [height of float],
// i.e., clearance = [height of float] - margin-top".
SetLogicalHeight(child.LogicalTop() + child_margins.NegativeMarginBefore());
} else {
// Increase our height by the amount we had to clear.
SetLogicalHeight(LogicalHeight() + height_increase);
if (margin_info.CanCollapseWithMarginBefore()) {
// We can no longer collapse with the top of the block since a clear
// occurred. The empty blocks collapse into the cleared block.
SetMaxMarginBeforeValues(old_top_pos_margin, old_top_neg_margin);
// In case the child discarded the before margin of the block we need to
// reset the mustDiscardMarginBefore flag to the initial value.
SetMustDiscardMarginBefore(StyleRef().MarginBeforeCollapse() ==
return y_pos + height_increase;
void LayoutBlockFlow::SetCollapsedBottomMargin(const MarginInfo& margin_info) {
if (margin_info.CanCollapseWithMarginAfter() &&
!margin_info.CanCollapseWithMarginBefore()) {
// Update the after side margin of the container to discard if the after
// margin of the last child also discards and we collapse with it.
// Don't update the max margin values because we won't need them anyway.
if (margin_info.DiscardMargin()) {
// Update our max pos/neg bottom margins, since we collapsed our bottom
// margins with our children.
std::max(MaxPositiveMarginAfter(), margin_info.PositiveMargin()),
std::max(MaxNegativeMarginAfter(), margin_info.NegativeMargin()));
if (!margin_info.HasMarginAfterQuirk())
if (margin_info.HasMarginAfterQuirk() && !MarginAfter()) {
// We have no bottom margin and our last child has a quirky margin.
// We will pick up this quirky margin and pass it through.
// This deals with the <td><div><p> case.
void LayoutBlockFlow::MarginBeforeEstimateForChild(
LayoutBox& child,
LayoutUnit& positive_margin_before,
LayoutUnit& negative_margin_before,
bool& discard_margin_before) const {
// Give up if in quirks mode and we're a body/table cell and the top margin of
// the child box is quirky.
// Give up if the child specified -webkit-margin-collapse: separate that
// prevents collapsing.
// FIXME: Use writing mode independent accessor for marginBeforeCollapse.
if ((GetDocument().InQuirksMode() && HasMarginBeforeQuirk(&child) &&
(IsTableCell() || IsBody())) ||
child.StyleRef().MarginBeforeCollapse() == EMarginCollapse::kSeparate)
// The margins are discarded by a child that specified
// -webkit-margin-collapse: discard.
// FIXME: Use writing mode independent accessor for marginBeforeCollapse.
if (child.StyleRef().MarginBeforeCollapse() == EMarginCollapse::kDiscard) {
positive_margin_before = LayoutUnit();
negative_margin_before = LayoutUnit();
discard_margin_before = true;
LayoutUnit before_child_margin = MarginBeforeForChild(child);
positive_margin_before =
std::max(positive_margin_before, before_child_margin);
negative_margin_before =
std::max(negative_margin_before, -before_child_margin);
if (!child.IsLayoutBlockFlow())
LayoutBlockFlow* child_block_flow = ToLayoutBlockFlow(&child);
if (child_block_flow->ChildrenInline() ||
MarginInfo child_margin_info(
child_block_flow->BorderBefore() + child_block_flow->PaddingBefore(),
child_block_flow->BorderAfter() + child_block_flow->PaddingAfter());
if (!child_margin_info.CanCollapseMarginBeforeWithChildren())
LayoutBox* grandchild_box = child_block_flow->FirstChildBox();
for (; grandchild_box; grandchild_box = grandchild_box->NextSiblingBox()) {
if (!grandchild_box->IsFloatingOrOutOfFlowPositioned() &&
if (!grandchild_box)
// Make sure to update the block margins now for the grandchild box so that
// we're looking at current values.
if (grandchild_box->NeedsLayout()) {
if (grandchild_box->IsLayoutBlock()) {
LayoutBlock* grandchild_block = ToLayoutBlock(grandchild_box);
// If we have a 'clear' value but also have a margin we may not actually
// require clearance to move past any floats. If that's the case we want to be
// sure we estimate the correct position including margins after any floats
// rather than use 'clearance' later which could give us the wrong position.
if (grandchild_box->StyleRef().Clear() != EClear::kNone &&
child_block_flow->MarginBeforeForChild(*grandchild_box) == 0)
// Collapse the margin of the grandchild box with our own to produce an
// estimate.
*grandchild_box, positive_margin_before, negative_margin_before,
LayoutUnit LayoutBlockFlow::EstimateLogicalTopPosition(
LayoutBox& child,
const BlockChildrenLayoutInfo& layout_info,
LayoutUnit& estimate_without_pagination) {
const MarginInfo& margin_info = layout_info.GetMarginInfo();
// FIXME: We need to eliminate the estimation of vertical position, because
// when it's wrong we sometimes trigger a pathological
// relayout if there are intruding floats.
LayoutUnit logical_top_estimate = LogicalHeight();
LayoutUnit positive_margin_before;
LayoutUnit negative_margin_before;
bool discard_margin_before = false;
if (!margin_info.CanCollapseWithMarginBefore()) {
if (child.SelfNeedsLayout()) {
// Try to do a basic estimation of how the collapse is going to go.
MarginBeforeEstimateForChild(child, positive_margin_before,
} else {
// Use the cached collapsed margin values from a previous layout. Most of
// the time they will be right.
LayoutBlockFlow::MarginValues margin_values = MarginValuesForChild(child);
positive_margin_before = std::max(positive_margin_before,
negative_margin_before = std::max(negative_margin_before,
discard_margin_before = MustDiscardMarginBeforeForChild(child);
// Collapse the result with our current margins.
if (!discard_margin_before)
logical_top_estimate +=
std::max(margin_info.PositiveMargin(), positive_margin_before) -
std::max(margin_info.NegativeMargin(), negative_margin_before);
LayoutState* layout_state = View()->GetLayoutState();
if (layout_state->IsPaginated() && IsPageLogicalHeightKnown()) {
LayoutUnit margin = AdjustedMarginBeforeForPagination(
child, LogicalHeight(), logical_top_estimate, layout_info);
logical_top_estimate = LogicalHeight() + margin;
logical_top_estimate += GetClearDelta(&child, logical_top_estimate);
estimate_without_pagination = logical_top_estimate;
if (layout_state->IsPaginated()) {
if (!layout_info.IsAtFirstInFlowChild()) {
// Estimate the need for a forced break in front of this child. The final
// break policy at this class A break point isn't known until we have laid
// out the children of |child|. There may be forced break-before values
// set on first-children inside that get propagated up to the child.
// Just make an estimate with what we know so far.
EBreakBetween break_value =
if (IsForcedFragmentainerBreakValue(break_value)) {
logical_top_estimate = ApplyForcedBreak(LogicalHeight(), break_value);
// Disregard previous margins, since they will collapse with the
// fragmentainer boundary, due to the forced break. Only apply margins
// that have been specified on the child or its descendants.
if (!discard_margin_before)
logical_top_estimate +=
positive_margin_before - negative_margin_before;
// Clearance may already have taken us past the beginning of the next
// fragmentainer.
return std::max(estimate_without_pagination, logical_top_estimate);
logical_top_estimate =
AdjustForUnsplittableChild(child, logical_top_estimate);
return logical_top_estimate;
void LayoutBlockFlow::AdjustFloatingBlock(const MarginInfo& margin_info) {
// The float should be positioned taking into account the bottom margin
// of the previous flow. We add that margin into the height, get the
// float positioned properly, and then subtract the margin out of the
// height again. In the case of self-collapsing blocks, we always just
// use the top margins, since the self-collapsing block collapsed its
// own bottom margin into its top margin.
// Note also that the previous flow may collapse its margin into the top of
// our block. If this is the case, then we do not add the margin in to our
// height when computing the position of the float. This condition can be
// tested for by simply calling canCollapseWithMarginBefore. See
// for
// an example of this scenario.
LayoutUnit logical_top = LogicalHeight();
if (!margin_info.CanCollapseWithMarginBefore())
logical_top += margin_info.Margin();
void LayoutBlockFlow::HandleAfterSideOfBlock(LayoutBox* last_child,
LayoutUnit before_side,
LayoutUnit after_side,
MarginInfo& margin_info) {
// If our last child was a self-collapsing block with clearance then our
// logical height is flush with the bottom edge of the float that the child
// clears. The correct vertical position for the margin-collapsing we want to
// perform now is at the child's margin-top - so adjust our height to that
// position.
if (margin_info.LastChildIsSelfCollapsingBlockWithClearance()) {
SetLogicalHeight(LogicalHeight() -
if (margin_info.CanCollapseMarginAfterWithChildren() &&
// If we can't collapse with children then go ahead and add in the bottom
// margin.
if (!margin_info.DiscardMargin() &&
(!margin_info.CanCollapseWithMarginAfter() &&
!margin_info.CanCollapseWithMarginBefore() &&
(!GetDocument().InQuirksMode() || !margin_info.QuirkContainer() ||
SetLogicalHeight(LogicalHeight() + margin_info.Margin());
// Now add in our bottom border/padding.
SetLogicalHeight(LogicalHeight() + after_side);
// 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.
SetLogicalHeight(std::max(LogicalHeight(), before_side + after_side));
// Update our bottom collapsed margin info.
// There's no class A break point right after the last child, only *between*
// siblings. So propagate the break-after value, and keep looking for a class
// A break point (at the next in-flow block-level object), where we'll join
// this break-after value with the break-before value there.
if (View()->GetLayoutState()->IsPaginated() && last_child)
JoinFragmentainerBreakValues(BreakAfter(), last_child->BreakAfter()));
void LayoutBlockFlow::SetMustDiscardMarginBefore(bool value) {
if (StyleRef().MarginBeforeCollapse() == EMarginCollapse::kDiscard) {
if (!rare_data_ && !value)
if (!rare_data_)
rare_data_ = std::make_unique<LayoutBlockFlowRareData>(this);
rare_data_->discard_margin_before_ = value;
void LayoutBlockFlow::SetMustDiscardMarginAfter(bool value) {
if (StyleRef().MarginAfterCollapse() == EMarginCollapse::kDiscard) {
if (!rare_data_ && !value)
if (!rare_data_)
rare_data_ = std::make_unique<LayoutBlockFlowRareData>(this);
rare_data_->discard_margin_after_ = value;
bool LayoutBlockFlow::MustDiscardMarginBefore() const {
return StyleRef().MarginBeforeCollapse() == EMarginCollapse::kDiscard ||
(rare_data_ && rare_data_->discard_margin_before_);
bool LayoutBlockFlow::MustDiscardMarginAfter() const {
return StyleRef().MarginAfterCollapse() == EMarginCollapse::kDiscard ||
(rare_data_ && rare_data_->discard_margin_after_);
bool LayoutBlockFlow::MustDiscardMarginBeforeForChild(
const LayoutBox& child) const {
DCHECK(!child.SelfNeedsLayout() || child.LayoutBlockedByDisplayLock());
if (!child.IsWritingModeRoot()) {
return child.IsLayoutBlockFlow()
? ToLayoutBlockFlow(&child)->MustDiscardMarginBefore()
: (child.StyleRef().MarginBeforeCollapse() ==
if (child.IsHorizontalWritingMode() == IsHorizontalWritingMode()) {
return child.IsLayoutBlockFlow()
? ToLayoutBlockFlow(&child)->MustDiscardMarginAfter()
: (child.StyleRef().MarginAfterCollapse() ==
// FIXME: We return false here because the implementation is not geometrically
// complete. We have values only for before/after, not start/end.
// In case the boxes are perpendicular we assume the property is not
// specified.
return false;
bool LayoutBlockFlow::MustDiscardMarginAfterForChild(
const LayoutBox& child) const {
DCHECK(!child.SelfNeedsLayout() || child.LayoutBlockedByDisplayLock());
if (!child.IsWritingModeRoot()) {
return child.IsLayoutBlockFlow()
? ToLayoutBlockFlow(&child)->MustDiscardMarginAfter()
: (child.StyleRef().MarginAfterCollapse() ==
if (child.IsHorizontalWritingMode() == IsHorizontalWritingMode()) {
return child.IsLayoutBlockFlow()
? ToLayoutBlockFlow(&child)->MustDiscardMarginBefore()
: (child.StyleRef().MarginBeforeCollapse() ==
// FIXME: See |mustDiscardMarginBeforeForChild| above.
return false;
void LayoutBlockFlow::SetMaxMarginBeforeValues(LayoutUnit pos, LayoutUnit neg) {
if (!rare_data_) {
if (pos == LayoutBlockFlowRareData::PositiveMarginBeforeDefault(this) &&
neg == LayoutBlockFlowRareData::NegativeMarginBeforeDefault(this))
rare_data_ = std::make_unique<LayoutBlockFlowRareData>(this);
void LayoutBlockFlow::SetMaxMarginAfterValues(LayoutUnit pos, LayoutUnit neg) {
if (!rare_data_) {
if (pos == LayoutBlockFlowRareData::PositiveMarginAfterDefault(this) &&
neg == LayoutBlockFlowRareData::NegativeMarginAfterDefault(this))
rare_data_ = std::make_unique<LayoutBlockFlowRareData>(this);
bool LayoutBlockFlow::MustSeparateMarginBeforeForChild(
const LayoutBox& child) const {
DCHECK(!child.SelfNeedsLayout() || child.LayoutBlockedByDisplayLock());
const ComputedStyle& child_style = child.StyleRef();
if (!child.IsWritingModeRoot())
return child_style.MarginBeforeCollapse() == EMarginCollapse::kSeparate;
if (child.IsHorizontalWritingMode() == IsHorizontalWritingMode())
return child_style.MarginAfterCollapse() == EMarginCollapse::kSeparate;
// FIXME: See |mustDiscardMarginBeforeForChild| above.
return false;
bool LayoutBlockFlow::MustSeparateMarginAfterForChild(
const LayoutBox& child) const {
DCHECK(!child.SelfNeedsLayout() || child.LayoutBlockedByDisplayLock());
const ComputedStyle& child_style = child.StyleRef();
if (!child.IsWritingModeRoot())
return child_style.MarginAfterCollapse() == EMarginCollapse::kSeparate;
if (child.IsHorizontalWritingMode() == IsHorizontalWritingMode())
return child_style.MarginBeforeCollapse() == EMarginCollapse::kSeparate;
// FIXME: See |mustDiscardMarginBeforeForChild| above.
return false;
LayoutUnit LayoutBlockFlow::ApplyForcedBreak(LayoutUnit logical_offset,
EBreakBetween break_value) {
if (!IsForcedFragmentainerBreakValue(break_value))
return logical_offset;
// TODO(mstensho): honor breakValue. There are different types of forced
// breaks. We currently just assume that we want to break to the top of the
// next fragmentainer of the fragmentation context we're in. However, we may
// want to find the next left or right page - even if we're inside a multicol
// container when printing.
if (!IsPageLogicalHeightKnown()) {
// Page height is still unknown, so we cannot insert forced breaks.
return logical_offset;
LayoutUnit remaining_logical_height = PageRemainingLogicalHeightForOffset(
logical_offset, kAssociateWithLatterPage);
if (remaining_logical_height == PageLogicalHeightForOffset(logical_offset))
return logical_offset; // Don't break if we're already at the block start
// of a fragmentainer.
// If this is the first forced break inside this object, store the
// location. We need this information later if there's a break-inside:avoid
// object further up. We need to know if there are any forced breaks inside
// such objects, in order to determine whether we need to push it to the next
// fragmentainer or not.
if (!FirstForcedBreakOffset())
return logical_offset + remaining_logical_height;
void LayoutBlockFlow::SetBreakBefore(EBreakBetween break_value) {
if (break_value != EBreakBetween::kAuto &&
break_value = EBreakBetween::kAuto;
if (break_value == EBreakBetween::kAuto && !rare_data_)
EnsureRareData().break_before_ = static_cast<unsigned>(break_value);
void LayoutBlockFlow::SetBreakAfter(EBreakBetween break_value) {
if (break_value != EBreakBetween::kAuto &&
break_value = EBreakBetween::kAuto;
if (break_value == EBreakBetween::kAuto && !rare_data_)
EnsureRareData().break_after_ = static_cast<unsigned>(break_value);
EBreakBetween LayoutBlockFlow::BreakBefore() const {
return rare_data_ ? static_cast<EBreakBetween>(rare_data_->break_before_)
: EBreakBetween::kAuto;
EBreakBetween LayoutBlockFlow::BreakAfter() const {
return rare_data_ ? static_cast<EBreakBetween>(rare_data_->break_after_)
: EBreakBetween::kAuto;
void LayoutBlockFlow::AddVisualOverflowFromFloats() {
if (!floating_objects_)
for (auto& floating_object : floating_objects_->Set()) {
if (floating_object->IsDescendant()) {
void LayoutBlockFlow::AddLayoutOverflowFromFloats() {
if (!floating_objects_)
for (auto& floating_object : floating_objects_->Set()) {
if (floating_object->IsDescendant()) {
scoped_refptr<NGLayoutResult> LayoutBlockFlow::CachedLayoutResult(
const NGConstraintSpace&,
const NGBreakToken*) {
return nullptr;
scoped_refptr<const NGLayoutResult>
LayoutBlockFlow::CachedLayoutResultForTesting() {
return nullptr;
void LayoutBlockFlow::SetCachedLayoutResult(const NGConstraintSpace&,
const NGBreakToken*,
const NGLayoutResult&) {}
void LayoutBlockFlow::ClearCachedLayoutResult() {}
bool LayoutBlockFlow::AreCachedLinesValidFor(const NGConstraintSpace&) const {
return false;
void LayoutBlockFlow::SetPaintFragment(const NGBreakToken*,
scoped_refptr<const NGPhysicalFragment>,
NGPhysicalOffset) {}
void LayoutBlockFlow::UpdatePaintFragmentFromCachedLayoutResult(
const NGBreakToken*,
scoped_refptr<const NGPhysicalFragment>,
NGPhysicalOffset) {}
void LayoutBlockFlow::ComputeVisualOverflow(
const LayoutRect& previous_visual_overflow_rect,
bool recompute_floats) {
if (recompute_floats || CreatesNewFormattingContext() ||
if (VisualOverflowRect() != previous_visual_overflow_rect) {
if (Layer())
void LayoutBlockFlow::ComputeLayoutOverflow(LayoutUnit old_client_after_edge,
bool recompute_floats) {
LayoutBlock::ComputeLayoutOverflow(old_client_after_edge, recompute_floats);
// TODO(chrishtr): why does it check for a self-painting layer? That should
// only apply to visual overflow.
if (recompute_floats || CreatesNewFormattingContext() ||
void LayoutBlockFlow::ComputeSelfHitTestRects(
Vector<LayoutRect>& rects,
const LayoutPoint& layer_offset) const {
LayoutBlock::ComputeSelfHitTestRects(rects, layer_offset);
if (!HasHorizontalLayoutOverflow() && !HasVerticalLayoutOverflow())
for (RootInlineBox* curr = FirstRootBox(); curr; curr = curr->NextRootBox()) {
LayoutUnit top = std::max<LayoutUnit>(curr->LineTop(), curr->Y());
LayoutUnit bottom =
std::min<LayoutUnit>(curr->LineBottom(), curr->Y() + curr->Height());
LayoutRect rect(layer_offset.X() + curr->X(), layer_offset.Y() + top,
curr->Width(), bottom - top);
// It's common for this rect to be entirely contained in our box, so exclude
// that simple case.
if (!rect.IsEmpty() && (rects.IsEmpty() || !rects[0].Contains(rect)))
void LayoutBlockFlow::AbsoluteRects(
Vector<IntRect>& rects,
const LayoutPoint& accumulated_offset) const {
if (!IsAnonymousBlockContinuation()) {
LayoutBlock::AbsoluteRects(rects, accumulated_offset);
// For blocks inside inlines, we go ahead and include margins so that we run
// right up to the inline boxes above and below us (thus getting merged with
// them to form a single irregular shape).
// FIXME: This is wrong for vertical writing-modes.
LayoutRect rect(accumulated_offset, Size());
accumulated_offset -
Location() +
void LayoutBlockFlow::AbsoluteQuads(Vector<FloatQuad>& quads,
MapCoordinatesFlags mode) const {
if (!IsAnonymousBlockContinuation()) {
LayoutBlock::AbsoluteQuads(quads, mode);
LayoutBoxModelObject::AbsoluteQuads(quads, mode);
void LayoutBlockFlow::AbsoluteQuadsForSelf(Vector<FloatQuad>& quads,
MapCoordinatesFlags mode) const {
// For blocks inside inlines, we go ahead and include margins so that we run
// right up to the inline boxes above and below us (thus getting merged with
// them to form a single irregular shape).
// FIXME: This is wrong for vertical writing-modes.
LayoutRect local_rect(LayoutPoint(), Size());
quads.push_back(LocalToAbsoluteQuad(FloatRect(local_rect), mode));
LayoutObject* LayoutBlockFlow::HoverAncestor() const {
return IsAnonymousBlockContinuation() ? Continuation()
: LayoutBlock::HoverAncestor();
RootInlineBox* LayoutBlockFlow::CreateAndAppendRootInlineBox() {
RootInlineBox* root_box = CreateRootInlineBox();
return root_box;
// Note: When this function is called from |LayoutInline::SplitFlow()|, some
// fragments point to destroyed |LayoutObject|.
void LayoutBlockFlow::DeleteLineBoxTree() {
if (ContainsFloats())
int LayoutBlockFlow::LineCount(
const RootInlineBox* stop_root_inline_box) const {
#ifndef NDEBUG
DCHECK(!stop_root_inline_box ||
stop_root_inline_box->Block().DebugPointer() == this);
if (!ChildrenInline())
return 0;
int count = 0;