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::_webkit_text_security::T as WebKitTextSecurity;
11use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
12use style::selector_parser::PseudoElement;
13use style::values::specified::text::TextTransformCase;
14use unicode_bidi::Level;
15
16use super::text_run::TextRun;
17use super::{
18 InlineBox, InlineBoxIdentifier, InlineBoxes, InlineFormattingContext, InlineItem,
19 SharedInlineStyles,
20};
21use crate::cell::ArcRefCell;
22use crate::context::LayoutContext;
23use crate::dom::LayoutBox;
24use crate::dom_traversal::NodeAndStyleInfo;
25use crate::flow::float::FloatBox;
26use crate::flow::inline::AnonymousBlockBox;
27use crate::flow::{BlockContainer, BlockLevelBox};
28use crate::formatting_contexts::IndependentFormattingContext;
29use crate::layout_box_base::LayoutBoxBase;
30use crate::positioned::AbsolutelyPositionedBox;
31use crate::style_ext::ComputedValuesExt;
32
33#[derive(Default)]
34pub(crate) struct InlineFormattingContextBuilder {
35 pub shared_inline_styles_stack: Vec<SharedInlineStyles>,
40
41 pub text_segments: Vec<String>,
44
45 current_text_offset: usize,
48
49 current_character_offset: usize,
53
54 pub shared_selection: Option<SharedSelection>,
57
58 last_inline_box_ended_with_collapsible_white_space: bool,
66
67 on_word_boundary: bool,
70
71 pub contains_floats: bool,
73
74 pub inline_items: Vec<InlineItem>,
78
79 pub inline_boxes: InlineBoxes,
81
82 inline_box_stack: Vec<InlineBoxIdentifier>,
91
92 pub is_empty: bool,
96}
97
98impl InlineFormattingContextBuilder {
99 pub(crate) fn new(info: &NodeAndStyleInfo, context: &LayoutContext) -> Self {
100 Self {
101 on_word_boundary: true,
103 is_empty: true,
104 shared_inline_styles_stack: vec![SharedInlineStyles::from_info_and_context(
105 info, context,
106 )],
107 shared_selection: info.node.selection(),
108 ..Default::default()
109 }
110 }
111
112 pub(crate) fn currently_processing_inline_box(&self) -> bool {
113 !self.inline_box_stack.is_empty()
114 }
115
116 fn push_control_character_string(&mut self, string_to_push: &str) {
117 self.text_segments.push(string_to_push.to_owned());
118 self.current_text_offset += string_to_push.len();
119 self.current_character_offset += string_to_push.chars().count();
120 }
121
122 fn shared_inline_styles(&self) -> SharedInlineStyles {
123 self.shared_inline_styles_stack
124 .last()
125 .expect("Should always have at least one SharedInlineStyles")
126 .clone()
127 }
128
129 pub(crate) fn push_atomic(
130 &mut self,
131 independent_formatting_context_creator: impl FnOnce()
132 -> ArcRefCell<IndependentFormattingContext>,
133 old_layout_box: Option<LayoutBox>,
134 ) -> InlineItem {
135 let independent_formatting_context = old_layout_box
137 .and_then(|layout_box| match layout_box {
138 LayoutBox::InlineLevel(InlineItem::Atomic(atomic, ..)) => Some(atomic.clone()),
139 _ => None,
140 })
141 .unwrap_or_else(independent_formatting_context_creator);
142
143 let inline_level_box = InlineItem::Atomic(
144 independent_formatting_context,
145 self.current_text_offset,
146 Level::ltr(), );
148 self.inline_items.push(inline_level_box.clone());
149 self.is_empty = false;
150
151 self.push_control_character_string("\u{fffc}");
154
155 self.last_inline_box_ended_with_collapsible_white_space = false;
156 self.on_word_boundary = true;
157
158 inline_level_box
159 }
160
161 pub(crate) fn push_absolutely_positioned_box(
162 &mut self,
163 absolutely_positioned_box_creator: impl FnOnce() -> ArcRefCell<AbsolutelyPositionedBox>,
164 old_layout_box: Option<LayoutBox>,
165 ) -> InlineItem {
166 let absolutely_positioned_box = old_layout_box
167 .and_then(|layout_box| match layout_box {
168 LayoutBox::InlineLevel(InlineItem::OutOfFlowAbsolutelyPositionedBox(
169 positioned_box,
170 ..,
171 )) => Some(positioned_box.clone()),
172 _ => None,
173 })
174 .unwrap_or_else(absolutely_positioned_box_creator);
175
176 let inline_level_box = InlineItem::OutOfFlowAbsolutelyPositionedBox(
178 absolutely_positioned_box,
179 self.current_text_offset,
180 );
181
182 self.inline_items.push(inline_level_box.clone());
183 self.is_empty = false;
184 inline_level_box
185 }
186
187 pub(crate) fn push_float_box(
188 &mut self,
189 float_box_creator: impl FnOnce() -> ArcRefCell<FloatBox>,
190 old_layout_box: Option<LayoutBox>,
191 ) -> InlineItem {
192 let inline_level_box = old_layout_box
193 .and_then(|layout_box| match layout_box {
194 LayoutBox::InlineLevel(inline_item) => Some(inline_item),
195 _ => None,
196 })
197 .unwrap_or_else(|| InlineItem::OutOfFlowFloatBox(float_box_creator()));
198
199 debug_assert!(
200 matches!(inline_level_box, InlineItem::OutOfFlowFloatBox(..),),
201 "Created float box with incompatible `old_layout_box`"
202 );
203
204 self.inline_items.push(inline_level_box.clone());
205 self.is_empty = false;
206 self.contains_floats = true;
207 inline_level_box
208 }
209
210 pub(crate) fn push_block_level_box(
211 &mut self,
212 block_level_box: ArcRefCell<BlockLevelBox>,
213 block_builder_info: &NodeAndStyleInfo,
214 layout_context: &LayoutContext,
215 ) {
216 assert!(self.currently_processing_inline_box());
217 self.contains_floats = self.contains_floats || block_level_box.borrow().contains_floats();
218
219 if let Some(InlineItem::AnonymousBlock(anonymous_block)) = self.inline_items.last() {
220 if let BlockContainer::BlockLevelBoxes(ref mut block_level_boxes) =
221 anonymous_block.borrow_mut().contents
222 {
223 block_level_boxes.push(block_level_box);
224 return;
225 }
226 }
227 let info = &block_builder_info
228 .with_pseudo_element(layout_context, PseudoElement::ServoAnonymousBox)
229 .expect("Should never fail to create anonymous box");
230 self.inline_items
231 .push(InlineItem::AnonymousBlock(ArcRefCell::new(
232 AnonymousBlockBox {
233 base: LayoutBoxBase::new(info.into(), info.style.clone()),
234 contents: BlockContainer::BlockLevelBoxes(vec![block_level_box]),
235 },
236 )));
237 }
238
239 pub(crate) fn start_inline_box(
240 &mut self,
241 inline_box_creator: impl FnOnce() -> ArcRefCell<InlineBox>,
242 old_layout_box: Option<LayoutBox>,
243 ) {
244 let inline_box = old_layout_box
246 .and_then(|layout_box| match layout_box {
247 LayoutBox::InlineLevel(InlineItem::StartInlineBox(inline_box)) => Some(inline_box),
248 _ => None,
249 })
250 .unwrap_or_else(inline_box_creator);
251
252 let borrowed_inline_box = inline_box.borrow();
253 self.push_control_character_string(borrowed_inline_box.base.style.bidi_control_chars().0);
254
255 self.shared_inline_styles_stack
256 .push(borrowed_inline_box.shared_inline_styles.clone());
257 std::mem::drop(borrowed_inline_box);
258
259 let identifier = self.inline_boxes.start_inline_box(inline_box.clone());
260 self.inline_items
261 .push(InlineItem::StartInlineBox(inline_box));
262 self.inline_box_stack.push(identifier);
263 self.is_empty = false;
264 }
265
266 pub(crate) fn end_inline_box(&mut self) {
271 self.shared_inline_styles_stack.pop();
272 self.inline_items.push(InlineItem::EndInlineBox);
273 let identifier = self
274 .inline_box_stack
275 .pop()
276 .expect("Ended non-existent inline box");
277 self.inline_boxes.end_inline_box(identifier);
278 let inline_level_box = self.inline_boxes.get(&identifier);
279 let bidi_control_chars = inline_level_box.borrow().base.style.bidi_control_chars();
280 self.push_control_character_string(bidi_control_chars.1);
281 }
282
283 pub(crate) fn push_text<'dom>(&mut self, text: Cow<'dom, str>, info: &NodeAndStyleInfo<'dom>) {
284 let white_space_collapse = info.style.clone_white_space_collapse();
285 let collapsed = WhitespaceCollapse::new(
286 text.chars(),
287 white_space_collapse,
288 self.last_inline_box_ended_with_collapsible_white_space,
289 );
290
291 let text_transform = info.style.clone_text_transform().case();
294 let capitalized_text: String;
295 let char_iterator: Box<dyn Iterator<Item = char>> = match text_transform {
296 TextTransformCase::None => Box::new(collapsed),
297 TextTransformCase::Capitalize => {
298 let collapsed_string: String = collapsed.collect();
305 capitalized_text = capitalize_string(&collapsed_string, self.on_word_boundary);
306 Box::new(capitalized_text.chars())
307 },
308 _ => {
309 Box::new(TextTransformation::new(collapsed, text_transform))
312 },
313 };
314
315 let char_iterator = if info.style.clone__webkit_text_security() != WebKitTextSecurity::None
316 {
317 Box::new(TextSecurityTransform::new(
318 char_iterator,
319 info.style.clone__webkit_text_security(),
320 ))
321 } else {
322 char_iterator
323 };
324
325 let white_space_collapse = info.style.clone_white_space_collapse();
326 let mut character_count = 0;
327 let new_text: String = char_iterator
328 .inspect(|&character| {
329 character_count += 1;
330
331 self.is_empty = self.is_empty &&
332 match white_space_collapse {
333 WhiteSpaceCollapse::Collapse => character.is_ascii_whitespace(),
334 WhiteSpaceCollapse::PreserveBreaks => {
335 character.is_ascii_whitespace() && character != '\n'
336 },
337 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces => false,
338 };
339 })
340 .collect();
341
342 if new_text.is_empty() {
343 return;
344 }
345
346 if let Some(last_character) = new_text.chars().next_back() {
347 self.on_word_boundary = last_character.is_whitespace();
348 self.last_inline_box_ended_with_collapsible_white_space =
349 self.on_word_boundary && white_space_collapse != WhiteSpaceCollapse::Preserve;
350 }
351
352 let new_range = self.current_text_offset..self.current_text_offset + new_text.len();
353 self.current_text_offset = new_range.end;
354
355 let new_character_range =
356 self.current_character_offset..self.current_character_offset + character_count;
357 self.current_character_offset = new_character_range.end;
358
359 self.text_segments.push(new_text);
360
361 let current_inline_styles = self.shared_inline_styles();
362
363 if let Some(InlineItem::TextRun(text_run)) = self.inline_items.last() {
364 if text_run
365 .borrow()
366 .inline_styles
367 .ptr_eq(¤t_inline_styles)
368 {
369 text_run.borrow_mut().text_range.end = new_range.end;
370 text_run.borrow_mut().character_range.end = new_character_range.end;
371 return;
372 }
373 }
374
375 self.inline_items
376 .push(InlineItem::TextRun(ArcRefCell::new(TextRun::new(
377 info.into(),
378 current_inline_styles,
379 new_range,
380 new_character_range,
381 ))));
382 }
383
384 pub(crate) fn enter_display_contents(&mut self, shared_inline_styles: SharedInlineStyles) {
385 self.shared_inline_styles_stack.push(shared_inline_styles);
386 }
387
388 pub(crate) fn leave_display_contents(&mut self) {
389 self.shared_inline_styles_stack.pop();
390 }
391
392 pub(crate) fn finish(
394 self,
395 layout_context: &LayoutContext,
396 has_first_formatted_line: bool,
397 is_single_line_text_input: bool,
398 default_bidi_level: Level,
399 ) -> Option<InlineFormattingContext> {
400 if self.is_empty {
401 return None;
402 }
403
404 assert!(self.inline_box_stack.is_empty());
405 Some(InlineFormattingContext::new_with_builder(
406 self,
407 layout_context,
408 has_first_formatted_line,
409 is_single_line_text_input,
410 default_bidi_level,
411 ))
412 }
413}
414
415fn preserve_segment_break() -> bool {
416 true
417}
418
419pub struct WhitespaceCollapse<InputIterator> {
420 char_iterator: InputIterator,
421 white_space_collapse: WhiteSpaceCollapse,
422
423 remove_collapsible_white_space_at_start: bool,
427
428 following_newline: bool,
431
432 have_seen_non_white_space_characters: bool,
435
436 inside_white_space: bool,
440
441 character_pending_to_return: Option<char>,
445}
446
447impl<InputIterator> WhitespaceCollapse<InputIterator> {
448 pub fn new(
449 char_iterator: InputIterator,
450 white_space_collapse: WhiteSpaceCollapse,
451 trim_beginning_white_space: bool,
452 ) -> Self {
453 Self {
454 char_iterator,
455 white_space_collapse,
456 remove_collapsible_white_space_at_start: trim_beginning_white_space,
457 inside_white_space: false,
458 following_newline: false,
459 have_seen_non_white_space_characters: false,
460 character_pending_to_return: None,
461 }
462 }
463
464 fn is_leading_trimmed_white_space(&self) -> bool {
465 !self.have_seen_non_white_space_characters && self.remove_collapsible_white_space_at_start
466 }
467
468 fn need_to_produce_space_character_after_white_space(&self) -> bool {
473 self.inside_white_space && !self.following_newline && !self.is_leading_trimmed_white_space()
474 }
475}
476
477impl<InputIterator> Iterator for WhitespaceCollapse<InputIterator>
478where
479 InputIterator: Iterator<Item = char>,
480{
481 type Item = char;
482
483 fn next(&mut self) -> Option<Self::Item> {
484 if self.white_space_collapse == WhiteSpaceCollapse::Preserve ||
490 self.white_space_collapse == WhiteSpaceCollapse::BreakSpaces
491 {
492 return match self.char_iterator.next() {
497 Some('\r') => Some(' '),
498 next => next,
499 };
500 }
501
502 if let Some(character) = self.character_pending_to_return.take() {
503 self.inside_white_space = false;
504 self.have_seen_non_white_space_characters = true;
505 self.following_newline = false;
506 return Some(character);
507 }
508
509 while let Some(character) = self.char_iterator.next() {
510 if character.is_ascii_whitespace() && character != '\n' {
514 self.inside_white_space = true;
515 continue;
516 }
517
518 if character == '\n' {
522 if self.white_space_collapse != WhiteSpaceCollapse::Collapse {
528 self.inside_white_space = false;
529 self.following_newline = true;
530 return Some(character);
531
532 } else if !self.following_newline &&
538 preserve_segment_break() &&
539 !self.is_leading_trimmed_white_space()
540 {
541 self.inside_white_space = false;
542 self.following_newline = true;
543 return Some(' ');
544 } else {
545 self.following_newline = true;
546 continue;
547 }
548 }
549
550 if self.need_to_produce_space_character_after_white_space() {
559 self.inside_white_space = false;
560 self.character_pending_to_return = Some(character);
561 return Some(' ');
562 }
563
564 self.inside_white_space = false;
565 self.have_seen_non_white_space_characters = true;
566 self.following_newline = false;
567 return Some(character);
568 }
569
570 if self.need_to_produce_space_character_after_white_space() {
571 self.inside_white_space = false;
572 return Some(' ');
573 }
574
575 None
576 }
577
578 fn size_hint(&self) -> (usize, Option<usize>) {
579 self.char_iterator.size_hint()
580 }
581
582 fn count(self) -> usize
583 where
584 Self: Sized,
585 {
586 self.char_iterator.count()
587 }
588}
589
590enum PendingCaseConversionResult {
591 Uppercase(ToUppercase),
592 Lowercase(ToLowercase),
593}
594
595impl PendingCaseConversionResult {
596 fn next(&mut self) -> Option<char> {
597 match self {
598 PendingCaseConversionResult::Uppercase(to_uppercase) => to_uppercase.next(),
599 PendingCaseConversionResult::Lowercase(to_lowercase) => to_lowercase.next(),
600 }
601 }
602}
603
604pub struct TextTransformation<InputIterator> {
609 char_iterator: InputIterator,
611 text_transform: TextTransformCase,
613 pending_case_conversion_result: Option<PendingCaseConversionResult>,
616}
617
618impl<InputIterator> TextTransformation<InputIterator> {
619 pub fn new(char_iterator: InputIterator, text_transform: TextTransformCase) -> Self {
620 Self {
621 char_iterator,
622 text_transform,
623 pending_case_conversion_result: None,
624 }
625 }
626}
627
628impl<InputIterator> Iterator for TextTransformation<InputIterator>
629where
630 InputIterator: Iterator<Item = char>,
631{
632 type Item = char;
633
634 fn next(&mut self) -> Option<Self::Item> {
635 if let Some(character) = self
636 .pending_case_conversion_result
637 .as_mut()
638 .and_then(|result| result.next())
639 {
640 return Some(character);
641 }
642 self.pending_case_conversion_result = None;
643
644 for character in self.char_iterator.by_ref() {
645 match self.text_transform {
646 TextTransformCase::None => return Some(character),
647 TextTransformCase::Uppercase => {
648 let mut pending_result =
649 PendingCaseConversionResult::Uppercase(character.to_uppercase());
650 if let Some(character) = pending_result.next() {
651 self.pending_case_conversion_result = Some(pending_result);
652 return Some(character);
653 }
654 },
655 TextTransformCase::Lowercase => {
656 let mut pending_result =
657 PendingCaseConversionResult::Lowercase(character.to_lowercase());
658 if let Some(character) = pending_result.next() {
659 self.pending_case_conversion_result = Some(pending_result);
660 return Some(character);
661 }
662 },
663 TextTransformCase::Capitalize => return Some(character),
666 }
667 }
668 None
669 }
670}
671
672pub struct TextSecurityTransform<InputIterator> {
673 char_iterator: InputIterator,
675 text_security: WebKitTextSecurity,
677}
678
679impl<InputIterator> TextSecurityTransform<InputIterator> {
680 pub fn new(char_iterator: InputIterator, text_security: WebKitTextSecurity) -> Self {
681 Self {
682 char_iterator,
683 text_security,
684 }
685 }
686}
687
688impl<InputIterator> Iterator for TextSecurityTransform<InputIterator>
689where
690 InputIterator: Iterator<Item = char>,
691{
692 type Item = char;
693
694 fn next(&mut self) -> Option<Self::Item> {
695 Some(match self.char_iterator.next()? {
699 '\u{200B}' => '\u{200B}',
703 '\n' => '\n',
705 character => match self.text_security {
706 WebKitTextSecurity::None => character,
707 WebKitTextSecurity::Circle => '○',
708 WebKitTextSecurity::Disc => '●',
709 WebKitTextSecurity::Square => '■',
710 },
711 })
712 }
713}
714
715pub(crate) fn capitalize_string(string: &str, allow_word_at_start: bool) -> String {
718 let mut output_string = String::new();
719 output_string.reserve(string.len());
720
721 let word_segmenter = WordSegmenter::new_auto();
722 let mut bounds = word_segmenter.segment_str(string).peekable();
723 let mut byte_index = 0;
724 for character in string.chars() {
725 let current_byte_index = byte_index;
726 byte_index += character.len_utf8();
727
728 if let Some(next_index) = bounds.peek() {
729 if *next_index == current_byte_index {
730 bounds.next();
731
732 if current_byte_index != 0 || allow_word_at_start {
733 output_string.extend(character.to_uppercase());
734 continue;
735 }
736 }
737 }
738
739 output_string.push(character);
740 }
741
742 output_string
743}