| 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..49490f85df0dc26a1f85a3dae1b9f33c9bfc456d 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
|
| @@ -15,15 +15,17 @@ NGInlineItemsBuilder::~NGInlineItemsBuilder() {
|
| DCHECK_EQ(text_.length(), items_->IsEmpty() ? 0 : items_->back().EndOffset());
|
| }
|
|
|
| -String NGInlineItemsBuilder::ToString() {
|
| +bool NGInlineItemsBuilder::Finalize() {
|
| // Segment Break Transformation Rules[1] defines to keep trailing new lines,
|
| // but it will be removed in Phase II[2]. We prefer not to add trailing new
|
| // 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);
|
| + return RemoveTrailingCollapsibleSpaceIfExists(&next_start_offset);
|
| +}
|
|
|
| +String NGInlineItemsBuilder::ToString() {
|
| return text_.ToString();
|
| }
|
|
|
| @@ -124,11 +126,34 @@ static inline bool IsControlItemCharacter(UChar c) {
|
| return c == kTabulationCharacter || c == kNewlineCharacter;
|
| }
|
|
|
| -void NGInlineItemsBuilder::Append(const String& string,
|
| - const ComputedStyle* style,
|
| - LayoutObject* layout_object) {
|
| +static inline size_t GetLastAppendedCharacterIndex(
|
| + const Vector<unsigned>& collapsed_indexes,
|
| + unsigned start,
|
| + unsigned end) {
|
| + if (start == end || collapsed_indexes.size() == end - start)
|
| + return kNotFound;
|
| + if (collapsed_indexes.IsEmpty())
|
| + return end - 1;
|
| + DCHECK_GT(end, collapsed_indexes.back());
|
| + if (collapsed_indexes.back() + 1 < end)
|
| + return collapsed_indexes.back() + 1;
|
| + for (unsigned i = collapsed_indexes.size(); i > 1;) {
|
| + unsigned current_collapsed = collapsed_indexes[--i];
|
| + unsigned last_collapsed = collapsed_indexes[i - 1];
|
| + DCHECK_GT(current_collapsed, last_collapsed);
|
| + if (last_collapsed + 1 < current_collapsed)
|
| + return last_collapsed + 1;
|
| + }
|
| + DCHECK_GT(collapsed_indexes[0], start);
|
| + return collapsed_indexes[0] - 1;
|
| +}
|
| +
|
| +NGInlineItemsBuilder::AppendResult NGInlineItemsBuilder::Append(
|
| + const String& string,
|
| + const ComputedStyle* style,
|
| + LayoutObject* layout_object) {
|
| if (string.IsEmpty())
|
| - return;
|
| + return AppendResult(false, {});
|
| text_.ReserveCapacity(string.length());
|
|
|
| EWhiteSpace whitespace = style->WhiteSpace();
|
| @@ -137,28 +162,30 @@ void NGInlineItemsBuilder::Append(const String& string,
|
| if (ComputedStyle::PreserveNewline(whitespace) && !is_svgtext_)
|
| return AppendWithPreservingNewlines(string, style, layout_object);
|
|
|
| - AppendWithWhiteSpaceCollapsing(string, 0, string.length(), style,
|
| - layout_object);
|
| + return AppendWithWhiteSpaceCollapsing(string, 0, string.length(), style,
|
| + layout_object);
|
| }
|
|
|
| -void NGInlineItemsBuilder::AppendWithWhiteSpaceCollapsing(
|
| +NGInlineItemsBuilder::AppendResult
|
| +NGInlineItemsBuilder::AppendWithWhiteSpaceCollapsing(
|
| const String& string,
|
| unsigned start,
|
| unsigned end,
|
| const ComputedStyle* style,
|
| LayoutObject* layout_object) {
|
| + AppendResult result(false, {});
|
| unsigned start_offset = text_.length();
|
| for (unsigned i = start; i < end;) {
|
| UChar c = string[i];
|
| if (c == kNewlineCharacter) {
|
| // LayoutBR does not set preserve_newline, but should be preserved.
|
| - if (!i && end == 1 && layout_object && layout_object->IsBR()) {
|
| - AppendForcedBreak(style, layout_object);
|
| - return;
|
| - }
|
| + if (!i && end == 1 && layout_object && layout_object->IsBR())
|
| + return AppendResult(AppendForcedBreak(style, layout_object), {});
|
|
|
| if (last_collapsible_space_ == CollapsibleSpace::kNone)
|
| text_.Append(kSpaceCharacter);
|
| + else
|
| + result.second.push_back(i);
|
| last_collapsible_space_ = CollapsibleSpace::kNewline;
|
| i++;
|
| continue;
|
| @@ -168,13 +195,26 @@ void NGInlineItemsBuilder::AppendWithWhiteSpaceCollapsing(
|
| if (last_collapsible_space_ == CollapsibleSpace::kNone) {
|
| text_.Append(kSpaceCharacter);
|
| last_collapsible_space_ = CollapsibleSpace::kSpace;
|
| + } else {
|
| + result.second.push_back(i);
|
| }
|
| i++;
|
| continue;
|
| }
|
|
|
| if (last_collapsible_space_ == CollapsibleSpace::kNewline) {
|
| - RemoveTrailingCollapsibleNewlineIfNeeded(&start_offset, string, i, style);
|
| + bool removed = RemoveTrailingCollapsibleNewlineIfNeeded(&start_offset,
|
| + string, i, style);
|
| + if (removed) {
|
| + const size_t last_appended =
|
| + GetLastAppendedCharacterIndex(result.second, start, i);
|
| + if (last_appended == kNotFound) {
|
| + result.first = true;
|
| + } else {
|
| + result.second.push_back(last_appended);
|
| + std::sort(result.second.begin(), result.second.end());
|
| + }
|
| + }
|
| }
|
|
|
| size_t end_of_non_space = string.Find(IsCollapsibleSpace, i + 1);
|
| @@ -189,11 +229,14 @@ void NGInlineItemsBuilder::AppendWithWhiteSpaceCollapsing(
|
| AppendItem(items_, NGInlineItem::kText, start_offset, text_.length(), style,
|
| layout_object);
|
| }
|
| +
|
| + return result;
|
| }
|
|
|
| // Even when without whitespace collapsing, control characters (newlines and
|
| // tabs) are in their own control items to make the line breaker easier.
|
| -void NGInlineItemsBuilder::AppendWithoutWhiteSpaceCollapsing(
|
| +NGInlineItemsBuilder::AppendResult
|
| +NGInlineItemsBuilder::AppendWithoutWhiteSpaceCollapsing(
|
| const String& string,
|
| const ComputedStyle* style,
|
| LayoutObject* layout_object) {
|
| @@ -216,15 +259,27 @@ void NGInlineItemsBuilder::AppendWithoutWhiteSpaceCollapsing(
|
| }
|
|
|
| last_collapsible_space_ = CollapsibleSpace::kNone;
|
| + return AppendResult(false, {});
|
| }
|
|
|
| -void NGInlineItemsBuilder::AppendWithPreservingNewlines(
|
| +NGInlineItemsBuilder::AppendResult
|
| +NGInlineItemsBuilder::AppendWithPreservingNewlines(
|
| const String& string,
|
| const ComputedStyle* style,
|
| LayoutObject* layout_object) {
|
| + AppendResult result(false, {});
|
| for (unsigned start = 0; start < string.length();) {
|
| if (string[start] == kNewlineCharacter) {
|
| - AppendForcedBreak(style, layout_object);
|
| + if (AppendForcedBreak(style, layout_object)) {
|
| + const size_t last_appended =
|
| + GetLastAppendedCharacterIndex(result.second, 0, start);
|
| + if (last_appended == kNotFound) {
|
| + result.first = true;
|
| + } else {
|
| + result.second.push_back(last_appended);
|
| + std::sort(result.second.begin(), result.second.end());
|
| + }
|
| + }
|
| start++;
|
| continue;
|
| }
|
| @@ -232,21 +287,35 @@ void NGInlineItemsBuilder::AppendWithPreservingNewlines(
|
| size_t end = string.find(kNewlineCharacter, start + 1);
|
| if (end == kNotFound)
|
| end = string.length();
|
| - AppendWithWhiteSpaceCollapsing(string, start, end, style, layout_object);
|
| + AppendResult run_result = AppendWithWhiteSpaceCollapsing(
|
| + string, start, end, style, layout_object);
|
| + if (run_result.first) {
|
| + const size_t last_appended =
|
| + GetLastAppendedCharacterIndex(result.second, 0, start);
|
| + if (last_appended == kNotFound) {
|
| + result.first = true;
|
| + } else {
|
| + result.second.push_back(last_appended);
|
| + std::sort(result.second.begin(), result.second.end());
|
| + }
|
| + }
|
| + result.second.AppendVector(run_result.second);
|
| start = end;
|
| }
|
| + return result;
|
| }
|
|
|
| -void NGInlineItemsBuilder::AppendForcedBreak(const ComputedStyle* style,
|
| +bool NGInlineItemsBuilder::AppendForcedBreak(const ComputedStyle* style,
|
| LayoutObject* layout_object) {
|
| // Remove collapsible spaces immediately before a preserved newline.
|
| unsigned start_offset = text_.length();
|
| - RemoveTrailingCollapsibleSpaceIfExists(&start_offset);
|
| + bool result = RemoveTrailingCollapsibleSpaceIfExists(&start_offset);
|
|
|
| Append(NGInlineItem::kControl, kNewlineCharacter, style, layout_object);
|
|
|
| // Remove collapsible spaces immediately after a preserved newline.
|
| last_collapsible_space_ = CollapsibleSpace::kSpace;
|
| + return result;
|
| }
|
|
|
| void NGInlineItemsBuilder::Append(NGInlineItem::NGInlineItemType type,
|
| @@ -269,7 +338,7 @@ void NGInlineItemsBuilder::Append(NGInlineItem::NGInlineItemType type,
|
| AppendItem(items_, type, end_offset, end_offset, style, layout_object);
|
| }
|
|
|
| -void NGInlineItemsBuilder::RemoveTrailingCollapsibleNewlineIfNeeded(
|
| +bool NGInlineItemsBuilder::RemoveTrailingCollapsibleNewlineIfNeeded(
|
| unsigned* next_start_offset,
|
| const String& after,
|
| unsigned after_index,
|
| @@ -277,7 +346,7 @@ void NGInlineItemsBuilder::RemoveTrailingCollapsibleNewlineIfNeeded(
|
| DCHECK_EQ(last_collapsible_space_, CollapsibleSpace::kNewline);
|
|
|
| if (text_.IsEmpty() || text_[text_.length() - 1] != kSpaceCharacter)
|
| - return;
|
| + return false;
|
|
|
| const ComputedStyle* before_style = after_style;
|
| if (!items_->IsEmpty()) {
|
| @@ -286,15 +355,20 @@ void NGInlineItemsBuilder::RemoveTrailingCollapsibleNewlineIfNeeded(
|
| before_style = item.Style();
|
| }
|
|
|
| - if (ShouldRemoveNewline(text_, before_style, after, after_index, after_style))
|
| - RemoveTrailingCollapsibleSpace(next_start_offset);
|
| + if (!ShouldRemoveNewline(text_, before_style, after, after_index,
|
| + after_style))
|
| + return false;
|
| + RemoveTrailingCollapsibleSpace(next_start_offset);
|
| + return true;
|
| }
|
|
|
| -void NGInlineItemsBuilder::RemoveTrailingCollapsibleSpaceIfExists(
|
| +bool NGInlineItemsBuilder::RemoveTrailingCollapsibleSpaceIfExists(
|
| unsigned* next_start_offset) {
|
| - if (last_collapsible_space_ != CollapsibleSpace::kNone && !text_.IsEmpty() &&
|
| - text_[text_.length() - 1] == kSpaceCharacter)
|
| - RemoveTrailingCollapsibleSpace(next_start_offset);
|
| + if (last_collapsible_space_ == CollapsibleSpace::kNone || text_.IsEmpty() ||
|
| + text_[text_.length() - 1] != kSpaceCharacter)
|
| + return false;
|
| + RemoveTrailingCollapsibleSpace(next_start_offset);
|
| + return true;
|
| }
|
|
|
| void NGInlineItemsBuilder::RemoveTrailingCollapsibleSpace(
|
|
|