Skip to main content

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