Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "core/layout/ng/inline/ng_inline_items_builder.h" | 5 #include "core/layout/ng/inline/ng_inline_items_builder.h" |
| 6 | 6 |
| 7 #include "core/layout/LayoutObject.h" | 7 #include "core/layout/LayoutObject.h" |
| 8 #include "core/layout/ng/inline/ng_inline_node.h" | 8 #include "core/layout/ng/inline/ng_inline_node.h" |
| 9 #include "core/style/ComputedStyle.h" | 9 #include "core/style/ComputedStyle.h" |
| 10 | 10 |
| 11 namespace blink { | 11 namespace blink { |
| 12 | 12 |
| 13 NGInlineItemsBuilder::~NGInlineItemsBuilder() { | 13 NGInlineItemsBuilder::~NGInlineItemsBuilder() { |
| 14 DCHECK_EQ(0u, exits_.size()); | 14 DCHECK_EQ(0u, exits_.size()); |
| 15 DCHECK_EQ(text_.length(), items_->IsEmpty() ? 0 : items_->back().EndOffset()); | 15 DCHECK_EQ(text_.length(), items_->IsEmpty() ? 0 : items_->back().EndOffset()); |
| 16 } | 16 } |
| 17 | 17 |
| 18 String NGInlineItemsBuilder::ToString() { | 18 String NGInlineItemsBuilder::ToString() { |
| 19 // Segment Break Transformation Rules[1] defines to keep trailing new lines, | 19 // Segment Break Transformation Rules[1] defines to keep trailing new lines, |
| 20 // but it will be removed in Phase II[2]. We prefer not to add trailing new | 20 // but it will be removed in Phase II[2]. We prefer not to add trailing new |
| 21 // lines and collapsible spaces in Phase I. | 21 // lines and collapsible spaces in Phase I. |
| 22 // [1] https://drafts.csswg.org/css-text-3/#line-break-transform | 22 // [1] https://drafts.csswg.org/css-text-3/#line-break-transform |
| 23 // [2] https://drafts.csswg.org/css-text-3/#white-space-phase-2 | 23 // [2] https://drafts.csswg.org/css-text-3/#white-space-phase-2 |
| 24 unsigned next_start_offset = text_.length(); | 24 unsigned next_start_offset = text_.length(); |
| 25 RemoveTrailingCollapsibleSpaceIfExists(&next_start_offset); | 25 RemoveTrailingCollapsibleSpaceIfExists( |
| 26 &next_start_offset, | |
| 27 input_strings_lengths_.size() ? input_strings_lengths_.back() : 0); | |
| 26 | 28 |
| 27 return text_.ToString(); | 29 return text_.ToString(); |
| 28 } | 30 } |
| 29 | 31 |
| 30 // Determine "Ambiguous" East Asian Width is Wide or Narrow. | 32 // Determine "Ambiguous" East Asian Width is Wide or Narrow. |
| 31 // Unicode East Asian Width | 33 // Unicode East Asian Width |
| 32 // http://unicode.org/reports/tr11/ | 34 // http://unicode.org/reports/tr11/ |
| 33 static bool IsAmbiguosEastAsianWidthWide(const ComputedStyle* style) { | 35 static bool IsAmbiguosEastAsianWidthWide(const ComputedStyle* style) { |
| 34 UScriptCode script = style->GetFontDescription().GetScript(); | 36 UScriptCode script = style->GetFontDescription().GetScript(); |
| 35 return script == USCRIPT_KATAKANA_OR_HIRAGANA || | 37 return script == USCRIPT_KATAKANA_OR_HIRAGANA || |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 117 return c == kSpaceCharacter || c == kTabulationCharacter || | 119 return c == kSpaceCharacter || c == kTabulationCharacter || |
| 118 c == kNewlineCharacter; | 120 c == kNewlineCharacter; |
| 119 } | 121 } |
| 120 | 122 |
| 121 // Characters needing a separate control item than other text items. | 123 // Characters needing a separate control item than other text items. |
| 122 // It makes the line breaker easier to handle. | 124 // It makes the line breaker easier to handle. |
| 123 static inline bool IsControlItemCharacter(UChar c) { | 125 static inline bool IsControlItemCharacter(UChar c) { |
| 124 return c == kTabulationCharacter || c == kNewlineCharacter; | 126 return c == kTabulationCharacter || c == kNewlineCharacter; |
| 125 } | 127 } |
| 126 | 128 |
| 129 // Returns the largest integer in [0, |max|) that is absent in |integers|, or | |
| 130 // kNotFound if such an integer does not exist. | |
| 131 static size_t GetLargestAbsentInteger(const Vector<unsigned>& integers, | |
| 132 unsigned max) { | |
| 133 DCHECK(integers.IsEmpty() || integers.back() < max); | |
| 134 if (!max || integers.size() == max) | |
| 135 return kNotFound; | |
| 136 if (integers.IsEmpty() || integers.back() + 1 < max) | |
| 137 return max - 1; | |
| 138 for (unsigned i = integers.size() - 1; i;) { | |
| 139 DCHECK_LT(integers[i - 1], integers[i]); | |
| 140 if (integers[i - 1] + 1 < integers[i] - 1) | |
| 141 return integers[i] - 1; | |
| 142 --i; | |
| 143 } | |
| 144 DCHECK_GT(integers[0], 0u); | |
| 145 return integers[0] - 1; | |
| 146 } | |
| 147 | |
| 127 void NGInlineItemsBuilder::Append(const String& string, | 148 void NGInlineItemsBuilder::Append(const String& string, |
| 128 const ComputedStyle* style, | 149 const ComputedStyle* style, |
| 129 LayoutObject* layout_object) { | 150 LayoutObject* layout_object) { |
| 151 if (collapsed_indexes_) { | |
| 152 input_strings_lengths_.push_back(string.length()); | |
| 153 collapsed_indexes_->emplace_back(); | |
| 154 } | |
| 155 | |
| 130 if (string.IsEmpty()) | 156 if (string.IsEmpty()) |
| 131 return; | 157 return; |
| 132 text_.ReserveCapacity(string.length()); | 158 text_.ReserveCapacity(string.length()); |
| 133 | 159 |
| 134 EWhiteSpace whitespace = style->WhiteSpace(); | 160 EWhiteSpace whitespace = style->WhiteSpace(); |
| 135 if (!ComputedStyle::CollapseWhiteSpace(whitespace)) | 161 if (!ComputedStyle::CollapseWhiteSpace(whitespace)) |
| 136 return AppendWithoutWhiteSpaceCollapsing(string, style, layout_object); | 162 return AppendWithoutWhiteSpaceCollapsing(string, style, layout_object); |
| 137 if (ComputedStyle::PreserveNewline(whitespace) && !is_svgtext_) | 163 if (ComputedStyle::PreserveNewline(whitespace) && !is_svgtext_) |
| 138 return AppendWithPreservingNewlines(string, style, layout_object); | 164 return AppendWithPreservingNewlines(string, style, layout_object); |
| 139 | 165 |
| 140 AppendWithWhiteSpaceCollapsing(string, 0, string.length(), style, | 166 AppendWithWhiteSpaceCollapsing(string, 0, string.length(), style, |
| 141 layout_object); | 167 layout_object); |
| 142 } | 168 } |
| 143 | 169 |
| 144 void NGInlineItemsBuilder::AppendWithWhiteSpaceCollapsing( | 170 void NGInlineItemsBuilder::AppendWithWhiteSpaceCollapsing( |
| 145 const String& string, | 171 const String& string, |
| 146 unsigned start, | 172 unsigned start, |
| 147 unsigned end, | 173 unsigned end, |
| 148 const ComputedStyle* style, | 174 const ComputedStyle* style, |
| 149 LayoutObject* layout_object) { | 175 LayoutObject* layout_object) { |
| 150 unsigned start_offset = text_.length(); | 176 unsigned start_offset = text_.length(); |
| 151 for (unsigned i = start; i < end;) { | 177 for (unsigned i = start; i < end;) { |
| 152 UChar c = string[i]; | 178 UChar c = string[i]; |
| 153 if (c == kNewlineCharacter) { | 179 if (c == kNewlineCharacter) { |
| 154 // LayoutBR does not set preserve_newline, but should be preserved. | 180 // LayoutBR does not set preserve_newline, but should be preserved. |
| 155 if (!i && end == 1 && layout_object && layout_object->IsBR()) { | 181 if (!i && end == 1 && layout_object && layout_object->IsBR()) { |
| 156 AppendForcedBreak(style, layout_object); | 182 AppendForcedBreak(style, layout_object, i); |
| 157 return; | 183 return; |
| 158 } | 184 } |
| 159 | 185 |
| 160 if (last_collapsible_space_ == CollapsibleSpace::kNone) | 186 if (last_collapsible_space_ == CollapsibleSpace::kNone) |
| 161 text_.Append(kSpaceCharacter); | 187 text_.Append(kSpaceCharacter); |
| 188 else if (collapsed_indexes_) | |
| 189 collapsed_indexes_->back().push_back(i); | |
| 162 last_collapsible_space_ = CollapsibleSpace::kNewline; | 190 last_collapsible_space_ = CollapsibleSpace::kNewline; |
| 163 i++; | 191 i++; |
| 164 continue; | 192 continue; |
| 165 } | 193 } |
| 166 | 194 |
| 167 if (c == kSpaceCharacter || c == kTabulationCharacter) { | 195 if (c == kSpaceCharacter || c == kTabulationCharacter) { |
| 168 if (last_collapsible_space_ == CollapsibleSpace::kNone) { | 196 if (last_collapsible_space_ == CollapsibleSpace::kNone) { |
| 169 text_.Append(kSpaceCharacter); | 197 text_.Append(kSpaceCharacter); |
| 170 last_collapsible_space_ = CollapsibleSpace::kSpace; | 198 last_collapsible_space_ = CollapsibleSpace::kSpace; |
| 199 } else if (collapsed_indexes_) { | |
| 200 collapsed_indexes_->back().push_back(i); | |
| 171 } | 201 } |
| 172 i++; | 202 i++; |
| 173 continue; | 203 continue; |
| 174 } | 204 } |
| 175 | 205 |
| 176 if (last_collapsible_space_ == CollapsibleSpace::kNewline) { | 206 if (last_collapsible_space_ == CollapsibleSpace::kNewline) { |
| 177 RemoveTrailingCollapsibleNewlineIfNeeded(&start_offset, string, i, style); | 207 RemoveTrailingCollapsibleNewlineIfNeeded(&start_offset, string, i, style); |
| 178 } | 208 } |
| 179 | 209 |
| 180 size_t end_of_non_space = string.Find(IsCollapsibleSpace, i + 1); | 210 size_t end_of_non_space = string.Find(IsCollapsibleSpace, i + 1); |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 217 | 247 |
| 218 last_collapsible_space_ = CollapsibleSpace::kNone; | 248 last_collapsible_space_ = CollapsibleSpace::kNone; |
| 219 } | 249 } |
| 220 | 250 |
| 221 void NGInlineItemsBuilder::AppendWithPreservingNewlines( | 251 void NGInlineItemsBuilder::AppendWithPreservingNewlines( |
| 222 const String& string, | 252 const String& string, |
| 223 const ComputedStyle* style, | 253 const ComputedStyle* style, |
| 224 LayoutObject* layout_object) { | 254 LayoutObject* layout_object) { |
| 225 for (unsigned start = 0; start < string.length();) { | 255 for (unsigned start = 0; start < string.length();) { |
| 226 if (string[start] == kNewlineCharacter) { | 256 if (string[start] == kNewlineCharacter) { |
| 227 AppendForcedBreak(style, layout_object); | 257 AppendForcedBreak(style, layout_object, start); |
| 228 start++; | 258 start++; |
| 229 continue; | 259 continue; |
| 230 } | 260 } |
| 231 | 261 |
| 232 size_t end = string.find(kNewlineCharacter, start + 1); | 262 size_t end = string.find(kNewlineCharacter, start + 1); |
| 233 if (end == kNotFound) | 263 if (end == kNotFound) |
| 234 end = string.length(); | 264 end = string.length(); |
| 235 AppendWithWhiteSpaceCollapsing(string, start, end, style, layout_object); | 265 AppendWithWhiteSpaceCollapsing(string, start, end, style, layout_object); |
| 236 start = end; | 266 start = end; |
| 237 } | 267 } |
| 238 } | 268 } |
| 239 | 269 |
| 240 void NGInlineItemsBuilder::AppendForcedBreak(const ComputedStyle* style, | 270 void NGInlineItemsBuilder::AppendForcedBreak(const ComputedStyle* style, |
| 241 LayoutObject* layout_object) { | 271 LayoutObject* layout_object, |
| 272 unsigned index) { | |
| 242 // Remove collapsible spaces immediately before a preserved newline. | 273 // Remove collapsible spaces immediately before a preserved newline. |
| 243 unsigned start_offset = text_.length(); | 274 unsigned start_offset = text_.length(); |
| 244 RemoveTrailingCollapsibleSpaceIfExists(&start_offset); | 275 RemoveTrailingCollapsibleSpaceIfExists(&start_offset, index); |
| 245 | 276 |
| 246 Append(NGInlineItem::kControl, kNewlineCharacter, style, layout_object); | 277 Append(NGInlineItem::kControl, kNewlineCharacter, style, layout_object); |
| 247 | 278 |
| 248 // Remove collapsible spaces immediately after a preserved newline. | 279 // Remove collapsible spaces immediately after a preserved newline. |
| 249 last_collapsible_space_ = CollapsibleSpace::kSpace; | 280 last_collapsible_space_ = CollapsibleSpace::kSpace; |
| 250 } | 281 } |
| 251 | 282 |
| 252 void NGInlineItemsBuilder::Append(NGInlineItem::NGInlineItemType type, | 283 void NGInlineItemsBuilder::Append(NGInlineItem::NGInlineItemType type, |
| 253 UChar character, | 284 UChar character, |
| 254 const ComputedStyle* style, | 285 const ComputedStyle* style, |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 280 return; | 311 return; |
| 281 | 312 |
| 282 const ComputedStyle* before_style = after_style; | 313 const ComputedStyle* before_style = after_style; |
| 283 if (!items_->IsEmpty()) { | 314 if (!items_->IsEmpty()) { |
| 284 NGInlineItem& item = items_->back(); | 315 NGInlineItem& item = items_->back(); |
| 285 if (text_.length() < item.EndOffset() + 2) | 316 if (text_.length() < item.EndOffset() + 2) |
| 286 before_style = item.Style(); | 317 before_style = item.Style(); |
| 287 } | 318 } |
| 288 | 319 |
| 289 if (ShouldRemoveNewline(text_, before_style, after, after_index, after_style)) | 320 if (ShouldRemoveNewline(text_, before_style, after, after_index, after_style)) |
| 290 RemoveTrailingCollapsibleSpace(next_start_offset); | 321 RemoveTrailingCollapsibleSpace(next_start_offset, after_index); |
| 291 } | 322 } |
| 292 | 323 |
| 293 void NGInlineItemsBuilder::RemoveTrailingCollapsibleSpaceIfExists( | 324 void NGInlineItemsBuilder::RemoveTrailingCollapsibleSpaceIfExists( |
| 294 unsigned* next_start_offset) { | 325 unsigned* next_start_offset, |
| 326 unsigned after_index) { | |
| 295 if (last_collapsible_space_ != CollapsibleSpace::kNone && !text_.IsEmpty() && | 327 if (last_collapsible_space_ != CollapsibleSpace::kNone && !text_.IsEmpty() && |
| 296 text_[text_.length() - 1] == kSpaceCharacter) | 328 text_[text_.length() - 1] == kSpaceCharacter) |
| 297 RemoveTrailingCollapsibleSpace(next_start_offset); | 329 RemoveTrailingCollapsibleSpace(next_start_offset, after_index); |
| 298 } | 330 } |
| 299 | 331 |
| 300 void NGInlineItemsBuilder::RemoveTrailingCollapsibleSpace( | 332 void NGInlineItemsBuilder::RemoveTrailingCollapsibleSpace( |
| 301 unsigned* next_start_offset) { | 333 unsigned* next_start_offset, |
| 334 unsigned after_index) { | |
| 302 DCHECK_NE(last_collapsible_space_, CollapsibleSpace::kNone); | 335 DCHECK_NE(last_collapsible_space_, CollapsibleSpace::kNone); |
| 303 DCHECK(!text_.IsEmpty()); | 336 DCHECK(!text_.IsEmpty()); |
| 304 DCHECK_EQ(text_[text_.length() - 1], kSpaceCharacter); | 337 DCHECK_EQ(text_[text_.length() - 1], kSpaceCharacter); |
| 305 | 338 |
| 306 unsigned new_size = text_.length() - 1; | 339 unsigned new_size = text_.length() - 1; |
| 307 text_.Resize(new_size); | 340 text_.Resize(new_size); |
| 308 last_collapsible_space_ = CollapsibleSpace::kNone; | 341 last_collapsible_space_ = CollapsibleSpace::kNone; |
| 309 | 342 |
| 343 if (collapsed_indexes_) | |
| 344 AddLastTrailingSpaceToRemovedIndexes(after_index); | |
| 345 | |
| 310 if (*next_start_offset <= new_size) | 346 if (*next_start_offset <= new_size) |
| 311 return; | 347 return; |
| 312 *next_start_offset = new_size; | 348 *next_start_offset = new_size; |
| 313 | 349 |
| 314 // Adjust the last item if the removed space is already appended. | 350 // Adjust the last item if the removed space is already appended. |
| 315 for (unsigned i = items_->size(); i > 0;) { | 351 for (unsigned i = items_->size(); i > 0;) { |
| 316 NGInlineItem& item = (*items_)[--i]; | 352 NGInlineItem& item = (*items_)[--i]; |
| 317 DCHECK_EQ(item.EndOffset(), new_size + 1); | 353 DCHECK_EQ(item.EndOffset(), new_size + 1); |
| 318 if (item.Type() == NGInlineItem::kText) { | 354 if (item.Type() == NGInlineItem::kText) { |
| 319 DCHECK_GE(item.Length(), 1u); | 355 DCHECK_GE(item.Length(), 1u); |
| 320 if (item.Length() > 1) | 356 if (item.Length() > 1) |
| 321 item.SetEndOffset(new_size); | 357 item.SetEndOffset(new_size); |
| 322 else | 358 else |
| 323 items_->erase(i); | 359 items_->erase(i); |
| 324 break; | 360 break; |
| 325 } | 361 } |
| 326 if (!item.Length()) { | 362 if (!item.Length()) { |
| 327 // Trailing spaces can be removed across non-character items. | 363 // Trailing spaces can be removed across non-character items. |
| 328 item.SetOffset(new_size, new_size); | 364 item.SetOffset(new_size, new_size); |
| 329 continue; | 365 continue; |
| 330 } | 366 } |
| 331 NOTREACHED(); | 367 NOTREACHED(); |
| 332 break; | 368 break; |
| 333 } | 369 } |
| 334 } | 370 } |
| 335 | 371 |
| 372 void NGInlineItemsBuilder::AddLastTrailingSpaceToRemovedIndexes( | |
|
yosin_UTC9
2017/06/20 05:39:09
nit: %s/RemovedIndexes/CollapsedIndexes/
kojii
2017/06/21 09:47:13
This may depend on above, but I can't understand w
Xiaocheng
2017/06/21 18:15:06
This function is called when RemoveTrailingCollaps
| |
| 373 unsigned last_string_appended_length) { | |
| 374 DCHECK(collapsed_indexes_); | |
| 375 DCHECK_EQ(collapsed_indexes_->size(), input_strings_lengths_.size()); | |
| 376 for (unsigned i = collapsed_indexes_->size(); i;) { | |
| 377 Vector<unsigned>& indexes = (*collapsed_indexes_)[--i]; | |
| 378 size_t last_unremoved = | |
| 379 GetLargestAbsentInteger(indexes, (i + 1) == collapsed_indexes_->size() | |
| 380 ? last_string_appended_length | |
| 381 : input_strings_lengths_[i]); | |
| 382 if (last_unremoved == kNotFound) | |
| 383 continue; | |
| 384 | |
| 385 indexes.insert( | |
| 386 std::lower_bound(indexes.begin(), indexes.end(), last_unremoved) - | |
| 387 indexes.begin(), | |
| 388 last_unremoved); | |
| 389 return; | |
| 390 } | |
| 391 NOTREACHED(); | |
| 392 } | |
| 393 | |
| 336 void NGInlineItemsBuilder::AppendBidiControl(const ComputedStyle* style, | 394 void NGInlineItemsBuilder::AppendBidiControl(const ComputedStyle* style, |
| 337 UChar ltr, | 395 UChar ltr, |
| 338 UChar rtl) { | 396 UChar rtl) { |
| 339 Append(NGInlineItem::kBidiControl, | 397 Append(NGInlineItem::kBidiControl, |
| 340 style->Direction() == TextDirection::kRtl ? rtl : ltr); | 398 style->Direction() == TextDirection::kRtl ? rtl : ltr); |
| 341 } | 399 } |
| 342 | 400 |
| 343 void NGInlineItemsBuilder::EnterBlock(const ComputedStyle* style) { | 401 void NGInlineItemsBuilder::EnterBlock(const ComputedStyle* style) { |
| 344 // Handle bidi-override on the block itself. | 402 // Handle bidi-override on the block itself. |
| 345 switch (style->GetUnicodeBidi()) { | 403 switch (style->GetUnicodeBidi()) { |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 422 } | 480 } |
| 423 | 481 |
| 424 void NGInlineItemsBuilder::Exit(LayoutObject* node) { | 482 void NGInlineItemsBuilder::Exit(LayoutObject* node) { |
| 425 while (!exits_.IsEmpty() && exits_.back().node == node) { | 483 while (!exits_.IsEmpty() && exits_.back().node == node) { |
| 426 Append(NGInlineItem::kBidiControl, exits_.back().character); | 484 Append(NGInlineItem::kBidiControl, exits_.back().character); |
| 427 exits_.pop_back(); | 485 exits_.pop_back(); |
| 428 } | 486 } |
| 429 } | 487 } |
| 430 | 488 |
| 431 } // namespace blink | 489 } // namespace blink |
| OLD | NEW |