Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Side by Side Diff: third_party/WebKit/Source/core/layout/ng/inline/ng_inline_items_builder.cc

Issue 2943573002: Make NGInlineItemsBuilder construct whitespace-collapsed offset mapping (Closed)
Patch Set: Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 bool NGInlineItemsBuilder::Finalize() {
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 return RemoveTrailingCollapsibleSpaceIfExists(&next_start_offset);
26 }
26 27
28 String NGInlineItemsBuilder::ToString() {
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 ||
36 script == USCRIPT_SIMPLIFIED_HAN || script == USCRIPT_TRADITIONAL_HAN; 38 script == USCRIPT_SIMPLIFIED_HAN || script == USCRIPT_TRADITIONAL_HAN;
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
127 void NGInlineItemsBuilder::Append(const String& string, 129 static inline size_t GetLastAppendedCharacterIndex(
128 const ComputedStyle* style, 130 const Vector<unsigned>& collapsed_indexes,
129 LayoutObject* layout_object) { 131 unsigned start,
132 unsigned end) {
133 if (start == end || collapsed_indexes.size() == end - start)
134 return kNotFound;
135 if (collapsed_indexes.IsEmpty())
136 return end - 1;
137 DCHECK_GT(end, collapsed_indexes.back());
138 if (collapsed_indexes.back() + 1 < end)
139 return collapsed_indexes.back() + 1;
140 for (unsigned i = collapsed_indexes.size(); i > 1;) {
141 unsigned current_collapsed = collapsed_indexes[--i];
142 unsigned last_collapsed = collapsed_indexes[i - 1];
143 DCHECK_GT(current_collapsed, last_collapsed);
144 if (last_collapsed + 1 < current_collapsed)
145 return last_collapsed + 1;
146 }
147 DCHECK_GT(collapsed_indexes[0], start);
148 return collapsed_indexes[0] - 1;
149 }
150
151 NGInlineItemsBuilder::AppendResult NGInlineItemsBuilder::Append(
152 const String& string,
153 const ComputedStyle* style,
154 LayoutObject* layout_object) {
130 if (string.IsEmpty()) 155 if (string.IsEmpty())
131 return; 156 return AppendResult(false, {});
132 text_.ReserveCapacity(string.length()); 157 text_.ReserveCapacity(string.length());
133 158
134 EWhiteSpace whitespace = style->WhiteSpace(); 159 EWhiteSpace whitespace = style->WhiteSpace();
135 if (!ComputedStyle::CollapseWhiteSpace(whitespace)) 160 if (!ComputedStyle::CollapseWhiteSpace(whitespace))
136 return AppendWithoutWhiteSpaceCollapsing(string, style, layout_object); 161 return AppendWithoutWhiteSpaceCollapsing(string, style, layout_object);
137 if (ComputedStyle::PreserveNewline(whitespace) && !is_svgtext_) 162 if (ComputedStyle::PreserveNewline(whitespace) && !is_svgtext_)
138 return AppendWithPreservingNewlines(string, style, layout_object); 163 return AppendWithPreservingNewlines(string, style, layout_object);
139 164
140 AppendWithWhiteSpaceCollapsing(string, 0, string.length(), style, 165 return AppendWithWhiteSpaceCollapsing(string, 0, string.length(), style,
141 layout_object); 166 layout_object);
142 } 167 }
143 168
144 void NGInlineItemsBuilder::AppendWithWhiteSpaceCollapsing( 169 NGInlineItemsBuilder::AppendResult
170 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) {
176 AppendResult result(false, {});
150 unsigned start_offset = text_.length(); 177 unsigned start_offset = text_.length();
151 for (unsigned i = start; i < end;) { 178 for (unsigned i = start; i < end;) {
152 UChar c = string[i]; 179 UChar c = string[i];
153 if (c == kNewlineCharacter) { 180 if (c == kNewlineCharacter) {
154 // LayoutBR does not set preserve_newline, but should be preserved. 181 // LayoutBR does not set preserve_newline, but should be preserved.
155 if (!i && end == 1 && layout_object && layout_object->IsBR()) { 182 if (!i && end == 1 && layout_object && layout_object->IsBR())
156 AppendForcedBreak(style, layout_object); 183 return AppendResult(AppendForcedBreak(style, layout_object), {});
157 return;
158 }
159 184
160 if (last_collapsible_space_ == CollapsibleSpace::kNone) 185 if (last_collapsible_space_ == CollapsibleSpace::kNone)
161 text_.Append(kSpaceCharacter); 186 text_.Append(kSpaceCharacter);
187 else
188 result.second.push_back(i);
162 last_collapsible_space_ = CollapsibleSpace::kNewline; 189 last_collapsible_space_ = CollapsibleSpace::kNewline;
163 i++; 190 i++;
164 continue; 191 continue;
165 } 192 }
166 193
167 if (c == kSpaceCharacter || c == kTabulationCharacter) { 194 if (c == kSpaceCharacter || c == kTabulationCharacter) {
168 if (last_collapsible_space_ == CollapsibleSpace::kNone) { 195 if (last_collapsible_space_ == CollapsibleSpace::kNone) {
169 text_.Append(kSpaceCharacter); 196 text_.Append(kSpaceCharacter);
170 last_collapsible_space_ = CollapsibleSpace::kSpace; 197 last_collapsible_space_ = CollapsibleSpace::kSpace;
198 } else {
199 result.second.push_back(i);
171 } 200 }
172 i++; 201 i++;
173 continue; 202 continue;
174 } 203 }
175 204
176 if (last_collapsible_space_ == CollapsibleSpace::kNewline) { 205 if (last_collapsible_space_ == CollapsibleSpace::kNewline) {
177 RemoveTrailingCollapsibleNewlineIfNeeded(&start_offset, string, i, style); 206 bool removed = RemoveTrailingCollapsibleNewlineIfNeeded(&start_offset,
207 string, i, style);
208 if (removed) {
209 const size_t last_appended =
210 GetLastAppendedCharacterIndex(result.second, start, i);
211 if (last_appended == kNotFound) {
212 result.first = true;
213 } else {
214 result.second.push_back(last_appended);
215 std::sort(result.second.begin(), result.second.end());
216 }
217 }
178 } 218 }
179 219
180 size_t end_of_non_space = string.Find(IsCollapsibleSpace, i + 1); 220 size_t end_of_non_space = string.Find(IsCollapsibleSpace, i + 1);
181 if (end_of_non_space == kNotFound) 221 if (end_of_non_space == kNotFound)
182 end_of_non_space = string.length(); 222 end_of_non_space = string.length();
183 text_.Append(string, i, end_of_non_space - i); 223 text_.Append(string, i, end_of_non_space - i);
184 i = end_of_non_space; 224 i = end_of_non_space;
185 last_collapsible_space_ = CollapsibleSpace::kNone; 225 last_collapsible_space_ = CollapsibleSpace::kNone;
186 } 226 }
187 227
188 if (text_.length() > start_offset) { 228 if (text_.length() > start_offset) {
189 AppendItem(items_, NGInlineItem::kText, start_offset, text_.length(), style, 229 AppendItem(items_, NGInlineItem::kText, start_offset, text_.length(), style,
190 layout_object); 230 layout_object);
191 } 231 }
232
233 return result;
192 } 234 }
193 235
194 // Even when without whitespace collapsing, control characters (newlines and 236 // Even when without whitespace collapsing, control characters (newlines and
195 // tabs) are in their own control items to make the line breaker easier. 237 // tabs) are in their own control items to make the line breaker easier.
196 void NGInlineItemsBuilder::AppendWithoutWhiteSpaceCollapsing( 238 NGInlineItemsBuilder::AppendResult
239 NGInlineItemsBuilder::AppendWithoutWhiteSpaceCollapsing(
197 const String& string, 240 const String& string,
198 const ComputedStyle* style, 241 const ComputedStyle* style,
199 LayoutObject* layout_object) { 242 LayoutObject* layout_object) {
200 for (unsigned start = 0; start < string.length();) { 243 for (unsigned start = 0; start < string.length();) {
201 UChar c = string[start]; 244 UChar c = string[start];
202 if (IsControlItemCharacter(c)) { 245 if (IsControlItemCharacter(c)) {
203 Append(NGInlineItem::kControl, c, style, layout_object); 246 Append(NGInlineItem::kControl, c, style, layout_object);
204 start++; 247 start++;
205 continue; 248 continue;
206 } 249 }
207 250
208 size_t end = string.Find(IsControlItemCharacter, start + 1); 251 size_t end = string.Find(IsControlItemCharacter, start + 1);
209 if (end == kNotFound) 252 if (end == kNotFound)
210 end = string.length(); 253 end = string.length();
211 unsigned start_offset = text_.length(); 254 unsigned start_offset = text_.length();
212 text_.Append(string, start, end - start); 255 text_.Append(string, start, end - start);
213 AppendItem(items_, NGInlineItem::kText, start_offset, text_.length(), style, 256 AppendItem(items_, NGInlineItem::kText, start_offset, text_.length(), style,
214 layout_object); 257 layout_object);
215 start = end; 258 start = end;
216 } 259 }
217 260
218 last_collapsible_space_ = CollapsibleSpace::kNone; 261 last_collapsible_space_ = CollapsibleSpace::kNone;
262 return AppendResult(false, {});
219 } 263 }
220 264
221 void NGInlineItemsBuilder::AppendWithPreservingNewlines( 265 NGInlineItemsBuilder::AppendResult
266 NGInlineItemsBuilder::AppendWithPreservingNewlines(
222 const String& string, 267 const String& string,
223 const ComputedStyle* style, 268 const ComputedStyle* style,
224 LayoutObject* layout_object) { 269 LayoutObject* layout_object) {
270 AppendResult result(false, {});
225 for (unsigned start = 0; start < string.length();) { 271 for (unsigned start = 0; start < string.length();) {
226 if (string[start] == kNewlineCharacter) { 272 if (string[start] == kNewlineCharacter) {
227 AppendForcedBreak(style, layout_object); 273 if (AppendForcedBreak(style, layout_object)) {
274 const size_t last_appended =
275 GetLastAppendedCharacterIndex(result.second, 0, start);
276 if (last_appended == kNotFound) {
277 result.first = true;
278 } else {
279 result.second.push_back(last_appended);
280 std::sort(result.second.begin(), result.second.end());
281 }
282 }
228 start++; 283 start++;
229 continue; 284 continue;
230 } 285 }
231 286
232 size_t end = string.find(kNewlineCharacter, start + 1); 287 size_t end = string.find(kNewlineCharacter, start + 1);
233 if (end == kNotFound) 288 if (end == kNotFound)
234 end = string.length(); 289 end = string.length();
235 AppendWithWhiteSpaceCollapsing(string, start, end, style, layout_object); 290 AppendResult run_result = AppendWithWhiteSpaceCollapsing(
291 string, start, end, style, layout_object);
292 if (run_result.first) {
293 const size_t last_appended =
294 GetLastAppendedCharacterIndex(result.second, 0, start);
295 if (last_appended == kNotFound) {
296 result.first = true;
297 } else {
298 result.second.push_back(last_appended);
299 std::sort(result.second.begin(), result.second.end());
300 }
301 }
302 result.second.AppendVector(run_result.second);
236 start = end; 303 start = end;
237 } 304 }
305 return result;
238 } 306 }
239 307
240 void NGInlineItemsBuilder::AppendForcedBreak(const ComputedStyle* style, 308 bool NGInlineItemsBuilder::AppendForcedBreak(const ComputedStyle* style,
241 LayoutObject* layout_object) { 309 LayoutObject* layout_object) {
242 // Remove collapsible spaces immediately before a preserved newline. 310 // Remove collapsible spaces immediately before a preserved newline.
243 unsigned start_offset = text_.length(); 311 unsigned start_offset = text_.length();
244 RemoveTrailingCollapsibleSpaceIfExists(&start_offset); 312 bool result = RemoveTrailingCollapsibleSpaceIfExists(&start_offset);
245 313
246 Append(NGInlineItem::kControl, kNewlineCharacter, style, layout_object); 314 Append(NGInlineItem::kControl, kNewlineCharacter, style, layout_object);
247 315
248 // Remove collapsible spaces immediately after a preserved newline. 316 // Remove collapsible spaces immediately after a preserved newline.
249 last_collapsible_space_ = CollapsibleSpace::kSpace; 317 last_collapsible_space_ = CollapsibleSpace::kSpace;
318 return result;
250 } 319 }
251 320
252 void NGInlineItemsBuilder::Append(NGInlineItem::NGInlineItemType type, 321 void NGInlineItemsBuilder::Append(NGInlineItem::NGInlineItemType type,
253 UChar character, 322 UChar character,
254 const ComputedStyle* style, 323 const ComputedStyle* style,
255 LayoutObject* layout_object) { 324 LayoutObject* layout_object) {
256 DCHECK_NE(character, kSpaceCharacter); 325 DCHECK_NE(character, kSpaceCharacter);
257 DCHECK_NE(character, kZeroWidthSpaceCharacter); 326 DCHECK_NE(character, kZeroWidthSpaceCharacter);
258 327
259 text_.Append(character); 328 text_.Append(character);
260 unsigned end_offset = text_.length(); 329 unsigned end_offset = text_.length();
261 AppendItem(items_, type, end_offset - 1, end_offset, style, layout_object); 330 AppendItem(items_, type, end_offset - 1, end_offset, style, layout_object);
262 last_collapsible_space_ = CollapsibleSpace::kNone; 331 last_collapsible_space_ = CollapsibleSpace::kNone;
263 } 332 }
264 333
265 void NGInlineItemsBuilder::Append(NGInlineItem::NGInlineItemType type, 334 void NGInlineItemsBuilder::Append(NGInlineItem::NGInlineItemType type,
266 const ComputedStyle* style, 335 const ComputedStyle* style,
267 LayoutObject* layout_object) { 336 LayoutObject* layout_object) {
268 unsigned end_offset = text_.length(); 337 unsigned end_offset = text_.length();
269 AppendItem(items_, type, end_offset, end_offset, style, layout_object); 338 AppendItem(items_, type, end_offset, end_offset, style, layout_object);
270 } 339 }
271 340
272 void NGInlineItemsBuilder::RemoveTrailingCollapsibleNewlineIfNeeded( 341 bool NGInlineItemsBuilder::RemoveTrailingCollapsibleNewlineIfNeeded(
273 unsigned* next_start_offset, 342 unsigned* next_start_offset,
274 const String& after, 343 const String& after,
275 unsigned after_index, 344 unsigned after_index,
276 const ComputedStyle* after_style) { 345 const ComputedStyle* after_style) {
277 DCHECK_EQ(last_collapsible_space_, CollapsibleSpace::kNewline); 346 DCHECK_EQ(last_collapsible_space_, CollapsibleSpace::kNewline);
278 347
279 if (text_.IsEmpty() || text_[text_.length() - 1] != kSpaceCharacter) 348 if (text_.IsEmpty() || text_[text_.length() - 1] != kSpaceCharacter)
280 return; 349 return false;
281 350
282 const ComputedStyle* before_style = after_style; 351 const ComputedStyle* before_style = after_style;
283 if (!items_->IsEmpty()) { 352 if (!items_->IsEmpty()) {
284 NGInlineItem& item = items_->back(); 353 NGInlineItem& item = items_->back();
285 if (text_.length() < item.EndOffset() + 2) 354 if (text_.length() < item.EndOffset() + 2)
286 before_style = item.Style(); 355 before_style = item.Style();
287 } 356 }
288 357
289 if (ShouldRemoveNewline(text_, before_style, after, after_index, after_style)) 358 if (!ShouldRemoveNewline(text_, before_style, after, after_index,
290 RemoveTrailingCollapsibleSpace(next_start_offset); 359 after_style))
360 return false;
361 RemoveTrailingCollapsibleSpace(next_start_offset);
362 return true;
291 } 363 }
292 364
293 void NGInlineItemsBuilder::RemoveTrailingCollapsibleSpaceIfExists( 365 bool NGInlineItemsBuilder::RemoveTrailingCollapsibleSpaceIfExists(
294 unsigned* next_start_offset) { 366 unsigned* next_start_offset) {
295 if (last_collapsible_space_ != CollapsibleSpace::kNone && !text_.IsEmpty() && 367 if (last_collapsible_space_ == CollapsibleSpace::kNone || text_.IsEmpty() ||
296 text_[text_.length() - 1] == kSpaceCharacter) 368 text_[text_.length() - 1] != kSpaceCharacter)
297 RemoveTrailingCollapsibleSpace(next_start_offset); 369 return false;
370 RemoveTrailingCollapsibleSpace(next_start_offset);
371 return true;
298 } 372 }
299 373
300 void NGInlineItemsBuilder::RemoveTrailingCollapsibleSpace( 374 void NGInlineItemsBuilder::RemoveTrailingCollapsibleSpace(
301 unsigned* next_start_offset) { 375 unsigned* next_start_offset) {
302 DCHECK_NE(last_collapsible_space_, CollapsibleSpace::kNone); 376 DCHECK_NE(last_collapsible_space_, CollapsibleSpace::kNone);
303 DCHECK(!text_.IsEmpty()); 377 DCHECK(!text_.IsEmpty());
304 DCHECK_EQ(text_[text_.length() - 1], kSpaceCharacter); 378 DCHECK_EQ(text_[text_.length() - 1], kSpaceCharacter);
305 379
306 unsigned new_size = text_.length() - 1; 380 unsigned new_size = text_.length() - 1;
307 text_.Resize(new_size); 381 text_.Resize(new_size);
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
422 } 496 }
423 497
424 void NGInlineItemsBuilder::Exit(LayoutObject* node) { 498 void NGInlineItemsBuilder::Exit(LayoutObject* node) {
425 while (!exits_.IsEmpty() && exits_.back().node == node) { 499 while (!exits_.IsEmpty() && exits_.back().node == node) {
426 Append(NGInlineItem::kBidiControl, exits_.back().character); 500 Append(NGInlineItem::kBidiControl, exits_.back().character);
427 exits_.pop_back(); 501 exits_.pop_back();
428 } 502 }
429 } 503 }
430 504
431 } // namespace blink 505 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698