blob: 81e1c35d09000a5b491bdb55cfa7944701e72a5e [file] [log] [blame]
// Copyright 2017 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.
#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h"
#include "third_party/blink/renderer/core/editing/editing_utilities.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h"
#include "third_party/blink/renderer/core/layout/ng/ng_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_relative_utils.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
namespace blink {
namespace {
struct SameSizeAsNGPhysicalLineBoxFragment : NGPhysicalContainerFragment {
NGLineHeightMetrics metrics;
};
static_assert(sizeof(NGPhysicalLineBoxFragment) ==
sizeof(SameSizeAsNGPhysicalLineBoxFragment),
"NGPhysicalLineBoxFragment should stay small");
} // namespace
scoped_refptr<const NGPhysicalLineBoxFragment>
NGPhysicalLineBoxFragment::Create(NGLineBoxFragmentBuilder* builder) {
// We store the children list inline in the fragment as a flexible
// array. Therefore, we need to make sure to allocate enough space for
// that array here, which requires a manual allocation + placement new.
// The initialization of the array is done by NGPhysicalContainerFragment;
// we pass the buffer as a constructor argument.
void* data = ::WTF::Partitions::FastMalloc(
sizeof(NGPhysicalLineBoxFragment) +
builder->children_.size() * sizeof(NGLink),
::WTF::GetStringWithTypeName<NGPhysicalLineBoxFragment>());
new (data) NGPhysicalLineBoxFragment(PassKey(), builder);
return base::AdoptRef(static_cast<NGPhysicalLineBoxFragment*>(data));
}
NGPhysicalLineBoxFragment::NGPhysicalLineBoxFragment(
PassKey key,
NGLineBoxFragmentBuilder* builder)
: NGPhysicalContainerFragment(builder,
builder->GetWritingMode(),
children_,
kFragmentLineBox,
builder->line_box_type_),
metrics_(builder->metrics_) {
// A line box must have a metrics unless it's an empty line box.
DCHECK(!metrics_.IsEmpty() || IsEmptyLineBox());
base_or_resolved_direction_ = static_cast<unsigned>(builder->base_direction_);
has_hanging_ = builder->hang_inline_size_ != 0;
has_propagated_descendants_ = has_floating_descendants_for_paint_ ||
HasOutOfFlowPositionedDescendants() ||
builder->unpositioned_list_marker_;
}
NGLineHeightMetrics NGPhysicalLineBoxFragment::BaselineMetrics() const {
// TODO(kojii): Computing other baseline types than the used one is not
// implemented yet.
// TODO(kojii): We might need locale/script to look up OpenType BASE table.
return metrics_;
}
namespace {
// Chop the hanging part from scrollable overflow. Children overflow in inline
// direction should hang, which should not cause scroll.
// TODO(kojii): Should move to text fragment to make this more accurate.
inline void AdjustScrollableOverflowForHanging(
const PhysicalRect& rect,
const WritingMode container_writing_mode,
PhysicalRect* overflow) {
if (IsHorizontalWritingMode(container_writing_mode)) {
if (overflow->offset.left < rect.offset.left)
overflow->offset.left = rect.offset.left;
if (overflow->Right() > rect.Right())
overflow->ShiftRightEdgeTo(rect.Right());
} else {
if (overflow->offset.top < rect.offset.top)
overflow->offset.top = rect.offset.top;
if (overflow->Bottom() > rect.Bottom())
overflow->ShiftBottomEdgeTo(rect.Bottom());
}
}
// Include the inline-size of the line-box in the overflow.
inline void AddInlineSizeToOverflow(const PhysicalRect& rect,
const WritingMode container_writing_mode,
PhysicalRect* overflow) {
PhysicalRect inline_rect;
inline_rect.offset = rect.offset;
if (IsHorizontalWritingMode(container_writing_mode))
inline_rect.size.width = rect.size.width;
else
inline_rect.size.height = rect.size.height;
overflow->UniteEvenIfEmpty(inline_rect);
}
} // namespace
PhysicalRect NGPhysicalLineBoxFragment::ScrollableOverflow(
const NGPhysicalBoxFragment& container,
const ComputedStyle& container_style) const {
const WritingMode container_writing_mode = container_style.GetWritingMode();
const TextDirection container_direction = container_style.Direction();
PhysicalRect overflow;
for (const auto& child : Children()) {
PhysicalRect child_scroll_overflow =
child->ScrollableOverflowForPropagation(container);
child_scroll_overflow.offset += child.Offset();
if (UNLIKELY(has_hanging_ && !child->IsFloatingOrOutOfFlowPositioned())) {
AdjustScrollableOverflowForHanging(LocalRect(), container_writing_mode,
&child_scroll_overflow);
}
// For implementation reasons, text nodes inherit computed style from their
// container, including everything, also non-inherited properties. So, if
// the container has a relative offset, this will be falsely reflected on
// text children. We need to guard against this.
if (!child->IsText()) {
child_scroll_overflow.offset +=
ComputeRelativeOffset(child->Style(), container_writing_mode,
container_direction, container.Size());
}
overflow.Unite(child_scroll_overflow);
}
// Make sure we include the inline-size of the line-box in the overflow.
AddInlineSizeToOverflow(LocalRect(), container_writing_mode, &overflow);
return overflow;
}
PhysicalRect NGPhysicalLineBoxFragment::ScrollableOverflow(
const NGPhysicalBoxFragment& container,
const ComputedStyle& container_style,
const NGFragmentItem& child,
const NGInlineCursor& cursor) const {
DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
DCHECK_EQ(&child, cursor.CurrentItem());
DCHECK_EQ(child.LineBoxFragment(), this);
const WritingMode container_writing_mode = container_style.GetWritingMode();
const TextDirection container_direction = container_style.Direction();
PhysicalRect overflow;
for (NGInlineCursor descendants = cursor.CursorForDescendants();
descendants;) {
const NGFragmentItem* item = descendants.CurrentItem();
DCHECK(item);
if (item->IsText()) {
PhysicalRect child_scroll_overflow = item->RectInContainerBlock();
if (UNLIKELY(has_hanging_)) {
AdjustScrollableOverflowForHanging(child.RectInContainerBlock(),
container_writing_mode,
&child_scroll_overflow);
}
overflow.Unite(child_scroll_overflow);
descendants.MoveToNextSkippingChildren();
continue;
}
if (const NGPhysicalBoxFragment* child_box = item->BoxFragment()) {
PhysicalRect child_scroll_overflow =
child_box->ScrollableOverflowForPropagation(container);
child_scroll_overflow.offset += item->OffsetInContainerBlock();
child_scroll_overflow.offset +=
ComputeRelativeOffset(child_box->Style(), container_writing_mode,
container_direction, container.Size());
overflow.Unite(child_scroll_overflow);
descendants.MoveToNextSkippingChildren();
continue;
}
// Add all children of a culled inline box; i.e., an inline box without
// margin/border/padding etc.
DCHECK_EQ(item->Type(), NGFragmentItem::kBox);
descendants.MoveToNext();
}
// Make sure we include the inline-size of the line-box in the overflow.
AddInlineSizeToOverflow(child.RectInContainerBlock(), container_writing_mode,
&overflow);
return overflow;
}
bool NGPhysicalLineBoxFragment::HasSoftWrapToNextLine() const {
const auto& break_token = To<NGInlineBreakToken>(*BreakToken());
return !break_token.IsFinished() && !break_token.IsForcedBreak();
}
} // namespace blink