Index: third_party/WebKit/Source/core/layout/ng/inline/ng_inline_items_builder.cc |
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_items_builder.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_items_builder.cc |
index 1038485901038a4a211dbd51b71fa06440c5c91e..8533bd9907a97669386f62a25511a084d274f610 100644 |
--- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_items_builder.cc |
+++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_items_builder.cc |
@@ -21,8 +21,7 @@ String NGInlineItemsBuilder::ToString() { |
// lines and collapsible spaces in Phase I. |
// [1] https://drafts.csswg.org/css-text-3/#line-break-transform |
// [2] https://drafts.csswg.org/css-text-3/#white-space-phase-2 |
- unsigned next_start_offset = text_.length(); |
- RemoveTrailingCollapsibleSpaceIfExists(&next_start_offset); |
+ RemoveTrailingCollapsibleSpaceIfExists(); |
return text_.ToString(); |
} |
@@ -174,7 +173,8 @@ void NGInlineItemsBuilder::AppendWithWhiteSpaceCollapsing( |
} |
if (last_collapsible_space_ == CollapsibleSpace::kNewline) { |
- RemoveTrailingCollapsibleNewlineIfNeeded(&start_offset, string, i, style); |
+ RemoveTrailingCollapsibleNewlineIfNeeded(string, i, style); |
+ start_offset = std::min(start_offset, text_.length()); |
} |
size_t end_of_non_space = string.Find(IsCollapsibleSpace, i + 1); |
@@ -240,8 +240,7 @@ void NGInlineItemsBuilder::AppendWithPreservingNewlines( |
void NGInlineItemsBuilder::AppendForcedBreak(const ComputedStyle* style, |
LayoutObject* layout_object) { |
// Remove collapsible spaces immediately before a preserved newline. |
- unsigned start_offset = text_.length(); |
- RemoveTrailingCollapsibleSpaceIfExists(&start_offset); |
+ RemoveTrailingCollapsibleSpaceIfExists(); |
Append(NGInlineItem::kControl, kNewlineCharacter, style, layout_object); |
@@ -262,15 +261,23 @@ void NGInlineItemsBuilder::Append(NGInlineItem::NGInlineItemType type, |
last_collapsible_space_ = CollapsibleSpace::kNone; |
} |
-void NGInlineItemsBuilder::Append(NGInlineItem::NGInlineItemType type, |
- const ComputedStyle* style, |
- LayoutObject* layout_object) { |
+void NGInlineItemsBuilder::AppendOpaque(NGInlineItem::NGInlineItemType type, |
+ UChar character) { |
+ text_.Append(character); |
+ unsigned end_offset = text_.length(); |
+ AppendItem(items_, type, end_offset - 1, end_offset, nullptr, nullptr); |
+} |
+ |
+void NGInlineItemsBuilder::AppendOpaque(NGInlineItem::NGInlineItemType type, |
+ const ComputedStyle* style, |
+ LayoutObject* layout_object) { |
unsigned end_offset = text_.length(); |
AppendItem(items_, type, end_offset, end_offset, style, layout_object); |
} |
+// Removes the collapsible newline at the end of |text_| if exists and the |
+// removal conditions met. |
void NGInlineItemsBuilder::RemoveTrailingCollapsibleNewlineIfNeeded( |
- unsigned* next_start_offset, |
const String& after, |
unsigned after_index, |
const ComputedStyle* after_style) { |
@@ -287,57 +294,67 @@ void NGInlineItemsBuilder::RemoveTrailingCollapsibleNewlineIfNeeded( |
} |
if (ShouldRemoveNewline(text_, before_style, after, after_index, after_style)) |
- RemoveTrailingCollapsibleSpace(next_start_offset); |
+ RemoveTrailingCollapsibleSpace(text_.length() - 1); |
} |
-void NGInlineItemsBuilder::RemoveTrailingCollapsibleSpaceIfExists( |
- unsigned* next_start_offset) { |
- if (last_collapsible_space_ != CollapsibleSpace::kNone && !text_.IsEmpty() && |
- text_[text_.length() - 1] == kSpaceCharacter) |
- RemoveTrailingCollapsibleSpace(next_start_offset); |
+// Removes the collapsible space at the end of |text_| if exists. |
+void NGInlineItemsBuilder::RemoveTrailingCollapsibleSpaceIfExists() { |
+ if (last_collapsible_space_ == CollapsibleSpace::kNone || text_.IsEmpty()) |
+ return; |
+ |
+ // Look for the last space character since characters that are opaque to |
+ // whitespace collapsing may be appended. |
+ for (unsigned i = text_.length(); i;) { |
+ UChar ch = text_[--i]; |
+ if (ch == kSpaceCharacter) { |
+ RemoveTrailingCollapsibleSpace(i); |
+ return; |
+ } |
+ |
+ // AppendForcedBreak sets CollapsibleSpace::kSpace to ignore leading |
+ // spaces. In this case, the trailing collapsible space does not exist. |
+ if (ch == kNewlineCharacter) |
+ return; |
+ } |
+ NOTREACHED(); |
} |
-void NGInlineItemsBuilder::RemoveTrailingCollapsibleSpace( |
- unsigned* next_start_offset) { |
+// Removes the collapsible space at the specified index. |
+void NGInlineItemsBuilder::RemoveTrailingCollapsibleSpace(unsigned index) { |
DCHECK_NE(last_collapsible_space_, CollapsibleSpace::kNone); |
DCHECK(!text_.IsEmpty()); |
- DCHECK_EQ(text_[text_.length() - 1], kSpaceCharacter); |
+ DCHECK_EQ(text_[index], kSpaceCharacter); |
- unsigned new_size = text_.length() - 1; |
- text_.Resize(new_size); |
+ text_.erase(index); |
last_collapsible_space_ = CollapsibleSpace::kNone; |
- if (*next_start_offset <= new_size) |
- return; |
- *next_start_offset = new_size; |
- |
- // Adjust the last item if the removed space is already appended. |
+ // Adjust items if the removed space is already included. |
for (unsigned i = items_->size(); i > 0;) { |
NGInlineItem& item = (*items_)[--i]; |
- DCHECK_EQ(item.EndOffset(), new_size + 1); |
- if (item.Type() == NGInlineItem::kText) { |
- DCHECK_GE(item.Length(), 1u); |
- if (item.Length() > 1) |
- item.SetEndOffset(new_size); |
- else |
+ if (index >= item.EndOffset()) |
+ return; |
+ if (item.StartOffset() <= index) { |
+ if (item.Length() == 1) { |
+ DCHECK_EQ(item.StartOffset(), index); |
+ DCHECK_EQ(item.Type(), NGInlineItem::kText); |
items_->erase(i); |
- break; |
- } |
- if (!item.Length()) { |
- // Trailing spaces can be removed across non-character items. |
- item.SetOffset(new_size, new_size); |
- continue; |
+ } else { |
+ item.SetEndOffset(item.EndOffset() - 1); |
+ } |
+ return; |
} |
- NOTREACHED(); |
- break; |
+ |
+ // Trailing spaces can be removed across non-character items. |
+ // Adjust their offsets if after the removed index. |
+ item.SetOffset(item.StartOffset() - 1, item.EndOffset() - 1); |
} |
} |
void NGInlineItemsBuilder::AppendBidiControl(const ComputedStyle* style, |
UChar ltr, |
UChar rtl) { |
- Append(NGInlineItem::kBidiControl, |
- style->Direction() == TextDirection::kRtl ? rtl : ltr); |
+ AppendOpaque(NGInlineItem::kBidiControl, |
+ IsLtr(style->Direction()) ? ltr : rtl); |
} |
void NGInlineItemsBuilder::EnterBlock(const ComputedStyle* style) { |
@@ -389,11 +406,11 @@ void NGInlineItemsBuilder::EnterInline(LayoutObject* node) { |
Enter(node, kPopDirectionalIsolateCharacter); |
break; |
case UnicodeBidi::kPlaintext: |
- Append(NGInlineItem::kBidiControl, kFirstStrongIsolateCharacter); |
+ AppendOpaque(NGInlineItem::kBidiControl, kFirstStrongIsolateCharacter); |
Enter(node, kPopDirectionalIsolateCharacter); |
break; |
case UnicodeBidi::kIsolateOverride: |
- Append(NGInlineItem::kBidiControl, kFirstStrongIsolateCharacter); |
+ AppendOpaque(NGInlineItem::kBidiControl, kFirstStrongIsolateCharacter); |
AppendBidiControl(style, kLeftToRightOverrideCharacter, |
kRightToLeftOverrideCharacter); |
Enter(node, kPopDirectionalIsolateCharacter); |
@@ -401,7 +418,7 @@ void NGInlineItemsBuilder::EnterInline(LayoutObject* node) { |
break; |
} |
- Append(NGInlineItem::kOpenTag, style, node); |
+ AppendOpaque(NGInlineItem::kOpenTag, style, node); |
} |
void NGInlineItemsBuilder::Enter(LayoutObject* node, UChar character_to_exit) { |
@@ -416,14 +433,14 @@ void NGInlineItemsBuilder::ExitBlock() { |
void NGInlineItemsBuilder::ExitInline(LayoutObject* node) { |
DCHECK(node); |
- Append(NGInlineItem::kCloseTag, node->Style(), node); |
+ AppendOpaque(NGInlineItem::kCloseTag, node->Style(), node); |
Exit(node); |
} |
void NGInlineItemsBuilder::Exit(LayoutObject* node) { |
while (!exits_.IsEmpty() && exits_.back().node == node) { |
- Append(NGInlineItem::kBidiControl, exits_.back().character); |
+ AppendOpaque(NGInlineItem::kBidiControl, exits_.back().character); |
exits_.pop_back(); |
} |
} |