Commit 61a2f5db authored by Yoshifumi Inoue's avatar Yoshifumi Inoue Committed by Commit Bot

Make NGInlineNode::SetTextWithOffset() to handle bidi text correctly

This patch intorudces |NGInlineItemsBuilder::DidFinishCollectInlines()| to
populate collected inline data to |NGInlineNodeData| at one place to ensure
valid |NGInlineNodeData|, e.g. valid value of |is_bidi_enabled_|.

Background:
Before this patch, we processed RTL text as LTR text when text is modified
with |LayoutText::SetTextWithOffset()| by Text.{append,replace}Data() and
chunked append at loading.

Bug: 1038254
Change-Id: I645f78f853e89704120d50b9db3234806f65c1ed
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2010590
Commit-Queue: Yoshifumi Inoue <yosin@chromium.org>
Commit-Queue: Koji Ishii <kojii@chromium.org>
Auto-Submit: Yoshifumi Inoue <yosin@chromium.org>
Reviewed-by: default avatarKoji Ishii <kojii@chromium.org>
Cr-Commit-Position: refs/heads/master@{#733296}
parent 96dc266d
...@@ -47,6 +47,20 @@ class LayoutNGTextTest : public PageTestBase { ...@@ -47,6 +47,20 @@ class LayoutNGTextTest : public PageTestBase {
} }
}; };
TEST_F(LayoutNGTextTest, SetTextWithOffsetAppendBidi) {
if (!RuntimeEnabledFeatures::LayoutNGEnabled())
return;
SetBodyInnerHTML(u"<div dir=rtl id=target>\u05D0\u05D1\u05BC\u05D2</div>");
Text& text = To<Text>(*GetElementById("target")->firstChild());
text.appendData(u"\u05D0\u05D1\u05BC\u05D2");
EXPECT_EQ(String(u"*{'\u05D0\u05D1\u05BC\u05D2\u05D0\u05D1\u05BC\u05D2', "
u"ShapeResult=0+8}\n")
.Utf8(),
GetItemsAsString(*text.GetLayoutObject()));
}
TEST_F(LayoutNGTextTest, SetTextWithOffsetAppendControl) { TEST_F(LayoutNGTextTest, SetTextWithOffsetAppendControl) {
if (!RuntimeEnabledFeatures::LayoutNGEnabled()) if (!RuntimeEnabledFeatures::LayoutNGEnabled())
return; return;
...@@ -136,8 +150,11 @@ TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteRTL) { ...@@ -136,8 +150,11 @@ TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteRTL) {
Text& text = To<Text>(*GetElementById("target")->firstChild()); Text& text = To<Text>(*GetElementById("target")->firstChild());
text.deleteData(2, 2, ASSERT_NO_EXCEPTION); // remove "23" text.deleteData(2, 2, ASSERT_NO_EXCEPTION); // remove "23"
EXPECT_EQ("*{'0 4', ShapeResult=0+3}\n", EXPECT_EQ(
GetItemsAsString(*text.GetLayoutObject())); "*{'0', ShapeResult=0+1}\n"
"*{' ', ShapeResult=1+1}\n"
"*{'4', ShapeResult=2+1}\n",
GetItemsAsString(*text.GetLayoutObject()));
} }
// http://crbug.com/1000685 // http://crbug.com/1000685
...@@ -149,8 +166,12 @@ TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteRTL2) { ...@@ -149,8 +166,12 @@ TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteRTL2) {
Text& text = To<Text>(*GetElementById("target")->firstChild()); Text& text = To<Text>(*GetElementById("target")->firstChild());
text.deleteData(0, 1, ASSERT_NO_EXCEPTION); // remove "0" text.deleteData(0, 1, ASSERT_NO_EXCEPTION); // remove "0"
EXPECT_EQ("*{'(xy)5', ShapeResult=0+5}\n", EXPECT_EQ(
GetItemsAsString(*text.GetLayoutObject())); "*{'(', ShapeResult=0+1}\n"
"*{'xy', ShapeResult=1+2}\n"
"*{')', ShapeResult=3+1}\n"
"*{'5', ShapeResult=4+1}\n",
GetItemsAsString(*text.GetLayoutObject()));
} }
// http://crbug.com/1039143 // http://crbug.com/1039143
......
...@@ -1230,6 +1230,27 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::Exit( ...@@ -1230,6 +1230,27 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::Exit(
} }
} }
template <typename OffsetMappingBuilder>
bool NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::MayBeBidiEnabled()
const {
return !text_.Is8Bit() || HasBidiControls();
}
template <typename OffsetMappingBuilder>
void NGInlineItemsBuilderTemplate<
OffsetMappingBuilder>::DidFinishCollectInlines(NGInlineNodeData* data) {
data->text_content = ToString();
// Set |is_bidi_enabled_| for all UTF-16 strings for now, because at this
// point the string may or may not contain RTL characters.
// |SegmentText()| will analyze the text and reset |is_bidi_enabled_| if it
// doesn't contain any RTL characters.
data->is_bidi_enabled_ = MayBeBidiEnabled();
data->is_empty_inline_ = IsEmptyInline();
data->is_block_level_ = IsBlockLevel();
data->changes_may_affect_earlier_lines_ = ChangesMayAffectEarlierLines();
}
template <typename OffsetMappingBuilder> template <typename OffsetMappingBuilder>
void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::SetIsSymbolMarker( void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::SetIsSymbolMarker(
bool b) { bool b) {
......
...@@ -134,6 +134,9 @@ class NGInlineItemsBuilderTemplate { ...@@ -134,6 +134,9 @@ class NGInlineItemsBuilderTemplate {
void EnterInline(LayoutInline*); void EnterInline(LayoutInline*);
void ExitInline(LayoutObject*); void ExitInline(LayoutObject*);
// Set collected inline items data to |data|.
void DidFinishCollectInlines(NGInlineNodeData* data);
OffsetMappingBuilder& GetOffsetMappingBuilder() { return mapping_builder_; } OffsetMappingBuilder& GetOffsetMappingBuilder() { return mapping_builder_; }
void SetIsSymbolMarker(bool b); void SetIsSymbolMarker(bool b);
...@@ -223,6 +226,8 @@ class NGInlineItemsBuilderTemplate { ...@@ -223,6 +226,8 @@ class NGInlineItemsBuilderTemplate {
void Exit(LayoutObject*); void Exit(LayoutObject*);
bool MayBeBidiEnabled() const;
bool ShouldInsertBreakOpportunityAfterLeadingPreservedSpaces( bool ShouldInsertBreakOpportunityAfterLeadingPreservedSpaces(
const String&, const String&,
const ComputedStyle&, const ComputedStyle&,
......
...@@ -427,13 +427,6 @@ void TruncateOrPadText(String* text, unsigned length) { ...@@ -427,13 +427,6 @@ void TruncateOrPadText(String* text, unsigned length) {
} }
} }
template <typename OffsetMappingBuilder>
bool MayBeBidiEnabled(
const String& text_content,
const NGInlineItemsBuilderTemplate<OffsetMappingBuilder>& builder) {
return !text_content.Is8Bit() || builder.HasBidiControls();
}
} // namespace } // namespace
NGInlineNode::NGInlineNode(LayoutBlockFlow* block) NGInlineNode::NGInlineNode(LayoutBlockFlow* block)
...@@ -778,7 +771,7 @@ bool NGInlineNode::SetTextWithOffset(LayoutText* layout_text, ...@@ -778,7 +771,7 @@ bool NGInlineNode::SetTextWithOffset(LayoutText* layout_text,
// inline items. // inline items.
layout_text->ClearInlineItems(); layout_text->ClearInlineItems();
CollectInlinesInternal(node.GetLayoutBlockFlow(), &builder, previous_data); CollectInlinesInternal(node.GetLayoutBlockFlow(), &builder, previous_data);
data->text_content = builder.ToString(); builder.DidFinishCollectInlines(data);
// Relocates |ShapeResult| in |previous_data| after |offset|+|length| // Relocates |ShapeResult| in |previous_data| after |offset|+|length|
editor.Run(); editor.Run();
node.SegmentText(data); node.SegmentText(data);
...@@ -886,17 +879,7 @@ void NGInlineNode::CollectInlines(NGInlineNodeData* data, ...@@ -886,17 +879,7 @@ void NGInlineNode::CollectInlines(NGInlineNodeData* data,
data->items.ReserveCapacity(EstimateInlineItemsCount(*block)); data->items.ReserveCapacity(EstimateInlineItemsCount(*block));
NGInlineItemsBuilder builder(&data->items, dirty_lines); NGInlineItemsBuilder builder(&data->items, dirty_lines);
CollectInlinesInternal(block, &builder, previous_data); CollectInlinesInternal(block, &builder, previous_data);
data->text_content = builder.ToString(); builder.DidFinishCollectInlines(data);
// Set |is_bidi_enabled_| for all UTF-16 strings for now, because at this
// point the string may or may not contain RTL characters.
// |SegmentText()| will analyze the text and reset |is_bidi_enabled_| if it
// doesn't contain any RTL characters.
data->is_bidi_enabled_ = MayBeBidiEnabled(data->text_content, builder);
data->is_empty_inline_ = builder.IsEmptyInline();
data->is_block_level_ = builder.IsBlockLevel();
data->changes_may_affect_earlier_lines_ =
builder.ChangesMayAffectEarlierLines();
} }
void NGInlineNode::SegmentText(NGInlineNodeData* data) { void NGInlineNode::SegmentText(NGInlineNodeData* data) {
......
...@@ -11,6 +11,9 @@ ...@@ -11,6 +11,9 @@
namespace blink { namespace blink {
template <typename OffsetMappingBuilder>
class NGInlineItemsBuilderTemplate;
// Data which is required for inline nodes. // Data which is required for inline nodes.
struct CORE_EXPORT NGInlineNodeData : NGInlineItemsData { struct CORE_EXPORT NGInlineNodeData : NGInlineItemsData {
public: public:
...@@ -40,6 +43,9 @@ struct CORE_EXPORT NGInlineNodeData : NGInlineItemsData { ...@@ -40,6 +43,9 @@ struct CORE_EXPORT NGInlineNodeData : NGInlineItemsData {
friend class NGInlineNodeForTest; friend class NGInlineNodeForTest;
friend class NGOffsetMappingTest; friend class NGOffsetMappingTest;
template <typename OffsetMappingBuilder>
friend class NGInlineItemsBuilderTemplate;
// Items to use for the first line, when the node has :first-line rules. // Items to use for the first line, when the node has :first-line rules.
// //
// Items have different ComputedStyle, and may also have different // Items have different ComputedStyle, and may also have different
......
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