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