1use std::sync::Arc;
6
7use app_units::Au;
8use atomic_refcell::{AtomicRef, AtomicRefMut};
9use base::id::PipelineId;
10use base::print_tree::PrintTree;
11use euclid::{Point2D, Rect, Size2D};
12use fonts::{FontMetrics, GlyphStore};
13use layout_api::BoxAreaType;
14use malloc_size_of_derive::MallocSizeOf;
15use style::Zero;
16use style_traits::CSSPixel;
17use webrender_api::{FontInstanceKey, ImageKey};
18
19use super::{
20 BaseFragment, BoxFragment, ContainingBlockManager, HoistedSharedFragment, PositioningFragment,
21 Tag,
22};
23use crate::SharedStyle;
24use crate::cell::ArcRefCell;
25use crate::flow::inline::line::TextRunOffsets;
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 Float(ArcRefCell<BoxFragment>),
38 Positioning(ArcRefCell<PositioningFragment>),
39 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 selected_style: SharedStyle,
69 #[conditional_malloc_size_of]
70 pub font_metrics: Arc<FontMetrics>,
71 pub font_key: FontInstanceKey,
72 #[conditional_malloc_size_of]
73 pub glyphs: Vec<Arc<GlyphStore>>,
74 pub justification_adjustment: Au,
76 pub offsets: Option<Box<TextRunOffsets>>,
79}
80
81#[derive(MallocSizeOf)]
82pub(crate) struct ImageFragment {
83 pub base: BaseFragment,
84 pub clip: PhysicalRect<Au>,
85 pub image_key: Option<ImageKey>,
86 pub showing_broken_image_icon: bool,
87}
88
89#[derive(MallocSizeOf)]
90pub(crate) struct IFrameFragment {
91 pub base: BaseFragment,
92 pub pipeline_id: PipelineId,
93}
94
95impl Fragment {
96 pub fn base<'a>(&'a self) -> Option<AtomicRef<'a, BaseFragment>> {
97 Some(match self {
98 Fragment::Box(fragment) => AtomicRef::map(fragment.borrow(), |fragment| &fragment.base),
99 Fragment::Text(fragment) => {
100 AtomicRef::map(fragment.borrow(), |fragment| &fragment.base)
101 },
102 Fragment::AbsoluteOrFixedPositioned(_) => return None,
103 Fragment::Positioning(fragment) => {
104 AtomicRef::map(fragment.borrow(), |fragment| &fragment.base)
105 },
106 Fragment::Image(fragment) => {
107 AtomicRef::map(fragment.borrow(), |fragment| &fragment.base)
108 },
109 Fragment::IFrame(fragment) => {
110 AtomicRef::map(fragment.borrow(), |fragment| &fragment.base)
111 },
112 Fragment::Float(fragment) => {
113 AtomicRef::map(fragment.borrow(), |fragment| &fragment.base)
114 },
115 })
116 }
117
118 pub fn base_mut<'a>(&'a self) -> Option<AtomicRefMut<'a, BaseFragment>> {
119 Some(match self {
120 Fragment::Box(fragment) => {
121 AtomicRefMut::map(fragment.borrow_mut(), |fragment| &mut fragment.base)
122 },
123 Fragment::Text(fragment) => {
124 AtomicRefMut::map(fragment.borrow_mut(), |fragment| &mut fragment.base)
125 },
126 Fragment::AbsoluteOrFixedPositioned(_) => return None,
127 Fragment::Positioning(fragment) => {
128 AtomicRefMut::map(fragment.borrow_mut(), |fragment| &mut fragment.base)
129 },
130 Fragment::Image(fragment) => {
131 AtomicRefMut::map(fragment.borrow_mut(), |fragment| &mut fragment.base)
132 },
133 Fragment::IFrame(fragment) => {
134 AtomicRefMut::map(fragment.borrow_mut(), |fragment| &mut fragment.base)
135 },
136 Fragment::Float(fragment) => {
137 AtomicRefMut::map(fragment.borrow_mut(), |fragment| &mut fragment.base)
138 },
139 })
140 }
141
142 pub(crate) fn set_containing_block(&self, containing_block: &PhysicalRect<Au>) {
143 match self {
144 Fragment::Box(box_fragment) => box_fragment
145 .borrow_mut()
146 .set_containing_block(containing_block),
147 Fragment::Float(float_fragment) => float_fragment
148 .borrow_mut()
149 .set_containing_block(containing_block),
150 Fragment::Positioning(positioning_fragment) => positioning_fragment
151 .borrow_mut()
152 .set_containing_block(containing_block),
153 Fragment::AbsoluteOrFixedPositioned(hoisted_shared_fragment) => {
154 if let Some(ref fragment) = hoisted_shared_fragment.borrow().fragment {
155 fragment.set_containing_block(containing_block);
156 }
157 },
158 Fragment::Text(_) => {},
159 Fragment::Image(_) => {},
160 Fragment::IFrame(_) => {},
161 }
162 }
163
164 pub fn tag(&self) -> Option<Tag> {
165 self.base().and_then(|base| base.tag)
166 }
167
168 pub fn print(&self, tree: &mut PrintTree) {
169 match self {
170 Fragment::Box(fragment) => fragment.borrow().print(tree),
171 Fragment::Float(fragment) => {
172 tree.new_level("Float".to_string());
173 fragment.borrow().print(tree);
174 tree.end_level();
175 },
176 Fragment::AbsoluteOrFixedPositioned(_) => {
177 tree.add_item("AbsoluteOrFixedPositioned".to_string());
178 },
179 Fragment::Positioning(fragment) => fragment.borrow().print(tree),
180 Fragment::Text(fragment) => fragment.borrow().print(tree),
181 Fragment::Image(fragment) => fragment.borrow().print(tree),
182 Fragment::IFrame(fragment) => fragment.borrow().print(tree),
183 }
184 }
185
186 pub(crate) fn scrolling_area(&self) -> PhysicalRect<Au> {
187 match self {
188 Fragment::Box(fragment) | Fragment::Float(fragment) => {
189 let fragment = fragment.borrow();
190 fragment.offset_by_containing_block(&fragment.scrollable_overflow())
191 },
192 _ => self.scrollable_overflow_for_parent(),
193 }
194 }
195
196 pub(crate) fn scrollable_overflow_for_parent(&self) -> PhysicalRect<Au> {
197 match self {
198 Fragment::Box(fragment) | Fragment::Float(fragment) => {
199 return fragment.borrow().scrollable_overflow_for_parent();
200 },
201 Fragment::Positioning(fragment) => fragment.borrow().scrollable_overflow_for_parent(),
202 Fragment::AbsoluteOrFixedPositioned(_) |
203 Fragment::Text(..) |
204 Fragment::Image(..) |
205 Fragment::IFrame(..) => self.base().map(|base| base.rect).unwrap_or_default(),
206 }
207 }
208
209 pub(crate) fn calculate_scrollable_overflow_for_parent(&self) -> PhysicalRect<Au> {
210 self.calculate_scrollable_overflow();
211 self.scrollable_overflow_for_parent()
212 }
213
214 pub(crate) fn calculate_scrollable_overflow(&self) {
215 match self {
216 Fragment::Box(fragment) | Fragment::Float(fragment) => {
217 fragment.borrow_mut().calculate_scrollable_overflow()
218 },
219 Fragment::Positioning(fragment) => {
220 fragment.borrow_mut().calculate_scrollable_overflow()
221 },
222 _ => {},
223 }
224 }
225
226 pub(crate) fn cumulative_box_area_rect(&self, area: BoxAreaType) -> Option<PhysicalRect<Au>> {
227 match self {
228 Fragment::Box(fragment) | Fragment::Float(fragment) => Some(match area {
229 BoxAreaType::Content => fragment.borrow().cumulative_content_box_rect(),
230 BoxAreaType::Padding => fragment.borrow().cumulative_padding_box_rect(),
231 BoxAreaType::Border => fragment.borrow().cumulative_border_box_rect(),
232 }),
233 Fragment::Positioning(fragment) => {
234 let fragment = fragment.borrow();
235 Some(fragment.offset_by_containing_block(&fragment.base.rect))
236 },
237 Fragment::Text(_) |
238 Fragment::AbsoluteOrFixedPositioned(_) |
239 Fragment::Image(_) |
240 Fragment::IFrame(_) => None,
241 }
242 }
243
244 pub(crate) fn client_rect(&self) -> Rect<i32, CSSPixel> {
245 let rect = match self {
246 Fragment::Box(fragment) | Fragment::Float(fragment) => {
247 let fragment = fragment.borrow();
253 if fragment.is_inline_box() {
254 return Rect::zero();
255 }
256
257 if fragment.is_table_wrapper() {
258 let mut rect = fragment.border_rect();
261 rect.origin = PhysicalPoint::zero();
262 rect
263 } else {
264 let mut rect = fragment.padding_rect();
265 rect.origin = PhysicalPoint::new(fragment.border.left, fragment.border.top);
266 rect
267 }
268 },
269 _ => return Rect::zero(),
270 };
271
272 let rect = Rect::new(
273 Point2D::new(rect.origin.x.to_f32_px(), rect.origin.y.to_f32_px()),
274 Size2D::new(rect.size.width.to_f32_px(), rect.size.height.to_f32_px()),
275 );
276 rect.round().to_i32()
277 }
278
279 pub(crate) fn children<'a>(&'a self) -> Option<AtomicRef<'a, Vec<Fragment>>> {
280 match self {
281 Fragment::Box(fragment) | Fragment::Float(fragment) => {
282 let fragment = fragment.borrow();
283 Some(AtomicRef::map(fragment, |fragment| &fragment.children))
284 },
285 Fragment::Positioning(fragment) => {
286 let fragment = fragment.borrow();
287 Some(AtomicRef::map(fragment, |fragment| &fragment.children))
288 },
289 _ => None,
290 }
291 }
292
293 pub(crate) fn find<T>(
294 &self,
295 manager: &ContainingBlockManager<PhysicalRect<Au>>,
296 level: usize,
297 process_func: &mut impl FnMut(&Fragment, usize, &PhysicalRect<Au>) -> Option<T>,
298 ) -> Option<T> {
299 let containing_block = manager.get_containing_block_for_fragment(self);
300 if let Some(result) = process_func(self, level, containing_block) {
301 return Some(result);
302 }
303
304 match self {
305 Fragment::Box(fragment) | Fragment::Float(fragment) => {
306 let fragment = fragment.borrow();
307 let style = fragment.style();
308 let content_rect = fragment
309 .content_rect()
310 .translate(containing_block.origin.to_vector());
311 let padding_rect = fragment
312 .padding_rect()
313 .translate(containing_block.origin.to_vector());
314 let new_manager = if style
315 .establishes_containing_block_for_all_descendants(fragment.base.flags)
316 {
317 manager.new_for_absolute_and_fixed_descendants(&content_rect, &padding_rect)
318 } else if style
319 .establishes_containing_block_for_absolute_descendants(fragment.base.flags)
320 {
321 manager.new_for_absolute_descendants(&content_rect, &padding_rect)
322 } else {
323 manager.new_for_non_absolute_descendants(&content_rect)
324 };
325
326 fragment
327 .children
328 .iter()
329 .find_map(|child| child.find(&new_manager, level + 1, process_func))
330 },
331 Fragment::Positioning(fragment) => {
332 let fragment = fragment.borrow();
333 let content_rect = fragment
334 .base
335 .rect
336 .translate(containing_block.origin.to_vector());
337 let new_manager = manager.new_for_non_absolute_descendants(&content_rect);
338 fragment
339 .children
340 .iter()
341 .find_map(|child| child.find(&new_manager, level + 1, process_func))
342 },
343 _ => None,
344 }
345 }
346
347 pub(crate) fn retrieve_box_fragment(&self) -> Option<&ArcRefCell<BoxFragment>> {
348 match self {
349 Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => Some(box_fragment),
350 _ => None,
351 }
352 }
353}
354
355impl TextFragment {
356 pub fn print(&self, tree: &mut PrintTree) {
357 tree.add_item(format!(
358 "Text num_glyphs={} box={:?}",
359 self.glyphs
360 .iter()
361 .map(|glyph_store| glyph_store.len())
362 .sum::<usize>(),
363 self.base.rect
364 ));
365 }
366
367 pub(crate) fn distance_to_point_for_glyph_offset(
371 &self,
372 point_in_fragment: Point2D<Au, CSSPixel>,
373 ) -> Option<Au> {
374 let rect = &self.base.rect;
377 if point_in_fragment.y < Au::zero() || point_in_fragment.y > rect.height() {
378 return None;
379 }
380 if point_in_fragment.x < Au::zero() {
382 return None;
383 }
384 Some(point_in_fragment.x - rect.width().max(Au::zero()))
385 }
386
387 pub(crate) fn character_offset(&self, point_in_fragment: Point2D<Au, CSSPixel>) -> usize {
390 let Some(offsets) = self.offsets.as_ref() else {
391 return 0;
392 };
393
394 let mut current_character = offsets.character_range.start;
395 let mut current_offset = Au::zero();
396 for glyph_store in &self.glyphs {
397 for glyph in glyph_store.glyphs() {
398 let mut advance = glyph.advance();
399 if glyph.char_is_word_separator() {
400 advance += self.justification_adjustment;
401 }
402 if current_offset + advance.scale_by(0.5) >= point_in_fragment.x {
403 return current_character;
404 }
405 current_offset += advance;
406 current_character += glyph.character_count();
407 }
408 }
409
410 current_character
411 }
412}
413
414impl ImageFragment {
415 pub fn print(&self, tree: &mut PrintTree) {
416 tree.add_item(format!(
417 "Image\
418 \nrect={:?}",
419 self.base.rect
420 ));
421 }
422}
423
424impl IFrameFragment {
425 pub fn print(&self, tree: &mut PrintTree) {
426 tree.add_item(format!(
427 "IFrame\
428 \npipeline={:?} rect={:?}",
429 self.pipeline_id, self.base.rect
430 ));
431 }
432}
433
434impl CollapsedBlockMargins {
435 pub fn from_margin(margin: &LogicalSides<Au>) -> Self {
436 Self {
437 collapsed_through: false,
438 start: CollapsedMargin::new(margin.block_start),
439 end: CollapsedMargin::new(margin.block_end),
440 }
441 }
442
443 pub fn zero() -> Self {
444 Self {
445 collapsed_through: false,
446 start: CollapsedMargin::zero(),
447 end: CollapsedMargin::zero(),
448 }
449 }
450}
451
452impl CollapsedMargin {
453 pub fn zero() -> Self {
454 Self {
455 max_positive: Au::zero(),
456 min_negative: Au::zero(),
457 }
458 }
459
460 pub fn new(margin: Au) -> Self {
461 Self {
462 max_positive: margin.max(Au::zero()),
463 min_negative: margin.min(Au::zero()),
464 }
465 }
466
467 pub fn adjoin(&self, other: &Self) -> Self {
468 Self {
469 max_positive: self.max_positive.max(other.max_positive),
470 min_negative: self.min_negative.min(other.min_negative),
471 }
472 }
473
474 pub fn adjoin_assign(&mut self, other: &Self) {
475 *self = self.adjoin(other);
476 }
477
478 pub fn solve(&self) -> Au {
479 self.max_positive + self.min_negative
480 }
481}