layout/display_list/
largest_contenful_paint_candidate_collector.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 compositing_traits::largest_contentful_paint_candidate::{
6    LCPCandidate, LCPCandidateID, LargestContentfulPaintType,
7};
8use euclid::Rect;
9use servo_geometry::{FastLayoutTransform, au_rect_to_f32_rect, f32_rect_to_au_rect};
10use webrender_api::units::{LayoutRect, LayoutSize};
11
12use crate::query::transform_au_rectangle;
13
14pub(crate) struct LargestContentfulPaintCandidateCollector {
15    /// The LCP candidate, it may be a image or text.
16    pub lcp_candidate: Option<LCPCandidate>,
17    /// The rect of viewport.
18    pub viewport_rect: LayoutRect,
19    /// Flag to indicate if there is an update to LCP candidate.
20    /// This is used to avoid sending duplicate LCP candidates to the compositor.
21    pub did_lcp_candidate_update: bool,
22}
23
24impl LargestContentfulPaintCandidateCollector {
25    pub fn new(viewport_size: LayoutSize) -> Self {
26        Self {
27            lcp_candidate: None,
28            viewport_rect: LayoutRect::from_size(viewport_size),
29            did_lcp_candidate_update: true,
30        }
31    }
32
33    pub fn add_or_update_candidate(
34        &mut self,
35        lcp_type: LargestContentfulPaintType,
36        lcp_candidate_id: LCPCandidateID,
37        clip_rect: LayoutRect,
38        bounds: LayoutRect,
39        transform: FastLayoutTransform,
40    ) {
41        let clipped_rect = bounds
42            .intersection(&clip_rect)
43            .unwrap_or(LayoutRect::zero());
44        let transformed_rect = transform_au_rectangle(
45            f32_rect_to_au_rect(clipped_rect.to_rect().to_untyped()),
46            transform,
47        )
48        .unwrap_or_default();
49        let transformed_rect = au_rect_to_f32_rect(transformed_rect);
50        let visual_rect = transformed_rect
51            .intersection(&self.viewport_rect.to_rect().to_untyped())
52            .unwrap_or(Rect::zero());
53        let area = visual_rect.size.width * visual_rect.size.height;
54        if area == 0.0 {
55            return;
56        }
57
58        self.update_candidate(LCPCandidate::new(lcp_candidate_id, lcp_type, area as usize));
59    }
60
61    fn update_candidate(&mut self, candidate: LCPCandidate) {
62        if let Some(ref mut latest_candidate) = self.lcp_candidate {
63            if candidate.area > latest_candidate.area {
64                *latest_candidate = candidate;
65                self.did_lcp_candidate_update = true;
66            }
67        } else {
68            self.lcp_candidate = Some(candidate);
69        }
70    }
71
72    pub fn largest_contentful_paint(&self) -> Option<LCPCandidate> {
73        self.lcp_candidate
74    }
75}