1use std::borrow::Cow;
6use std::char::{ToLowercase, ToUppercase};
7
8use icu_segmenter::WordSegmenter;
9use layout_api::wrapper_traits::{SharedSelection, ThreadSafeLayoutNode};
10use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
11use style::values::specified::text::TextTransformCase;
12use unicode_bidi::Level;
13
14use super::text_run::TextRun;
15use super::{
16 InlineBox, InlineBoxIdentifier, InlineBoxes, InlineFormattingContext, InlineItem,
17 SharedInlineStyles,
18};
19use crate::cell::ArcRefCell;
20use crate::context::LayoutContext;
21use crate::dom::LayoutBox;
22use crate::dom_traversal::NodeAndStyleInfo;
23use crate::flow::float::FloatBox;
24use crate::flow::inline::AnonymousBlockBox;
25use crate::flow::{BlockContainer, BlockLevelBox, PseudoElement};
26use crate::formatting_contexts::IndependentFormattingContext;
27use crate::layout_box_base::LayoutBoxBase;
28use crate::positioned::AbsolutelyPositionedBox;
29use crate::style_ext::ComputedValuesExt;
30
31#[derive(Default)]
32pub(crate) struct InlineFormattingContextBuilder {
33 pub shared_inline_styles_stack: Vec<SharedInlineStyles>,
38
39 pub text_segments: Vec<String>,
42
43 current_text_offset: usize,
46
47 current_character_offset: usize,
51
52 pub shared_selection: Option<SharedSelection>,
55
56 last_inline_box_ended_with_collapsible_white_space: bool,
64
65 on_word_boundary: bool,
68
69 pub contains_floats: bool,
71
72 pub inline_items: Vec<InlineItem>,
76
77 pub inline_boxes: InlineBoxes,
79
80 inline_box_stack: Vec<InlineBoxIdentifier>,
89
90 pub is_empty: bool,
94}
95
96impl InlineFormattingContextBuilder {
97 pub(crate) fn new(info: &NodeAndStyleInfo) -> Self {
98 Self {
99 on_word_boundary: true,
101 is_empty: true,
102 shared_inline_styles_stack: vec![info.into()],
103 shared_selection: info.node.selection(),
104 ..Default::default()
105 }
106 }
107
108 pub(crate) fn currently_processing_inline_box(&self) -> bool {
109 !self.inline_box_stack.is_empty()
110 }
111
112 fn push_control_character_string(&mut self, string_to_push: &str) {
113 self.text_segments.push(string_to_push.to_owned());
114 self.current_text_offset += string_to_push.len();
115 self.current_character_offset += string_to_push.chars().count();
116 }
117
118 fn shared_inline_styles(&self) -> SharedInlineStyles {
119 self.shared_inline_styles_stack
120 .last()
121 .expect("Should always have at least one SharedInlineStyles")
122 .clone()
123 }
124
125 pub(crate) fn push_atomic(
126 &mut self,
127 independent_formatting_context_creator: impl FnOnce()
128 -> ArcRefCell<IndependentFormattingContext>,
129 old_layout_box: Option<LayoutBox>,
130 ) -> InlineItem {
131 let independent_formatting_context = old_layout_box
133 .and_then(|layout_box| match layout_box {
134 LayoutBox::InlineLevel(InlineItem::Atomic(atomic, ..)) => Some(atomic.clone()),
135 _ => None,
136 })
137 .unwrap_or_else(independent_formatting_context_creator);
138
139 let inline_level_box = InlineItem::Atomic(
140 independent_formatting_context,
141 self.current_text_offset,
142 Level::ltr(), );
144 self.inline_items.push(inline_level_box.clone());
145 self.is_empty = false;
146
147 self.push_control_character_string("\u{fffc}");
150
151 self.last_inline_box_ended_with_collapsible_white_space = false;
152 self.on_word_boundary = true;
153
154 inline_level_box
155 }
156
157 pub(crate) fn push_absolutely_positioned_box(
158 &mut self,
159 absolutely_positioned_box_creator: impl FnOnce() -> ArcRefCell<AbsolutelyPositionedBox>,
160 old_layout_box: Option<LayoutBox>,
161 ) -> InlineItem {
162 let absolutely_positioned_box = old_layout_box
163 .and_then(|layout_box| match layout_box {
164 LayoutBox::InlineLevel(InlineItem::OutOfFlowAbsolutelyPositionedBox(
165 positioned_box,
166 ..,
167 )) => Some(positioned_box.clone()),
168 _ => None,
169 })
170 .unwrap_or_else(absolutely_positioned_box_creator);
171
172 let inline_level_box = InlineItem::OutOfFlowAbsolutelyPositionedBox(
174 absolutely_positioned_box,
175 self.current_text_offset,
176 );
177
178 self.inline_items.push(inline_level_box.clone());
179 self.is_empty = false;
180 inline_level_box
181 }
182
183 pub(crate) fn push_float_box(
184 &mut self,
185 float_box_creator: impl FnOnce() -> ArcRefCell<FloatBox>,
186 old_layout_box: Option<LayoutBox>,
187 ) -> InlineItem {
188 let inline_level_box = old_layout_box
189 .and_then(|layout_box| match layout_box {
190 LayoutBox::InlineLevel(inline_item) => Some(inline_item),
191 _ => None,
192 })
193 .unwrap_or_else(|| InlineItem::OutOfFlowFloatBox(float_box_creator()));
194
195 debug_assert!(
196 matches!(inline_level_box, InlineItem::OutOfFlowFloatBox(..),),
197 "Created float box with incompatible `old_layout_box`"
198 );
199
200 self.inline_items.push(inline_level_box.clone());
201 self.is_empty = false;
202 self.contains_floats = true;
203 inline_level_box
204 }
205
206 pub(crate) fn push_block_level_box(
207 &mut self,
208 block_level_box: ArcRefCell<BlockLevelBox>,
209 block_builder_info: &NodeAndStyleInfo,
210 layout_context: &LayoutContext,
211 ) {
212 assert!(self.currently_processing_inline_box());
213 self.contains_floats = self.contains_floats || block_level_box.borrow().contains_floats();
214
215 if let Some(InlineItem::AnonymousBlock(anonymous_block)) = self.inline_items.last() {
216 if let BlockContainer::BlockLevelBoxes(ref mut block_level_boxes) =
217 anonymous_block.borrow_mut().contents
218 {
219 block_level_boxes.push(block_level_box);
220 return;
221 }
222 }
223 let info = &block_builder_info
224 .with_pseudo_element(layout_context, PseudoElement::ServoAnonymousBox)
225 .expect("Should never fail to create anonymous box");
226 self.inline_items
227 .push(InlineItem::AnonymousBlock(ArcRefCell::new(
228 AnonymousBlockBox {
229 base: LayoutBoxBase::new(info.into(), info.style.clone()),
230 contents: BlockContainer::BlockLevelBoxes(vec![block_level_box]),
231 },
232 )));
233 }
234
235 pub(crate) fn start_inline_box(
236 &mut self,
237 inline_box_creator: impl FnOnce() -> ArcRefCell<InlineBox>,
238 old_layout_box: Option<LayoutBox>,
239 ) {
240 let inline_box = old_layout_box
242 .and_then(|layout_box| match layout_box {
243 LayoutBox::InlineLevel(InlineItem::StartInlineBox(inline_box)) => Some(inline_box),
244 _ => None,
245 })
246 .unwrap_or_else(inline_box_creator);
247
248 let borrowed_inline_box = inline_box.borrow();
249 self.push_control_character_string(borrowed_inline_box.base.style.bidi_control_chars().0);
250
251 self.shared_inline_styles_stack
252 .push(borrowed_inline_box.shared_inline_styles.clone());
253 std::mem::drop(borrowed_inline_box);
254
255 let identifier = self.inline_boxes.start_inline_box(inline_box.clone());
256 self.inline_items
257 .push(InlineItem::StartInlineBox(inline_box));
258 self.inline_box_stack.push(identifier);
259 self.is_empty = false;
260 }
261
262 pub(crate) fn end_inline_box(&mut self) {
267 self.shared_inline_styles_stack.pop();
268 self.inline_items.push(InlineItem::EndInlineBox);
269 let identifier = self
270 .inline_box_stack
271 .pop()
272 .expect("Ended non-existent inline box");
273 self.inline_boxes.end_inline_box(identifier);
274 let inline_level_box = self.inline_boxes.get(&identifier);
275 let bidi_control_chars = inline_level_box.borrow().base.style.bidi_control_chars();
276 self.push_control_character_string(bidi_control_chars.1);
277 }
278
279 pub(crate) fn push_text<'dom>(&mut self, text: Cow<'dom, str>, info: &NodeAndStyleInfo<'dom>) {
280 let white_space_collapse = info.style.clone_white_space_collapse();
281 let collapsed = WhitespaceCollapse::new(
282 text.chars(),
283 white_space_collapse,
284 self.last_inline_box_ended_with_collapsible_white_space,
285 );
286
287 let text_transform = info.style.clone_text_transform().case();
290 let capitalized_text: String;
291 let char_iterator: Box<dyn Iterator<Item = char>> = match text_transform {
292 TextTransformCase::None => Box::new(collapsed),
293 TextTransformCase::Capitalize => {
294 let collapsed_string: String = collapsed.collect();
301 capitalized_text = capitalize_string(&collapsed_string, self.on_word_boundary);
302 Box::new(capitalized_text.chars())
303 },
304 _ => {
305 Box::new(TextTransformation::new(collapsed, text_transform))
308 },
309 };
310
311 let white_space_collapse = info.style.clone_white_space_collapse();
312 let mut character_count = 0;
313 let new_text: String = char_iterator
314 .inspect(|&character| {
315 character_count += 1;
316
317 self.is_empty = self.is_empty &&
318 match white_space_collapse {
319 WhiteSpaceCollapse::Collapse => character.is_ascii_whitespace(),
320 WhiteSpaceCollapse::PreserveBreaks => {
321 character.is_ascii_whitespace() && character != '\n'
322 },
323 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces => false,
324 };
325 })
326 .collect();
327
328 if new_text.is_empty() {
329 return;
330 }
331
332 if let Some(last_character) = new_text.chars().next_back() {
333 self.on_word_boundary = last_character.is_whitespace();
334 self.last_inline_box_ended_with_collapsible_white_space =
335 self.on_word_boundary && white_space_collapse != WhiteSpaceCollapse::Preserve;
336 }
337
338 let new_range = self.current_text_offset..self.current_text_offset + new_text.len();
339 self.current_text_offset = new_range.end;
340
341 let new_character_range =
342 self.current_character_offset..self.current_character_offset + character_count;
343 self.current_character_offset = new_character_range.end;
344
345 self.text_segments.push(new_text);
346
347 let current_inline_styles = self.shared_inline_styles();
348
349 if let Some(InlineItem::TextRun(text_run)) = self.inline_items.last() {
350 if text_run
351 .borrow()
352 .inline_styles
353 .ptr_eq(¤t_inline_styles)
354 {
355 text_run.borrow_mut().text_range.end = new_range.end;
356 text_run.borrow_mut().character_range.end = new_character_range.end;
357 return;
358 }
359 }
360
361 self.inline_items
362 .push(InlineItem::TextRun(ArcRefCell::new(TextRun::new(
363 info.into(),
364 current_inline_styles,
365 new_range,
366 new_character_range,
367 ))));
368 }
369
370 pub(crate) fn enter_display_contents(&mut self, shared_inline_styles: SharedInlineStyles) {
371 self.shared_inline_styles_stack.push(shared_inline_styles);
372 }
373
374 pub(crate) fn leave_display_contents(&mut self) {
375 self.shared_inline_styles_stack.pop();
376 }
377
378 pub(crate) fn finish(
380 self,
381 layout_context: &LayoutContext,
382 has_first_formatted_line: bool,
383 is_single_line_text_input: bool,
384 default_bidi_level: Level,
385 ) -> Option<InlineFormattingContext> {
386 if self.is_empty {
387 return None;
388 }
389
390 assert!(self.inline_box_stack.is_empty());
391 Some(InlineFormattingContext::new_with_builder(
392 self,
393 layout_context,
394 has_first_formatted_line,
395 is_single_line_text_input,
396 default_bidi_level,
397 ))
398 }
399}
400
401fn preserve_segment_break() -> bool {
402 true
403}
404
405pub struct WhitespaceCollapse<InputIterator> {
406 char_iterator: InputIterator,
407 white_space_collapse: WhiteSpaceCollapse,
408
409 remove_collapsible_white_space_at_start: bool,
413
414 following_newline: bool,
417
418 have_seen_non_white_space_characters: bool,
421
422 inside_white_space: bool,
426
427 character_pending_to_return: Option<char>,
431}
432
433impl<InputIterator> WhitespaceCollapse<InputIterator> {
434 pub fn new(
435 char_iterator: InputIterator,
436 white_space_collapse: WhiteSpaceCollapse,
437 trim_beginning_white_space: bool,
438 ) -> Self {
439 Self {
440 char_iterator,
441 white_space_collapse,
442 remove_collapsible_white_space_at_start: trim_beginning_white_space,
443 inside_white_space: false,
444 following_newline: false,
445 have_seen_non_white_space_characters: false,
446 character_pending_to_return: None,
447 }
448 }
449
450 fn is_leading_trimmed_white_space(&self) -> bool {
451 !self.have_seen_non_white_space_characters && self.remove_collapsible_white_space_at_start
452 }
453
454 fn need_to_produce_space_character_after_white_space(&self) -> bool {
459 self.inside_white_space && !self.following_newline && !self.is_leading_trimmed_white_space()
460 }
461}
462
463impl<InputIterator> Iterator for WhitespaceCollapse<InputIterator>
464where
465 InputIterator: Iterator<Item = char>,
466{
467 type Item = char;
468
469 fn next(&mut self) -> Option<Self::Item> {
470 if self.white_space_collapse == WhiteSpaceCollapse::Preserve ||
476 self.white_space_collapse == WhiteSpaceCollapse::BreakSpaces
477 {
478 return match self.char_iterator.next() {
483 Some('\r') => Some(' '),
484 next => next,
485 };
486 }
487
488 if let Some(character) = self.character_pending_to_return.take() {
489 self.inside_white_space = false;
490 self.have_seen_non_white_space_characters = true;
491 self.following_newline = false;
492 return Some(character);
493 }
494
495 while let Some(character) = self.char_iterator.next() {
496 if character.is_ascii_whitespace() && character != '\n' {
500 self.inside_white_space = true;
501 continue;
502 }
503
504 if character == '\n' {
508 if self.white_space_collapse != WhiteSpaceCollapse::Collapse {
514 self.inside_white_space = false;
515 self.following_newline = true;
516 return Some(character);
517
518 } else if !self.following_newline &&
524 preserve_segment_break() &&
525 !self.is_leading_trimmed_white_space()
526 {
527 self.inside_white_space = false;
528 self.following_newline = true;
529 return Some(' ');
530 } else {
531 self.following_newline = true;
532 continue;
533 }
534 }
535
536 if self.need_to_produce_space_character_after_white_space() {
545 self.inside_white_space = false;
546 self.character_pending_to_return = Some(character);
547 return Some(' ');
548 }
549
550 self.inside_white_space = false;
551 self.have_seen_non_white_space_characters = true;
552 self.following_newline = false;
553 return Some(character);
554 }
555
556 if self.need_to_produce_space_character_after_white_space() {
557 self.inside_white_space = false;
558 return Some(' ');
559 }
560
561 None
562 }
563
564 fn size_hint(&self) -> (usize, Option<usize>) {
565 self.char_iterator.size_hint()
566 }
567
568 fn count(self) -> usize
569 where
570 Self: Sized,
571 {
572 self.char_iterator.count()
573 }
574}
575
576enum PendingCaseConversionResult {
577 Uppercase(ToUppercase),
578 Lowercase(ToLowercase),
579}
580
581impl PendingCaseConversionResult {
582 fn next(&mut self) -> Option<char> {
583 match self {
584 PendingCaseConversionResult::Uppercase(to_uppercase) => to_uppercase.next(),
585 PendingCaseConversionResult::Lowercase(to_lowercase) => to_lowercase.next(),
586 }
587 }
588}
589
590pub struct TextTransformation<InputIterator> {
595 char_iterator: InputIterator,
597 text_transform: TextTransformCase,
599 pending_case_conversion_result: Option<PendingCaseConversionResult>,
602}
603
604impl<InputIterator> TextTransformation<InputIterator> {
605 pub fn new(char_iterator: InputIterator, text_transform: TextTransformCase) -> Self {
606 Self {
607 char_iterator,
608 text_transform,
609 pending_case_conversion_result: None,
610 }
611 }
612}
613
614impl<InputIterator> Iterator for TextTransformation<InputIterator>
615where
616 InputIterator: Iterator<Item = char>,
617{
618 type Item = char;
619
620 fn next(&mut self) -> Option<Self::Item> {
621 if let Some(character) = self
622 .pending_case_conversion_result
623 .as_mut()
624 .and_then(|result| result.next())
625 {
626 return Some(character);
627 }
628 self.pending_case_conversion_result = None;
629
630 for character in self.char_iterator.by_ref() {
631 match self.text_transform {
632 TextTransformCase::None => return Some(character),
633 TextTransformCase::Uppercase => {
634 let mut pending_result =
635 PendingCaseConversionResult::Uppercase(character.to_uppercase());
636 if let Some(character) = pending_result.next() {
637 self.pending_case_conversion_result = Some(pending_result);
638 return Some(character);
639 }
640 },
641 TextTransformCase::Lowercase => {
642 let mut pending_result =
643 PendingCaseConversionResult::Lowercase(character.to_lowercase());
644 if let Some(character) = pending_result.next() {
645 self.pending_case_conversion_result = Some(pending_result);
646 return Some(character);
647 }
648 },
649 TextTransformCase::Capitalize => return Some(character),
652 }
653 }
654 None
655 }
656}
657
658pub(crate) fn capitalize_string(string: &str, allow_word_at_start: bool) -> String {
661 let mut output_string = String::new();
662 output_string.reserve(string.len());
663
664 let word_segmenter = WordSegmenter::new_auto();
665 let mut bounds = word_segmenter.segment_str(string).peekable();
666 let mut byte_index = 0;
667 for character in string.chars() {
668 let current_byte_index = byte_index;
669 byte_index += character.len_utf8();
670
671 if let Some(next_index) = bounds.peek() {
672 if *next_index == current_byte_index {
673 bounds.next();
674
675 if current_byte_index != 0 || allow_word_at_start {
676 output_string.extend(character.to_uppercase());
677 continue;
678 }
679 }
680 }
681
682 output_string.push(character);
683 }
684
685 output_string
686}