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