layout/display_list/
paint_timing_handler.rs1use euclid::Rect;
6use paint_api::largest_contentful_paint_candidate::{LCPCandidate, LCPCandidateID};
7use servo_geometry::{FastLayoutTransform, au_rect_to_f32_rect, f32_rect_to_au_rect};
8use style_traits::CSSPixel;
9use webrender_api::units::{LayoutRect, LayoutSize};
10
11use crate::query::transform_au_rectangle;
12
13pub(crate) struct PaintTimingHandler {
14 lcp_size: f32,
16 lcp_candidate: Option<LCPCandidate>,
18 viewport_rect: LayoutRect,
20 lcp_candidate_updated: bool,
23}
24
25impl PaintTimingHandler {
26 pub(crate) fn new(viewport_size: LayoutSize) -> Self {
27 Self {
28 lcp_size: 0.0,
29 lcp_candidate: None,
30 viewport_rect: LayoutRect::from_size(viewport_size),
31 lcp_candidate_updated: false,
32 }
33 }
34
35 pub(crate) fn check_bounding_rect(&self, bounds: LayoutRect, clip_rect: LayoutRect) -> bool {
37 let clipped_rect = bounds
38 .intersection(&clip_rect)
39 .unwrap_or(LayoutRect::zero())
40 .to_rect();
41
42 let bounding_rect = clipped_rect
43 .intersection(&self.viewport_rect.to_rect().cast_unit())
44 .unwrap_or(Rect::zero());
45
46 !bounding_rect.is_empty()
47 }
48
49 fn calculate_intersection_rect(
50 &self,
51 bounds: LayoutRect,
52 clip_rect: LayoutRect,
53 transform: FastLayoutTransform,
54 ) -> Rect<f32, CSSPixel> {
55 let clipped_rect = bounds
56 .intersection(&clip_rect)
57 .unwrap_or(LayoutRect::zero());
58
59 let transformed_rect = transform_au_rectangle(
60 f32_rect_to_au_rect(clipped_rect.to_rect().cast_unit()),
61 transform,
62 )
63 .unwrap_or_default();
64
65 let transformed_rect = au_rect_to_f32_rect(transformed_rect);
66
67 let intersection_rect =
68 transformed_rect.intersection(&self.viewport_rect.to_rect().cast_unit());
69
70 intersection_rect.unwrap_or(Rect::zero())
71 }
72
73 pub(crate) fn update_lcp_candidate(
74 &mut self,
75 lcp_candidate_id: LCPCandidateID,
76 bounds: LayoutRect,
77 clip_rect: LayoutRect,
78 transform: FastLayoutTransform,
79 ) {
80 let intersection_rect = self.calculate_intersection_rect(bounds, clip_rect, transform);
83
84 let size = intersection_rect.size.width * intersection_rect.size.height;
86
87 if size <= self.lcp_size {
89 return;
90 }
91
92 self.lcp_candidate = Some(LCPCandidate::new(lcp_candidate_id, size as usize));
94 self.lcp_size = size;
95 self.lcp_candidate_updated = true;
96 }
97
98 pub(crate) fn did_lcp_candidate_update(&self) -> bool {
99 self.lcp_candidate_updated
100 }
101
102 pub(crate) fn unset_lcp_candidate_updated(&mut self) {
103 self.lcp_candidate_updated = false;
104 }
105
106 pub(crate) fn largest_contentful_paint_candidate(&self) -> Option<LCPCandidate> {
107 self.lcp_candidate
108 }
109}