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( | |
14 Vector<NGInlineItem>* items, | |
15 Vector<Vector<unsigned>>* removed_indexes) | |
16 : items_(items), | |
17 removed_indexes_(removed_indexes), | |
18 input_strings_lengths_(Vector<unsigned>()) { | |
19 DCHECK(removed_indexes); | |
20 } | |
21 | |
13 NGInlineItemsBuilder::~NGInlineItemsBuilder() { | 22 NGInlineItemsBuilder::~NGInlineItemsBuilder() { |
14 DCHECK_EQ(0u, exits_.size()); | 23 DCHECK_EQ(0u, exits_.size()); |
15 DCHECK_EQ(text_.length(), items_->IsEmpty() ? 0 : items_->back().EndOffset()); | 24 DCHECK_EQ(text_.length(), items_->IsEmpty() ? 0 : items_->back().EndOffset()); |
16 } | 25 } |
17 | 26 |
18 String NGInlineItemsBuilder::ToString() { | 27 String NGInlineItemsBuilder::ToString() { |
19 // Segment Break Transformation Rules[1] defines to keep trailing new lines, | 28 // 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 | 29 // but it will be removed in Phase II[2]. We prefer not to add trailing new |
21 // lines and collapsible spaces in Phase I. | 30 // lines and collapsible spaces in Phase I. |
22 // [1] https://drafts.csswg.org/css-text-3/#line-break-transform | 31 // [1] https://drafts.csswg.org/css-text-3/#line-break-transform |
23 // [2] https://drafts.csswg.org/css-text-3/#white-space-phase-2 | 32 // [2] https://drafts.csswg.org/css-text-3/#white-space-phase-2 |
24 unsigned next_start_offset = text_.length(); | 33 unsigned next_start_offset = text_.length(); |
25 RemoveTrailingCollapsibleSpaceIfExists(&next_start_offset); | 34 bool trailing_space_removed = |
35 RemoveTrailingCollapsibleSpaceIfExists(&next_start_offset); | |
36 if (trailing_space_removed && removed_indexes_) { | |
37 DCHECK(input_strings_lengths_); | |
38 AddLastTrailingSpaceToRemovedIndexes(input_strings_lengths_->back()); | |
39 } | |
26 | 40 |
27 return text_.ToString(); | 41 return text_.ToString(); |
28 } | 42 } |
29 | 43 |
30 // Determine "Ambiguous" East Asian Width is Wide or Narrow. | 44 // Determine "Ambiguous" East Asian Width is Wide or Narrow. |
31 // Unicode East Asian Width | 45 // Unicode East Asian Width |
32 // http://unicode.org/reports/tr11/ | 46 // http://unicode.org/reports/tr11/ |
33 static bool IsAmbiguosEastAsianWidthWide(const ComputedStyle* style) { | 47 static bool IsAmbiguosEastAsianWidthWide(const ComputedStyle* style) { |
34 UScriptCode script = style->GetFontDescription().GetScript(); | 48 UScriptCode script = style->GetFontDescription().GetScript(); |
35 return script == USCRIPT_KATAKANA_OR_HIRAGANA || | 49 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 || | 131 return c == kSpaceCharacter || c == kTabulationCharacter || |
118 c == kNewlineCharacter; | 132 c == kNewlineCharacter; |
119 } | 133 } |
120 | 134 |
121 // Characters needing a separate control item than other text items. | 135 // Characters needing a separate control item than other text items. |
122 // It makes the line breaker easier to handle. | 136 // It makes the line breaker easier to handle. |
123 static inline bool IsControlItemCharacter(UChar c) { | 137 static inline bool IsControlItemCharacter(UChar c) { |
124 return c == kTabulationCharacter || c == kNewlineCharacter; | 138 return c == kTabulationCharacter || c == kNewlineCharacter; |
125 } | 139 } |
126 | 140 |
141 // Returns the largest integer in [0, |max|) that is absent in |integers|, or | |
142 // kNotFound if such an integer does not exist. | |
143 static size_t GetLargestAbsentInteger(const Vector<unsigned>& integers, | |
144 unsigned max) { | |
145 DCHECK(integers.IsEmpty() || integers.back() < max); | |
146 if (!max || integers.size() == max) | |
147 return kNotFound; | |
148 if (integers.IsEmpty() || integers.back() + 1 < max) | |
149 return max - 1; | |
150 for (unsigned i = integers.size() - 1; i;) { | |
151 DCHECK_LT(integers[i - 1], integers[i]); | |
152 if (integers[i - 1] + 1 < integers[i] - 1) | |
153 return integers[i] - 1; | |
154 --i; | |
155 } | |
156 DCHECK_GT(integers[0], 0u); | |
157 return integers[0] - 1; | |
158 } | |
159 | |
127 void NGInlineItemsBuilder::Append(const String& string, | 160 void NGInlineItemsBuilder::Append(const String& string, |
128 const ComputedStyle* style, | 161 const ComputedStyle* style, |
129 LayoutObject* layout_object) { | 162 LayoutObject* layout_object) { |
163 if (removed_indexes_) { | |
164 DCHECK(input_strings_lengths_); | |
165 input_strings_lengths_->push_back(string.length()); | |
166 removed_indexes_->emplace_back(); | |
167 } | |
168 | |
130 if (string.IsEmpty()) | 169 if (string.IsEmpty()) |
131 return; | 170 return; |
132 text_.ReserveCapacity(string.length()); | 171 text_.ReserveCapacity(string.length()); |
133 | 172 |
134 EWhiteSpace whitespace = style->WhiteSpace(); | 173 EWhiteSpace whitespace = style->WhiteSpace(); |
135 if (!ComputedStyle::CollapseWhiteSpace(whitespace)) | 174 if (!ComputedStyle::CollapseWhiteSpace(whitespace)) |
136 return AppendWithoutWhiteSpaceCollapsing(string, style, layout_object); | 175 return AppendWithoutWhiteSpaceCollapsing(string, style, layout_object); |
137 if (ComputedStyle::PreserveNewline(whitespace) && !is_svgtext_) | 176 if (ComputedStyle::PreserveNewline(whitespace) && !is_svgtext_) |
138 return AppendWithPreservingNewlines(string, style, layout_object); | 177 return AppendWithPreservingNewlines(string, style, layout_object); |
139 | 178 |
140 AppendWithWhiteSpaceCollapsing(string, 0, string.length(), style, | 179 AppendWithWhiteSpaceCollapsing(string, 0, string.length(), style, |
141 layout_object); | 180 layout_object); |
142 } | 181 } |
143 | 182 |
144 void NGInlineItemsBuilder::AppendWithWhiteSpaceCollapsing( | 183 void NGInlineItemsBuilder::AppendWithWhiteSpaceCollapsing( |
145 const String& string, | 184 const String& string, |
146 unsigned start, | 185 unsigned start, |
147 unsigned end, | 186 unsigned end, |
148 const ComputedStyle* style, | 187 const ComputedStyle* style, |
149 LayoutObject* layout_object) { | 188 LayoutObject* layout_object) { |
150 unsigned start_offset = text_.length(); | 189 unsigned start_offset = text_.length(); |
151 for (unsigned i = start; i < end;) { | 190 for (unsigned i = start; i < end;) { |
152 UChar c = string[i]; | 191 UChar c = string[i]; |
153 if (c == kNewlineCharacter) { | 192 if (c == kNewlineCharacter) { |
154 // LayoutBR does not set preserve_newline, but should be preserved. | 193 // LayoutBR does not set preserve_newline, but should be preserved. |
155 if (!i && end == 1 && layout_object && layout_object->IsBR()) { | 194 if (!i && end == 1 && layout_object && layout_object->IsBR()) { |
156 AppendForcedBreak(style, layout_object); | 195 bool trailing_space_removed = AppendForcedBreak(style, layout_object); |
196 if (trailing_space_removed && removed_indexes_) | |
197 AddLastTrailingSpaceToRemovedIndexes(i); | |
kojii
2017/06/19 05:56:33
Why don't we add to removed_indexes in RemoveTrail
Xiaocheng
2017/06/19 17:53:01
Updating |removed_indexes_| needs the number of ch
| |
157 return; | 198 return; |
158 } | 199 } |
159 | 200 |
160 if (last_collapsible_space_ == CollapsibleSpace::kNone) | 201 if (last_collapsible_space_ == CollapsibleSpace::kNone) |
161 text_.Append(kSpaceCharacter); | 202 text_.Append(kSpaceCharacter); |
203 else if (removed_indexes_) | |
204 removed_indexes_->back().push_back(i); | |
162 last_collapsible_space_ = CollapsibleSpace::kNewline; | 205 last_collapsible_space_ = CollapsibleSpace::kNewline; |
163 i++; | 206 i++; |
164 continue; | 207 continue; |
165 } | 208 } |
166 | 209 |
167 if (c == kSpaceCharacter || c == kTabulationCharacter) { | 210 if (c == kSpaceCharacter || c == kTabulationCharacter) { |
168 if (last_collapsible_space_ == CollapsibleSpace::kNone) { | 211 if (last_collapsible_space_ == CollapsibleSpace::kNone) { |
169 text_.Append(kSpaceCharacter); | 212 text_.Append(kSpaceCharacter); |
170 last_collapsible_space_ = CollapsibleSpace::kSpace; | 213 last_collapsible_space_ = CollapsibleSpace::kSpace; |
214 } else if (removed_indexes_) { | |
215 removed_indexes_->back().push_back(i); | |
171 } | 216 } |
172 i++; | 217 i++; |
173 continue; | 218 continue; |
174 } | 219 } |
175 | 220 |
176 if (last_collapsible_space_ == CollapsibleSpace::kNewline) { | 221 if (last_collapsible_space_ == CollapsibleSpace::kNewline) { |
177 RemoveTrailingCollapsibleNewlineIfNeeded(&start_offset, string, i, style); | 222 bool trailing_newline_removed = RemoveTrailingCollapsibleNewlineIfNeeded( |
223 &start_offset, string, i, style); | |
224 if (trailing_newline_removed && removed_indexes_) | |
225 AddLastTrailingSpaceToRemovedIndexes(i); | |
178 } | 226 } |
179 | 227 |
180 size_t end_of_non_space = string.Find(IsCollapsibleSpace, i + 1); | 228 size_t end_of_non_space = string.Find(IsCollapsibleSpace, i + 1); |
181 if (end_of_non_space == kNotFound) | 229 if (end_of_non_space == kNotFound) |
182 end_of_non_space = string.length(); | 230 end_of_non_space = string.length(); |
183 text_.Append(string, i, end_of_non_space - i); | 231 text_.Append(string, i, end_of_non_space - i); |
184 i = end_of_non_space; | 232 i = end_of_non_space; |
185 last_collapsible_space_ = CollapsibleSpace::kNone; | 233 last_collapsible_space_ = CollapsibleSpace::kNone; |
186 } | 234 } |
187 | 235 |
(...skipping 29 matching lines...) Expand all Loading... | |
217 | 265 |
218 last_collapsible_space_ = CollapsibleSpace::kNone; | 266 last_collapsible_space_ = CollapsibleSpace::kNone; |
219 } | 267 } |
220 | 268 |
221 void NGInlineItemsBuilder::AppendWithPreservingNewlines( | 269 void NGInlineItemsBuilder::AppendWithPreservingNewlines( |
222 const String& string, | 270 const String& string, |
223 const ComputedStyle* style, | 271 const ComputedStyle* style, |
224 LayoutObject* layout_object) { | 272 LayoutObject* layout_object) { |
225 for (unsigned start = 0; start < string.length();) { | 273 for (unsigned start = 0; start < string.length();) { |
226 if (string[start] == kNewlineCharacter) { | 274 if (string[start] == kNewlineCharacter) { |
227 AppendForcedBreak(style, layout_object); | 275 bool trailing_space_removed = AppendForcedBreak(style, layout_object); |
276 if (trailing_space_removed && removed_indexes_) | |
277 AddLastTrailingSpaceToRemovedIndexes(start); | |
228 start++; | 278 start++; |
229 continue; | 279 continue; |
230 } | 280 } |
231 | 281 |
232 size_t end = string.find(kNewlineCharacter, start + 1); | 282 size_t end = string.find(kNewlineCharacter, start + 1); |
233 if (end == kNotFound) | 283 if (end == kNotFound) |
234 end = string.length(); | 284 end = string.length(); |
235 AppendWithWhiteSpaceCollapsing(string, start, end, style, layout_object); | 285 AppendWithWhiteSpaceCollapsing(string, start, end, style, layout_object); |
236 start = end; | 286 start = end; |
237 } | 287 } |
238 } | 288 } |
239 | 289 |
240 void NGInlineItemsBuilder::AppendForcedBreak(const ComputedStyle* style, | 290 bool NGInlineItemsBuilder::AppendForcedBreak(const ComputedStyle* style, |
241 LayoutObject* layout_object) { | 291 LayoutObject* layout_object) { |
242 // Remove collapsible spaces immediately before a preserved newline. | 292 // Remove collapsible spaces immediately before a preserved newline. |
243 unsigned start_offset = text_.length(); | 293 unsigned start_offset = text_.length(); |
244 RemoveTrailingCollapsibleSpaceIfExists(&start_offset); | 294 bool trailing_space_removed = |
295 RemoveTrailingCollapsibleSpaceIfExists(&start_offset); | |
245 | 296 |
246 Append(NGInlineItem::kControl, kNewlineCharacter, style, layout_object); | 297 Append(NGInlineItem::kControl, kNewlineCharacter, style, layout_object); |
247 | 298 |
248 // Remove collapsible spaces immediately after a preserved newline. | 299 // Remove collapsible spaces immediately after a preserved newline. |
249 last_collapsible_space_ = CollapsibleSpace::kSpace; | 300 last_collapsible_space_ = CollapsibleSpace::kSpace; |
301 return trailing_space_removed; | |
250 } | 302 } |
251 | 303 |
252 void NGInlineItemsBuilder::Append(NGInlineItem::NGInlineItemType type, | 304 void NGInlineItemsBuilder::Append(NGInlineItem::NGInlineItemType type, |
253 UChar character, | 305 UChar character, |
254 const ComputedStyle* style, | 306 const ComputedStyle* style, |
255 LayoutObject* layout_object) { | 307 LayoutObject* layout_object) { |
256 DCHECK_NE(character, kSpaceCharacter); | 308 DCHECK_NE(character, kSpaceCharacter); |
257 DCHECK_NE(character, kZeroWidthSpaceCharacter); | 309 DCHECK_NE(character, kZeroWidthSpaceCharacter); |
258 | 310 |
259 text_.Append(character); | 311 text_.Append(character); |
260 unsigned end_offset = text_.length(); | 312 unsigned end_offset = text_.length(); |
261 AppendItem(items_, type, end_offset - 1, end_offset, style, layout_object); | 313 AppendItem(items_, type, end_offset - 1, end_offset, style, layout_object); |
262 last_collapsible_space_ = CollapsibleSpace::kNone; | 314 last_collapsible_space_ = CollapsibleSpace::kNone; |
263 } | 315 } |
264 | 316 |
265 void NGInlineItemsBuilder::Append(NGInlineItem::NGInlineItemType type, | 317 void NGInlineItemsBuilder::Append(NGInlineItem::NGInlineItemType type, |
266 const ComputedStyle* style, | 318 const ComputedStyle* style, |
267 LayoutObject* layout_object) { | 319 LayoutObject* layout_object) { |
268 unsigned end_offset = text_.length(); | 320 unsigned end_offset = text_.length(); |
269 AppendItem(items_, type, end_offset, end_offset, style, layout_object); | 321 AppendItem(items_, type, end_offset, end_offset, style, layout_object); |
270 } | 322 } |
271 | 323 |
272 void NGInlineItemsBuilder::RemoveTrailingCollapsibleNewlineIfNeeded( | 324 bool NGInlineItemsBuilder::RemoveTrailingCollapsibleNewlineIfNeeded( |
273 unsigned* next_start_offset, | 325 unsigned* next_start_offset, |
274 const String& after, | 326 const String& after, |
275 unsigned after_index, | 327 unsigned after_index, |
276 const ComputedStyle* after_style) { | 328 const ComputedStyle* after_style) { |
277 DCHECK_EQ(last_collapsible_space_, CollapsibleSpace::kNewline); | 329 DCHECK_EQ(last_collapsible_space_, CollapsibleSpace::kNewline); |
278 | 330 |
279 if (text_.IsEmpty() || text_[text_.length() - 1] != kSpaceCharacter) | 331 if (text_.IsEmpty() || text_[text_.length() - 1] != kSpaceCharacter) |
280 return; | 332 return false; |
281 | 333 |
282 const ComputedStyle* before_style = after_style; | 334 const ComputedStyle* before_style = after_style; |
283 if (!items_->IsEmpty()) { | 335 if (!items_->IsEmpty()) { |
284 NGInlineItem& item = items_->back(); | 336 NGInlineItem& item = items_->back(); |
285 if (text_.length() < item.EndOffset() + 2) | 337 if (text_.length() < item.EndOffset() + 2) |
286 before_style = item.Style(); | 338 before_style = item.Style(); |
287 } | 339 } |
288 | 340 |
289 if (ShouldRemoveNewline(text_, before_style, after, after_index, after_style)) | 341 if (!ShouldRemoveNewline(text_, before_style, after, after_index, |
290 RemoveTrailingCollapsibleSpace(next_start_offset); | 342 after_style)) |
343 return false; | |
344 RemoveTrailingCollapsibleSpace(next_start_offset); | |
345 return true; | |
291 } | 346 } |
292 | 347 |
293 void NGInlineItemsBuilder::RemoveTrailingCollapsibleSpaceIfExists( | 348 bool NGInlineItemsBuilder::RemoveTrailingCollapsibleSpaceIfExists( |
294 unsigned* next_start_offset) { | 349 unsigned* next_start_offset) { |
295 if (last_collapsible_space_ != CollapsibleSpace::kNone && !text_.IsEmpty() && | 350 if (last_collapsible_space_ == CollapsibleSpace::kNone || text_.IsEmpty() || |
296 text_[text_.length() - 1] == kSpaceCharacter) | 351 text_[text_.length() - 1] != kSpaceCharacter) |
297 RemoveTrailingCollapsibleSpace(next_start_offset); | 352 return false; |
353 RemoveTrailingCollapsibleSpace(next_start_offset); | |
354 return true; | |
298 } | 355 } |
299 | 356 |
300 void NGInlineItemsBuilder::RemoveTrailingCollapsibleSpace( | 357 void NGInlineItemsBuilder::RemoveTrailingCollapsibleSpace( |
301 unsigned* next_start_offset) { | 358 unsigned* next_start_offset) { |
302 DCHECK_NE(last_collapsible_space_, CollapsibleSpace::kNone); | 359 DCHECK_NE(last_collapsible_space_, CollapsibleSpace::kNone); |
303 DCHECK(!text_.IsEmpty()); | 360 DCHECK(!text_.IsEmpty()); |
304 DCHECK_EQ(text_[text_.length() - 1], kSpaceCharacter); | 361 DCHECK_EQ(text_[text_.length() - 1], kSpaceCharacter); |
305 | 362 |
306 unsigned new_size = text_.length() - 1; | 363 unsigned new_size = text_.length() - 1; |
307 text_.Resize(new_size); | 364 text_.Resize(new_size); |
(...skipping 18 matching lines...) Expand all Loading... | |
326 if (!item.Length()) { | 383 if (!item.Length()) { |
327 // Trailing spaces can be removed across non-character items. | 384 // Trailing spaces can be removed across non-character items. |
328 item.SetOffset(new_size, new_size); | 385 item.SetOffset(new_size, new_size); |
329 continue; | 386 continue; |
330 } | 387 } |
331 NOTREACHED(); | 388 NOTREACHED(); |
332 break; | 389 break; |
333 } | 390 } |
334 } | 391 } |
335 | 392 |
393 void NGInlineItemsBuilder::AddLastTrailingSpaceToRemovedIndexes( | |
394 unsigned last_string_end) { | |
395 DCHECK(removed_indexes_); | |
396 DCHECK(input_strings_lengths_); | |
397 DCHECK_EQ(removed_indexes_->size(), input_strings_lengths_->size()); | |
398 for (unsigned i = removed_indexes_->size(); i;) { | |
399 Vector<unsigned>& indexes = (*removed_indexes_)[--i]; | |
400 size_t last_unremoved = | |
401 GetLargestAbsentInteger(indexes, (i + 1) == removed_indexes_->size() | |
402 ? last_string_end | |
403 : (*input_strings_lengths_)[i]); | |
404 if (last_unremoved == kNotFound) | |
405 continue; | |
406 | |
407 indexes.insert( | |
408 std::lower_bound(indexes.begin(), indexes.end(), last_unremoved) - | |
409 indexes.begin(), | |
410 last_unremoved); | |
411 return; | |
412 } | |
413 NOTREACHED(); | |
414 } | |
415 | |
336 void NGInlineItemsBuilder::AppendBidiControl(const ComputedStyle* style, | 416 void NGInlineItemsBuilder::AppendBidiControl(const ComputedStyle* style, |
337 UChar ltr, | 417 UChar ltr, |
338 UChar rtl) { | 418 UChar rtl) { |
339 Append(NGInlineItem::kBidiControl, | 419 Append(NGInlineItem::kBidiControl, |
340 style->Direction() == TextDirection::kRtl ? rtl : ltr); | 420 style->Direction() == TextDirection::kRtl ? rtl : ltr); |
341 } | 421 } |
342 | 422 |
343 void NGInlineItemsBuilder::EnterBlock(const ComputedStyle* style) { | 423 void NGInlineItemsBuilder::EnterBlock(const ComputedStyle* style) { |
344 // Handle bidi-override on the block itself. | 424 // Handle bidi-override on the block itself. |
345 switch (style->GetUnicodeBidi()) { | 425 switch (style->GetUnicodeBidi()) { |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
422 } | 502 } |
423 | 503 |
424 void NGInlineItemsBuilder::Exit(LayoutObject* node) { | 504 void NGInlineItemsBuilder::Exit(LayoutObject* node) { |
425 while (!exits_.IsEmpty() && exits_.back().node == node) { | 505 while (!exits_.IsEmpty() && exits_.back().node == node) { |
426 Append(NGInlineItem::kBidiControl, exits_.back().character); | 506 Append(NGInlineItem::kBidiControl, exits_.back().character); |
427 exits_.pop_back(); | 507 exits_.pop_back(); |
428 } | 508 } |
429 } | 509 } |
430 | 510 |
431 } // namespace blink | 511 } // namespace blink |
OLD | NEW |