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                                pipeline_id = ?pipeline_id,
512                            );
513                            self.send_to_constellation(
514                                EmbedderToConstellationMessage::PaintMetric(
515                                    *pipeline_id,
516                                    PaintMetricEvent::LargestContentfulPaint(
517                                        lcp.paint_time,
518                                        lcp.area,
519                                    ),
520                                ),
521                            );
522                        }
523                        pipeline
524                            .largest_contentful_paint_metric
525                            .set(PaintMetricState::Sent);
526                    },
527                    _ => {},
528                }
529            }
530        }
531    }
532
533    /// Queue a new frame in the transaction and increase the pending frames count.
534    pub(crate) fn generate_frame(&self, transaction: &mut Transaction, reason: RenderReasons) {
535        transaction.generate_frame(0, true /* present */, false /* tracked */, reason);
536        self.pending_frames.set(self.pending_frames.get() + 1);
537    }
538
539    pub(crate) fn hit_test_at_point_with_api_and_document(
540        webrender_api: &RenderApi,
541        webrender_document: DocumentId,
542        point: DevicePoint,
543    ) -> Vec<PaintHitTestResult> {
544        // DevicePoint and WorldPoint are the same for us.
545        let world_point = WorldPoint::from_untyped(point.to_untyped());
546        let results = webrender_api.hit_test(webrender_document, world_point);
547
548        results
549            .items
550            .iter()
551            .map(|item| {
552                let pipeline_id = item.pipeline.into();
553                let external_scroll_id = ExternalScrollId(item.tag.0, item.pipeline);
554                PaintHitTestResult {
555                    pipeline_id,
556                    point_in_viewport: Point2D::from_untyped(item.point_in_viewport.to_untyped()),
557                    external_scroll_id,
558                }
559            })
560            .collect()
561    }
562
563    pub(crate) fn send_transaction(&mut self, transaction: Transaction) {
564        let _ = self.rendering_context.make_current();
565        self.webrender_api
566            .send_transaction(self.webrender_document, transaction);
567    }
568
569    /// Set the root pipeline for our WebRender scene to a display list that consists of an iframe
570    /// for each visible top-level browsing context, applying a transformation on the root for
571    /// pinch zoom, page zoom, and HiDPI scaling.
572    fn send_root_pipeline_display_list_in_transaction(&self, transaction: &mut Transaction) {
573        // Every display list needs a pipeline, but we'd like to choose one that is unlikely
574        // to conflict with our content pipelines, which start at (1, 1). (0, 0) is WebRender's
575        // dummy pipeline, so we choose (0, 1).
576        let root_pipeline = WebRenderPipelineId(0, 1);
577        transaction.set_root_pipeline(root_pipeline);
578
579        let mut builder = webrender_api::DisplayListBuilder::new(root_pipeline);
580        builder.begin();
581
582        let root_reference_frame = SpatialId::root_reference_frame(root_pipeline);
583
584        let viewport_size = self.rendering_context.size2d().to_f32().to_untyped();
585        let viewport_rect = LayoutRect::from_origin_and_size(
586            LayoutPoint::zero(),
587            LayoutSize::from_untyped(viewport_size),
588        );
589
590        let root_clip_id = builder.define_clip_rect(root_reference_frame, viewport_rect);
591        let clip_chain_id = builder.define_clip_chain(None, [root_clip_id]);
592        for webview_renderer in self.webview_renderers.values() {
593            if webview_renderer.hidden() {
594                continue;
595            }
596            let Some(pipeline_id) = webview_renderer.root_pipeline_id else {
597                continue;
598            };
599
600            let pinch_zoom_transform = webview_renderer.pinch_zoom().transform().to_untyped();
601            let device_pixels_per_page_pixel_not_including_pinch_zoom = webview_renderer
602                .device_pixels_per_page_pixel_not_including_pinch_zoom()
603                .get();
604
605            let transform = LayoutTransform::scale(
606                device_pixels_per_page_pixel_not_including_pinch_zoom,
607                device_pixels_per_page_pixel_not_including_pinch_zoom,
608                1.0,
609            )
610            .then(&LayoutTransform::from_untyped(
611                &pinch_zoom_transform.to_3d(),
612            ));
613
614            let webview_reference_frame = builder.push_reference_frame(
615                LayoutPoint::zero(),
616                root_reference_frame,
617                TransformStyle::Flat,
618                PropertyBinding::Value(transform),
619                ReferenceFrameKind::Transform {
620                    is_2d_scale_translation: true,
621                    should_snap: true,
622                    paired_with_perspective: false,
623                },
624                webview_renderer.id.into(),
625            );
626
627            let scaled_webview_rect = webview_renderer.rect /
628                webview_renderer.device_pixels_per_page_pixel_not_including_pinch_zoom();
629            builder.push_iframe(
630                LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()),
631                LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()),
632                &SpaceAndClipInfo {
633                    spatial_id: webview_reference_frame,
634                    clip_chain_id,
635                },
636                pipeline_id.into(),
637                true,
638            );
639        }
640
641        let built_display_list = builder.end();
642
643        // NB: We are always passing 0 as the epoch here, but this doesn't seem to
644        // be an issue. WebRender will still update the scene and generate a new
645        // frame even though the epoch hasn't changed.
646        transaction.set_display_list(WebRenderEpoch(0), built_display_list);
647        self.update_transaction_with_all_scroll_offsets(transaction);
648    }
649
650    /// Set the root pipeline for our WebRender scene to a display list that consists of an iframe
651    /// for each visible top-level browsing context, applying a transformation on the root for
652    /// pinch zoom, page zoom, and HiDPI scaling.
653    fn send_root_pipeline_display_list(&mut self) {
654        let mut transaction = Transaction::new();
655        self.send_root_pipeline_display_list_in_transaction(&mut transaction);
656        self.generate_frame(&mut transaction, RenderReasons::SCENE);
657        self.send_transaction(transaction);
658    }
659
660    /// Update the given transaction with the scroll offsets of all active scroll nodes in
661    /// the WebRender scene. This is necessary because WebRender does not preserve scroll
662    /// offsets between scroll tree modifications. If a display list could potentially
663    /// modify a scroll tree branch, WebRender needs to have scroll offsets for that
664    /// branch.
665    ///
666    /// TODO(mrobinson): Could we only send offsets for the branch being modified
667    /// and not the entire scene?
668    fn update_transaction_with_all_scroll_offsets(&self, transaction: &mut Transaction) {
669        for webview_renderer in self.webview_renderers.values() {
670            for details in webview_renderer.pipelines.values() {
671                for node in details.scroll_tree.nodes.iter() {
672                    let (Some(offset), Some(external_id)) = (node.offset(), node.external_id())
673                    else {
674                        continue;
675                    };
676                    // Skip scroll offsets that are zero, as they are the default.
677                    if offset == LayoutVector2D::zero() {
678                        continue;
679                    }
680                    transaction.set_scroll_offsets(
681                        external_id,
682                        vec![SampledScrollOffset {
683                            offset,
684                            generation: 0,
685                        }],
686                    );
687                }
688            }
689        }
690    }
691
692    fn send_zoom_and_scroll_offset_updates(
693        &mut self,
694        need_zoom: bool,
695        scroll_offset_updates: Vec<ScrollResult>,
696    ) {
697        if !need_zoom && scroll_offset_updates.is_empty() {
698            return;
699        }
700
701        let mut transaction = Transaction::new();
702        if need_zoom {
703            self.send_root_pipeline_display_list_in_transaction(&mut transaction);
704        }
705        for update in scroll_offset_updates {
706            transaction.set_scroll_offsets(
707                update.external_scroll_id,
708                vec![SampledScrollOffset {
709                    offset: update.offset,
710                    generation: 0,
711                }],
712            );
713        }
714
715        self.generate_frame(&mut transaction, RenderReasons::APZ);
716        self.send_transaction(transaction);
717    }
718
719    pub(crate) fn toggle_webrender_debug(&mut self, option: WebRenderDebugOption) {
720        let Some(renderer) = self.webrender_renderer.as_mut() else {
721            return;
722        };
723        let mut flags = renderer.get_debug_flags();
724        let flag = match option {
725            WebRenderDebugOption::Profiler => {
726                webrender::DebugFlags::PROFILER_DBG |
727                    webrender::DebugFlags::GPU_TIME_QUERIES |
728                    webrender::DebugFlags::GPU_SAMPLE_QUERIES
729            },
730            WebRenderDebugOption::TextureCacheDebug => webrender::DebugFlags::TEXTURE_CACHE_DBG,
731            WebRenderDebugOption::RenderTargetDebug => webrender::DebugFlags::RENDER_TARGET_DBG,
732        };
733        flags.toggle(flag);
734        renderer.set_debug_flags(flags);
735
736        let mut txn = Transaction::new();
737        self.generate_frame(&mut txn, RenderReasons::TESTING);
738        self.send_transaction(txn);
739    }
740
741    pub(crate) fn decrement_pending_frames(&self) {
742        self.pending_frames.set(self.pending_frames.get() - 1);
743    }
744
745    pub(crate) fn report_memory(&self) -> MemoryReport {
746        self.webrender_api
747            .report_memory(MallocSizeOfOps::new(servo_allocator::usable_size, None))
748    }
749
750    pub(crate) fn change_running_animations_state(
751        &mut self,
752        webview_id: WebViewId,
753        pipeline_id: PipelineId,
754        animation_state: embedder_traits::AnimationState,
755    ) {
756        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
757            return;
758        };
759        if !webview_renderer.change_pipeline_running_animations_state(pipeline_id, animation_state)
760        {
761            return;
762        }
763        if !self
764            .animation_refresh_driver_observer
765            .notify_animation_state_changed(webview_renderer)
766        {
767            return;
768        }
769
770        self.refresh_driver
771            .add_observer(self.animation_refresh_driver_observer.clone());
772    }
773
774    pub(crate) fn set_frame_tree_for_webview(&mut self, frame_tree: &SendableFrameTree) {
775        debug!("{}: Setting frame tree for webview", frame_tree.pipeline.id);
776
777        let webview_id = frame_tree.pipeline.webview_id;
778        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
779            warn!(
780                "Attempted to set frame tree on unknown WebView (perhaps closed?): {webview_id:?}"
781            );
782            return;
783        };
784
785        webview_renderer.set_frame_tree(frame_tree);
786        self.send_root_pipeline_display_list();
787    }
788
789    pub(crate) fn set_throttled(
790        &mut self,
791        webview_id: WebViewId,
792        pipeline_id: PipelineId,
793        throttled: bool,
794    ) {
795        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
796            return;
797        };
798        if !webview_renderer.set_throttled(pipeline_id, throttled) {
799            return;
800        }
801
802        if self
803            .animation_refresh_driver_observer
804            .notify_animation_state_changed(webview_renderer)
805        {
806            self.refresh_driver
807                .add_observer(self.animation_refresh_driver_observer.clone());
808        }
809    }
810
811    pub(crate) fn notify_pipeline_exited(
812        &mut self,
813        webview_id: WebViewId,
814        pipeline_id: PipelineId,
815        pipeline_exit_source: PipelineExitSource,
816    ) {
817        debug!("Paint got pipeline exited: {webview_id:?} {pipeline_id:?}",);
818        if let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) {
819            webview_renderer.pipeline_exited(pipeline_id, pipeline_exit_source);
820        }
821        self.lcp_calculator
822            .remove_lcp_candidates_for_pipeline(&pipeline_id.into());
823    }
824
825    pub(crate) fn send_initial_pipeline_transaction(
826        &mut self,
827        webview_id: WebViewId,
828        pipeline_id: WebRenderPipelineId,
829    ) {
830        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
831            return warn!("Could not find WebView for incoming display list");
832        };
833
834        let starting_epoch = Epoch(0);
835        let details = webview_renderer.ensure_pipeline_details(pipeline_id.into());
836        details.display_list_epoch = Some(starting_epoch);
837
838        let mut txn = Transaction::new();
839        txn.set_display_list(starting_epoch.into(), (pipeline_id, Default::default()));
840
841        self.generate_frame(&mut txn, RenderReasons::SCENE);
842        self.send_transaction(txn);
843    }
844
845    pub(crate) fn scroll_node_by_delta(
846        &mut self,
847        webview_id: WebViewId,
848        pipeline_id: WebRenderPipelineId,
849        offset: LayoutVector2D,
850        external_scroll_id: webrender_api::ExternalScrollId,
851    ) {
852        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
853            return;
854        };
855
856        let pipeline_id = pipeline_id.into();
857        let Some(pipeline_details) = webview_renderer.pipelines.get_mut(&pipeline_id) else {
858            return;
859        };
860
861        let Some(offset) = pipeline_details
862            .scroll_tree
863            .set_scroll_offset_for_node_with_external_scroll_id(
864                external_scroll_id,
865                offset,
866                ScrollType::Script,
867            )
868        else {
869            // The renderer should be fully up-to-date with script at this point and script
870            // should never try to scroll to an invalid location.
871            warn!("Could not scroll node with id: {external_scroll_id:?}");
872            return;
873        };
874
875        let mut transaction = Transaction::new();
876        transaction.set_scroll_offsets(
877            external_scroll_id,
878            vec![SampledScrollOffset {
879                offset,
880                generation: 0,
881            }],
882        );
883
884        self.generate_frame(&mut transaction, RenderReasons::APZ);
885        self.send_transaction(transaction);
886    }
887
888    pub(crate) fn scroll_viewport_by_delta(
889        &mut self,
890        webview_id: WebViewId,
891        delta: LayoutVector2D,
892    ) {
893        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
894            return;
895        };
896        let (pinch_zoom_result, scroll_results) = webview_renderer.scroll_viewport_by_delta(delta);
897        self.send_zoom_and_scroll_offset_updates(
898            pinch_zoom_result == PinchZoomResult::DidPinchZoom,
899            scroll_results,
900        );
901    }
902
903    pub(crate) fn update_epoch(
904        &mut self,
905        webview_id: WebViewId,
906        pipeline_id: PipelineId,
907        epoch: Epoch,
908    ) {
909        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
910            return warn!("Could not find WebView for Epoch update.");
911        };
912        webview_renderer
913            .ensure_pipeline_details(pipeline_id)
914            .display_list_epoch = Some(Epoch(epoch.0));
915    }
916
917    #[servo_tracing::instrument(skip_all)]
918    pub(crate) fn handle_new_display_list(
919        &mut self,
920        webview_id: WebViewId,
921        display_list_descriptor: BuiltDisplayListDescriptor,
922        display_list_info_receiver: GenericReceiver<PaintDisplayListInfo>,
923        display_list_data_receiver: GenericReceiver<SerializableDisplayListPayload>,
924    ) {
925        let Ok(display_list_info) = display_list_info_receiver.recv() else {
926            return log::error!("Could not receive display list info");
927        };
928        let Ok(display_list_data) = display_list_data_receiver.recv() else {
929            return log::error!("Could not receive display list data");
930        };
931
932        let items_data = display_list_data.items_data;
933        let cache_data = display_list_data.cache_data;
934        let spatial_tree = display_list_data.spatial_tree;
935
936        let built_display_list = BuiltDisplayList::from_data(
937            DisplayListPayload {
938                items_data,
939                cache_data,
940                spatial_tree,
941            },
942            display_list_descriptor,
943        );
944        let _span = profile_traits::trace_span!("PaintMessage::SendDisplayList",).entered();
945        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
946            return warn!("Could not find WebView for incoming display list");
947        };
948
949        let old_scale = webview_renderer.device_pixels_per_page_pixel();
950        let pipeline_id = display_list_info.pipeline_id;
951        let details = webview_renderer.ensure_pipeline_details(pipeline_id.into());
952
953        details.install_new_scroll_tree(display_list_info.scroll_tree);
954        details.viewport_scale = Some(display_list_info.viewport_details.hidpi_scale_factor);
955
956        let epoch = display_list_info.epoch.into();
957        let first_reflow = display_list_info.first_reflow;
958        if details.first_paint_metric.get() == PaintMetricState::Waiting {
959            details
960                .first_paint_metric
961                .set(PaintMetricState::Seen(epoch, first_reflow));
962        }
963
964        if details.first_contentful_paint_metric.get() == PaintMetricState::Waiting &&
965            display_list_info.is_contentful
966        {
967            details
968                .first_contentful_paint_metric
969                .set(PaintMetricState::Seen(epoch, first_reflow));
970        }
971
972        let mut transaction = Transaction::new();
973        let is_root_pipeline = Some(pipeline_id.into()) == webview_renderer.root_pipeline_id;
974        if is_root_pipeline && old_scale != webview_renderer.device_pixels_per_page_pixel() {
975            self.send_root_pipeline_display_list_in_transaction(&mut transaction);
976        }
977
978        transaction.set_display_list(epoch, (pipeline_id, built_display_list));
979
980        self.update_transaction_with_all_scroll_offsets(&mut transaction);
981        self.send_transaction(transaction);
982    }
983
984    pub(crate) fn generate_frame_for_script(&mut self) {
985        self.frame_delayer.set_pending_frame(true);
986
987        if !self.frame_delayer.needs_new_frame() {
988            return;
989        }
990
991        let mut transaction = Transaction::new();
992        self.generate_frame(&mut transaction, RenderReasons::SCENE);
993        self.send_transaction(transaction);
994
995        let waiting_pipelines = self.frame_delayer.take_waiting_pipelines();
996
997        self.send_to_constellation(
998            EmbedderToConstellationMessage::NoLongerWaitingOnAsynchronousImageUpdates(
999                waiting_pipelines,
1000            ),
1001        );
1002
1003        self.frame_delayer.set_pending_frame(false);
1004        self.screenshot_taker
1005            .prepare_screenshot_requests_for_render(self)
1006    }
1007
1008    fn serializable_image_data_to_image_data_maybe_caching(
1009        &mut self,
1010        key: ImageKey,
1011        data: SerializableImageData,
1012        is_animated_image: bool,
1013    ) -> ImageData {
1014        match data {
1015            SerializableImageData::Raw(shared_memory) => {
1016                let data = Arc::new(shared_memory.to_vec());
1017                if is_animated_image {
1018                    self.animation_image_cache.insert(key, Arc::clone(&data));
1019                }
1020                ImageData::Raw(data)
1021            },
1022            SerializableImageData::External(image) => ImageData::External(image),
1023        }
1024    }
1025
1026    pub(crate) fn update_images(&mut self, updates: SmallVec<[ImageUpdate; 1]>) {
1027        let mut txn = Transaction::new();
1028        for update in updates {
1029            match update {
1030                ImageUpdate::AddImage(key, description, data, is_animated_image) => {
1031                    txn.add_image(
1032                        key,
1033                        description,
1034                        self.serializable_image_data_to_image_data_maybe_caching(
1035                            key,
1036                            data,
1037                            is_animated_image,
1038                        ),
1039                        None,
1040                    );
1041                },
1042                ImageUpdate::DeleteImage(key) => {
1043                    txn.delete_image(key);
1044                    self.frame_delayer.delete_image(key);
1045                    self.animation_image_cache.remove(&key);
1046                },
1047                ImageUpdate::UpdateImage(key, desc, data, epoch) => {
1048                    if let Some(epoch) = epoch {
1049                        self.frame_delayer.update_image(key, epoch);
1050                    }
1051                    txn.update_image(key, desc, data.into(), &DirtyRect::All)
1052                },
1053                ImageUpdate::UpdateImageForAnimation(image_key, desc) => {
1054                    let Some(image) = self.animation_image_cache.get(&image_key) else {
1055                        error!("Could not find image key in image cache.");
1056                        continue;
1057                    };
1058                    txn.update_image(
1059                        image_key,
1060                        desc,
1061                        ImageData::new_shared(image.clone()),
1062                        &DirtyRect::All,
1063                    );
1064                },
1065            }
1066        }
1067
1068        if self.frame_delayer.needs_new_frame() {
1069            self.frame_delayer.set_pending_frame(false);
1070            self.generate_frame(&mut txn, RenderReasons::SCENE);
1071            let waiting_pipelines = self.frame_delayer.take_waiting_pipelines();
1072
1073            self.send_to_constellation(
1074                EmbedderToConstellationMessage::NoLongerWaitingOnAsynchronousImageUpdates(
1075                    waiting_pipelines,
1076                ),
1077            );
1078
1079            self.screenshot_taker
1080                .prepare_screenshot_requests_for_render(&*self);
1081        }
1082
1083        self.send_transaction(txn);
1084    }
1085
1086    pub(crate) fn delay_new_frames_for_canvas(
1087        &mut self,
1088        pipeline_id: PipelineId,
1089        canvas_epoch: Epoch,
1090        image_keys: Vec<ImageKey>,
1091    ) {
1092        self.frame_delayer
1093            .add_delay(pipeline_id, canvas_epoch, image_keys);
1094    }
1095
1096    pub(crate) fn add_font(
1097        &mut self,
1098        font_key: FontKey,
1099        data: Arc<GenericSharedMemory>,
1100        index: u32,
1101    ) {
1102        let mut transaction = Transaction::new();
1103        transaction.add_raw_font(font_key, (**data).into(), index);
1104        self.send_transaction(transaction);
1105    }
1106
1107    pub(crate) fn add_system_font(&mut self, font_key: FontKey, native_handle: NativeFontHandle) {
1108        let mut transaction = Transaction::new();
1109        transaction.add_native_font(font_key, native_handle);
1110        self.send_transaction(transaction);
1111    }
1112
1113    pub(crate) fn add_font_instance(
1114        &mut self,
1115        instance_key: FontInstanceKey,
1116        font_key: FontKey,
1117        size: f32,
1118        flags: FontInstanceFlags,
1119        variations: Vec<FontVariation>,
1120    ) {
1121        let variations = if pref!(layout_variable_fonts_enabled) {
1122            variations
1123        } else {
1124            vec![]
1125        };
1126
1127        let mut transaction = Transaction::new();
1128
1129        let font_instance_options = FontInstanceOptions {
1130            flags,
1131            ..Default::default()
1132        };
1133        transaction.add_font_instance(
1134            instance_key,
1135            font_key,
1136            size,
1137            Some(font_instance_options),
1138            None,
1139            variations,
1140        );
1141
1142        self.send_transaction(transaction);
1143    }
1144
1145    pub(crate) fn remove_fonts(&mut self, keys: Vec<FontKey>, instance_keys: Vec<FontInstanceKey>) {
1146        let mut transaction = Transaction::new();
1147
1148        for instance in instance_keys.into_iter() {
1149            transaction.delete_font_instance(instance);
1150        }
1151        for key in keys.into_iter() {
1152            transaction.delete_font(key);
1153        }
1154
1155        self.send_transaction(transaction);
1156    }
1157
1158    pub(crate) fn set_viewport_description(
1159        &mut self,
1160        webview_id: WebViewId,
1161        viewport_description: ViewportDescription,
1162    ) {
1163        if let Some(webview) = self.webview_renderers.get_mut(&webview_id) {
1164            webview.set_viewport_description(viewport_description);
1165        }
1166    }
1167
1168    pub(crate) fn handle_screenshot_readiness_reply(
1169        &self,
1170        webview_id: WebViewId,
1171        expected_epochs: FxHashMap<PipelineId, Epoch>,
1172    ) {
1173        self.screenshot_taker
1174            .handle_screenshot_readiness_reply(webview_id, expected_epochs, self);
1175    }
1176
1177    pub(crate) fn add_webview(
1178        &mut self,
1179        webview: Box<dyn WebViewTrait>,
1180        viewport_details: ViewportDetails,
1181    ) {
1182        self.webview_renderers
1183            .entry(webview.id())
1184            .or_insert(WebViewRenderer::new(
1185                webview,
1186                viewport_details,
1187                self.embedder_to_constellation_sender.clone(),
1188                self.refresh_driver.clone(),
1189                self.webrender_document,
1190            ));
1191    }
1192
1193    pub(crate) fn remove_webview(&mut self, webview_id: WebViewId) {
1194        if self.webview_renderers.remove(&webview_id).is_none() {
1195            warn!("Tried removing unknown WebView: {webview_id:?}");
1196            return;
1197        };
1198
1199        self.send_root_pipeline_display_list();
1200        self.lcp_calculator.enable_for_webview(&webview_id);
1201    }
1202
1203    pub(crate) fn is_empty(&mut self) -> bool {
1204        self.webview_renderers.is_empty()
1205    }
1206
1207    pub(crate) fn set_webview_hidden(
1208        &mut self,
1209        webview_id: WebViewId,
1210        hidden: bool,
1211    ) -> Result<(), UnknownWebView> {
1212        debug!("Setting WebView visiblity for {webview_id:?} to hidden={hidden}");
1213        let Some(webview_renderer) = self.webview_renderer_mut(webview_id) else {
1214            return Err(UnknownWebView(webview_id));
1215        };
1216        if !webview_renderer.set_hidden(hidden) {
1217            return Ok(());
1218        }
1219        self.send_root_pipeline_display_list();
1220        Ok(())
1221    }
1222
1223    pub(crate) fn set_hidpi_scale_factor(
1224        &mut self,
1225        webview_id: WebViewId,
1226        new_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1227    ) {
1228        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
1229            return;
1230        };
1231        if !webview_renderer.set_hidpi_scale_factor(new_scale_factor) {
1232            return;
1233        }
1234
1235        self.send_root_pipeline_display_list();
1236        self.set_needs_repaint(RepaintReason::Resize);
1237    }
1238
1239    pub(crate) fn resize_rendering_context(&mut self, new_size: PhysicalSize<u32>) {
1240        if self.rendering_context.size() == new_size {
1241            return;
1242        }
1243
1244        if let Err(error) = self.rendering_context.make_current() {
1245            error!("Failed to make the rendering context current: {error:?}");
1246        }
1247        self.rendering_context.resize(new_size);
1248
1249        let new_size = Size2D::new(new_size.width as f32, new_size.height as f32);
1250        let new_viewport_rect = Rect::from(new_size).to_box2d();
1251        for webview_renderer in self.webview_renderers.values_mut() {
1252            webview_renderer.set_rect(new_viewport_rect);
1253        }
1254
1255        let mut transaction = Transaction::new();
1256        transaction.set_document_view(new_viewport_rect.to_i32());
1257        self.send_transaction(transaction);
1258
1259        self.send_root_pipeline_display_list();
1260        self.set_needs_repaint(RepaintReason::Resize);
1261    }
1262
1263    pub(crate) fn set_page_zoom(&mut self, webview_id: WebViewId, new_zoom: f32) {
1264        if let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) {
1265            webview_renderer.set_page_zoom(Scale::new(new_zoom));
1266        }
1267    }
1268
1269    pub(crate) fn page_zoom(&self, webview_id: WebViewId) -> f32 {
1270        self.webview_renderers
1271            .get(&webview_id)
1272            .map(|webview_renderer| webview_renderer.page_zoom.get())
1273            .unwrap_or_default()
1274    }
1275
1276    pub(crate) fn notify_input_event(&mut self, webview_id: WebViewId, event: InputEventAndId) {
1277        if let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) {
1278            match &event.event {
1279                InputEvent::MouseMove(event) => {
1280                    // We only track the last mouse move position for non-touch events.
1281                    if !event.is_compatibility_event_for_touch {
1282                        let event_point = event
1283                            .point
1284                            .as_device_point(webview_renderer.device_pixels_per_page_pixel());
1285                        self.last_mouse_move_position = Some(event_point);
1286                    }
1287                },
1288                InputEvent::MouseLeftViewport(_) => {
1289                    self.last_mouse_move_position = None;
1290                },
1291                _ => {
1292                    // Disable LCP calculation on any other input event except mouse moves.
1293                    self.lcp_calculator.disable_for_webview(webview_id);
1294                },
1295            }
1296
1297            webview_renderer.notify_input_event(&self.webrender_api, &self.needs_repaint, event);
1298        }
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        // Disable LCP calculation on any scroll event.
1311        self.lcp_calculator.disable_for_webview(webview_id);
1312    }
1313
1314    pub(crate) fn enable_lcp_calculation(&mut self, webview_id: &WebViewId) {
1315        self.lcp_calculator.enable_for_webview(webview_id);
1316    }
1317
1318    pub(crate) fn lcp_calculation_enabled_for_webview(&self, webview_id: &WebViewId) -> bool {
1319        self.lcp_calculator.enabled_for_webview(webview_id)
1320    }
1321
1322    pub(crate) fn pinch_zoom(
1323        &mut self,
1324        webview_id: WebViewId,
1325        pinch_zoom_delta: f32,
1326        center: DevicePoint,
1327    ) {
1328        if let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) {
1329            webview_renderer.adjust_pinch_zoom(pinch_zoom_delta, center);
1330        }
1331    }
1332
1333    pub(crate) fn device_pixels_per_page_pixel(
1334        &self,
1335        webview_id: WebViewId,
1336    ) -> Scale<f32, CSSPixel, DevicePixel> {
1337        self.webview_renderers
1338            .get(&webview_id)
1339            .map(WebViewRenderer::device_pixels_per_page_pixel)
1340            .unwrap_or_default()
1341    }
1342
1343    pub(crate) fn request_screenshot(
1344        &self,
1345        webview_id: WebViewId,
1346        rect: Option<WebViewRect>,
1347        callback: Box<dyn FnOnce(Result<RgbaImage, ScreenshotCaptureError>) + 'static>,
1348    ) {
1349        let Some(webview) = self.webview_renderers.get(&webview_id) else {
1350            return;
1351        };
1352
1353        let rect = rect.map(|rect| rect.as_device_rect(webview.device_pixels_per_page_pixel()));
1354        self.screenshot_taker
1355            .request_screenshot(webview_id, rect, callback);
1356        self.send_to_constellation(EmbedderToConstellationMessage::RequestScreenshotReadiness(
1357            webview_id,
1358        ));
1359    }
1360
1361    pub(crate) fn notify_input_event_handled(
1362        &mut self,
1363        webview_id: WebViewId,
1364        input_event_id: InputEventId,
1365        result: InputEventResult,
1366    ) {
1367        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
1368            warn!("Handled input event for unknown webview: {webview_id}");
1369            return;
1370        };
1371        webview_renderer.notify_input_event_handled(
1372            &self.webrender_api,
1373            &self.needs_repaint,
1374            input_event_id,
1375            result,
1376        );
1377    }
1378
1379    pub(crate) fn refresh_cursor(&self) {
1380        let Some(last_mouse_move_position) = self.last_mouse_move_position else {
1381            return;
1382        };
1383
1384        let Some(hit_test_result) = Self::hit_test_at_point_with_api_and_document(
1385            &self.webrender_api,
1386            self.webrender_document,
1387            last_mouse_move_position,
1388        )
1389        .first()
1390        .cloned() else {
1391            return;
1392        };
1393
1394        if let Err(error) = self.embedder_to_constellation_sender.send(
1395            EmbedderToConstellationMessage::RefreshCursor(hit_test_result.pipeline_id),
1396        ) {
1397            warn!("Sending event to constellation failed ({:?}).", error);
1398        }
1399    }
1400
1401    pub(crate) fn handle_new_webrender_frame_ready(&self, repaint_needed: bool) {
1402        if repaint_needed {
1403            self.refresh_cursor()
1404        }
1405
1406        if repaint_needed || self.animation_callbacks_running() {
1407            self.set_needs_repaint(RepaintReason::NewWebRenderFrame);
1408        }
1409
1410        // If we received a new frame and a repaint isn't necessary, it may be that this
1411        // is the last frame that was pending. In that case, trigger a manual repaint so
1412        // that the screenshot can be taken at the end of the repaint procedure.
1413        if !repaint_needed {
1414            self.screenshot_taker
1415                .maybe_trigger_paint_for_screenshot(self);
1416        }
1417    }
1418
1419    pub(crate) fn webviews_needing_repaint(&self) -> Vec<WebViewId> {
1420        if self.needs_repaint() {
1421            self.webview_renderers
1422                .values()
1423                .map(|webview_renderer| webview_renderer.id)
1424                .collect()
1425        } else {
1426            Vec::new()
1427        }
1428    }
1429
1430    pub(crate) fn scroll_trees_memory_usage(
1431        &self,
1432        ops: &mut malloc_size_of::MallocSizeOfOps,
1433    ) -> usize {
1434        self.webview_renderers
1435            .values()
1436            .map(|renderer| renderer.scroll_trees_memory_usage(ops))
1437            .sum::<usize>()
1438    }
1439
1440    pub(crate) fn append_lcp_candidate(
1441        &mut self,
1442        lcp_candidate: LCPCandidate,
1443        webview_id: WebViewId,
1444        pipeline_id: PipelineId,
1445        epoch: Epoch,
1446    ) {
1447        if self.lcp_calculation_enabled_for_webview(&webview_id) {
1448            self.lcp_calculator.append_lcp_candidate(
1449                lcp_candidate,
1450                pipeline_id.into(),
1451                &webview_id,
1452            );
1453            if let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) {
1454                webview_renderer
1455                    .ensure_pipeline_details(pipeline_id)
1456                    .largest_contentful_paint_metric
1457                    .set(PaintMetricState::Seen(epoch.into(), false));
1458            }
1459        };
1460    }
1461}
1462
1463/// A struct that is reponsible for delaying frame requests until all new canvas images
1464/// for a particular "update the rendering" call in the `ScriptThread` have been
1465/// sent to WebRender.
1466///
1467/// These images may be updated in WebRender asynchronously in the canvas task. A frame
1468/// is then requested if:
1469///
1470///  - The renderer has received a GenerateFrame message from a `ScriptThread`.
1471///  - All pending image updates have finished and have been noted in the [`FrameDelayer`].
1472#[derive(Default)]
1473pub(crate) struct FrameDelayer {
1474    /// The latest [`Epoch`] of canvas images that have been sent to WebRender. Note
1475    /// that this only records the `Epoch`s for canvases and only ones that are involved
1476    /// in "update the rendering".
1477    image_epochs: FxHashMap<ImageKey, Epoch>,
1478    /// A map of all pending canvas images
1479    pending_canvas_images: FxHashMap<ImageKey, Epoch>,
1480    /// Whether or not we have a pending frame.
1481    pub(crate) pending_frame: bool,
1482    /// A list of pipelines that should be notified when we are no longer waiting for
1483    /// canvas images.
1484    waiting_pipelines: FxHashSet<PipelineId>,
1485}
1486
1487impl FrameDelayer {
1488    pub(crate) fn delete_image(&mut self, image_key: ImageKey) {
1489        self.image_epochs.remove(&image_key);
1490        self.pending_canvas_images.remove(&image_key);
1491    }
1492
1493    pub(crate) fn update_image(&mut self, image_key: ImageKey, epoch: Epoch) {
1494        self.image_epochs.insert(image_key, epoch);
1495        let Entry::Occupied(entry) = self.pending_canvas_images.entry(image_key) else {
1496            return;
1497        };
1498        if *entry.get() <= epoch {
1499            entry.remove();
1500        }
1501    }
1502
1503    pub(crate) fn add_delay(
1504        &mut self,
1505        pipeline_id: PipelineId,
1506        canvas_epoch: Epoch,
1507        image_keys: Vec<ImageKey>,
1508    ) {
1509        for image_key in image_keys.into_iter() {
1510            // If we've already seen the necessary epoch for this image, do not
1511            // start waiting for it.
1512            if self
1513                .image_epochs
1514                .get(&image_key)
1515                .is_some_and(|epoch_seen| *epoch_seen >= canvas_epoch)
1516            {
1517                continue;
1518            }
1519            self.pending_canvas_images.insert(image_key, canvas_epoch);
1520        }
1521        self.waiting_pipelines.insert(pipeline_id);
1522    }
1523
1524    pub(crate) fn needs_new_frame(&self) -> bool {
1525        self.pending_frame && self.pending_canvas_images.is_empty()
1526    }
1527
1528    pub(crate) fn set_pending_frame(&mut self, value: bool) {
1529        self.pending_frame = value;
1530    }
1531
1532    pub(crate) fn take_waiting_pipelines(&mut self) -> Vec<PipelineId> {
1533        self.waiting_pipelines.drain().collect()
1534    }
1535}
1536
1537/// The paint status of a particular pipeline in a [`Painter`]. This is used to trigger metrics
1538/// in script (via the constellation) when display lists are received.
1539///
1540/// See <https://w3c.github.io/paint-timing/#first-contentful-paint>.
1541#[derive(Clone, Copy, PartialEq)]
1542pub(crate) enum PaintMetricState {
1543    /// The painter is still waiting to process a display list which triggers this metric.
1544    Waiting,
1545    /// The painter has processed the display list which will trigger this event, marked the Servo
1546    /// instance ready to paint, and is waiting for the given epoch to actually be rendered.
1547    Seen(WebRenderEpoch, bool /* first_reflow */),
1548    /// The metric has been sent to the constellation and no more work needs to be done.
1549    Sent,
1550}