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