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};
6use std::collections::hash_map::Entry;
7use std::env;
8use std::fs::create_dir_all;
9use std::iter::once;
10use std::rc::Rc;
11use std::sync::Arc;
12use std::time::{SystemTime, UNIX_EPOCH};
13
14use base::Epoch;
15use base::cross_process_instant::CrossProcessInstant;
16use base::generic_channel::{GenericSender, RoutedReceiver};
17use base::id::{PipelineId, RenderingGroupId, WebViewId};
18use bitflags::bitflags;
19use compositing_traits::display_list::{CompositorDisplayListInfo, ScrollTree, ScrollType};
20use compositing_traits::rendering_context::RenderingContext;
21use compositing_traits::{
22    CompositionPipeline, CompositorMsg, ImageUpdate, PipelineExitSource, SendableFrameTree,
23    WebViewTrait,
24};
25use constellation_traits::{EmbedderToConstellationMessage, PaintMetricEvent};
26use crossbeam_channel::Sender;
27use dpi::PhysicalSize;
28use embedder_traits::{
29    CompositorHitTestResult, InputEventAndId, ScreenshotCaptureError, ShutdownState,
30    ViewportDetails,
31};
32use euclid::{Point2D, Scale, Size2D, Transform3D};
33use image::RgbaImage;
34use ipc_channel::ipc::{self, IpcSharedMemory};
35use log::{debug, info, trace, warn};
36use profile_traits::mem::{
37    ProcessReports, ProfilerRegistration, Report, ReportKind, perform_memory_report,
38};
39use profile_traits::time::{self as profile_time, ProfilerCategory};
40use profile_traits::{path, time_profile};
41use rustc_hash::{FxHashMap, FxHashSet};
42use servo_config::pref;
43use servo_geometry::DeviceIndependentPixel;
44use style_traits::CSSPixel;
45use webrender::{CaptureBits, RenderApi, Transaction};
46use webrender_api::units::{
47    DeviceIntPoint, DeviceIntRect, DevicePixel, DevicePoint, DeviceRect, LayoutPoint, LayoutRect,
48    LayoutSize, WorldPoint,
49};
50use webrender_api::{
51    self, BuiltDisplayList, DirtyRect, DisplayListPayload, DocumentId, Epoch as WebRenderEpoch,
52    ExternalScrollId, FontInstanceFlags, FontInstanceKey, FontInstanceOptions, FontKey,
53    FontVariation, ImageKey, PipelineId as WebRenderPipelineId, PropertyBinding,
54    ReferenceFrameKind, RenderReasons, SampledScrollOffset, ScrollLocation, SpaceAndClipInfo,
55    SpatialId, SpatialTreeItemKey, TransformStyle,
56};
57
58use crate::InitialCompositorState;
59use crate::refresh_driver::RefreshDriver;
60use crate::screenshot::ScreenshotTaker;
61use crate::webview_manager::WebViewManager;
62use crate::webview_renderer::{PinchZoomResult, UnknownWebView, WebViewRenderer};
63
64/// An option to control what kind of WebRender debugging is enabled while Servo is running.
65#[derive(Clone)]
66pub enum WebRenderDebugOption {
67    Profiler,
68    TextureCacheDebug,
69    RenderTargetDebug,
70}
71
72/// Data that is shared by all WebView renderers.
73pub struct ServoRenderer {
74    /// The [`RefreshDriver`] which manages the rythym of painting.
75    refresh_driver: RefreshDriver,
76
77    /// Tracks whether we are in the process of shutting down, or have shut down and should close
78    /// the compositor. This is shared with the `Servo` instance.
79    shutdown_state: Rc<Cell<ShutdownState>>,
80
81    /// The port on which we receive messages.
82    compositor_receiver: RoutedReceiver<CompositorMsg>,
83
84    /// The channel on which messages can be sent to the constellation.
85    pub(crate) constellation_sender: Sender<EmbedderToConstellationMessage>,
86
87    /// The channel on which messages can be sent to the time profiler.
88    time_profiler_chan: profile_time::ProfilerChan,
89
90    /// The WebRender [`RenderApi`] interface used to communicate with WebRender.
91    pub(crate) webrender_api: RenderApi,
92
93    /// The active webrender document.
94    pub(crate) webrender_document: DocumentId,
95
96    /// The GL bindings for webrender
97    webrender_gl: Rc<dyn gleam::gl::Gl>,
98
99    #[cfg(feature = "webxr")]
100    /// Some XR devices want to run on the main thread.
101    webxr_main_thread: webxr::MainThreadRegistry,
102
103    /// The last position in the rendered view that the mouse moved over. This becomes `None`
104    /// when the mouse leaves the rendered view.
105    pub(crate) last_mouse_move_position: Option<DevicePoint>,
106
107    /// A [`FrameRequestDelayer`] which is used to wait for canvas image updates to
108    /// arrive before requesting a new frame, as these happen asynchronously with
109    /// `ScriptThread` display list construction.
110    frame_delayer: FrameDelayer,
111}
112
113/// NB: Never block on the constellation, because sometimes the constellation blocks on us.
114pub struct IOCompositor {
115    /// Data that is shared by all WebView renderers.
116    global: Rc<RefCell<ServoRenderer>>,
117
118    /// Our [`WebViewRenderer`]s, one for every `WebView`.
119    webview_renderers: WebViewManager<WebViewRenderer>,
120
121    /// Tracks whether or not the view needs to be repainted.
122    needs_repaint: Cell<RepaintReason>,
123
124    /// The webrender renderer.
125    webrender: Option<webrender::Renderer>,
126
127    /// The [`RenderingContext`] instance that webrender targets, which is the viewport.
128    rendering_context: Rc<dyn RenderingContext>,
129
130    /// The number of frames pending to receive from WebRender.
131    pending_frames: Cell<usize>,
132
133    /// A [`ScreenshotTaker`] responsible for handling all screenshot requests.
134    screenshot_taker: ScreenshotTaker,
135
136    /// A handle to the memory profiler which will automatically unregister
137    /// when it's dropped.
138    _mem_profiler_registration: ProfilerRegistration,
139}
140
141/// Why we need to be repainted. This is used for debugging.
142#[derive(Clone, Copy, Default, PartialEq)]
143pub(crate) struct RepaintReason(u8);
144
145bitflags! {
146    impl RepaintReason: u8 {
147        /// We're performing the single repaint in headless mode.
148        const ReadyForScreenshot = 1 << 0;
149        /// We're performing a repaint to run an animation.
150        const ChangedAnimationState = 1 << 1;
151        /// A new WebRender frame has arrived.
152        const NewWebRenderFrame = 1 << 2;
153        /// The window has been resized and will need to be synchronously repainted.
154        const Resize = 1 << 3;
155    }
156}
157
158/// The paint status of a particular pipeline in the Servo renderer. This is used to trigger metrics
159/// in script (via the constellation) when display lists are received.
160///
161/// See <https://w3c.github.io/paint-timing/#first-contentful-paint>.
162#[derive(PartialEq)]
163pub(crate) enum PaintMetricState {
164    /// The renderer is still waiting to process a display list which triggers this metric.
165    Waiting,
166    /// The renderer has processed the display list which will trigger this event, marked the Servo
167    /// instance ready to paint, and is waiting for the given epoch to actually be rendered.
168    Seen(WebRenderEpoch, bool /* first_reflow */),
169    /// The metric has been sent to the constellation and no more work needs to be done.
170    Sent,
171}
172
173pub(crate) struct PipelineDetails {
174    /// The pipeline associated with this PipelineDetails object.
175    pub pipeline: Option<CompositionPipeline>,
176
177    /// The id of the parent pipeline, if any.
178    pub parent_pipeline_id: Option<PipelineId>,
179
180    /// Whether animations are running
181    pub animations_running: bool,
182
183    /// Whether there are animation callbacks
184    pub animation_callbacks_running: bool,
185
186    /// Whether to use less resources by stopping animations.
187    pub throttled: bool,
188
189    /// The compositor-side [ScrollTree]. This is used to allow finding and scrolling
190    /// nodes in the compositor before forwarding new offsets to WebRender.
191    pub scroll_tree: ScrollTree,
192
193    /// The paint metric status of the first paint.
194    pub first_paint_metric: PaintMetricState,
195
196    /// The paint metric status of the first contentful paint.
197    pub first_contentful_paint_metric: PaintMetricState,
198
199    /// The CSS pixel to device pixel scale of the viewport of this pipeline, including
200    /// page zoom, but not including any pinch zoom amount. This is used to detect
201    /// situations where the current display list is for an old scale.
202    pub viewport_scale: Option<Scale<f32, CSSPixel, DevicePixel>>,
203
204    /// Which parts of Servo have reported that this `Pipeline` has exited. Only when all
205    /// have done so will it be discarded.
206    pub exited: PipelineExitSource,
207
208    /// The [`Epoch`] of the latest display list received for this `Pipeline` or `None` if no
209    /// display list has been received.
210    pub display_list_epoch: Option<Epoch>,
211}
212
213impl PipelineDetails {
214    pub(crate) fn animation_callbacks_running(&self) -> bool {
215        self.animation_callbacks_running
216    }
217
218    pub(crate) fn animating(&self) -> bool {
219        !self.throttled && (self.animation_callbacks_running || self.animations_running)
220    }
221}
222
223impl PipelineDetails {
224    pub(crate) fn new() -> PipelineDetails {
225        PipelineDetails {
226            pipeline: None,
227            parent_pipeline_id: None,
228            viewport_scale: None,
229            animations_running: false,
230            animation_callbacks_running: false,
231            throttled: false,
232            scroll_tree: ScrollTree::default(),
233            first_paint_metric: PaintMetricState::Waiting,
234            first_contentful_paint_metric: PaintMetricState::Waiting,
235            exited: PipelineExitSource::empty(),
236            display_list_epoch: None,
237        }
238    }
239
240    fn install_new_scroll_tree(&mut self, new_scroll_tree: ScrollTree) {
241        let old_scroll_offsets = self.scroll_tree.scroll_offsets();
242        self.scroll_tree = new_scroll_tree;
243        self.scroll_tree.set_all_scroll_offsets(&old_scroll_offsets);
244    }
245}
246
247impl ServoRenderer {
248    pub fn shutdown_state(&self) -> ShutdownState {
249        self.shutdown_state.get()
250    }
251
252    pub(crate) fn hit_test_at_point(&self, point: DevicePoint) -> Vec<CompositorHitTestResult> {
253        // DevicePoint and WorldPoint are the same for us.
254        let world_point = WorldPoint::from_untyped(point.to_untyped());
255        let results = self
256            .webrender_api
257            .hit_test(self.webrender_document, world_point);
258
259        results
260            .items
261            .iter()
262            .map(|item| {
263                let pipeline_id = item.pipeline.into();
264                let external_scroll_id = ExternalScrollId(item.tag.0, item.pipeline);
265                CompositorHitTestResult {
266                    pipeline_id,
267                    point_in_viewport: Point2D::from_untyped(item.point_in_viewport.to_untyped()),
268                    external_scroll_id,
269                }
270            })
271            .collect()
272    }
273
274    pub(crate) fn send_transaction(&mut self, transaction: Transaction) {
275        self.webrender_api
276            .send_transaction(self.webrender_document, transaction);
277    }
278}
279
280impl IOCompositor {
281    pub fn new(state: InitialCompositorState) -> Self {
282        let registration = state.mem_profiler_chan.prepare_memory_reporting(
283            "compositor".into(),
284            state.sender.clone(),
285            CompositorMsg::CollectMemoryReport,
286        );
287        let compositor = IOCompositor {
288            global: Rc::new(RefCell::new(ServoRenderer {
289                refresh_driver: RefreshDriver::new(
290                    state.constellation_chan.clone(),
291                    state.event_loop_waker,
292                ),
293                shutdown_state: state.shutdown_state,
294                compositor_receiver: state.receiver,
295                constellation_sender: state.constellation_chan,
296                time_profiler_chan: state.time_profiler_chan,
297                webrender_api: state.webrender_api,
298                webrender_document: state.webrender_document,
299                webrender_gl: state.webrender_gl,
300                #[cfg(feature = "webxr")]
301                webxr_main_thread: state.webxr_main_thread,
302                last_mouse_move_position: None,
303                frame_delayer: Default::default(),
304            })),
305            webview_renderers: WebViewManager::default(),
306            needs_repaint: Cell::default(),
307            webrender: Some(state.webrender),
308            rendering_context: state.rendering_context,
309            pending_frames: Default::default(),
310            screenshot_taker: Default::default(),
311            _mem_profiler_registration: registration,
312        };
313
314        {
315            let gl = &compositor.global.borrow().webrender_gl;
316            info!("Running on {}", gl.get_string(gleam::gl::RENDERER));
317            info!("OpenGL Version {}", gl.get_string(gleam::gl::VERSION));
318        }
319        compositor.assert_gl_framebuffer_complete();
320        compositor
321    }
322
323    pub fn deinit(&mut self) {
324        if let Err(err) = self.rendering_context.make_current() {
325            warn!("Failed to make the rendering context current: {:?}", err);
326        }
327        if let Some(webrender) = self.webrender.take() {
328            webrender.deinit();
329        }
330    }
331
332    pub(crate) fn rendering_context(&self) -> &dyn RenderingContext {
333        &*self.rendering_context
334    }
335
336    pub fn rendering_context_size(&self) -> Size2D<u32, DevicePixel> {
337        self.rendering_context.size2d()
338    }
339
340    pub fn webxr_running(&self) -> bool {
341        #[cfg(feature = "webxr")]
342        {
343            self.global.borrow().webxr_main_thread.running()
344        }
345        #[cfg(not(feature = "webxr"))]
346        {
347            false
348        }
349    }
350
351    pub(crate) fn webview_renderer(&self, webview_id: WebViewId) -> Option<&WebViewRenderer> {
352        self.webview_renderers.get(webview_id)
353    }
354
355    pub(crate) fn set_needs_repaint(&self, reason: RepaintReason) {
356        let mut needs_repaint = self.needs_repaint.get();
357        needs_repaint.insert(reason);
358        self.needs_repaint.set(needs_repaint);
359    }
360
361    pub fn needs_repaint(&self) -> bool {
362        let repaint_reason = self.needs_repaint.get();
363        if repaint_reason.is_empty() {
364            return false;
365        }
366
367        !self
368            .global
369            .borrow()
370            .refresh_driver
371            .wait_to_paint(repaint_reason)
372    }
373
374    pub fn finish_shutting_down(&mut self) {
375        // Drain compositor port, sometimes messages contain channels that are blocking
376        // another thread from finishing (i.e. SetFrameTree).
377        while self
378            .global
379            .borrow_mut()
380            .compositor_receiver
381            .try_recv()
382            .is_ok()
383        {}
384
385        // Tell the profiler, memory profiler, and scrolling timer to shut down.
386        if let Ok((sender, receiver)) = ipc::channel() {
387            self.global
388                .borrow()
389                .time_profiler_chan
390                .send(profile_time::ProfilerMsg::Exit(sender));
391            let _ = receiver.recv();
392        }
393    }
394
395    fn handle_browser_message(&mut self, msg: CompositorMsg) {
396        trace_msg_from_constellation!(msg, "{msg:?}");
397
398        match self.shutdown_state() {
399            ShutdownState::NotShuttingDown => {},
400            ShutdownState::ShuttingDown => {
401                self.handle_browser_message_while_shutting_down(msg);
402                return;
403            },
404            ShutdownState::FinishedShuttingDown => {
405                // Messages to the compositor are ignored after shutdown is complete.
406                return;
407            },
408        }
409
410        match msg {
411            CompositorMsg::CollectMemoryReport(sender) => {
412                let ops =
413                    wr_malloc_size_of::MallocSizeOfOps::new(servo_allocator::usable_size, None);
414                let report = self.global.borrow().webrender_api.report_memory(ops);
415                let mut reports = vec![
416                    Report {
417                        path: path!["webrender", "fonts"],
418                        kind: ReportKind::ExplicitJemallocHeapSize,
419                        size: report.fonts,
420                    },
421                    Report {
422                        path: path!["webrender", "images"],
423                        kind: ReportKind::ExplicitJemallocHeapSize,
424                        size: report.images,
425                    },
426                    Report {
427                        path: path!["webrender", "display-list"],
428                        kind: ReportKind::ExplicitJemallocHeapSize,
429                        size: report.display_list,
430                    },
431                ];
432
433                perform_memory_report(|ops| {
434                    reports.push(Report {
435                        path: path!["compositor", "scroll-tree"],
436                        kind: ReportKind::ExplicitJemallocHeapSize,
437                        size: self.webview_renderers.scroll_trees_memory_usage(ops),
438                    });
439                });
440
441                sender.send(ProcessReports::new(reports));
442            },
443
444            CompositorMsg::ChangeRunningAnimationsState(
445                webview_id,
446                pipeline_id,
447                animation_state,
448            ) => {
449                let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
450                    return;
451                };
452
453                if webview_renderer
454                    .change_pipeline_running_animations_state(pipeline_id, animation_state)
455                {
456                    self.global
457                        .borrow()
458                        .refresh_driver
459                        .notify_animation_state_changed(webview_renderer);
460                }
461            },
462
463            CompositorMsg::CreateOrUpdateWebView(frame_tree) => {
464                self.set_frame_tree_for_webview(&frame_tree);
465            },
466
467            CompositorMsg::RemoveWebView(webview_id) => {
468                self.remove_webview(webview_id);
469            },
470
471            CompositorMsg::TouchEventProcessed(webview_id, result) => {
472                let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
473                    warn!("Handling input event for unknown webview: {webview_id}");
474                    return;
475                };
476                webview_renderer.on_touch_event_processed(result);
477            },
478
479            CompositorMsg::SetThrottled(webview_id, pipeline_id, throttled) => {
480                let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
481                    return;
482                };
483
484                if webview_renderer.set_throttled(pipeline_id, throttled) {
485                    self.global
486                        .borrow()
487                        .refresh_driver
488                        .notify_animation_state_changed(webview_renderer);
489                }
490            },
491
492            CompositorMsg::PipelineExited(webview_id, pipeline_id, pipeline_exit_source) => {
493                debug!(
494                    "Compositor got pipeline exited: {:?} {:?}",
495                    webview_id, pipeline_id
496                );
497                if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
498                    webview_renderer.pipeline_exited(pipeline_id, pipeline_exit_source);
499                }
500            },
501
502            CompositorMsg::NewWebRenderFrameReady(..) => {
503                unreachable!("New WebRender frames should be handled in the caller.");
504            },
505
506            CompositorMsg::SendInitialTransaction(webview_id, pipeline_id) => {
507                let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
508                    return warn!("Could not find WebView for incoming display list");
509                };
510
511                let starting_epoch = Epoch(0);
512                let details = webview_renderer.ensure_pipeline_details(pipeline_id.into());
513                details.display_list_epoch = Some(starting_epoch);
514
515                let mut txn = Transaction::new();
516                txn.set_display_list(starting_epoch.into(), (pipeline_id, Default::default()));
517                self.generate_frame(&mut txn, RenderReasons::SCENE);
518                self.global.borrow_mut().send_transaction(txn);
519            },
520
521            CompositorMsg::SendScrollNode(webview_id, pipeline_id, offset, external_scroll_id) => {
522                let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
523                    return;
524                };
525
526                let pipeline_id = pipeline_id.into();
527                let Some(pipeline_details) = webview_renderer.pipelines.get_mut(&pipeline_id)
528                else {
529                    return;
530                };
531
532                let Some(offset) = pipeline_details
533                    .scroll_tree
534                    .set_scroll_offset_for_node_with_external_scroll_id(
535                        external_scroll_id,
536                        offset,
537                        ScrollType::Script,
538                    )
539                else {
540                    // The renderer should be fully up-to-date with script at this point and script
541                    // should never try to scroll to an invalid location.
542                    warn!("Could not scroll node with id: {external_scroll_id:?}");
543                    return;
544                };
545
546                let mut txn = Transaction::new();
547                txn.set_scroll_offsets(
548                    external_scroll_id,
549                    vec![SampledScrollOffset {
550                        offset,
551                        generation: 0,
552                    }],
553                );
554                self.generate_frame(&mut txn, RenderReasons::APZ);
555                self.global.borrow_mut().send_transaction(txn);
556            },
557
558            CompositorMsg::UpdateEpoch {
559                webview_id,
560                pipeline_id,
561                epoch,
562            } => {
563                let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
564                    return warn!("Could not find WebView for Epoch update.");
565                };
566                webview_renderer
567                    .ensure_pipeline_details(pipeline_id)
568                    .display_list_epoch = Some(Epoch(epoch.0));
569            },
570
571            CompositorMsg::SendDisplayList {
572                webview_id,
573                display_list_descriptor,
574                display_list_receiver,
575            } => {
576                // This must match the order from the sender, currently in `shared/script/lib.rs`.
577                let display_list_info = match display_list_receiver.recv() {
578                    Ok(display_list_info) => display_list_info,
579                    Err(error) => {
580                        return warn!("Could not receive display list info: {error}");
581                    },
582                };
583                let display_list_info: CompositorDisplayListInfo =
584                    match bincode::deserialize(&display_list_info) {
585                        Ok(display_list_info) => display_list_info,
586                        Err(error) => {
587                            return warn!("Could not deserialize display list info: {error}");
588                        },
589                    };
590                let items_data = match display_list_receiver.recv() {
591                    Ok(display_list_data) => display_list_data,
592                    Err(error) => {
593                        return warn!(
594                            "Could not receive WebRender display list items data: {error}"
595                        );
596                    },
597                };
598                let cache_data = match display_list_receiver.recv() {
599                    Ok(display_list_data) => display_list_data,
600                    Err(error) => {
601                        return warn!(
602                            "Could not receive WebRender display list cache data: {error}"
603                        );
604                    },
605                };
606                let spatial_tree = match display_list_receiver.recv() {
607                    Ok(display_list_data) => display_list_data,
608                    Err(error) => {
609                        return warn!(
610                            "Could not receive WebRender display list spatial tree: {error}."
611                        );
612                    },
613                };
614                let built_display_list = BuiltDisplayList::from_data(
615                    DisplayListPayload {
616                        items_data,
617                        cache_data,
618                        spatial_tree,
619                    },
620                    display_list_descriptor,
621                );
622
623                #[cfg(feature = "tracing")]
624                let _span = tracing::trace_span!(
625                    "ScriptToCompositorMsg::BuiltDisplayList",
626                    servo_profiling = true,
627                )
628                .entered();
629
630                let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
631                    return warn!("Could not find WebView for incoming display list");
632                };
633
634                let old_scale = webview_renderer.device_pixels_per_page_pixel();
635
636                let pipeline_id = display_list_info.pipeline_id;
637                let details = webview_renderer.ensure_pipeline_details(pipeline_id.into());
638                details.install_new_scroll_tree(display_list_info.scroll_tree);
639                details.viewport_scale =
640                    Some(display_list_info.viewport_details.hidpi_scale_factor);
641
642                let epoch = display_list_info.epoch.into();
643                let first_reflow = display_list_info.first_reflow;
644                if details.first_paint_metric == PaintMetricState::Waiting {
645                    details.first_paint_metric = PaintMetricState::Seen(epoch, first_reflow);
646                }
647                if details.first_contentful_paint_metric == PaintMetricState::Waiting &&
648                    display_list_info.is_contentful
649                {
650                    details.first_contentful_paint_metric =
651                        PaintMetricState::Seen(epoch, first_reflow);
652                }
653
654                let mut transaction = Transaction::new();
655                let is_root_pipeline =
656                    Some(pipeline_id.into()) == webview_renderer.root_pipeline_id;
657                if is_root_pipeline && old_scale != webview_renderer.device_pixels_per_page_pixel()
658                {
659                    self.send_root_pipeline_display_list_in_transaction(&mut transaction);
660                }
661
662                transaction.set_display_list(epoch, (pipeline_id, built_display_list));
663                self.update_transaction_with_all_scroll_offsets(&mut transaction);
664                self.global.borrow_mut().send_transaction(transaction);
665            },
666
667            CompositorMsg::GenerateFrame => {
668                let mut global = self.global.borrow_mut();
669                global.frame_delayer.set_pending_frame(true);
670
671                if !global.frame_delayer.needs_new_frame() {
672                    return;
673                }
674
675                let mut transaction = Transaction::new();
676                self.generate_frame(&mut transaction, RenderReasons::SCENE);
677                global.send_transaction(transaction);
678
679                let waiting_pipelines = global.frame_delayer.take_waiting_pipelines();
680                let _ = global.constellation_sender.send(
681                    EmbedderToConstellationMessage::NoLongerWaitingOnAsynchronousImageUpdates(
682                        waiting_pipelines,
683                    ),
684                );
685                global.frame_delayer.set_pending_frame(false);
686                self.screenshot_taker
687                    .prepare_screenshot_requests_for_render(self)
688            },
689
690            CompositorMsg::GenerateImageKey(sender) => {
691                let _ = sender.send(self.global.borrow().webrender_api.generate_image_key());
692            },
693
694            CompositorMsg::GenerateImageKeysForPipeline(pipeline_id) => {
695                let image_keys = (0..pref!(image_key_batch_size))
696                    .map(|_| self.global.borrow().webrender_api.generate_image_key())
697                    .collect();
698                if let Err(error) = self.global.borrow().constellation_sender.send(
699                    EmbedderToConstellationMessage::SendImageKeysForPipeline(
700                        pipeline_id,
701                        image_keys,
702                    ),
703                ) {
704                    warn!("Sending Image Keys to Constellation failed with({error:?}).");
705                }
706            },
707            CompositorMsg::UpdateImages(updates) => {
708                let mut global = self.global.borrow_mut();
709                let mut txn = Transaction::new();
710                for update in updates {
711                    match update {
712                        ImageUpdate::AddImage(key, desc, data) => {
713                            txn.add_image(key, desc, data.into(), None)
714                        },
715                        ImageUpdate::DeleteImage(key) => {
716                            txn.delete_image(key);
717                            global.frame_delayer.delete_image(key);
718                        },
719                        ImageUpdate::UpdateImage(key, desc, data, epoch) => {
720                            if let Some(epoch) = epoch {
721                                global.frame_delayer.update_image(key, epoch);
722                            }
723                            txn.update_image(key, desc, data.into(), &DirtyRect::All)
724                        },
725                    }
726                }
727
728                if global.frame_delayer.needs_new_frame() {
729                    global.frame_delayer.set_pending_frame(false);
730                    self.generate_frame(&mut txn, RenderReasons::SCENE);
731                    let waiting_pipelines = global.frame_delayer.take_waiting_pipelines();
732                    let _ = global.constellation_sender.send(
733                        EmbedderToConstellationMessage::NoLongerWaitingOnAsynchronousImageUpdates(
734                            waiting_pipelines,
735                        ),
736                    );
737                    self.screenshot_taker
738                        .prepare_screenshot_requests_for_render(self);
739                }
740
741                global.send_transaction(txn);
742            },
743
744            CompositorMsg::DelayNewFrameForCanvas(pipeline_id, canvas_epoch, image_keys) => self
745                .global
746                .borrow_mut()
747                .frame_delayer
748                .add_delay(pipeline_id, canvas_epoch, image_keys),
749
750            CompositorMsg::AddFont(font_key, data, index) => {
751                self.add_font(font_key, index, data);
752            },
753
754            CompositorMsg::AddSystemFont(font_key, native_handle) => {
755                let mut transaction = Transaction::new();
756                transaction.add_native_font(font_key, native_handle);
757                self.global.borrow_mut().send_transaction(transaction);
758            },
759
760            CompositorMsg::AddFontInstance(
761                font_instance_key,
762                font_key,
763                size,
764                flags,
765                variations,
766            ) => {
767                self.add_font_instance(font_instance_key, font_key, size, flags, variations);
768            },
769
770            CompositorMsg::RemoveFonts(keys, instance_keys) => {
771                let mut transaction = Transaction::new();
772
773                for instance in instance_keys.into_iter() {
774                    transaction.delete_font_instance(instance);
775                }
776                for key in keys.into_iter() {
777                    transaction.delete_font(key);
778                }
779
780                self.global.borrow_mut().send_transaction(transaction);
781            },
782
783            CompositorMsg::GenerateFontKeys(
784                number_of_font_keys,
785                number_of_font_instance_keys,
786                result_sender,
787                rendering_group_id,
788            ) => {
789                self.handle_generate_font_keys(
790                    number_of_font_keys,
791                    number_of_font_instance_keys,
792                    result_sender,
793                    rendering_group_id,
794                );
795            },
796            CompositorMsg::Viewport(webview_id, viewport_description) => {
797                if let Some(webview) = self.webview_renderers.get_mut(webview_id) {
798                    webview.set_viewport_description(viewport_description);
799                }
800            },
801            CompositorMsg::ScreenshotReadinessReponse(webview_id, pipelines_and_epochs) => {
802                self.screenshot_taker.handle_screenshot_readiness_reply(
803                    webview_id,
804                    pipelines_and_epochs,
805                    self,
806                );
807            },
808        }
809    }
810
811    /// Handle messages sent to the compositor during the shutdown process. In general,
812    /// the things the compositor can do in this state are limited. It's very important to
813    /// answer any synchronous messages though as other threads might be waiting on the
814    /// results to finish their own shut down process. We try to do as little as possible
815    /// during this time.
816    ///
817    /// When that involves generating WebRender ids, our approach here is to simply
818    /// generate them, but assume they will never be used, since once shutting down the
819    /// compositor no longer does any WebRender frame generation.
820    fn handle_browser_message_while_shutting_down(&mut self, msg: CompositorMsg) {
821        match msg {
822            CompositorMsg::PipelineExited(webview_id, pipeline_id, pipeline_exit_source) => {
823                debug!(
824                    "Compositor got pipeline exited: {:?} {:?}",
825                    webview_id, pipeline_id
826                );
827                if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
828                    webview_renderer.pipeline_exited(pipeline_id, pipeline_exit_source);
829                }
830            },
831            CompositorMsg::GenerateImageKey(sender) => {
832                let _ = sender.send(self.global.borrow().webrender_api.generate_image_key());
833            },
834            CompositorMsg::GenerateFontKeys(
835                number_of_font_keys,
836                number_of_font_instance_keys,
837                result_sender,
838                rendering_group_id,
839            ) => {
840                self.handle_generate_font_keys(
841                    number_of_font_keys,
842                    number_of_font_instance_keys,
843                    result_sender,
844                    rendering_group_id,
845                );
846            },
847            _ => {
848                debug!("Ignoring message ({:?} while shutting down", msg);
849            },
850        }
851    }
852
853    /// Generate the font keys and send them to the `result_sender`.
854    /// Currently `RenderingGroupId` is not used.
855    fn handle_generate_font_keys(
856        &self,
857        number_of_font_keys: usize,
858        number_of_font_instance_keys: usize,
859        result_sender: GenericSender<(Vec<FontKey>, Vec<FontInstanceKey>)>,
860        _rendering_group_id: RenderingGroupId,
861    ) {
862        let font_keys = (0..number_of_font_keys)
863            .map(|_| self.global.borrow().webrender_api.generate_font_key())
864            .collect();
865        let font_instance_keys = (0..number_of_font_instance_keys)
866            .map(|_| {
867                self.global
868                    .borrow()
869                    .webrender_api
870                    .generate_font_instance_key()
871            })
872            .collect();
873        let _ = result_sender.send((font_keys, font_instance_keys));
874    }
875
876    /// Queue a new frame in the transaction and increase the pending frames count.
877    pub(crate) fn generate_frame(&self, transaction: &mut Transaction, reason: RenderReasons) {
878        transaction.generate_frame(0, true /* present */, false /* tracked */, reason);
879        self.pending_frames.set(self.pending_frames.get() + 1);
880    }
881
882    /// Set the root pipeline for our WebRender scene to a display list that consists of an iframe
883    /// for each visible top-level browsing context, applying a transformation on the root for
884    /// pinch zoom, page zoom, and HiDPI scaling.
885    fn send_root_pipeline_display_list(&mut self) {
886        let mut transaction = Transaction::new();
887        self.send_root_pipeline_display_list_in_transaction(&mut transaction);
888        self.generate_frame(&mut transaction, RenderReasons::SCENE);
889        self.global.borrow_mut().send_transaction(transaction);
890    }
891
892    /// Set the root pipeline for our WebRender scene to a display list that consists of an iframe
893    /// for each visible top-level browsing context, applying a transformation on the root for
894    /// pinch zoom, page zoom, and HiDPI scaling.
895    pub(crate) fn send_root_pipeline_display_list_in_transaction(
896        &self,
897        transaction: &mut Transaction,
898    ) {
899        // Every display list needs a pipeline, but we'd like to choose one that is unlikely
900        // to conflict with our content pipelines, which start at (1, 1). (0, 0) is WebRender's
901        // dummy pipeline, so we choose (0, 1).
902        let root_pipeline = WebRenderPipelineId(0, 1);
903        transaction.set_root_pipeline(root_pipeline);
904
905        let mut builder = webrender_api::DisplayListBuilder::new(root_pipeline);
906        builder.begin();
907
908        let root_reference_frame = SpatialId::root_reference_frame(root_pipeline);
909
910        let viewport_size = self.rendering_context.size2d().to_f32().to_untyped();
911        let viewport_rect = LayoutRect::from_origin_and_size(
912            LayoutPoint::zero(),
913            LayoutSize::from_untyped(viewport_size),
914        );
915
916        let root_clip_id = builder.define_clip_rect(root_reference_frame, viewport_rect);
917        let clip_chain_id = builder.define_clip_chain(None, [root_clip_id]);
918        for (_, webview_renderer) in self.webview_renderers.painting_order() {
919            let Some(pipeline_id) = webview_renderer.root_pipeline_id else {
920                continue;
921            };
922
923            let device_pixels_per_page_pixel = webview_renderer.device_pixels_per_page_pixel().0;
924            let webview_reference_frame = builder.push_reference_frame(
925                LayoutPoint::zero(),
926                root_reference_frame,
927                TransformStyle::Flat,
928                PropertyBinding::Value(Transform3D::scale(
929                    device_pixels_per_page_pixel,
930                    device_pixels_per_page_pixel,
931                    1.,
932                )),
933                ReferenceFrameKind::Transform {
934                    is_2d_scale_translation: true,
935                    should_snap: true,
936                    paired_with_perspective: false,
937                },
938                SpatialTreeItemKey::new(0, 0),
939            );
940
941            let scaled_webview_rect = webview_renderer.rect / device_pixels_per_page_pixel;
942            builder.push_iframe(
943                LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()),
944                LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()),
945                &SpaceAndClipInfo {
946                    spatial_id: webview_reference_frame,
947                    clip_chain_id,
948                },
949                pipeline_id.into(),
950                true,
951            );
952        }
953
954        let built_display_list = builder.end();
955
956        // NB: We are always passing 0 as the epoch here, but this doesn't seem to
957        // be an issue. WebRender will still update the scene and generate a new
958        // frame even though the epoch hasn't changed.
959        transaction.set_display_list(WebRenderEpoch(0), built_display_list);
960        self.update_transaction_with_all_scroll_offsets(transaction);
961    }
962
963    /// Update the given transaction with the scroll offsets of all active scroll nodes in
964    /// the WebRender scene. This is necessary because WebRender does not preserve scroll
965    /// offsets between scroll tree modifications. If a display list could potentially
966    /// modify a scroll tree branch, WebRender needs to have scroll offsets for that
967    /// branch.
968    ///
969    /// TODO(mrobinson): Could we only send offsets for the branch being modified
970    /// and not the entire scene?
971    fn update_transaction_with_all_scroll_offsets(&self, transaction: &mut Transaction) {
972        for webview_renderer in self.webview_renderers.iter() {
973            for details in webview_renderer.pipelines.values() {
974                for node in details.scroll_tree.nodes.iter() {
975                    let (Some(offset), Some(external_id)) = (node.offset(), node.external_id())
976                    else {
977                        continue;
978                    };
979
980                    transaction.set_scroll_offsets(
981                        external_id,
982                        vec![SampledScrollOffset {
983                            offset,
984                            generation: 0,
985                        }],
986                    );
987                }
988            }
989        }
990    }
991
992    pub fn add_webview(
993        &mut self,
994        webview: Box<dyn WebViewTrait>,
995        viewport_details: ViewportDetails,
996    ) {
997        self.webview_renderers
998            .entry(webview.id())
999            .or_insert(WebViewRenderer::new(
1000                self.global.clone(),
1001                webview,
1002                viewport_details,
1003            ));
1004    }
1005
1006    fn set_frame_tree_for_webview(&mut self, frame_tree: &SendableFrameTree) {
1007        debug!("{}: Setting frame tree for webview", frame_tree.pipeline.id);
1008
1009        let webview_id = frame_tree.pipeline.webview_id;
1010        let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
1011            warn!(
1012                "Attempted to set frame tree on unknown WebView (perhaps closed?): {webview_id:?}"
1013            );
1014            return;
1015        };
1016
1017        webview_renderer.set_frame_tree(frame_tree);
1018        self.send_root_pipeline_display_list();
1019    }
1020
1021    fn remove_webview(&mut self, webview_id: WebViewId) {
1022        debug!("{}: Removing", webview_id);
1023        if self.webview_renderers.remove(webview_id).is_err() {
1024            warn!("{webview_id}: Removing unknown webview");
1025            return;
1026        };
1027
1028        self.send_root_pipeline_display_list();
1029    }
1030
1031    pub fn show_webview(
1032        &mut self,
1033        webview_id: WebViewId,
1034        hide_others: bool,
1035    ) -> Result<(), UnknownWebView> {
1036        debug!("{webview_id}: Showing webview; hide_others={hide_others}");
1037        let painting_order_changed = if hide_others {
1038            let result = self
1039                .webview_renderers
1040                .painting_order()
1041                .map(|(&id, _)| id)
1042                .ne(once(webview_id));
1043            self.webview_renderers.hide_all();
1044            self.webview_renderers.show(webview_id)?;
1045            result
1046        } else {
1047            self.webview_renderers.show(webview_id)?
1048        };
1049        if painting_order_changed {
1050            self.send_root_pipeline_display_list();
1051        }
1052        Ok(())
1053    }
1054
1055    pub fn hide_webview(&mut self, webview_id: WebViewId) -> Result<(), UnknownWebView> {
1056        debug!("{webview_id}: Hiding webview");
1057        if self.webview_renderers.hide(webview_id)? {
1058            self.send_root_pipeline_display_list();
1059        }
1060        Ok(())
1061    }
1062
1063    pub fn raise_webview_to_top(
1064        &mut self,
1065        webview_id: WebViewId,
1066        hide_others: bool,
1067    ) -> Result<(), UnknownWebView> {
1068        debug!("{webview_id}: Raising webview to top; hide_others={hide_others}");
1069        let painting_order_changed = if hide_others {
1070            let result = self
1071                .webview_renderers
1072                .painting_order()
1073                .map(|(&id, _)| id)
1074                .ne(once(webview_id));
1075            self.webview_renderers.hide_all();
1076            self.webview_renderers.raise_to_top(webview_id)?;
1077            result
1078        } else {
1079            self.webview_renderers.raise_to_top(webview_id)?
1080        };
1081        if painting_order_changed {
1082            self.send_root_pipeline_display_list();
1083        }
1084        Ok(())
1085    }
1086
1087    pub fn move_resize_webview(&mut self, webview_id: WebViewId, rect: DeviceRect) {
1088        if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
1089            return;
1090        }
1091        let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
1092            return;
1093        };
1094        if !webview_renderer.set_rect(rect) {
1095            return;
1096        }
1097
1098        self.send_root_pipeline_display_list();
1099        self.set_needs_repaint(RepaintReason::Resize);
1100    }
1101
1102    pub fn set_hidpi_scale_factor(
1103        &mut self,
1104        webview_id: WebViewId,
1105        new_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1106    ) {
1107        if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
1108            return;
1109        }
1110        let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
1111            return;
1112        };
1113        if !webview_renderer.set_hidpi_scale_factor(new_scale_factor) {
1114            return;
1115        }
1116
1117        self.send_root_pipeline_display_list();
1118        self.set_needs_repaint(RepaintReason::Resize);
1119    }
1120
1121    pub fn resize_rendering_context(&mut self, new_size: PhysicalSize<u32>) {
1122        if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
1123            return;
1124        }
1125        if self.rendering_context.size() == new_size {
1126            return;
1127        }
1128
1129        self.rendering_context.resize(new_size);
1130
1131        let mut transaction = Transaction::new();
1132        let output_region = DeviceIntRect::new(
1133            Point2D::zero(),
1134            Point2D::new(new_size.width as i32, new_size.height as i32),
1135        );
1136        transaction.set_document_view(output_region);
1137        self.global.borrow_mut().send_transaction(transaction);
1138
1139        self.send_root_pipeline_display_list();
1140        self.set_needs_repaint(RepaintReason::Resize);
1141    }
1142
1143    pub fn on_zoom_window_event(&mut self, webview_id: WebViewId, new_zoom: f32) {
1144        if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
1145            return;
1146        }
1147
1148        if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
1149            webview_renderer.set_page_zoom(Scale::new(new_zoom));
1150        }
1151    }
1152
1153    /// Returns true if any animation callbacks (ie `requestAnimationFrame`) are waiting for a response.
1154    fn animation_callbacks_running(&self) -> bool {
1155        self.webview_renderers
1156            .iter()
1157            .any(WebViewRenderer::animation_callbacks_running)
1158    }
1159
1160    /// Render the WebRender scene to the active `RenderingContext`.
1161    pub fn render(&mut self) {
1162        self.global
1163            .borrow()
1164            .refresh_driver
1165            .notify_will_paint(self.webview_renderers.iter());
1166
1167        self.render_inner();
1168
1169        // We've painted the default target, which means that from the embedder's perspective,
1170        // the scene no longer needs to be repainted.
1171        self.needs_repaint.set(RepaintReason::empty());
1172    }
1173
1174    #[servo_tracing::instrument(skip_all)]
1175    fn render_inner(&mut self) {
1176        if let Err(err) = self.rendering_context.make_current() {
1177            warn!("Failed to make the rendering context current: {:?}", err);
1178        }
1179        self.assert_no_gl_error();
1180
1181        if let Some(webrender) = self.webrender.as_mut() {
1182            webrender.update();
1183        }
1184
1185        self.rendering_context.prepare_for_rendering();
1186
1187        let time_profiler_chan = self.global.borrow().time_profiler_chan.clone();
1188        time_profile!(
1189            ProfilerCategory::Compositing,
1190            None,
1191            time_profiler_chan,
1192            || {
1193                trace!("Compositing");
1194
1195                // Paint the scene.
1196                // TODO(gw): Take notice of any errors the renderer returns!
1197                self.clear_background();
1198                if let Some(webrender) = self.webrender.as_mut() {
1199                    let size = self.rendering_context.size2d().to_i32();
1200                    webrender.render(size, 0 /* buffer_age */).ok();
1201                }
1202            },
1203        );
1204
1205        self.send_pending_paint_metrics_messages_after_composite();
1206        self.screenshot_taker.maybe_take_screenshots(self);
1207    }
1208
1209    /// Send all pending paint metrics messages after a composite operation, which may advance
1210    /// the epoch for pipelines in the WebRender scene.
1211    ///
1212    /// If there are pending paint metrics, we check if any of the painted epochs is one
1213    /// of the ones that the paint metrics recorder is expecting. In that case, we get the
1214    /// current time, inform the constellation about it and remove the pending metric from
1215    /// the list.
1216    fn send_pending_paint_metrics_messages_after_composite(&mut self) {
1217        let paint_time = CrossProcessInstant::now();
1218        let document_id = self.webrender_document();
1219        for webview_renderer in self.webview_renderers.iter_mut() {
1220            for (pipeline_id, pipeline) in webview_renderer.pipelines.iter_mut() {
1221                let Some(current_epoch) = self
1222                    .webrender
1223                    .as_ref()
1224                    .and_then(|wr| wr.current_epoch(document_id, pipeline_id.into()))
1225                else {
1226                    continue;
1227                };
1228
1229                match pipeline.first_paint_metric {
1230                    // We need to check whether the current epoch is later, because
1231                    // CrossProcessCompositorMessage::SendInitialTransaction sends an
1232                    // empty display list to WebRender which can happen before we receive
1233                    // the first "real" display list.
1234                    PaintMetricState::Seen(epoch, first_reflow) if epoch <= current_epoch => {
1235                        assert!(epoch <= current_epoch);
1236                        #[cfg(feature = "tracing")]
1237                        tracing::info!(
1238                            name: "FirstPaint",
1239                            servo_profiling = true,
1240                            epoch = ?epoch,
1241                            paint_time = ?paint_time,
1242                            pipeline_id = ?pipeline_id,
1243                        );
1244                        if let Err(error) = self.global.borrow().constellation_sender.send(
1245                            EmbedderToConstellationMessage::PaintMetric(
1246                                *pipeline_id,
1247                                PaintMetricEvent::FirstPaint(paint_time, first_reflow),
1248                            ),
1249                        ) {
1250                            warn!(
1251                                "Sending paint metric event to constellation failed ({error:?})."
1252                            );
1253                        }
1254                        pipeline.first_paint_metric = PaintMetricState::Sent;
1255                    },
1256                    _ => {},
1257                }
1258
1259                match pipeline.first_contentful_paint_metric {
1260                    PaintMetricState::Seen(epoch, first_reflow) if epoch <= current_epoch => {
1261                        #[cfg(feature = "tracing")]
1262                        tracing::info!(
1263                            name: "FirstContentfulPaint",
1264                            servo_profiling = true,
1265                            epoch = ?epoch,
1266                            paint_time = ?paint_time,
1267                            pipeline_id = ?pipeline_id,
1268                        );
1269                        if let Err(error) = self.global.borrow().constellation_sender.send(
1270                            EmbedderToConstellationMessage::PaintMetric(
1271                                *pipeline_id,
1272                                PaintMetricEvent::FirstContentfulPaint(paint_time, first_reflow),
1273                            ),
1274                        ) {
1275                            warn!(
1276                                "Sending paint metric event to constellation failed ({error:?})."
1277                            );
1278                        }
1279                        pipeline.first_contentful_paint_metric = PaintMetricState::Sent;
1280                    },
1281                    _ => {},
1282                }
1283            }
1284        }
1285    }
1286
1287    fn clear_background(&self) {
1288        let gl = &self.global.borrow().webrender_gl;
1289        self.assert_gl_framebuffer_complete();
1290
1291        // Always clear the entire RenderingContext, regardless of how many WebViews there are
1292        // or where they are positioned. This is so WebView actually clears even before the
1293        // first WebView is ready.
1294        let color = servo_config::pref!(shell_background_color_rgba);
1295        gl.clear_color(
1296            color[0] as f32,
1297            color[1] as f32,
1298            color[2] as f32,
1299            color[3] as f32,
1300        );
1301        gl.clear(gleam::gl::COLOR_BUFFER_BIT);
1302    }
1303
1304    #[track_caller]
1305    fn assert_no_gl_error(&self) {
1306        debug_assert_eq!(
1307            self.global.borrow().webrender_gl.get_error(),
1308            gleam::gl::NO_ERROR
1309        );
1310    }
1311
1312    #[track_caller]
1313    fn assert_gl_framebuffer_complete(&self) {
1314        debug_assert_eq!(
1315            (
1316                self.global.borrow().webrender_gl.get_error(),
1317                self.global
1318                    .borrow()
1319                    .webrender_gl
1320                    .check_frame_buffer_status(gleam::gl::FRAMEBUFFER)
1321            ),
1322            (gleam::gl::NO_ERROR, gleam::gl::FRAMEBUFFER_COMPLETE)
1323        );
1324    }
1325
1326    /// Get the message receiver for this [`IOCompositor`].
1327    pub fn receiver(&self) -> Ref<'_, RoutedReceiver<CompositorMsg>> {
1328        Ref::map(self.global.borrow(), |global| &global.compositor_receiver)
1329    }
1330
1331    #[servo_tracing::instrument(skip_all)]
1332    pub fn handle_messages(&mut self, mut messages: Vec<CompositorMsg>) {
1333        // Pull out the `NewWebRenderFrameReady` messages from the list of messages and handle them
1334        // at the end of this function. This prevents overdraw when more than a single message of
1335        // this type of received. In addition, if any of these frames need a repaint, that reflected
1336        // when calling `handle_new_webrender_frame_ready`.
1337        let mut repaint_needed = false;
1338        let mut saw_webrender_frame_ready = false;
1339
1340        messages.retain(|message| match message {
1341            CompositorMsg::NewWebRenderFrameReady(_, need_repaint) => {
1342                self.pending_frames.set(self.pending_frames.get() - 1);
1343                repaint_needed |= need_repaint;
1344                saw_webrender_frame_ready = true;
1345
1346                false
1347            },
1348            _ => true,
1349        });
1350
1351        for message in messages {
1352            self.handle_browser_message(message);
1353            if self.global.borrow().shutdown_state() == ShutdownState::FinishedShuttingDown {
1354                return;
1355            }
1356        }
1357
1358        if saw_webrender_frame_ready {
1359            self.handle_new_webrender_frame_ready(repaint_needed);
1360        }
1361    }
1362
1363    #[servo_tracing::instrument(skip_all)]
1364    pub fn perform_updates(&mut self) -> bool {
1365        if self.global.borrow().shutdown_state() == ShutdownState::FinishedShuttingDown {
1366            return false;
1367        }
1368
1369        #[cfg(feature = "webxr")]
1370        // Run the WebXR main thread
1371        self.global.borrow_mut().webxr_main_thread.run_one_frame();
1372
1373        // The WebXR thread may make a different context current
1374        if let Err(err) = self.rendering_context.make_current() {
1375            warn!("Failed to make the rendering context current: {:?}", err);
1376        }
1377
1378        let mut need_zoom = false;
1379        let scroll_offset_updates: Vec<_> = self
1380            .webview_renderers
1381            .iter_mut()
1382            .filter_map(|webview_renderer| {
1383                let (zoom, scroll_result) =
1384                    webview_renderer.process_pending_scroll_and_pinch_zoom_events();
1385                need_zoom = need_zoom || (zoom == PinchZoomResult::DidPinchZoom);
1386                scroll_result
1387            })
1388            .collect();
1389
1390        if need_zoom || !scroll_offset_updates.is_empty() {
1391            let mut transaction = Transaction::new();
1392            if need_zoom {
1393                self.send_root_pipeline_display_list_in_transaction(&mut transaction);
1394            }
1395            for update in scroll_offset_updates {
1396                transaction.set_scroll_offsets(
1397                    update.external_scroll_id,
1398                    vec![SampledScrollOffset {
1399                        offset: update.offset,
1400                        generation: 0,
1401                    }],
1402                );
1403            }
1404
1405            self.generate_frame(&mut transaction, RenderReasons::APZ);
1406            self.global.borrow_mut().send_transaction(transaction);
1407        }
1408
1409        self.global.borrow().shutdown_state() != ShutdownState::FinishedShuttingDown
1410    }
1411
1412    pub fn toggle_webrender_debug(&mut self, option: WebRenderDebugOption) {
1413        let Some(webrender) = self.webrender.as_mut() else {
1414            return;
1415        };
1416        let mut flags = webrender.get_debug_flags();
1417        let flag = match option {
1418            WebRenderDebugOption::Profiler => {
1419                webrender::DebugFlags::PROFILER_DBG |
1420                    webrender::DebugFlags::GPU_TIME_QUERIES |
1421                    webrender::DebugFlags::GPU_SAMPLE_QUERIES
1422            },
1423            WebRenderDebugOption::TextureCacheDebug => webrender::DebugFlags::TEXTURE_CACHE_DBG,
1424            WebRenderDebugOption::RenderTargetDebug => webrender::DebugFlags::RENDER_TARGET_DBG,
1425        };
1426        flags.toggle(flag);
1427        webrender.set_debug_flags(flags);
1428
1429        let mut txn = Transaction::new();
1430        self.generate_frame(&mut txn, RenderReasons::TESTING);
1431        self.global.borrow_mut().send_transaction(txn);
1432    }
1433
1434    pub fn capture_webrender(&mut self) {
1435        let capture_id = SystemTime::now()
1436            .duration_since(UNIX_EPOCH)
1437            .unwrap_or_default()
1438            .as_secs()
1439            .to_string();
1440        let available_path = [env::current_dir(), Ok(env::temp_dir())]
1441            .iter()
1442            .filter_map(|val| {
1443                val.as_ref()
1444                    .map(|dir| dir.join("webrender-captures").join(&capture_id))
1445                    .ok()
1446            })
1447            .find(|val| create_dir_all(val).is_ok());
1448
1449        let Some(capture_path) = available_path else {
1450            eprintln!("Couldn't create a path for WebRender captures.");
1451            return;
1452        };
1453
1454        println!("Saving WebRender capture to {capture_path:?}");
1455        self.global
1456            .borrow()
1457            .webrender_api
1458            .save_capture(capture_path.clone(), CaptureBits::all());
1459    }
1460
1461    fn add_font_instance(
1462        &mut self,
1463        instance_key: FontInstanceKey,
1464        font_key: FontKey,
1465        size: f32,
1466        flags: FontInstanceFlags,
1467        variations: Vec<FontVariation>,
1468    ) {
1469        let variations = if pref!(layout_variable_fonts_enabled) {
1470            variations
1471        } else {
1472            vec![]
1473        };
1474
1475        let mut transaction = Transaction::new();
1476
1477        let font_instance_options = FontInstanceOptions {
1478            flags,
1479            ..Default::default()
1480        };
1481        transaction.add_font_instance(
1482            instance_key,
1483            font_key,
1484            size,
1485            Some(font_instance_options),
1486            None,
1487            variations,
1488        );
1489
1490        self.global.borrow_mut().send_transaction(transaction);
1491    }
1492
1493    fn add_font(&mut self, font_key: FontKey, index: u32, data: Arc<IpcSharedMemory>) {
1494        let mut transaction = Transaction::new();
1495        transaction.add_raw_font(font_key, (**data).into(), index);
1496        self.global.borrow_mut().send_transaction(transaction);
1497    }
1498
1499    pub fn notify_input_event(&mut self, webview_id: WebViewId, event: InputEventAndId) {
1500        if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
1501            webview_renderer.notify_input_event(event);
1502        }
1503    }
1504
1505    pub fn notify_scroll_event(
1506        &mut self,
1507        webview_id: WebViewId,
1508        scroll_location: ScrollLocation,
1509        cursor: DeviceIntPoint,
1510    ) {
1511        if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
1512            webview_renderer.notify_scroll_event(scroll_location, cursor);
1513        }
1514    }
1515
1516    pub fn on_vsync(&mut self, webview_id: WebViewId) {
1517        if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
1518            webview_renderer.on_vsync();
1519        }
1520    }
1521
1522    pub fn set_pinch_zoom(&mut self, webview_id: WebViewId, magnification: f32) {
1523        if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
1524            webview_renderer.set_pinch_zoom(magnification);
1525        }
1526    }
1527
1528    pub fn device_pixels_per_page_pixel(
1529        &self,
1530        webview_id: WebViewId,
1531    ) -> Scale<f32, CSSPixel, DevicePixel> {
1532        self.webview_renderers
1533            .get(webview_id)
1534            .map(WebViewRenderer::device_pixels_per_page_pixel)
1535            .unwrap_or_default()
1536    }
1537
1538    fn webrender_document(&self) -> DocumentId {
1539        self.global.borrow().webrender_document
1540    }
1541
1542    fn shutdown_state(&self) -> ShutdownState {
1543        self.global.borrow().shutdown_state()
1544    }
1545
1546    fn refresh_cursor(&self) {
1547        let global = self.global.borrow();
1548        let Some(last_mouse_move_position) = global.last_mouse_move_position else {
1549            return;
1550        };
1551
1552        let Some(hit_test_result) = global
1553            .hit_test_at_point(last_mouse_move_position)
1554            .first()
1555            .cloned()
1556        else {
1557            return;
1558        };
1559
1560        if let Err(error) =
1561            global
1562                .constellation_sender
1563                .send(EmbedderToConstellationMessage::RefreshCursor(
1564                    hit_test_result.pipeline_id,
1565                ))
1566        {
1567            warn!("Sending event to constellation failed ({:?}).", error);
1568        }
1569    }
1570
1571    fn handle_new_webrender_frame_ready(&mut self, repaint_needed: bool) {
1572        if repaint_needed {
1573            self.refresh_cursor();
1574        }
1575        if repaint_needed || self.animation_callbacks_running() {
1576            self.set_needs_repaint(RepaintReason::NewWebRenderFrame);
1577        }
1578
1579        // If we received a new frame and a repaint isn't necessary, it may be that this
1580        // is the last frame that was pending. In that case, trigger a manual repaint so
1581        // that the screenshot can be taken at the end of the repaint procedure.
1582        if !repaint_needed {
1583            self.screenshot_taker
1584                .maybe_trigger_paint_for_screenshot(self);
1585        }
1586    }
1587
1588    /// Whether or not the renderer is waiting on a frame, either because it has been sent
1589    /// to WebRender and is not ready yet or because the [`FrameDelayer`] is delaying a frame
1590    /// waiting for asynchronous (canvas) image updates to complete.
1591    pub(crate) fn has_pending_frames(&self) -> bool {
1592        self.pending_frames.get() != 0 || self.global.borrow().frame_delayer.pending_frame
1593    }
1594
1595    pub fn request_screenshot(
1596        &self,
1597        webview_id: WebViewId,
1598        rect: Option<DeviceRect>,
1599        callback: Box<dyn FnOnce(Result<RgbaImage, ScreenshotCaptureError>) + 'static>,
1600    ) {
1601        self.screenshot_taker
1602            .request_screenshot(webview_id, rect, callback);
1603        let _ = self.global.borrow().constellation_sender.send(
1604            EmbedderToConstellationMessage::RequestScreenshotReadiness(webview_id),
1605        );
1606    }
1607}
1608
1609/// A struct that is reponsible for delaying frame requests until all new canvas images
1610/// for a particular "update the rendering" call in the `ScriptThread` have been
1611/// sent to WebRender.
1612///
1613/// These images may be updated in WebRender asynchronously in the canvas task. A frame
1614/// is then requested if:
1615///
1616///  - The renderer has received a GenerateFrame message from a `ScriptThread`.
1617///  - All pending image updates have finished and have been noted in the [`FrameDelayer`].
1618#[derive(Default)]
1619struct FrameDelayer {
1620    /// The latest [`Epoch`] of canvas images that have been sent to WebRender. Note
1621    /// that this only records the `Epoch`s for canvases and only ones that are involved
1622    /// in "update the rendering".
1623    image_epochs: FxHashMap<ImageKey, Epoch>,
1624    /// A map of all pending canvas images
1625    pending_canvas_images: FxHashMap<ImageKey, Epoch>,
1626    /// Whether or not we have a pending frame.
1627    pending_frame: bool,
1628    /// A list of pipelines that should be notified when we are no longer waiting for
1629    /// canvas images.
1630    waiting_pipelines: FxHashSet<PipelineId>,
1631}
1632
1633impl FrameDelayer {
1634    fn delete_image(&mut self, image_key: ImageKey) {
1635        self.image_epochs.remove(&image_key);
1636        self.pending_canvas_images.remove(&image_key);
1637    }
1638
1639    fn update_image(&mut self, image_key: ImageKey, epoch: Epoch) {
1640        self.image_epochs.insert(image_key, epoch);
1641        let Entry::Occupied(entry) = self.pending_canvas_images.entry(image_key) else {
1642            return;
1643        };
1644        if *entry.get() <= epoch {
1645            entry.remove();
1646        }
1647    }
1648
1649    fn add_delay(
1650        &mut self,
1651        pipeline_id: PipelineId,
1652        canvas_epoch: Epoch,
1653        image_keys: Vec<ImageKey>,
1654    ) {
1655        for image_key in image_keys.into_iter() {
1656            // If we've already seen the necessary epoch for this image, do not
1657            // start waiting for it.
1658            if self
1659                .image_epochs
1660                .get(&image_key)
1661                .is_some_and(|epoch_seen| *epoch_seen >= canvas_epoch)
1662            {
1663                continue;
1664            }
1665            self.pending_canvas_images.insert(image_key, canvas_epoch);
1666        }
1667        self.waiting_pipelines.insert(pipeline_id);
1668    }
1669
1670    fn needs_new_frame(&self) -> bool {
1671        self.pending_frame && self.pending_canvas_images.is_empty()
1672    }
1673
1674    fn set_pending_frame(&mut self, value: bool) {
1675        self.pending_frame = value;
1676    }
1677
1678    fn take_waiting_pipelines(&mut self) -> Vec<PipelineId> {
1679        self.waiting_pipelines.drain().collect()
1680    }
1681}