paint/
largest_contentful_paint_calculator.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 base::cross_process_instant::CrossProcessInstant;
6use base::id::WebViewId;
7use paint_api::largest_contentful_paint_candidate::{LCPCandidate, LargestContentfulPaint};
8use rustc_hash::{FxHashMap, FxHashSet};
9use webrender_api::PipelineId;
10
11/// Holds the [`LargestContentfulPaintsContainer`] for each pipeline.
12#[derive(Default)]
13pub(crate) struct LargestContentfulPaintCalculator {
14    lcp_containers: FxHashMap<PipelineId, LargestContentfulPaintsContainer>,
15    disabled_webviews: FxHashSet<WebViewId>,
16}
17
18impl LargestContentfulPaintCalculator {
19    pub(crate) fn new() -> Self {
20        Self {
21            lcp_containers: Default::default(),
22            disabled_webviews: Default::default(),
23        }
24    }
25
26    pub(crate) fn append_lcp_candidate(
27        &mut self,
28        candidate: LCPCandidate,
29        pipeline_id: PipelineId,
30        webview_id: &WebViewId,
31    ) {
32        assert!(self.enabled_for_webview(webview_id));
33        self.lcp_containers
34            .entry(pipeline_id)
35            .or_default()
36            .lcp_candidates
37            .push(candidate);
38    }
39
40    pub(crate) fn enabled_for_webview(&self, webview_id: &WebViewId) -> bool {
41        !self.disabled_webviews.contains(webview_id)
42    }
43
44    pub(crate) fn remove_lcp_candidates_for_pipeline(&mut self, pipeline_id: &PipelineId) {
45        self.lcp_containers.remove(pipeline_id);
46    }
47
48    pub(crate) fn calculate_largest_contentful_paint(
49        &mut self,
50        paint_time: CrossProcessInstant,
51        pipeline_id: PipelineId,
52    ) -> Option<LargestContentfulPaint> {
53        self.lcp_containers
54            .get_mut(&pipeline_id)
55            .and_then(|container| container.calculate_largest_contentful_paint(paint_time))
56    }
57
58    /// <https://www.w3.org/TR/largest-contentful-paint/#limitations>
59    pub(crate) fn disable_for_webview(&mut self, webview_id: WebViewId) {
60        self.disabled_webviews.insert(webview_id);
61    }
62
63    pub(crate) fn enable_for_webview(&mut self, webview_id: &WebViewId) {
64        self.disabled_webviews.remove(webview_id);
65    }
66}
67
68/// Holds the LCP candidates and the latest LCP for a specific pipeline.
69#[derive(Default)]
70struct LargestContentfulPaintsContainer {
71    /// List of candidates for Largest Contentful Paint in this pipeline.
72    lcp_candidates: Vec<LCPCandidate>,
73    /// The most recent Largest Contentful Paint, if any.
74    latest_lcp: Option<LargestContentfulPaint>,
75}
76
77impl LargestContentfulPaintsContainer {
78    fn calculate_largest_contentful_paint(
79        &mut self,
80        paint_time: CrossProcessInstant,
81    ) -> Option<LargestContentfulPaint> {
82        if self.lcp_candidates.is_empty() {
83            return self.latest_lcp;
84        }
85
86        let candidates = std::mem::take(&mut self.lcp_candidates);
87        if let Some(max_candidate) = candidates.into_iter().max_by_key(|c| c.area) {
88            match self.latest_lcp {
89                None => {
90                    self.latest_lcp = Some(LargestContentfulPaint::from(max_candidate, paint_time));
91                },
92                Some(ref latest_lcp) => {
93                    if max_candidate.area > latest_lcp.area {
94                        self.latest_lcp =
95                            Some(LargestContentfulPaint::from(max_candidate, paint_time));
96                    }
97                },
98            }
99        }
100
101        self.latest_lcp
102    }
103}