[LayoutNG] Fix crash when hit-testing flow control text fragments
Bug: 866556
Cq-Include-Trybots: luci.chromium.try:linux_layout_tests_layout_ng
Change-Id: Ie9977ebaa733e43d54fa6006e88adb3fce7cfb50
Reviewed-on: https://chromium-review.googlesource.com/1146882
Reviewed-by: Xiaocheng Hu <xiaochengh@chromium.org>
Commit-Queue: Koji Ishii <kojii@chromium.org>
Cr-Commit-Position: refs/heads/master@{#578372}
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc
index 7c70596..2246db2 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc
@@ -7,6 +7,7 @@
#include "third_party/blink/renderer/core/dom/node.h"
#include "third_party/blink/renderer/core/layout/layout_text_fragment.h"
#include "third_party/blink/renderer/core/layout/line/line_orientation_utils.h"
+#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h"
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
@@ -187,15 +188,31 @@
unsigned NGPhysicalTextFragment::TextOffsetForPoint(
const NGPhysicalOffset& point) const {
- if (IsLineBreak())
- return StartOffset();
- DCHECK(TextShapeResult());
+ const ComputedStyle& style = Style();
const LayoutUnit& point_in_line_direction =
- Style().IsHorizontalWritingMode() ? point.left : point.top;
- return TextShapeResult()->OffsetForPosition(point_in_line_direction.ToFloat(),
- IncludePartialGlyphs,
- BreakGlyphs) +
- StartOffset();
+ style.IsHorizontalWritingMode() ? point.left : point.top;
+ if (const ShapeResult* shape_result = TextShapeResult()) {
+ return shape_result->OffsetForPosition(point_in_line_direction.ToFloat(),
+ IncludePartialGlyphs, BreakGlyphs) +
+ StartOffset();
+ }
+
+ // Flow control fragments such as forced line break, tabulation, soft-wrap
+ // opportunities, etc. do not have ShapeResult.
+ DCHECK(IsFlowControl());
+
+ // Zero-inline-size objects such as newline always return the start offset.
+ NGLogicalSize size = Size().ConvertToLogical(style.GetWritingMode());
+ if (!size.inline_size)
+ return StartOffset();
+
+ // Sized objects such as tabulation returns the next offset if the given point
+ // is on the right half.
+ LayoutUnit inline_offset = IsLtr(ResolvedDirection())
+ ? point_in_line_direction
+ : size.inline_size - point_in_line_direction;
+ DCHECK_EQ(1u, Length());
+ return inline_offset <= size.inline_size / 2 ? StartOffset() : EndOffset();
}
UBiDiLevel NGPhysicalTextFragment::BidiLevel() const {
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment_test.cc
index 8962d68..4419bfc 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment_test.cc
@@ -245,4 +245,48 @@
EXPECT_TRUE(closed_quote.IsAnonymousText());
}
+TEST_F(NGPhysicalTextFragmentTest, TextOffsetForPointForTabulation) {
+ LoadAhem();
+ SetBodyInnerHTML(R"HTML(
+ <style>
+ #container {
+ white-space: pre;
+ font-family: Ahem;
+ font-size: 10px;
+ tab-size: 8;
+ }
+ </style>
+ <div id="container">	</div>
+ )HTML");
+ auto text_fragments = CollectTextFragmentsInContainer("container");
+ ASSERT_EQ(1u, text_fragments.size());
+ const NGPhysicalTextFragment& text = *text_fragments[0];
+ EXPECT_EQ(0u, text.TextOffsetForPoint({LayoutUnit(), LayoutUnit()}));
+ EXPECT_EQ(0u, text.TextOffsetForPoint({LayoutUnit(39), LayoutUnit()}));
+ EXPECT_EQ(1u, text.TextOffsetForPoint({LayoutUnit(41), LayoutUnit()}));
+ EXPECT_EQ(1u, text.TextOffsetForPoint({LayoutUnit(80), LayoutUnit()}));
+}
+
+TEST_F(NGPhysicalTextFragmentTest, TextOffsetForPointForTabulationRtl) {
+ LoadAhem();
+ SetBodyInnerHTML(R"HTML(
+ <style>
+ #container {
+ white-space: pre;
+ font-family: Ahem;
+ font-size: 10px;
+ tab-size: 8;
+ }
+ </style>
+ <div id="container" dir="rtl">	</div>
+ )HTML");
+ auto text_fragments = CollectTextFragmentsInContainer("container");
+ ASSERT_EQ(1u, text_fragments.size());
+ const NGPhysicalTextFragment& text = *text_fragments[0];
+ EXPECT_EQ(1u, text.TextOffsetForPoint({LayoutUnit(), LayoutUnit()}));
+ EXPECT_EQ(1u, text.TextOffsetForPoint({LayoutUnit(39), LayoutUnit()}));
+ EXPECT_EQ(0u, text.TextOffsetForPoint({LayoutUnit(41), LayoutUnit()}));
+ EXPECT_EQ(0u, text.TextOffsetForPoint({LayoutUnit(80), LayoutUnit()}));
+}
+
} // namespace blink