blob: cced70239eae4ce7e7431a273703bf2dc3a6661a [file] [log] [blame]
* Copyright (C) 1999 Lars Knoll (
* (C) 1999 Antti Koivisto (
* (C) 2007 David Smith (
* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc.
* All rights reserved.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* Library General Public License for more details.
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
#ifndef LayoutBlock_h
#define LayoutBlock_h
#include <memory>
#include "core/CoreExport.h"
#include "core/layout/LayoutBox.h"
#include "platform/wtf/ListHashSet.h"
namespace blink {
struct PaintInfo;
class LineLayoutBox;
class WordMeasurement;
typedef WTF::ListHashSet<LayoutBox*, 16> TrackedLayoutBoxListHashSet;
typedef WTF::HashMap<const LayoutBlock*,
typedef WTF::HashMap<const LayoutBox*, LayoutBlock*> TrackedContainerMap;
typedef Vector<WordMeasurement, 64> WordMeasurements;
enum ContainingBlockState { kNewContainingBlock, kSameContainingBlock };
// LayoutBlock is the class that is used by any LayoutObject
// that is a containing block.
// See also LayoutObject::containingBlock() that is the function
// used to get the containing block of a LayoutObject.
// CSS is inconsistent and allows inline elements (LayoutInline) to be
// containing blocks, even though they are not blocks. Our
// implementation is as confused with inlines. See e.g.
// LayoutObject::containingBlock() vs LayoutObject::container().
// Containing blocks are a central concept for layout, in
// particular to the layout of out-of-flow positioned
// elements. They are used to determine the sizing as well
// as the positioning of the LayoutObjects.
// LayoutBlock is the class that handles out-of-flow positioned elements in
// Blink, in particular for layout (see layoutPositionedObjects()). That's why
// LayoutBlock keeps track of them through |gPositionedDescendantsMap| (see
// LayoutBlock.cpp).
// Note that this is a design decision made in Blink that doesn't reflect CSS:
// CSS allows relatively positioned inlines (LayoutInline) to be containing
// blocks, but they don't have the logic to handle out-of-flow positioned
// objects. This induces some complexity around choosing an enclosing
// LayoutBlock (for inserting out-of-flow objects during layout) vs the CSS
// containing block (for sizing, invalidation).
// A positioned object gets inserted into an enclosing LayoutBlock's positioned
// map. This is determined by LayoutObject::containingBlock().
// Care should be taken to handle out-of-flow positioned objects during
// certain tree walks (e.g. layout()). The rule is that anything that
// cares about containing blocks should skip the out-of-flow elements
// in the normal tree walk and do an optional follow-up pass for them
// using LayoutBlock::positionedObjects().
// Not doing so will result in passing the wrong containing
// block as tree walks will always pass the parent as the
// containing block.
// Sample code of how to handle positioned objects in LayoutBlock:
// for (LayoutObject* child = firstChild(); child; child = child->nextSibling())
// {
// if (child->isOutOfFlowPositioned())
// continue;
// // Handle normal flow children.
// ...
// }
// for (LayoutBox* positionedObject : positionedObjects()) {
// // Handle out-of-flow positioned objects.
// ...
// }
class CORE_EXPORT LayoutBlock : public LayoutBox {
explicit LayoutBlock(ContainerNode*);
~LayoutBlock() override;
LayoutObject* FirstChild() const {
DCHECK_EQ(Children(), VirtualChildren());
return Children()->FirstChild();
LayoutObject* LastChild() const {
DCHECK_EQ(Children(), VirtualChildren());
return Children()->LastChild();
// If you have a LayoutBlock, use firstChild or lastChild instead.
void SlowFirstChild() const = delete;
void SlowLastChild() const = delete;
const LayoutObjectChildList* Children() const { return &children_; }
LayoutObjectChildList* Children() { return &children_; }
// These two functions are overridden for inline-block.
LayoutUnit LineHeight(
bool first_line,
LinePositionMode = kPositionOnContainingLine) const final;
LayoutUnit BaselinePosition(
bool first_line,
LinePositionMode = kPositionOnContainingLine) const override;
bool UseLogicalBottomMarginEdgeForInlineBlockBaseline() const;
LayoutUnit MinLineHeightForReplacedObject(bool is_first_line,
LayoutUnit replaced_height) const;
virtual bool CreatesNewFormattingContext() const { return true; }
const char* GetName() const override;
// Insert a child correctly into the tree when |beforeDescendant| isn't a
// direct child of |this|. This happens e.g. when there's an anonymous block
// child of |this| and |beforeDescendant| has been reparented into that one.
// Such things are invisible to the DOM, and addChild() is typically called
// with the DOM tree (and not the layout tree) in mind.
void AddChildBeforeDescendant(LayoutObject* new_child,
LayoutObject* before_descendant);
void AddChild(LayoutObject* new_child,
LayoutObject* before_child = nullptr) override;
virtual void UpdateBlockLayout(bool relayout_children);
void InsertPositionedObject(LayoutBox*);
static void RemovePositionedObject(LayoutBox*);
void RemovePositionedObjects(LayoutObject*,
ContainingBlockState = kSameContainingBlock);
TrackedLayoutBoxListHashSet* PositionedObjects() const {
return HasPositionedObjects() ? PositionedObjectsInternal() : nullptr;
bool HasPositionedObjects() const {
DCHECK(has_positioned_objects_ ? (PositionedObjectsInternal() &&
: !PositionedObjectsInternal());
return has_positioned_objects_;
void AddPercentHeightDescendant(LayoutBox*);
void RemovePercentHeightDescendant(LayoutBox*);
bool HasPercentHeightDescendant(LayoutBox* o) const {
return HasPercentHeightDescendants() &&
TrackedLayoutBoxListHashSet* PercentHeightDescendants() const {
return HasPercentHeightDescendants() ? PercentHeightDescendantsInternal()
: nullptr;
bool HasPercentHeightDescendants() const {
? (PercentHeightDescendantsInternal() &&
: !PercentHeightDescendantsInternal());
return has_percent_height_descendants_;
void NotifyScrollbarThicknessChanged() {
width_available_to_children_changed_ = true;
void SetHasMarkupTruncation(bool b) { has_markup_truncation_ = b; }
bool HasMarkupTruncation() const { return has_markup_truncation_; }
void SetHasMarginBeforeQuirk(bool b) { has_margin_before_quirk_ = b; }
void SetHasMarginAfterQuirk(bool b) { has_margin_after_quirk_ = b; }
bool HasMarginBeforeQuirk() const { return has_margin_before_quirk_; }
bool HasMarginAfterQuirk() const { return has_margin_after_quirk_; }
bool HasMarginBeforeQuirk(const LayoutBox* child) const;
bool HasMarginAfterQuirk(const LayoutBox* child) const;
void MarkPositionedObjectsForLayout();
LayoutUnit TextIndentOffset() const;
PositionWithAffinity PositionForPoint(const LayoutPoint&) override;
LayoutUnit BlockDirectionOffset(const LayoutSize& offset_from_block) const;
LayoutUnit InlineDirectionOffset(const LayoutSize& offset_from_block) const;
static LayoutBlock* CreateAnonymousWithParentAndDisplay(
const LayoutObject*,
EDisplay = EDisplay::kBlock);
LayoutBlock* CreateAnonymousBlock(EDisplay display = EDisplay::kBlock) const {
return CreateAnonymousWithParentAndDisplay(this, display);
LayoutBox* CreateAnonymousBoxWithSameTypeAs(
const LayoutObject* parent) const override;
// Accessors for logical width/height and margins in the containing block's
// block-flow direction.
LayoutUnit LogicalWidthForChild(const LayoutBox& child) const {
return LogicalWidthForChildSize(child.Size());
LayoutUnit LogicalWidthForChildSize(LayoutSize child_size) const {
return IsHorizontalWritingMode() ? child_size.Width() : child_size.Height();
LayoutUnit LogicalHeightForChild(const LayoutBox& child) const {
return IsHorizontalWritingMode() ? child.Size().Height()
: child.Size().Width();
LayoutSize LogicalSizeForChild(const LayoutBox& child) const {
return IsHorizontalWritingMode() ? child.Size()
: child.Size().TransposedSize();
LayoutUnit LogicalTopForChild(const LayoutBox& child) const {
return IsHorizontalWritingMode() ? child.Location().Y()
: child.Location().X();
MarginBeforeForChild(const LayoutBoxModelObject& child) const {
return child.MarginBefore(Style());
MarginAfterForChild(const LayoutBoxModelObject& child) const {
return child.MarginAfter(Style());
MarginStartForChild(const LayoutBoxModelObject& child) const {
return child.MarginStart(Style());
LayoutUnit MarginEndForChild(const LayoutBoxModelObject& child) const {
return child.MarginEnd(Style());
void SetMarginStartForChild(LayoutBox& child, LayoutUnit value) const {
child.SetMarginStart(value, Style());
void SetMarginEndForChild(LayoutBox& child, LayoutUnit value) const {
child.SetMarginEnd(value, Style());
void SetMarginBeforeForChild(LayoutBox& child, LayoutUnit value) const {
child.SetMarginBefore(value, Style());
void SetMarginAfterForChild(LayoutBox& child, LayoutUnit value) const {
child.SetMarginAfter(value, Style());
LayoutUnit CollapsedMarginBeforeForChild(const LayoutBox& child) const;
LayoutUnit CollapsedMarginAfterForChild(const LayoutBox& child) const;
enum ScrollbarChangeContext { kStyleChange, kLayout };
virtual void ScrollbarsChanged(bool horizontal_scrollbar_changed,
bool vertical_scrollbar_changed,
ScrollbarChangeContext = kLayout);
LayoutUnit AvailableLogicalWidthForContent() const {
return (LogicalRightOffsetForContent() - LogicalLeftOffsetForContent())
DISABLE_CFI_PERF LayoutUnit LogicalLeftOffsetForContent() const {
return IsHorizontalWritingMode() ? BorderLeft() + PaddingLeft()
: BorderTop() + PaddingTop();
LayoutUnit LogicalRightOffsetForContent() const {
return LogicalLeftOffsetForContent() + AvailableLogicalWidth();
LayoutUnit StartOffsetForContent() const {
return Style()->IsLeftToRightDirection()
? LogicalLeftOffsetForContent()
: LogicalWidth() - LogicalRightOffsetForContent();
LayoutUnit EndOffsetForContent() const {
return !Style()->IsLeftToRightDirection()
? LogicalLeftOffsetForContent()
: LogicalWidth() - LogicalRightOffsetForContent();
virtual LayoutUnit LogicalLeftSelectionOffset(const LayoutBlock* root_block,
LayoutUnit position) const;
virtual LayoutUnit LogicalRightSelectionOffset(const LayoutBlock* root_block,
LayoutUnit position) const;
void CheckPositionedObjectsNeedLayout();
LayoutUnit AvailableLogicalHeightForPercentageComputation() const;
bool HasDefiniteLogicalHeight() const;
bool RecalcNormalFlowChildOverflowIfNeeded(LayoutObject*);
bool RecalcPositionedDescendantsOverflowAfterStyleChange();
bool RecalcSelfOverflowAfterStyleChange();
bool RecalcChildOverflowAfterStyleChange();
bool RecalcOverflowAfterStyleChange() override;
// An example explaining layout tree structure about first-line style:
// <style>
// #enclosingFirstLineStyleBlock::first-line { ... }
// </style>
// <div id="enclosingFirstLineStyleBlock">
// <div>
// <div id="nearestInnerBlockWithFirstLine">
// [<span>]first line text[</span>]
// </div>
// </div>
// </div>
// Returns the nearest enclosing block (including this block) that contributes
// a first-line style to our first line.
const LayoutBlock* EnclosingFirstLineStyleBlock() const;
// Returns this block or the nearest inner block containing the actual first
// line.
LayoutBlockFlow* NearestInnerBlockWithFirstLine();
void WillBeDestroyed() override;
void DirtyForLayoutFromPercentageHeightDescendants(SubtreeLayoutScope&);
void UpdateLayout() override;
enum PositionedLayoutBehavior {
virtual void LayoutPositionedObjects(
bool relayout_children,
PositionedLayoutBehavior = kDefaultLayout);
void LayoutPositionedObject(LayoutBox*,
bool relayout_children,
PositionedLayoutBehavior info);
void MarkFixedPositionObjectForLayoutIfNeeded(LayoutObject* child,
LayoutUnit MarginIntrinsicLogicalWidthForChild(const LayoutBox& child) const;
LayoutUnit BeforeMarginInLineDirection(LineDirectionMode) const;
void Paint(const PaintInfo&, const LayoutPoint&) const override;
virtual void PaintObject(const PaintInfo&, const LayoutPoint&) const;
virtual void PaintChildren(const PaintInfo&, const LayoutPoint&) const;
void UpdateAfterLayout() override;
virtual void AdjustInlineDirectionLineBounds(
unsigned /* expansionOpportunityCount */,
LayoutUnit& /* logicalLeft */,
LayoutUnit& /* logicalWidth */) const {}
void ComputeIntrinsicLogicalWidths(
LayoutUnit& min_logical_width,
LayoutUnit& max_logical_width) const override;
void ComputePreferredLogicalWidths() override;
void ComputeChildPreferredLogicalWidths(
LayoutObject& child,
LayoutUnit& min_preferred_logical_width,
LayoutUnit& max_preferred_logical_width) const;
LayoutUnit FirstLineBoxBaseline() const override;
LayoutUnit InlineBlockBaseline(LineDirectionMode) const override;
// This function disables the 'overflow' check in inlineBlockBaseline.
// For 'inline-block', CSS says that the baseline is the bottom margin edge
// if 'overflow' is not visible. But some descendant classes want to ignore
// this condition.
virtual bool ShouldIgnoreOverflowPropertyForInlineBlockBaseline() const {
return false;
bool HitTestOverflowControl(HitTestResult&,
const HitTestLocation&,
const LayoutPoint& adjusted_location) override;
bool HitTestChildren(HitTestResult&,
const HitTestLocation& location_in_container,
const LayoutPoint& accumulated_offset,
HitTestAction) override;
void StyleWillChange(StyleDifference,
const ComputedStyle& new_style) override;
void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override;
void UpdateFromStyle() override;
// Returns true if non-visible overflow should be respected. Otherwise
// hasOverflowClip() will be false and we won't create scrollable area for
// this object even if overflow is non-visible.
virtual bool AllowsOverflowClip() const;
virtual bool HasLineIfEmpty() const;
bool SimplifiedLayout();
virtual void SimplifiedNormalFlowLayout();
virtual void ComputeOverflow(LayoutUnit old_client_after_edge,
bool recompute_floats = false);
virtual void AddOverflowFromChildren();
void AddOverflowFromPositionedObjects();
void AddOverflowFromBlockChildren();
void AddVisualOverflowFromTheme();
void AddOutlineRects(Vector<LayoutRect>&,
const LayoutPoint& additional_offset,
IncludeBlockVisualOverflowOrNot) const override;
void UpdateBlockChildDirtyBitsBeforeLayout(bool relayout_children,
// TODO(jchaffraix): We should rename this function as inline-flex and
// inline-grid as also covered.
// Alternatively it should be removed as we clarify the meaning of
// isAtomicInlineLevel to imply isInline.
bool IsInlineBlockOrInlineTable() const final {
return IsInline() && IsAtomicInlineLevel();
bool NeedsPreferredWidthsRecalculation() const override;
LayoutObjectChildList* VirtualChildren() final { return Children(); }
const LayoutObjectChildList* VirtualChildren() const final {
return Children();
bool IsLayoutBlock() const final { return true; }
virtual void RemoveLeftoverAnonymousBlock(LayoutBlock* child);
TrackedLayoutBoxListHashSet* PositionedObjectsInternal() const;
TrackedLayoutBoxListHashSet* PercentHeightDescendantsInternal() const;
// Returns true if the positioned movement-only layout succeeded.
bool TryLayoutDoingPositionedMovementOnly();
bool IsInSelfHitTestingPhase(HitTestAction hit_test_action) const final {
return hit_test_action == kHitTestBlockBackground ||
hit_test_action == kHitTestChildBlockBackground;
bool IsPointInOverflowControl(HitTestResult&,
const LayoutPoint& location_in_container,
const LayoutPoint& accumulated_offset) const;
void ComputeBlockPreferredLogicalWidths(LayoutUnit& min_logical_width,
LayoutUnit& max_logical_width) const;
bool ShouldPaintCursorCaret() const;
bool ShouldPaintDragCaret() const;
bool ShouldPaintCarets() const {
return ShouldPaintCursorCaret() || ShouldPaintDragCaret();
PaintInvalidationReason InvalidatePaint(
const PaintInvalidatorContext&) const override;
void ClearPreviousVisualRects() override;
LayoutRect LocalCaretRect(
const InlineBox*,
int caret_offset,
LayoutUnit* extra_width_to_end_of_line = nullptr) const final;
bool IsInlineBoxWrapperActuallyChild() const;
Position PositionForBox(InlineBox*, bool start = true) const;
// End helper functions and structs used by layoutBlockChildren.
void RemoveFromGlobalMaps();
bool WidthAvailableToChildrenHasChanged();
// Paginated content inside this block was laid out.
// |logicalBottomOffsetAfterPagination| is the logical bottom offset of the
// child content after applying any forced or unforced breaks as needed.
void PaginatedContentWasLaidOut(
LayoutUnit logical_bottom_offset_after_pagination);
// Adjust from painting offsets to the local coords of this layoutObject
void OffsetForContents(LayoutPoint&) const;
PositionWithAffinity PositionForPointRespectingEditingBoundaries(
LineLayoutBox child,
const LayoutPoint& point_in_parent_coordinates);
PositionWithAffinity PositionForPointIfOutsideAtomicInlineLevel(
const LayoutPoint&);
virtual bool UpdateLogicalWidthAndColumnWidth();
LayoutObjectChildList children_;
has_margin_before_quirk_ : 1; // Note these quirk values can't be put
// in LayoutBlockRareData since they are
// set too frequently.
unsigned has_margin_after_quirk_ : 1;
unsigned being_destroyed_ : 1;
unsigned has_markup_truncation_ : 1;
unsigned width_available_to_children_changed_ : 1;
unsigned height_available_to_children_changed_ : 1;
unsigned is_self_collapsing_ : 1; // True if margin-before and margin-after
// are adjoining.
unsigned descendants_with_floats_marked_for_layout_ : 1;
unsigned has_positioned_objects_ : 1;
unsigned has_percent_height_descendants_ : 1;
// When an object ceases to establish a fragmentation context (e.g. the
// LayoutView when we're no longer printing), we need a deep layout
// afterwards, to clear all pagination struts. Likewise, when an object
// becomes fragmented, we need to re-lay out the entire subtree. There might
// be forced breaks somewhere in there that we suddenly have to pay attention
// to, for all we know.
unsigned pagination_state_changed_ : 1;
// FIXME: This is temporary as we move code that accesses block flow
// member variables out of LayoutBlock and into LayoutBlockFlow.
friend class LayoutBlockFlow;
// This is necessary for now for interoperability between the old and new
// layout code. Primarily for calling layoutPositionedObjects at the moment.
friend class NGBlockNode;
// TODO(loonybear): Temporary in order to ensure compatibility with existing
// layout test results.
virtual void AdjustChildDebugRect(LayoutRect&) const {}
} // namespace blink
#endif // LayoutBlock_h