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