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    pub showing_broken_image_icon: bool,
88}
89
90#[derive(MallocSizeOf)]
91pub(crate) struct IFrameFragment {
92    pub base: BaseFragment,
93    pub pipeline_id: PipelineId,
94    pub rect: PhysicalRect<Au>,
95    pub style: ServoArc<ComputedValues>,
96}
97
98impl Fragment {
99    pub fn base(&self) -> Option<BaseFragment> {
100        Some(match self {
101            Fragment::Box(fragment) => fragment.borrow().base.clone(),
102            Fragment::Text(fragment) => fragment.borrow().base.clone(),
103            Fragment::AbsoluteOrFixedPositioned(_) => return None,
104            Fragment::Positioning(fragment) => fragment.borrow().base.clone(),
105            Fragment::Image(fragment) => fragment.borrow().base.clone(),
106            Fragment::IFrame(fragment) => fragment.borrow().base.clone(),
107            Fragment::Float(fragment) => fragment.borrow().base.clone(),
108        })
109    }
110
111    pub(crate) fn mutate_content_rect(&mut self, callback: impl FnOnce(&mut PhysicalRect<Au>)) {
112        match self {
113            Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
114                callback(&mut box_fragment.borrow_mut().content_rect)
115            },
116            Fragment::Positioning(_) | Fragment::AbsoluteOrFixedPositioned(_) => {},
117            Fragment::Text(text_fragment) => callback(&mut text_fragment.borrow_mut().rect),
118            Fragment::Image(image_fragment) => callback(&mut image_fragment.borrow_mut().rect),
119            Fragment::IFrame(iframe_fragment) => callback(&mut iframe_fragment.borrow_mut().rect),
120        }
121    }
122
123    pub(crate) fn set_containing_block(&self, containing_block: &PhysicalRect<Au>) {
124        match self {
125            Fragment::Box(box_fragment) => box_fragment
126                .borrow_mut()
127                .set_containing_block(containing_block),
128            Fragment::Float(float_fragment) => float_fragment
129                .borrow_mut()
130                .set_containing_block(containing_block),
131            Fragment::Positioning(positioning_fragment) => positioning_fragment
132                .borrow_mut()
133                .set_containing_block(containing_block),
134            Fragment::AbsoluteOrFixedPositioned(hoisted_shared_fragment) => {
135                if let Some(ref fragment) = hoisted_shared_fragment.borrow().fragment {
136                    fragment.set_containing_block(containing_block);
137                }
138            },
139            Fragment::Text(_) => {},
140            Fragment::Image(_) => {},
141            Fragment::IFrame(_) => {},
142        }
143    }
144
145    pub fn tag(&self) -> Option<Tag> {
146        self.base().and_then(|base| base.tag)
147    }
148
149    pub fn print(&self, tree: &mut PrintTree) {
150        match self {
151            Fragment::Box(fragment) => fragment.borrow().print(tree),
152            Fragment::Float(fragment) => {
153                tree.new_level("Float".to_string());
154                fragment.borrow().print(tree);
155                tree.end_level();
156            },
157            Fragment::AbsoluteOrFixedPositioned(_) => {
158                tree.add_item("AbsoluteOrFixedPositioned".to_string());
159            },
160            Fragment::Positioning(fragment) => fragment.borrow().print(tree),
161            Fragment::Text(fragment) => fragment.borrow().print(tree),
162            Fragment::Image(fragment) => fragment.borrow().print(tree),
163            Fragment::IFrame(fragment) => fragment.borrow().print(tree),
164        }
165    }
166
167    pub(crate) fn scrolling_area(&self) -> PhysicalRect<Au> {
168        match self {
169            Fragment::Box(fragment) | Fragment::Float(fragment) => {
170                let fragment = fragment.borrow();
171                fragment.offset_by_containing_block(&fragment.scrollable_overflow())
172            },
173            _ => self.scrollable_overflow_for_parent(),
174        }
175    }
176
177    pub(crate) fn scrollable_overflow_for_parent(&self) -> PhysicalRect<Au> {
178        match self {
179            Fragment::Box(fragment) | Fragment::Float(fragment) => {
180                return fragment.borrow().scrollable_overflow_for_parent();
181            },
182            Fragment::AbsoluteOrFixedPositioned(_) => PhysicalRect::zero(),
183            Fragment::Positioning(fragment) => fragment.borrow().scrollable_overflow_for_parent(),
184            Fragment::Text(fragment) => fragment.borrow().rect,
185            Fragment::Image(fragment) => fragment.borrow().rect,
186            Fragment::IFrame(fragment) => fragment.borrow().rect,
187        }
188    }
189
190    pub(crate) fn calculate_scrollable_overflow_for_parent(&self) -> PhysicalRect<Au> {
191        self.calculate_scrollable_overflow();
192        self.scrollable_overflow_for_parent()
193    }
194
195    pub(crate) fn calculate_scrollable_overflow(&self) {
196        match self {
197            Fragment::Box(fragment) | Fragment::Float(fragment) => {
198                fragment.borrow_mut().calculate_scrollable_overflow()
199            },
200            Fragment::Positioning(fragment) => {
201                fragment.borrow_mut().calculate_scrollable_overflow()
202            },
203            _ => {},
204        }
205    }
206
207    pub(crate) fn cumulative_box_area_rect(&self, area: BoxAreaType) -> Option<PhysicalRect<Au>> {
208        match self {
209            Fragment::Box(fragment) | Fragment::Float(fragment) => Some(match area {
210                BoxAreaType::Content => fragment.borrow().cumulative_content_box_rect(),
211                BoxAreaType::Padding => fragment.borrow().cumulative_padding_box_rect(),
212                BoxAreaType::Border => fragment.borrow().cumulative_border_box_rect(),
213            }),
214            Fragment::Positioning(fragment) => {
215                let fragment = fragment.borrow();
216                Some(fragment.offset_by_containing_block(&fragment.rect))
217            },
218            Fragment::Text(_) |
219            Fragment::AbsoluteOrFixedPositioned(_) |
220            Fragment::Image(_) |
221            Fragment::IFrame(_) => None,
222        }
223    }
224
225    pub(crate) fn client_rect(&self) -> Rect<i32, UnknownUnit> {
226        let rect = match self {
227            Fragment::Box(fragment) | Fragment::Float(fragment) => {
228                // https://drafts.csswg.org/cssom-view/#dom-element-clienttop
229                // " If the element has no associated CSS layout box or if the
230                //   CSS layout box is inline, return zero." For this check we
231                // also explicitly ignore the list item portion of the display
232                // style.
233                let fragment = fragment.borrow();
234                if fragment.is_inline_box() {
235                    return Rect::zero();
236                }
237
238                if fragment.is_table_wrapper() {
239                    // For tables the border actually belongs to the table grid box,
240                    // so we need to include it in the dimension of the table wrapper box.
241                    let mut rect = fragment.border_rect();
242                    rect.origin = PhysicalPoint::zero();
243                    rect
244                } else {
245                    let mut rect = fragment.padding_rect();
246                    rect.origin = PhysicalPoint::new(fragment.border.left, fragment.border.top);
247                    rect
248                }
249            },
250            _ => return Rect::zero(),
251        }
252        .to_untyped();
253
254        let rect = Rect::new(
255            Point2D::new(rect.origin.x.to_f32_px(), rect.origin.y.to_f32_px()),
256            Size2D::new(rect.size.width.to_f32_px(), rect.size.height.to_f32_px()),
257        );
258        rect.round().to_i32()
259    }
260
261    pub(crate) fn find<T>(
262        &self,
263        manager: &ContainingBlockManager<PhysicalRect<Au>>,
264        level: usize,
265        process_func: &mut impl FnMut(&Fragment, usize, &PhysicalRect<Au>) -> Option<T>,
266    ) -> Option<T> {
267        let containing_block = manager.get_containing_block_for_fragment(self);
268        if let Some(result) = process_func(self, level, containing_block) {
269            return Some(result);
270        }
271
272        match self {
273            Fragment::Box(fragment) | Fragment::Float(fragment) => {
274                let fragment = fragment.borrow();
275                let content_rect = fragment
276                    .content_rect
277                    .translate(containing_block.origin.to_vector());
278                let padding_rect = fragment
279                    .padding_rect()
280                    .translate(containing_block.origin.to_vector());
281                let new_manager = if fragment
282                    .style
283                    .establishes_containing_block_for_all_descendants(fragment.base.flags)
284                {
285                    manager.new_for_absolute_and_fixed_descendants(&content_rect, &padding_rect)
286                } else if fragment
287                    .style
288                    .establishes_containing_block_for_absolute_descendants(fragment.base.flags)
289                {
290                    manager.new_for_absolute_descendants(&content_rect, &padding_rect)
291                } else {
292                    manager.new_for_non_absolute_descendants(&content_rect)
293                };
294
295                fragment
296                    .children
297                    .iter()
298                    .find_map(|child| child.find(&new_manager, level + 1, process_func))
299            },
300            Fragment::Positioning(fragment) => {
301                let fragment = fragment.borrow();
302                let content_rect = fragment.rect.translate(containing_block.origin.to_vector());
303                let new_manager = manager.new_for_non_absolute_descendants(&content_rect);
304                fragment
305                    .children
306                    .iter()
307                    .find_map(|child| child.find(&new_manager, level + 1, process_func))
308            },
309            _ => None,
310        }
311    }
312
313    pub(crate) fn repair_style(&self, style: &ServoArc<ComputedValues>) {
314        match self {
315            Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
316                box_fragment.borrow_mut().style = style.clone()
317            },
318            Fragment::Positioning(positioning_fragment) => {
319                positioning_fragment.borrow_mut().style = style.clone();
320            },
321            Fragment::AbsoluteOrFixedPositioned(positioned_fragment) => {
322                if let Some(ref fragment) = positioned_fragment.borrow().fragment {
323                    fragment.repair_style(style);
324                }
325            },
326            Fragment::Text(..) => unreachable!("Should never try to repair style of TextFragment"),
327            Fragment::Image(image_fragment) => image_fragment.borrow_mut().style = style.clone(),
328            Fragment::IFrame(iframe_fragment) => iframe_fragment.borrow_mut().style = style.clone(),
329        }
330    }
331
332    pub(crate) fn retrieve_box_fragment(&self) -> Option<&ArcRefCell<BoxFragment>> {
333        match self {
334            Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => Some(box_fragment),
335            _ => None,
336        }
337    }
338}
339
340impl TextFragment {
341    pub fn print(&self, tree: &mut PrintTree) {
342        tree.add_item(format!(
343            "Text num_glyphs={} box={:?}",
344            self.glyphs
345                .iter()
346                .map(|glyph_store| glyph_store.len().0)
347                .sum::<isize>(),
348            self.rect,
349        ));
350    }
351
352    pub fn has_selection(&self) -> bool {
353        self.selection_range.is_some()
354    }
355}
356
357impl ImageFragment {
358    pub fn print(&self, tree: &mut PrintTree) {
359        tree.add_item(format!(
360            "Image\
361                \nrect={:?}",
362            self.rect
363        ));
364    }
365}
366
367impl IFrameFragment {
368    pub fn print(&self, tree: &mut PrintTree) {
369        tree.add_item(format!(
370            "IFrame\
371                \npipeline={:?} rect={:?}",
372            self.pipeline_id, self.rect
373        ));
374    }
375}
376
377impl CollapsedBlockMargins {
378    pub fn from_margin(margin: &LogicalSides<Au>) -> Self {
379        Self {
380            collapsed_through: false,
381            start: CollapsedMargin::new(margin.block_start),
382            end: CollapsedMargin::new(margin.block_end),
383        }
384    }
385
386    pub fn zero() -> Self {
387        Self {
388            collapsed_through: false,
389            start: CollapsedMargin::zero(),
390            end: CollapsedMargin::zero(),
391        }
392    }
393}
394
395impl CollapsedMargin {
396    pub fn zero() -> Self {
397        Self {
398            max_positive: Au::zero(),
399            min_negative: Au::zero(),
400        }
401    }
402
403    pub fn new(margin: Au) -> Self {
404        Self {
405            max_positive: margin.max(Au::zero()),
406            min_negative: margin.min(Au::zero()),
407        }
408    }
409
410    pub fn adjoin(&self, other: &Self) -> Self {
411        Self {
412            max_positive: self.max_positive.max(other.max_positive),
413            min_negative: self.min_negative.min(other.min_negative),
414        }
415    }
416
417    pub fn adjoin_assign(&mut self, other: &Self) {
418        *self = self.adjoin(other);
419    }
420
421    pub fn solve(&self) -> Au {
422        self.max_positive + self.min_negative
423    }
424}