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