1use std::cell::{Cell, Ref, RefCell};
6use std::collections::hash_map::Entry;
7use std::collections::{HashMap, HashSet};
8use std::env;
9use std::fs::create_dir_all;
10use std::iter::once;
11use std::rc::Rc;
12use std::sync::Arc;
13use std::time::{SystemTime, UNIX_EPOCH};
14
15use base::Epoch;
16use base::cross_process_instant::CrossProcessInstant;
17use base::generic_channel::{GenericSender, RoutedReceiver};
18use base::id::{PipelineId, WebViewId};
19use bitflags::bitflags;
20use compositing_traits::display_list::{CompositorDisplayListInfo, ScrollTree, ScrollType};
21use compositing_traits::rendering_context::RenderingContext;
22use compositing_traits::{
23 CompositionPipeline, CompositorMsg, ImageUpdate, PipelineExitSource, SendableFrameTree,
24 WebViewTrait,
25};
26use constellation_traits::{EmbedderToConstellationMessage, PaintMetricEvent};
27use crossbeam_channel::Sender;
28use dpi::PhysicalSize;
29use embedder_traits::{CompositorHitTestResult, InputEvent, ShutdownState, ViewportDetails};
30use euclid::{Point2D, Rect, Scale, Size2D, Transform3D};
31use ipc_channel::ipc::{self, IpcSharedMemory};
32use log::{debug, info, trace, warn};
33use pixels::{CorsStatus, ImageFrame, ImageMetadata, PixelFormat, RasterImage};
34use profile_traits::mem::{
35 ProcessReports, ProfilerRegistration, Report, ReportKind, perform_memory_report,
36};
37use profile_traits::time::{self as profile_time, ProfilerCategory};
38use profile_traits::{path, time_profile};
39use servo_config::{opts, pref};
40use servo_geometry::DeviceIndependentPixel;
41use style_traits::CSSPixel;
42use webrender::{CaptureBits, RenderApi, Transaction};
43use webrender_api::units::{
44 DeviceIntPoint, DeviceIntRect, DevicePixel, DevicePoint, DeviceRect, LayoutPoint, LayoutRect,
45 LayoutSize, WorldPoint,
46};
47use webrender_api::{
48 self, BuiltDisplayList, DirtyRect, DisplayListPayload, DocumentId, Epoch as WebRenderEpoch,
49 ExternalScrollId, FontInstanceFlags, FontInstanceKey, FontInstanceOptions, FontKey,
50 FontVariation, HitTestFlags, ImageKey, PipelineId as WebRenderPipelineId, PropertyBinding,
51 ReferenceFrameKind, RenderReasons, SampledScrollOffset, ScrollLocation, SpaceAndClipInfo,
52 SpatialId, SpatialTreeItemKey, TransformStyle,
53};
54
55use crate::InitialCompositorState;
56use crate::refresh_driver::RefreshDriver;
57use crate::webview_manager::WebViewManager;
58use crate::webview_renderer::{PinchZoomResult, UnknownWebView, WebViewRenderer};
59
60#[derive(Debug, PartialEq)]
61pub enum UnableToComposite {
62 NotReadyToPaintImage(NotReadyToPaint),
63}
64
65#[derive(Debug, PartialEq)]
66pub enum NotReadyToPaint {
67 JustNotifiedConstellation,
68 WaitingOnConstellation,
69}
70
71#[derive(Clone, Copy, Debug, PartialEq)]
74enum ReadyState {
75 Unknown,
76 WaitingForConstellationReply,
77 ReadyToSaveImage,
78}
79
80#[derive(Clone)]
82pub enum WebRenderDebugOption {
83 Profiler,
84 TextureCacheDebug,
85 RenderTargetDebug,
86}
87
88pub struct ServoRenderer {
90 refresh_driver: RefreshDriver,
92
93 shutdown_state: Rc<Cell<ShutdownState>>,
96
97 compositor_receiver: RoutedReceiver<CompositorMsg>,
99
100 pub(crate) constellation_sender: Sender<EmbedderToConstellationMessage>,
102
103 time_profiler_chan: profile_time::ProfilerChan,
105
106 pub(crate) webrender_api: RenderApi,
108
109 pub(crate) webrender_document: DocumentId,
111
112 webrender_gl: Rc<dyn gleam::gl::Gl>,
114
115 #[cfg(feature = "webxr")]
116 webxr_main_thread: webxr::MainThreadRegistry,
118
119 pub(crate) convert_mouse_to_touch: bool,
121
122 pub(crate) last_mouse_move_position: Option<DevicePoint>,
125
126 frame_delayer: FrameDelayer,
130}
131
132pub struct IOCompositor {
134 global: Rc<RefCell<ServoRenderer>>,
136
137 webview_renderers: WebViewManager<WebViewRenderer>,
139
140 needs_repaint: Cell<RepaintReason>,
142
143 ready_to_save_state: ReadyState,
146
147 webrender: Option<webrender::Renderer>,
149
150 rendering_context: Rc<dyn RenderingContext>,
152
153 pending_frames: Cell<usize>,
155
156 _mem_profiler_registration: ProfilerRegistration,
159}
160
161#[derive(Clone, Copy, Default, PartialEq)]
163pub(crate) struct RepaintReason(u8);
164
165bitflags! {
166 impl RepaintReason: u8 {
167 const ReadyForScreenshot = 1 << 0;
169 const ChangedAnimationState = 1 << 1;
171 const NewWebRenderFrame = 1 << 2;
173 const Resize = 1 << 3;
175 }
176}
177
178#[derive(PartialEq)]
183pub(crate) enum PaintMetricState {
184 Waiting,
186 Seen(WebRenderEpoch, bool ),
189 Sent,
191}
192
193pub(crate) struct PipelineDetails {
194 pub pipeline: Option<CompositionPipeline>,
196
197 pub parent_pipeline_id: Option<PipelineId>,
199
200 pub animations_running: bool,
202
203 pub animation_callbacks_running: bool,
205
206 pub throttled: bool,
208
209 pub scroll_tree: ScrollTree,
212
213 pub first_paint_metric: PaintMetricState,
215
216 pub first_contentful_paint_metric: PaintMetricState,
218
219 pub viewport_scale: Option<Scale<f32, CSSPixel, DevicePixel>>,
223
224 pub exited: PipelineExitSource,
227}
228
229impl PipelineDetails {
230 pub(crate) fn animation_callbacks_running(&self) -> bool {
231 self.animation_callbacks_running
232 }
233
234 pub(crate) fn animating(&self) -> bool {
235 !self.throttled && (self.animation_callbacks_running || self.animations_running)
236 }
237}
238
239impl PipelineDetails {
240 pub(crate) fn new() -> PipelineDetails {
241 PipelineDetails {
242 pipeline: None,
243 parent_pipeline_id: None,
244 viewport_scale: None,
245 animations_running: false,
246 animation_callbacks_running: false,
247 throttled: false,
248 scroll_tree: ScrollTree::default(),
249 first_paint_metric: PaintMetricState::Waiting,
250 first_contentful_paint_metric: PaintMetricState::Waiting,
251 exited: PipelineExitSource::empty(),
252 }
253 }
254
255 fn install_new_scroll_tree(&mut self, new_scroll_tree: ScrollTree) {
256 let old_scroll_offsets = self.scroll_tree.scroll_offsets();
257 self.scroll_tree = new_scroll_tree;
258 self.scroll_tree.set_all_scroll_offsets(&old_scroll_offsets);
259 }
260}
261
262impl ServoRenderer {
263 pub fn shutdown_state(&self) -> ShutdownState {
264 self.shutdown_state.get()
265 }
266
267 pub(crate) fn hit_test_at_point(&self, point: DevicePoint) -> Vec<CompositorHitTestResult> {
268 self.hit_test_at_point_with_flags(point, HitTestFlags::empty())
269 }
270
271 pub(crate) fn hit_test_at_point_with_flags(
273 &self,
274 point: DevicePoint,
275 flags: HitTestFlags,
276 ) -> Vec<CompositorHitTestResult> {
277 let world_point = WorldPoint::from_untyped(point.to_untyped());
279 let results = self.webrender_api.hit_test(
280 self.webrender_document,
281 None, world_point,
283 flags,
284 );
285
286 results
287 .items
288 .iter()
289 .map(|item| {
290 let pipeline_id = item.pipeline.into();
291 let external_scroll_id = ExternalScrollId(item.tag.0, item.pipeline);
292 CompositorHitTestResult {
293 pipeline_id,
294 point_in_viewport: Point2D::from_untyped(item.point_in_viewport.to_untyped()),
295 external_scroll_id,
296 }
297 })
298 .collect()
299 }
300
301 pub(crate) fn send_transaction(&mut self, transaction: Transaction) {
302 self.webrender_api
303 .send_transaction(self.webrender_document, transaction);
304 }
305}
306
307impl IOCompositor {
308 pub fn new(state: InitialCompositorState, convert_mouse_to_touch: bool) -> Self {
309 let registration = state.mem_profiler_chan.prepare_memory_reporting(
310 "compositor".into(),
311 state.sender.clone(),
312 CompositorMsg::CollectMemoryReport,
313 );
314 let compositor = IOCompositor {
315 global: Rc::new(RefCell::new(ServoRenderer {
316 refresh_driver: RefreshDriver::new(
317 state.constellation_chan.clone(),
318 state.event_loop_waker,
319 ),
320 shutdown_state: state.shutdown_state,
321 compositor_receiver: state.receiver,
322 constellation_sender: state.constellation_chan,
323 time_profiler_chan: state.time_profiler_chan,
324 webrender_api: state.webrender_api,
325 webrender_document: state.webrender_document,
326 webrender_gl: state.webrender_gl,
327 #[cfg(feature = "webxr")]
328 webxr_main_thread: state.webxr_main_thread,
329 convert_mouse_to_touch,
330 last_mouse_move_position: None,
331 frame_delayer: Default::default(),
332 })),
333 webview_renderers: WebViewManager::default(),
334 needs_repaint: Cell::default(),
335 ready_to_save_state: ReadyState::Unknown,
336 webrender: Some(state.webrender),
337 rendering_context: state.rendering_context,
338 pending_frames: Cell::new(0),
339 _mem_profiler_registration: registration,
340 };
341
342 {
343 let gl = &compositor.global.borrow().webrender_gl;
344 info!("Running on {}", gl.get_string(gleam::gl::RENDERER));
345 info!("OpenGL Version {}", gl.get_string(gleam::gl::VERSION));
346 }
347 compositor.assert_gl_framebuffer_complete();
348 compositor
349 }
350
351 pub fn deinit(&mut self) {
352 if let Err(err) = self.rendering_context.make_current() {
353 warn!("Failed to make the rendering context current: {:?}", err);
354 }
355 if let Some(webrender) = self.webrender.take() {
356 webrender.deinit();
357 }
358 }
359
360 pub fn rendering_context_size(&self) -> Size2D<u32, DevicePixel> {
361 self.rendering_context.size2d()
362 }
363
364 pub fn webxr_running(&self) -> bool {
365 #[cfg(feature = "webxr")]
366 {
367 self.global.borrow().webxr_main_thread.running()
368 }
369 #[cfg(not(feature = "webxr"))]
370 {
371 false
372 }
373 }
374
375 fn set_needs_repaint(&self, reason: RepaintReason) {
376 let mut needs_repaint = self.needs_repaint.get();
377 needs_repaint.insert(reason);
378 self.needs_repaint.set(needs_repaint);
379 }
380
381 pub fn needs_repaint(&self) -> bool {
382 let repaint_reason = self.needs_repaint.get();
383 if repaint_reason.is_empty() {
384 return false;
385 }
386
387 !self
388 .global
389 .borrow()
390 .refresh_driver
391 .wait_to_paint(repaint_reason)
392 }
393
394 pub fn finish_shutting_down(&mut self) {
395 while self
398 .global
399 .borrow_mut()
400 .compositor_receiver
401 .try_recv()
402 .is_ok()
403 {}
404
405 if let Ok((sender, receiver)) = ipc::channel() {
407 self.global
408 .borrow()
409 .time_profiler_chan
410 .send(profile_time::ProfilerMsg::Exit(sender));
411 let _ = receiver.recv();
412 }
413 }
414
415 fn handle_browser_message(&mut self, msg: CompositorMsg) {
416 trace_msg_from_constellation!(msg, "{msg:?}");
417
418 match self.shutdown_state() {
419 ShutdownState::NotShuttingDown => {},
420 ShutdownState::ShuttingDown => {
421 self.handle_browser_message_while_shutting_down(msg);
422 return;
423 },
424 ShutdownState::FinishedShuttingDown => {
425 return;
427 },
428 }
429
430 match msg {
431 CompositorMsg::CollectMemoryReport(sender) => {
432 let ops =
433 wr_malloc_size_of::MallocSizeOfOps::new(servo_allocator::usable_size, None);
434 let report = self.global.borrow().webrender_api.report_memory(ops);
435 let mut reports = vec![
436 Report {
437 path: path!["webrender", "fonts"],
438 kind: ReportKind::ExplicitJemallocHeapSize,
439 size: report.fonts,
440 },
441 Report {
442 path: path!["webrender", "images"],
443 kind: ReportKind::ExplicitJemallocHeapSize,
444 size: report.images,
445 },
446 Report {
447 path: path!["webrender", "display-list"],
448 kind: ReportKind::ExplicitJemallocHeapSize,
449 size: report.display_list,
450 },
451 ];
452
453 perform_memory_report(|ops| {
454 reports.push(Report {
455 path: path!["compositor", "scroll-tree"],
456 kind: ReportKind::ExplicitJemallocHeapSize,
457 size: self.webview_renderers.scroll_trees_memory_usage(ops),
458 });
459 });
460
461 sender.send(ProcessReports::new(reports));
462 },
463
464 CompositorMsg::ChangeRunningAnimationsState(
465 webview_id,
466 pipeline_id,
467 animation_state,
468 ) => {
469 let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
470 return;
471 };
472
473 if webview_renderer
474 .change_pipeline_running_animations_state(pipeline_id, animation_state)
475 {
476 self.global
477 .borrow()
478 .refresh_driver
479 .notify_animation_state_changed(webview_renderer);
480 }
481 },
482
483 CompositorMsg::CreateOrUpdateWebView(frame_tree) => {
484 self.set_frame_tree_for_webview(&frame_tree);
485 },
486
487 CompositorMsg::RemoveWebView(webview_id) => {
488 self.remove_webview(webview_id);
489 },
490
491 CompositorMsg::TouchEventProcessed(webview_id, result) => {
492 let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
493 warn!("Handling input event for unknown webview: {webview_id}");
494 return;
495 };
496 webview_renderer.on_touch_event_processed(result);
497 },
498 CompositorMsg::IsReadyToSaveImageReply(is_ready) => {
499 assert_eq!(
500 self.ready_to_save_state,
501 ReadyState::WaitingForConstellationReply
502 );
503 if is_ready && self.pending_frames.get() == 0 {
504 self.ready_to_save_state = ReadyState::ReadyToSaveImage;
505 } else {
506 self.ready_to_save_state = ReadyState::Unknown;
507 }
508 self.set_needs_repaint(RepaintReason::ReadyForScreenshot);
509 },
510
511 CompositorMsg::SetThrottled(webview_id, pipeline_id, throttled) => {
512 let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
513 return;
514 };
515
516 if webview_renderer.set_throttled(pipeline_id, throttled) {
517 self.global
518 .borrow()
519 .refresh_driver
520 .notify_animation_state_changed(webview_renderer);
521 }
522 },
523
524 CompositorMsg::PipelineExited(webview_id, pipeline_id, pipeline_exit_source) => {
525 debug!(
526 "Compositor got pipeline exited: {:?} {:?}",
527 webview_id, pipeline_id
528 );
529 if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
530 webview_renderer.pipeline_exited(pipeline_id, pipeline_exit_source);
531 }
532 },
533
534 CompositorMsg::NewWebRenderFrameReady(_document_id, recomposite_needed) => {
535 self.handle_new_webrender_frame_ready(recomposite_needed);
536 },
537
538 CompositorMsg::LoadComplete(_) => {
539 if opts::get().wait_for_stable_image {
540 self.set_needs_repaint(RepaintReason::ReadyForScreenshot);
541 }
542 },
543
544 CompositorMsg::SendInitialTransaction(pipeline) => {
545 let mut txn = Transaction::new();
546 txn.set_display_list(WebRenderEpoch(0), (pipeline, Default::default()));
547 self.generate_frame(&mut txn, RenderReasons::SCENE);
548 self.global.borrow_mut().send_transaction(txn);
549 },
550
551 CompositorMsg::SendScrollNode(webview_id, pipeline_id, offset, external_scroll_id) => {
552 let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
553 return;
554 };
555
556 let pipeline_id = pipeline_id.into();
557 let Some(pipeline_details) = webview_renderer.pipelines.get_mut(&pipeline_id)
558 else {
559 return;
560 };
561
562 let Some(offset) = pipeline_details
563 .scroll_tree
564 .set_scroll_offset_for_node_with_external_scroll_id(
565 external_scroll_id,
566 offset,
567 ScrollType::Script,
568 )
569 else {
570 warn!("Could not scroll node with id: {external_scroll_id:?}");
573 return;
574 };
575
576 let mut txn = Transaction::new();
577 txn.set_scroll_offsets(
578 external_scroll_id,
579 vec![SampledScrollOffset {
580 offset,
581 generation: 0,
582 }],
583 );
584 self.generate_frame(&mut txn, RenderReasons::APZ);
585 self.global.borrow_mut().send_transaction(txn);
586 },
587
588 CompositorMsg::SendDisplayList {
589 webview_id,
590 display_list_descriptor,
591 display_list_receiver,
592 } => {
593 let display_list_info = match display_list_receiver.recv() {
595 Ok(display_list_info) => display_list_info,
596 Err(error) => {
597 return warn!("Could not receive display list info: {error}");
598 },
599 };
600 let display_list_info: CompositorDisplayListInfo =
601 match bincode::deserialize(&display_list_info) {
602 Ok(display_list_info) => display_list_info,
603 Err(error) => {
604 return warn!("Could not deserialize display list info: {error}");
605 },
606 };
607 let items_data = match display_list_receiver.recv() {
608 Ok(display_list_data) => display_list_data,
609 Err(error) => {
610 return warn!(
611 "Could not receive WebRender display list items data: {error}"
612 );
613 },
614 };
615 let cache_data = match display_list_receiver.recv() {
616 Ok(display_list_data) => display_list_data,
617 Err(error) => {
618 return warn!(
619 "Could not receive WebRender display list cache data: {error}"
620 );
621 },
622 };
623 let spatial_tree = match display_list_receiver.recv() {
624 Ok(display_list_data) => display_list_data,
625 Err(error) => {
626 return warn!(
627 "Could not receive WebRender display list spatial tree: {error}."
628 );
629 },
630 };
631 let built_display_list = BuiltDisplayList::from_data(
632 DisplayListPayload {
633 items_data,
634 cache_data,
635 spatial_tree,
636 },
637 display_list_descriptor,
638 );
639
640 #[cfg(feature = "tracing")]
641 let _span = tracing::trace_span!(
642 "ScriptToCompositorMsg::BuiltDisplayList",
643 servo_profiling = true,
644 )
645 .entered();
646
647 let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
648 return warn!("Could not find WebView for incoming display list");
649 };
650
651 let old_scale = webview_renderer.device_pixels_per_page_pixel();
652
653 let pipeline_id = display_list_info.pipeline_id;
654 let details = webview_renderer.ensure_pipeline_details(pipeline_id.into());
655 details.install_new_scroll_tree(display_list_info.scroll_tree);
656 details.viewport_scale =
657 Some(display_list_info.viewport_details.hidpi_scale_factor);
658
659 let epoch = display_list_info.epoch;
660 let first_reflow = display_list_info.first_reflow;
661 if details.first_paint_metric == PaintMetricState::Waiting {
662 details.first_paint_metric = PaintMetricState::Seen(epoch, first_reflow);
663 }
664 if details.first_contentful_paint_metric == PaintMetricState::Waiting &&
665 display_list_info.is_contentful
666 {
667 details.first_contentful_paint_metric =
668 PaintMetricState::Seen(epoch, first_reflow);
669 }
670
671 let mut transaction = Transaction::new();
672 let is_root_pipeline =
673 Some(pipeline_id.into()) == webview_renderer.root_pipeline_id;
674 if is_root_pipeline && old_scale != webview_renderer.device_pixels_per_page_pixel()
675 {
676 self.send_root_pipeline_display_list_in_transaction(&mut transaction);
677 }
678
679 transaction
680 .set_display_list(display_list_info.epoch, (pipeline_id, built_display_list));
681 self.update_transaction_with_all_scroll_offsets(&mut transaction);
682 self.global.borrow_mut().send_transaction(transaction);
683 },
684
685 CompositorMsg::GenerateFrame => {
686 let mut global = self.global.borrow_mut();
687 global.frame_delayer.set_pending_frame(true);
688
689 if global.frame_delayer.needs_new_frame() {
690 let mut transaction = Transaction::new();
691 self.generate_frame(&mut transaction, RenderReasons::SCENE);
692 global.send_transaction(transaction);
693
694 let waiting_pipelines = global.frame_delayer.take_waiting_pipelines();
695 let _ = global.constellation_sender.send(
696 EmbedderToConstellationMessage::NoLongerWaitingOnAsynchronousImageUpdates(
697 waiting_pipelines,
698 ),
699 );
700 global.frame_delayer.set_pending_frame(false);
701 }
702 },
703
704 CompositorMsg::GenerateImageKey(sender) => {
705 let _ = sender.send(self.global.borrow().webrender_api.generate_image_key());
706 },
707
708 CompositorMsg::GenerateImageKeysForPipeline(pipeline_id) => {
709 let image_keys = (0..pref!(image_key_batch_size))
710 .map(|_| self.global.borrow().webrender_api.generate_image_key())
711 .collect();
712 if let Err(error) = self.global.borrow().constellation_sender.send(
713 EmbedderToConstellationMessage::SendImageKeysForPipeline(
714 pipeline_id,
715 image_keys,
716 ),
717 ) {
718 warn!("Sending Image Keys to Constellation failed with({error:?}).");
719 }
720 },
721 CompositorMsg::UpdateImages(updates) => {
722 let mut global = self.global.borrow_mut();
723 let mut txn = Transaction::new();
724 for update in updates {
725 match update {
726 ImageUpdate::AddImage(key, desc, data) => {
727 txn.add_image(key, desc, data.into(), None)
728 },
729 ImageUpdate::DeleteImage(key) => {
730 txn.delete_image(key);
731 global.frame_delayer.delete_image(key);
732 },
733 ImageUpdate::UpdateImage(key, desc, data, epoch) => {
734 if let Some(epoch) = epoch {
735 global.frame_delayer.update_image(key, epoch);
736 }
737 txn.update_image(key, desc, data.into(), &DirtyRect::All)
738 },
739 }
740 }
741
742 if global.frame_delayer.needs_new_frame() {
743 global.frame_delayer.set_pending_frame(false);
744 self.generate_frame(&mut txn, RenderReasons::SCENE);
745 let waiting_pipelines = global.frame_delayer.take_waiting_pipelines();
746 let _ = global.constellation_sender.send(
747 EmbedderToConstellationMessage::NoLongerWaitingOnAsynchronousImageUpdates(
748 waiting_pipelines,
749 ),
750 );
751 }
752
753 global.send_transaction(txn);
754 },
755
756 CompositorMsg::DelayNewFrameForCanvas(pipeline_id, canvas_epoch, image_keys) => self
757 .global
758 .borrow_mut()
759 .frame_delayer
760 .add_delay(pipeline_id, canvas_epoch, image_keys),
761
762 CompositorMsg::AddFont(font_key, data, index) => {
763 self.add_font(font_key, index, data);
764 },
765
766 CompositorMsg::AddSystemFont(font_key, native_handle) => {
767 let mut transaction = Transaction::new();
768 transaction.add_native_font(font_key, native_handle);
769 self.global.borrow_mut().send_transaction(transaction);
770 },
771
772 CompositorMsg::AddFontInstance(
773 font_instance_key,
774 font_key,
775 size,
776 flags,
777 variations,
778 ) => {
779 self.add_font_instance(font_instance_key, font_key, size, flags, variations);
780 },
781
782 CompositorMsg::RemoveFonts(keys, instance_keys) => {
783 let mut transaction = Transaction::new();
784
785 for instance in instance_keys.into_iter() {
786 transaction.delete_font_instance(instance);
787 }
788 for key in keys.into_iter() {
789 transaction.delete_font(key);
790 }
791
792 self.global.borrow_mut().send_transaction(transaction);
793 },
794
795 CompositorMsg::GenerateFontKeys(
796 number_of_font_keys,
797 number_of_font_instance_keys,
798 result_sender,
799 ) => {
800 self.handle_generate_font_keys(
801 number_of_font_keys,
802 number_of_font_instance_keys,
803 result_sender,
804 );
805 },
806 CompositorMsg::Viewport(webview_id, viewport_description) => {
807 if let Some(webview) = self.webview_renderers.get_mut(webview_id) {
808 webview.set_viewport_description(viewport_description);
809 }
810 },
811 }
812 }
813
814 fn handle_browser_message_while_shutting_down(&mut self, msg: CompositorMsg) {
824 match msg {
825 CompositorMsg::PipelineExited(webview_id, pipeline_id, pipeline_exit_source) => {
826 debug!(
827 "Compositor got pipeline exited: {:?} {:?}",
828 webview_id, pipeline_id
829 );
830 if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
831 webview_renderer.pipeline_exited(pipeline_id, pipeline_exit_source);
832 }
833 },
834 CompositorMsg::GenerateImageKey(sender) => {
835 let _ = sender.send(self.global.borrow().webrender_api.generate_image_key());
836 },
837 CompositorMsg::GenerateFontKeys(
838 number_of_font_keys,
839 number_of_font_instance_keys,
840 result_sender,
841 ) => {
842 self.handle_generate_font_keys(
843 number_of_font_keys,
844 number_of_font_instance_keys,
845 result_sender,
846 );
847 },
848 CompositorMsg::NewWebRenderFrameReady(..) => {
849 self.pending_frames.set(self.pending_frames.get() - 1);
851 },
852 _ => {
853 debug!("Ignoring message ({:?} while shutting down", msg);
854 },
855 }
856 }
857
858 fn handle_generate_font_keys(
860 &self,
861 number_of_font_keys: usize,
862 number_of_font_instance_keys: usize,
863 result_sender: GenericSender<(Vec<FontKey>, Vec<FontInstanceKey>)>,
864 ) {
865 let font_keys = (0..number_of_font_keys)
866 .map(|_| self.global.borrow().webrender_api.generate_font_key())
867 .collect();
868 let font_instance_keys = (0..number_of_font_instance_keys)
869 .map(|_| {
870 self.global
871 .borrow()
872 .webrender_api
873 .generate_font_instance_key()
874 })
875 .collect();
876 let _ = result_sender.send((font_keys, font_instance_keys));
877 }
878
879 pub(crate) fn generate_frame(&self, transaction: &mut Transaction, reason: RenderReasons) {
881 self.pending_frames.set(self.pending_frames.get() + 1);
882 transaction.generate_frame(0, true , reason);
883 }
884
885 fn send_root_pipeline_display_list(&mut self) {
889 let mut transaction = Transaction::new();
890 self.send_root_pipeline_display_list_in_transaction(&mut transaction);
891 self.generate_frame(&mut transaction, RenderReasons::SCENE);
892 self.global.borrow_mut().send_transaction(transaction);
893 }
894
895 pub(crate) fn send_root_pipeline_display_list_in_transaction(
899 &self,
900 transaction: &mut Transaction,
901 ) {
902 let root_pipeline = WebRenderPipelineId(0, 1);
906 transaction.set_root_pipeline(root_pipeline);
907
908 let mut builder = webrender_api::DisplayListBuilder::new(root_pipeline);
909 builder.begin();
910
911 let root_reference_frame = SpatialId::root_reference_frame(root_pipeline);
912
913 let viewport_size = self.rendering_context.size2d().to_f32().to_untyped();
914 let viewport_rect = LayoutRect::from_origin_and_size(
915 LayoutPoint::zero(),
916 LayoutSize::from_untyped(viewport_size),
917 );
918
919 let root_clip_id = builder.define_clip_rect(root_reference_frame, viewport_rect);
920 let clip_chain_id = builder.define_clip_chain(None, [root_clip_id]);
921 for (_, webview_renderer) in self.webview_renderers.painting_order() {
922 let Some(pipeline_id) = webview_renderer.root_pipeline_id else {
923 continue;
924 };
925
926 let device_pixels_per_page_pixel = webview_renderer.device_pixels_per_page_pixel().0;
927 let webview_reference_frame = builder.push_reference_frame(
928 LayoutPoint::zero(),
929 root_reference_frame,
930 TransformStyle::Flat,
931 PropertyBinding::Value(Transform3D::scale(
932 device_pixels_per_page_pixel,
933 device_pixels_per_page_pixel,
934 1.,
935 )),
936 ReferenceFrameKind::Transform {
937 is_2d_scale_translation: true,
938 should_snap: true,
939 paired_with_perspective: false,
940 },
941 SpatialTreeItemKey::new(0, 0),
942 );
943
944 let scaled_webview_rect = webview_renderer.rect / device_pixels_per_page_pixel;
945 builder.push_iframe(
946 LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()),
947 LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()),
948 &SpaceAndClipInfo {
949 spatial_id: webview_reference_frame,
950 clip_chain_id,
951 },
952 pipeline_id.into(),
953 true,
954 );
955 }
956
957 let built_display_list = builder.end();
958
959 transaction.set_display_list(WebRenderEpoch(0), built_display_list);
963 self.update_transaction_with_all_scroll_offsets(transaction);
964 }
965
966 fn update_transaction_with_all_scroll_offsets(&self, transaction: &mut Transaction) {
975 for webview_renderer in self.webview_renderers.iter() {
976 for details in webview_renderer.pipelines.values() {
977 for node in details.scroll_tree.nodes.iter() {
978 let (Some(offset), Some(external_id)) = (node.offset(), node.external_id())
979 else {
980 continue;
981 };
982
983 transaction.set_scroll_offsets(
984 external_id,
985 vec![SampledScrollOffset {
986 offset,
987 generation: 0,
988 }],
989 );
990 }
991 }
992 }
993 }
994
995 pub fn add_webview(
996 &mut self,
997 webview: Box<dyn WebViewTrait>,
998 viewport_details: ViewportDetails,
999 ) {
1000 self.webview_renderers
1001 .entry(webview.id())
1002 .or_insert(WebViewRenderer::new(
1003 self.global.clone(),
1004 webview,
1005 viewport_details,
1006 ));
1007 }
1008
1009 fn set_frame_tree_for_webview(&mut self, frame_tree: &SendableFrameTree) {
1010 debug!("{}: Setting frame tree for webview", frame_tree.pipeline.id);
1011
1012 let webview_id = frame_tree.pipeline.webview_id;
1013 let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
1014 warn!(
1015 "Attempted to set frame tree on unknown WebView (perhaps closed?): {webview_id:?}"
1016 );
1017 return;
1018 };
1019
1020 webview_renderer.set_frame_tree(frame_tree);
1021 self.send_root_pipeline_display_list();
1022 }
1023
1024 fn remove_webview(&mut self, webview_id: WebViewId) {
1025 debug!("{}: Removing", webview_id);
1026 if self.webview_renderers.remove(webview_id).is_err() {
1027 warn!("{webview_id}: Removing unknown webview");
1028 return;
1029 };
1030
1031 self.send_root_pipeline_display_list();
1032 }
1033
1034 pub fn show_webview(
1035 &mut self,
1036 webview_id: WebViewId,
1037 hide_others: bool,
1038 ) -> Result<(), UnknownWebView> {
1039 debug!("{webview_id}: Showing webview; hide_others={hide_others}");
1040 let painting_order_changed = if hide_others {
1041 let result = self
1042 .webview_renderers
1043 .painting_order()
1044 .map(|(&id, _)| id)
1045 .ne(once(webview_id));
1046 self.webview_renderers.hide_all();
1047 self.webview_renderers.show(webview_id)?;
1048 result
1049 } else {
1050 self.webview_renderers.show(webview_id)?
1051 };
1052 if painting_order_changed {
1053 self.send_root_pipeline_display_list();
1054 }
1055 Ok(())
1056 }
1057
1058 pub fn hide_webview(&mut self, webview_id: WebViewId) -> Result<(), UnknownWebView> {
1059 debug!("{webview_id}: Hiding webview");
1060 if self.webview_renderers.hide(webview_id)? {
1061 self.send_root_pipeline_display_list();
1062 }
1063 Ok(())
1064 }
1065
1066 pub fn raise_webview_to_top(
1067 &mut self,
1068 webview_id: WebViewId,
1069 hide_others: bool,
1070 ) -> Result<(), UnknownWebView> {
1071 debug!("{webview_id}: Raising webview to top; hide_others={hide_others}");
1072 let painting_order_changed = if hide_others {
1073 let result = self
1074 .webview_renderers
1075 .painting_order()
1076 .map(|(&id, _)| id)
1077 .ne(once(webview_id));
1078 self.webview_renderers.hide_all();
1079 self.webview_renderers.raise_to_top(webview_id)?;
1080 result
1081 } else {
1082 self.webview_renderers.raise_to_top(webview_id)?
1083 };
1084 if painting_order_changed {
1085 self.send_root_pipeline_display_list();
1086 }
1087 Ok(())
1088 }
1089
1090 pub fn move_resize_webview(&mut self, webview_id: WebViewId, rect: DeviceRect) {
1091 if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
1092 return;
1093 }
1094 let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
1095 return;
1096 };
1097 if !webview_renderer.set_rect(rect) {
1098 return;
1099 }
1100
1101 self.send_root_pipeline_display_list();
1102 self.set_needs_repaint(RepaintReason::Resize);
1103 }
1104
1105 pub fn set_hidpi_scale_factor(
1106 &mut self,
1107 webview_id: WebViewId,
1108 new_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1109 ) {
1110 if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
1111 return;
1112 }
1113 let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
1114 return;
1115 };
1116 if !webview_renderer.set_hidpi_scale_factor(new_scale_factor) {
1117 return;
1118 }
1119
1120 self.send_root_pipeline_display_list();
1121 self.set_needs_repaint(RepaintReason::Resize);
1122 }
1123
1124 pub fn resize_rendering_context(&mut self, new_size: PhysicalSize<u32>) {
1125 if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
1126 return;
1127 }
1128 if self.rendering_context.size() == new_size {
1129 return;
1130 }
1131
1132 self.rendering_context.resize(new_size);
1133
1134 let mut transaction = Transaction::new();
1135 let output_region = DeviceIntRect::new(
1136 Point2D::zero(),
1137 Point2D::new(new_size.width as i32, new_size.height as i32),
1138 );
1139 transaction.set_document_view(output_region);
1140 self.global.borrow_mut().send_transaction(transaction);
1141
1142 self.send_root_pipeline_display_list();
1143 self.set_needs_repaint(RepaintReason::Resize);
1144 }
1145
1146 pub fn on_zoom_reset_window_event(&mut self, webview_id: WebViewId) {
1147 if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
1148 return;
1149 }
1150
1151 if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
1152 webview_renderer.set_page_zoom(Scale::new(1.0));
1153 }
1154 }
1155
1156 pub fn on_zoom_window_event(&mut self, webview_id: WebViewId, magnification: f32) {
1157 if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
1158 return;
1159 }
1160
1161 if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
1162 let current_page_zoom = webview_renderer.page_zoom();
1163 webview_renderer.set_page_zoom(current_page_zoom * Scale::new(magnification));
1164 }
1165 }
1166
1167 fn animation_callbacks_running(&self) -> bool {
1169 self.webview_renderers
1170 .iter()
1171 .any(WebViewRenderer::animation_callbacks_running)
1172 }
1173
1174 fn is_ready_to_paint_image_output(&mut self) -> Result<(), NotReadyToPaint> {
1178 match self.ready_to_save_state {
1179 ReadyState::Unknown => {
1180 let mut pipeline_epochs = HashMap::new();
1187 for id in self
1188 .webview_renderers
1189 .iter()
1190 .flat_map(WebViewRenderer::pipeline_ids)
1191 {
1192 if let Some(WebRenderEpoch(epoch)) = self
1193 .webrender
1194 .as_ref()
1195 .and_then(|wr| wr.current_epoch(self.webrender_document(), id.into()))
1196 {
1197 let epoch = Epoch(epoch);
1198 pipeline_epochs.insert(*id, epoch);
1199 }
1200 }
1201
1202 let msg = EmbedderToConstellationMessage::IsReadyToSaveImage(pipeline_epochs);
1205 if let Err(e) = self.global.borrow().constellation_sender.send(msg) {
1206 warn!("Sending ready to save to constellation failed ({:?}).", e);
1207 }
1208 self.ready_to_save_state = ReadyState::WaitingForConstellationReply;
1209 Err(NotReadyToPaint::JustNotifiedConstellation)
1210 },
1211 ReadyState::WaitingForConstellationReply => {
1212 Err(NotReadyToPaint::WaitingOnConstellation)
1215 },
1216 ReadyState::ReadyToSaveImage => {
1217 self.ready_to_save_state = ReadyState::Unknown;
1223 Ok(())
1224 },
1225 }
1226 }
1227
1228 pub fn render(&mut self) -> bool {
1231 self.global
1232 .borrow()
1233 .refresh_driver
1234 .notify_will_paint(self.webview_renderers.iter());
1235
1236 if let Err(error) = self.render_inner() {
1237 warn!("Unable to render: {error:?}");
1238 return false;
1239 }
1240
1241 self.needs_repaint.set(RepaintReason::empty());
1244
1245 true
1246 }
1247
1248 pub fn render_to_shared_memory(
1251 &mut self,
1252 webview_id: WebViewId,
1253 page_rect: Option<Rect<f32, CSSPixel>>,
1254 ) -> Result<Option<RasterImage>, UnableToComposite> {
1255 self.render_inner()?;
1256
1257 let size = self.rendering_context.size2d().to_i32();
1258 let rect = if let Some(rect) = page_rect {
1259 let scale = self
1260 .webview_renderers
1261 .get(webview_id)
1262 .map(WebViewRenderer::device_pixels_per_page_pixel)
1263 .unwrap_or_else(|| Scale::new(1.0));
1264 let rect = scale.transform_rect(&rect);
1265
1266 let x = rect.origin.x as i32;
1267 let y = (size.height as f32 - rect.origin.y - rect.size.height) as i32;
1270 let w = rect.size.width as i32;
1271 let h = rect.size.height as i32;
1272
1273 DeviceIntRect::from_origin_and_size(Point2D::new(x, y), Size2D::new(w, h))
1274 } else {
1275 DeviceIntRect::from_origin_and_size(Point2D::origin(), size)
1276 };
1277
1278 Ok(self
1279 .rendering_context
1280 .read_to_image(rect)
1281 .map(|image| RasterImage {
1282 metadata: ImageMetadata {
1283 width: image.width(),
1284 height: image.height(),
1285 },
1286 format: PixelFormat::RGBA8,
1287 frames: vec![ImageFrame {
1288 delay: None,
1289 byte_range: 0..image.len(),
1290 width: image.width(),
1291 height: image.height(),
1292 }],
1293 bytes: ipc::IpcSharedMemory::from_bytes(&image),
1294 id: None,
1295 cors_status: CorsStatus::Safe,
1296 }))
1297 }
1298
1299 #[servo_tracing::instrument(skip_all)]
1300 fn render_inner(&mut self) -> Result<(), UnableToComposite> {
1301 if let Err(err) = self.rendering_context.make_current() {
1302 warn!("Failed to make the rendering context current: {:?}", err);
1303 }
1304 self.assert_no_gl_error();
1305
1306 if let Some(webrender) = self.webrender.as_mut() {
1307 webrender.update();
1308 }
1309
1310 if opts::get().wait_for_stable_image {
1311 if let Err(result) = self.is_ready_to_paint_image_output() {
1312 return Err(UnableToComposite::NotReadyToPaintImage(result));
1313 }
1314 }
1315
1316 self.rendering_context.prepare_for_rendering();
1317
1318 let time_profiler_chan = self.global.borrow().time_profiler_chan.clone();
1319 time_profile!(
1320 ProfilerCategory::Compositing,
1321 None,
1322 time_profiler_chan,
1323 || {
1324 trace!("Compositing");
1325
1326 self.clear_background();
1329 if let Some(webrender) = self.webrender.as_mut() {
1330 let size = self.rendering_context.size2d().to_i32();
1331 webrender.render(size, 0 ).ok();
1332 }
1333 },
1334 );
1335
1336 self.send_pending_paint_metrics_messages_after_composite();
1337 Ok(())
1338 }
1339
1340 fn send_pending_paint_metrics_messages_after_composite(&mut self) {
1348 let paint_time = CrossProcessInstant::now();
1349 let document_id = self.webrender_document();
1350 for webview_renderer in self.webview_renderers.iter_mut() {
1351 for (pipeline_id, pipeline) in webview_renderer.pipelines.iter_mut() {
1352 let Some(current_epoch) = self
1353 .webrender
1354 .as_ref()
1355 .and_then(|wr| wr.current_epoch(document_id, pipeline_id.into()))
1356 else {
1357 continue;
1358 };
1359
1360 match pipeline.first_paint_metric {
1361 PaintMetricState::Seen(epoch, first_reflow) if epoch <= current_epoch => {
1366 assert!(epoch <= current_epoch);
1367 if let Err(error) = self.global.borrow().constellation_sender.send(
1368 EmbedderToConstellationMessage::PaintMetric(
1369 *pipeline_id,
1370 PaintMetricEvent::FirstPaint(paint_time, first_reflow),
1371 ),
1372 ) {
1373 warn!(
1374 "Sending paint metric event to constellation failed ({error:?})."
1375 );
1376 }
1377 pipeline.first_paint_metric = PaintMetricState::Sent;
1378 },
1379 _ => {},
1380 }
1381
1382 match pipeline.first_contentful_paint_metric {
1383 PaintMetricState::Seen(epoch, first_reflow) if epoch <= current_epoch => {
1384 if let Err(error) = self.global.borrow().constellation_sender.send(
1385 EmbedderToConstellationMessage::PaintMetric(
1386 *pipeline_id,
1387 PaintMetricEvent::FirstContentfulPaint(paint_time, first_reflow),
1388 ),
1389 ) {
1390 warn!(
1391 "Sending paint metric event to constellation failed ({error:?})."
1392 );
1393 }
1394 pipeline.first_contentful_paint_metric = PaintMetricState::Sent;
1395 },
1396 _ => {},
1397 }
1398 }
1399 }
1400 }
1401
1402 fn clear_background(&self) {
1403 let gl = &self.global.borrow().webrender_gl;
1404 self.assert_gl_framebuffer_complete();
1405
1406 let color = servo_config::pref!(shell_background_color_rgba);
1410 gl.clear_color(
1411 color[0] as f32,
1412 color[1] as f32,
1413 color[2] as f32,
1414 color[3] as f32,
1415 );
1416 gl.clear(gleam::gl::COLOR_BUFFER_BIT);
1417 }
1418
1419 #[track_caller]
1420 fn assert_no_gl_error(&self) {
1421 debug_assert_eq!(
1422 self.global.borrow().webrender_gl.get_error(),
1423 gleam::gl::NO_ERROR
1424 );
1425 }
1426
1427 #[track_caller]
1428 fn assert_gl_framebuffer_complete(&self) {
1429 debug_assert_eq!(
1430 (
1431 self.global.borrow().webrender_gl.get_error(),
1432 self.global
1433 .borrow()
1434 .webrender_gl
1435 .check_frame_buffer_status(gleam::gl::FRAMEBUFFER)
1436 ),
1437 (gleam::gl::NO_ERROR, gleam::gl::FRAMEBUFFER_COMPLETE)
1438 );
1439 }
1440
1441 pub fn receiver(&self) -> Ref<'_, RoutedReceiver<CompositorMsg>> {
1443 Ref::map(self.global.borrow(), |global| &global.compositor_receiver)
1444 }
1445
1446 #[servo_tracing::instrument(skip_all)]
1447 pub fn handle_messages(&mut self, mut messages: Vec<CompositorMsg>) {
1448 let mut found_recomposite_msg = false;
1450 messages.retain(|message| {
1451 match message {
1452 CompositorMsg::NewWebRenderFrameReady(..) if found_recomposite_msg => {
1453 self.pending_frames.set(self.pending_frames.get() - 1);
1456 false
1457 },
1458 CompositorMsg::NewWebRenderFrameReady(..) => {
1459 found_recomposite_msg = true;
1460 true
1461 },
1462 _ => true,
1463 }
1464 });
1465
1466 for message in messages {
1467 self.handle_browser_message(message);
1468 if self.global.borrow().shutdown_state() == ShutdownState::FinishedShuttingDown {
1469 return;
1470 }
1471 }
1472 }
1473
1474 #[servo_tracing::instrument(skip_all)]
1475 pub fn perform_updates(&mut self) -> bool {
1476 if self.global.borrow().shutdown_state() == ShutdownState::FinishedShuttingDown {
1477 return false;
1478 }
1479
1480 #[cfg(feature = "webxr")]
1481 self.global.borrow_mut().webxr_main_thread.run_one_frame();
1483
1484 if let Err(err) = self.rendering_context.make_current() {
1486 warn!("Failed to make the rendering context current: {:?}", err);
1487 }
1488
1489 let mut need_zoom = false;
1490 let scroll_offset_updates: Vec<_> = self
1491 .webview_renderers
1492 .iter_mut()
1493 .filter_map(|webview_renderer| {
1494 let (zoom, scroll_result) =
1495 webview_renderer.process_pending_scroll_and_pinch_zoom_events();
1496 need_zoom = need_zoom || (zoom == PinchZoomResult::DidPinchZoom);
1497 scroll_result
1498 })
1499 .collect();
1500
1501 if need_zoom || !scroll_offset_updates.is_empty() {
1502 let mut transaction = Transaction::new();
1503 if need_zoom {
1504 self.send_root_pipeline_display_list_in_transaction(&mut transaction);
1505 }
1506 for update in scroll_offset_updates {
1507 transaction.set_scroll_offsets(
1508 update.external_scroll_id,
1509 vec![SampledScrollOffset {
1510 offset: update.offset,
1511 generation: 0,
1512 }],
1513 );
1514 }
1515
1516 self.generate_frame(&mut transaction, RenderReasons::APZ);
1517 self.global.borrow_mut().send_transaction(transaction);
1518 }
1519
1520 self.global.borrow().shutdown_state() != ShutdownState::FinishedShuttingDown
1521 }
1522
1523 pub fn toggle_webrender_debug(&mut self, option: WebRenderDebugOption) {
1524 let Some(webrender) = self.webrender.as_mut() else {
1525 return;
1526 };
1527 let mut flags = webrender.get_debug_flags();
1528 let flag = match option {
1529 WebRenderDebugOption::Profiler => {
1530 webrender::DebugFlags::PROFILER_DBG |
1531 webrender::DebugFlags::GPU_TIME_QUERIES |
1532 webrender::DebugFlags::GPU_SAMPLE_QUERIES
1533 },
1534 WebRenderDebugOption::TextureCacheDebug => webrender::DebugFlags::TEXTURE_CACHE_DBG,
1535 WebRenderDebugOption::RenderTargetDebug => webrender::DebugFlags::RENDER_TARGET_DBG,
1536 };
1537 flags.toggle(flag);
1538 webrender.set_debug_flags(flags);
1539
1540 let mut txn = Transaction::new();
1541 self.generate_frame(&mut txn, RenderReasons::TESTING);
1542 self.global.borrow_mut().send_transaction(txn);
1543 }
1544
1545 pub fn capture_webrender(&mut self) {
1546 let capture_id = SystemTime::now()
1547 .duration_since(UNIX_EPOCH)
1548 .unwrap_or_default()
1549 .as_secs()
1550 .to_string();
1551 let available_path = [env::current_dir(), Ok(env::temp_dir())]
1552 .iter()
1553 .filter_map(|val| {
1554 val.as_ref()
1555 .map(|dir| dir.join("webrender-captures").join(&capture_id))
1556 .ok()
1557 })
1558 .find(|val| create_dir_all(val).is_ok());
1559
1560 let Some(capture_path) = available_path else {
1561 eprintln!("Couldn't create a path for WebRender captures.");
1562 return;
1563 };
1564
1565 println!("Saving WebRender capture to {capture_path:?}");
1566 self.global
1567 .borrow()
1568 .webrender_api
1569 .save_capture(capture_path.clone(), CaptureBits::all());
1570 }
1571
1572 fn add_font_instance(
1573 &mut self,
1574 instance_key: FontInstanceKey,
1575 font_key: FontKey,
1576 size: f32,
1577 flags: FontInstanceFlags,
1578 variations: Vec<FontVariation>,
1579 ) {
1580 let variations = if pref!(layout_variable_fonts_enabled) {
1581 variations
1582 } else {
1583 vec![]
1584 };
1585
1586 let mut transaction = Transaction::new();
1587
1588 let font_instance_options = FontInstanceOptions {
1589 flags,
1590 ..Default::default()
1591 };
1592 transaction.add_font_instance(
1593 instance_key,
1594 font_key,
1595 size,
1596 Some(font_instance_options),
1597 None,
1598 variations,
1599 );
1600
1601 self.global.borrow_mut().send_transaction(transaction);
1602 }
1603
1604 fn add_font(&mut self, font_key: FontKey, index: u32, data: Arc<IpcSharedMemory>) {
1605 let mut transaction = Transaction::new();
1606 transaction.add_raw_font(font_key, (**data).into(), index);
1607 self.global.borrow_mut().send_transaction(transaction);
1608 }
1609
1610 pub fn notify_input_event(&mut self, webview_id: WebViewId, event: InputEvent) {
1611 if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
1612 webview_renderer.notify_input_event(event);
1613 }
1614 }
1615
1616 pub fn notify_scroll_event(
1617 &mut self,
1618 webview_id: WebViewId,
1619 scroll_location: ScrollLocation,
1620 cursor: DeviceIntPoint,
1621 ) {
1622 if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
1623 webview_renderer.notify_scroll_event(scroll_location, cursor);
1624 }
1625 }
1626
1627 pub fn on_vsync(&mut self, webview_id: WebViewId) {
1628 if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
1629 webview_renderer.on_vsync();
1630 }
1631 }
1632
1633 pub fn set_pinch_zoom(&mut self, webview_id: WebViewId, magnification: f32) {
1634 if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
1635 webview_renderer.set_pinch_zoom(magnification);
1636 }
1637 }
1638
1639 fn webrender_document(&self) -> DocumentId {
1640 self.global.borrow().webrender_document
1641 }
1642
1643 fn shutdown_state(&self) -> ShutdownState {
1644 self.global.borrow().shutdown_state()
1645 }
1646
1647 fn refresh_cursor(&self) {
1648 let global = self.global.borrow();
1649 let Some(last_mouse_move_position) = global.last_mouse_move_position else {
1650 return;
1651 };
1652
1653 let Some(hit_test_result) = global
1654 .hit_test_at_point(last_mouse_move_position)
1655 .first()
1656 .cloned()
1657 else {
1658 return;
1659 };
1660
1661 if let Err(error) =
1662 global
1663 .constellation_sender
1664 .send(EmbedderToConstellationMessage::RefreshCursor(
1665 hit_test_result.pipeline_id,
1666 ))
1667 {
1668 warn!("Sending event to constellation failed ({:?}).", error);
1669 }
1670 }
1671
1672 fn handle_new_webrender_frame_ready(&mut self, recomposite_needed: bool) {
1673 self.pending_frames.set(self.pending_frames.get() - 1);
1674 if recomposite_needed {
1675 self.refresh_cursor();
1676 }
1677 if recomposite_needed || self.animation_callbacks_running() {
1678 self.set_needs_repaint(RepaintReason::NewWebRenderFrame);
1679 }
1680 }
1681}
1682
1683#[derive(Default)]
1693struct FrameDelayer {
1694 image_epochs: HashMap<ImageKey, Epoch>,
1698 pending_canvas_images: HashMap<ImageKey, Epoch>,
1700 pending_frame: bool,
1702 waiting_pipelines: HashSet<PipelineId>,
1705}
1706
1707impl FrameDelayer {
1708 fn delete_image(&mut self, image_key: ImageKey) {
1709 self.image_epochs.remove(&image_key);
1710 self.pending_canvas_images.remove(&image_key);
1711 }
1712
1713 fn update_image(&mut self, image_key: ImageKey, epoch: Epoch) {
1714 self.image_epochs.insert(image_key, epoch);
1715 let Entry::Occupied(entry) = self.pending_canvas_images.entry(image_key) else {
1716 return;
1717 };
1718 if *entry.get() <= epoch {
1719 entry.remove();
1720 }
1721 }
1722
1723 fn add_delay(
1724 &mut self,
1725 pipeline_id: PipelineId,
1726 canvas_epoch: Epoch,
1727 image_keys: Vec<ImageKey>,
1728 ) {
1729 for image_key in image_keys.into_iter() {
1730 if self
1733 .image_epochs
1734 .get(&image_key)
1735 .is_some_and(|epoch_seen| *epoch_seen >= canvas_epoch)
1736 {
1737 continue;
1738 }
1739 self.pending_canvas_images.insert(image_key, canvas_epoch);
1740 }
1741 self.waiting_pipelines.insert(pipeline_id);
1742 }
1743
1744 fn needs_new_frame(&self) -> bool {
1745 self.pending_frame && self.pending_canvas_images.is_empty()
1746 }
1747
1748 fn set_pending_frame(&mut self, value: bool) {
1749 self.pending_frame = value;
1750 }
1751
1752 fn take_waiting_pipelines(&mut self) -> Vec<PipelineId> {
1753 self.waiting_pipelines.drain().collect()
1754 }
1755}