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 old_layout_box: Option<LayoutBox>,
222 ) {
223 if let Some(LayoutBox::InlineLevel(inline_level_box)) = old_layout_box {
225 let old_block_in_inline_splits: Vec<ArcRefCell<InlineBox>> = inline_level_box
226 .iter()
227 .rev() .filter_map(|inline_item| match &*inline_item.borrow() {
229 InlineItem::StartInlineBox(inline_box) => Some(inline_box.clone()),
230 _ => None,
231 })
232 .collect();
233
234 debug_assert!(
235 old_block_in_inline_splits.is_empty() ||
236 old_block_in_inline_splits.len() == inline_level_box.len(),
237 "Create inline box with incompatible `old_layout_box`"
238 );
239
240 self.start_inline_box_internal(inline_box_creator, None, old_block_in_inline_splits);
241 } else {
242 self.start_inline_box_internal(inline_box_creator, None, vec![]);
243 }
244 }
245
246 fn start_inline_box_internal(
247 &mut self,
248 inline_box_creator: impl FnOnce() -> ArcRefCell<InlineBox>,
249 block_in_inline_splits: Option<Vec<ArcRefCell<InlineItem>>>,
250 mut old_block_in_inline_splits: Vec<ArcRefCell<InlineBox>>,
251 ) {
252 let inline_box = old_block_in_inline_splits
253 .pop()
254 .unwrap_or_else(inline_box_creator);
255
256 let borrowed_inline_box = inline_box.borrow();
257 self.push_control_character_string(borrowed_inline_box.base.style.bidi_control_chars().0);
258
259 if borrowed_inline_box.is_first_split {
263 self.shared_inline_styles_stack
264 .push(borrowed_inline_box.shared_inline_styles.clone());
265 }
266 std::mem::drop(borrowed_inline_box);
267
268 let identifier = self.inline_boxes.start_inline_box(inline_box.clone());
269 let inline_level_box = ArcRefCell::new(InlineItem::StartInlineBox(inline_box));
270 self.inline_items.push(inline_level_box.clone());
271 self.inline_box_stack.push(identifier);
272 self.is_empty = false;
273
274 let mut block_in_inline_splits = block_in_inline_splits.unwrap_or_default();
275 block_in_inline_splits.push(inline_level_box);
276 self.block_in_inline_splits.push(block_in_inline_splits);
277
278 self.old_block_in_inline_splits
279 .push(old_block_in_inline_splits);
280 }
281
282 pub(crate) fn end_inline_box(&mut self) -> Vec<ArcRefCell<InlineItem>> {
287 self.shared_inline_styles_stack.pop();
288
289 let (identifier, block_in_inline_splits) = self.end_inline_box_internal();
290 let inline_level_box = self.inline_boxes.get(&identifier);
291 {
292 let mut inline_level_box = inline_level_box.borrow_mut();
293 inline_level_box.is_last_split = true;
294 self.push_control_character_string(inline_level_box.base.style.bidi_control_chars().1);
295 }
296
297 debug_assert!(
298 self.old_block_in_inline_splits
299 .last()
300 .is_some_and(|inline_boxes| inline_boxes.is_empty()),
301 "Reuse incompatible `old_block_in_inline_splits` for inline boxes",
302 );
303 let _ = self.old_block_in_inline_splits.pop();
304
305 block_in_inline_splits.unwrap_or_default()
306 }
307
308 fn end_inline_box_internal(
309 &mut self,
310 ) -> (InlineBoxIdentifier, Option<Vec<ArcRefCell<InlineItem>>>) {
311 let identifier = self
312 .inline_box_stack
313 .pop()
314 .expect("Ended non-existent inline box");
315 self.inline_items
316 .push(ArcRefCell::new(InlineItem::EndInlineBox));
317 self.is_empty = false;
318
319 self.inline_boxes.end_inline_box(identifier);
320
321 let block_in_inline_splits = self.block_in_inline_splits.pop();
324
325 (identifier, block_in_inline_splits)
326 }
327
328 pub(crate) fn push_text<'dom>(&mut self, text: Cow<'dom, str>, info: &NodeAndStyleInfo<'dom>) {
329 let white_space_collapse = info.style.clone_white_space_collapse();
330 let collapsed = WhitespaceCollapse::new(
331 text.chars(),
332 white_space_collapse,
333 self.last_inline_box_ended_with_collapsible_white_space,
334 );
335
336 let text_transform = info.style.clone_text_transform().case();
339 let capitalized_text: String;
340 let char_iterator: Box<dyn Iterator<Item = char>> = match text_transform {
341 TextTransformCase::None => Box::new(collapsed),
342 TextTransformCase::Capitalize => {
343 let collapsed_string: String = collapsed.collect();
350 capitalized_text = capitalize_string(&collapsed_string, self.on_word_boundary);
351 Box::new(capitalized_text.chars())
352 },
353 _ => {
354 Box::new(TextTransformation::new(collapsed, text_transform))
357 },
358 };
359
360 let white_space_collapse = info.style.clone_white_space_collapse();
361 let new_text: String = char_iterator
362 .inspect(|&character| {
363 self.is_empty = self.is_empty &&
364 match white_space_collapse {
365 WhiteSpaceCollapse::Collapse => character.is_ascii_whitespace(),
366 WhiteSpaceCollapse::PreserveBreaks => {
367 character.is_ascii_whitespace() && character != '\n'
368 },
369 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces => false,
370 };
371 })
372 .collect();
373
374 if new_text.is_empty() {
375 return;
376 }
377
378 let selection_range = info.get_selection_range();
379 if let Some(last_character) = new_text.chars().next_back() {
380 self.on_word_boundary = last_character.is_whitespace();
381 self.last_inline_box_ended_with_collapsible_white_space =
382 self.on_word_boundary && white_space_collapse != WhiteSpaceCollapse::Preserve;
383 }
384
385 let new_range = self.current_text_offset..self.current_text_offset + new_text.len();
386 self.current_text_offset = new_range.end;
387 self.text_segments.push(new_text);
388
389 if let Some(inline_item) = self.inline_items.last() {
390 if let InlineItem::TextRun(text_run) = &mut *inline_item.borrow_mut() {
391 text_run.borrow_mut().text_range.end = new_range.end;
392 return;
393 }
394 }
395
396 self.inline_items
397 .push(ArcRefCell::new(InlineItem::TextRun(ArcRefCell::new(
398 TextRun::new(
399 info.into(),
400 self.shared_inline_styles(),
401 new_range,
402 selection_range,
403 ),
404 ))));
405 }
406
407 pub(crate) fn enter_display_contents(&mut self, shared_inline_styles: SharedInlineStyles) {
408 self.shared_inline_styles_stack.push(shared_inline_styles);
409 }
410
411 pub(crate) fn leave_display_contents(&mut self) {
412 self.shared_inline_styles_stack.pop();
413 }
414
415 pub(crate) fn split_around_block_and_finish(
416 &mut self,
417 layout_context: &LayoutContext,
418 has_first_formatted_line: bool,
419 default_bidi_level: Level,
420 ) -> Option<InlineFormattingContext> {
421 if self.is_empty {
422 return None;
423 }
424
425 let mut new_builder = Self::new_for_shared_styles(self.shared_inline_styles_stack.clone());
430
431 let block_in_inline_splits = std::mem::take(&mut self.block_in_inline_splits);
432 let old_block_in_inline_splits = std::mem::take(&mut self.old_block_in_inline_splits);
433 for (identifier, already_collected_inline_boxes, being_recollected_inline_boxes) in izip!(
434 self.inline_box_stack.iter(),
435 block_in_inline_splits,
436 old_block_in_inline_splits
437 ) {
438 let split_inline_box_callback = || {
446 ArcRefCell::new(
447 self.inline_boxes
448 .get(identifier)
449 .borrow()
450 .split_around_block(),
451 )
452 };
453 new_builder.start_inline_box_internal(
454 split_inline_box_callback,
455 Some(already_collected_inline_boxes),
456 being_recollected_inline_boxes,
457 );
458 }
459 let mut inline_builder_from_before_split = std::mem::replace(self, new_builder);
460
461 while !inline_builder_from_before_split.inline_box_stack.is_empty() {
465 inline_builder_from_before_split.end_inline_box_internal();
466 }
467
468 inline_builder_from_before_split.finish(
469 layout_context,
470 has_first_formatted_line,
471 false,
472 default_bidi_level,
473 )
474 }
475
476 pub(crate) fn finish(
478 self,
479 layout_context: &LayoutContext,
480 has_first_formatted_line: bool,
481 is_single_line_text_input: bool,
482 default_bidi_level: Level,
483 ) -> Option<InlineFormattingContext> {
484 if self.is_empty {
485 return None;
486 }
487
488 assert!(self.inline_box_stack.is_empty());
489 debug_assert!(self.old_block_in_inline_splits.is_empty());
490 Some(InlineFormattingContext::new_with_builder(
491 self,
492 layout_context,
493 has_first_formatted_line,
494 is_single_line_text_input,
495 default_bidi_level,
496 ))
497 }
498}
499
500fn preserve_segment_break() -> bool {
501 true
502}
503
504pub struct WhitespaceCollapse<InputIterator> {
505 char_iterator: InputIterator,
506 white_space_collapse: WhiteSpaceCollapse,
507
508 remove_collapsible_white_space_at_start: bool,
512
513 following_newline: bool,
516
517 have_seen_non_white_space_characters: bool,
520
521 inside_white_space: bool,
525
526 character_pending_to_return: Option<char>,
530}
531
532impl<InputIterator> WhitespaceCollapse<InputIterator> {
533 pub fn new(
534 char_iterator: InputIterator,
535 white_space_collapse: WhiteSpaceCollapse,
536 trim_beginning_white_space: bool,
537 ) -> Self {
538 Self {
539 char_iterator,
540 white_space_collapse,
541 remove_collapsible_white_space_at_start: trim_beginning_white_space,
542 inside_white_space: false,
543 following_newline: false,
544 have_seen_non_white_space_characters: false,
545 character_pending_to_return: None,
546 }
547 }
548
549 fn is_leading_trimmed_white_space(&self) -> bool {
550 !self.have_seen_non_white_space_characters && self.remove_collapsible_white_space_at_start
551 }
552
553 fn need_to_produce_space_character_after_white_space(&self) -> bool {
558 self.inside_white_space && !self.following_newline && !self.is_leading_trimmed_white_space()
559 }
560}
561
562impl<InputIterator> Iterator for WhitespaceCollapse<InputIterator>
563where
564 InputIterator: Iterator<Item = char>,
565{
566 type Item = char;
567
568 fn next(&mut self) -> Option<Self::Item> {
569 if self.white_space_collapse == WhiteSpaceCollapse::Preserve ||
575 self.white_space_collapse == WhiteSpaceCollapse::BreakSpaces
576 {
577 return match self.char_iterator.next() {
582 Some('\r') => Some(' '),
583 next => next,
584 };
585 }
586
587 if let Some(character) = self.character_pending_to_return.take() {
588 self.inside_white_space = false;
589 self.have_seen_non_white_space_characters = true;
590 self.following_newline = false;
591 return Some(character);
592 }
593
594 while let Some(character) = self.char_iterator.next() {
595 if character.is_ascii_whitespace() && character != '\n' {
599 self.inside_white_space = true;
600 continue;
601 }
602
603 if character == '\n' {
607 if self.white_space_collapse != WhiteSpaceCollapse::Collapse {
613 self.inside_white_space = false;
614 self.following_newline = true;
615 return Some(character);
616
617 } else if !self.following_newline &&
623 preserve_segment_break() &&
624 !self.is_leading_trimmed_white_space()
625 {
626 self.inside_white_space = false;
627 self.following_newline = true;
628 return Some(' ');
629 } else {
630 self.following_newline = true;
631 continue;
632 }
633 }
634
635 if self.need_to_produce_space_character_after_white_space() {
644 self.inside_white_space = false;
645 self.character_pending_to_return = Some(character);
646 return Some(' ');
647 }
648
649 self.inside_white_space = false;
650 self.have_seen_non_white_space_characters = true;
651 self.following_newline = false;
652 return Some(character);
653 }
654
655 if self.need_to_produce_space_character_after_white_space() {
656 self.inside_white_space = false;
657 return Some(' ');
658 }
659
660 None
661 }
662
663 fn size_hint(&self) -> (usize, Option<usize>) {
664 self.char_iterator.size_hint()
665 }
666
667 fn count(self) -> usize
668 where
669 Self: Sized,
670 {
671 self.char_iterator.count()
672 }
673}
674
675enum PendingCaseConversionResult {
676 Uppercase(ToUppercase),
677 Lowercase(ToLowercase),
678}
679
680impl PendingCaseConversionResult {
681 fn next(&mut self) -> Option<char> {
682 match self {
683 PendingCaseConversionResult::Uppercase(to_uppercase) => to_uppercase.next(),
684 PendingCaseConversionResult::Lowercase(to_lowercase) => to_lowercase.next(),
685 }
686 }
687}
688
689pub struct TextTransformation<InputIterator> {
694 char_iterator: InputIterator,
696 text_transform: TextTransformCase,
698 pending_case_conversion_result: Option<PendingCaseConversionResult>,
701}
702
703impl<InputIterator> TextTransformation<InputIterator> {
704 pub fn new(char_iterator: InputIterator, text_transform: TextTransformCase) -> Self {
705 Self {
706 char_iterator,
707 text_transform,
708 pending_case_conversion_result: None,
709 }
710 }
711}
712
713impl<InputIterator> Iterator for TextTransformation<InputIterator>
714where
715 InputIterator: Iterator<Item = char>,
716{
717 type Item = char;
718
719 fn next(&mut self) -> Option<Self::Item> {
720 if let Some(character) = self
721 .pending_case_conversion_result
722 .as_mut()
723 .and_then(|result| result.next())
724 {
725 return Some(character);
726 }
727 self.pending_case_conversion_result = None;
728
729 for character in self.char_iterator.by_ref() {
730 match self.text_transform {
731 TextTransformCase::None => return Some(character),
732 TextTransformCase::Uppercase => {
733 let mut pending_result =
734 PendingCaseConversionResult::Uppercase(character.to_uppercase());
735 if let Some(character) = pending_result.next() {
736 self.pending_case_conversion_result = Some(pending_result);
737 return Some(character);
738 }
739 },
740 TextTransformCase::Lowercase => {
741 let mut pending_result =
742 PendingCaseConversionResult::Lowercase(character.to_lowercase());
743 if let Some(character) = pending_result.next() {
744 self.pending_case_conversion_result = Some(pending_result);
745 return Some(character);
746 }
747 },
748 TextTransformCase::Capitalize => return Some(character),
751 }
752 }
753 None
754 }
755}
756
757pub(crate) fn capitalize_string(string: &str, allow_word_at_start: bool) -> String {
760 let mut output_string = String::new();
761 output_string.reserve(string.len());
762
763 let word_segmenter = WordSegmenter::new_auto();
764 let mut bounds = word_segmenter.segment_str(string).peekable();
765 let mut byte_index = 0;
766 for character in string.chars() {
767 let current_byte_index = byte_index;
768 byte_index += character.len_utf8();
769
770 if let Some(next_index) = bounds.peek() {
771 if *next_index == current_byte_index {
772 bounds.next();
773
774 if current_byte_index != 0 || allow_word_at_start {
775 output_string.extend(character.to_uppercase());
776 continue;
777 }
778 }
779 }
780
781 output_string.push(character);
782 }
783
784 output_string
785}