1use std::ops::Range;
6use std::sync::Arc;
7use std::sync::atomic::AtomicBool;
8
9use app_units::Au;
10use atomic_refcell::AtomicRef;
11use euclid::{Point2D, Rect, Size2D};
12use fonts::{FontMetrics, ShapedTextSlice};
13use layout_api::BoxAreaType;
14use malloc_size_of_derive::MallocSizeOf;
15use servo_arc::Arc as ServoArc;
16use servo_base::id::PipelineId;
17use servo_base::print_tree::PrintTree;
18use servo_base::text::Utf32CodeUnits;
19use servo_url::ServoUrl;
20use style::Zero;
21use style::properties::ComputedValues;
22use style_traits::CSSPixel;
23use webrender_api::{FontInstanceKey, ImageKey};
24
25use super::{
26 BaseFragment, BoxFragment, ContainingBlockManager, HoistedSharedFragment, PositioningFragment,
27 Tag,
28};
29use crate::SharedStyle;
30use crate::cell::{ArcRefCell, RefOrAtomicRef};
31use crate::flow::inline::text_run::SharedTextRunData;
32use crate::fragment_tree::FragmentStatus;
33use crate::geom::{LogicalSides, PhysicalPoint, PhysicalRect};
34use crate::layout_impl::LayoutThread;
35use crate::style_ext::ComputedValuesExt;
36
37#[derive(Clone, MallocSizeOf)]
38pub(crate) enum Fragment {
39 LayoutRoot(LayoutRootFragment),
40 Box(#[conditional_malloc_size_of] Arc<BoxFragment>),
41 Float(#[conditional_malloc_size_of] Arc<BoxFragment>),
47 Positioning(#[conditional_malloc_size_of] Arc<PositioningFragment>),
48 AbsoluteOrFixedPositionedPlaceholder(ArcRefCell<HoistedSharedFragment>),
54 Text(#[conditional_malloc_size_of] Arc<TextFragment>),
55 Image(#[conditional_malloc_size_of] Arc<ImageFragment>),
56 IFrame(#[conditional_malloc_size_of] Arc<IFrameFragment>),
57}
58
59#[derive(Clone, MallocSizeOf)]
60pub(crate) struct CollapsedBlockMargins {
61 pub collapsed_through: bool,
62 pub start: CollapsedMargin,
63 pub end: CollapsedMargin,
64}
65
66#[derive(Clone, Copy, Debug, MallocSizeOf)]
67pub(crate) struct CollapsedMargin {
68 max_positive: Au,
69 min_negative: Au,
70}
71
72#[derive(Clone, MallocSizeOf)]
73pub(crate) struct LayoutRootFragment {
74 pub fragment: ArcRefCell<HoistedSharedFragment>,
75}
76
77impl LayoutRootFragment {
78 pub(crate) fn inner(&self) -> AtomicRef<'_, Fragment> {
79 AtomicRef::map(self.fragment.borrow(), |fragment| {
80 fragment
81 .fragment
82 .as_ref()
83 .expect("Should never create LayoutRoot without a Fragment")
84 })
85 }
86
87 pub(crate) fn inner_box_fragment(&self) -> AtomicRef<'_, Arc<BoxFragment>> {
88 AtomicRef::map(self.inner(), |fragment| match fragment {
89 Fragment::Box(box_fragment) => box_fragment,
90 _ => unreachable!("Layout root should always contain box fragment"),
91 })
92 }
93}
94
95#[derive(MallocSizeOf)]
96pub(crate) struct TextFragment {
97 pub base: BaseFragment,
98 #[conditional_malloc_size_of]
99 pub run_data: Arc<SharedTextRunData>,
100 #[conditional_malloc_size_of]
101 pub font_metrics: Arc<FontMetrics>,
102 pub font_key: FontInstanceKey,
103 #[conditional_malloc_size_of]
104 pub glyphs: Vec<Arc<ShapedTextSlice>>,
105 pub justification_adjustment: Au,
107 pub character_range_in_dom_node: Range<Utf32CodeUnits>,
110 pub is_empty_for_text_cursor: bool,
113}
114
115#[derive(MallocSizeOf)]
116pub(crate) struct ImageFragment {
117 pub base: BaseFragment,
118 pub style: SharedStyle,
119 pub selected_style: SharedStyle,
122 pub clip: PhysicalRect<Au>,
123 pub image_key: Option<ImageKey>,
124 pub showing_broken_image_icon: bool,
125 pub url: Option<ServoUrl>,
126 pub natural_width: Option<Au>,
128 pub natural_height: Option<Au>,
130 #[conditional_malloc_size_of]
132 pub selected: Arc<AtomicBool>,
133}
134
135#[derive(MallocSizeOf)]
136pub(crate) struct IFrameFragment {
137 pub base: BaseFragment,
138 pub style: SharedStyle,
139 pub pipeline_id: PipelineId,
140}
141
142impl Fragment {
143 pub fn base(&self) -> Option<RefOrAtomicRef<'_, BaseFragment>> {
144 Some(match self {
145 Fragment::LayoutRoot(fragment) => RefOrAtomicRef::AtomicRef(AtomicRef::map(
146 fragment.inner_box_fragment(),
147 |box_fragment| &box_fragment.base,
148 )),
149 Fragment::Box(fragment) => RefOrAtomicRef::Ref(&fragment.base),
150 Fragment::Text(fragment) => RefOrAtomicRef::Ref(&fragment.base),
151 Fragment::AbsoluteOrFixedPositionedPlaceholder(_) => return None,
152 Fragment::Positioning(fragment) => RefOrAtomicRef::Ref(&fragment.base),
153 Fragment::Image(fragment) => RefOrAtomicRef::Ref(&fragment.base),
154 Fragment::IFrame(fragment) => RefOrAtomicRef::Ref(&fragment.base),
155 Fragment::Float(fragment) => RefOrAtomicRef::Ref(&fragment.base),
156 })
157 }
158
159 pub(crate) fn set_containing_block(&self, containing_block: &PhysicalRect<Au>) {
160 match self {
161 Fragment::LayoutRoot(layout_root_fragment) => layout_root_fragment
162 .inner()
163 .set_containing_block(containing_block),
164 Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
165 box_fragment.set_containing_block(containing_block)
166 },
167 Fragment::Positioning(positioning_fragment) => {
168 positioning_fragment.set_containing_block(containing_block)
169 },
170 Fragment::AbsoluteOrFixedPositionedPlaceholder(..) |
171 Fragment::Text(..) |
172 Fragment::Image(..) |
173 Fragment::IFrame(..) => {},
174 }
175 }
176
177 pub fn tag(&self) -> Option<Tag> {
178 self.base().and_then(|base| base.tag)
179 }
180
181 pub fn print(&self, tree: &mut PrintTree) {
182 match self {
183 Fragment::LayoutRoot(layout_root_fragment) => layout_root_fragment.inner().print(tree),
184 Fragment::Box(fragment) => fragment.print(tree),
185 Fragment::Float(fragment) => {
186 tree.new_level("Float".to_string());
187 fragment.print(tree);
188 tree.end_level();
189 },
190 Fragment::AbsoluteOrFixedPositionedPlaceholder(_) => {
191 tree.add_item("AbsoluteOrFixedPositioned".to_string());
192 },
193 Fragment::Positioning(fragment) => fragment.print(tree),
194 Fragment::Text(fragment) => fragment.print(tree),
195 Fragment::Image(fragment) => fragment.print(tree),
196 Fragment::IFrame(fragment) => fragment.print(tree),
197 }
198 }
199
200 pub(crate) fn scrolling_area(&self, layout_thread: &LayoutThread) -> PhysicalRect<Au> {
201 self.retrieve_box_fragment().map_or_else(
202 || self.scrollable_overflow_for_parent(),
203 |box_fragment| {
204 box_fragment.offset_by_containing_block(
205 &box_fragment.with_style().scrollable_overflow(),
206 layout_thread.into(),
207 )
208 },
209 )
210 }
211
212 pub(crate) fn clear_scrollable_overflow(&self) {
216 match self {
217 Fragment::LayoutRoot(fragment) => {
218 fragment.inner_box_fragment().clear_scrollable_overflow()
219 },
220 Fragment::Box(fragment) | Fragment::Float(fragment) => {
221 fragment.clear_scrollable_overflow()
222 },
223 Fragment::Positioning(fragment) => fragment.clear_scrollable_overflow(),
224 _ => {},
225 }
226 }
227
228 pub(crate) fn scrollable_overflow_for_parent(&self) -> PhysicalRect<Au> {
229 match self {
230 Fragment::LayoutRoot(layout_root) => {
231 layout_root.inner().scrollable_overflow_for_parent()
232 },
233 Fragment::Box(fragment) | Fragment::Float(fragment) => {
234 fragment.with_style().scrollable_overflow_for_parent()
235 },
236 Fragment::Positioning(fragment) => fragment.scrollable_overflow_for_parent(),
237 Fragment::Text(fragment) => fragment.scrollable_overflow_for_parent(),
238 Fragment::AbsoluteOrFixedPositionedPlaceholder(_) |
239 Fragment::Image(..) |
240 Fragment::IFrame(..) => self.base().map(|base| base.rect()).unwrap_or_default(),
241 }
242 }
243
244 pub(crate) fn scrollable_overflow_padding_contribution_for_parent(
253 &self,
254 ) -> Option<PhysicalRect<Au>> {
255 match self {
256 Fragment::Box(fragment) | Fragment::Float(fragment)
258 if !fragment
259 .style()
260 .get_box()
261 .position
262 .is_absolutely_positioned() =>
263 {
264 Some(fragment.margin_rect())
265 },
266 Fragment::Box(..) | Fragment::Float(..) | Fragment::LayoutRoot(..) => None,
269 Fragment::Positioning(fragment) => Some(fragment.base.rect()),
273 Fragment::AbsoluteOrFixedPositionedPlaceholder(_) => None,
274 Fragment::Text(..) | Fragment::Image(..) | Fragment::IFrame(..) => {
275 Some(self.base()?.rect())
276 },
277 }
278 }
279
280 pub(crate) fn cumulative_box_area_rect(
281 &self,
282 area: BoxAreaType,
283 containing_block_computation: ContainingBlockCalculation<'_>,
284 ) -> Option<PhysicalRect<Au>> {
285 match self {
286 Fragment::LayoutRoot(layout_root_fragment) => layout_root_fragment
287 .inner()
288 .cumulative_box_area_rect(area, containing_block_computation),
289 Fragment::Box(fragment) | Fragment::Float(fragment) => Some(match area {
290 BoxAreaType::Content => {
291 fragment.cumulative_content_box_rect(containing_block_computation)
292 },
293 BoxAreaType::Padding => {
294 fragment.cumulative_padding_box_rect(containing_block_computation)
295 },
296 BoxAreaType::Border => {
297 fragment.cumulative_border_box_rect(containing_block_computation)
298 },
299 }),
300 Fragment::Positioning(fragment) => {
301 Some(fragment.offset_by_containing_block(
302 &fragment.base.rect(),
303 containing_block_computation,
304 ))
305 },
306 Fragment::Text(_) |
307 Fragment::AbsoluteOrFixedPositionedPlaceholder(_) |
308 Fragment::Image(_) |
309 Fragment::IFrame(_) => None,
310 }
311 }
312
313 pub(crate) fn client_rect(&self) -> Rect<i32, CSSPixel> {
314 let Some(fragment) = self.retrieve_box_fragment() else {
315 return Rect::zero();
316 };
317 let fragment = fragment.with_style();
318
319 if fragment.is_inline_box() {
325 return Rect::zero();
326 }
327
328 let rect = if fragment.is_table_wrapper() {
329 let mut rect = fragment.border_rect();
332 rect.origin = PhysicalPoint::zero();
333 rect
334 } else {
335 let mut rect = fragment.padding_rect();
336 rect.origin = PhysicalPoint::new(fragment.border.left, fragment.border.top);
337 rect
338 };
339
340 let rect = Rect::new(
341 Point2D::new(rect.origin.x.to_f32_px(), rect.origin.y.to_f32_px()),
342 Size2D::new(rect.size.width.to_f32_px(), rect.size.height.to_f32_px()),
343 );
344 rect.round().to_i32()
345 }
346
347 pub(crate) fn children(&self) -> Option<RefOrAtomicRef<'_, Vec<Fragment>>> {
348 match self {
349 Fragment::LayoutRoot(fragment) => Some(RefOrAtomicRef::AtomicRef(AtomicRef::map(
350 fragment.inner_box_fragment(),
351 |fragment| &fragment.children,
352 ))),
353 Fragment::Box(fragment) | Fragment::Float(fragment) => {
354 Some(RefOrAtomicRef::Ref(&fragment.children))
355 },
356 Fragment::Positioning(fragment) => Some(RefOrAtomicRef::Ref(&fragment.children)),
357 _ => None,
358 }
359 }
360
361 pub(crate) fn find<T>(
362 &self,
363 manager: &ContainingBlockManager<PhysicalRect<Au>>,
364 level: usize,
365 process_func: &mut impl FnMut(&Fragment, usize, &PhysicalRect<Au>) -> Option<T>,
366 ) -> Option<T> {
367 let containing_block = manager.get_containing_block_for_fragment(self);
368 if let Some(result) = process_func(self, level, containing_block) {
369 return Some(result);
370 }
371
372 match self {
373 Fragment::LayoutRoot(layout_root_fragment) => {
374 layout_root_fragment
375 .inner()
376 .find(manager, level, process_func)
377 },
378 Fragment::Box(fragment) | Fragment::Float(fragment) => {
379 let style = fragment.style();
380 let content_rect = fragment
381 .content_rect()
382 .translate(containing_block.origin.to_vector());
383 let padding_rect = fragment
384 .padding_rect()
385 .translate(containing_block.origin.to_vector());
386 let new_manager = if style
387 .establishes_containing_block_for_all_descendants(fragment.base.flags)
388 {
389 manager.new_for_absolute_and_fixed_descendants(&content_rect, &padding_rect)
390 } else if style
391 .establishes_containing_block_for_absolute_descendants(fragment.base.flags)
392 {
393 manager.new_for_absolute_descendants(&content_rect, &padding_rect)
394 } else {
395 manager.new_for_non_absolute_descendants(&content_rect)
396 };
397
398 fragment
399 .children
400 .iter()
401 .find_map(|child| child.find(&new_manager, level + 1, process_func))
402 },
403 Fragment::Positioning(fragment) => {
404 let content_rect = fragment
405 .base
406 .rect()
407 .translate(containing_block.origin.to_vector());
408 let new_manager = manager.new_for_non_absolute_descendants(&content_rect);
409 fragment
410 .children
411 .iter()
412 .find_map(|child| child.find(&new_manager, level + 1, process_func))
413 },
414 _ => None,
415 }
416 }
417
418 pub(crate) fn repair_style(&self, new_style: &ServoArc<ComputedValues>) {
419 if let Some(base) = self.base() {
420 base.set_status(FragmentStatus::StyleChanged);
421 }
422
423 let inner_box_fragment;
424 let shared_style = match self {
425 Fragment::LayoutRoot(fragment) => {
426 inner_box_fragment = fragment.inner_box_fragment();
427 &inner_box_fragment.style
428 },
429 Fragment::Box(fragment) => &fragment.style,
430 Fragment::Text(fragment) => &fragment.run_data.inline_styles.style,
431 Fragment::AbsoluteOrFixedPositionedPlaceholder(_) => return,
432 Fragment::Positioning(fragment) => &fragment.style,
433 Fragment::Image(fragment) => &fragment.style,
434 Fragment::IFrame(fragment) => &fragment.style,
435 Fragment::Float(fragment) => &fragment.style,
436 };
437 *shared_style.borrow_mut() = new_style.clone();
438 }
439
440 pub(crate) fn retrieve_box_fragment(&self) -> Option<RefOrAtomicRef<'_, Arc<BoxFragment>>> {
441 match self {
442 Fragment::LayoutRoot(layout_root_fragment) => Some(RefOrAtomicRef::AtomicRef(
443 layout_root_fragment.inner_box_fragment(),
444 )),
445 Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
446 Some(RefOrAtomicRef::Ref(box_fragment))
447 },
448 _ => None,
449 }
450 }
451}
452
453impl TextFragment {
454 pub(crate) fn style<'a>(&'a self) -> AtomicRef<'a, ServoArc<ComputedValues>> {
455 self.run_data.inline_styles.style.borrow()
456 }
457
458 pub(crate) fn selected_style<'a>(&'a self) -> AtomicRef<'a, ServoArc<ComputedValues>> {
459 self.run_data.inline_styles.selected.borrow()
460 }
461
462 pub fn print(&self, tree: &mut PrintTree) {
463 tree.add_item(format!(
464 "Text num_glyphs={} box={:?}",
465 self.glyphs
466 .iter()
467 .map(|shaped_text_slice| shaped_text_slice.glyph_count())
468 .sum::<usize>(),
469 self.base.rect()
470 ));
471 }
472
473 pub(crate) fn point_is_within_vertical_boundaries(
476 &self,
477 point_in_fragment: Point2D<Au, CSSPixel>,
478 ) -> bool {
479 let rect = &self.base.rect();
480 rect.min_y() <= point_in_fragment.y && rect.max_y() >= point_in_fragment.y
481 }
482
483 pub(crate) fn distance_to_point_for_glyph_offset(
487 &self,
488 point_in_fragment: Point2D<Au, CSSPixel>,
489 ) -> Au {
490 let rect = Rect::new(Point2D::origin(), self.base.rect().size);
492
493 let dx = (rect.min_x() - point_in_fragment.x)
496 .max(Au::zero())
497 .max(point_in_fragment.x - rect.max_x());
498 let dy = (rect.min_y() - point_in_fragment.y)
499 .max(Au::zero())
500 .max(point_in_fragment.y - rect.max_y());
501 Au::from_f64_px(dx.to_f64_px().hypot(dy.to_f64_px()))
502 }
503
504 pub(crate) fn character_offset(
512 &self,
513 point_in_fragment: Point2D<Au, CSSPixel>,
514 ) -> Option<Utf32CodeUnits> {
515 self.unmapped_character_offset(point_in_fragment)
516 .map(|character_offset| {
517 self.run_data
518 .map_transformed_offset_to_dom_offset(character_offset)
519 })
520 }
521
522 fn unmapped_character_offset(
526 &self,
527 point_in_fragment: Point2D<Au, CSSPixel>,
528 ) -> Option<Utf32CodeUnits> {
529 let max_vertical_offset = self.base.rect().height().scale_by(0.25);
531 if point_in_fragment.y < -max_vertical_offset {
532 return Some(self.character_range_in_dom_node.start);
533 }
534
535 if point_in_fragment.y > self.base.rect().max_y() + max_vertical_offset {
541 return None;
542 }
543
544 let mut current_character = self.character_range_in_dom_node.start;
545 let mut current_offset = Au::zero();
546 for glyph_store in &self.glyphs {
547 for glyph in glyph_store.glyphs() {
548 let mut advance = glyph.advance();
549 if glyph.char_is_word_separator() {
550 advance += self.justification_adjustment;
551 }
552 if current_offset + advance.scale_by(0.5) >= point_in_fragment.x {
553 return Some(current_character);
554 }
555 current_offset += advance;
556 current_character += glyph.character_count();
557 }
558 }
559
560 Some(current_character)
561 }
562
563 fn scrollable_overflow_for_parent(&self) -> PhysicalRect<Au> {
564 let mut rect = self.base.rect();
565 let mut hanging_advance = Au::zero();
566 for slice in self.glyphs.iter().rev() {
567 hanging_advance += slice.hangable_advance();
568 if slice.has_non_hangable_non_removable_content() {
569 break;
570 }
571 }
572
573 rect.size.width -= hanging_advance;
575
576 rect
577 }
578}
579
580impl ImageFragment {
581 pub fn print(&self, tree: &mut PrintTree) {
582 tree.add_item(format!(
583 "Image\
584 \nrect={:?}",
585 self.base.rect()
586 ));
587 }
588}
589
590impl IFrameFragment {
591 pub fn print(&self, tree: &mut PrintTree) {
592 tree.add_item(format!(
593 "IFrame\
594 \npipeline={:?} rect={:?}",
595 self.pipeline_id,
596 self.base.rect()
597 ));
598 }
599}
600
601impl CollapsedBlockMargins {
602 pub fn from_margin(margin: &LogicalSides<Au>) -> Self {
603 Self {
604 collapsed_through: false,
605 start: CollapsedMargin::new(margin.block_start),
606 end: CollapsedMargin::new(margin.block_end),
607 }
608 }
609
610 pub fn zero() -> Self {
611 Self {
612 collapsed_through: false,
613 start: CollapsedMargin::zero(),
614 end: CollapsedMargin::zero(),
615 }
616 }
617}
618
619impl CollapsedMargin {
620 pub fn zero() -> Self {
621 Self {
622 max_positive: Au::zero(),
623 min_negative: Au::zero(),
624 }
625 }
626
627 pub fn new(margin: Au) -> Self {
628 Self {
629 max_positive: margin.max(Au::zero()),
630 min_negative: margin.min(Au::zero()),
631 }
632 }
633
634 pub fn adjoin(&self, other: &Self) -> Self {
635 Self {
636 max_positive: self.max_positive.max(other.max_positive),
637 min_negative: self.min_negative.min(other.min_negative),
638 }
639 }
640
641 pub fn adjoin_assign(&mut self, other: &Self) {
642 *self = self.adjoin(other);
643 }
644
645 pub fn solve(&self) -> Au {
646 self.max_positive + self.min_negative
647 }
648}
649
650pub(crate) enum ContainingBlockCalculation<'a> {
657 Lazy { layout_thread: &'a LayoutThread },
661 AlreadyDoneWithStackingContextTree,
667}
668
669impl ContainingBlockCalculation<'_> {
670 pub(crate) fn ensure(&self) {
671 match self {
672 Self::Lazy { layout_thread } => layout_thread.ensure_containing_block_calculation(),
673 Self::AlreadyDoneWithStackingContextTree => {},
674 }
675 }
676}
677
678impl<'a> From<&'a LayoutThread> for ContainingBlockCalculation<'a> {
679 fn from(layout_thread: &'a LayoutThread) -> Self {
680 Self::Lazy { layout_thread }
681 }
682}