Commit 90e84c72 authored by Yoshifumi Inoue's avatar Yoshifumi Inoue Committed by Commit Bot

[LayoutNG] Improve LayoutText::SetTextWithOffset() by reducing text shaping

This patch changes |LayoutText::SetTextWithOffset()| to reduce text shaping by
reusing |ShapeResult| of before and after replaced range for improving
performance, 10% to 15% faster but 3 times slower than legacy layout.
(Profile data: http://bit.ly/33B9rfg)

From this patch, |SetTextWithOffset()| does:
 1. Get offset mapping to map DOM offsets to text content offsets for replaced
  range.
 2. Collect inlines into |text_content| and |NGInlineItem| to get newly inserted
  text.
 3. Copy before and after |ShapeResult| from current data to |NGInlineItem| list
  in step 2.
 4. Call |NGInlineNode::ShapeText()| with |NGInlineItem| list to reuse
  |ShapeResult|.

This patch gets rid of NGInlineNodeTest.InvalidateSetTextWithOffset because
new test case replaces this test and this test call |SetTextWithOffset()| with
wrong parameter. It shoulbe |SetTextWithOffset("baftere", 1, 4)| as result of
|"before".replaceData(1, 4, "after")|.

Bug: 707656
Change-Id: I078b19615d6f7e31362c1e6398aac88fb17b29c1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1729330
Commit-Queue: Yoshifumi Inoue <yosin@chromium.org>
Auto-Submit: Yoshifumi Inoue <yosin@chromium.org>
Reviewed-by: default avatarKoji Ishii <kojii@chromium.org>
Reviewed-by: default avatarEmil A Eklund <eae@chromium.org>
Cr-Commit-Position: refs/heads/master@{#691080}
parent b696977a
<!DOCTYPE html>
<body>
<script src="../resources/runner.js"></script>
<script src="resources/line-layout-perf-test.js"></script>
<style>
#container {
width: 120ch;
white-space: pre-wrap;
overflow-wrap: break-word;
}
</style>
<pre id="container">A</pre>
<script>
const kNumberOfWords = 100;
const kSampleText = (() => {
const words = [];
for (let i = 1; i <= kNumberOfWords; ++i) {
words.push(TextGenerator.createWord(i % 12 + 3));
if (i % 10 === 0)
words.push('\n');
}
return words.join(' ');
})().repeat(1);
const kCount = 10;
const container = document.getElementById('container');
const editable = container.firstChild;
PerfTestRunner.measureRunsPerSecond({
run: function() {
for (let i = 0; i < kCount; ++i) {
editable.nodeValue = kSampleText;
PerfTestRunner.forceLayout();
editable.appendData('xyz');
PerfTestRunner.forceLayout();
}
},
});
</script>
<!DOCTYPE html>
<body>
<script src="../resources/runner.js"></script>
<script src="resources/line-layout-perf-test.js"></script>
<style>
#container {
width: 120ch;
white-space: pre-wrap;
overflow-wrap: break-word;
}
</style>
<pre id="container">A</pre>
<script>
const kNumberOfWords = 10;
const kSampleText = (() => {
const words = [];
for (let i = 1; i <= kNumberOfWords; ++i) {
words.push(TextGenerator.createWord(i % 12 + 3));
}
return words.join(' ');
})().repeat(1);
const kCount = 10;
const container = document.getElementById('container');
const editable = container.firstChild;
PerfTestRunner.measureRunsPerSecond({
run: function() {
for (let i = 0; i < kCount; ++i) {
editable.nodeValue = kSampleText;
PerfTestRunner.forceLayout();
editable.appendData('XYZ');
PerfTestRunner.forceLayout();
}
},
});
</script>
<!DOCTYPE html>
<body>
<script src="../resources/runner.js"></script>
<script src="resources/line-layout-perf-test.js"></script>
<style>
#container {
width: 120ch;
white-space: pre-wrap;
overflow-wrap: break-word;
}
</style>
<pre id="container">A</pre>
<script>
const kNumberOfWords = 100;
const kSampleText = (() => {
const words = [];
for (let i = 1; i <= kNumberOfWords; ++i) {
words.push(TextGenerator.createWord(i % 12 + 3));
if (i % 10 === 0)
words.push('\n');
}
return words.join(' ');
})().repeat(1);
const kCount = 10;
const container = document.getElementById('container');
const editable = container.firstChild;
PerfTestRunner.measureRunsPerSecond({
run: function() {
for (let i = 0; i < kCount; ++i) {
editable.nodeValue = kSampleText;
PerfTestRunner.forceLayout();
editable.deleteData(10, 20);
PerfTestRunner.forceLayout();
}
},
});
</script>
<!DOCTYPE html>
<body>
<script src="../resources/runner.js"></script>
<script src="resources/line-layout-perf-test.js"></script>
<style>
#container {
width: 120ch;
white-space: pre-wrap;
overflow-wrap: break-word;
}
</style>
<pre id="container">A</pre>
<script>
const kNumberOfWords = 100;
const kSampleText = (() => {
const words = [];
for (let i = 1; i <= kNumberOfWords; ++i) {
words.push(TextGenerator.createWord(i % 12 + 3));
if (i % 10 === 0)
words.push('\n');
}
return words.join(' ');
})().repeat(1);
const kCount = 10;
const container = document.getElementById('container');
const editable = container.firstChild;
PerfTestRunner.measureRunsPerSecond({
run: function() {
for (let i = 0; i < kCount; ++i) {
editable.nodeValue = kSampleText;
PerfTestRunner.forceLayout();
editable.insertData(10, 'xyz');
PerfTestRunner.forceLayout();
}
},
});
</script>
<!DOCTYPE html>
<body>
<script src="../resources/runner.js"></script>
<script src="resources/line-layout-perf-test.js"></script>
<style>
#container {
width: 120ch;
white-space: pre-wrap;
overflow-wrap: break-word;
}
</style>
<pre id="container">A</pre>
<script>
const kNumberOfWords = 100;
const kSampleText = (() => {
const words = [];
for (let i = 1; i <= kNumberOfWords; ++i) {
words.push(TextGenerator.createWord(i % 12 + 3));
if (i % 10 === 0)
words.push('\n');
}
return words.join(' ');
})().repeat(1);
const kCount = 10;
const container = document.getElementById('container');
const editable = container.firstChild;
PerfTestRunner.measureRunsPerSecond({
run: function() {
for (let i = 0; i < kCount; ++i) {
editable.nodeValue = kSampleText;
PerfTestRunner.forceLayout();
editable.insertData(0, 'xyz');
PerfTestRunner.forceLayout();
}
},
});
</script>
...@@ -1359,6 +1359,7 @@ jumbo_source_set("unit_tests") { ...@@ -1359,6 +1359,7 @@ jumbo_source_set("unit_tests") {
"layout/ng/exclusions/ng_exclusion_space_test.cc", "layout/ng/exclusions/ng_exclusion_space_test.cc",
"layout/ng/geometry/ng_box_strut_test.cc", "layout/ng/geometry/ng_box_strut_test.cc",
"layout/ng/geometry/ng_static_position_test.cc", "layout/ng/geometry/ng_static_position_test.cc",
"layout/ng/inline/layout_ng_text_test.cc",
"layout/ng/inline/ng_baseline_test.cc", "layout/ng/inline/ng_baseline_test.cc",
"layout/ng/inline/ng_caret_position_test.cc", "layout/ng/inline/ng_caret_position_test.cc",
"layout/ng/inline/ng_fragment_item_test.cc", "layout/ng/inline/ng_fragment_item_test.cc",
......
...@@ -1322,6 +1322,7 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, ...@@ -1322,6 +1322,7 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver,
void SetNeedsCollectInlines(); void SetNeedsCollectInlines();
void SetChildNeedsCollectInlines(); void SetChildNeedsCollectInlines();
void ClearNeedsCollectInlines() { SetNeedsCollectInlines(false); } void ClearNeedsCollectInlines() { SetNeedsCollectInlines(false); }
void SetNeedsCollectInlines(bool b) { bitfields_.SetNeedsCollectInlines(b); }
void MarkContainerChainForLayout(bool schedule_relayout = true, void MarkContainerChainForLayout(bool schedule_relayout = true,
SubtreeLayoutScope* = nullptr); SubtreeLayoutScope* = nullptr);
...@@ -3245,7 +3246,6 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, ...@@ -3245,7 +3246,6 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver,
void SetNeedsSimplifiedNormalFlowLayout(bool b) { void SetNeedsSimplifiedNormalFlowLayout(bool b) {
bitfields_.SetNeedsSimplifiedNormalFlowLayout(b); bitfields_.SetNeedsSimplifiedNormalFlowLayout(b);
} }
void SetNeedsCollectInlines(bool b) { bitfields_.SetNeedsCollectInlines(b); }
private: private:
friend class LineLayoutItem; friend class LineLayoutItem;
......
...@@ -1662,6 +1662,16 @@ void LayoutText::SetTextWithOffset(scoped_refptr<StringImpl> text, ...@@ -1662,6 +1662,16 @@ void LayoutText::SetTextWithOffset(scoped_refptr<StringImpl> text,
} }
} }
if (NGInlineNode::SetTextWithOffset(this, text, offset, len)) {
DCHECK(!NeedsCollectInlines());
// Prevent |TextDidChange()| to propagate |NeedsCollectInlines|
SetNeedsCollectInlines(true);
TextDidChange();
valid_ng_items_ = true;
ClearNeedsCollectInlines();
return;
}
unsigned old_len = TextLength(); unsigned old_len = TextLength();
unsigned new_len = text->length(); unsigned new_len = text->length();
int delta = new_len - old_len; int delta = new_len - old_len;
......
...@@ -192,6 +192,7 @@ class CORE_EXPORT LayoutText : public LayoutObject { ...@@ -192,6 +192,7 @@ class CORE_EXPORT LayoutText : public LayoutObject {
void SetTextWithOffset(scoped_refptr<StringImpl>, void SetTextWithOffset(scoped_refptr<StringImpl>,
unsigned offset, unsigned offset,
unsigned len); unsigned len);
void SetTextInternal(scoped_refptr<StringImpl>);
virtual void TransformText(); virtual void TransformText();
...@@ -322,11 +323,12 @@ class CORE_EXPORT LayoutText : public LayoutObject { ...@@ -322,11 +323,12 @@ class CORE_EXPORT LayoutText : public LayoutObject {
void SetHasBidiControlInlineItems() { has_bidi_control_items_ = true; } void SetHasBidiControlInlineItems() { has_bidi_control_items_ = true; }
void ClearHasBidiControlInlineItems() { has_bidi_control_items_ = false; } void ClearHasBidiControlInlineItems() { has_bidi_control_items_ = false; }
protected:
virtual const base::span<NGInlineItem>* GetNGInlineItems() const { virtual const base::span<NGInlineItem>* GetNGInlineItems() const {
return nullptr; return nullptr;
} }
virtual base::span<NGInlineItem>* GetNGInlineItems() { return nullptr; } virtual base::span<NGInlineItem>* GetNGInlineItems() { return nullptr; }
protected:
void WillBeDestroyed() override; void WillBeDestroyed() override;
void StyleWillChange(StyleDifference, const ComputedStyle&) final {} void StyleWillChange(StyleDifference, const ComputedStyle&) final {}
...@@ -334,7 +336,6 @@ class CORE_EXPORT LayoutText : public LayoutObject { ...@@ -334,7 +336,6 @@ class CORE_EXPORT LayoutText : public LayoutObject {
void InLayoutNGInlineFormattingContextWillChange(bool) final; void InLayoutNGInlineFormattingContextWillChange(bool) final;
void SetTextInternal(scoped_refptr<StringImpl>);
virtual void TextDidChange(); virtual void TextDidChange();
virtual InlineTextBox* CreateTextBox(int start, virtual InlineTextBox* CreateTextBox(int start,
......
// Copyright 2019 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/layout_ng_text.h"
#include <sstream>
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/layout/layout_block_flow.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
namespace blink {
class LayoutNGTextTest : public PageTestBase {
protected:
std::string GetItemsAsString(const LayoutText& layout_text) {
if (layout_text.NeedsCollectInlines())
return "LayoutText has NeedsCollectInlines";
if (!layout_text.HasValidInlineItems())
return "No valid inline items in LayoutText";
const LayoutBlockFlow& block_flow = *layout_text.ContainingNGBlockFlow();
if (block_flow.NeedsCollectInlines())
return "LayoutBlockFlow has NeedsCollectInlines";
const NGInlineNodeData& data = *block_flow.GetNGInlineNodeData();
std::ostringstream stream;
for (const NGInlineItem& item : data.items) {
if (item.Type() != NGInlineItem::kText)
continue;
if (item.GetLayoutObject() == layout_text)
stream << "*";
stream << "{'"
<< data.text_content.Substring(item.StartOffset(), item.Length())
.Utf8()
<< "'";
if (item.TextShapeResult()) {
stream << ", ShapeResult=" << item.TextShapeResult()->StartIndex()
<< "+" << item.TextShapeResult()->NumCharacters();
}
stream << "}" << std::endl;
}
return stream.str();
}
};
TEST_F(LayoutNGTextTest, SetTextWithOffsetAppendCollapseWhiteSpace) {
if (!RuntimeEnabledFeatures::LayoutNGEnabled())
return;
SetBodyInnerHTML(u"<p id=target>abc </p>");
Text& text = To<Text>(*GetElementById("target")->firstChild());
text.appendData("XYZ");
EXPECT_EQ("*{'abc XYZ', ShapeResult=0+7}\n",
GetItemsAsString(*text.GetLayoutObject()));
}
TEST_F(LayoutNGTextTest, SetTextWithOffsetAppend) {
if (!RuntimeEnabledFeatures::LayoutNGEnabled())
return;
SetBodyInnerHTML(u"<pre id=target><a>abc</a>XYZ<b>def</b></pre>");
Text& text = To<Text>(*GetElementById("target")->firstChild()->nextSibling());
text.appendData("xyz");
EXPECT_EQ(
"{'abc', ShapeResult=0+3}\n"
"*{'XYZxyz', ShapeResult=3+6}\n"
"{'def', ShapeResult=9+3}\n",
GetItemsAsString(*text.GetLayoutObject()));
}
TEST_F(LayoutNGTextTest, SetTextWithOffsetDelete) {
if (!RuntimeEnabledFeatures::LayoutNGEnabled())
return;
SetBodyInnerHTML(u"<pre id=target><a>abc</a>xXYZyz<b>def</b></pre>");
Text& text = To<Text>(*GetElementById("target")->firstChild()->nextSibling());
text.deleteData(1, 3, ASSERT_NO_EXCEPTION);
EXPECT_EQ(
"{'abc', ShapeResult=0+3}\n"
"*{'xyz', ShapeResult=3+3}\n"
"{'def', ShapeResult=6+3}\n",
GetItemsAsString(*text.GetLayoutObject()));
}
TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteCollapseWhiteSpace) {
if (!RuntimeEnabledFeatures::LayoutNGEnabled())
return;
SetBodyInnerHTML(u"<p id=target>ab XY cd</p>");
Text& text = To<Text>(*GetElementById("target")->firstChild());
text.deleteData(4, 2, ASSERT_NO_EXCEPTION); // remove "XY"
EXPECT_EQ("*{'ab cd', ShapeResult=0+5}\n",
GetItemsAsString(*text.GetLayoutObject()));
}
TEST_F(LayoutNGTextTest, SetTextWithOffsetInsert) {
if (!RuntimeEnabledFeatures::LayoutNGEnabled())
return;
SetBodyInnerHTML(u"<pre id=target><a>abc</a>XYZ<b>def</b></pre>");
Text& text = To<Text>(*GetElementById("target")->firstChild()->nextSibling());
text.insertData(1, "xyz", ASSERT_NO_EXCEPTION);
EXPECT_EQ(
"{'abc', ShapeResult=0+3}\n"
"*{'XxyzYZ', ShapeResult=3+6}\n"
"{'def', ShapeResult=9+3}\n",
GetItemsAsString(*text.GetLayoutObject()));
}
TEST_F(LayoutNGTextTest, SetTextWithOffsetInsertAfterSpace) {
if (!RuntimeEnabledFeatures::LayoutNGEnabled())
return;
SetBodyInnerHTML(u"<p id=target>ab cd</p>");
Text& text = To<Text>(*GetElementById("target")->firstChild());
text.insertData(3, " XYZ ", ASSERT_NO_EXCEPTION);
EXPECT_EQ("*{'ab XYZ cd', ShapeResult=0+9}\n",
GetItemsAsString(*text.GetLayoutObject()));
}
TEST_F(LayoutNGTextTest, SetTextWithOffsetInserBeforetSpace) {
if (!RuntimeEnabledFeatures::LayoutNGEnabled())
return;
SetBodyInnerHTML(u"<p id=target>ab cd</p>");
Text& text = To<Text>(*GetElementById("target")->firstChild());
text.insertData(2, " XYZ ", ASSERT_NO_EXCEPTION);
EXPECT_EQ("*{'ab XYZ cd', ShapeResult=0+9}\n",
GetItemsAsString(*text.GetLayoutObject()));
}
TEST_F(LayoutNGTextTest, SetTextWithOffsetNoRelocation) {
if (!RuntimeEnabledFeatures::LayoutNGEnabled())
return;
SetBodyInnerHTML(u"<pre id=target><a>abc</a>XYZ<b>def</b></pre>");
Text& text = To<Text>(*GetElementById("target")->firstChild()->nextSibling());
// Note: |CharacterData::setData()| is implementation of Node::setNodeValue()
// for |CharacterData|.
text.setData("xyz");
EXPECT_EQ("LayoutText has NeedsCollectInlines",
GetItemsAsString(*text.GetLayoutObject()))
<< "There are no optimization for setData()";
}
TEST_F(LayoutNGTextTest, SetTextWithOffsetPrepend) {
if (!RuntimeEnabledFeatures::LayoutNGEnabled())
return;
SetBodyInnerHTML(u"<pre id=target><a>abc</a>XYZ<b>def</b></pre>");
Text& text = To<Text>(*GetElementById("target")->firstChild()->nextSibling());
text.insertData(1, "xyz", ASSERT_NO_EXCEPTION);
EXPECT_EQ(
"{'abc', ShapeResult=0+3}\n"
"*{'XxyzYZ', ShapeResult=3+6}\n"
"{'def', ShapeResult=9+3}\n",
GetItemsAsString(*text.GetLayoutObject()));
}
TEST_F(LayoutNGTextTest, SetTextWithOffsetReplace) {
if (!RuntimeEnabledFeatures::LayoutNGEnabled())
return;
SetBodyInnerHTML(u"<pre id=target><a>abc</a>XYZW<b>def</b></pre>");
Text& text = To<Text>(*GetElementById("target")->firstChild()->nextSibling());
text.replaceData(1, 2, "yz", ASSERT_NO_EXCEPTION);
EXPECT_EQ(
"{'abc', ShapeResult=0+3}\n"
"*{'XyzW', ShapeResult=3+4}\n"
"{'def', ShapeResult=7+3}\n",
GetItemsAsString(*text.GetLayoutObject()));
}
TEST_F(LayoutNGTextTest, SetTextWithOffsetReplaceCollapseWhiteSpace) {
if (!RuntimeEnabledFeatures::LayoutNGEnabled())
return;
SetBodyInnerHTML(u"<p id=target>ab XY cd</p>");
Text& text = To<Text>(*GetElementById("target")->firstChild());
text.replaceData(4, 2, " ", ASSERT_NO_EXCEPTION); // replace "XY" to " "
EXPECT_EQ("*{'ab cd', ShapeResult=0+5}\n",
GetItemsAsString(*text.GetLayoutObject()));
}
TEST_F(LayoutNGTextTest, SetTextWithOffsetReplaceToExtend) {
if (!RuntimeEnabledFeatures::LayoutNGEnabled())
return;
SetBodyInnerHTML(u"<pre id=target><a>abc</a>XYZW<b>def</b></pre>");
Text& text = To<Text>(*GetElementById("target")->firstChild()->nextSibling());
text.replaceData(1, 2, "xyz", ASSERT_NO_EXCEPTION);
EXPECT_EQ(
"{'abc', ShapeResult=0+3}\n"
"*{'XxyzW', ShapeResult=3+5}\n"
"{'def', ShapeResult=8+3}\n",
GetItemsAsString(*text.GetLayoutObject()));
}
TEST_F(LayoutNGTextTest, SetTextWithOffsetReplaceToShrink) {
if (!RuntimeEnabledFeatures::LayoutNGEnabled())
return;
SetBodyInnerHTML(u"<pre id=target><a>abc</a>XYZW<b>def</b></pre>");
Text& text = To<Text>(*GetElementById("target")->firstChild()->nextSibling());
text.replaceData(1, 2, "y", ASSERT_NO_EXCEPTION);
EXPECT_EQ(
"{'abc', ShapeResult=0+3}\n"
"*{'XyW', ShapeResult=3+3}\n"
"{'def', ShapeResult=6+3}\n",
GetItemsAsString(*text.GetLayoutObject()));
}
TEST_F(LayoutNGTextTest, SetTextWithOffsetToEmpty) {
if (!RuntimeEnabledFeatures::LayoutNGEnabled())
return;
SetBodyInnerHTML(u"<pre id=target><a>abc</a>XYZ<b>def</b></pre>");
Text& text = To<Text>(*GetElementById("target")->firstChild()->nextSibling());
// Note: |CharacterData::setData()| is implementation of Node::setNodeValue()
// for |CharacterData|.
// Note: |setData()| detaches layout object from |Text| node since
// |Text::TextLayoutObjectIsNeeded()| returns false for empty text.
text.setData("");
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(nullptr, text.GetLayoutObject());
}
} // namespace blink
...@@ -250,6 +250,7 @@ class CORE_EXPORT NGInlineItem { ...@@ -250,6 +250,7 @@ class CORE_EXPORT NGInlineItem {
unsigned is_symbol_marker_ : 1; unsigned is_symbol_marker_ : 1;
unsigned is_generated_for_line_break_ : 1; unsigned is_generated_for_line_break_ : 1;
friend class NGInlineNode; friend class NGInlineNode;
friend class NGInlineNodeDataEditor;
}; };
inline void NGInlineItem::AssertOffset(unsigned offset) const { inline void NGInlineItem::AssertOffset(unsigned offset) const {
......
...@@ -78,6 +78,15 @@ class CORE_EXPORT NGInlineNode : public NGLayoutInputNode { ...@@ -78,6 +78,15 @@ class CORE_EXPORT NGInlineNode : public NGLayoutInputNode {
static void ClearAssociatedFragments(const NGPhysicalFragment& fragment, static void ClearAssociatedFragments(const NGPhysicalFragment& fragment,
const NGBlockBreakToken* break_token); const NGBlockBreakToken* break_token);
// Returns true if we don't need to collect inline items after replacing
// |layout_text| after deleting replacing subtext from |offset| to |length|
// |new_text| is new text of |layout_text|.
// This is optimized version of |PrepareLayout()|.
static bool SetTextWithOffset(LayoutText* layout_text,
scoped_refptr<StringImpl> new_text,
unsigned offset,
unsigned length);
// Returns the DOM to text content offset mapping of this block. If it is not // Returns the DOM to text content offset mapping of this block. If it is not
// computed before, compute and store it in NGInlineNodeData. // computed before, compute and store it in NGInlineNodeData.
// This funciton must be called with clean layout. // This funciton must be called with clean layout.
...@@ -124,7 +133,8 @@ class CORE_EXPORT NGInlineNode : public NGLayoutInputNode { ...@@ -124,7 +133,8 @@ class CORE_EXPORT NGInlineNode : public NGLayoutInputNode {
void SegmentFontOrientation(NGInlineNodeData*); void SegmentFontOrientation(NGInlineNodeData*);
void SegmentBidiRuns(NGInlineNodeData*); void SegmentBidiRuns(NGInlineNodeData*);
void ShapeText(NGInlineItemsData*, void ShapeText(NGInlineItemsData*,
NGInlineItemsData* previous_data = nullptr); const String* previous_text = nullptr,
const Vector<NGInlineItem>* previous_items = nullptr);
void ShapeTextForFirstLineIfNeeded(NGInlineNodeData*); void ShapeTextForFirstLineIfNeeded(NGInlineNodeData*);
void AssociateItemsWithInlines(NGInlineNodeData*); void AssociateItemsWithInlines(NGInlineNodeData*);
......
...@@ -945,15 +945,6 @@ TEST_F(NGInlineNodeTest, InvalidateSetText) { ...@@ -945,15 +945,6 @@ TEST_F(NGInlineNodeTest, InvalidateSetText) {
EXPECT_TRUE(layout_block_flow_->NeedsCollectInlines()); EXPECT_TRUE(layout_block_flow_->NeedsCollectInlines());
} }
TEST_F(NGInlineNodeTest, InvalidateSetTextWithOffset) {
SetupHtml("t", "<div id=t>before</div>");
EXPECT_FALSE(layout_block_flow_->NeedsCollectInlines());
LayoutText* text = ToLayoutText(layout_block_flow_->FirstChild());
text->SetTextWithOffset(String("after").Impl(), 1, 4);
EXPECT_TRUE(layout_block_flow_->NeedsCollectInlines());
}
TEST_F(NGInlineNodeTest, InvalidateAddAbsolute) { TEST_F(NGInlineNodeTest, InvalidateAddAbsolute) {
SetupHtml("t", SetupHtml("t",
"<style>span { position: absolute; }</style>" "<style>span { position: absolute; }</style>"
......
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