paint/
paint.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, Ref, RefCell, RefMut};
6use std::collections::HashMap;
7use std::env;
8use std::fs::create_dir_all;
9use std::rc::Rc;
10use std::time::{SystemTime, UNIX_EPOCH};
11
12use base::generic_channel::{self, GenericSender, RoutedReceiver};
13use base::id::{PainterId, PipelineId, WebViewId};
14use bitflags::bitflags;
15use canvas_traits::webgl::{WebGLContextId, WebGLThreads};
16use constellation_traits::EmbedderToConstellationMessage;
17use crossbeam_channel::Sender;
18use dpi::PhysicalSize;
19use embedder_traits::{
20    EventLoopWaker, InputEventAndId, InputEventId, InputEventResult, ScreenshotCaptureError,
21    Scroll, ShutdownState, ViewportDetails, WebViewPoint, WebViewRect,
22};
23use euclid::{Scale, Size2D};
24use image::RgbaImage;
25use ipc_channel::ipc::{self};
26use log::{debug, warn};
27use paint_api::rendering_context::RenderingContext;
28use paint_api::{
29    PaintMessage, PaintProxy, PainterSurfmanDetails, PainterSurfmanDetailsMap,
30    WebRenderExternalImageIdManager, WebViewTrait,
31};
32use profile_traits::mem::{
33    ProcessReports, ProfilerRegistration, Report, ReportKind, perform_memory_report,
34};
35use profile_traits::path;
36use profile_traits::time::{self as profile_time};
37use servo_config::pref;
38use servo_geometry::DeviceIndependentPixel;
39use style_traits::CSSPixel;
40use surfman::Device;
41use surfman::chains::SwapChains;
42use webgl::WebGLComm;
43use webgl::webgl_thread::WebGLContextBusyMap;
44#[cfg(feature = "webgpu")]
45use webgpu::canvas_context::WebGpuExternalImageMap;
46use webrender::{CaptureBits, MemoryReport};
47use webrender_api::units::{DevicePixel, DevicePoint};
48use webrender_api::{FontInstanceKey, FontKey, ImageKey};
49
50use crate::InitialPaintState;
51use crate::painter::Painter;
52use crate::webview_renderer::UnknownWebView;
53
54/// An option to control what kind of WebRender debugging is enabled while Servo is running.
55#[derive(Copy, Clone)]
56pub enum WebRenderDebugOption {
57    Profiler,
58    TextureCacheDebug,
59    RenderTargetDebug,
60}
61
62/// [`Paint`] is Servo's rendering subsystem. It has a few responsibilities:
63///
64/// 1. Maintain a WebRender instance for each [`RenderingContext`] that Servo knows about.
65///    [`RenderingContext`]s are per-`WebView`, but more than one `WebView` can use the same
66///    [`RenderingContext`]. This allows multiple `WebView`s to share the same WebRender
67///    instance which is more efficient. This is useful for tabbed web browsers.
68/// 2. Receive display lists from the layout of all of the currently active `Pipeline`s
69///    (frames). These display lists are sent to WebRender, and new frames are generated.
70///    Once the frame is ready the [`Painter`] for the WebRender instance will ask libservo
71///    to inform the embedder that a new frame is ready so that it can trigger a paint.
72/// 3. Drive animation and animation callback updates. Animation updates should ideally be
73///    coordinated with the system vsync signal, so the `RefreshDriver` is exposed in the
74///    API to allow the embedder to do this. The [`Painter`] then asks its `WebView`s to
75///    update their rendering, which triggers layouts.
76/// 4. Eagerly handle scrolling and touch events. In order to avoid latency when handling
77///    these kind of actions, each [`Painter`] will eagerly process touch events and
78///    perform panning and zooming operations on their WebRender contents -- informing the
79///    WebView contents asynchronously.
80///
81/// `Paint` and all of its contained structs should **never** block on the Constellation,
82/// because sometimes the Constellation blocks on us.
83pub struct Paint {
84    /// All of the [`Painters`] for this [`Paint`]. Each [`Painter`] handles painting to
85    /// a single [`RenderingContext`].
86    painters: Vec<Rc<RefCell<Painter>>>,
87
88    /// A [`PaintProxy`] which can be used to allow other parts of Servo to communicate
89    /// with this [`Paint`].
90    pub(crate) paint_proxy: PaintProxy,
91
92    /// An [`EventLoopWaker`] used to wake up the main embedder event loop when the renderer needs
93    /// to run.
94    pub(crate) event_loop_waker: Box<dyn EventLoopWaker>,
95
96    /// Tracks whether we are in the process of shutting down, or have shut down and
97    /// should shut down `Paint`. This is shared with the `Servo` instance.
98    shutdown_state: Rc<Cell<ShutdownState>>,
99
100    /// The port on which we receive messages.
101    paint_receiver: RoutedReceiver<PaintMessage>,
102
103    /// The channel on which messages can be sent to the constellation.
104    pub(crate) embedder_to_constellation_sender: Sender<EmbedderToConstellationMessage>,
105
106    /// The [`WebRenderExternalImageIdManager`] used to generate new `ExternalImageId`s.
107    webrender_external_image_id_manager: WebRenderExternalImageIdManager,
108
109    /// A [`HashMap`] of [`PainterId`] to the Surfaman types (`Device`, `Adapter`) that
110    /// are specific to a particular [`Painter`].
111    pub(crate) painter_surfman_details_map: PainterSurfmanDetailsMap,
112
113    /// A [`HashMap`] of `WebGLContextId` to a usage count. This count indicates when
114    /// WebRender is still rendering the context. This is used to ensure properly clean
115    /// up of all Surfman `Surface`s.
116    pub(crate) busy_webgl_contexts_map: WebGLContextBusyMap,
117
118    /// The [`WebGLThreads`] for this renderer.
119    webgl_threads: WebGLThreads,
120
121    /// The shared [`SwapChains`] used by [`WebGLThreads`] for this renderer.
122    pub(crate) swap_chains: SwapChains<WebGLContextId, Device>,
123
124    /// The channel on which messages can be sent to the time profiler.
125    time_profiler_chan: profile_time::ProfilerChan,
126
127    /// A handle to the memory profiler which will automatically unregister
128    /// when it's dropped.
129    _mem_profiler_registration: ProfilerRegistration,
130
131    /// Some XR devices want to run on the main thread.
132    #[cfg(feature = "webxr")]
133    webxr_main_thread: RefCell<webxr::MainThreadRegistry>,
134
135    /// An map of external images shared between all `WebGpuExternalImages`.
136    #[cfg(feature = "webgpu")]
137    webgpu_image_map: std::cell::OnceCell<WebGpuExternalImageMap>,
138}
139
140/// Why we need to be repainted. This is used for debugging.
141#[derive(Clone, Copy, Default, PartialEq)]
142pub(crate) struct RepaintReason(u8);
143
144bitflags! {
145    impl RepaintReason: u8 {
146        /// We're performing the single repaint in headless mode.
147        const ReadyForScreenshot = 1 << 0;
148        /// We're performing a repaint to run an animation.
149        const ChangedAnimationState = 1 << 1;
150        /// A new WebRender frame has arrived.
151        const NewWebRenderFrame = 1 << 2;
152        /// The window has been resized and will need to be synchronously repainted.
153        const Resize = 1 << 3;
154        /// A fling has started and a repaint needs to happen to process the animation.
155        const StartedFlinging = 1 << 4;
156        /// A blinking text caret requires a redraw.
157        const BlinkingCaret = 1 << 5;
158    }
159}
160
161impl Paint {
162    pub fn new(state: InitialPaintState) -> Rc<RefCell<Self>> {
163        let registration = state.mem_profiler_chan.prepare_memory_reporting(
164            "paint".into(),
165            state.paint_proxy.clone(),
166            PaintMessage::CollectMemoryReport,
167        );
168
169        let webrender_external_image_id_manager = WebRenderExternalImageIdManager::default();
170        let painter_surfman_details_map = PainterSurfmanDetailsMap::default();
171        let WebGLComm {
172            webgl_threads,
173            swap_chains,
174            busy_webgl_context_map,
175            #[cfg(feature = "webxr")]
176            webxr_layer_grand_manager,
177        } = WebGLComm::new(
178            state.paint_proxy.cross_process_paint_api.clone(),
179            webrender_external_image_id_manager.clone(),
180            painter_surfman_details_map.clone(),
181        );
182
183        // Create the WebXR main thread
184        #[cfg(feature = "webxr")]
185        let webxr_main_thread = {
186            use servo_config::pref;
187
188            let mut webxr_main_thread = webxr::MainThreadRegistry::new(
189                state.event_loop_waker.clone(),
190                webxr_layer_grand_manager,
191            )
192            .expect("Failed to create WebXR device registry");
193            if pref!(dom_webxr_enabled) {
194                state.webxr_registry.register(&mut webxr_main_thread);
195            }
196            webxr_main_thread
197        };
198
199        Rc::new(RefCell::new(Paint {
200            painters: Default::default(),
201            paint_proxy: state.paint_proxy,
202            event_loop_waker: state.event_loop_waker,
203            shutdown_state: state.shutdown_state,
204            paint_receiver: state.receiver,
205            embedder_to_constellation_sender: state.embedder_to_constellation_sender.clone(),
206            webrender_external_image_id_manager,
207            webgl_threads,
208            swap_chains,
209            time_profiler_chan: state.time_profiler_chan,
210            _mem_profiler_registration: registration,
211            painter_surfman_details_map,
212            busy_webgl_contexts_map: busy_webgl_context_map,
213            #[cfg(feature = "webxr")]
214            webxr_main_thread: RefCell::new(webxr_main_thread),
215            #[cfg(feature = "webgpu")]
216            webgpu_image_map: Default::default(),
217        }))
218    }
219
220    pub fn register_rendering_context(
221        &mut self,
222        rendering_context: Rc<dyn RenderingContext>,
223    ) -> PainterId {
224        if let Some(painter_id) = self.painters.iter().find_map(|painter| {
225            let painter = painter.borrow();
226            if Rc::ptr_eq(&painter.rendering_context, &rendering_context) {
227                Some(painter.painter_id)
228            } else {
229                None
230            }
231        }) {
232            return painter_id;
233        }
234
235        let painter = Painter::new(rendering_context.clone(), self);
236        let connection = rendering_context
237            .connection()
238            .expect("Failed to get connection");
239        let adapter = connection
240            .create_adapter()
241            .expect("Failed to create adapter");
242
243        let painter_surfman_details = PainterSurfmanDetails {
244            connection,
245            adapter,
246        };
247        self.painter_surfman_details_map
248            .insert(painter.painter_id, painter_surfman_details);
249
250        let painter_id = painter.painter_id;
251        self.painters.push(Rc::new(RefCell::new(painter)));
252        painter_id
253    }
254
255    fn remove_painter(&mut self, painter_id: PainterId) {
256        self.painters
257            .retain(|painter| painter.borrow().painter_id != painter_id);
258        self.painter_surfman_details_map.remove(painter_id);
259    }
260
261    pub(crate) fn maybe_painter<'a>(&'a self, painter_id: PainterId) -> Option<Ref<'a, Painter>> {
262        self.painters
263            .iter()
264            .map(|painter| painter.borrow())
265            .find(|painter| painter.painter_id == painter_id)
266    }
267
268    pub(crate) fn painter<'a>(&'a self, painter_id: PainterId) -> Ref<'a, Painter> {
269        self.maybe_painter(painter_id)
270            .expect("painter_id not found")
271    }
272
273    pub(crate) fn maybe_painter_mut<'a>(
274        &'a self,
275        painter_id: PainterId,
276    ) -> Option<RefMut<'a, Painter>> {
277        self.painters
278            .iter()
279            .map(|painter| painter.borrow_mut())
280            .find(|painter| painter.painter_id == painter_id)
281    }
282
283    pub(crate) fn painter_mut<'a>(&'a self, painter_id: PainterId) -> RefMut<'a, Painter> {
284        self.maybe_painter_mut(painter_id)
285            .expect("painter_id not found")
286    }
287
288    pub fn painter_id(&self) -> PainterId {
289        self.painters[0].borrow().painter_id
290    }
291
292    pub fn rendering_context_size(&self, painter_id: PainterId) -> Size2D<u32, DevicePixel> {
293        self.painter(painter_id).rendering_context.size2d()
294    }
295
296    pub fn webgl_threads(&self) -> WebGLThreads {
297        self.webgl_threads.clone()
298    }
299
300    pub fn webrender_external_image_id_manager(&self) -> WebRenderExternalImageIdManager {
301        self.webrender_external_image_id_manager.clone()
302    }
303
304    pub fn webxr_running(&self) -> bool {
305        #[cfg(feature = "webxr")]
306        {
307            self.webxr_main_thread.borrow().running()
308        }
309        #[cfg(not(feature = "webxr"))]
310        {
311            false
312        }
313    }
314
315    #[cfg(feature = "webxr")]
316    pub fn webxr_main_thread_registry(&self) -> webxr_api::Registry {
317        self.webxr_main_thread.borrow().registry()
318    }
319
320    #[cfg(feature = "webgpu")]
321    pub fn webgpu_image_map(&self) -> WebGpuExternalImageMap {
322        self.webgpu_image_map.get_or_init(Default::default).clone()
323    }
324
325    pub fn webviews_needing_repaint(&self) -> Vec<WebViewId> {
326        self.painters
327            .iter()
328            .flat_map(|painter| painter.borrow().webviews_needing_repaint())
329            .collect()
330    }
331
332    pub fn finish_shutting_down(&self) {
333        // Drain paint port, sometimes messages contain channels that are blocking
334        // another thread from finishing (i.e. SetFrameTree).
335        while self.paint_receiver.try_recv().is_ok() {}
336
337        let (webgl_exit_sender, webgl_exit_receiver) =
338            generic_channel::channel().expect("Failed to create IPC channel!");
339        if !self
340            .webgl_threads
341            .exit(webgl_exit_sender)
342            .is_ok_and(|_| webgl_exit_receiver.recv().is_ok())
343        {
344            warn!("Could not exit WebGLThread.");
345        }
346
347        // Tell the profiler, memory profiler, and scrolling timer to shut down.
348        if let Ok((sender, receiver)) = ipc::channel() {
349            self.time_profiler_chan
350                .send(profile_time::ProfilerMsg::Exit(sender));
351            let _ = receiver.recv();
352        }
353    }
354
355    fn handle_browser_message(&self, msg: PaintMessage) {
356        trace_msg_from_constellation!(msg, "{msg:?}");
357
358        match self.shutdown_state() {
359            ShutdownState::NotShuttingDown => {},
360            ShutdownState::ShuttingDown => {
361                self.handle_browser_message_while_shutting_down(msg);
362                return;
363            },
364            ShutdownState::FinishedShuttingDown => {
365                // Messages to Paint are ignored after shutdown is complete.
366                return;
367            },
368        }
369
370        match msg {
371            PaintMessage::CollectMemoryReport(sender) => {
372                self.collect_memory_report(sender);
373            },
374            PaintMessage::ChangeRunningAnimationsState(
375                webview_id,
376                pipeline_id,
377                animation_state,
378            ) => {
379                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
380                    painter.change_running_animations_state(
381                        webview_id,
382                        pipeline_id,
383                        animation_state,
384                    );
385                }
386            },
387            PaintMessage::SetFrameTreeForWebView(webview_id, frame_tree) => {
388                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
389                    painter.set_frame_tree_for_webview(&frame_tree);
390                }
391            },
392            PaintMessage::SetThrottled(webview_id, pipeline_id, throttled) => {
393                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
394                    painter.set_throttled(webview_id, pipeline_id, throttled);
395                }
396            },
397            PaintMessage::PipelineExited(webview_id, pipeline_id, pipeline_exit_source) => {
398                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
399                    painter.notify_pipeline_exited(webview_id, pipeline_id, pipeline_exit_source);
400                }
401            },
402            PaintMessage::NewWebRenderFrameReady(..) => {
403                unreachable!("New WebRender frames should be handled in the caller.");
404            },
405            PaintMessage::SendInitialTransaction(webview_id, pipeline_id) => {
406                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
407                    painter.send_initial_pipeline_transaction(webview_id, pipeline_id);
408                }
409            },
410            PaintMessage::ScrollNodeByDelta(
411                webview_id,
412                pipeline_id,
413                offset,
414                external_scroll_id,
415            ) => {
416                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
417                    painter.scroll_node_by_delta(
418                        webview_id,
419                        pipeline_id,
420                        offset,
421                        external_scroll_id,
422                    );
423                }
424            },
425            PaintMessage::ScrollViewportByDelta(webview_id, delta) => {
426                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
427                    painter.scroll_viewport_by_delta(webview_id, delta);
428                }
429            },
430            PaintMessage::UpdateEpoch {
431                webview_id,
432                pipeline_id,
433                epoch,
434            } => {
435                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
436                    painter.update_epoch(webview_id, pipeline_id, epoch);
437                }
438            },
439            PaintMessage::SendDisplayList {
440                webview_id,
441                display_list_descriptor,
442                display_list_info_receiver,
443                display_list_data_receiver,
444            } => {
445                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
446                    painter.handle_new_display_list(
447                        webview_id,
448                        display_list_descriptor,
449                        display_list_info_receiver,
450                        display_list_data_receiver,
451                    );
452                }
453            },
454            PaintMessage::GenerateFrame(painter_ids) => {
455                for painter_id in painter_ids {
456                    if let Some(mut painter) = self.maybe_painter_mut(painter_id) {
457                        painter.generate_frame_for_script();
458                    }
459                }
460            },
461            PaintMessage::GenerateImageKey(webview_id, result_sender) => {
462                self.handle_generate_image_key(webview_id, result_sender);
463            },
464            PaintMessage::GenerateImageKeysForPipeline(webview_id, pipeline_id) => {
465                self.handle_generate_image_keys_for_pipeline(webview_id, pipeline_id);
466            },
467            PaintMessage::UpdateImages(painter_id, updates) => {
468                if let Some(mut painter) = self.maybe_painter_mut(painter_id) {
469                    painter.update_images(updates);
470                }
471            },
472            PaintMessage::DelayNewFrameForCanvas(
473                webview_id,
474                pipeline_id,
475                canvas_epoch,
476                image_keys,
477            ) => {
478                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
479                    painter.delay_new_frames_for_canvas(pipeline_id, canvas_epoch, image_keys);
480                }
481            },
482            PaintMessage::AddFont(painter_id, font_key, data, index) => {
483                debug_assert!(painter_id == font_key.into());
484
485                if let Some(mut painter) = self.maybe_painter_mut(painter_id) {
486                    painter.add_font(font_key, data, index);
487                }
488            },
489            PaintMessage::AddSystemFont(painter_id, font_key, native_handle) => {
490                debug_assert!(painter_id == font_key.into());
491
492                if let Some(mut painter) = self.maybe_painter_mut(painter_id) {
493                    painter.add_system_font(font_key, native_handle);
494                }
495            },
496            PaintMessage::AddFontInstance(
497                painter_id,
498                font_instance_key,
499                font_key,
500                size,
501                flags,
502                variations,
503            ) => {
504                debug_assert!(painter_id == font_key.into());
505                debug_assert!(painter_id == font_instance_key.into());
506
507                if let Some(mut painter) = self.maybe_painter_mut(painter_id) {
508                    painter.add_font_instance(font_instance_key, font_key, size, flags, variations);
509                }
510            },
511            PaintMessage::RemoveFonts(painter_id, keys, instance_keys) => {
512                if let Some(mut painter) = self.maybe_painter_mut(painter_id) {
513                    painter.remove_fonts(keys, instance_keys);
514                }
515            },
516            PaintMessage::GenerateFontKeys(
517                number_of_font_keys,
518                number_of_font_instance_keys,
519                result_sender,
520                painter_id,
521            ) => {
522                self.handle_generate_font_keys(
523                    number_of_font_keys,
524                    number_of_font_instance_keys,
525                    result_sender,
526                    painter_id,
527                );
528            },
529            PaintMessage::Viewport(webview_id, viewport_description) => {
530                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
531                    painter.set_viewport_description(webview_id, viewport_description);
532                }
533            },
534            PaintMessage::ScreenshotReadinessReponse(webview_id, pipelines_and_epochs) => {
535                if let Some(painter) = self.maybe_painter(webview_id.into()) {
536                    painter.handle_screenshot_readiness_reply(webview_id, pipelines_and_epochs);
537                }
538            },
539            PaintMessage::SendLCPCandidate(lcp_candidate, webview_id, pipeline_id, epoch) => {
540                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
541                    painter.append_lcp_candidate(lcp_candidate, webview_id, pipeline_id, epoch);
542                }
543            },
544            PaintMessage::EnableLCPCalculation(webview_id) => {
545                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
546                    painter.enable_lcp_calculation(&webview_id);
547                }
548            },
549        }
550    }
551
552    pub fn remove_webview(&mut self, webview_id: WebViewId) {
553        let painter_id = webview_id.into();
554
555        {
556            let mut painter = self.painter_mut(painter_id);
557            painter.remove_webview(webview_id);
558            if !painter.is_empty() {
559                return;
560            }
561        }
562
563        self.remove_painter(painter_id);
564    }
565
566    fn collect_memory_report(&self, sender: profile_traits::mem::ReportsChan) {
567        let mut memory_report = MemoryReport::default();
568        for painter in &self.painters {
569            memory_report += painter.borrow().report_memory();
570        }
571
572        let mut reports = vec![
573            Report {
574                path: path!["webrender", "fonts"],
575                kind: ReportKind::ExplicitJemallocHeapSize,
576                size: memory_report.fonts,
577            },
578            Report {
579                path: path!["webrender", "images"],
580                kind: ReportKind::ExplicitJemallocHeapSize,
581                size: memory_report.images,
582            },
583            Report {
584                path: path!["webrender", "display-list"],
585                kind: ReportKind::ExplicitJemallocHeapSize,
586                size: memory_report.display_list,
587            },
588        ];
589
590        perform_memory_report(|ops| {
591            let scroll_trees_memory_usage = self
592                .painters
593                .iter()
594                .map(|painter| painter.borrow().scroll_trees_memory_usage(ops))
595                .sum();
596            reports.push(Report {
597                path: path!["paint", "scroll-tree"],
598                kind: ReportKind::ExplicitJemallocHeapSize,
599                size: scroll_trees_memory_usage,
600            });
601        });
602
603        sender.send(ProcessReports::new(reports));
604    }
605
606    /// Handle messages sent to `Paint` during the shutdown process. In general,
607    /// the things `Paint` can do in this state are limited. It's very important to
608    /// answer any synchronous messages though as other threads might be waiting on the
609    /// results to finish their own shut down process. We try to do as little as possible
610    /// during this time.
611    ///
612    /// When that involves generating WebRender ids, our approach here is to simply
613    /// generate them, but assume they will never be used, since once shutting down
614    /// `Paint` no longer does any WebRender frame generation.
615    fn handle_browser_message_while_shutting_down(&self, msg: PaintMessage) {
616        match msg {
617            PaintMessage::PipelineExited(webview_id, pipeline_id, pipeline_exit_source) => {
618                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
619                    painter.notify_pipeline_exited(webview_id, pipeline_id, pipeline_exit_source);
620                }
621            },
622            PaintMessage::GenerateImageKey(webview_id, result_sender) => {
623                self.handle_generate_image_key(webview_id, result_sender);
624            },
625            PaintMessage::GenerateImageKeysForPipeline(webview_id, pipeline_id) => {
626                self.handle_generate_image_keys_for_pipeline(webview_id, pipeline_id);
627            },
628            PaintMessage::GenerateFontKeys(
629                number_of_font_keys,
630                number_of_font_instance_keys,
631                result_sender,
632                painter_id,
633            ) => {
634                self.handle_generate_font_keys(
635                    number_of_font_keys,
636                    number_of_font_instance_keys,
637                    result_sender,
638                    painter_id,
639                );
640            },
641            _ => {
642                debug!("Ignoring message ({:?} while shutting down", msg);
643            },
644        }
645    }
646
647    pub fn add_webview(&self, webview: Box<dyn WebViewTrait>, viewport_details: ViewportDetails) {
648        self.painter_mut(webview.id().into())
649            .add_webview(webview, viewport_details);
650    }
651
652    pub fn show_webview(&self, webview_id: WebViewId) -> Result<(), UnknownWebView> {
653        self.painter_mut(webview_id.into())
654            .set_webview_hidden(webview_id, false)
655    }
656
657    pub fn hide_webview(&self, webview_id: WebViewId) -> Result<(), UnknownWebView> {
658        self.painter_mut(webview_id.into())
659            .set_webview_hidden(webview_id, true)
660    }
661
662    pub fn set_hidpi_scale_factor(
663        &self,
664        webview_id: WebViewId,
665        new_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
666    ) {
667        if self.shutdown_state() != ShutdownState::NotShuttingDown {
668            return;
669        }
670        self.painter_mut(webview_id.into())
671            .set_hidpi_scale_factor(webview_id, new_scale_factor);
672    }
673
674    pub fn resize_rendering_context(&self, webview_id: WebViewId, new_size: PhysicalSize<u32>) {
675        if self.shutdown_state() != ShutdownState::NotShuttingDown {
676            return;
677        }
678        self.painter_mut(webview_id.into())
679            .resize_rendering_context(new_size);
680    }
681
682    pub fn set_page_zoom(&self, webview_id: WebViewId, new_zoom: f32) {
683        if self.shutdown_state() != ShutdownState::NotShuttingDown {
684            return;
685        }
686        self.painter_mut(webview_id.into())
687            .set_page_zoom(webview_id, new_zoom);
688    }
689
690    pub fn page_zoom(&self, webview_id: WebViewId) -> f32 {
691        self.painter(webview_id.into()).page_zoom(webview_id)
692    }
693
694    /// Render the WebRender scene to the active `RenderingContext`.
695    pub fn render(&self, webview_id: WebViewId) {
696        self.painter_mut(webview_id.into())
697            .render(&self.time_profiler_chan);
698    }
699
700    /// Get the message receiver for this [`Paint`].
701    pub fn receiver(&self) -> &RoutedReceiver<PaintMessage> {
702        &self.paint_receiver
703    }
704
705    #[servo_tracing::instrument(skip_all)]
706    pub fn handle_messages(&self, mut messages: Vec<PaintMessage>) {
707        // Pull out the `NewWebRenderFrameReady` messages from the list of messages and handle them
708        // at the end of this function. This prevents overdraw when more than a single message of
709        // this type of received. In addition, if any of these frames need a repaint, that reflected
710        // when calling `handle_new_webrender_frame_ready`.
711        let mut saw_webrender_frame_ready_for_painter = HashMap::new();
712        messages.retain(|message| match message {
713            PaintMessage::NewWebRenderFrameReady(painter_id, _document_id, need_repaint) => {
714                if let Some(painter) = self.maybe_painter(*painter_id) {
715                    painter.decrement_pending_frames();
716                    *saw_webrender_frame_ready_for_painter
717                        .entry(*painter_id)
718                        .or_insert(*need_repaint) |= *need_repaint;
719                }
720
721                false
722            },
723            _ => true,
724        });
725
726        for message in messages {
727            self.handle_browser_message(message);
728            if self.shutdown_state() == ShutdownState::FinishedShuttingDown {
729                return;
730            }
731        }
732
733        for (painter_id, repaint_needed) in saw_webrender_frame_ready_for_painter.iter() {
734            if let Some(painter) = self.maybe_painter(*painter_id) {
735                painter.handle_new_webrender_frame_ready(*repaint_needed);
736            }
737        }
738    }
739
740    #[servo_tracing::instrument(skip_all)]
741    pub fn perform_updates(&self) -> bool {
742        if self.shutdown_state() == ShutdownState::FinishedShuttingDown {
743            return false;
744        }
745
746        // Run the WebXR main thread
747        #[cfg(feature = "webxr")]
748        self.webxr_main_thread.borrow_mut().run_one_frame();
749
750        for painter in &self.painters {
751            painter.borrow_mut().perform_updates();
752        }
753
754        self.shutdown_state() != ShutdownState::FinishedShuttingDown
755    }
756
757    pub fn toggle_webrender_debug(&self, option: WebRenderDebugOption) {
758        for painter in &self.painters {
759            painter.borrow_mut().toggle_webrender_debug(option);
760        }
761    }
762
763    pub fn capture_webrender(&self, webview_id: WebViewId) {
764        let capture_id = SystemTime::now()
765            .duration_since(UNIX_EPOCH)
766            .unwrap_or_default()
767            .as_secs()
768            .to_string();
769        let available_path = [env::current_dir(), Ok(env::temp_dir())]
770            .iter()
771            .filter_map(|val| {
772                val.as_ref()
773                    .map(|dir| dir.join("webrender-captures").join(&capture_id))
774                    .ok()
775            })
776            .find(|val| create_dir_all(val).is_ok());
777
778        let Some(capture_path) = available_path else {
779            log::error!("Couldn't create a path for WebRender captures.");
780            return;
781        };
782
783        log::info!("Saving WebRender capture to {capture_path:?}");
784        self.painter(webview_id.into())
785            .webrender_api
786            .save_capture(capture_path, CaptureBits::all());
787    }
788
789    pub fn notify_input_event(&self, webview_id: WebViewId, event: InputEventAndId) {
790        if self.shutdown_state() != ShutdownState::NotShuttingDown {
791            return;
792        }
793        self.painter_mut(webview_id.into())
794            .notify_input_event(webview_id, event);
795    }
796
797    pub fn notify_scroll_event(&self, webview_id: WebViewId, scroll: Scroll, point: WebViewPoint) {
798        if self.shutdown_state() != ShutdownState::NotShuttingDown {
799            return;
800        }
801        self.painter_mut(webview_id.into())
802            .notify_scroll_event(webview_id, scroll, point);
803    }
804
805    pub fn pinch_zoom(&self, webview_id: WebViewId, pinch_zoom_delta: f32, center: DevicePoint) {
806        if self.shutdown_state() != ShutdownState::NotShuttingDown {
807            return;
808        }
809        self.painter_mut(webview_id.into())
810            .pinch_zoom(webview_id, pinch_zoom_delta, center);
811    }
812
813    pub fn device_pixels_per_page_pixel(
814        &self,
815        webview_id: WebViewId,
816    ) -> Scale<f32, CSSPixel, DevicePixel> {
817        self.painter_mut(webview_id.into())
818            .device_pixels_per_page_pixel(webview_id)
819    }
820
821    pub(crate) fn shutdown_state(&self) -> ShutdownState {
822        self.shutdown_state.get()
823    }
824
825    pub fn request_screenshot(
826        &self,
827        webview_id: WebViewId,
828        rect: Option<WebViewRect>,
829        callback: Box<dyn FnOnce(Result<RgbaImage, ScreenshotCaptureError>) + 'static>,
830    ) {
831        self.painter(webview_id.into())
832            .request_screenshot(webview_id, rect, callback);
833    }
834
835    pub fn notify_input_event_handled(
836        &self,
837        webview_id: WebViewId,
838        input_event_id: InputEventId,
839        result: InputEventResult,
840    ) {
841        if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
842            painter.notify_input_event_handled(webview_id, input_event_id, result);
843        }
844    }
845
846    /// Generate an image key from the appropriate [`Painter`] or, if it is unknown, generate
847    /// a dummy image key. The unknown case needs to be handled because requests for keys
848    /// could theoretically come after a [`Painter`] has been released. A dummy key is okay
849    /// in this case because we will never render again in that case.
850    fn handle_generate_image_key(
851        &self,
852        webview_id: WebViewId,
853        result_sender: GenericSender<ImageKey>,
854    ) {
855        let painter_id = webview_id.into();
856        let image_key = self.maybe_painter(painter_id).map_or_else(
857            || ImageKey::new(painter_id.into(), 0),
858            |painter| painter.webrender_api.generate_image_key(),
859        );
860        let _ = result_sender.send(image_key);
861    }
862
863    /// Generate image keys from the appropriate [`Painter`] or, if it is unknown, generate
864    /// dummy image keys. The unknown case needs to be handled because requests for keys
865    /// could theoretically come after a [`Painter`] has been released. A dummy key is okay
866    /// in this case because we will never render again in that case.
867    fn handle_generate_image_keys_for_pipeline(
868        &self,
869        webview_id: WebViewId,
870        pipeline_id: PipelineId,
871    ) {
872        let painter_id = webview_id.into();
873        let painter = self.maybe_painter(painter_id);
874        let image_keys = (0..pref!(image_key_batch_size))
875            .map(|_| {
876                painter.as_ref().map_or_else(
877                    || ImageKey::new(painter_id.into(), 0),
878                    |painter| painter.webrender_api.generate_image_key(),
879                )
880            })
881            .collect();
882
883        let _ = self.embedder_to_constellation_sender.send(
884            EmbedderToConstellationMessage::SendImageKeysForPipeline(pipeline_id, image_keys),
885        );
886    }
887
888    /// Generate font keys from the appropriate [`Painter`] or, if it is unknown, generate
889    /// dummy font keys. The unknown case needs to be handled because requests for keys
890    /// could theoretically come after a [`Painter`] has been released. A dummy key is okay
891    /// in this case because we will never render again in that case.
892    fn handle_generate_font_keys(
893        &self,
894        number_of_font_keys: usize,
895        number_of_font_instance_keys: usize,
896        result_sender: GenericSender<(Vec<FontKey>, Vec<FontInstanceKey>)>,
897        painter_id: PainterId,
898    ) {
899        let painter = self.maybe_painter(painter_id);
900        let font_keys = (0..number_of_font_keys)
901            .map(|_| {
902                painter.as_ref().map_or_else(
903                    || FontKey::new(painter_id.into(), 0),
904                    |painter| painter.webrender_api.generate_font_key(),
905                )
906            })
907            .collect();
908        let font_instance_keys = (0..number_of_font_instance_keys)
909            .map(|_| {
910                painter.as_ref().map_or_else(
911                    || FontInstanceKey::new(painter_id.into(), 0),
912                    |painter| painter.webrender_api.generate_font_instance_key(),
913                )
914            })
915            .collect();
916
917        let _ = result_sender.send((font_keys, font_instance_keys));
918    }
919}