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