use euclid::Rect;
use paint_api::largest_contentful_paint_candidate::{LCPCandidate, LCPCandidateID};
use servo_geometry::{FastLayoutTransform, au_rect_to_f32_rect, f32_rect_to_au_rect};
use servo_url::ServoUrl;
use style_traits::CSSPixel;
use webrender_api::units::{LayoutRect, LayoutSize};
use crate::fragment_tree::Tag;
use crate::query::transform_au_rectangle;
pub(crate) struct PaintTimingHandler {
lcp_size: f32,
lcp_candidate: Option<LCPCandidate>,
viewport_rect: LayoutRect,
lcp_candidate_updated: bool,
}
impl PaintTimingHandler {
pub(crate) fn new(viewport_size: LayoutSize) -> Self {
Self {
lcp_size: 0.0,
lcp_candidate: None,
viewport_rect: LayoutRect::from_size(viewport_size),
lcp_candidate_updated: false,
}
}
pub(crate) fn check_bounding_rect(&self, bounds: LayoutRect, clip_rect: LayoutRect) -> bool {
let clipped_rect = bounds
.intersection(&clip_rect)
.unwrap_or(LayoutRect::zero())
.to_rect();
let bounding_rect = clipped_rect
.intersection(&self.viewport_rect.to_rect().cast_unit())
.unwrap_or(Rect::zero());
!bounding_rect.is_empty()
}
fn calculate_intersection_rect(
&self,
bounds: LayoutRect,
clip_rect: LayoutRect,
transform: FastLayoutTransform,
) -> Rect<f32, CSSPixel> {
let clipped_rect = bounds
.intersection(&clip_rect)
.unwrap_or(LayoutRect::zero());
let transformed_rect = transform_au_rectangle(
f32_rect_to_au_rect(clipped_rect.to_rect().cast_unit()),
transform,
)
.unwrap_or_default();
let transformed_rect = au_rect_to_f32_rect(transformed_rect);
let intersection_rect =
transformed_rect.intersection(&self.viewport_rect.to_rect().cast_unit());
intersection_rect.unwrap_or(Rect::zero())
}
pub(crate) fn update_lcp_candidate(
&mut self,
tag: Option<Tag>,
bounds: LayoutRect,
clip_rect: LayoutRect,
transform: FastLayoutTransform,
url: Option<ServoUrl>,
) {
let intersection_rect = self.calculate_intersection_rect(bounds, clip_rect, transform);
let size = intersection_rect.size.width * intersection_rect.size.height;
if size <= self.lcp_size {
return;
}
let id = tag
.map(|tag| LCPCandidateID(tag.node.id()))
.unwrap_or(LCPCandidateID(0));
self.lcp_candidate = Some(LCPCandidate::new(id, size as usize, url));
self.lcp_size = size;
self.lcp_candidate_updated = true;
}
pub(crate) fn did_lcp_candidate_update(&self) -> bool {
self.lcp_candidate_updated
}
pub(crate) fn unset_lcp_candidate_updated(&mut self) {
self.lcp_candidate_updated = false;
}
pub(crate) fn largest_contentful_paint_candidate(&self) -> Option<LCPCandidate> {
self.lcp_candidate.clone()
}
}