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 |