Commit 03b14220 authored by Koji Ishii's avatar Koji Ishii Committed by Commit Bot

[LayoutNG] Fix VisualRect for flipped block (vertical-rl)

This patch fixes VisualRect for flipped block (vertical-rl)
writing-mode.

LayoutObject::LocalVisualRect should be in flipped physical
coordinate system, but was not flipped in LayoutNG. This patch
fixed it to be flipped physical coordinate system.

In addition:
* LocalVisualRectFor() is moved to NGPaintFragment, by using
  NGPaintFragment::InlineFragmentsFor().
* PaintInvalidator::UpdateVisualRect() applies the same mapping
  to fragments as the LayoutObject. Before this patch computed
  the mapping from local and backing rect, but this method does
  not work in flipped writing-mode.

11 tests turn to pass. More tests were expected to pass, but it
turned out that there are more fixes needed for vertical-rl in
paint and a few others.

Bug: 636993
Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel;master.tryserver.chromium.linux:linux_layout_tests_layout_ng;master.tryserver.chromium.linux:linux_layout_tests_slimming_paint_v2
Change-Id: I3176a89bd96afa75ef024b1441d85fec5d30d75a
Reviewed-on: https://chromium-review.googlesource.com/960403Reviewed-by: default avatarEmil A Eklund <eae@chromium.org>
Commit-Queue: Emil A Eklund <eae@chromium.org>
Cr-Commit-Position: refs/heads/master@{#542898}
parent 05f356ec
......@@ -1235,9 +1235,9 @@ LayoutRect LayoutInline::AbsoluteVisualRect() const {
LayoutRect LayoutInline::LocalVisualRectIgnoringVisibility() const {
if (RuntimeEnabledFeatures::LayoutNGEnabled()) {
NGPhysicalOffsetRect visual_rect;
if (LayoutNGBlockFlow::LocalVisualRectFor(this, &visual_rect))
return visual_rect.ToLayoutRect();
LayoutRect visual_rect;
if (NGPaintFragment::FlippedLocalVisualRectFor(this, &visual_rect))
return visual_rect;
}
// If we don't create line boxes, we don't have any invalidations to do.
......
......@@ -1908,9 +1908,9 @@ LayoutRect LayoutText::VisualOverflowRect() const {
LayoutRect LayoutText::LocalVisualRectIgnoringVisibility() const {
if (RuntimeEnabledFeatures::LayoutNGEnabled()) {
NGPhysicalOffsetRect visual_rect;
if (LayoutNGBlockFlow::LocalVisualRectFor(this, &visual_rect))
return visual_rect.ToLayoutRect();
LayoutRect visual_rect;
if (NGPaintFragment::FlippedLocalVisualRectFor(this, &visual_rect))
return visual_rect;
}
return UnionRect(VisualOverflowRect(), LocalSelectionRect());
......
......@@ -236,24 +236,4 @@ void LayoutNGBlockFlow::UpdateOutOfFlowBlockLayout() {
DCHECK_EQ(fragment->Children()[0]->GetLayoutObject(), this);
}
bool LayoutNGBlockFlow::LocalVisualRectFor(const LayoutObject* layout_object,
NGPhysicalOffsetRect* visual_rect) {
DCHECK(layout_object &&
(layout_object->IsText() || layout_object->IsLayoutInline()));
DCHECK(visual_rect);
const NGPhysicalBoxFragment* box_fragment =
layout_object->EnclosingBlockFlowFragment();
// TODO(kojii): CurrentFragment isn't always available after layout clean.
// Investigate why.
if (!box_fragment)
return false;
auto children =
NGInlineFragmentTraversal::SelfFragmentsOf(*box_fragment, layout_object);
for (const auto& child : children) {
NGPhysicalOffsetRect child_visual_rect = child.fragment->SelfVisualRect();
visual_rect->Unite(child_visual_rect + child.offset_to_container_box);
}
return true;
}
} // namespace blink
......@@ -21,8 +21,6 @@ class CORE_EXPORT LayoutNGBlockFlow : public LayoutNGMixin<LayoutBlockFlow> {
const char* GetName() const override { return "LayoutNGBlockFlow"; }
static bool LocalVisualRectFor(const LayoutObject*, NGPhysicalOffsetRect*);
protected:
bool IsOfType(LayoutObjectType) const override;
......
......@@ -58,7 +58,8 @@ template <typename Rect, typename Point>
LayoutRect PaintInvalidator::MapLocalRectToVisualRectInBacking(
const LayoutObject& object,
const Rect& local_rect,
const PaintInvalidatorContext& context) {
const PaintInvalidatorContext& context,
bool disable_flip) {
DCHECK(context.NeedsVisualRectUpdate(object));
if (local_rect.IsEmpty())
return LayoutRect();
......@@ -71,7 +72,7 @@ LayoutRect PaintInvalidator::MapLocalRectToVisualRectInBacking(
// coordinates.
Rect rect = local_rect;
// Writing-mode flipping doesn't apply to non-root SVG.
if (!is_svg_child) {
if (!is_svg_child && !disable_flip) {
if (object.IsBox()) {
ToLayoutBox(object).FlipForWritingMode(rect);
} else if (!(context.subtree_flags &
......@@ -207,6 +208,21 @@ LayoutRect PaintInvalidator::ComputeVisualRectInBacking(
object, local_rect, context);
}
LayoutRect PaintInvalidator::ComputeVisualRectInBacking(
const NGPaintFragment& fragment,
const LayoutObject& object,
const PaintInvalidatorContext& context) {
const NGPhysicalFragment& physical_fragment = fragment.PhysicalFragment();
LayoutRect local_rect = physical_fragment.SelfVisualRect().ToLayoutRect();
bool disable_flip = true;
LayoutRect backing_rect =
MapLocalRectToVisualRectInBacking<LayoutRect, LayoutPoint>(
object, local_rect, context, disable_flip);
if (!object.IsBox())
backing_rect.Move(fragment.InlineOffsetToContainerBox().ToLayoutSize());
return backing_rect;
}
LayoutPoint PaintInvalidator::ComputeLocationInBacking(
const LayoutObject& object,
const PaintInvalidatorContext& context) {
......@@ -447,26 +463,12 @@ void PaintInvalidator::UpdateVisualRect(const LayoutObject& object,
// An inline LayoutObject can produce multiple NGPaintFragment. Compute
// VisualRect for each fragment from |new_visual_rect|.
auto fragments = NGPaintFragment::InlineFragmentsFor(&object);
if (!fragments.IsInLayoutNGInlineFormattingContext())
return;
// Compute the offset of |new_visual_rect| from the local coordinate.
LayoutRect local_object_rect = object.LocalVisualRect();
NGPhysicalOffset object_offset = NGPhysicalOffset(
new_visual_rect.Location() - local_object_rect.Location());
// Compute VisualRect for each fragment, by mapping each local VisualRect to
// the coordinate space of |new_visual_rect|.
// TODO(kojii): This logic needs to incorporate writing-mode.
for (NGPaintFragment* fragment : fragments) {
const NGPhysicalFragment& physical_fragment =
fragment->PhysicalFragment();
NGPhysicalOffsetRect fragment_rect = physical_fragment.SelfVisualRect();
fragment_rect.offset += object_offset;
// LayoutBox::LocalVisualRect() is in its local coordinate space, not in
// its inline formatting context, and that |object_offset| includes the
// offset to the inline container box.
if (!object.IsBox())
fragment_rect.offset += fragment->InlineOffsetToContainerBox();
fragment->SetVisualRect(fragment_rect.ToLayoutRect());
if (fragments.IsInLayoutNGInlineFormattingContext()) {
for (NGPaintFragment* fragment : fragments) {
LayoutRect fragment_visual_rect =
ComputeVisualRectInBacking(*fragment, object, context);
fragment->SetVisualRect(fragment_visual_rect);
}
}
}
}
......
......@@ -12,6 +12,7 @@
namespace blink {
class NGPaintFragment;
class PrePaintTreeWalk;
struct CORE_EXPORT PaintInvalidatorContext {
DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
......@@ -158,11 +159,16 @@ class PaintInvalidator {
static LayoutRect MapLocalRectToVisualRectInBacking(
const LayoutObject&,
const Rect&,
const PaintInvalidatorContext&);
const PaintInvalidatorContext&,
bool disable_flip = false);
ALWAYS_INLINE LayoutRect
ComputeVisualRectInBacking(const LayoutObject&,
const PaintInvalidatorContext&);
ALWAYS_INLINE LayoutRect
ComputeVisualRectInBacking(const NGPaintFragment&,
const LayoutObject&,
const PaintInvalidatorContext&);
ALWAYS_INLINE LayoutPoint
ComputeLocationInBacking(const LayoutObject&, const PaintInvalidatorContext&);
ALWAYS_INLINE void UpdatePaintingLayer(const LayoutObject&,
......
......@@ -141,6 +141,28 @@ NGPaintFragment::FragmentRange NGPaintFragment::InlineFragmentsFor(
return FragmentRange(nullptr, false);
}
bool NGPaintFragment::FlippedLocalVisualRectFor(
const LayoutObject* layout_object,
LayoutRect* visual_rect) {
auto fragments = InlineFragmentsFor(layout_object);
if (!fragments.IsInLayoutNGInlineFormattingContext())
return false;
for (NGPaintFragment* fragment : fragments) {
NGPhysicalOffsetRect child_visual_rect =
fragment->PhysicalFragment().SelfVisualRect();
child_visual_rect.offset += fragment->InlineOffsetToContainerBox();
visual_rect->Unite(child_visual_rect.ToLayoutRect());
}
if (!layout_object->HasFlippedBlocksWritingMode())
return true;
NGPaintFragment* container = GetForInlineContainer(layout_object);
DCHECK(container);
ToLayoutBox(container->GetLayoutObject())->FlipForWritingMode(*visual_rect);
return true;
}
void NGPaintFragment::UpdateVisualRectForNonLayoutObjectChildren() {
// Scan direct children only beause line boxes are always direct children of
// the inline formatting context.
......
......@@ -154,6 +154,14 @@ class CORE_EXPORT NGPaintFragment : public DisplayItemClient,
// for a LayoutObject.
static FragmentRange InlineFragmentsFor(const LayoutObject*);
// Computes LocalVisualRect for an inline LayoutObject in the
// LayoutObject::LocalVisualRect semantics; i.e., physical coordinates with
// flipped block-flow direction. See layout/README.md for the coordinate
// spaces.
// Returns false if the LayoutObject is not in LayoutNG inline formatting
// context.
static bool FlippedLocalVisualRectFor(const LayoutObject*, LayoutRect*);
private:
void PopulateDescendants(
const NGPhysicalOffset inline_offset_to_container_box,
......
......@@ -235,11 +235,14 @@ TEST_F(NGPaintFragmentTest, InlineBlock) {
EXPECT_TRUE(box1_inner);
EXPECT_EQ(box1_inner->GetLayoutObject(), box1.GetLayoutObject());
// Test |InlineFragmentsFor| can find the inner text of "box1".
// Test the text fragment inside of the inline block.
const NGPaintFragment& inner_line_box = *box1_inner->Children()[0];
const NGPaintFragment& inner_text = *inner_line_box.Children()[0];
EXPECT_EQ(NGPhysicalFragment::kFragmentText,
inner_text.PhysicalFragment().Type());
EXPECT_EQ(LayoutRect(60, 0, 10, 10), inner_text.VisualRect());
// Test |InlineFragmentsFor| can find the inner text of "box1".
LayoutObject* layout_inner_text = layout_box1->SlowFirstChild();
EXPECT_TRUE(layout_inner_text && layout_inner_text->IsText());
fragments = NGPaintFragment::InlineFragmentsFor(layout_inner_text);
......@@ -385,4 +388,55 @@ TEST_F(NGPaintFragmentTest, RelativeBlockAndInline) {
EXPECT_EQ(LayoutRect(0, 30, 30, 10), inner_text2.VisualRect());
}
TEST_F(NGPaintFragmentTest, FlippedBlock) {
LoadAhem();
SetBodyInnerHTML(R"HTML(
<!DOCTYPE html>
<style>
html, body { margin: 0; }
div {
writing-mode: vertical-rl;
font: 10px Ahem;
width: 20em;
height: 10em;
}
span { background: yellow; }
</style>
<body>
<div id="container">1234567890
pppp<span>XXX</span></div>
</body>
)HTML");
const NGPaintFragment* container = GetPaintFragmentByElementId("container");
EXPECT_EQ(2u, container->Children().size());
const NGPaintFragment& line1 = *container->Children()[0];
EXPECT_EQ(NGPhysicalFragment::kFragmentLineBox,
line1.PhysicalFragment().Type());
EXPECT_EQ(LayoutRect(190, 0, 10, 100), line1.VisualRect());
EXPECT_EQ(1u, line1.Children().size());
const NGPaintFragment& text1 = *line1.Children()[0];
EXPECT_EQ(NGPhysicalFragment::kFragmentText, text1.PhysicalFragment().Type());
EXPECT_EQ(LayoutRect(190, 0, 10, 100), text1.VisualRect());
const NGPaintFragment& line2 = *container->Children()[1];
EXPECT_EQ(NGPhysicalFragment::kFragmentLineBox,
line2.PhysicalFragment().Type());
EXPECT_EQ(LayoutRect(180, 0, 10, 70), line2.VisualRect());
EXPECT_EQ(2u, line2.Children().size());
const NGPaintFragment& text2 = *line2.Children()[0];
EXPECT_EQ(NGPhysicalFragment::kFragmentText, text2.PhysicalFragment().Type());
EXPECT_EQ(LayoutRect(180, 0, 10, 40), text2.VisualRect());
const NGPaintFragment& box = *line2.Children()[1];
EXPECT_EQ(NGPhysicalFragment::kFragmentBox, box.PhysicalFragment().Type());
EXPECT_EQ(LayoutRect(180, 40, 10, 30), box.VisualRect());
EXPECT_EQ(1u, box.Children().size());
const NGPaintFragment& text3 = *box.Children()[0];
EXPECT_EQ(NGPhysicalFragment::kFragmentText, text3.PhysicalFragment().Type());
EXPECT_EQ(LayoutRect(180, 40, 10, 30), text3.VisualRect());
}
} // namespace blink
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment