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