paint/
painter.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 std::cell::Cell;
6use std::collections::hash_map::Entry;
7use std::rc::Rc;
8use std::sync::Arc;
9
10use base::Epoch;
11use base::cross_process_instant::CrossProcessInstant;
12use base::generic_channel::{GenericReceiver, GenericSharedMemory};
13use base::id::{PainterId, PipelineId, WebViewId};
14use constellation_traits::{EmbedderToConstellationMessage, PaintMetricEvent};
15use crossbeam_channel::Sender;
16use dpi::PhysicalSize;
17use embedder_traits::{
18    InputEvent, InputEventAndId, InputEventId, InputEventResult, PaintHitTestResult,
19    ScreenshotCaptureError, Scroll, ViewportDetails, WebViewPoint, WebViewRect,
20};
21use euclid::{Point2D, Rect, Scale, Size2D};
22use gleam::gl::RENDERER;
23use image::RgbaImage;
24use log::{debug, error, info, warn};
25use media::WindowGLContext;
26use paint_api::display_list::{PaintDisplayListInfo, ScrollType};
27use paint_api::largest_contentful_paint_candidate::LCPCandidate;
28use paint_api::rendering_context::RenderingContext;
29use paint_api::viewport_description::ViewportDescription;
30use paint_api::{
31    ImageUpdate, PipelineExitSource, SendableFrameTree, SerializableDisplayListPayload,
32    SerializableImageData, WebRenderExternalImageHandlers, WebRenderImageHandlerType, WebViewTrait,
33};
34use profile_traits::time::{ProfilerCategory, ProfilerChan};
35use profile_traits::time_profile;
36use rustc_hash::{FxHashMap, FxHashSet};
37use servo_config::{opts, pref};
38use servo_geometry::DeviceIndependentPixel;
39use smallvec::SmallVec;
40use style_traits::CSSPixel;
41use webrender::{
42    MemoryReport, ONE_TIME_USAGE_HINT, RenderApi, ShaderPrecacheFlags, Transaction, UploadMethod,
43};
44use webrender_api::units::{
45    DevicePixel, DevicePoint, LayoutPoint, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D,
46    WorldPoint,
47};
48use webrender_api::{
49    self, BuiltDisplayList, BuiltDisplayListDescriptor, ColorF, DirtyRect, DisplayListPayload,
50    DocumentId, Epoch as WebRenderEpoch, ExternalScrollId, FontInstanceFlags, FontInstanceKey,
51    FontInstanceOptions, FontKey, FontVariation, ImageData, ImageKey, NativeFontHandle,
52    PipelineId as WebRenderPipelineId, PropertyBinding, ReferenceFrameKind, RenderReasons,
53    SampledScrollOffset, SpaceAndClipInfo, SpatialId, TransformStyle,
54};
55use wr_malloc_size_of::MallocSizeOfOps;
56
57use crate::Paint;
58use crate::largest_contentful_paint_calculator::LargestContentfulPaintCalculator;
59use crate::paint::{RepaintReason, WebRenderDebugOption};
60use crate::refresh_driver::{AnimationRefreshDriverObserver, BaseRefreshDriver};
61use crate::render_notifier::RenderNotifier;
62use crate::screenshot::ScreenshotTaker;
63use crate::webrender_external_images::WebGLExternalImages;
64use crate::webview_renderer::{PinchZoomResult, ScrollResult, UnknownWebView, WebViewRenderer};
65
66/// A [`Painter`] is responsible for all of the painting to a particular [`RenderingContext`].
67/// This holds all of the WebRender specific data structures and state necessary for painting
68/// and handling events that happen to `WebView`s that use a particular [`RenderingContext`].
69/// Notable is that a [`Painter`] might be responsible for painting more than a single
70/// [`WebView`] as long as they share the same [`RenderingContext`].
71///
72/// Each [`Painter`] also has its own [`RefreshDriver`] as well, which may be shared with
73/// other [`Painter`]s. It's up to the embedder to decide which [`RefreshDriver`]s are associated
74/// with a particular [`RenderingContext`].
75pub(crate) struct Painter {
76    /// The [`RenderingContext`] instance that webrender targets, which is the viewport.
77    pub(crate) rendering_context: Rc<dyn RenderingContext>,
78
79    /// The ID of this painter.
80    pub(crate) painter_id: PainterId,
81
82    /// Our [`WebViewRenderer`]s, one for every `WebView`.
83    pub(crate) webview_renderers: FxHashMap<WebViewId, WebViewRenderer>,
84
85    /// Tracks whether or not the view needs to be repainted.
86    pub(crate) needs_repaint: Cell<RepaintReason>,
87
88    /// The number of frames pending to receive from WebRender.
89    pub(crate) pending_frames: Cell<usize>,
90
91    /// The [`BaseRefreshDriver`] which manages the painting of `WebView`s during animations.
92    refresh_driver: Rc<BaseRefreshDriver>,
93
94    /// A [`RefreshDriverObserver`] for WebView content animations.
95    animation_refresh_driver_observer: Rc<AnimationRefreshDriverObserver>,
96
97    /// The WebRender [`RenderApi`] interface used to communicate with WebRender.
98    pub(crate) webrender_api: RenderApi,
99
100    /// The active webrender document.
101    pub(crate) webrender_document: DocumentId,
102
103    /// The webrender renderer.
104    pub(crate) webrender_renderer: Option<webrender::Renderer>,
105
106    /// The GL bindings for webrender
107    webrender_gl: Rc<dyn gleam::gl::Gl>,
108
109    /// The last position in the rendered view that the mouse moved over. This becomes `None`
110    /// when the mouse leaves the rendered view.
111    pub(crate) last_mouse_move_position: Option<DevicePoint>,
112
113    /// A [`ScreenshotTaker`] responsible for handling all screenshot requests.
114    pub(crate) screenshot_taker: ScreenshotTaker,
115
116    /// A [`FrameRequestDelayer`] which is used to wait for canvas image updates to
117    /// arrive before requesting a new frame, as these happen asynchronously with
118    /// `ScriptThread` display list construction.
119    pub(crate) frame_delayer: FrameDelayer,
120
121    /// The channel on which messages can be sent to the constellation.
122    embedder_to_constellation_sender: Sender<EmbedderToConstellationMessage>,
123
124    /// Calculater for largest-contentful-paint.
125    lcp_calculator: LargestContentfulPaintCalculator,
126
127    /// A cache that stores data for all animating images uploaded to WebRender. This is used
128    /// for animated images, which only need to update their offset in the data.
129    animation_image_cache: FxHashMap<ImageKey, Arc<Vec<u8>>>,
130}
131
132impl Drop for Painter {
133    fn drop(&mut self) {
134        if let Err(error) = self.rendering_context.make_current() {
135            error!("Failed to make the rendering context current: {error:?}");
136        }
137
138        self.webrender_api.stop_render_backend();
139        self.webrender_api.shut_down(true);
140
141        if let Some(renderer) = self.webrender_renderer.take() {
142            renderer.deinit();
143        }
144    }
145}
146
147impl Painter {
148    pub(crate) fn new(rendering_context: Rc<dyn RenderingContext>, paint: &Paint) -> Self {
149        let webrender_gl = rendering_context.gleam_gl_api();
150
151        // Make sure the gl context is made current.
152        if let Err(err) = rendering_context.make_current() {
153            warn!("Failed to make the rendering context current: {:?}", err);
154        }
155        debug_assert_eq!(webrender_gl.get_error(), gleam::gl::NO_ERROR,);
156
157        let id_manager = paint.webrender_external_image_id_manager();
158        let mut external_image_handlers = Box::new(WebRenderExternalImageHandlers::new(id_manager));
159
160        // Set WebRender external image handler for WebGL textures.
161        let image_handler = Box::new(WebGLExternalImages::new(
162            paint.webgl_threads(),
163            rendering_context.clone(),
164            paint.swap_chains.clone(),
165            paint.busy_webgl_contexts_map.clone(),
166        ));
167        external_image_handlers.set_handler(image_handler, WebRenderImageHandlerType::WebGl);
168
169        #[cfg(feature = "webgpu")]
170        external_image_handlers.set_handler(
171            Box::new(webgpu::WebGpuExternalImages::new(paint.webgpu_image_map())),
172            WebRenderImageHandlerType::WebGpu,
173        );
174
175        WindowGLContext::initialize_image_handler(&mut external_image_handlers);
176
177        let embedder_to_constellation_sender = paint.embedder_to_constellation_sender.clone();
178        let refresh_driver = Rc::new(BaseRefreshDriver::new(
179            paint.event_loop_waker.clone_box(),
180            rendering_context.refresh_driver(),
181        ));
182        let animation_refresh_driver_observer = Rc::new(AnimationRefreshDriverObserver::new(
183            embedder_to_constellation_sender.clone(),
184        ));
185
186        rendering_context.prepare_for_rendering();
187        let clear_color = servo_config::pref!(shell_background_color_rgba);
188        let clear_color = ColorF::new(
189            clear_color[0] as f32,
190            clear_color[1] as f32,
191            clear_color[2] as f32,
192            clear_color[3] as f32,
193        );
194
195        // Use same texture upload method as Gecko with ANGLE:
196        // https://searchfox.org/mozilla-central/source/gfx/webrender_bindings/src/bindings.rs#1215-1219
197        let upload_method = if webrender_gl.get_string(RENDERER).starts_with("ANGLE") {
198            UploadMethod::Immediate
199        } else {
200            UploadMethod::PixelBuffer(ONE_TIME_USAGE_HINT)
201        };
202        let worker_threads = std::thread::available_parallelism()
203            .map(|i| i.get())
204            .unwrap_or(pref!(threadpools_fallback_worker_num) as usize)
205            .min(pref!(threadpools_webrender_workers_max).max(1) as usize);
206        let workers = Some(Arc::new(
207            rayon::ThreadPoolBuilder::new()
208                .num_threads(worker_threads)
209                .thread_name(|idx| format!("WRWorker#{}", idx))
210                .build()
211                .expect("Unable to initialize WebRender worker pool."),
212        ));
213
214        let painter_id = PainterId::next();
215        let (mut webrender_renderer, webrender_api_sender) = webrender::create_webrender_instance(
216            webrender_gl.clone(),
217            Box::new(RenderNotifier::new(painter_id, paint.paint_proxy.clone())),
218            webrender::WebRenderOptions {
219                // We force the use of optimized shaders here because rendering is broken
220                // on Android emulators with unoptimized shaders. This is due to a known
221                // issue in the emulator's OpenGL emulation layer.
222                // See: https://github.com/servo/servo/issues/31726
223                use_optimized_shaders: true,
224                resource_override_path: opts::get().shaders_path.clone(),
225                debug_flags: webrender::DebugFlags::empty(),
226                precache_flags: if pref!(gfx_precache_shaders) {
227                    ShaderPrecacheFlags::FULL_COMPILE
228                } else {
229                    ShaderPrecacheFlags::empty()
230                },
231                enable_aa: pref!(gfx_text_antialiasing_enabled),
232                enable_subpixel_aa: pref!(gfx_subpixel_text_antialiasing_enabled),
233                allow_texture_swizzling: pref!(gfx_texture_swizzling_enabled),
234                clear_color,
235                upload_method,
236                workers,
237                size_of_op: Some(servo_allocator::usable_size),
238                // This ensures that we can use the `PainterId` as the `IdNamespace`, which allows mapping
239                // from `FontKey`, `FontInstanceKey`, and `ImageKey` back to `PainterId`.
240                namespace_alloc_by_client: true,
241                shared_font_namespace: Some(painter_id.into()),
242                ..Default::default()
243            },
244            None,
245        )
246        .expect("Unable to initialize WebRender.");
247
248        webrender_renderer.set_external_image_handler(external_image_handlers);
249
250        let webrender_api = webrender_api_sender.create_api_by_client(painter_id.into());
251        let webrender_document = webrender_api.add_document(rendering_context.size2d().to_i32());
252
253        let gl_renderer = webrender_gl.get_string(gleam::gl::RENDERER);
254        let gl_version = webrender_gl.get_string(gleam::gl::VERSION);
255        info!("Running on {gl_renderer} with OpenGL version {gl_version}");
256
257        let painter = Painter {
258            painter_id,
259            embedder_to_constellation_sender,
260            webview_renderers: Default::default(),
261            rendering_context,
262            needs_repaint: Cell::default(),
263            pending_frames: Default::default(),
264            screenshot_taker: Default::default(),
265            refresh_driver,
266            animation_refresh_driver_observer,
267            webrender_renderer: Some(webrender_renderer),
268            webrender_api,
269            webrender_document,
270            webrender_gl,
271            last_mouse_move_position: None,
272            frame_delayer: Default::default(),
273            lcp_calculator: LargestContentfulPaintCalculator::new(),
274            animation_image_cache: FxHashMap::default(),
275        };
276        painter.assert_gl_framebuffer_complete();
277        painter.clear_background();
278        painter
279    }
280
281    pub(crate) fn perform_updates(&mut self) {
282        // The WebXR thread may make a different context current
283        if let Err(err) = self.rendering_context.make_current() {
284            warn!("Failed to make the rendering context current: {:?}", err);
285        }
286
287        let mut need_zoom = false;
288        let scroll_offset_updates: Vec<_> = self
289            .webview_renderers
290            .values_mut()
291            .filter_map(|webview_renderer| {
292                let (zoom, scroll_result) = webview_renderer
293                    .process_pending_scroll_and_pinch_zoom_events(&self.webrender_api);
294                need_zoom = need_zoom || (zoom == PinchZoomResult::DidPinchZoom);
295                scroll_result
296            })
297            .collect();
298
299        self.send_zoom_and_scroll_offset_updates(need_zoom, scroll_offset_updates);
300    }
301
302    #[track_caller]
303    fn assert_no_gl_error(&self) {
304        debug_assert_eq!(self.webrender_gl.get_error(), gleam::gl::NO_ERROR);
305    }
306
307    #[track_caller]
308    fn assert_gl_framebuffer_complete(&self) {
309        debug_assert_eq!(
310            (
311                self.webrender_gl.get_error(),
312                self.webrender_gl
313                    .check_frame_buffer_status(gleam::gl::FRAMEBUFFER)
314            ),
315            (gleam::gl::NO_ERROR, gleam::gl::FRAMEBUFFER_COMPLETE)
316        );
317    }
318
319    pub(crate) fn webview_renderer(&self, webview_id: WebViewId) -> Option<&WebViewRenderer> {
320        self.webview_renderers.get(&webview_id)
321    }
322
323    pub(crate) fn webview_renderer_mut(
324        &mut self,
325        webview_id: WebViewId,
326    ) -> Option<&mut WebViewRenderer> {
327        self.webview_renderers.get_mut(&webview_id)
328    }
329
330    /// Whether or not the renderer is waiting on a frame, either because it has been sent
331    /// to WebRender and is not ready yet or because the [`FrameDelayer`] is delaying a frame
332    /// waiting for asynchronous (canvas) image updates to complete.
333    pub(crate) fn has_pending_frames(&self) -> bool {
334        self.pending_frames.get() != 0 || self.frame_delayer.pending_frame
335    }
336
337    pub(crate) fn set_needs_repaint(&self, reason: RepaintReason) {
338        let mut needs_repaint = self.needs_repaint.get();
339        needs_repaint.insert(reason);
340        self.needs_repaint.set(needs_repaint);
341    }
342
343    pub(crate) fn needs_repaint(&self) -> bool {
344        let repaint_reason = self.needs_repaint.get();
345        if repaint_reason.is_empty() {
346            return false;
347        }
348
349        !self.refresh_driver.wait_to_paint()
350    }
351
352    /// Returns true if any animation callbacks (ie `requestAnimationFrame`) are waiting for a response.
353    pub(crate) fn animation_callbacks_running(&self) -> bool {
354        self.webview_renderers
355            .values()
356            .any(WebViewRenderer::animation_callbacks_running)
357    }
358
359    pub(crate) fn animating_webviews(&self) -> Vec<WebViewId> {
360        self.webview_renderers
361            .values()
362            .filter_map(|webview_renderer| {
363                if webview_renderer.animating() {
364                    Some(webview_renderer.id)
365                } else {
366                    None
367                }
368            })
369            .collect()
370    }
371
372    pub(crate) fn send_to_constellation(&self, message: EmbedderToConstellationMessage) {
373        if let Err(error) = self.embedder_to_constellation_sender.send(message) {
374            warn!("Could not send message to constellation ({error:?})");
375        }
376    }
377
378    #[servo_tracing::instrument(skip_all)]
379    pub(crate) fn render(&mut self, time_profiler_channel: &ProfilerChan) {
380        let refresh_driver = self.refresh_driver.clone();
381        refresh_driver.notify_will_paint(self);
382
383        if let Err(error) = self.rendering_context.make_current() {
384            error!("Failed to make the rendering context current: {error:?}");
385        }
386        self.assert_no_gl_error();
387
388        self.rendering_context.prepare_for_rendering();
389
390        time_profile!(
391            ProfilerCategory::Painting,
392            None,
393            time_profiler_channel.clone(),
394            || {
395                if let Some(renderer) = self.webrender_renderer.as_mut() {
396                    renderer.update();
397                }
398
399                // Paint the scene.
400                // TODO(gw): Take notice of any errors the renderer returns!
401                self.clear_background();
402                if let Some(renderer) = self.webrender_renderer.as_mut() {
403                    let size = self.rendering_context.size2d().to_i32();
404                    renderer.render(size, 0 /* buffer_age */).ok();
405                }
406            }
407        );
408
409        // We've painted the default target, which means that from the embedder's perspective,
410        // the scene no longer needs to be repainted.
411        self.needs_repaint.set(RepaintReason::empty());
412
413        self.screenshot_taker.maybe_take_screenshots(self);
414        self.send_pending_paint_metrics_messages_after_composite();
415    }
416
417    fn clear_background(&self) {
418        self.assert_gl_framebuffer_complete();
419
420        // Always clear the entire RenderingContext, regardless of how many WebViews there are
421        // or where they are positioned. This is so WebView actually clears even before the
422        // first WebView is ready.
423        let color = servo_config::pref!(shell_background_color_rgba);
424        self.webrender_gl.clear_color(
425            color[0] as f32,
426            color[1] as f32,
427            color[2] as f32,
428            color[3] as f32,
429        );
430        self.webrender_gl.clear(gleam::gl::COLOR_BUFFER_BIT);
431    }
432
433    /// Send all pending paint metrics messages after a composite operation, which may advance
434    /// the epoch for pipelines in the WebRender scene.
435    ///
436    /// If there are pending paint metrics, we check if any of the painted epochs is one
437    /// of the ones that the paint metrics recorder is expecting. In that case, we get the
438    /// current time, inform the constellation about it and remove the pending metric from
439    /// the list.
440    fn send_pending_paint_metrics_messages_after_composite(&mut self) {
441        let paint_time = CrossProcessInstant::now();
442        for webview_renderer in self.webview_renderers.values() {
443            for (pipeline_id, pipeline) in webview_renderer.pipelines.iter() {
444                let Some(current_epoch) = self
445                    .webrender_renderer
446                    .as_ref()
447                    .and_then(|wr| wr.current_epoch(self.webrender_document, pipeline_id.into()))
448                else {
449                    continue;
450                };
451
452                match pipeline.first_paint_metric.get() {
453                    // We need to check whether the current epoch is later, because
454                    // CrossProcessPaintMessage::SendInitialTransaction sends an
455                    // empty display list to WebRender which can happen before we receive
456                    // the first "real" display list.
457                    PaintMetricState::Seen(epoch, first_reflow) if epoch <= current_epoch => {
458                        assert!(epoch <= current_epoch);
459                        #[cfg(feature = "tracing")]
460                        tracing::info!(
461                            name: "FirstPaint",
462                            servo_profiling = true,
463                            epoch = ?epoch,
464                            paint_time = ?paint_time,
465                            pipeline_id = ?pipeline_id,
466                        );
467
468                        self.send_to_constellation(EmbedderToConstellationMessage::PaintMetric(
469                            *pipeline_id,
470                            PaintMetricEvent::FirstPaint(paint_time, first_reflow),
471                        ));
472
473                        pipeline.first_paint_metric.set(PaintMetricState::Sent);
474                    },
475                    _ => {},
476                }
477
478                match pipeline.first_contentful_paint_metric.get() {
479                    PaintMetricState::Seen(epoch, first_reflow) if epoch <= current_epoch => {
480                        #[cfg(feature = "tracing")]
481                        tracing::info!(
482                            name: "FirstContentfulPaint",
483                            servo_profiling = true,
484                            epoch = ?epoch,
485                            paint_time = ?paint_time,
486                            pipeline_id = ?pipeline_id,
487                        );
488                        self.send_to_constellation(EmbedderToConstellationMessage::PaintMetric(
489                            *pipeline_id,
490                            PaintMetricEvent::FirstContentfulPaint(paint_time, first_reflow),
491                        ));
492                        pipeline
493                            .first_contentful_paint_metric
494                            .set(PaintMetricState::Sent);
495                    },
496                    _ => {},
497                }
498
499                match pipeline.largest_contentful_paint_metric.get() {
500                    PaintMetricState::Seen(epoch, _) if epoch <= current_epoch => {
501                        if let Some(lcp) = self
502                            .lcp_calculator
503                            .calculate_largest_contentful_paint(paint_time, pipeline_id.into())
504                        {
505                            #[cfg(feature = "tracing")]
506                            tracing::info!(
507                                name: "LargestContentfulPaint",
508                                servo_profiling = true,
509                                paint_time = ?paint_time,
510                                area = ?lcp.area,
511                                lcp_type = ?lcp.lcp_type,
512                                pipeline_id = ?pipeline_id,
513                            );
514                            self.send_to_constellation(
515                                EmbedderToConstellationMessage::PaintMetric(
516                                    *pipeline_id,
517                                    PaintMetricEvent::LargestContentfulPaint(
518                                        lcp.paint_time,
519                                        lcp.area,
520                                        lcp.lcp_type,
521                                    ),
522                                ),
523                            );
524                        }
525                        pipeline
526                            .largest_contentful_paint_metric
527                            .set(PaintMetricState::Sent);
528                    },
529                    _ => {},
530                }
531            }
532        }
533    }
534
535    /// Queue a new frame in the transaction and increase the pending frames count.
536    pub(crate) fn generate_frame(&self, transaction: &mut Transaction, reason: RenderReasons) {
537        transaction.generate_frame(0, true /* present */, false /* tracked */, reason);
538        self.pending_frames.set(self.pending_frames.get() + 1);
539    }
540
541    pub(crate) fn hit_test_at_point_with_api_and_document(
542        webrender_api: &RenderApi,
543        webrender_document: DocumentId,
544        point: DevicePoint,
545    ) -> Vec<PaintHitTestResult> {
546        // DevicePoint and WorldPoint are the same for us.
547        let world_point = WorldPoint::from_untyped(point.to_untyped());
548        let results = webrender_api.hit_test(webrender_document, world_point);
549
550        results
551            .items
552            .iter()
553            .map(|item| {
554                let pipeline_id = item.pipeline.into();
555                let external_scroll_id = ExternalScrollId(item.tag.0, item.pipeline);
556                PaintHitTestResult {
557                    pipeline_id,
558                    point_in_viewport: Point2D::from_untyped(item.point_in_viewport.to_untyped()),
559                    external_scroll_id,
560                }
561            })
562            .collect()
563    }
564
565    pub(crate) fn send_transaction(&mut self, transaction: Transaction) {
566        let _ = self.rendering_context.make_current();
567        self.webrender_api
568            .send_transaction(self.webrender_document, transaction);
569    }
570
571    /// Set the root pipeline for our WebRender scene to a display list that consists of an iframe
572    /// for each visible top-level browsing context, applying a transformation on the root for
573    /// pinch zoom, page zoom, and HiDPI scaling.
574    fn send_root_pipeline_display_list_in_transaction(&self, transaction: &mut Transaction) {
575        // Every display list needs a pipeline, but we'd like to choose one that is unlikely
576        // to conflict with our content pipelines, which start at (1, 1). (0, 0) is WebRender's
577        // dummy pipeline, so we choose (0, 1).
578        let root_pipeline = WebRenderPipelineId(0, 1);
579        transaction.set_root_pipeline(root_pipeline);
580
581        let mut builder = webrender_api::DisplayListBuilder::new(root_pipeline);
582        builder.begin();
583
584        let root_reference_frame = SpatialId::root_reference_frame(root_pipeline);
585
586        let viewport_size = self.rendering_context.size2d().to_f32().to_untyped();
587        let viewport_rect = LayoutRect::from_origin_and_size(
588            LayoutPoint::zero(),
589            LayoutSize::from_untyped(viewport_size),
590        );
591
592        let root_clip_id = builder.define_clip_rect(root_reference_frame, viewport_rect);
593        let clip_chain_id = builder.define_clip_chain(None, [root_clip_id]);
594        for webview_renderer in self.webview_renderers.values() {
595            if webview_renderer.hidden() {
596                continue;
597            }
598            let Some(pipeline_id) = webview_renderer.root_pipeline_id else {
599                continue;
600            };
601
602            let pinch_zoom_transform = webview_renderer.pinch_zoom().transform().to_untyped();
603            let device_pixels_per_page_pixel_not_including_pinch_zoom = webview_renderer
604                .device_pixels_per_page_pixel_not_including_pinch_zoom()
605                .get();
606
607            let transform = LayoutTransform::scale(
608                device_pixels_per_page_pixel_not_including_pinch_zoom,
609                device_pixels_per_page_pixel_not_including_pinch_zoom,
610                1.0,
611            )
612            .then(&LayoutTransform::from_untyped(
613                &pinch_zoom_transform.to_3d(),
614            ));
615
616            let webview_reference_frame = builder.push_reference_frame(
617                LayoutPoint::zero(),
618                root_reference_frame,
619                TransformStyle::Flat,
620                PropertyBinding::Value(transform),
621                ReferenceFrameKind::Transform {
622                    is_2d_scale_translation: true,
623                    should_snap: true,
624                    paired_with_perspective: false,
625                },
626                webview_renderer.id.into(),
627            );
628
629            let scaled_webview_rect = webview_renderer.rect /
630                webview_renderer.device_pixels_per_page_pixel_not_including_pinch_zoom();
631            builder.push_iframe(
632                LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()),
633                LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()),
634                &SpaceAndClipInfo {
635                    spatial_id: webview_reference_frame,
636                    clip_chain_id,
637                },
638                pipeline_id.into(),
639                true,
640            );
641        }
642
643        let built_display_list = builder.end();
644
645        // NB: We are always passing 0 as the epoch here, but this doesn't seem to
646        // be an issue. WebRender will still update the scene and generate a new
647        // frame even though the epoch hasn't changed.
648        transaction.set_display_list(WebRenderEpoch(0), built_display_list);
649        self.update_transaction_with_all_scroll_offsets(transaction);
650    }
651
652    /// Set the root pipeline for our WebRender scene to a display list that consists of an iframe
653    /// for each visible top-level browsing context, applying a transformation on the root for
654    /// pinch zoom, page zoom, and HiDPI scaling.
655    fn send_root_pipeline_display_list(&mut self) {
656        let mut transaction = Transaction::new();
657        self.send_root_pipeline_display_list_in_transaction(&mut transaction);
658        self.generate_frame(&mut transaction, RenderReasons::SCENE);
659        self.send_transaction(transaction);
660    }
661
662    /// Update the given transaction with the scroll offsets of all active scroll nodes in
663    /// the WebRender scene. This is necessary because WebRender does not preserve scroll
664    /// offsets between scroll tree modifications. If a display list could potentially
665    /// modify a scroll tree branch, WebRender needs to have scroll offsets for that
666    /// branch.
667    ///
668    /// TODO(mrobinson): Could we only send offsets for the branch being modified
669    /// and not the entire scene?
670    fn update_transaction_with_all_scroll_offsets(&self, transaction: &mut Transaction) {
671        for webview_renderer in self.webview_renderers.values() {
672            for details in webview_renderer.pipelines.values() {
673                for node in details.scroll_tree.nodes.iter() {
674                    let (Some(offset), Some(external_id)) = (node.offset(), node.external_id())
675                    else {
676                        continue;
677                    };
678                    // Skip scroll offsets that are zero, as they are the default.
679                    if offset == LayoutVector2D::zero() {
680                        continue;
681                    }
682                    transaction.set_scroll_offsets(
683                        external_id,
684                        vec![SampledScrollOffset {
685                            offset,
686                            generation: 0,
687                        }],
688                    );
689                }
690            }
691        }
692    }
693
694    fn send_zoom_and_scroll_offset_updates(
695        &mut self,
696        need_zoom: bool,
697        scroll_offset_updates: Vec<ScrollResult>,
698    ) {
699        if !need_zoom && scroll_offset_updates.is_empty() {
700            return;
701        }
702
703        let mut transaction = Transaction::new();
704        if need_zoom {
705            self.send_root_pipeline_display_list_in_transaction(&mut transaction);
706        }
707        for update in scroll_offset_updates {
708            transaction.set_scroll_offsets(
709                update.external_scroll_id,
710                vec![SampledScrollOffset {
711                    offset: update.offset,
712                    generation: 0,
713                }],
714            );
715        }
716
717        self.generate_frame(&mut transaction, RenderReasons::APZ);
718        self.send_transaction(transaction);
719    }
720
721    pub(crate) fn toggle_webrender_debug(&mut self, option: WebRenderDebugOption) {
722        let Some(renderer) = self.webrender_renderer.as_mut() else {
723            return;
724        };
725        let mut flags = renderer.get_debug_flags();
726        let flag = match option {
727            WebRenderDebugOption::Profiler => {
728                webrender::DebugFlags::PROFILER_DBG |
729                    webrender::DebugFlags::GPU_TIME_QUERIES |
730                    webrender::DebugFlags::GPU_SAMPLE_QUERIES
731            },
732            WebRenderDebugOption::TextureCacheDebug => webrender::DebugFlags::TEXTURE_CACHE_DBG,
733            WebRenderDebugOption::RenderTargetDebug => webrender::DebugFlags::RENDER_TARGET_DBG,
734        };
735        flags.toggle(flag);
736        renderer.set_debug_flags(flags);
737
738        let mut txn = Transaction::new();
739        self.generate_frame(&mut txn, RenderReasons::TESTING);
740        self.send_transaction(txn);
741    }
742
743    pub(crate) fn decrement_pending_frames(&self) {
744        self.pending_frames.set(self.pending_frames.get() - 1);
745    }
746
747    pub(crate) fn report_memory(&self) -> MemoryReport {
748        self.webrender_api
749            .report_memory(MallocSizeOfOps::new(servo_allocator::usable_size, None))
750    }
751
752    pub(crate) fn change_running_animations_state(
753        &mut self,
754        webview_id: WebViewId,
755        pipeline_id: PipelineId,
756        animation_state: embedder_traits::AnimationState,
757    ) {
758        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
759            return;
760        };
761        if !webview_renderer.change_pipeline_running_animations_state(pipeline_id, animation_state)
762        {
763            return;
764        }
765        if !self
766            .animation_refresh_driver_observer
767            .notify_animation_state_changed(webview_renderer)
768        {
769            return;
770        }
771
772        self.refresh_driver
773            .add_observer(self.animation_refresh_driver_observer.clone());
774    }
775
776    pub(crate) fn set_frame_tree_for_webview(&mut self, frame_tree: &SendableFrameTree) {
777        debug!("{}: Setting frame tree for webview", frame_tree.pipeline.id);
778
779        let webview_id = frame_tree.pipeline.webview_id;
780        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
781            warn!(
782                "Attempted to set frame tree on unknown WebView (perhaps closed?): {webview_id:?}"
783            );
784            return;
785        };
786
787        webview_renderer.set_frame_tree(frame_tree);
788        self.send_root_pipeline_display_list();
789    }
790
791    pub(crate) fn set_throttled(
792        &mut self,
793        webview_id: WebViewId,
794        pipeline_id: PipelineId,
795        throttled: bool,
796    ) {
797        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
798            return;
799        };
800        if !webview_renderer.set_throttled(pipeline_id, throttled) {
801            return;
802        }
803
804        if self
805            .animation_refresh_driver_observer
806            .notify_animation_state_changed(webview_renderer)
807        {
808            self.refresh_driver
809                .add_observer(self.animation_refresh_driver_observer.clone());
810        }
811    }
812
813    pub(crate) fn notify_pipeline_exited(
814        &mut self,
815        webview_id: WebViewId,
816        pipeline_id: PipelineId,
817        pipeline_exit_source: PipelineExitSource,
818    ) {
819        debug!("Paint got pipeline exited: {webview_id:?} {pipeline_id:?}",);
820        if let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) {
821            webview_renderer.pipeline_exited(pipeline_id, pipeline_exit_source);
822        }
823        self.lcp_calculator
824            .remove_lcp_candidates_for_pipeline(pipeline_id.into());
825    }
826
827    pub(crate) fn send_initial_pipeline_transaction(
828        &mut self,
829        webview_id: WebViewId,
830        pipeline_id: WebRenderPipelineId,
831    ) {
832        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
833            return warn!("Could not find WebView for incoming display list");
834        };
835
836        let starting_epoch = Epoch(0);
837        let details = webview_renderer.ensure_pipeline_details(pipeline_id.into());
838        details.display_list_epoch = Some(starting_epoch);
839
840        let mut txn = Transaction::new();
841        txn.set_display_list(starting_epoch.into(), (pipeline_id, Default::default()));
842
843        self.generate_frame(&mut txn, RenderReasons::SCENE);
844        self.send_transaction(txn);
845    }
846
847    pub(crate) fn scroll_node_by_delta(
848        &mut self,
849        webview_id: WebViewId,
850        pipeline_id: WebRenderPipelineId,
851        offset: LayoutVector2D,
852        external_scroll_id: webrender_api::ExternalScrollId,
853    ) {
854        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
855            return;
856        };
857
858        let pipeline_id = pipeline_id.into();
859        let Some(pipeline_details) = webview_renderer.pipelines.get_mut(&pipeline_id) else {
860            return;
861        };
862
863        let Some(offset) = pipeline_details
864            .scroll_tree
865            .set_scroll_offset_for_node_with_external_scroll_id(
866                external_scroll_id,
867                offset,
868                ScrollType::Script,
869            )
870        else {
871            // The renderer should be fully up-to-date with script at this point and script
872            // should never try to scroll to an invalid location.
873            warn!("Could not scroll node with id: {external_scroll_id:?}");
874            return;
875        };
876
877        let mut transaction = Transaction::new();
878        transaction.set_scroll_offsets(
879            external_scroll_id,
880            vec![SampledScrollOffset {
881                offset,
882                generation: 0,
883            }],
884        );
885
886        self.generate_frame(&mut transaction, RenderReasons::APZ);
887        self.send_transaction(transaction);
888    }
889
890    pub(crate) fn scroll_viewport_by_delta(
891        &mut self,
892        webview_id: WebViewId,
893        delta: LayoutVector2D,
894    ) {
895        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
896            return;
897        };
898        let (pinch_zoom_result, scroll_results) = webview_renderer.scroll_viewport_by_delta(delta);
899        self.send_zoom_and_scroll_offset_updates(
900            pinch_zoom_result == PinchZoomResult::DidPinchZoom,
901            scroll_results,
902        );
903    }
904
905    pub(crate) fn update_epoch(
906        &mut self,
907        webview_id: WebViewId,
908        pipeline_id: PipelineId,
909        epoch: Epoch,
910    ) {
911        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
912            return warn!("Could not find WebView for Epoch update.");
913        };
914        webview_renderer
915            .ensure_pipeline_details(pipeline_id)
916            .display_list_epoch = Some(Epoch(epoch.0));
917    }
918
919    #[servo_tracing::instrument(skip_all)]
920    pub(crate) fn handle_new_display_list(
921        &mut self,
922        webview_id: WebViewId,
923        display_list_descriptor: BuiltDisplayListDescriptor,
924        display_list_info_receiver: GenericReceiver<PaintDisplayListInfo>,
925        display_list_data_receiver: GenericReceiver<SerializableDisplayListPayload>,
926    ) {
927        let Ok(display_list_info) = display_list_info_receiver.recv() else {
928            return log::error!("Could not receive display list info");
929        };
930        let Ok(display_list_data) = display_list_data_receiver.recv() else {
931            return log::error!("Could not receive display list data");
932        };
933
934        let items_data = display_list_data.items_data;
935        let cache_data = display_list_data.cache_data;
936        let spatial_tree = display_list_data.spatial_tree;
937
938        let built_display_list = BuiltDisplayList::from_data(
939            DisplayListPayload {
940                items_data,
941                cache_data,
942                spatial_tree,
943            },
944            display_list_descriptor,
945        );
946        let _span = profile_traits::trace_span!("PaintMessage::SendDisplayList",).entered();
947        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
948            return warn!("Could not find WebView for incoming display list");
949        };
950
951        let old_scale = webview_renderer.device_pixels_per_page_pixel();
952        let pipeline_id = display_list_info.pipeline_id;
953        let details = webview_renderer.ensure_pipeline_details(pipeline_id.into());
954
955        details.install_new_scroll_tree(display_list_info.scroll_tree);
956        details.viewport_scale = Some(display_list_info.viewport_details.hidpi_scale_factor);
957
958        let epoch = display_list_info.epoch.into();
959        let first_reflow = display_list_info.first_reflow;
960        if details.first_paint_metric.get() == PaintMetricState::Waiting {
961            details
962                .first_paint_metric
963                .set(PaintMetricState::Seen(epoch, first_reflow));
964        }
965
966        if details.first_contentful_paint_metric.get() == PaintMetricState::Waiting &&
967            display_list_info.is_contentful
968        {
969            details
970                .first_contentful_paint_metric
971                .set(PaintMetricState::Seen(epoch, first_reflow));
972        }
973
974        let mut transaction = Transaction::new();
975        let is_root_pipeline = Some(pipeline_id.into()) == webview_renderer.root_pipeline_id;
976        if is_root_pipeline && old_scale != webview_renderer.device_pixels_per_page_pixel() {
977            self.send_root_pipeline_display_list_in_transaction(&mut transaction);
978        }
979
980        transaction.set_display_list(epoch, (pipeline_id, built_display_list));
981
982        self.update_transaction_with_all_scroll_offsets(&mut transaction);
983        self.send_transaction(transaction);
984    }
985
986    pub(crate) fn generate_frame_for_script(&mut self) {
987        self.frame_delayer.set_pending_frame(true);
988
989        if !self.frame_delayer.needs_new_frame() {
990            return;
991        }
992
993        let mut transaction = Transaction::new();
994        self.generate_frame(&mut transaction, RenderReasons::SCENE);
995        self.send_transaction(transaction);
996
997        let waiting_pipelines = self.frame_delayer.take_waiting_pipelines();
998
999        self.send_to_constellation(
1000            EmbedderToConstellationMessage::NoLongerWaitingOnAsynchronousImageUpdates(
1001                waiting_pipelines,
1002            ),
1003        );
1004
1005        self.frame_delayer.set_pending_frame(false);
1006        self.screenshot_taker
1007            .prepare_screenshot_requests_for_render(self)
1008    }
1009
1010    fn serializable_image_data_to_image_data_maybe_caching(
1011        &mut self,
1012        key: ImageKey,
1013        data: SerializableImageData,
1014        is_animated_image: bool,
1015    ) -> ImageData {
1016        match data {
1017            SerializableImageData::Raw(shared_memory) => {
1018                let data = Arc::new(shared_memory.to_vec());
1019                if is_animated_image {
1020                    self.animation_image_cache.insert(key, Arc::clone(&data));
1021                }
1022                ImageData::Raw(data)
1023            },
1024            SerializableImageData::External(image) => ImageData::External(image),
1025        }
1026    }
1027
1028    pub(crate) fn update_images(&mut self, updates: SmallVec<[ImageUpdate; 1]>) {
1029        let mut txn = Transaction::new();
1030        for update in updates {
1031            match update {
1032                ImageUpdate::AddImage(key, description, data, is_animated_image) => {
1033                    txn.add_image(
1034                        key,
1035                        description,
1036                        self.serializable_image_data_to_image_data_maybe_caching(
1037                            key,
1038                            data,
1039                            is_animated_image,
1040                        ),
1041                        None,
1042                    );
1043                },
1044                ImageUpdate::DeleteImage(key) => {
1045                    txn.delete_image(key);
1046                    self.frame_delayer.delete_image(key);
1047                    self.animation_image_cache.remove(&key);
1048                },
1049                ImageUpdate::UpdateImage(key, desc, data, epoch) => {
1050                    if let Some(epoch) = epoch {
1051                        self.frame_delayer.update_image(key, epoch);
1052                    }
1053                    txn.update_image(key, desc, data.into(), &DirtyRect::All)
1054                },
1055                ImageUpdate::UpdateImageForAnimation(image_key, desc) => {
1056                    let Some(image) = self.animation_image_cache.get(&image_key) else {
1057                        error!("Could not find image key in image cache.");
1058                        continue;
1059                    };
1060                    txn.update_image(
1061                        image_key,
1062                        desc,
1063                        ImageData::new_shared(image.clone()),
1064                        &DirtyRect::All,
1065                    );
1066                },
1067            }
1068        }
1069
1070        if self.frame_delayer.needs_new_frame() {
1071            self.frame_delayer.set_pending_frame(false);
1072            self.generate_frame(&mut txn, RenderReasons::SCENE);
1073            let waiting_pipelines = self.frame_delayer.take_waiting_pipelines();
1074
1075            self.send_to_constellation(
1076                EmbedderToConstellationMessage::NoLongerWaitingOnAsynchronousImageUpdates(
1077                    waiting_pipelines,
1078                ),
1079            );
1080
1081            self.screenshot_taker
1082                .prepare_screenshot_requests_for_render(&*self);
1083        }
1084
1085        self.send_transaction(txn);
1086    }
1087
1088    pub(crate) fn delay_new_frames_for_canvas(
1089        &mut self,
1090        pipeline_id: PipelineId,
1091        canvas_epoch: Epoch,
1092        image_keys: Vec<ImageKey>,
1093    ) {
1094        self.frame_delayer
1095            .add_delay(pipeline_id, canvas_epoch, image_keys);
1096    }
1097
1098    pub(crate) fn add_font(
1099        &mut self,
1100        font_key: FontKey,
1101        data: Arc<GenericSharedMemory>,
1102        index: u32,
1103    ) {
1104        let mut transaction = Transaction::new();
1105        transaction.add_raw_font(font_key, (**data).into(), index);
1106        self.send_transaction(transaction);
1107    }
1108
1109    pub(crate) fn add_system_font(&mut self, font_key: FontKey, native_handle: NativeFontHandle) {
1110        let mut transaction = Transaction::new();
1111        transaction.add_native_font(font_key, native_handle);
1112        self.send_transaction(transaction);
1113    }
1114
1115    pub(crate) fn add_font_instance(
1116        &mut self,
1117        instance_key: FontInstanceKey,
1118        font_key: FontKey,
1119        size: f32,
1120        flags: FontInstanceFlags,
1121        variations: Vec<FontVariation>,
1122    ) {
1123        let variations = if pref!(layout_variable_fonts_enabled) {
1124            variations
1125        } else {
1126            vec![]
1127        };
1128
1129        let mut transaction = Transaction::new();
1130
1131        let font_instance_options = FontInstanceOptions {
1132            flags,
1133            ..Default::default()
1134        };
1135        transaction.add_font_instance(
1136            instance_key,
1137            font_key,
1138            size,
1139            Some(font_instance_options),
1140            None,
1141            variations,
1142        );
1143
1144        self.send_transaction(transaction);
1145    }
1146
1147    pub(crate) fn remove_fonts(&mut self, keys: Vec<FontKey>, instance_keys: Vec<FontInstanceKey>) {
1148        let mut transaction = Transaction::new();
1149
1150        for instance in instance_keys.into_iter() {
1151            transaction.delete_font_instance(instance);
1152        }
1153        for key in keys.into_iter() {
1154            transaction.delete_font(key);
1155        }
1156
1157        self.send_transaction(transaction);
1158    }
1159
1160    pub(crate) fn set_viewport_description(
1161        &mut self,
1162        webview_id: WebViewId,
1163        viewport_description: ViewportDescription,
1164    ) {
1165        if let Some(webview) = self.webview_renderers.get_mut(&webview_id) {
1166            webview.set_viewport_description(viewport_description);
1167        }
1168    }
1169
1170    pub(crate) fn handle_screenshot_readiness_reply(
1171        &self,
1172        webview_id: WebViewId,
1173        expected_epochs: FxHashMap<PipelineId, Epoch>,
1174    ) {
1175        self.screenshot_taker
1176            .handle_screenshot_readiness_reply(webview_id, expected_epochs, self);
1177    }
1178
1179    pub(crate) fn add_webview(
1180        &mut self,
1181        webview: Box<dyn WebViewTrait>,
1182        viewport_details: ViewportDetails,
1183    ) {
1184        self.webview_renderers
1185            .entry(webview.id())
1186            .or_insert(WebViewRenderer::new(
1187                webview,
1188                viewport_details,
1189                self.embedder_to_constellation_sender.clone(),
1190                self.refresh_driver.clone(),
1191                self.webrender_document,
1192            ));
1193    }
1194
1195    pub(crate) fn remove_webview(&mut self, webview_id: WebViewId) {
1196        if self.webview_renderers.remove(&webview_id).is_none() {
1197            warn!("Tried removing unknown WebView: {webview_id:?}");
1198            return;
1199        };
1200
1201        self.send_root_pipeline_display_list();
1202        self.lcp_calculator.note_webview_removed(webview_id);
1203    }
1204
1205    pub(crate) fn is_empty(&mut self) -> bool {
1206        self.webview_renderers.is_empty()
1207    }
1208
1209    pub(crate) fn set_webview_hidden(
1210        &mut self,
1211        webview_id: WebViewId,
1212        hidden: bool,
1213    ) -> Result<(), UnknownWebView> {
1214        debug!("Setting WebView visiblity for {webview_id:?} to hidden={hidden}");
1215        let Some(webview_renderer) = self.webview_renderer_mut(webview_id) else {
1216            return Err(UnknownWebView(webview_id));
1217        };
1218        if !webview_renderer.set_hidden(hidden) {
1219            return Ok(());
1220        }
1221        self.send_root_pipeline_display_list();
1222        Ok(())
1223    }
1224
1225    pub(crate) fn set_hidpi_scale_factor(
1226        &mut self,
1227        webview_id: WebViewId,
1228        new_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1229    ) {
1230        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
1231            return;
1232        };
1233        if !webview_renderer.set_hidpi_scale_factor(new_scale_factor) {
1234            return;
1235        }
1236
1237        self.send_root_pipeline_display_list();
1238        self.set_needs_repaint(RepaintReason::Resize);
1239    }
1240
1241    pub(crate) fn resize_rendering_context(&mut self, new_size: PhysicalSize<u32>) {
1242        if self.rendering_context.size() == new_size {
1243            return;
1244        }
1245
1246        if let Err(error) = self.rendering_context.make_current() {
1247            error!("Failed to make the rendering context current: {error:?}");
1248        }
1249        self.rendering_context.resize(new_size);
1250
1251        let new_size = Size2D::new(new_size.width as f32, new_size.height as f32);
1252        let new_viewport_rect = Rect::from(new_size).to_box2d();
1253        for webview_renderer in self.webview_renderers.values_mut() {
1254            webview_renderer.set_rect(new_viewport_rect);
1255        }
1256
1257        let mut transaction = Transaction::new();
1258        transaction.set_document_view(new_viewport_rect.to_i32());
1259        self.send_transaction(transaction);
1260
1261        self.send_root_pipeline_display_list();
1262        self.set_needs_repaint(RepaintReason::Resize);
1263    }
1264
1265    pub(crate) fn set_page_zoom(&mut self, webview_id: WebViewId, new_zoom: f32) {
1266        if let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) {
1267            webview_renderer.set_page_zoom(Scale::new(new_zoom));
1268        }
1269    }
1270
1271    pub(crate) fn page_zoom(&self, webview_id: WebViewId) -> f32 {
1272        self.webview_renderers
1273            .get(&webview_id)
1274            .map(|webview_renderer| webview_renderer.page_zoom.get())
1275            .unwrap_or_default()
1276    }
1277
1278    pub(crate) fn notify_input_event(&mut self, webview_id: WebViewId, event: InputEventAndId) {
1279        if let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) {
1280            match &event.event {
1281                InputEvent::MouseMove(event) => {
1282                    // We only track the last mouse move position for non-touch events.
1283                    if !event.is_compatibility_event_for_touch {
1284                        let event_point = event
1285                            .point
1286                            .as_device_point(webview_renderer.device_pixels_per_page_pixel());
1287                        self.last_mouse_move_position = Some(event_point);
1288                    }
1289                },
1290                InputEvent::MouseLeftViewport(_) => {
1291                    self.last_mouse_move_position = None;
1292                },
1293                _ => {},
1294            }
1295
1296            webview_renderer.notify_input_event(&self.webrender_api, &self.needs_repaint, event);
1297        }
1298        self.disable_lcp_calculation_for_webview(webview_id);
1299    }
1300
1301    pub(crate) fn notify_scroll_event(
1302        &mut self,
1303        webview_id: WebViewId,
1304        scroll: Scroll,
1305        point: WebViewPoint,
1306    ) {
1307        if let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) {
1308            webview_renderer.notify_scroll_event(scroll, point);
1309        }
1310        self.disable_lcp_calculation_for_webview(webview_id);
1311    }
1312
1313    pub(crate) fn pinch_zoom(
1314        &mut self,
1315        webview_id: WebViewId,
1316        pinch_zoom_delta: f32,
1317        center: DevicePoint,
1318    ) {
1319        if let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) {
1320            webview_renderer.adjust_pinch_zoom(pinch_zoom_delta, center);
1321        }
1322    }
1323
1324    pub(crate) fn device_pixels_per_page_pixel(
1325        &self,
1326        webview_id: WebViewId,
1327    ) -> Scale<f32, CSSPixel, DevicePixel> {
1328        self.webview_renderers
1329            .get(&webview_id)
1330            .map(WebViewRenderer::device_pixels_per_page_pixel)
1331            .unwrap_or_default()
1332    }
1333
1334    pub(crate) fn request_screenshot(
1335        &self,
1336        webview_id: WebViewId,
1337        rect: Option<WebViewRect>,
1338        callback: Box<dyn FnOnce(Result<RgbaImage, ScreenshotCaptureError>) + 'static>,
1339    ) {
1340        let Some(webview) = self.webview_renderers.get(&webview_id) else {
1341            return;
1342        };
1343
1344        let rect = rect.map(|rect| rect.as_device_rect(webview.device_pixels_per_page_pixel()));
1345        self.screenshot_taker
1346            .request_screenshot(webview_id, rect, callback);
1347        self.send_to_constellation(EmbedderToConstellationMessage::RequestScreenshotReadiness(
1348            webview_id,
1349        ));
1350    }
1351
1352    pub(crate) fn notify_input_event_handled(
1353        &mut self,
1354        webview_id: WebViewId,
1355        input_event_id: InputEventId,
1356        result: InputEventResult,
1357    ) {
1358        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
1359            warn!("Handled input event for unknown webview: {webview_id}");
1360            return;
1361        };
1362        webview_renderer.notify_input_event_handled(
1363            &self.webrender_api,
1364            &self.needs_repaint,
1365            input_event_id,
1366            result,
1367        );
1368    }
1369
1370    pub(crate) fn refresh_cursor(&self) {
1371        let Some(last_mouse_move_position) = self.last_mouse_move_position else {
1372            return;
1373        };
1374
1375        let Some(hit_test_result) = Self::hit_test_at_point_with_api_and_document(
1376            &self.webrender_api,
1377            self.webrender_document,
1378            last_mouse_move_position,
1379        )
1380        .first()
1381        .cloned() else {
1382            return;
1383        };
1384
1385        if let Err(error) = self.embedder_to_constellation_sender.send(
1386            EmbedderToConstellationMessage::RefreshCursor(hit_test_result.pipeline_id),
1387        ) {
1388            warn!("Sending event to constellation failed ({:?}).", error);
1389        }
1390    }
1391
1392    pub(crate) fn handle_new_webrender_frame_ready(&self, repaint_needed: bool) {
1393        if repaint_needed {
1394            self.refresh_cursor()
1395        }
1396
1397        if repaint_needed || self.animation_callbacks_running() {
1398            self.set_needs_repaint(RepaintReason::NewWebRenderFrame);
1399        }
1400
1401        // If we received a new frame and a repaint isn't necessary, it may be that this
1402        // is the last frame that was pending. In that case, trigger a manual repaint so
1403        // that the screenshot can be taken at the end of the repaint procedure.
1404        if !repaint_needed {
1405            self.screenshot_taker
1406                .maybe_trigger_paint_for_screenshot(self);
1407        }
1408    }
1409
1410    pub(crate) fn webviews_needing_repaint(&self) -> Vec<WebViewId> {
1411        if self.needs_repaint() {
1412            self.webview_renderers
1413                .values()
1414                .map(|webview_renderer| webview_renderer.id)
1415                .collect()
1416        } else {
1417            Vec::new()
1418        }
1419    }
1420
1421    pub(crate) fn scroll_trees_memory_usage(
1422        &self,
1423        ops: &mut malloc_size_of::MallocSizeOfOps,
1424    ) -> usize {
1425        self.webview_renderers
1426            .values()
1427            .map(|renderer| renderer.scroll_trees_memory_usage(ops))
1428            .sum::<usize>()
1429    }
1430
1431    pub(crate) fn append_lcp_candidate(
1432        &mut self,
1433        lcp_candidate: LCPCandidate,
1434        webview_id: WebViewId,
1435        pipeline_id: PipelineId,
1436        epoch: Epoch,
1437    ) {
1438        if self
1439            .lcp_calculator
1440            .append_lcp_candidate(webview_id, pipeline_id.into(), lcp_candidate)
1441        {
1442            if let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) {
1443                webview_renderer
1444                    .ensure_pipeline_details(pipeline_id)
1445                    .largest_contentful_paint_metric
1446                    .set(PaintMetricState::Seen(epoch.into(), false));
1447            }
1448        };
1449    }
1450
1451    /// Disable LCP feature when the user interacts with the page.
1452    fn disable_lcp_calculation_for_webview(&mut self, webview_id: WebViewId) {
1453        self.lcp_calculator.disable_for_webview(webview_id);
1454    }
1455}
1456
1457/// A struct that is reponsible for delaying frame requests until all new canvas images
1458/// for a particular "update the rendering" call in the `ScriptThread` have been
1459/// sent to WebRender.
1460///
1461/// These images may be updated in WebRender asynchronously in the canvas task. A frame
1462/// is then requested if:
1463///
1464///  - The renderer has received a GenerateFrame message from a `ScriptThread`.
1465///  - All pending image updates have finished and have been noted in the [`FrameDelayer`].
1466#[derive(Default)]
1467pub(crate) struct FrameDelayer {
1468    /// The latest [`Epoch`] of canvas images that have been sent to WebRender. Note
1469    /// that this only records the `Epoch`s for canvases and only ones that are involved
1470    /// in "update the rendering".
1471    image_epochs: FxHashMap<ImageKey, Epoch>,
1472    /// A map of all pending canvas images
1473    pending_canvas_images: FxHashMap<ImageKey, Epoch>,
1474    /// Whether or not we have a pending frame.
1475    pub(crate) pending_frame: bool,
1476    /// A list of pipelines that should be notified when we are no longer waiting for
1477    /// canvas images.
1478    waiting_pipelines: FxHashSet<PipelineId>,
1479}
1480
1481impl FrameDelayer {
1482    pub(crate) fn delete_image(&mut self, image_key: ImageKey) {
1483        self.image_epochs.remove(&image_key);
1484        self.pending_canvas_images.remove(&image_key);
1485    }
1486
1487    pub(crate) fn update_image(&mut self, image_key: ImageKey, epoch: Epoch) {
1488        self.image_epochs.insert(image_key, epoch);
1489        let Entry::Occupied(entry) = self.pending_canvas_images.entry(image_key) else {
1490            return;
1491        };
1492        if *entry.get() <= epoch {
1493            entry.remove();
1494        }
1495    }
1496
1497    pub(crate) fn add_delay(
1498        &mut self,
1499        pipeline_id: PipelineId,
1500        canvas_epoch: Epoch,
1501        image_keys: Vec<ImageKey>,
1502    ) {
1503        for image_key in image_keys.into_iter() {
1504            // If we've already seen the necessary epoch for this image, do not
1505            // start waiting for it.
1506            if self
1507                .image_epochs
1508                .get(&image_key)
1509                .is_some_and(|epoch_seen| *epoch_seen >= canvas_epoch)
1510            {
1511                continue;
1512            }
1513            self.pending_canvas_images.insert(image_key, canvas_epoch);
1514        }
1515        self.waiting_pipelines.insert(pipeline_id);
1516    }
1517
1518    pub(crate) fn needs_new_frame(&self) -> bool {
1519        self.pending_frame && self.pending_canvas_images.is_empty()
1520    }
1521
1522    pub(crate) fn set_pending_frame(&mut self, value: bool) {
1523        self.pending_frame = value;
1524    }
1525
1526    pub(crate) fn take_waiting_pipelines(&mut self) -> Vec<PipelineId> {
1527        self.waiting_pipelines.drain().collect()
1528    }
1529}
1530
1531/// The paint status of a particular pipeline in a [`Painter`]. This is used to trigger metrics
1532/// in script (via the constellation) when display lists are received.
1533///
1534/// See <https://w3c.github.io/paint-timing/#first-contentful-paint>.
1535#[derive(Clone, Copy, PartialEq)]
1536pub(crate) enum PaintMetricState {
1537    /// The painter is still waiting to process a display list which triggers this metric.
1538    Waiting,
1539    /// The painter has processed the display list which will trigger this event, marked the Servo
1540    /// instance ready to paint, and is waiting for the given epoch to actually be rendered.
1541    Seen(WebRenderEpoch, bool /* first_reflow */),
1542    /// The metric has been sent to the constellation and no more work needs to be done.
1543    Sent,
1544}