Skip to main content

paint/
painter.rs

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