1use std::sync::Arc;
6
7use app_units::Au;
8use atomic_refcell::AtomicRef;
9use euclid::{Point2D, Rect, Size2D};
10use fonts::{FontMetrics, ShapedTextSlice};
11use layout_api::BoxAreaType;
12use malloc_size_of_derive::MallocSizeOf;
13use servo_base::id::PipelineId;
14use servo_base::print_tree::PrintTree;
15use servo_url::ServoUrl;
16use style::Zero;
17use style_traits::CSSPixel;
18use webrender_api::{FontInstanceKey, ImageKey};
19
20use super::{
21 BaseFragment, BoxFragment, ContainingBlockManager, HoistedSharedFragment, PositioningFragment,
22 Tag,
23};
24use crate::SharedStyle;
25use crate::cell::{ArcRefCell, RefOrAtomicRef};
26use crate::flow::inline::line::TextRunOffsets;
27use crate::geom::{LogicalSides, PhysicalPoint, PhysicalRect};
28use crate::layout_impl::LayoutThread;
29use crate::style_ext::ComputedValuesExt;
30
31#[derive(Clone, MallocSizeOf)]
32pub(crate) enum Fragment {
33 LayoutRoot(LayoutRootFragment),
34 Box(#[conditional_malloc_size_of] Arc<BoxFragment>),
35 Float(#[conditional_malloc_size_of] Arc<BoxFragment>),
41 Positioning(#[conditional_malloc_size_of] Arc<PositioningFragment>),
42 AbsoluteOrFixedPositionedPlaceholder(ArcRefCell<HoistedSharedFragment>),
48 Text(#[conditional_malloc_size_of] Arc<TextFragment>),
49 Image(#[conditional_malloc_size_of] Arc<ImageFragment>),
50 IFrame(#[conditional_malloc_size_of] Arc<IFrameFragment>),
51}
52
53#[derive(Clone, MallocSizeOf)]
54pub(crate) struct CollapsedBlockMargins {
55 pub collapsed_through: bool,
56 pub start: CollapsedMargin,
57 pub end: CollapsedMargin,
58}
59
60#[derive(Clone, Copy, Debug, MallocSizeOf)]
61pub(crate) struct CollapsedMargin {
62 max_positive: Au,
63 min_negative: Au,
64}
65
66#[derive(Clone, MallocSizeOf)]
67pub(crate) struct LayoutRootFragment {
68 pub fragment: ArcRefCell<HoistedSharedFragment>,
69}
70
71impl LayoutRootFragment {
72 pub(crate) fn inner(&self) -> AtomicRef<'_, Fragment> {
73 AtomicRef::map(self.fragment.borrow(), |fragment| {
74 fragment
75 .fragment
76 .as_ref()
77 .expect("Should never create LayoutRoot without a Fragment")
78 })
79 }
80
81 pub(crate) fn inner_box_fragment(&self) -> AtomicRef<'_, Arc<BoxFragment>> {
82 AtomicRef::map(self.inner(), |fragment| match fragment {
83 Fragment::Box(box_fragment) => box_fragment,
84 _ => unreachable!("Layout root should always contain box fragment"),
85 })
86 }
87}
88
89#[derive(MallocSizeOf)]
90pub(crate) struct TextFragment {
91 pub base: BaseFragment,
92 pub selected_style: SharedStyle,
93 #[conditional_malloc_size_of]
94 pub font_metrics: Arc<FontMetrics>,
95 pub font_key: FontInstanceKey,
96 #[conditional_malloc_size_of]
97 pub glyphs: Vec<Arc<ShapedTextSlice>>,
98 pub justification_adjustment: Au,
100 pub offsets: Option<Box<TextRunOffsets>>,
103 pub is_empty_for_text_cursor: bool,
106}
107
108#[derive(MallocSizeOf)]
109pub(crate) struct ImageFragment {
110 pub base: BaseFragment,
111 pub clip: PhysicalRect<Au>,
112 pub image_key: Option<ImageKey>,
113 pub showing_broken_image_icon: bool,
114 pub url: Option<ServoUrl>,
115}
116
117#[derive(MallocSizeOf)]
118pub(crate) struct IFrameFragment {
119 pub base: BaseFragment,
120 pub pipeline_id: PipelineId,
121}
122
123impl Fragment {
124 pub fn base(&self) -> Option<RefOrAtomicRef<'_, BaseFragment>> {
125 Some(match self {
126 Fragment::LayoutRoot(fragment) => RefOrAtomicRef::AtomicRef(AtomicRef::map(
127 fragment.inner_box_fragment(),
128 |box_fragment| &box_fragment.base,
129 )),
130 Fragment::Box(fragment) => RefOrAtomicRef::Ref(&fragment.base),
131 Fragment::Text(fragment) => RefOrAtomicRef::Ref(&fragment.base),
132 Fragment::AbsoluteOrFixedPositionedPlaceholder(_) => return None,
133 Fragment::Positioning(fragment) => RefOrAtomicRef::Ref(&fragment.base),
134 Fragment::Image(fragment) => RefOrAtomicRef::Ref(&fragment.base),
135 Fragment::IFrame(fragment) => RefOrAtomicRef::Ref(&fragment.base),
136 Fragment::Float(fragment) => RefOrAtomicRef::Ref(&fragment.base),
137 })
138 }
139
140 pub(crate) fn set_containing_block(&self, containing_block: &PhysicalRect<Au>) {
141 match self {
142 Fragment::LayoutRoot(layout_root_fragment) => layout_root_fragment
143 .inner()
144 .set_containing_block(containing_block),
145 Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
146 box_fragment.set_containing_block(containing_block)
147 },
148 Fragment::Positioning(positioning_fragment) => {
149 positioning_fragment.set_containing_block(containing_block)
150 },
151 Fragment::AbsoluteOrFixedPositionedPlaceholder(..) |
152 Fragment::Text(..) |
153 Fragment::Image(..) |
154 Fragment::IFrame(..) => {},
155 }
156 }
157
158 pub fn tag(&self) -> Option<Tag> {
159 self.base().and_then(|base| base.tag)
160 }
161
162 pub fn print(&self, tree: &mut PrintTree) {
163 match self {
164 Fragment::LayoutRoot(layout_root_fragment) => layout_root_fragment.inner().print(tree),
165 Fragment::Box(fragment) => fragment.print(tree),
166 Fragment::Float(fragment) => {
167 tree.new_level("Float".to_string());
168 fragment.print(tree);
169 tree.end_level();
170 },
171 Fragment::AbsoluteOrFixedPositionedPlaceholder(_) => {
172 tree.add_item("AbsoluteOrFixedPositioned".to_string());
173 },
174 Fragment::Positioning(fragment) => fragment.print(tree),
175 Fragment::Text(fragment) => fragment.print(tree),
176 Fragment::Image(fragment) => fragment.print(tree),
177 Fragment::IFrame(fragment) => fragment.print(tree),
178 }
179 }
180
181 pub(crate) fn scrolling_area(&self, layout_thread: &LayoutThread) -> PhysicalRect<Au> {
182 self.retrieve_box_fragment().map_or_else(
183 || self.scrollable_overflow_for_parent(),
184 |box_fragment| {
185 box_fragment.offset_by_containing_block(
186 &box_fragment.with_style().scrollable_overflow(),
187 layout_thread.into(),
188 )
189 },
190 )
191 }
192
193 pub(crate) fn clear_scrollable_overflow(&self) {
197 match self {
198 Fragment::LayoutRoot(fragment) => {
199 fragment.inner_box_fragment().clear_scrollable_overflow()
200 },
201 Fragment::Box(fragment) | Fragment::Float(fragment) => {
202 fragment.clear_scrollable_overflow()
203 },
204 Fragment::Positioning(fragment) => fragment.clear_scrollable_overflow(),
205 _ => {},
206 }
207 }
208
209 pub(crate) fn scrollable_overflow_for_parent(&self) -> PhysicalRect<Au> {
210 match self {
211 Fragment::LayoutRoot(layout_root) => {
212 layout_root.inner().scrollable_overflow_for_parent()
213 },
214 Fragment::Box(fragment) | Fragment::Float(fragment) => {
215 fragment.with_style().scrollable_overflow_for_parent()
216 },
217 Fragment::Positioning(fragment) => fragment.scrollable_overflow_for_parent(),
218 Fragment::AbsoluteOrFixedPositionedPlaceholder(_) |
219 Fragment::Text(..) |
220 Fragment::Image(..) |
221 Fragment::IFrame(..) => self.base().map(|base| base.rect()).unwrap_or_default(),
222 }
223 }
224
225 pub(crate) fn scrollable_overflow_padding_contribution_for_parent(
234 &self,
235 ) -> Option<PhysicalRect<Au>> {
236 match self {
237 Fragment::Box(fragment) | Fragment::Float(fragment)
239 if !fragment.style().clone_position().is_absolutely_positioned() =>
240 {
241 Some(fragment.margin_rect())
242 },
243 Fragment::Box(..) | Fragment::Float(..) | Fragment::LayoutRoot(..) => None,
246 Fragment::Positioning(fragment) => Some(fragment.base.rect()),
250 Fragment::AbsoluteOrFixedPositionedPlaceholder(_) => None,
251 Fragment::Text(..) | Fragment::Image(..) | Fragment::IFrame(..) => {
252 Some(self.base()?.rect())
253 },
254 }
255 }
256
257 pub(crate) fn cumulative_box_area_rect(
258 &self,
259 area: BoxAreaType,
260 containing_block_computation: ContainingBlockCalculation<'_>,
261 ) -> Option<PhysicalRect<Au>> {
262 match self {
263 Fragment::LayoutRoot(layout_root_fragment) => layout_root_fragment
264 .inner()
265 .cumulative_box_area_rect(area, containing_block_computation),
266 Fragment::Box(fragment) | Fragment::Float(fragment) => Some(match area {
267 BoxAreaType::Content => {
268 fragment.cumulative_content_box_rect(containing_block_computation)
269 },
270 BoxAreaType::Padding => {
271 fragment.cumulative_padding_box_rect(containing_block_computation)
272 },
273 BoxAreaType::Border => {
274 fragment.cumulative_border_box_rect(containing_block_computation)
275 },
276 }),
277 Fragment::Positioning(fragment) => {
278 Some(fragment.offset_by_containing_block(
279 &fragment.base.rect(),
280 containing_block_computation,
281 ))
282 },
283 Fragment::Text(_) |
284 Fragment::AbsoluteOrFixedPositionedPlaceholder(_) |
285 Fragment::Image(_) |
286 Fragment::IFrame(_) => None,
287 }
288 }
289
290 pub(crate) fn client_rect(&self) -> Rect<i32, CSSPixel> {
291 let Some(fragment) = self.retrieve_box_fragment() else {
292 return Rect::zero();
293 };
294 let fragment = fragment.with_style();
295
296 if fragment.is_inline_box() {
302 return Rect::zero();
303 }
304
305 let rect = if fragment.is_table_wrapper() {
306 let mut rect = fragment.border_rect();
309 rect.origin = PhysicalPoint::zero();
310 rect
311 } else {
312 let mut rect = fragment.padding_rect();
313 rect.origin = PhysicalPoint::new(fragment.border.left, fragment.border.top);
314 rect
315 };
316
317 let rect = Rect::new(
318 Point2D::new(rect.origin.x.to_f32_px(), rect.origin.y.to_f32_px()),
319 Size2D::new(rect.size.width.to_f32_px(), rect.size.height.to_f32_px()),
320 );
321 rect.round().to_i32()
322 }
323
324 pub(crate) fn children(&self) -> Option<RefOrAtomicRef<'_, Vec<Fragment>>> {
325 match self {
326 Fragment::LayoutRoot(fragment) => Some(RefOrAtomicRef::AtomicRef(AtomicRef::map(
327 fragment.inner_box_fragment(),
328 |fragment| &fragment.children,
329 ))),
330 Fragment::Box(fragment) | Fragment::Float(fragment) => {
331 Some(RefOrAtomicRef::Ref(&fragment.children))
332 },
333 Fragment::Positioning(fragment) => Some(RefOrAtomicRef::Ref(&fragment.children)),
334 _ => None,
335 }
336 }
337
338 pub(crate) fn find<T>(
339 &self,
340 manager: &ContainingBlockManager<PhysicalRect<Au>>,
341 level: usize,
342 process_func: &mut impl FnMut(&Fragment, usize, &PhysicalRect<Au>) -> Option<T>,
343 ) -> Option<T> {
344 let containing_block = manager.get_containing_block_for_fragment(self);
345 if let Some(result) = process_func(self, level, containing_block) {
346 return Some(result);
347 }
348
349 match self {
350 Fragment::LayoutRoot(layout_root_fragment) => {
351 layout_root_fragment
352 .inner()
353 .find(manager, level, process_func)
354 },
355 Fragment::Box(fragment) | Fragment::Float(fragment) => {
356 let style = fragment.style();
357 let content_rect = fragment
358 .content_rect()
359 .translate(containing_block.origin.to_vector());
360 let padding_rect = fragment
361 .padding_rect()
362 .translate(containing_block.origin.to_vector());
363 let new_manager = if style
364 .establishes_containing_block_for_all_descendants(fragment.base.flags)
365 {
366 manager.new_for_absolute_and_fixed_descendants(&content_rect, &padding_rect)
367 } else if style
368 .establishes_containing_block_for_absolute_descendants(fragment.base.flags)
369 {
370 manager.new_for_absolute_descendants(&content_rect, &padding_rect)
371 } else {
372 manager.new_for_non_absolute_descendants(&content_rect)
373 };
374
375 fragment
376 .children
377 .iter()
378 .find_map(|child| child.find(&new_manager, level + 1, process_func))
379 },
380 Fragment::Positioning(fragment) => {
381 let content_rect = fragment
382 .base
383 .rect()
384 .translate(containing_block.origin.to_vector());
385 let new_manager = manager.new_for_non_absolute_descendants(&content_rect);
386 fragment
387 .children
388 .iter()
389 .find_map(|child| child.find(&new_manager, level + 1, process_func))
390 },
391 _ => None,
392 }
393 }
394
395 pub(crate) fn retrieve_box_fragment(&self) -> Option<RefOrAtomicRef<'_, Arc<BoxFragment>>> {
396 match self {
397 Fragment::LayoutRoot(layout_root_fragment) => Some(RefOrAtomicRef::AtomicRef(
398 layout_root_fragment.inner_box_fragment(),
399 )),
400 Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
401 Some(RefOrAtomicRef::Ref(box_fragment))
402 },
403 _ => None,
404 }
405 }
406}
407
408impl TextFragment {
409 pub fn print(&self, tree: &mut PrintTree) {
410 tree.add_item(format!(
411 "Text num_glyphs={} box={:?}",
412 self.glyphs
413 .iter()
414 .map(|shaped_text_slice| shaped_text_slice.glyph_count())
415 .sum::<usize>(),
416 self.base.rect()
417 ));
418 }
419
420 pub(crate) fn point_is_within_vertical_boundaries(
423 &self,
424 point_in_fragment: Point2D<Au, CSSPixel>,
425 ) -> bool {
426 let rect = &self.base.rect();
427 rect.min_y() <= point_in_fragment.y && rect.max_y() >= point_in_fragment.y
428 }
429
430 pub(crate) fn distance_to_point_for_glyph_offset(
434 &self,
435 point_in_fragment: Point2D<Au, CSSPixel>,
436 ) -> Au {
437 let rect = &self.base.rect();
440 let dx = (rect.min_x() - point_in_fragment.x)
441 .max(Au::zero())
442 .max(point_in_fragment.x - rect.max_x());
443 let dy = (rect.min_y() - point_in_fragment.y)
444 .max(Au::zero())
445 .max(point_in_fragment.y - rect.max_y());
446 Au::from_f64_px((dx.to_f64_px().powi(2) + dy.to_f64_px().powi(2)).sqrt())
447 }
448
449 pub(crate) fn character_offset(
457 &self,
458 point_in_fragment: Point2D<Au, CSSPixel>,
459 ) -> Option<usize> {
460 let offsets = self.offsets.as_ref()?;
462 let max_vertical_offset = self.base.rect().height().scale_by(0.25);
463 if point_in_fragment.y < -max_vertical_offset {
464 return Some(offsets.character_range.start);
465 }
466
467 if point_in_fragment.y > self.base.rect().max_y() + max_vertical_offset {
473 return None;
474 }
475
476 let mut current_character = offsets.character_range.start;
477 let mut current_offset = Au::zero();
478 for glyph_store in &self.glyphs {
479 for glyph in glyph_store.glyphs() {
480 let mut advance = glyph.advance();
481 if glyph.char_is_word_separator() {
482 advance += self.justification_adjustment;
483 }
484 if current_offset + advance.scale_by(0.5) >= point_in_fragment.x {
485 return Some(current_character);
486 }
487 current_offset += advance;
488 current_character += glyph.character_count();
489 }
490 }
491
492 Some(current_character)
493 }
494}
495
496impl ImageFragment {
497 pub fn print(&self, tree: &mut PrintTree) {
498 tree.add_item(format!(
499 "Image\
500 \nrect={:?}",
501 self.base.rect()
502 ));
503 }
504}
505
506impl IFrameFragment {
507 pub fn print(&self, tree: &mut PrintTree) {
508 tree.add_item(format!(
509 "IFrame\
510 \npipeline={:?} rect={:?}",
511 self.pipeline_id,
512 self.base.rect()
513 ));
514 }
515}
516
517impl CollapsedBlockMargins {
518 pub fn from_margin(margin: &LogicalSides<Au>) -> Self {
519 Self {
520 collapsed_through: false,
521 start: CollapsedMargin::new(margin.block_start),
522 end: CollapsedMargin::new(margin.block_end),
523 }
524 }
525
526 pub fn zero() -> Self {
527 Self {
528 collapsed_through: false,
529 start: CollapsedMargin::zero(),
530 end: CollapsedMargin::zero(),
531 }
532 }
533}
534
535impl CollapsedMargin {
536 pub fn zero() -> Self {
537 Self {
538 max_positive: Au::zero(),
539 min_negative: Au::zero(),
540 }
541 }
542
543 pub fn new(margin: Au) -> Self {
544 Self {
545 max_positive: margin.max(Au::zero()),
546 min_negative: margin.min(Au::zero()),
547 }
548 }
549
550 pub fn adjoin(&self, other: &Self) -> Self {
551 Self {
552 max_positive: self.max_positive.max(other.max_positive),
553 min_negative: self.min_negative.min(other.min_negative),
554 }
555 }
556
557 pub fn adjoin_assign(&mut self, other: &Self) {
558 *self = self.adjoin(other);
559 }
560
561 pub fn solve(&self) -> Au {
562 self.max_positive + self.min_negative
563 }
564}
565
566pub(crate) enum ContainingBlockCalculation<'a> {
573 Lazy { layout_thread: &'a LayoutThread },
577 AlreadyDoneWithStackingContextTree,
583}
584
585impl ContainingBlockCalculation<'_> {
586 pub(crate) fn ensure(&self) {
587 match self {
588 Self::Lazy { layout_thread } => layout_thread.ensure_containing_block_calculation(),
589 Self::AlreadyDoneWithStackingContextTree => {},
590 }
591 }
592}
593
594impl<'a> From<&'a LayoutThread> for ContainingBlockCalculation<'a> {
595 fn from(layout_thread: &'a LayoutThread) -> Self {
596 Self::Lazy { layout_thread }
597 }
598}