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::{
22    PaintDisplayListInfo, PaintTimingInfo, PaintTimingReport, ScrollType,
23};
24use paint_api::rendering_context::RenderingContext;
25use paint_api::viewport_description::ViewportDescription;
26use paint_api::{
27    ImageUpdate, PipelineExitSource, SendableFrameTree, SerializableDisplayListPayload,
28    SerializableImageData, WebRenderExternalImageHandlers, WebRenderImageHandlerType, WebViewTrait,
29};
30use profile_traits::time::{ProfilerCategory, ProfilerChan};
31use profile_traits::time_profile;
32use rustc_hash::{FxHashMap, FxHashSet};
33use servo_base::Epoch;
34use servo_base::cross_process_instant::CrossProcessInstant;
35use servo_base::generic_channel::{GenericReceiver, GenericSharedMemory};
36use servo_base::id::{PainterId, PipelineId, WebViewId};
37use servo_base::threadboost::{BoostAffinity, ThreadPriority};
38use servo_config::{opts, pref};
39use servo_constellation_traits::{EmbedderToConstellationMessage, PaintMetricEvent};
40use servo_geometry::DeviceIndependentPixel;
41use smallvec::SmallVec;
42use style_traits::CSSPixel;
43use webrender::{
44    MemoryReport, ONE_TIME_USAGE_HINT, RenderApi, RenderBackendHooks, SceneBuilderHooks,
45    ShaderPrecacheFlags, Transaction, UploadMethod,
46};
47use webrender_api::units::{
48    DevicePixel, DevicePoint, LayoutPoint, LayoutRect, LayoutSize, LayoutVector2D, WorldPoint,
49};
50use webrender_api::{
51    self, BuiltDisplayList, BuiltDisplayListDescriptor, ColorF, DirtyRect, DisplayListPayload,
52    DocumentId, DynamicProperties, Epoch as WebRenderEpoch, ExternalScrollId, FontInstanceFlags,
53    FontInstanceKey, FontInstanceOptions, FontInstancePlatformOptions, FontKey, FontVariation,
54    ImageData, ImageKey, NativeFontHandle, PipelineId as WebRenderPipelineId, PropertyBinding,
55    PropertyBindingKey, PropertyValue, ReferenceFrameKind, RenderReasons, SampledScrollOffset,
56    SpaceAndClipInfo, SpatialId, TransformStyle,
57};
58use wr_malloc_size_of::MallocSizeOfOps;
59
60use crate::Paint;
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    /// 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    /// A counter used to generate new property binding keys for [`WebView`]s.
137    property_binding_key_counter: Cell<u64>,
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            animation_image_cache: FxHashMap::default(),
296            web_content_animator: WebContentAnimator::new(
297                paint.event_loop_waker.clone_box(),
298                (*timer_refresh_driver).clone(),
299            ),
300            property_binding_key_counter: Default::default(),
301        };
302        painter.assert_gl_framebuffer_complete();
303        painter.clear_background();
304        painter
305    }
306
307    fn generate_property_binding_key<T>(&self) -> PropertyBindingKey<T> {
308        let value = self.property_binding_key_counter.get();
309        self.property_binding_key_counter
310            .update(|counter| counter + 1);
311        PropertyBindingKey::<T>::new(value)
312    }
313
314    pub(crate) fn perform_updates(&mut self) {
315        let mut need_zoom = false;
316        let scroll_offset_updates: Vec<_> = self
317            .webview_renderers
318            .values_mut()
319            .filter_map(|webview_renderer| {
320                let (zoom, scroll_result) = webview_renderer
321                    .process_pending_scroll_and_pinch_zoom_events(&self.webrender_api);
322                need_zoom = need_zoom || (zoom == PinchZoomResult::DidPinchZoom);
323                scroll_result
324            })
325            .collect();
326
327        let dynamic_properties_updated =
328            self.web_content_animator.update(&self.webview_renderers) || need_zoom;
329        self.maybe_send_scroll_and_dynamic_property_updates_transaction(
330            dynamic_properties_updated,
331            scroll_offset_updates,
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        let notified_refresh_driver_observers = 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        if !notified_refresh_driver_observers {
447            self.set_needs_repaint(RepaintReason::FrameStart);
448        }
449
450        self.screenshot_taker.maybe_take_screenshots(self);
451        self.send_pending_paint_metrics_messages_after_composite();
452    }
453
454    fn clear_background(&self) {
455        self.assert_gl_framebuffer_complete();
456
457        // Always clear the entire RenderingContext, regardless of how many WebViews there are
458        // or where they are positioned. This is so WebView actually clears even before the
459        // first WebView is ready.
460        let color = servo_config::pref!(shell_background_color_rgba);
461        self.webrender_gl.clear_color(
462            color[0] as f32,
463            color[1] as f32,
464            color[2] as f32,
465            color[3] as f32,
466        );
467        self.webrender_gl.clear(gleam::gl::COLOR_BUFFER_BIT);
468    }
469
470    /// Send all pending paint metrics messages after a composite operation, which may advance
471    /// the epoch for pipelines in the WebRender scene.
472    ///
473    /// If there are pending paint metrics, we check if any of the painted epochs is one
474    /// of the ones that the paint metrics recorder is expecting. In that case, we get the
475    /// current time, inform the constellation about it and remove the pending metric from
476    /// the list.
477    fn send_pending_paint_metrics_messages_after_composite(&mut self) {
478        // From: <https://www.w3.org/TR/paint-timing/#mark-paint-timing>
479        // Step 12.2. Set paintTimingInfo’s implementation-defined presentation
480        // time to the current high resolution time given document’s relevant
481        // global object.
482        //
483        // Note: As per Servo, this is next in line after `Painter::render`,
484        // when the frame is presented to the screen. So we append current time
485        // to [`PaintTimingInfo`] and send it to constellation.
486        let paint_time = CrossProcessInstant::now();
487        let mut paint_metric_events = Vec::new();
488
489        for webview_renderer in self.webview_renderers.values_mut() {
490            for (pipeline_id, pipeline) in webview_renderer.pipelines.iter_mut() {
491                let Some(current_epoch) = self
492                    .webrender_renderer
493                    .as_ref()
494                    .and_then(|wr| wr.current_epoch(self.webrender_document, pipeline_id.into()))
495                else {
496                    continue;
497                };
498
499                match pipeline.first_paint_metric {
500                    // We need to check whether the current epoch is later, because
501                    // CrossProcessPaintMessage::SendInitialTransaction sends an
502                    // empty display list to WebRender which can happen before we receive
503                    // the first "real" display list.
504                    PaintMetricState::Seen(epoch, first_reflow, paint_timing_info)
505                        if epoch <= current_epoch =>
506                    {
507                        assert!(epoch <= current_epoch);
508                        #[cfg(feature = "tracing")]
509                        tracing::info!(
510                            name: "FirstPaint",
511                            servo_profiling = true,
512                            epoch = ?epoch,
513                            paint_time = ?paint_time,
514                            pipeline_id = ?pipeline_id,
515                        );
516
517                        paint_metric_events.push((
518                            *pipeline_id,
519                            PaintMetricEvent::FirstPaint(
520                                paint_timing_info.with_presentation_time(paint_time),
521                                first_reflow,
522                            ),
523                        ));
524
525                        pipeline.first_paint_metric = PaintMetricState::Sent;
526                    },
527                    _ => {},
528                }
529
530                match pipeline.first_contentful_paint_metric {
531                    PaintMetricState::Seen(epoch, first_reflow, paint_timing_info)
532                        if epoch <= current_epoch =>
533                    {
534                        #[cfg(feature = "tracing")]
535                        tracing::info!(
536                            name: "FirstContentfulPaint",
537                            servo_profiling = true,
538                            epoch = ?epoch,
539                            paint_time = ?paint_time,
540                            pipeline_id = ?pipeline_id,
541                        );
542                        paint_metric_events.push((
543                            *pipeline_id,
544                            PaintMetricEvent::FirstContentfulPaint(
545                                paint_timing_info.with_presentation_time(paint_time),
546                                first_reflow,
547                            ),
548                        ));
549                        pipeline.first_contentful_paint_metric = PaintMetricState::Sent;
550                    },
551                    _ => {},
552                }
553
554                let pending_lcp_candidates = &mut pipeline.lcp_candidates;
555                while let Some((epoch, (id, area), paint_timing_info)) =
556                    pending_lcp_candidates.pop_front()
557                {
558                    if epoch > current_epoch {
559                        pending_lcp_candidates.push_front((epoch, (id, area), paint_timing_info));
560                        break;
561                    }
562                    #[cfg(feature = "tracing")]
563                    tracing::info!(
564                        name: "LargestContentfulPaint",
565                        servo_profiling = true,
566                        paint_time = ?paint_time,
567                        area = ?area,
568                        pipeline_id = ?pipeline_id,
569                    );
570                    paint_metric_events.push((
571                        *pipeline_id,
572                        PaintMetricEvent::LargestContentfulPaint(
573                            paint_timing_info.with_presentation_time(paint_time),
574                            id,
575                        ),
576                    ));
577                }
578
579                let pending_container_timing = &mut pipeline.container_timing_candidates;
580                while let Some((epoch, ids)) = pending_container_timing.pop_front() {
581                    if epoch > current_epoch {
582                        pending_container_timing.push_front((epoch, ids));
583                        break;
584                    }
585                    #[cfg(feature = "tracing")]
586                    tracing::info!(
587                        name: "ContainerTiming",
588                        servo_profiling = true,
589                        paint_time = ?paint_time,
590                        count = ids.len(),
591                        pipeline_id = ?pipeline_id,
592                    );
593                    paint_metric_events.push((
594                        *pipeline_id,
595                        PaintMetricEvent::ContainerTiming(paint_time, ids),
596                    ));
597                }
598            }
599        }
600
601        for (pipeline_id, event) in paint_metric_events {
602            self.send_to_constellation(EmbedderToConstellationMessage::PaintMetric(
603                pipeline_id,
604                event,
605            ));
606        }
607    }
608
609    /// Queue a new frame in the transaction and increase the pending frames count.
610    pub(crate) fn generate_frame(&self, transaction: &mut Transaction, reason: RenderReasons) {
611        transaction.generate_frame(0, true /* present */, false /* tracked */, reason);
612        self.pending_frames.set(self.pending_frames.get() + 1);
613    }
614
615    pub(crate) fn hit_test_at_point_with_api_and_document(
616        webrender_api: &RenderApi,
617        webrender_document: DocumentId,
618        point: DevicePoint,
619    ) -> Vec<PaintHitTestResult> {
620        // DevicePoint and WorldPoint are the same for us.
621        let world_point = WorldPoint::from_untyped(point.to_untyped());
622        let results = webrender_api.hit_test(webrender_document, world_point);
623
624        results
625            .items
626            .iter()
627            .map(|item| {
628                let pipeline_id = item.pipeline.into();
629                let external_scroll_id = ExternalScrollId(item.tag.0, item.pipeline);
630                PaintHitTestResult {
631                    pipeline_id,
632                    point_in_viewport: Point2D::from_untyped(item.point_in_viewport.to_untyped()),
633                    external_scroll_id,
634                }
635            })
636            .collect()
637    }
638
639    pub(crate) fn send_transaction(&mut self, transaction: Transaction) {
640        let _ = self.rendering_context.make_current();
641        self.webrender_api
642            .send_transaction(self.webrender_document, transaction);
643    }
644
645    /// Adds a reset operation that resets all dynamic properties in this WebRender instance,
646    /// and then adds all available dynamic properties (with their current values) to the
647    /// transaction. This resets the state of all dynamic WebRender properties.
648    fn add_dynamic_properties_to_transaction(&self, transaction: &mut Transaction) {
649        transaction.reset_dynamic_properties();
650        let colors = self.web_content_animator.values(&self.webview_renderers);
651        let transforms: Vec<_> = self
652            .webview_renderers
653            .values()
654            .filter_map(|webview_renderer| {
655                if webview_renderer.hidden() {
656                    return None;
657                }
658                Some(PropertyValue {
659                    key: webview_renderer.pinch_zoom_property_binding_key(),
660                    value: webview_renderer.root_transform(),
661                })
662            })
663            .collect();
664        transaction.append_dynamic_properties(DynamicProperties {
665            transforms,
666            floats: Vec::new(),
667            colors,
668        });
669    }
670
671    /// Set the root pipeline for our WebRender scene to a display list that consists of an iframe
672    /// for each visible top-level browsing context, applying a transformation on the root for
673    /// pinch zoom, page zoom, and HiDPI scaling.
674    fn send_root_pipeline_display_list_in_transaction(&self, transaction: &mut Transaction) {
675        // Every display list needs a pipeline, but we'd like to choose one that is unlikely
676        // to conflict with our content pipelines, which start at (1, 1). (0, 0) is WebRender's
677        // dummy pipeline, so we choose (0, 1).
678        let root_pipeline = WebRenderPipelineId(0, 1);
679        transaction.set_root_pipeline(root_pipeline);
680
681        let mut builder = webrender_api::DisplayListBuilder::new(root_pipeline);
682        builder.begin();
683
684        let root_reference_frame = SpatialId::root_reference_frame(root_pipeline);
685
686        let viewport_size = self.rendering_context.size2d().to_f32().to_untyped();
687        let viewport_rect = LayoutRect::from_origin_and_size(
688            LayoutPoint::zero(),
689            LayoutSize::from_untyped(viewport_size),
690        );
691
692        let root_clip_id = builder.define_clip_rect(root_reference_frame, viewport_rect);
693        let clip_chain_id = builder.define_clip_chain(None, [root_clip_id]);
694        for webview_renderer in self.webview_renderers.values() {
695            if webview_renderer.hidden() {
696                continue;
697            }
698            let Some(pipeline_id) = webview_renderer.root_pipeline_id else {
699                continue;
700            };
701
702            let transform = webview_renderer.root_transform();
703            let property_binding = PropertyBinding::Binding(
704                webview_renderer.pinch_zoom_property_binding_key(),
705                transform,
706            );
707
708            let webview_reference_frame = builder.push_reference_frame(
709                LayoutPoint::zero(),
710                root_reference_frame,
711                TransformStyle::Flat,
712                property_binding,
713                ReferenceFrameKind::Transform {
714                    is_2d_scale_translation: true,
715                    should_snap: true,
716                    paired_with_perspective: false,
717                },
718            );
719
720            let scaled_webview_rect = webview_renderer.rect /
721                webview_renderer.device_pixels_per_page_pixel_not_including_pinch_zoom();
722            builder.push_iframe(
723                LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()),
724                LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()),
725                &SpaceAndClipInfo {
726                    spatial_id: webview_reference_frame,
727                    clip_chain_id,
728                },
729                pipeline_id.into(),
730                true,
731            );
732        }
733
734        let built_display_list = builder.end();
735
736        // NB: We are always passing 0 as the epoch here, but this doesn't seem to
737        // be an issue. WebRender will still update the scene and generate a new
738        // frame even though the epoch hasn't changed.
739        transaction.set_display_list(WebRenderEpoch(0), built_display_list);
740        self.update_transaction_with_all_scroll_offsets(transaction);
741        self.add_dynamic_properties_to_transaction(transaction);
742    }
743
744    /// Set the root pipeline for our WebRender scene to a display list that consists of an iframe
745    /// for each visible top-level browsing context, applying a transformation on the root for
746    /// pinch zoom, page zoom, and HiDPI scaling.
747    fn send_root_pipeline_display_list(&mut self) {
748        let mut transaction = Transaction::new();
749        self.send_root_pipeline_display_list_in_transaction(&mut transaction);
750        self.generate_frame(&mut transaction, RenderReasons::SCENE);
751        self.send_transaction(transaction);
752    }
753
754    /// Update the given transaction with the scroll offsets of all active scroll nodes in
755    /// the WebRender scene. This is necessary because WebRender does not preserve scroll
756    /// offsets between scroll tree modifications. If a display list could potentially
757    /// modify a scroll tree branch, WebRender needs to have scroll offsets for that
758    /// branch.
759    ///
760    /// TODO(mrobinson): Could we only send offsets for the branch being modified
761    /// and not the entire scene?
762    fn update_transaction_with_all_scroll_offsets(&self, transaction: &mut Transaction) {
763        for webview_renderer in self.webview_renderers.values() {
764            for details in webview_renderer.pipelines.values() {
765                for node in details.scroll_tree.nodes.iter() {
766                    let (Some(offset), Some(external_id)) = (node.offset(), node.external_id())
767                    else {
768                        continue;
769                    };
770                    // Skip scroll offsets that are zero, as they are the default.
771                    if offset == LayoutVector2D::zero() {
772                        continue;
773                    }
774                    transaction.set_scroll_offsets(
775                        external_id,
776                        vec![SampledScrollOffset {
777                            offset,
778                            generation: 0,
779                        }],
780                    );
781                }
782            }
783        }
784    }
785
786    fn maybe_send_scroll_and_dynamic_property_updates_transaction(
787        &mut self,
788        dynamic_properties_updated: bool,
789        scroll_offset_updates: Vec<ScrollResult>,
790    ) {
791        if scroll_offset_updates.is_empty() && !dynamic_properties_updated {
792            return;
793        }
794
795        let mut transaction = Transaction::new();
796        if dynamic_properties_updated {
797            self.add_dynamic_properties_to_transaction(&mut transaction);
798        }
799
800        for update in scroll_offset_updates {
801            transaction.set_scroll_offsets(
802                update.external_scroll_id,
803                vec![SampledScrollOffset {
804                    offset: update.offset,
805                    generation: 0,
806                }],
807            );
808        }
809
810        self.generate_frame(
811            &mut transaction,
812            RenderReasons::ANIMATED_PROPERTY | RenderReasons::APZ,
813        );
814        self.send_transaction(transaction);
815    }
816
817    pub(crate) fn toggle_webrender_debug(&mut self, option: WebRenderDebugOption) {
818        let Some(renderer) = self.webrender_renderer.as_mut() else {
819            return;
820        };
821        let mut flags = renderer.get_debug_flags();
822        let flag = match option {
823            WebRenderDebugOption::Profiler => {
824                webrender::DebugFlags::PROFILER_DBG |
825                    webrender::DebugFlags::GPU_TIME_QUERIES |
826                    webrender::DebugFlags::GPU_SAMPLE_QUERIES
827            },
828            WebRenderDebugOption::TextureCacheDebug => webrender::DebugFlags::TEXTURE_CACHE_DBG,
829            WebRenderDebugOption::RenderTargetDebug => webrender::DebugFlags::RENDER_TARGET_DBG,
830        };
831        flags.toggle(flag);
832        renderer.set_debug_flags(flags);
833
834        let mut txn = Transaction::new();
835        self.generate_frame(&mut txn, RenderReasons::TESTING);
836        self.send_transaction(txn);
837    }
838
839    pub(crate) fn decrement_pending_frames(&self) {
840        self.pending_frames.set(self.pending_frames.get() - 1);
841    }
842
843    pub(crate) fn report_memory(&self) -> MemoryReport {
844        self.webrender_api
845            .report_memory(MallocSizeOfOps::new(servo_allocator::usable_size, None))
846    }
847
848    pub(crate) fn change_running_animations_state(
849        &mut self,
850        webview_id: WebViewId,
851        pipeline_id: PipelineId,
852        animation_state: embedder_traits::AnimationState,
853    ) {
854        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
855            return;
856        };
857        if !webview_renderer.change_pipeline_running_animations_state(pipeline_id, animation_state)
858        {
859            return;
860        }
861        if !self
862            .animation_refresh_driver_observer
863            .notify_animation_state_changed(webview_renderer)
864        {
865            return;
866        }
867
868        self.refresh_driver
869            .add_observer(self.animation_refresh_driver_observer.clone());
870    }
871
872    pub(crate) fn set_frame_tree_for_webview(&mut self, frame_tree: &SendableFrameTree) {
873        debug!("{}: Setting frame tree for webview", frame_tree.pipeline.id);
874
875        let webview_id = frame_tree.pipeline.webview_id;
876        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
877            warn!(
878                "Attempted to set frame tree on unknown WebView (perhaps closed?): {webview_id:?}"
879            );
880            return;
881        };
882
883        webview_renderer.set_frame_tree(frame_tree);
884        self.send_root_pipeline_display_list();
885    }
886
887    pub(crate) fn set_throttled(
888        &mut self,
889        webview_id: WebViewId,
890        pipeline_id: PipelineId,
891        throttled: bool,
892    ) {
893        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
894            return;
895        };
896        if !webview_renderer.set_throttled(pipeline_id, throttled) {
897            return;
898        }
899
900        if self
901            .animation_refresh_driver_observer
902            .notify_animation_state_changed(webview_renderer)
903        {
904            self.refresh_driver
905                .add_observer(self.animation_refresh_driver_observer.clone());
906        }
907    }
908
909    pub(crate) fn notify_pipeline_exited(
910        &mut self,
911        webview_id: WebViewId,
912        pipeline_id: PipelineId,
913        pipeline_exit_source: PipelineExitSource,
914    ) {
915        debug!("Paint got pipeline exited: {webview_id:?} {pipeline_id:?}",);
916        let fully_exited =
917            self.webview_renderers
918                .get_mut(&webview_id)
919                .is_some_and(|webview_renderer| {
920                    webview_renderer.pipeline_exited(pipeline_id, pipeline_exit_source)
921                });
922
923        // Once every part of Servo has finished with this `Pipeline`, drop its
924        // retained scene state (built display list, spatial/clip data) from
925        // WebRender as well. Without this, `Scene.pipelines` accumulates one
926        // entry per navigation for the lifetime of the process.
927        if fully_exited {
928            let mut transaction = Transaction::new();
929            transaction.remove_pipeline(pipeline_id.into());
930            self.send_transaction(transaction);
931        }
932    }
933
934    pub(crate) fn send_initial_pipeline_transaction(
935        &mut self,
936        webview_id: WebViewId,
937        pipeline_id: WebRenderPipelineId,
938    ) {
939        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
940            return warn!("Could not find WebView for incoming display list");
941        };
942
943        let starting_epoch = Epoch(0);
944        let details = webview_renderer.ensure_pipeline_details(pipeline_id.into());
945        details.display_list_epoch = Some(starting_epoch);
946
947        let mut txn = Transaction::new();
948        txn.set_display_list(starting_epoch.into(), (pipeline_id, Default::default()));
949
950        self.generate_frame(&mut txn, RenderReasons::SCENE);
951        self.send_transaction(txn);
952    }
953
954    pub(crate) fn scroll_node_by_delta(
955        &mut self,
956        webview_id: WebViewId,
957        pipeline_id: WebRenderPipelineId,
958        offset: LayoutVector2D,
959        external_scroll_id: webrender_api::ExternalScrollId,
960    ) {
961        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
962            return;
963        };
964
965        let pipeline_id = pipeline_id.into();
966        let Some(pipeline_details) = webview_renderer.pipelines.get_mut(&pipeline_id) else {
967            return;
968        };
969
970        let Some(offset) = pipeline_details
971            .scroll_tree
972            .set_scroll_offset_for_node_with_external_scroll_id(
973                external_scroll_id,
974                offset,
975                ScrollType::Script,
976            )
977        else {
978            // The renderer should be fully up-to-date with script at this point and script
979            // should never try to scroll to an invalid location.
980            warn!("Could not scroll node with id: {external_scroll_id:?}");
981            return;
982        };
983
984        let mut transaction = Transaction::new();
985        transaction.set_scroll_offsets(
986            external_scroll_id,
987            vec![SampledScrollOffset {
988                offset,
989                generation: 0,
990            }],
991        );
992
993        self.generate_frame(&mut transaction, RenderReasons::APZ);
994        self.send_transaction(transaction);
995    }
996
997    pub(crate) fn scroll_viewport_by_delta(
998        &mut self,
999        webview_id: WebViewId,
1000        delta: LayoutVector2D,
1001    ) {
1002        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
1003            return;
1004        };
1005        let (pinch_zoom_result, scroll_results) = webview_renderer.scroll_viewport_by_delta(delta);
1006        let did_pinch_zoom = pinch_zoom_result == PinchZoomResult::DidPinchZoom;
1007        self.maybe_send_scroll_and_dynamic_property_updates_transaction(
1008            did_pinch_zoom,
1009            scroll_results,
1010        );
1011    }
1012
1013    pub(crate) fn update_epoch(
1014        &mut self,
1015        webview_id: WebViewId,
1016        pipeline_id: PipelineId,
1017        epoch: Epoch,
1018    ) {
1019        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
1020            return warn!("Could not find WebView for Epoch update.");
1021        };
1022        webview_renderer
1023            .ensure_pipeline_details(pipeline_id)
1024            .display_list_epoch = Some(Epoch(epoch.0));
1025    }
1026
1027    #[servo_tracing::instrument(skip_all)]
1028    pub(crate) fn handle_new_display_list(
1029        &mut self,
1030        webview_id: WebViewId,
1031        display_list_descriptor: BuiltDisplayListDescriptor,
1032        display_list_info_receiver: GenericReceiver<PaintDisplayListInfo>,
1033        display_list_data_receiver: GenericReceiver<SerializableDisplayListPayload>,
1034    ) {
1035        let Ok(display_list_info) = display_list_info_receiver.recv() else {
1036            return log::error!("Could not receive display list info");
1037        };
1038        let Ok(display_list_data) = display_list_data_receiver.recv() else {
1039            return log::error!("Could not receive display list data");
1040        };
1041
1042        let items_data = display_list_data.items_data;
1043        let spatial_tree = display_list_data.spatial_tree;
1044
1045        let built_display_list = BuiltDisplayList::from_data(
1046            DisplayListPayload {
1047                items_data,
1048                spatial_tree,
1049            },
1050            display_list_descriptor,
1051        );
1052        let _span = profile_traits::trace_span!("PaintMessage::SendDisplayList").entered();
1053        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
1054            return warn!("Could not find WebView for incoming display list");
1055        };
1056
1057        let old_scale = webview_renderer.device_pixels_per_page_pixel();
1058        let pipeline_id = display_list_info.pipeline_id;
1059        let details = webview_renderer.ensure_pipeline_details(pipeline_id.into());
1060
1061        details.install_new_scroll_tree(display_list_info.scroll_tree);
1062        details.viewport_scale = Some(display_list_info.viewport_details.hidpi_scale_factor);
1063
1064        let epoch = display_list_info.epoch.into();
1065        let first_reflow = display_list_info.first_reflow;
1066        if details.first_paint_metric == PaintMetricState::Waiting &&
1067            display_list_info
1068                .paint_timing_report
1069                .contains(PaintTimingReport::FirstPaint)
1070        {
1071            details.first_paint_metric =
1072                PaintMetricState::Seen(epoch, first_reflow, display_list_info.paint_timing_info);
1073        }
1074
1075        if details.first_contentful_paint_metric == PaintMetricState::Waiting &&
1076            display_list_info
1077                .paint_timing_report
1078                .contains(PaintTimingReport::FirstContentfulPaint)
1079        {
1080            details.first_contentful_paint_metric =
1081                PaintMetricState::Seen(epoch, first_reflow, display_list_info.paint_timing_info);
1082        }
1083
1084        if let Some(lcp_candidate) = display_list_info.lcp_candidate {
1085            details.lcp_candidates.push_back((
1086                epoch,
1087                lcp_candidate,
1088                display_list_info.paint_timing_info,
1089            ));
1090        }
1091
1092        let container_timing_candidates = display_list_info.container_timing_candidates;
1093        if !container_timing_candidates.is_empty() {
1094            details
1095                .container_timing_candidates
1096                .push_back((epoch, container_timing_candidates));
1097        }
1098
1099        details.animations.handle_new_display_list(
1100            display_list_info.caret_property_binding,
1101            &self.web_content_animator,
1102        );
1103
1104        let mut transaction = Transaction::new();
1105        let is_root_pipeline = Some(pipeline_id.into()) == webview_renderer.root_pipeline_id;
1106        if is_root_pipeline && old_scale != webview_renderer.device_pixels_per_page_pixel() {
1107            self.send_root_pipeline_display_list_in_transaction(&mut transaction);
1108        }
1109
1110        transaction.set_display_list(epoch, (pipeline_id, built_display_list));
1111
1112        self.update_transaction_with_all_scroll_offsets(&mut transaction);
1113        self.send_transaction(transaction);
1114    }
1115
1116    pub(crate) fn generate_frame_for_script(&mut self) {
1117        self.frame_delayer.set_pending_frame(true);
1118
1119        if !self.frame_delayer.needs_new_frame() {
1120            return;
1121        }
1122
1123        let mut transaction = Transaction::new();
1124        self.generate_frame(&mut transaction, RenderReasons::SCENE);
1125        self.send_transaction(transaction);
1126
1127        let waiting_pipelines = self.frame_delayer.take_waiting_pipelines();
1128
1129        self.send_to_constellation(
1130            EmbedderToConstellationMessage::NoLongerWaitingOnAsynchronousImageUpdates(
1131                waiting_pipelines,
1132            ),
1133        );
1134
1135        self.frame_delayer.set_pending_frame(false);
1136        self.screenshot_taker
1137            .prepare_screenshot_requests_for_render(self)
1138    }
1139
1140    fn serializable_image_data_to_image_data_maybe_caching(
1141        &mut self,
1142        key: ImageKey,
1143        data: SerializableImageData,
1144        is_animated_image: bool,
1145    ) -> ImageData {
1146        match data {
1147            SerializableImageData::Raw(shared_memory) => {
1148                let data = shared_memory.into_arc_vec();
1149                if is_animated_image {
1150                    self.animation_image_cache.insert(key, Arc::clone(&data));
1151                }
1152                ImageData::Raw(data)
1153            },
1154            SerializableImageData::External(image) => ImageData::External(image),
1155        }
1156    }
1157
1158    pub(crate) fn update_images(&mut self, updates: SmallVec<[ImageUpdate; 1]>) {
1159        let mut txn = Transaction::new();
1160        for update in updates {
1161            match update {
1162                ImageUpdate::AddImage(key, description, data, is_animated_image) => {
1163                    txn.add_image(
1164                        key,
1165                        description,
1166                        self.serializable_image_data_to_image_data_maybe_caching(
1167                            key,
1168                            data,
1169                            is_animated_image,
1170                        ),
1171                        None,
1172                    );
1173                },
1174                ImageUpdate::DeleteImage(key) => {
1175                    txn.delete_image(key);
1176                    self.frame_delayer.delete_image(key);
1177                    self.animation_image_cache.remove(&key);
1178                },
1179                ImageUpdate::UpdateImage(key, desc, data, epoch) => {
1180                    if let Some(epoch) = epoch {
1181                        self.frame_delayer.update_image(key, epoch);
1182                    }
1183                    txn.update_image(key, desc, data.into(), &DirtyRect::All)
1184                },
1185                ImageUpdate::UpdateImageForAnimation(image_key, desc) => {
1186                    let Some(image) = self.animation_image_cache.get(&image_key) else {
1187                        error!("Could not find image key in image cache.");
1188                        continue;
1189                    };
1190                    txn.update_image(
1191                        image_key,
1192                        desc,
1193                        ImageData::new_shared(image.clone()),
1194                        &DirtyRect::All,
1195                    );
1196                },
1197            }
1198        }
1199
1200        if self.frame_delayer.needs_new_frame() {
1201            self.frame_delayer.set_pending_frame(false);
1202            self.generate_frame(&mut txn, RenderReasons::SCENE);
1203            let waiting_pipelines = self.frame_delayer.take_waiting_pipelines();
1204
1205            self.send_to_constellation(
1206                EmbedderToConstellationMessage::NoLongerWaitingOnAsynchronousImageUpdates(
1207                    waiting_pipelines,
1208                ),
1209            );
1210
1211            self.screenshot_taker
1212                .prepare_screenshot_requests_for_render(&*self);
1213        }
1214
1215        self.send_transaction(txn);
1216    }
1217
1218    pub(crate) fn delay_new_frames_for_canvas(
1219        &mut self,
1220        pipeline_id: PipelineId,
1221        canvas_epoch: Epoch,
1222        image_keys: Vec<ImageKey>,
1223    ) {
1224        self.frame_delayer
1225            .add_delay(pipeline_id, canvas_epoch, image_keys);
1226    }
1227
1228    pub(crate) fn add_font(
1229        &mut self,
1230        font_key: FontKey,
1231        data: Arc<GenericSharedMemory>,
1232        index: u32,
1233    ) {
1234        let mut transaction = Transaction::new();
1235        transaction.add_raw_font(font_key, (**data).into(), index);
1236        self.send_transaction(transaction);
1237    }
1238
1239    pub(crate) fn add_system_font(&mut self, font_key: FontKey, native_handle: NativeFontHandle) {
1240        let mut transaction = Transaction::new();
1241        transaction.add_native_font(font_key, native_handle);
1242        self.send_transaction(transaction);
1243    }
1244
1245    pub(crate) fn add_font_instance(
1246        &mut self,
1247        instance_key: FontInstanceKey,
1248        font_key: FontKey,
1249        size: f32,
1250        flags: FontInstanceFlags,
1251        platform_options: FontInstancePlatformOptions,
1252        variations: Vec<FontVariation>,
1253    ) {
1254        let variations = if pref!(layout_css_font_variations_enabled) {
1255            variations
1256        } else {
1257            vec![]
1258        };
1259
1260        let mut transaction = Transaction::new();
1261
1262        let font_instance_options = FontInstanceOptions {
1263            flags,
1264            ..Default::default()
1265        };
1266        transaction.add_font_instance(
1267            instance_key,
1268            font_key,
1269            size,
1270            Some(font_instance_options),
1271            Some(platform_options),
1272            variations,
1273        );
1274
1275        self.send_transaction(transaction);
1276    }
1277
1278    pub(crate) fn remove_fonts(&mut self, keys: Vec<FontKey>, instance_keys: Vec<FontInstanceKey>) {
1279        let mut transaction = Transaction::new();
1280
1281        for instance in instance_keys.into_iter() {
1282            transaction.delete_font_instance(instance);
1283        }
1284        for key in keys.into_iter() {
1285            transaction.delete_font(key);
1286        }
1287
1288        self.send_transaction(transaction);
1289    }
1290
1291    pub(crate) fn set_viewport_description(
1292        &mut self,
1293        webview_id: WebViewId,
1294        viewport_description: ViewportDescription,
1295    ) {
1296        if let Some(webview) = self.webview_renderers.get_mut(&webview_id) {
1297            webview.set_viewport_description(viewport_description);
1298        }
1299    }
1300
1301    pub(crate) fn handle_screenshot_readiness_reply(
1302        &self,
1303        webview_id: WebViewId,
1304        expected_epochs: FxHashMap<PipelineId, Epoch>,
1305    ) {
1306        self.screenshot_taker
1307            .handle_screenshot_readiness_reply(webview_id, expected_epochs, self);
1308    }
1309
1310    pub(crate) fn add_webview(
1311        &mut self,
1312        webview: Box<dyn WebViewTrait>,
1313        viewport_details: ViewportDetails,
1314    ) {
1315        let property_binding_key = self.generate_property_binding_key();
1316        self.webview_renderers
1317            .entry(webview.id())
1318            .or_insert(WebViewRenderer::new(
1319                webview,
1320                viewport_details,
1321                self.embedder_to_constellation_sender.clone(),
1322                self.refresh_driver.clone(),
1323                self.webrender_document,
1324                property_binding_key,
1325            ));
1326    }
1327
1328    pub(crate) fn remove_webview(&mut self, webview_id: WebViewId) {
1329        let Some(webview_renderer) = self.webview_renderers.remove(&webview_id) else {
1330            warn!("Tried removing unknown WebView: {webview_id:?}");
1331            return;
1332        };
1333
1334        let mut transaction = Transaction::new();
1335        for pipeline_id in webview_renderer.pipelines.keys() {
1336            transaction.remove_pipeline(pipeline_id.into());
1337        }
1338        self.send_transaction(transaction);
1339
1340        self.send_root_pipeline_display_list();
1341    }
1342
1343    pub(crate) fn is_empty(&mut self) -> bool {
1344        self.webview_renderers.is_empty()
1345    }
1346
1347    pub(crate) fn set_webview_hidden(
1348        &mut self,
1349        webview_id: WebViewId,
1350        hidden: bool,
1351    ) -> Result<(), UnknownWebView> {
1352        debug!("Setting WebView visiblity for {webview_id:?} to hidden={hidden}");
1353        let Some(webview_renderer) = self.webview_renderer_mut(webview_id) else {
1354            return Err(UnknownWebView(webview_id));
1355        };
1356        if !webview_renderer.set_hidden(hidden) {
1357            return Ok(());
1358        }
1359        self.send_root_pipeline_display_list();
1360        Ok(())
1361    }
1362
1363    pub(crate) fn set_hidpi_scale_factor(
1364        &mut self,
1365        webview_id: WebViewId,
1366        new_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1367    ) {
1368        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
1369            return;
1370        };
1371        if !webview_renderer.set_hidpi_scale_factor(new_scale_factor) {
1372            return;
1373        }
1374        webview_renderer.webview.notify_viewport_updated();
1375
1376        self.send_root_pipeline_display_list();
1377        self.set_needs_repaint(RepaintReason::Resize);
1378    }
1379
1380    pub(crate) fn set_screen_size(
1381        &mut self,
1382        webview_id: WebViewId,
1383        new_size: Size2D<f32, DevicePixel>,
1384    ) {
1385        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
1386            return;
1387        };
1388        webview_renderer.set_screen_size(new_size);
1389    }
1390
1391    pub(crate) fn resize_rendering_context(&mut self, new_size: PhysicalSize<u32>) {
1392        if self.rendering_context.size() == new_size {
1393            return;
1394        }
1395
1396        if let Err(error) = self.rendering_context.make_current() {
1397            error!("Failed to make the rendering context current: {error:?}");
1398        }
1399        self.rendering_context.resize(new_size);
1400
1401        let new_size = Size2D::new(new_size.width as f32, new_size.height as f32);
1402        let new_viewport_rect = Rect::from(new_size).to_box2d();
1403        for webview_renderer in self.webview_renderers.values_mut() {
1404            webview_renderer.set_rect(new_viewport_rect);
1405            webview_renderer.webview.notify_viewport_updated();
1406        }
1407
1408        let mut transaction = Transaction::new();
1409        transaction.set_document_view(new_viewport_rect.to_i32());
1410        self.send_transaction(transaction);
1411
1412        self.send_root_pipeline_display_list();
1413        self.set_needs_repaint(RepaintReason::Resize);
1414    }
1415
1416    pub(crate) fn set_page_zoom(&mut self, webview_id: WebViewId, new_zoom: f32) {
1417        if let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) &&
1418            webview_renderer.set_page_zoom(Scale::new(new_zoom))
1419        {
1420            webview_renderer.webview.notify_viewport_updated();
1421        }
1422    }
1423
1424    pub(crate) fn page_zoom(&self, webview_id: WebViewId) -> f32 {
1425        self.webview_renderers
1426            .get(&webview_id)
1427            .map(|webview_renderer| webview_renderer.page_zoom.get())
1428            .unwrap_or(1.)
1429    }
1430
1431    pub(crate) fn notify_input_event(
1432        &mut self,
1433        webview_id: WebViewId,
1434        event: InputEventAndId,
1435    ) -> bool {
1436        self.webview_renderers
1437            .get_mut(&webview_id)
1438            .is_some_and(|webview_renderer| {
1439                match &event.event {
1440                    InputEvent::MouseMove(event) => {
1441                        // We only track the last mouse move position for non-touch events.
1442                        if !event.is_compatibility_event_for_touch {
1443                            let event_point = event
1444                                .point
1445                                .as_device_point(webview_renderer.device_pixels_per_page_pixel());
1446                            self.last_mouse_move_position = Some(event_point);
1447                        }
1448                    },
1449                    InputEvent::MouseLeftViewport(_) => {
1450                        self.last_mouse_move_position = None;
1451                    },
1452                    _ => {},
1453                }
1454
1455                webview_renderer.notify_input_event(&self.webrender_api, &self.needs_repaint, event)
1456            })
1457    }
1458
1459    pub(crate) fn notify_scroll_event(
1460        &mut self,
1461        webview_id: WebViewId,
1462        scroll: Scroll,
1463        point: WebViewPoint,
1464    ) {
1465        if let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) {
1466            webview_renderer.notify_scroll_event(scroll, point);
1467        }
1468    }
1469
1470    pub(crate) fn adjust_pinch_zoom(
1471        &mut self,
1472        webview_id: WebViewId,
1473        pinch_zoom_delta: f32,
1474        center: DevicePoint,
1475    ) {
1476        if let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) {
1477            webview_renderer.adjust_pinch_zoom(pinch_zoom_delta, center);
1478        }
1479    }
1480
1481    pub(crate) fn pinch_zoom(&self, webview_id: WebViewId) -> f32 {
1482        self.webview_renderers
1483            .get(&webview_id)
1484            .map(|webview_renderer| webview_renderer.pinch_zoom().zoom_factor().0)
1485            .unwrap_or(1.)
1486    }
1487
1488    pub(crate) fn device_pixels_per_page_pixel(
1489        &self,
1490        webview_id: WebViewId,
1491    ) -> Scale<f32, CSSPixel, DevicePixel> {
1492        self.webview_renderers
1493            .get(&webview_id)
1494            .map(WebViewRenderer::device_pixels_per_page_pixel)
1495            .unwrap_or_default()
1496    }
1497
1498    pub(crate) fn request_screenshot(
1499        &self,
1500        webview_id: WebViewId,
1501        rect: Option<WebViewRect>,
1502        callback: Box<dyn FnOnce(Result<RgbaImage, ScreenshotCaptureError>) + 'static>,
1503    ) {
1504        let Some(webview) = self.webview_renderers.get(&webview_id) else {
1505            return;
1506        };
1507
1508        let rect = rect.map(|rect| rect.as_device_rect(webview.device_pixels_per_page_pixel()));
1509        self.screenshot_taker
1510            .request_screenshot(webview_id, rect, callback);
1511        self.send_to_constellation(EmbedderToConstellationMessage::RequestScreenshotReadiness(
1512            webview_id,
1513        ));
1514    }
1515
1516    pub(crate) fn notify_input_event_handled(
1517        &mut self,
1518        webview_id: WebViewId,
1519        input_event_id: InputEventId,
1520        result: InputEventResult,
1521    ) {
1522        let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
1523            warn!("Handled input event for unknown webview: {webview_id}");
1524            return;
1525        };
1526        webview_renderer.notify_input_event_handled(&self.needs_repaint, input_event_id, result);
1527    }
1528
1529    pub(crate) fn refresh_cursor(&self) {
1530        let Some(last_mouse_move_position) = self.last_mouse_move_position else {
1531            return;
1532        };
1533
1534        let Some(hit_test_result) = Self::hit_test_at_point_with_api_and_document(
1535            &self.webrender_api,
1536            self.webrender_document,
1537            last_mouse_move_position,
1538        )
1539        .first()
1540        .cloned() else {
1541            return;
1542        };
1543
1544        if let Err(error) = self.embedder_to_constellation_sender.send(
1545            EmbedderToConstellationMessage::RefreshCursor(hit_test_result.pipeline_id),
1546        ) {
1547            warn!("Sending event to constellation failed ({:?}).", error);
1548        }
1549    }
1550
1551    pub(crate) fn handle_new_webrender_frame_ready(&self, repaint_needed: bool) {
1552        if repaint_needed {
1553            self.refresh_cursor()
1554        }
1555
1556        if repaint_needed || self.animation_callbacks_running() {
1557            self.set_needs_repaint(RepaintReason::NewWebRenderFrame);
1558        }
1559
1560        // If we received a new frame and a repaint isn't necessary, it may be that this
1561        // is the last frame that was pending. In that case, trigger a manual repaint so
1562        // that the screenshot can be taken at the end of the repaint procedure.
1563        if !repaint_needed {
1564            self.screenshot_taker
1565                .maybe_trigger_paint_for_screenshot(self);
1566        }
1567    }
1568
1569    pub(crate) fn webviews_needing_repaint(&self) -> Vec<WebViewId> {
1570        if self.needs_repaint() {
1571            self.webview_renderers
1572                .values()
1573                .map(|webview_renderer| webview_renderer.id)
1574                .collect()
1575        } else {
1576            Vec::new()
1577        }
1578    }
1579
1580    pub(crate) fn scroll_trees_memory_usage(
1581        &self,
1582        ops: &mut malloc_size_of::MallocSizeOfOps,
1583    ) -> usize {
1584        self.webview_renderers
1585            .values()
1586            .map(|renderer| renderer.scroll_trees_memory_usage(ops))
1587            .sum::<usize>()
1588    }
1589}
1590
1591/// A struct that is reponsible for delaying frame requests until all new canvas images
1592/// for a particular "update the rendering" call in the `ScriptThread` have been
1593/// sent to WebRender.
1594///
1595/// These images may be updated in WebRender asynchronously in the canvas task. A frame
1596/// is then requested if:
1597///
1598///  - The renderer has received a GenerateFrame message from a `ScriptThread`.
1599///  - All pending image updates have finished and have been noted in the [`FrameDelayer`].
1600#[derive(Default)]
1601pub(crate) struct FrameDelayer {
1602    /// The latest [`Epoch`] of canvas images that have been sent to WebRender. Note
1603    /// that this only records the `Epoch`s for canvases and only ones that are involved
1604    /// in "update the rendering".
1605    image_epochs: FxHashMap<ImageKey, Epoch>,
1606    /// A map of all pending canvas images
1607    pending_canvas_images: FxHashMap<ImageKey, Epoch>,
1608    /// Whether or not we have a pending frame.
1609    pub(crate) pending_frame: bool,
1610    /// A list of pipelines that should be notified when we are no longer waiting for
1611    /// canvas images.
1612    waiting_pipelines: FxHashSet<PipelineId>,
1613}
1614
1615impl FrameDelayer {
1616    pub(crate) fn delete_image(&mut self, image_key: ImageKey) {
1617        self.image_epochs.remove(&image_key);
1618        self.pending_canvas_images.remove(&image_key);
1619    }
1620
1621    pub(crate) fn update_image(&mut self, image_key: ImageKey, epoch: Epoch) {
1622        self.image_epochs.insert(image_key, epoch);
1623        let Entry::Occupied(entry) = self.pending_canvas_images.entry(image_key) else {
1624            return;
1625        };
1626        if *entry.get() <= epoch {
1627            entry.remove();
1628        }
1629    }
1630
1631    pub(crate) fn add_delay(
1632        &mut self,
1633        pipeline_id: PipelineId,
1634        canvas_epoch: Epoch,
1635        image_keys: Vec<ImageKey>,
1636    ) {
1637        for image_key in image_keys.into_iter() {
1638            // If we've already seen the necessary epoch for this image, do not
1639            // start waiting for it.
1640            if self
1641                .image_epochs
1642                .get(&image_key)
1643                .is_some_and(|epoch_seen| *epoch_seen >= canvas_epoch)
1644            {
1645                continue;
1646            }
1647            self.pending_canvas_images.insert(image_key, canvas_epoch);
1648        }
1649        self.waiting_pipelines.insert(pipeline_id);
1650    }
1651
1652    pub(crate) fn needs_new_frame(&self) -> bool {
1653        self.pending_frame && self.pending_canvas_images.is_empty()
1654    }
1655
1656    pub(crate) fn set_pending_frame(&mut self, value: bool) {
1657        self.pending_frame = value;
1658    }
1659
1660    pub(crate) fn take_waiting_pipelines(&mut self) -> Vec<PipelineId> {
1661        self.waiting_pipelines.drain().collect()
1662    }
1663}
1664
1665/// The paint status of a particular pipeline in a [`Painter`]. This is used to trigger metrics
1666/// in script (via the constellation) when display lists are received.
1667///
1668/// See <https://w3c.github.io/paint-timing/#first-contentful-paint>.
1669#[derive(Clone, Copy, PartialEq)]
1670pub(crate) enum PaintMetricState {
1671    /// The painter is still waiting to process a display list which triggers this metric.
1672    Waiting,
1673    /// The painter has processed the display list which will trigger this event, marked the Servo
1674    /// instance ready to paint, and is waiting for the given epoch to actually be rendered.
1675    /// Carries the paint timing info of the display list that triggered this metric.
1676    Seen(
1677        WebRenderEpoch,
1678        bool, /* first_reflow */
1679        PaintTimingInfo,
1680    ),
1681    /// The metric has been sent to the constellation and no more work needs to be done.
1682    Sent,
1683}
1684
1685/// Hook implementation to boost webrender thread priority.
1686struct BoostWebRenderThread;
1687
1688impl RenderBackendHooks for BoostWebRenderThread {
1689    fn init_thread(&self) {
1690        servo_base::threadboost::boost_thread(ThreadPriority::Elevated, BoostAffinity::Boost);
1691    }
1692}
1693
1694impl SceneBuilderHooks for BoostWebRenderThread {
1695    fn register(&self) {
1696        servo_base::threadboost::boost_thread(ThreadPriority::Elevated, BoostAffinity::Boost);
1697    }
1698
1699    fn pre_scene_build(&self) {}
1700
1701    fn pre_scene_swap(&self) {}
1702
1703    fn post_scene_swap(
1704        &self,
1705        _document_id: &Vec<DocumentId>,
1706        _info: webrender::PipelineInfo,
1707        _schedule_frame: bool,
1708    ) {
1709    }
1710
1711    fn post_resource_update(&self, _document_ids: &Vec<DocumentId>) {}
1712
1713    fn post_empty_scene_build(&self) {}
1714
1715    fn poke(&self) {}
1716
1717    fn deregister(&self) {}
1718}