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