layout/fragment_tree/
fragment.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::sync::Arc;
6
7use app_units::Au;
8use base::id::PipelineId;
9use base::print_tree::PrintTree;
10use euclid::{Point2D, Rect, Size2D, UnknownUnit};
11use fonts::{ByteIndex, FontMetrics, GlyphStore};
12use layout_api::BoxAreaType;
13use malloc_size_of_derive::MallocSizeOf;
14use range::Range as ServoRange;
15use servo_arc::Arc as ServoArc;
16use style::Zero;
17use style::properties::ComputedValues;
18use webrender_api::{FontInstanceKey, ImageKey};
19
20use super::{
21    BaseFragment, BoxFragment, ContainingBlockManager, HoistedSharedFragment, PositioningFragment,
22    Tag,
23};
24use crate::cell::ArcRefCell;
25use crate::flow::inline::SharedInlineStyles;
26use crate::geom::{LogicalSides, PhysicalPoint, PhysicalRect};
27use crate::style_ext::ComputedValuesExt;
28
29#[derive(Clone, MallocSizeOf)]
30pub(crate) enum Fragment {
31    Box(ArcRefCell<BoxFragment>),
32    /// Floating content. A floated fragment is very similar to a normal
33    /// [BoxFragment] but it isn't positioned using normal in block flow
34    /// positioning rules (margin collapse, etc). Instead, they are laid
35    /// out by the [crate::flow::float::SequentialLayoutState] of their
36    /// float containing block formatting context.
37    Float(ArcRefCell<BoxFragment>),
38    Positioning(ArcRefCell<PositioningFragment>),
39    /// Absolute and fixed position fragments are hoisted up so that they
40    /// are children of the BoxFragment that establishes their containing
41    /// blocks, so that they can be laid out properly. When this happens
42    /// an `AbsoluteOrFixedPositioned` fragment is left at the original tree
43    /// position. This allows these hoisted fragments to be painted with
44    /// regard to their original tree order during stacking context tree /
45    /// display list construction.
46    AbsoluteOrFixedPositioned(ArcRefCell<HoistedSharedFragment>),
47    Text(ArcRefCell<TextFragment>),
48    Image(ArcRefCell<ImageFragment>),
49    IFrame(ArcRefCell<IFrameFragment>),
50}
51
52#[derive(Clone, MallocSizeOf)]
53pub(crate) struct CollapsedBlockMargins {
54    pub collapsed_through: bool,
55    pub start: CollapsedMargin,
56    pub end: CollapsedMargin,
57}
58
59#[derive(Clone, Copy, Debug, MallocSizeOf)]
60pub(crate) struct CollapsedMargin {
61    max_positive: Au,
62    min_negative: Au,
63}
64
65#[derive(MallocSizeOf)]
66pub(crate) struct TextFragment {
67    pub base: BaseFragment,
68    pub inline_styles: SharedInlineStyles,
69    pub rect: PhysicalRect<Au>,
70    pub font_metrics: FontMetrics,
71    pub font_key: FontInstanceKey,
72    #[conditional_malloc_size_of]
73    pub glyphs: Vec<Arc<GlyphStore>>,
74
75    /// Extra space to add for each justification opportunity.
76    pub justification_adjustment: Au,
77    pub selection_range: Option<ServoRange<ByteIndex>>,
78}
79
80#[derive(MallocSizeOf)]
81pub(crate) struct ImageFragment {
82    pub base: BaseFragment,
83    pub style: ServoArc<ComputedValues>,
84    pub rect: PhysicalRect<Au>,
85    pub clip: PhysicalRect<Au>,
86    pub image_key: Option<ImageKey>,
87}
88
89#[derive(MallocSizeOf)]
90pub(crate) struct IFrameFragment {
91    pub base: BaseFragment,
92    pub pipeline_id: PipelineId,
93    pub rect: PhysicalRect<Au>,
94    pub style: ServoArc<ComputedValues>,
95}
96
97impl Fragment {
98    pub fn base(&self) -> Option<BaseFragment> {
99        Some(match self {
100            Fragment::Box(fragment) => fragment.borrow().base.clone(),
101            Fragment::Text(fragment) => fragment.borrow().base.clone(),
102            Fragment::AbsoluteOrFixedPositioned(_) => return None,
103            Fragment::Positioning(fragment) => fragment.borrow().base.clone(),
104            Fragment::Image(fragment) => fragment.borrow().base.clone(),
105            Fragment::IFrame(fragment) => fragment.borrow().base.clone(),
106            Fragment::Float(fragment) => fragment.borrow().base.clone(),
107        })
108    }
109
110    pub(crate) fn mutate_content_rect(&mut self, callback: impl FnOnce(&mut PhysicalRect<Au>)) {
111        match self {
112            Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
113                callback(&mut box_fragment.borrow_mut().content_rect)
114            },
115            Fragment::Positioning(_) | Fragment::AbsoluteOrFixedPositioned(_) => {},
116            Fragment::Text(text_fragment) => callback(&mut text_fragment.borrow_mut().rect),
117            Fragment::Image(image_fragment) => callback(&mut image_fragment.borrow_mut().rect),
118            Fragment::IFrame(iframe_fragment) => callback(&mut iframe_fragment.borrow_mut().rect),
119        }
120    }
121
122    pub(crate) fn set_containing_block(&self, containing_block: &PhysicalRect<Au>) {
123        match self {
124            Fragment::Box(box_fragment) => box_fragment
125                .borrow_mut()
126                .set_containing_block(containing_block),
127            Fragment::Float(float_fragment) => float_fragment
128                .borrow_mut()
129                .set_containing_block(containing_block),
130            Fragment::Positioning(positioning_fragment) => positioning_fragment
131                .borrow_mut()
132                .set_containing_block(containing_block),
133            Fragment::AbsoluteOrFixedPositioned(hoisted_shared_fragment) => {
134                if let Some(ref fragment) = hoisted_shared_fragment.borrow().fragment {
135                    fragment.set_containing_block(containing_block);
136                }
137            },
138            Fragment::Text(_) => {},
139            Fragment::Image(_) => {},
140            Fragment::IFrame(_) => {},
141        }
142    }
143
144    pub fn tag(&self) -> Option<Tag> {
145        self.base().and_then(|base| base.tag)
146    }
147
148    pub fn print(&self, tree: &mut PrintTree) {
149        match self {
150            Fragment::Box(fragment) => fragment.borrow().print(tree),
151            Fragment::Float(fragment) => {
152                tree.new_level("Float".to_string());
153                fragment.borrow().print(tree);
154                tree.end_level();
155            },
156            Fragment::AbsoluteOrFixedPositioned(_) => {
157                tree.add_item("AbsoluteOrFixedPositioned".to_string());
158            },
159            Fragment::Positioning(fragment) => fragment.borrow().print(tree),
160            Fragment::Text(fragment) => fragment.borrow().print(tree),
161            Fragment::Image(fragment) => fragment.borrow().print(tree),
162            Fragment::IFrame(fragment) => fragment.borrow().print(tree),
163        }
164    }
165
166    pub(crate) fn scrolling_area(&self) -> PhysicalRect<Au> {
167        match self {
168            Fragment::Box(fragment) | Fragment::Float(fragment) => {
169                let fragment = fragment.borrow();
170                fragment.offset_by_containing_block(&fragment.scrollable_overflow())
171            },
172            _ => self.scrollable_overflow_for_parent(),
173        }
174    }
175
176    pub(crate) fn scrollable_overflow_for_parent(&self) -> PhysicalRect<Au> {
177        match self {
178            Fragment::Box(fragment) | Fragment::Float(fragment) => {
179                return fragment.borrow().scrollable_overflow_for_parent();
180            },
181            Fragment::AbsoluteOrFixedPositioned(_) => PhysicalRect::zero(),
182            Fragment::Positioning(fragment) => fragment.borrow().scrollable_overflow_for_parent(),
183            Fragment::Text(fragment) => fragment.borrow().rect,
184            Fragment::Image(fragment) => fragment.borrow().rect,
185            Fragment::IFrame(fragment) => fragment.borrow().rect,
186        }
187    }
188
189    pub(crate) fn calculate_scrollable_overflow_for_parent(&self) -> PhysicalRect<Au> {
190        self.calculate_scrollable_overflow();
191        self.scrollable_overflow_for_parent()
192    }
193
194    pub(crate) fn calculate_scrollable_overflow(&self) {
195        match self {
196            Fragment::Box(fragment) | Fragment::Float(fragment) => {
197                fragment.borrow_mut().calculate_scrollable_overflow()
198            },
199            Fragment::Positioning(fragment) => {
200                fragment.borrow_mut().calculate_scrollable_overflow()
201            },
202            _ => {},
203        }
204    }
205
206    pub(crate) fn cumulative_box_area_rect(&self, area: BoxAreaType) -> Option<PhysicalRect<Au>> {
207        match self {
208            Fragment::Box(fragment) | Fragment::Float(fragment) => Some(match area {
209                BoxAreaType::Content => fragment.borrow().cumulative_content_box_rect(),
210                BoxAreaType::Padding => fragment.borrow().cumulative_padding_box_rect(),
211                BoxAreaType::Border => fragment.borrow().cumulative_border_box_rect(),
212            }),
213            Fragment::Positioning(fragment) => {
214                let fragment = fragment.borrow();
215                Some(fragment.offset_by_containing_block(&fragment.rect))
216            },
217            Fragment::Text(_) |
218            Fragment::AbsoluteOrFixedPositioned(_) |
219            Fragment::Image(_) |
220            Fragment::IFrame(_) => None,
221        }
222    }
223
224    pub(crate) fn client_rect(&self) -> Rect<i32, UnknownUnit> {
225        let rect = match self {
226            Fragment::Box(fragment) | Fragment::Float(fragment) => {
227                // https://drafts.csswg.org/cssom-view/#dom-element-clienttop
228                // " If the element has no associated CSS layout box or if the
229                //   CSS layout box is inline, return zero." For this check we
230                // also explicitly ignore the list item portion of the display
231                // style.
232                let fragment = fragment.borrow();
233                if fragment.is_inline_box() {
234                    return Rect::zero();
235                }
236
237                if fragment.is_table_wrapper() {
238                    // For tables the border actually belongs to the table grid box,
239                    // so we need to include it in the dimension of the table wrapper box.
240                    let mut rect = fragment.border_rect();
241                    rect.origin = PhysicalPoint::zero();
242                    rect
243                } else {
244                    let mut rect = fragment.padding_rect();
245                    rect.origin = PhysicalPoint::new(fragment.border.left, fragment.border.top);
246                    rect
247                }
248            },
249            _ => return Rect::zero(),
250        }
251        .to_untyped();
252
253        let rect = Rect::new(
254            Point2D::new(rect.origin.x.to_f32_px(), rect.origin.y.to_f32_px()),
255            Size2D::new(rect.size.width.to_f32_px(), rect.size.height.to_f32_px()),
256        );
257        rect.round().to_i32()
258    }
259
260    pub(crate) fn find<T>(
261        &self,
262        manager: &ContainingBlockManager<PhysicalRect<Au>>,
263        level: usize,
264        process_func: &mut impl FnMut(&Fragment, usize, &PhysicalRect<Au>) -> Option<T>,
265    ) -> Option<T> {
266        let containing_block = manager.get_containing_block_for_fragment(self);
267        if let Some(result) = process_func(self, level, containing_block) {
268            return Some(result);
269        }
270
271        match self {
272            Fragment::Box(fragment) | Fragment::Float(fragment) => {
273                let fragment = fragment.borrow();
274                let content_rect = fragment
275                    .content_rect
276                    .translate(containing_block.origin.to_vector());
277                let padding_rect = fragment
278                    .padding_rect()
279                    .translate(containing_block.origin.to_vector());
280                let new_manager = if fragment
281                    .style
282                    .establishes_containing_block_for_all_descendants(fragment.base.flags)
283                {
284                    manager.new_for_absolute_and_fixed_descendants(&content_rect, &padding_rect)
285                } else if fragment
286                    .style
287                    .establishes_containing_block_for_absolute_descendants(fragment.base.flags)
288                {
289                    manager.new_for_absolute_descendants(&content_rect, &padding_rect)
290                } else {
291                    manager.new_for_non_absolute_descendants(&content_rect)
292                };
293
294                fragment
295                    .children
296                    .iter()
297                    .find_map(|child| child.find(&new_manager, level + 1, process_func))
298            },
299            Fragment::Positioning(fragment) => {
300                let fragment = fragment.borrow();
301                let content_rect = fragment.rect.translate(containing_block.origin.to_vector());
302                let new_manager = manager.new_for_non_absolute_descendants(&content_rect);
303                fragment
304                    .children
305                    .iter()
306                    .find_map(|child| child.find(&new_manager, level + 1, process_func))
307            },
308            _ => None,
309        }
310    }
311
312    pub(crate) fn repair_style(&self, style: &ServoArc<ComputedValues>) {
313        match self {
314            Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
315                box_fragment.borrow_mut().style = style.clone()
316            },
317            Fragment::Positioning(positioning_fragment) => {
318                positioning_fragment.borrow_mut().style = style.clone();
319            },
320            Fragment::AbsoluteOrFixedPositioned(positioned_fragment) => {
321                if let Some(ref fragment) = positioned_fragment.borrow().fragment {
322                    fragment.repair_style(style);
323                }
324            },
325            Fragment::Text(..) => unreachable!("Should never try to repair style of TextFragment"),
326            Fragment::Image(image_fragment) => image_fragment.borrow_mut().style = style.clone(),
327            Fragment::IFrame(iframe_fragment) => iframe_fragment.borrow_mut().style = style.clone(),
328        }
329    }
330
331    pub(crate) fn retrieve_box_fragment(&self) -> Option<&ArcRefCell<BoxFragment>> {
332        match self {
333            Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => Some(box_fragment),
334            _ => None,
335        }
336    }
337}
338
339impl TextFragment {
340    pub fn print(&self, tree: &mut PrintTree) {
341        tree.add_item(format!(
342            "Text num_glyphs={} box={:?}",
343            self.glyphs
344                .iter()
345                .map(|glyph_store| glyph_store.len().0)
346                .sum::<isize>(),
347            self.rect,
348        ));
349    }
350
351    pub fn has_selection(&self) -> bool {
352        self.selection_range.is_some()
353    }
354}
355
356impl ImageFragment {
357    pub fn print(&self, tree: &mut PrintTree) {
358        tree.add_item(format!(
359            "Image\
360                \nrect={:?}",
361            self.rect
362        ));
363    }
364}
365
366impl IFrameFragment {
367    pub fn print(&self, tree: &mut PrintTree) {
368        tree.add_item(format!(
369            "IFrame\
370                \npipeline={:?} rect={:?}",
371            self.pipeline_id, self.rect
372        ));
373    }
374}
375
376impl CollapsedBlockMargins {
377    pub fn from_margin(margin: &LogicalSides<Au>) -> Self {
378        Self {
379            collapsed_through: false,
380            start: CollapsedMargin::new(margin.block_start),
381            end: CollapsedMargin::new(margin.block_end),
382        }
383    }
384
385    pub fn zero() -> Self {
386        Self {
387            collapsed_through: false,
388            start: CollapsedMargin::zero(),
389            end: CollapsedMargin::zero(),
390        }
391    }
392}
393
394impl CollapsedMargin {
395    pub fn zero() -> Self {
396        Self {
397            max_positive: Au::zero(),
398            min_negative: Au::zero(),
399        }
400    }
401
402    pub fn new(margin: Au) -> Self {
403        Self {
404            max_positive: margin.max(Au::zero()),
405            min_negative: margin.min(Au::zero()),
406        }
407    }
408
409    pub fn adjoin(&self, other: &Self) -> Self {
410        Self {
411            max_positive: self.max_positive.max(other.max_positive),
412            min_negative: self.min_negative.min(other.min_negative),
413        }
414    }
415
416    pub fn adjoin_assign(&mut self, other: &Self) {
417        *self = self.adjoin(other);
418    }
419
420    pub fn solve(&self) -> Au {
421        self.max_positive + self.min_negative
422    }
423}