blob: 87f90b9a69e59e679dd6206d51ca53e48ea8c4c3 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_PHYSICAL_BOX_FRAGMENT_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_PHYSICAL_BOX_FRAGMENT_H_
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/layout/geometry/box_sides.h"
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h"
#include "third_party/blink/renderer/core/layout/ng/mathml/ng_mathml_paint_info.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/table/ng_table_borders.h"
#include "third_party/blink/renderer/core/layout/ng/table/ng_table_fragment_data.h"
#include "third_party/blink/renderer/platform/graphics/overlay_scrollbar_clip_behavior.h"
#include "third_party/blink/renderer/platform/wtf/casting.h"
namespace blink {
class NGBoxFragmentBuilder;
enum class NGOutlineType;
class CORE_EXPORT NGPhysicalBoxFragment final
: public NGPhysicalContainerFragment {
public:
static scoped_refptr<const NGPhysicalBoxFragment> Create(
NGBoxFragmentBuilder* builder,
WritingMode block_or_line_writing_mode);
// Creates a copy of |other| but uses the "post-layout" fragments to ensure
// fragment-tree consistency.
static scoped_refptr<const NGPhysicalBoxFragment>
CloneWithPostLayoutFragments(const NGPhysicalBoxFragment& other,
const base::Optional<PhysicalRect>
updated_layout_overflow = base::nullopt);
using MulticolCollection = NGContainerFragmentBuilder::MulticolCollection;
using PassKey = base::PassKey<NGPhysicalBoxFragment>;
NGPhysicalBoxFragment(PassKey,
NGBoxFragmentBuilder* builder,
bool has_layout_overflow,
const PhysicalRect& layout_overflow,
bool has_borders,
const NGPhysicalBoxStrut& borders,
bool has_padding,
const NGPhysicalBoxStrut& padding,
const base::Optional<PhysicalRect>& inflow_bounds,
bool has_rare_data,
WritingMode block_or_line_writing_mode);
NGPhysicalBoxFragment(PassKey,
const NGPhysicalBoxFragment& other,
bool has_layout_overflow,
const PhysicalRect& layout_overflow,
bool recalculate_layout_overflow);
scoped_refptr<const NGLayoutResult> CloneAsHiddenForPaint() const;
~NGPhysicalBoxFragment() {
if (has_fragment_items_)
ComputeItemsAddress()->~NGFragmentItems();
if (has_rare_data_)
ComputeRareDataAddress()->~RareData();
for (const NGLink& child : Children())
child.fragment->Release();
}
const NGPhysicalBoxFragment* PostLayout() const;
// Returns |NGFragmentItems| if this fragment has one.
bool HasItems() const { return has_fragment_items_; }
const NGFragmentItems* Items() const {
return has_fragment_items_ ? ComputeItemsAddress() : nullptr;
}
base::Optional<LayoutUnit> Baseline() const {
if (has_baseline_)
return baseline_;
return base::nullopt;
}
base::Optional<LayoutUnit> LastBaseline() const {
if (has_last_baseline_)
return last_baseline_;
return base::nullopt;
}
PhysicalRect TableGridRect() const {
return ComputeRareDataAddress()->table_grid_rect;
}
const NGTableFragmentData::ColumnGeometries* TableColumnGeometries() const {
return &ComputeRareDataAddress()->table_column_geometries;
}
const NGTableBorders* TableCollapsedBorders() const {
return ComputeRareDataAddress()->table_collapsed_borders.get();
}
const NGTableFragmentData::CollapsedBordersGeometry*
TableCollapsedBordersGeometry() const {
return ComputeRareDataAddress()->table_collapsed_borders_geometry.get();
}
wtf_size_t TableCellColumnIndex() const {
return ComputeRareDataAddress()->table_cell_column_index;
}
// Returns the layout-overflow for this fragment.
const PhysicalRect LayoutOverflow() const {
if (is_legacy_layout_root_)
return To<LayoutBox>(GetLayoutObject())->PhysicalLayoutOverflowRect();
if (!has_layout_overflow_)
return {{}, Size()};
return *ComputeLayoutOverflowAddress();
}
const NGPhysicalBoxStrut Borders() const {
if (!has_borders_)
return NGPhysicalBoxStrut();
return *ComputeBordersAddress();
}
const NGPhysicalBoxStrut Padding() const {
if (!has_padding_)
return NGPhysicalBoxStrut();
return *ComputePaddingAddress();
}
// Returns the bounds of any inflow children for this fragment (specifically
// no out-of-flow positioned objects). This will return |base::nullopt| if:
// - The fragment is *not* a scroll container.
// - The scroll container contains no inflow children.
const base::Optional<PhysicalRect> InflowBounds() const {
if (!has_inflow_bounds_)
return base::nullopt;
return *ComputeInflowBoundsAddress();
}
bool HasOutOfFlowPositionedFragmentainerDescendants() const {
if (!has_rare_data_)
return false;
return !ComputeRareDataAddress()
->oof_positioned_fragmentainer_descendants.IsEmpty();
}
base::span<NGPhysicalOutOfFlowPositionedNode>
OutOfFlowPositionedFragmentainerDescendants() const {
if (!has_rare_data_)
return base::span<NGPhysicalOutOfFlowPositionedNode>();
Vector<NGPhysicalOutOfFlowPositionedNode>& descendants =
const_cast<Vector<NGPhysicalOutOfFlowPositionedNode>&>(
ComputeRareDataAddress()->oof_positioned_fragmentainer_descendants);
return {descendants.data(), descendants.size()};
}
bool HasMulticolsWithPendingOOFs() const {
if (!has_rare_data_)
return false;
return !ComputeRareDataAddress()->multicols_with_pending_oofs.IsEmpty();
}
MulticolCollection MulticolsWithPendingOOFs() const {
if (!has_rare_data_)
return MulticolCollection();
return const_cast<MulticolCollection&>(
ComputeRareDataAddress()->multicols_with_pending_oofs);
}
NGPixelSnappedPhysicalBoxStrut PixelSnappedPadding() const {
if (!has_padding_)
return NGPixelSnappedPhysicalBoxStrut();
return ComputePaddingAddress()->SnapToDevicePixels();
}
// Return true if this is either a container that establishes an inline
// formatting context, or if it's non-atomic inline content participating in
// one. Empty blocks don't establish an inline formatting context.
//
// The return value from this method is undefined and irrelevant if the object
// establishes a different type of formatting context than block/inline, such
// as table or flexbox.
//
// Example:
// <div> <!-- false -->
// <div> <!-- true -->
// <div style="float:left;"></div> <!-- false -->
// <div style="float:left;"> <!-- true -->
// xxx <!-- true -->
// </div>
// <div style="float:left;"> <!-- false -->
// <div style="float:left;"></div> <!-- false -->
// </div>
// <span> <!-- true -->
// xxx <!-- true -->
// <span style="display:inline-block;"> <!-- false -->
// <div></div> <!-- false -->
// </span>
// <span style="display:inline-block;"> <!-- true -->
// xxx <!-- true -->
// </span>
// <span style="display:inline-flex;"> <!-- N/A -->
bool IsInlineFormattingContext() const {
return is_inline_formatting_context_;
}
// The |LayoutBox| whose |PhysicalFragments()| contains |this|. This is
// different from |GetLayoutObject()| if |this.IsColumnBox()|.
const LayoutBox* OwnerLayoutBox() const;
LayoutBox* MutableOwnerLayoutBox() const;
// Returns the offset in the |OwnerLayoutBox| coordinate system.
PhysicalOffset OffsetFromOwnerLayoutBox() const;
PhysicalRect ScrollableOverflow(TextHeightType height_type) const;
PhysicalRect ScrollableOverflowFromChildren(TextHeightType height_type) const;
OverflowClipAxes GetOverflowClipAxes() const {
if (const auto* layout_object = GetLayoutObject())
return layout_object->GetOverflowClipAxes();
return kNoOverflowClip;
}
// TODO(layout-dev): These three methods delegate to legacy layout for now,
// update them to use LayoutNG based overflow information from the fragment
// and change them to use NG geometry types once LayoutNG supports overflow.
PhysicalRect OverflowClipRect(
const PhysicalOffset& location,
OverlayScrollbarClipBehavior = kIgnoreOverlayScrollbarSize) const;
LayoutSize PixelSnappedScrolledContentOffset() const;
PhysicalSize ScrollSize() const;
// Compute visual overflow of this box in the local coordinate.
PhysicalRect ComputeSelfInkOverflow() const;
// |InkOverflow| includes self and contents ink overflow, unless it has
// clipping, in that case it includes self ink overflow only.
PhysicalRect InkOverflow() const;
// Contents ink overflow includes anything that would bleed out of the box and
// would be clipped by the overflow clip ('overflow' != visible). This
// corresponds to children that overflows their parent.
PhysicalRect ContentsInkOverflow() const;
// Fast check if |NodeAtPoint| may find a hit.
bool MayIntersect(const HitTestResult& result,
const HitTestLocation& hit_test_location,
const PhysicalOffset& accumulated_offset) const;
// In order to paint united outline rectangles, the "owner" fragment paints
// outlines for non-owner fragments.
bool IsOutlineOwner() const {
return !IsInlineBox() || InlineContainerFragmentIfOutlineOwner();
}
const NGPhysicalBoxFragment* InlineContainerFragmentIfOutlineOwner() const;
// Returns the |ComputedStyle| to use for painting outlines. When |this| is
// a block in a continuation-chain, it may need to paint outlines if its
// ancestor inline boxes in the DOM tree has outlines.
const ComputedStyle* StyleForContinuationOutline() const {
if (const auto* layout_object = GetLayoutObject())
return layout_object->StyleForContinuationOutline();
return nullptr;
}
// Fragment offset is this fragment's offset from parent.
// Needed to compensate for LayoutInline Legacy code offsets.
void AddSelfOutlineRects(const PhysicalOffset& additional_offset,
NGOutlineType include_block_overflows,
Vector<PhysicalRect>* outline_rects) const;
// Same as |AddSelfOutlineRects|, except when |this.IsInlineBox()|, in which
// case the coordinate system is relative to the inline formatting context.
void AddOutlineRects(const PhysicalOffset& additional_offset,
NGOutlineType include_block_overflows,
Vector<PhysicalRect>* outline_rects) const;
PositionWithAffinity PositionForPoint(PhysicalOffset) const;
UBiDiLevel BidiLevel() const;
PhysicalBoxSides SidesToInclude() const {
return PhysicalBoxSides(include_border_top_, include_border_right_,
include_border_bottom_, include_border_left_);
}
NGPixelSnappedPhysicalBoxStrut BorderWidths() const;
// Return true if this is the first fragment generated from a node.
bool IsFirstForNode() const { return is_first_for_node_; }
// Returns true if we have a descendant within this formatting context, which
// is potentially above our block-start edge.
bool MayHaveDescendantAboveBlockStart() const {
return may_have_descendant_above_block_start_;
}
#if DCHECK_IS_ON()
void CheckSameForSimplifiedLayout(const NGPhysicalBoxFragment&,
bool check_same_block_size) const;
#endif
bool HasExtraMathMLPainting() const {
if (IsMathMLFraction())
return true;
if (has_rare_data_ && ComputeRareDataAddress()->mathml_paint_info)
return true;
return false;
}
const NGMathMLPaintInfo& GetMathMLPaintInfo() const {
return *ComputeRareDataAddress()->mathml_paint_info;
}
private:
static size_t ByteSize(wtf_size_t num_fragment_items,
wtf_size_t num_children,
bool has_layout_overflow,
bool has_borders,
bool has_padding,
bool has_inflow_bounds,
bool has_rare_data);
struct RareData {
RareData(const RareData&);
RareData(NGBoxFragmentBuilder*, PhysicalSize size);
Vector<NGPhysicalOutOfFlowPositionedNode>
oof_positioned_fragmentainer_descendants;
MulticolCollection multicols_with_pending_oofs;
const std::unique_ptr<const NGMathMLPaintInfo> mathml_paint_info;
// TablesNG rare data.
PhysicalRect table_grid_rect;
NGTableFragmentData::ColumnGeometries table_column_geometries;
scoped_refptr<const NGTableBorders> table_collapsed_borders;
std::unique_ptr<NGTableFragmentData::CollapsedBordersGeometry>
table_collapsed_borders_geometry;
wtf_size_t table_cell_column_index;
};
const NGFragmentItems* ComputeItemsAddress() const {
DCHECK(has_fragment_items_ || has_layout_overflow_ || has_borders_ ||
has_padding_ || has_inflow_bounds_ || has_rare_data_);
const NGLink* children_end = children_ + Children().size();
return reinterpret_cast<const NGFragmentItems*>(children_end);
}
const PhysicalRect* ComputeLayoutOverflowAddress() const {
DCHECK(has_layout_overflow_ || has_borders_ || has_padding_ ||
has_inflow_bounds_ || has_rare_data_);
const NGFragmentItems* items = ComputeItemsAddress();
if (!has_fragment_items_)
return reinterpret_cast<const PhysicalRect*>(items);
return reinterpret_cast<const PhysicalRect*>(
reinterpret_cast<const uint8_t*>(items) + items->ByteSize());
}
const NGPhysicalBoxStrut* ComputeBordersAddress() const {
DCHECK(has_borders_ || has_padding_ || has_inflow_bounds_ ||
has_rare_data_);
const PhysicalRect* address = ComputeLayoutOverflowAddress();
return has_layout_overflow_
? reinterpret_cast<const NGPhysicalBoxStrut*>(address + 1)
: reinterpret_cast<const NGPhysicalBoxStrut*>(address);
}
const NGPhysicalBoxStrut* ComputePaddingAddress() const {
DCHECK(has_padding_ || has_inflow_bounds_ || has_rare_data_);
const NGPhysicalBoxStrut* address = ComputeBordersAddress();
return has_borders_ ? address + 1 : address;
}
const PhysicalRect* ComputeInflowBoundsAddress() const {
DCHECK(has_inflow_bounds_ || has_rare_data_);
NGPhysicalBoxStrut* address =
const_cast<NGPhysicalBoxStrut*>(ComputePaddingAddress());
return has_padding_ ? reinterpret_cast<PhysicalRect*>(address + 1)
: reinterpret_cast<PhysicalRect*>(address);
}
const RareData* ComputeRareDataAddress() const {
DCHECK(has_rare_data_);
PhysicalRect* address =
const_cast<PhysicalRect*>(ComputeInflowBoundsAddress());
return has_inflow_bounds_ ? reinterpret_cast<RareData*>(address + 1)
: reinterpret_cast<RareData*>(address);
}
void AddOutlineRects(const PhysicalOffset& additional_offset,
NGOutlineType include_block_overflows,
bool inline_container_relative,
Vector<PhysicalRect>* outline_rects) const;
void AddOutlineRectsForInlineBox(PhysicalOffset additional_offset,
NGOutlineType include_block_overflows,
bool inline_container_relative,
Vector<PhysicalRect>* outline_rects) const;
#if DCHECK_IS_ON()
void CheckIntegrity() const;
#endif
LayoutUnit baseline_;
LayoutUnit last_baseline_;
NGLink children_[];
// fragment_items, borders, padding, and rare_data are after |children_| if
// they are not empty/initial.
};
template <>
struct DowncastTraits<NGPhysicalBoxFragment> {
static bool AllowFrom(const NGPhysicalFragment& fragment) {
return fragment.Type() == NGPhysicalFragment::kFragmentBox;
}
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_PHYSICAL_BOX_FRAGMENT_H_