blob: 02e9f218dba2a578c16087a4519286fa7fabfddd [file] [log] [blame]
/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2007 David Smith (catfish.man@gmail.com)
* Copyright (C) 2003, 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
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* 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 THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_BLOCK_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_BLOCK_H_
#include "base/check_op.h"
#include "base/dcheck_is_on.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/layout/layout_box.h"
#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
#include "third_party/blink/renderer/platform/wtf/casting.h"
#include "third_party/blink/renderer/platform/wtf/linked_hash_set.h"
namespace blink {
class BlockNode;
struct PaintInfo;
typedef HeapLinkedHashSet<Member<LayoutBox>> TrackedLayoutBoxLinkedHashSet;
typedef HeapHashMap<WeakMember<const LayoutBlock>,
Member<TrackedLayoutBoxLinkedHashSet>>
TrackedDescendantsMap;
// LayoutBlock is the class that is used by any LayoutObject
// that is a containing block.
// http://www.w3.org/TR/CSS2/visuren.html#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 |GetPositionedDescendantsMap()| (see
// layout_block.cc).
// 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).
//
//
// ***** WHO LAYS OUT OUT-OF-FLOW POSITIONED OBJECTS? *****
// A positioned object gets inserted into an enclosing LayoutBlock's positioned
// map. This is determined by LayoutObject::ContainingBlock().
//
//
// ***** HANDLING OUT-OF-FLOW POSITIONED OBJECTS *****
// 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* positioned_object : PositionedObjects()) {
// // Handle out-of-flow positioned objects.
// ...
// }
class CORE_EXPORT LayoutBlock : public LayoutBox {
protected:
explicit LayoutBlock(ContainerNode*);
public:
void Trace(Visitor*) const override;
bool IsLayoutNGObject() const override;
LayoutObject* FirstChild() const {
NOT_DESTROYED();
DCHECK_EQ(Children(), VirtualChildren());
return Children()->FirstChild();
}
LayoutObject* LastChild() const {
NOT_DESTROYED();
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 {
NOT_DESTROYED();
return &children_;
}
LayoutObjectChildList* Children() {
NOT_DESTROYED();
return &children_;
}
// These two functions are overridden for inline-block.
LayoutUnit FirstLineHeight() const override;
bool UseLogicalBottomMarginEdgeForInlineBlockBaseline() const;
const char* GetName() const override;
protected:
// Insert a child correctly into the tree when |before_descendant| isn't a
// direct child of |this|. This happens e.g. when there's an anonymous block
// child of |this| and |before_descendant| 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);
public:
void AddChild(LayoutObject* new_child,
LayoutObject* before_child = nullptr) override;
void RemovePositionedObjects(LayoutObject*);
void AddSvgTextDescendant(LayoutBox& svg_text);
void RemoveSvgTextDescendant(LayoutBox& svg_text);
LayoutUnit TextIndentOffset() const;
PositionWithAffinity PositionForPoint(const PhysicalOffset&) const override;
static LayoutBlock* CreateAnonymousWithParentAndDisplay(
const LayoutObject*,
EDisplay = EDisplay::kBlock);
LayoutBlock* CreateAnonymousBlock(EDisplay display = EDisplay::kBlock) const {
NOT_DESTROYED();
return CreateAnonymousWithParentAndDisplay(this, display);
}
LayoutBox* CreateAnonymousBoxWithSameTypeAs(
const LayoutObject* parent) const override;
public:
RecalcScrollableOverflowResult RecalcScrollableOverflow() override;
void RecalcVisualOverflow() 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>
// Return the parent LayoutObject if it can contribute to our ::first-line
// style.
const LayoutBlock* FirstLineStyleParentBlock() const;
// Returns this block or the nearest inner block containing the actual first
// line.
LayoutBlockFlow* NearestInnerBlockWithFirstLine();
protected:
void WillBeDestroyed() override;
public:
void Paint(const PaintInfo&) const override;
virtual bool HasLineIfEmpty() const;
// Returns baseline offset if we can get |SimpleFontData| from primary font.
// Or returns no value if we can't get font data.
std::optional<LayoutUnit> BaselineForEmptyLine() const;
bool NodeAtPoint(HitTestResult&,
const HitTestLocation&,
const PhysicalOffset& accumulated_offset,
HitTestPhase) override;
protected:
bool HitTestChildren(HitTestResult&,
const HitTestLocation&,
const PhysicalOffset& accumulated_offset,
HitTestPhase) override;
void StyleWillChange(StyleDifference,
const ComputedStyle& new_style) override;
void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override;
bool RespectsCSSOverflow() const override;
protected:
void AddOutlineRects(OutlineRectCollector&,
OutlineInfo*,
const PhysicalOffset& additional_offset,
OutlineType) const override;
bool IsInSelfHitTestingPhase(HitTestPhase phase) const final {
NOT_DESTROYED();
return phase == HitTestPhase::kSelfBlockBackground;
}
private:
LayoutObjectChildList* VirtualChildren() final {
NOT_DESTROYED();
return Children();
}
const LayoutObjectChildList* VirtualChildren() const final {
NOT_DESTROYED();
return Children();
}
bool IsLayoutBlock() const final {
NOT_DESTROYED();
return true;
}
virtual void RemoveLeftoverAnonymousBlock(LayoutBlock* child);
protected:
void InvalidatePaint(const PaintInvalidatorContext&) const override;
void ImageChanged(WrappedImagePtr, CanDeferInvalidation) override;
private:
PhysicalRect LocalCaretRect(
int caret_offset,
LayoutUnit* extra_width_to_end_of_line = nullptr) const final;
bool IsInlineBoxWrapperActuallyChild() const;
// End helper functions and structs used by layoutBlockChildren.
void RemoveFromGlobalMaps();
protected:
PositionWithAffinity PositionForPointIfOutsideAtomicInlineLevel(
const PhysicalOffset&) const;
LayoutObjectChildList children_;
unsigned has_svg_text_descendants_ : 1;
unsigned may_be_non_contiguous_ifc_ : 1 = false;
// 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 BlockNode;
};
template <>
struct DowncastTraits<LayoutBlock> {
static bool AllowFrom(const LayoutObject& object) {
return object.IsLayoutBlock();
}
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_BLOCK_H_