Commit 4f10679e authored by Xianzhu Wang's avatar Xianzhu Wang Committed by Commit Bot

Simplify LayoutText line box collection

This uses the same mechanism used in
https://chromium-review.googlesource.com/c/chromium/src/+/1612358
for LayoutInline.

The new code favors LayoutNG for performance, to avoid back-and-forth
flipping in some cases for LayoutNG and allow that for Legacy.

Also fix an issue of unnecessary flipping for SVG text.

Change-Id: I70dc61807d2284a3cdda1fd4733b7ab2bfdd00e0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1615680
Commit-Queue: Xianzhu Wang <wangxianzhu@chromium.org>
Reviewed-by: default avatarAleks Totic <atotic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#660998}
parent b2dad40f
...@@ -421,55 +421,48 @@ static IntRect EllipsisRectForBox(InlineTextBox* box, ...@@ -421,55 +421,48 @@ static IntRect EllipsisRectForBox(InlineTextBox* box,
return IntRect(); return IntRect();
} }
void LayoutText::AccumlateQuads(Vector<FloatQuad>& quads, template <typename PhysicalRectCollector>
const IntRect& ellipsis_rect, void LayoutText::CollectLineBoxRects(const PhysicalRectCollector& yield,
LocalOrAbsoluteOption local_or_absolute, ClippingOption option) const {
MapCoordinatesFlags mode,
const LayoutRect& passed_boundaries) const {
FloatRect boundaries(passed_boundaries);
if (!ellipsis_rect.IsEmpty()) {
if (StyleRef().IsHorizontalWritingMode())
boundaries.SetWidth(ellipsis_rect.MaxX() - boundaries.X());
else
boundaries.SetHeight(ellipsis_rect.MaxY() - boundaries.Y());
}
quads.push_back(local_or_absolute == kAbsoluteQuads
? LocalToAbsoluteQuad(boundaries, mode)
: boundaries);
}
void LayoutText::Quads(Vector<FloatQuad>& quads,
ClippingOption option,
LocalOrAbsoluteOption local_or_absolute,
MapCoordinatesFlags mode) const {
if (const NGPhysicalBoxFragment* box_fragment = if (const NGPhysicalBoxFragment* box_fragment =
ContainingBlockFlowFragment()) { ContainingBlockFlowFragment()) {
const auto children = const auto children =
NGInlineFragmentTraversal::SelfFragmentsOf(*box_fragment, this); NGInlineFragmentTraversal::SelfFragmentsOf(*box_fragment, this);
const LayoutBlock* block_for_flipping = nullptr;
if (UNLIKELY(HasFlippedBlocksWritingMode()))
block_for_flipping = ContainingBlock();
for (const auto& child : children) { for (const auto& child : children) {
// TODO(layout-dev): We should have NG version of |EllipsisRectForBox()| // TODO(layout-dev): We should have NG version of |EllipsisRectForBox()|
LayoutRect rect = child.RectInContainerBox().ToLayoutRect(); yield(child.RectInContainerBox());
if (UNLIKELY(block_for_flipping))
block_for_flipping->FlipForWritingMode(rect);
AccumlateQuads(quads, IntRect(), local_or_absolute, mode, rect);
} }
return; return;
} }
const LayoutBlock* block_for_flipping =
UNLIKELY(NeedsFlipForWritingMode()) ? ContainingBlock() : nullptr;
for (InlineTextBox* box : TextBoxes()) { for (InlineTextBox* box : TextBoxes()) {
LayoutRect boundaries = box->FrameRect();
const IntRect ellipsis_rect = (option == kClipToEllipsis) const IntRect ellipsis_rect = (option == kClipToEllipsis)
? EllipsisRectForBox(box, 0, TextLength()) ? EllipsisRectForBox(box, 0, TextLength())
: IntRect(); : IntRect();
AccumlateQuads(quads, ellipsis_rect, local_or_absolute, mode, if (!ellipsis_rect.IsEmpty()) {
box->FrameRect()); if (IsHorizontalWritingMode())
boundaries.SetWidth(ellipsis_rect.MaxX() - boundaries.X());
else
boundaries.SetHeight(ellipsis_rect.MaxY() - boundaries.Y());
}
yield(FlipForWritingMode(boundaries, block_for_flipping));
} }
} }
void LayoutText::AbsoluteQuads(Vector<FloatQuad>& quads, void LayoutText::AbsoluteQuads(Vector<FloatQuad>& quads,
MapCoordinatesFlags mode) const { MapCoordinatesFlags mode) const {
Quads(quads, kNoClipping, kAbsoluteQuads, mode); const LayoutBlock* block_for_flipping =
UNLIKELY(NeedsFlipForWritingMode()) ? ContainingBlock() : nullptr;
CollectLineBoxRects(
[this, &quads, mode, block_for_flipping](const PhysicalRect& r) {
// LocalToAbsoluteQuad requires the input to be in flipped blocks
// direction.
LayoutRect rect = FlipForWritingMode(r, block_for_flipping);
quads.push_back(LocalToAbsoluteQuad(FloatRect(rect), mode));
});
} }
bool LayoutText::MapDOMOffsetToTextContentOffset(const NGOffsetMapping& mapping, bool LayoutText::MapDOMOffsetToTextContentOffset(const NGOffsetMapping& mapping,
...@@ -560,12 +553,12 @@ void LayoutText::AbsoluteQuadsForRange(Vector<FloatQuad>& quads, ...@@ -560,12 +553,12 @@ void LayoutText::AbsoluteQuadsForRange(Vector<FloatQuad>& quads,
const unsigned clamped_start = const unsigned clamped_start =
std::max(start, text_fragment.StartOffset()); std::max(start, text_fragment.StartOffset());
const unsigned clamped_end = std::min(end, text_fragment.EndOffset()); const unsigned clamped_end = std::min(end, text_fragment.EndOffset());
LayoutRect rect = PhysicalRect rect = text_fragment.LocalRect(clamped_start, clamped_end);
text_fragment.LocalRect(clamped_start, clamped_end).ToLayoutRect(); rect.Move(fragment->InlineOffsetToContainerBox());
rect.MoveBy(fragment->InlineOffsetToContainerBox().ToLayoutPoint()); // LocalToAbsoluteQuad requires the input to be in flipped blocks
if (UNLIKELY(block_for_flipping)) // direction.
block_for_flipping->FlipForWritingMode(rect); LayoutRect layout_rect = FlipForWritingMode(rect, block_for_flipping);
const FloatQuad quad = LocalToAbsoluteQuad(FloatRect(rect)); const FloatQuad quad = LocalToAbsoluteQuad(FloatRect(layout_rect));
if (clamped_start < clamped_end) { if (clamped_start < clamped_end) {
quads.push_back(quad); quads.push_back(quad);
found_non_collapsed_quad = true; found_non_collapsed_quad = true;
...@@ -621,10 +614,16 @@ void LayoutText::AbsoluteQuadsForRange(Vector<FloatQuad>& quads, ...@@ -621,10 +614,16 @@ void LayoutText::AbsoluteQuadsForRange(Vector<FloatQuad>& quads,
FloatRect LayoutText::LocalBoundingBoxRectForAccessibility() const { FloatRect LayoutText::LocalBoundingBoxRectForAccessibility() const {
FloatRect result; FloatRect result;
Vector<FloatQuad> quads; const LayoutBlock* block_for_flipping =
Quads(quads, LayoutText::kClipToEllipsis, LayoutText::kLocalQuads); UNLIKELY(NeedsFlipForWritingMode()) ? ContainingBlock() : nullptr;
for (const FloatQuad& quad : quads) CollectLineBoxRects(
result.Unite(quad.BoundingBox()); [this, &result, block_for_flipping](const PhysicalRect& r) {
LayoutRect rect = FlipForWritingMode(r, block_for_flipping);
result.Unite(FloatRect(rect));
},
kClipToEllipsis);
// TODO(wangxianzhu): This is one of a few cases that a FloatRect is required
// to be in flipped blocks direction. Should eliminite them.
return result; return result;
} }
...@@ -1546,8 +1545,8 @@ PhysicalOffset LayoutText::FirstLineBoxTopLeft() const { ...@@ -1546,8 +1545,8 @@ PhysicalOffset LayoutText::FirstLineBoxTopLeft() const {
if (const NGPaintFragment* fragment = FirstInlineFragment()) if (const NGPaintFragment* fragment = FirstInlineFragment())
return fragment->InlineOffsetToContainerBox(); return fragment->InlineOffsetToContainerBox();
if (const auto* text_box = FirstTextBox()) { if (const auto* text_box = FirstTextBox()) {
auto location = text_box->Location(); LayoutPoint location = text_box->Location();
if (UNLIKELY(HasFlippedBlocksWritingMode())) { if (UNLIKELY(NeedsFlipForWritingMode())) {
location = ContainingBlock()->FlipForWritingMode(location); location = ContainingBlock()->FlipForWritingMode(location);
location.Move(-text_box->Width(), LayoutUnit()); location.Move(-text_box->Width(), LayoutUnit());
} }
...@@ -1962,55 +1961,19 @@ float LayoutText::Width(unsigned from, ...@@ -1962,55 +1961,19 @@ float LayoutText::Width(unsigned from,
} }
PhysicalRect LayoutText::PhysicalLinesBoundingBox() const { PhysicalRect LayoutText::PhysicalLinesBoundingBox() const {
if (const NGPhysicalBoxFragment* box_fragment = PhysicalRect result;
ContainingBlockFlowFragment()) { CollectLineBoxRects(
PhysicalRect bounding_box; [&result](const PhysicalRect& r) { result.UniteIfNonZero(r); });
auto children = // Some callers expect correct offset even if the rect is empty.
NGInlineFragmentTraversal::SelfFragmentsOf(*box_fragment, this); if (result == PhysicalRect())
for (const auto& child : children) result.offset = FirstLineBoxTopLeft();
bounding_box.UniteIfNonZero(child.RectInContainerBox()); return result;
return bounding_box;
}
LayoutRect result;
DCHECK_EQ(!FirstTextBox(),
!LastTextBox()); // Either both are null or both exist.
if (FirstTextBox() && LastTextBox()) {
// Return the width of the minimal left side and the maximal right side.
float logical_left_side = 0;
float logical_right_side = 0;
for (InlineTextBox* curr : TextBoxes()) {
if (curr == FirstTextBox() || curr->LogicalLeft() < logical_left_side)
logical_left_side = curr->LogicalLeft().ToFloat();
if (curr == FirstTextBox() || curr->LogicalRight() > logical_right_side)
logical_right_side = curr->LogicalRight().ToFloat();
}
bool is_horizontal = StyleRef().IsHorizontalWritingMode();
float x = is_horizontal ? logical_left_side : FirstTextBox()->X().ToFloat();
float y = is_horizontal ? FirstTextBox()->Y().ToFloat() : logical_left_side;
float width = is_horizontal ? logical_right_side - logical_left_side
: LastTextBox()->LogicalBottom() - x;
float height = is_horizontal ? LastTextBox()->LogicalBottom() - y
: logical_right_side - logical_left_side;
result = EnclosingLayoutRect(FloatRect(x, y, width, height));
}
if (UNLIKELY(HasFlippedBlocksWritingMode()))
ContainingBlock()->FlipForWritingMode(result);
return PhysicalRect(result);
} }
LayoutRect LayoutText::VisualOverflowRect() const { LayoutRect LayoutText::VisualOverflowRect() const {
if (base::Optional<PhysicalRect> physical_rect = if (base::Optional<PhysicalRect> physical_rect =
NGPaintFragment::LocalVisualRectFor(*this)) { NGPaintFragment::LocalVisualRectFor(*this))
LayoutRect rect = physical_rect->ToLayoutRect(); return FlipForWritingMode(*physical_rect);
if (UNLIKELY(HasFlippedBlocksWritingMode()))
ContainingBlock()->FlipForWritingMode(rect);
return rect;
}
if (!FirstTextBox()) if (!FirstTextBox())
return LayoutRect(); return LayoutRect();
...@@ -2057,13 +2020,12 @@ LayoutRect LayoutText::VisualOverflowRect() const { ...@@ -2057,13 +2020,12 @@ LayoutRect LayoutText::VisualOverflowRect() const {
} }
PhysicalRect LayoutText::LocalVisualRectIgnoringVisibility() const { PhysicalRect LayoutText::LocalVisualRectIgnoringVisibility() const {
if (const auto& rect = NGPaintFragment::LocalVisualRectFor(*this)) PhysicalRect rect;
return UnionRect(*rect, LocalSelectionVisualRect()); if (const auto& r = NGPaintFragment::LocalVisualRectFor(*this))
rect = *r;
auto rect = VisualOverflowRect(); else
if (UNLIKELY(HasFlippedBlocksWritingMode())) rect = FlipForWritingMode(VisualOverflowRect());
ContainingBlock()->FlipForWritingMode(rect); return UnionRect(rect, LocalSelectionVisualRect());
return UnionRect(PhysicalRect(rect), LocalSelectionVisualRect());
} }
PhysicalRect LayoutText::LocalSelectionVisualRect() const { PhysicalRect LayoutText::LocalSelectionVisualRect() const {
...@@ -2100,9 +2062,7 @@ PhysicalRect LayoutText::LocalSelectionVisualRect() const { ...@@ -2100,9 +2062,7 @@ PhysicalRect LayoutText::LocalSelectionVisualRect() const {
rect.Unite(LayoutRect(EllipsisRectForBox(box, start_pos, end_pos))); rect.Unite(LayoutRect(EllipsisRectForBox(box, start_pos, end_pos)));
} }
if (UNLIKELY(HasFlippedBlocksWritingMode())) return FlipForWritingMode(rect);
ContainingBlock()->FlipForWritingMode(rect);
return PhysicalRect(rect);
} }
const NGOffsetMapping* LayoutText::GetNGOffsetMapping() const { const NGOffsetMapping* LayoutText::GetNGOffsetMapping() const {
...@@ -2467,4 +2427,28 @@ const NGInlineItems& LayoutText::InlineItems() const { ...@@ -2467,4 +2427,28 @@ const NGInlineItems& LayoutText::InlineItems() const {
return *GetNGInlineItems(); return *GetNGInlineItems();
} }
LayoutRect LayoutText::FlipForWritingMode(
const PhysicalRect& r,
const LayoutBlock* block_for_flipping) const {
LayoutRect rect = r.ToLayoutRect();
if (UNLIKELY(NeedsFlipForWritingMode())) {
DCHECK(!block_for_flipping || block_for_flipping == ContainingBlock());
(block_for_flipping ? block_for_flipping : ContainingBlock())
->FlipForWritingMode(rect);
}
return rect;
}
PhysicalRect LayoutText::FlipForWritingMode(
const LayoutRect& r,
const LayoutBlock* block_for_flipping) const {
LayoutRect rect = r;
if (UNLIKELY(NeedsFlipForWritingMode())) {
DCHECK(!block_for_flipping || block_for_flipping == ContainingBlock());
(block_for_flipping ? block_for_flipping : ContainingBlock())
->FlipForWritingMode(rect);
}
return PhysicalRect(rect);
}
} // namespace blink } // namespace blink
...@@ -120,11 +120,8 @@ class CORE_EXPORT LayoutText : public LayoutObject { ...@@ -120,11 +120,8 @@ class CORE_EXPORT LayoutText : public LayoutObject {
FloatRect LocalBoundingBoxRectForAccessibility() const final; FloatRect LocalBoundingBoxRectForAccessibility() const final;
enum ClippingOption { kNoClipping, kClipToEllipsis }; enum ClippingOption { kNoClipping, kClipToEllipsis };
enum LocalOrAbsoluteOption { kLocalQuads, kAbsoluteQuads }; void LocalQuadsInFlippedBlocksDirection(Vector<FloatQuad>&,
void Quads(Vector<FloatQuad>&, ClippingOption = kNoClipping) const;
ClippingOption = kNoClipping,
LocalOrAbsoluteOption = kAbsoluteQuads,
MapCoordinatesFlags mode = 0) const;
PositionWithAffinity PositionForPoint(const LayoutPoint&) const override; PositionWithAffinity PositionForPoint(const LayoutPoint&) const override;
...@@ -346,11 +343,11 @@ class CORE_EXPORT LayoutText : public LayoutObject { ...@@ -346,11 +343,11 @@ class CORE_EXPORT LayoutText : public LayoutObject {
private: private:
InlineTextBoxList& MutableTextBoxes(); InlineTextBoxList& MutableTextBoxes();
void AccumlateQuads(Vector<FloatQuad>&, // PhysicalRectCollector should be like a function:
const IntRect& ellipsis_rect, // void (const PhysicalRect&).
LocalOrAbsoluteOption, template <typename PhysicalRectCollector>
MapCoordinatesFlags mode, void CollectLineBoxRects(const PhysicalRectCollector&,
const LayoutRect&) const; ClippingOption option = kNoClipping) const;
void ComputePreferredLogicalWidths(float lead_width); void ComputePreferredLogicalWidths(float lead_width);
void ComputePreferredLogicalWidths( void ComputePreferredLogicalWidths(
...@@ -395,6 +392,22 @@ class CORE_EXPORT LayoutText : public LayoutObject { ...@@ -395,6 +392,22 @@ class CORE_EXPORT LayoutText : public LayoutObject {
bool CanOptimizeSetText() const; bool CanOptimizeSetText() const;
void SetFirstTextBoxLogicalLeft(float text_width) const; void SetFirstTextBoxLogicalLeft(float text_width) const;
bool NeedsFlipForWritingMode() const {
return HasFlippedBlocksWritingMode() && !IsSVG();
}
// These functions flip the input rect in ContainingBlock() if
// NeedsFlipForWritingMode() is true. If |block_for_flipping| is not null,
// it should be ContainingBlock(), otherwise the function will call
// ContainingBlock() by themselves. The caller should prepare
// |block_for_flipping| if it will loop through many rects to flip to avoid
// the cost of repeated ContainingBlock() calls.
ALWAYS_INLINE WARN_UNUSED_RESULT LayoutRect
FlipForWritingMode(const PhysicalRect& r,
const LayoutBlock* block_for_flipping = nullptr) const;
ALWAYS_INLINE WARN_UNUSED_RESULT PhysicalRect
FlipForWritingMode(const LayoutRect& r,
const LayoutBlock* block_for_flipping = nullptr) const;
private: private:
ContentCaptureManager* GetContentCaptureManager(); ContentCaptureManager* GetContentCaptureManager();
NodeHolder node_holder_; NodeHolder node_holder_;
......
...@@ -662,21 +662,6 @@ TEST_P(ParameterizedLayoutTextTest, PhysicalLinesBoundingBoxVerticalRL) { ...@@ -662,21 +662,6 @@ TEST_P(ParameterizedLayoutTextTest, PhysicalLinesBoundingBoxVerticalRL) {
->PhysicalLinesBoundingBox()); ->PhysicalLinesBoundingBox());
} }
TEST_P(ParameterizedLayoutTextTest, QuadsBasic) {
GetDocument().SetCompatibilityMode(Document::kQuirksMode);
LoadAhem();
SetBasicBody(
"<style>p {font: 13px/17px Ahem;}</style>"
"<p id=one>one</p>");
const Element& one = *GetDocument().getElementById("one");
Vector<FloatQuad> actual_quads;
ToLayoutText(one.firstChild()->GetLayoutObject())->Quads(actual_quads);
EXPECT_EQ(
Vector<FloatQuad>({FloatQuad(FloatPoint(8, 10), FloatPoint(47, 10),
FloatPoint(47, 23), FloatPoint(8, 23))}),
actual_quads);
}
TEST_P(ParameterizedLayoutTextTest, WordBreakElement) { TEST_P(ParameterizedLayoutTextTest, WordBreakElement) {
SetBasicBody("foo <wbr> bar"); SetBasicBody("foo <wbr> bar");
...@@ -807,4 +792,51 @@ TEST_P(ParameterizedLayoutTextTest, LocalSelectionRectLineHeightVertical) { ...@@ -807,4 +792,51 @@ TEST_P(ParameterizedLayoutTextTest, LocalSelectionRectLineHeightVertical) {
"foo bar b^a|z</div>")); "foo bar b^a|z</div>"));
} }
TEST_P(ParameterizedLayoutTextTest, VisualRectInDocumentSVGTspan) {
LoadAhem();
SetBodyInnerHTML(R"HTML(
<style>
body {
margin:0px;
font: 20px/20px Ahem;
}
</style>
<svg>
<text x="10" y="50" width="100">
<tspan id="target" dx="15" dy="25">tspan</tspan>
</text>
</svg>
)HTML");
LayoutText* target =
ToLayoutText(GetLayoutObjectByElementId("target")->SlowFirstChild());
const int ascent = 16;
PhysicalRect expected(10 + 15, 50 + 25 - ascent, 20 * 5, 20);
EXPECT_EQ(expected, target->VisualRectInDocument());
EXPECT_EQ(expected, target->VisualRectInDocument(kUseGeometryMapper));
}
TEST_P(ParameterizedLayoutTextTest, VisualRectInDocumentSVGTspanTB) {
LoadAhem();
SetBodyInnerHTML(R"HTML(
<style>
body {
margin:0px;
font: 20px/20px Ahem;
}
</style>
<svg>
<text x="50" y="10" width="100" writing-mode="tb">
<tspan id="target" dx="15" dy="25">tspan</tspan>
</text>
</svg>
)HTML");
LayoutText* target =
ToLayoutText(GetLayoutObjectByElementId("target")->SlowFirstChild());
PhysicalRect expected(50 + 15 - 20 / 2, 10 + 25, 20, 20 * 5);
EXPECT_EQ(expected, target->VisualRectInDocument());
EXPECT_EQ(expected, target->VisualRectInDocument(kUseGeometryMapper));
}
} // namespace blink } // 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