1use std::borrow::Cow;
6use std::char::{ToLowercase, ToUppercase};
7
8use icu_segmenter::WordSegmenter;
9use itertools::izip;
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::formatting_contexts::IndependentFormattingContext;
25use crate::positioned::AbsolutelyPositionedBox;
26use crate::style_ext::ComputedValuesExt;
27
28#[derive(Default)]
29pub(crate) struct InlineFormattingContextBuilder {
30 pub shared_inline_styles_stack: Vec<SharedInlineStyles>,
35
36 pub text_segments: Vec<String>,
39
40 current_text_offset: usize,
43
44 last_inline_box_ended_with_collapsible_white_space: bool,
52
53 on_word_boundary: bool,
56
57 pub contains_floats: bool,
59
60 pub inline_items: Vec<ArcRefCell<InlineItem>>,
64
65 pub inline_boxes: InlineBoxes,
67
68 inline_box_stack: Vec<InlineBoxIdentifier>,
77
78 block_in_inline_splits: Vec<Vec<ArcRefCell<InlineItem>>>,
87
88 old_block_in_inline_splits: Vec<Vec<ArcRefCell<InlineBox>>>,
96
97 pub is_empty: bool,
101}
102
103impl InlineFormattingContextBuilder {
104 pub(crate) fn new(info: &NodeAndStyleInfo) -> Self {
105 Self::new_for_shared_styles(vec![info.into()])
106 }
107
108 pub(crate) fn new_for_shared_styles(
109 shared_inline_styles_stack: Vec<SharedInlineStyles>,
110 ) -> Self {
111 Self {
112 on_word_boundary: true,
114 is_empty: true,
115 shared_inline_styles_stack,
116 ..Default::default()
117 }
118 }
119
120 pub(crate) fn currently_processing_inline_box(&self) -> bool {
121 !self.inline_box_stack.is_empty()
122 }
123
124 fn push_control_character_string(&mut self, string_to_push: &str) {
125 self.text_segments.push(string_to_push.to_owned());
126 self.current_text_offset += string_to_push.len();
127 }
128
129 fn shared_inline_styles(&self) -> SharedInlineStyles {
130 self.shared_inline_styles_stack
131 .last()
132 .expect("Should always have at least one SharedInlineStyles")
133 .clone()
134 }
135
136 pub(crate) fn push_atomic(
137 &mut self,
138 independent_formatting_context_creator: impl FnOnce()
139 -> ArcRefCell<IndependentFormattingContext>,
140 old_layout_box: Option<LayoutBox>,
141 ) -> ArcRefCell<InlineItem> {
142 let independent_formatting_context = old_layout_box
144 .and_then(LayoutBox::unsplit_inline_level_layout_box)
145 .and_then(|inline_item| match &*inline_item.borrow() {
146 InlineItem::Atomic(atomic, ..) => Some(atomic.clone()),
147 _ => None,
148 })
149 .unwrap_or_else(independent_formatting_context_creator);
150
151 let inline_level_box = ArcRefCell::new(InlineItem::Atomic(
152 independent_formatting_context,
153 self.current_text_offset,
154 Level::ltr(), ));
156 self.inline_items.push(inline_level_box.clone());
157 self.is_empty = false;
158
159 self.push_control_character_string("\u{fffc}");
162
163 self.last_inline_box_ended_with_collapsible_white_space = false;
164 self.on_word_boundary = true;
165
166 inline_level_box
167 }
168
169 pub(crate) fn push_absolutely_positioned_box(
170 &mut self,
171 absolutely_positioned_box_creator: impl FnOnce() -> ArcRefCell<AbsolutelyPositionedBox>,
172 old_layout_box: Option<LayoutBox>,
173 ) -> ArcRefCell<InlineItem> {
174 let absolutely_positioned_box = old_layout_box
175 .and_then(LayoutBox::unsplit_inline_level_layout_box)
176 .and_then(|inline_item| match &*inline_item.borrow() {
177 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
178 Some(positioned_box.clone())
179 },
180 _ => None,
181 })
182 .unwrap_or_else(absolutely_positioned_box_creator);
183
184 let inline_level_box = ArcRefCell::new(InlineItem::OutOfFlowAbsolutelyPositionedBox(
186 absolutely_positioned_box,
187 self.current_text_offset,
188 ));
189
190 self.inline_items.push(inline_level_box.clone());
191 self.is_empty = false;
192 inline_level_box
193 }
194
195 pub(crate) fn push_float_box(
196 &mut self,
197 float_box_creator: impl FnOnce() -> ArcRefCell<FloatBox>,
198 old_layout_box: Option<LayoutBox>,
199 ) -> ArcRefCell<InlineItem> {
200 let inline_level_box = old_layout_box
201 .and_then(LayoutBox::unsplit_inline_level_layout_box)
202 .unwrap_or_else(|| ArcRefCell::new(InlineItem::OutOfFlowFloatBox(float_box_creator())));
203
204 debug_assert!(
205 matches!(
206 &*inline_level_box.borrow(),
207 InlineItem::OutOfFlowFloatBox(..),
208 ),
209 "Created float box with incompatible `old_layout_box`"
210 );
211
212 self.inline_items.push(inline_level_box.clone());
213 self.is_empty = false;
214 self.contains_floats = true;
215 inline_level_box
216 }
217
218 pub(crate) fn start_inline_box(
219 &mut self,
220 inline_box_creator: impl FnOnce() -> ArcRefCell<InlineBox>,
221 block_in_inline_splits: Option<Vec<ArcRefCell<InlineItem>>>,
222 old_layout_box: Option<LayoutBox>,
223 ) {
224 if let Some(LayoutBox::InlineLevel(inline_level_box)) = old_layout_box {
226 let old_block_in_inline_splits: Vec<ArcRefCell<InlineBox>> = inline_level_box
227 .iter()
228 .rev() .filter_map(|inline_item| match &*inline_item.borrow() {
230 InlineItem::StartInlineBox(inline_box) => Some(inline_box.clone()),
231 _ => None,
232 })
233 .collect();
234
235 debug_assert!(
236 old_block_in_inline_splits.is_empty() ||
237 old_block_in_inline_splits.len() == inline_level_box.len(),
238 "Create inline box with incompatible `old_layout_box`"
239 );
240
241 self.start_inline_box_internal(
242 inline_box_creator,
243 block_in_inline_splits,
244 old_block_in_inline_splits,
245 );
246 } else {
247 self.start_inline_box_internal(inline_box_creator, block_in_inline_splits, vec![]);
248 }
249 }
250
251 pub fn start_inline_box_internal(
252 &mut self,
253 inline_box_creator: impl FnOnce() -> ArcRefCell<InlineBox>,
254 block_in_inline_splits: Option<Vec<ArcRefCell<InlineItem>>>,
255 mut old_block_in_inline_splits: Vec<ArcRefCell<InlineBox>>,
256 ) {
257 let inline_box = old_block_in_inline_splits
258 .pop()
259 .unwrap_or_else(inline_box_creator);
260
261 let borrowed_inline_box = inline_box.borrow();
262 self.push_control_character_string(borrowed_inline_box.base.style.bidi_control_chars().0);
263
264 if borrowed_inline_box.is_first_split {
268 self.shared_inline_styles_stack
269 .push(borrowed_inline_box.shared_inline_styles.clone());
270 }
271 std::mem::drop(borrowed_inline_box);
272
273 let identifier = self.inline_boxes.start_inline_box(inline_box.clone());
274 let inline_level_box = ArcRefCell::new(InlineItem::StartInlineBox(inline_box));
275 self.inline_items.push(inline_level_box.clone());
276 self.inline_box_stack.push(identifier);
277 self.is_empty = false;
278
279 let mut block_in_inline_splits = block_in_inline_splits.unwrap_or_default();
280 block_in_inline_splits.push(inline_level_box);
281 self.block_in_inline_splits.push(block_in_inline_splits);
282
283 self.old_block_in_inline_splits
284 .push(old_block_in_inline_splits);
285 }
286
287 pub(crate) fn end_inline_box(&mut self) -> Vec<ArcRefCell<InlineItem>> {
292 self.shared_inline_styles_stack.pop();
293
294 let (identifier, block_in_inline_splits) = self.end_inline_box_internal();
295 let inline_level_box = self.inline_boxes.get(&identifier);
296 {
297 let mut inline_level_box = inline_level_box.borrow_mut();
298 inline_level_box.is_last_split = true;
299 self.push_control_character_string(inline_level_box.base.style.bidi_control_chars().1);
300 }
301
302 debug_assert!(
303 self.old_block_in_inline_splits
304 .last()
305 .is_some_and(|inline_boxes| inline_boxes.is_empty()),
306 "Reuse incompatible `old_block_in_inline_splits` for inline boxes",
307 );
308 let _ = self.old_block_in_inline_splits.pop();
309
310 block_in_inline_splits.unwrap_or_default()
311 }
312
313 fn end_inline_box_internal(
314 &mut self,
315 ) -> (InlineBoxIdentifier, Option<Vec<ArcRefCell<InlineItem>>>) {
316 let identifier = self
317 .inline_box_stack
318 .pop()
319 .expect("Ended non-existent inline box");
320 self.inline_items
321 .push(ArcRefCell::new(InlineItem::EndInlineBox));
322 self.is_empty = false;
323
324 self.inline_boxes.end_inline_box(identifier);
325
326 let block_in_inline_splits = self.block_in_inline_splits.pop();
329
330 (identifier, block_in_inline_splits)
331 }
332
333 pub(crate) fn push_text<'dom>(&mut self, text: Cow<'dom, str>, info: &NodeAndStyleInfo<'dom>) {
334 let white_space_collapse = info.style.clone_white_space_collapse();
335 let collapsed = WhitespaceCollapse::new(
336 text.chars(),
337 white_space_collapse,
338 self.last_inline_box_ended_with_collapsible_white_space,
339 );
340
341 let text_transform = info.style.clone_text_transform().case();
344 let capitalized_text: String;
345 let char_iterator: Box<dyn Iterator<Item = char>> = match text_transform {
346 TextTransformCase::None => Box::new(collapsed),
347 TextTransformCase::Capitalize => {
348 let collapsed_string: String = collapsed.collect();
355 capitalized_text = capitalize_string(&collapsed_string, self.on_word_boundary);
356 Box::new(capitalized_text.chars())
357 },
358 _ => {
359 Box::new(TextTransformation::new(collapsed, text_transform))
362 },
363 };
364
365 let white_space_collapse = info.style.clone_white_space_collapse();
366 let new_text: String = char_iterator
367 .inspect(|&character| {
368 self.is_empty = self.is_empty &&
369 match white_space_collapse {
370 WhiteSpaceCollapse::Collapse => character.is_ascii_whitespace(),
371 WhiteSpaceCollapse::PreserveBreaks => {
372 character.is_ascii_whitespace() && character != '\n'
373 },
374 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces => false,
375 };
376 })
377 .collect();
378
379 if new_text.is_empty() {
380 return;
381 }
382
383 let selection_range = info.get_selection_range();
384 if let Some(last_character) = new_text.chars().next_back() {
385 self.on_word_boundary = last_character.is_whitespace();
386 self.last_inline_box_ended_with_collapsible_white_space =
387 self.on_word_boundary && white_space_collapse != WhiteSpaceCollapse::Preserve;
388 }
389
390 let new_range = self.current_text_offset..self.current_text_offset + new_text.len();
391 self.current_text_offset = new_range.end;
392 self.text_segments.push(new_text);
393
394 if let Some(inline_item) = self.inline_items.last() {
395 if let InlineItem::TextRun(text_run) = &mut *inline_item.borrow_mut() {
396 text_run.borrow_mut().text_range.end = new_range.end;
397 return;
398 }
399 }
400
401 self.inline_items
402 .push(ArcRefCell::new(InlineItem::TextRun(ArcRefCell::new(
403 TextRun::new(
404 info.into(),
405 self.shared_inline_styles(),
406 new_range,
407 selection_range,
408 ),
409 ))));
410 }
411
412 pub(crate) fn enter_display_contents(&mut self, shared_inline_styles: SharedInlineStyles) {
413 self.shared_inline_styles_stack.push(shared_inline_styles);
414 }
415
416 pub(crate) fn leave_display_contents(&mut self) {
417 self.shared_inline_styles_stack.pop();
418 }
419
420 pub(crate) fn split_around_block_and_finish(
421 &mut self,
422 layout_context: &LayoutContext,
423 has_first_formatted_line: bool,
424 default_bidi_level: Level,
425 ) -> Option<InlineFormattingContext> {
426 if self.is_empty {
427 return None;
428 }
429
430 let mut new_builder = Self::new_for_shared_styles(self.shared_inline_styles_stack.clone());
435
436 let block_in_inline_splits = std::mem::take(&mut self.block_in_inline_splits);
437 let old_block_in_inline_splits = std::mem::take(&mut self.old_block_in_inline_splits);
438 for (identifier, already_collected_inline_boxes, being_recollected_inline_boxes) in izip!(
439 self.inline_box_stack.iter(),
440 block_in_inline_splits,
441 old_block_in_inline_splits
442 ) {
443 let split_inline_box_callback = || {
451 ArcRefCell::new(
452 self.inline_boxes
453 .get(identifier)
454 .borrow()
455 .split_around_block(),
456 )
457 };
458 new_builder.start_inline_box_internal(
459 split_inline_box_callback,
460 Some(already_collected_inline_boxes),
461 being_recollected_inline_boxes,
462 );
463 }
464 let mut inline_builder_from_before_split = std::mem::replace(self, new_builder);
465
466 while !inline_builder_from_before_split.inline_box_stack.is_empty() {
470 inline_builder_from_before_split.end_inline_box_internal();
471 }
472
473 inline_builder_from_before_split.finish(
474 layout_context,
475 has_first_formatted_line,
476 false,
477 default_bidi_level,
478 )
479 }
480
481 pub(crate) fn finish(
483 self,
484 layout_context: &LayoutContext,
485 has_first_formatted_line: bool,
486 is_single_line_text_input: bool,
487 default_bidi_level: Level,
488 ) -> Option<InlineFormattingContext> {
489 if self.is_empty {
490 return None;
491 }
492
493 assert!(self.inline_box_stack.is_empty());
494 debug_assert!(self.old_block_in_inline_splits.is_empty());
495 Some(InlineFormattingContext::new_with_builder(
496 self,
497 layout_context,
498 has_first_formatted_line,
499 is_single_line_text_input,
500 default_bidi_level,
501 ))
502 }
503}
504
505fn preserve_segment_break() -> bool {
506 true
507}
508
509pub struct WhitespaceCollapse<InputIterator> {
510 char_iterator: InputIterator,
511 white_space_collapse: WhiteSpaceCollapse,
512
513 remove_collapsible_white_space_at_start: bool,
517
518 following_newline: bool,
521
522 have_seen_non_white_space_characters: bool,
525
526 inside_white_space: bool,
530
531 character_pending_to_return: Option<char>,
535}
536
537impl<InputIterator> WhitespaceCollapse<InputIterator> {
538 pub fn new(
539 char_iterator: InputIterator,
540 white_space_collapse: WhiteSpaceCollapse,
541 trim_beginning_white_space: bool,
542 ) -> Self {
543 Self {
544 char_iterator,
545 white_space_collapse,
546 remove_collapsible_white_space_at_start: trim_beginning_white_space,
547 inside_white_space: false,
548 following_newline: false,
549 have_seen_non_white_space_characters: false,
550 character_pending_to_return: None,
551 }
552 }
553
554 fn is_leading_trimmed_white_space(&self) -> bool {
555 !self.have_seen_non_white_space_characters && self.remove_collapsible_white_space_at_start
556 }
557
558 fn need_to_produce_space_character_after_white_space(&self) -> bool {
563 self.inside_white_space && !self.following_newline && !self.is_leading_trimmed_white_space()
564 }
565}
566
567impl<InputIterator> Iterator for WhitespaceCollapse<InputIterator>
568where
569 InputIterator: Iterator<Item = char>,
570{
571 type Item = char;
572
573 fn next(&mut self) -> Option<Self::Item> {
574 if self.white_space_collapse == WhiteSpaceCollapse::Preserve ||
580 self.white_space_collapse == WhiteSpaceCollapse::BreakSpaces
581 {
582 return match self.char_iterator.next() {
587 Some('\r') => Some(' '),
588 next => next,
589 };
590 }
591
592 if let Some(character) = self.character_pending_to_return.take() {
593 self.inside_white_space = false;
594 self.have_seen_non_white_space_characters = true;
595 self.following_newline = false;
596 return Some(character);
597 }
598
599 while let Some(character) = self.char_iterator.next() {
600 if character.is_ascii_whitespace() && character != '\n' {
604 self.inside_white_space = true;
605 continue;
606 }
607
608 if character == '\n' {
612 if self.white_space_collapse != WhiteSpaceCollapse::Collapse {
618 self.inside_white_space = false;
619 self.following_newline = true;
620 return Some(character);
621
622 } else if !self.following_newline &&
628 preserve_segment_break() &&
629 !self.is_leading_trimmed_white_space()
630 {
631 self.inside_white_space = false;
632 self.following_newline = true;
633 return Some(' ');
634 } else {
635 self.following_newline = true;
636 continue;
637 }
638 }
639
640 if self.need_to_produce_space_character_after_white_space() {
649 self.inside_white_space = false;
650 self.character_pending_to_return = Some(character);
651 return Some(' ');
652 }
653
654 self.inside_white_space = false;
655 self.have_seen_non_white_space_characters = true;
656 self.following_newline = false;
657 return Some(character);
658 }
659
660 if self.need_to_produce_space_character_after_white_space() {
661 self.inside_white_space = false;
662 return Some(' ');
663 }
664
665 None
666 }
667
668 fn size_hint(&self) -> (usize, Option<usize>) {
669 self.char_iterator.size_hint()
670 }
671
672 fn count(self) -> usize
673 where
674 Self: Sized,
675 {
676 self.char_iterator.count()
677 }
678}
679
680enum PendingCaseConversionResult {
681 Uppercase(ToUppercase),
682 Lowercase(ToLowercase),
683}
684
685impl PendingCaseConversionResult {
686 fn next(&mut self) -> Option<char> {
687 match self {
688 PendingCaseConversionResult::Uppercase(to_uppercase) => to_uppercase.next(),
689 PendingCaseConversionResult::Lowercase(to_lowercase) => to_lowercase.next(),
690 }
691 }
692}
693
694pub struct TextTransformation<InputIterator> {
699 char_iterator: InputIterator,
701 text_transform: TextTransformCase,
703 pending_case_conversion_result: Option<PendingCaseConversionResult>,
706}
707
708impl<InputIterator> TextTransformation<InputIterator> {
709 pub fn new(char_iterator: InputIterator, text_transform: TextTransformCase) -> Self {
710 Self {
711 char_iterator,
712 text_transform,
713 pending_case_conversion_result: None,
714 }
715 }
716}
717
718impl<InputIterator> Iterator for TextTransformation<InputIterator>
719where
720 InputIterator: Iterator<Item = char>,
721{
722 type Item = char;
723
724 fn next(&mut self) -> Option<Self::Item> {
725 if let Some(character) = self
726 .pending_case_conversion_result
727 .as_mut()
728 .and_then(|result| result.next())
729 {
730 return Some(character);
731 }
732 self.pending_case_conversion_result = None;
733
734 for character in self.char_iterator.by_ref() {
735 match self.text_transform {
736 TextTransformCase::None => return Some(character),
737 TextTransformCase::Uppercase => {
738 let mut pending_result =
739 PendingCaseConversionResult::Uppercase(character.to_uppercase());
740 if let Some(character) = pending_result.next() {
741 self.pending_case_conversion_result = Some(pending_result);
742 return Some(character);
743 }
744 },
745 TextTransformCase::Lowercase => {
746 let mut pending_result =
747 PendingCaseConversionResult::Lowercase(character.to_lowercase());
748 if let Some(character) = pending_result.next() {
749 self.pending_case_conversion_result = Some(pending_result);
750 return Some(character);
751 }
752 },
753 TextTransformCase::Capitalize => return Some(character),
756 }
757 }
758 None
759 }
760}
761
762pub(crate) fn capitalize_string(string: &str, allow_word_at_start: bool) -> String {
765 let mut output_string = String::new();
766 output_string.reserve(string.len());
767
768 let word_segmenter = WordSegmenter::new_auto();
769 let mut bounds = word_segmenter.segment_str(string).peekable();
770 let mut byte_index = 0;
771 for character in string.chars() {
772 let current_byte_index = byte_index;
773 byte_index += character.len_utf8();
774
775 if let Some(next_index) = bounds.peek() {
776 if *next_index == current_byte_index {
777 bounds.next();
778
779 if current_byte_index != 0 || allow_word_at_start {
780 output_string.extend(character.to_uppercase());
781 continue;
782 }
783 }
784 }
785
786 output_string.push(character);
787 }
788
789 output_string
790}