1use std::cell::{Cell, Ref, RefCell, RefMut};
6use std::collections::HashMap;
7use std::env;
8use std::fs::create_dir_all;
9use std::rc::Rc;
10use std::time::{SystemTime, UNIX_EPOCH};
11
12use base::generic_channel::{self, GenericSender, RoutedReceiver};
13use base::id::{PainterId, PipelineId, WebViewId};
14use bitflags::bitflags;
15use canvas_traits::webgl::{WebGLContextId, WebGLThreads};
16use constellation_traits::EmbedderToConstellationMessage;
17use crossbeam_channel::Sender;
18use dpi::PhysicalSize;
19use embedder_traits::{
20 EventLoopWaker, InputEventAndId, InputEventId, InputEventResult, ScreenshotCaptureError,
21 Scroll, ShutdownState, ViewportDetails, WebViewPoint, WebViewRect,
22};
23use euclid::{Scale, Size2D};
24use image::RgbaImage;
25use ipc_channel::ipc::{self};
26use log::{debug, warn};
27use paint_api::rendering_context::RenderingContext;
28use paint_api::{
29 PaintMessage, PaintProxy, PainterSurfmanDetails, PainterSurfmanDetailsMap,
30 WebRenderExternalImageIdManager, WebViewTrait,
31};
32use profile_traits::mem::{
33 ProcessReports, ProfilerRegistration, Report, ReportKind, perform_memory_report,
34};
35use profile_traits::path;
36use profile_traits::time::{self as profile_time};
37use servo_config::pref;
38use servo_geometry::DeviceIndependentPixel;
39use style_traits::CSSPixel;
40use surfman::Device;
41use surfman::chains::SwapChains;
42use webgl::WebGLComm;
43use webgl::webgl_thread::WebGLContextBusyMap;
44#[cfg(feature = "webgpu")]
45use webgpu::canvas_context::WebGpuExternalImageMap;
46use webrender::{CaptureBits, MemoryReport};
47use webrender_api::units::{DevicePixel, DevicePoint};
48use webrender_api::{FontInstanceKey, FontKey, ImageKey};
49
50use crate::InitialPaintState;
51use crate::painter::Painter;
52use crate::webview_renderer::UnknownWebView;
53
54#[derive(Copy, Clone)]
56pub enum WebRenderDebugOption {
57 Profiler,
58 TextureCacheDebug,
59 RenderTargetDebug,
60}
61
62pub struct Paint {
84 painters: Vec<Rc<RefCell<Painter>>>,
87
88 pub(crate) paint_proxy: PaintProxy,
91
92 pub(crate) event_loop_waker: Box<dyn EventLoopWaker>,
95
96 shutdown_state: Rc<Cell<ShutdownState>>,
99
100 paint_receiver: RoutedReceiver<PaintMessage>,
102
103 pub(crate) embedder_to_constellation_sender: Sender<EmbedderToConstellationMessage>,
105
106 webrender_external_image_id_manager: WebRenderExternalImageIdManager,
108
109 pub(crate) painter_surfman_details_map: PainterSurfmanDetailsMap,
112
113 pub(crate) busy_webgl_contexts_map: WebGLContextBusyMap,
117
118 webgl_threads: WebGLThreads,
120
121 pub(crate) swap_chains: SwapChains<WebGLContextId, Device>,
123
124 time_profiler_chan: profile_time::ProfilerChan,
126
127 _mem_profiler_registration: ProfilerRegistration,
130
131 #[cfg(feature = "webxr")]
133 webxr_main_thread: RefCell<webxr::MainThreadRegistry>,
134
135 #[cfg(feature = "webgpu")]
137 webgpu_image_map: std::cell::OnceCell<WebGpuExternalImageMap>,
138}
139
140#[derive(Clone, Copy, Default, PartialEq)]
142pub(crate) struct RepaintReason(u8);
143
144bitflags! {
145 impl RepaintReason: u8 {
146 const ReadyForScreenshot = 1 << 0;
148 const ChangedAnimationState = 1 << 1;
150 const NewWebRenderFrame = 1 << 2;
152 const Resize = 1 << 3;
154 const StartedFlinging = 1 << 4;
156 const BlinkingCaret = 1 << 5;
158 }
159}
160
161impl Paint {
162 pub fn new(state: InitialPaintState) -> Rc<RefCell<Self>> {
163 let registration = state.mem_profiler_chan.prepare_memory_reporting(
164 "paint".into(),
165 state.paint_proxy.clone(),
166 PaintMessage::CollectMemoryReport,
167 );
168
169 let webrender_external_image_id_manager = WebRenderExternalImageIdManager::default();
170 let painter_surfman_details_map = PainterSurfmanDetailsMap::default();
171 let WebGLComm {
172 webgl_threads,
173 swap_chains,
174 busy_webgl_context_map,
175 #[cfg(feature = "webxr")]
176 webxr_layer_grand_manager,
177 } = WebGLComm::new(
178 state.paint_proxy.cross_process_paint_api.clone(),
179 webrender_external_image_id_manager.clone(),
180 painter_surfman_details_map.clone(),
181 );
182
183 #[cfg(feature = "webxr")]
185 let webxr_main_thread = {
186 use servo_config::pref;
187
188 let mut webxr_main_thread = webxr::MainThreadRegistry::new(
189 state.event_loop_waker.clone(),
190 webxr_layer_grand_manager,
191 )
192 .expect("Failed to create WebXR device registry");
193 if pref!(dom_webxr_enabled) {
194 state.webxr_registry.register(&mut webxr_main_thread);
195 }
196 webxr_main_thread
197 };
198
199 Rc::new(RefCell::new(Paint {
200 painters: Default::default(),
201 paint_proxy: state.paint_proxy,
202 event_loop_waker: state.event_loop_waker,
203 shutdown_state: state.shutdown_state,
204 paint_receiver: state.receiver,
205 embedder_to_constellation_sender: state.embedder_to_constellation_sender.clone(),
206 webrender_external_image_id_manager,
207 webgl_threads,
208 swap_chains,
209 time_profiler_chan: state.time_profiler_chan,
210 _mem_profiler_registration: registration,
211 painter_surfman_details_map,
212 busy_webgl_contexts_map: busy_webgl_context_map,
213 #[cfg(feature = "webxr")]
214 webxr_main_thread: RefCell::new(webxr_main_thread),
215 #[cfg(feature = "webgpu")]
216 webgpu_image_map: Default::default(),
217 }))
218 }
219
220 pub fn register_rendering_context(
221 &mut self,
222 rendering_context: Rc<dyn RenderingContext>,
223 ) -> PainterId {
224 if let Some(painter_id) = self.painters.iter().find_map(|painter| {
225 let painter = painter.borrow();
226 if Rc::ptr_eq(&painter.rendering_context, &rendering_context) {
227 Some(painter.painter_id)
228 } else {
229 None
230 }
231 }) {
232 return painter_id;
233 }
234
235 let painter = Painter::new(rendering_context.clone(), self);
236 let connection = rendering_context
237 .connection()
238 .expect("Failed to get connection");
239 let adapter = connection
240 .create_adapter()
241 .expect("Failed to create adapter");
242
243 let painter_surfman_details = PainterSurfmanDetails {
244 connection,
245 adapter,
246 };
247 self.painter_surfman_details_map
248 .insert(painter.painter_id, painter_surfman_details);
249
250 let painter_id = painter.painter_id;
251 self.painters.push(Rc::new(RefCell::new(painter)));
252 painter_id
253 }
254
255 fn remove_painter(&mut self, painter_id: PainterId) {
256 self.painters
257 .retain(|painter| painter.borrow().painter_id != painter_id);
258 self.painter_surfman_details_map.remove(painter_id);
259 }
260
261 pub(crate) fn maybe_painter<'a>(&'a self, painter_id: PainterId) -> Option<Ref<'a, Painter>> {
262 self.painters
263 .iter()
264 .map(|painter| painter.borrow())
265 .find(|painter| painter.painter_id == painter_id)
266 }
267
268 pub(crate) fn painter<'a>(&'a self, painter_id: PainterId) -> Ref<'a, Painter> {
269 self.maybe_painter(painter_id)
270 .expect("painter_id not found")
271 }
272
273 pub(crate) fn maybe_painter_mut<'a>(
274 &'a self,
275 painter_id: PainterId,
276 ) -> Option<RefMut<'a, Painter>> {
277 self.painters
278 .iter()
279 .map(|painter| painter.borrow_mut())
280 .find(|painter| painter.painter_id == painter_id)
281 }
282
283 pub(crate) fn painter_mut<'a>(&'a self, painter_id: PainterId) -> RefMut<'a, Painter> {
284 self.maybe_painter_mut(painter_id)
285 .expect("painter_id not found")
286 }
287
288 pub fn painter_id(&self) -> PainterId {
289 self.painters[0].borrow().painter_id
290 }
291
292 pub fn rendering_context_size(&self, painter_id: PainterId) -> Size2D<u32, DevicePixel> {
293 self.painter(painter_id).rendering_context.size2d()
294 }
295
296 pub fn webgl_threads(&self) -> WebGLThreads {
297 self.webgl_threads.clone()
298 }
299
300 pub fn webrender_external_image_id_manager(&self) -> WebRenderExternalImageIdManager {
301 self.webrender_external_image_id_manager.clone()
302 }
303
304 pub fn webxr_running(&self) -> bool {
305 #[cfg(feature = "webxr")]
306 {
307 self.webxr_main_thread.borrow().running()
308 }
309 #[cfg(not(feature = "webxr"))]
310 {
311 false
312 }
313 }
314
315 #[cfg(feature = "webxr")]
316 pub fn webxr_main_thread_registry(&self) -> webxr_api::Registry {
317 self.webxr_main_thread.borrow().registry()
318 }
319
320 #[cfg(feature = "webgpu")]
321 pub fn webgpu_image_map(&self) -> WebGpuExternalImageMap {
322 self.webgpu_image_map.get_or_init(Default::default).clone()
323 }
324
325 pub fn webviews_needing_repaint(&self) -> Vec<WebViewId> {
326 self.painters
327 .iter()
328 .flat_map(|painter| painter.borrow().webviews_needing_repaint())
329 .collect()
330 }
331
332 pub fn finish_shutting_down(&self) {
333 while self.paint_receiver.try_recv().is_ok() {}
336
337 let (webgl_exit_sender, webgl_exit_receiver) =
338 generic_channel::channel().expect("Failed to create IPC channel!");
339 if !self
340 .webgl_threads
341 .exit(webgl_exit_sender)
342 .is_ok_and(|_| webgl_exit_receiver.recv().is_ok())
343 {
344 warn!("Could not exit WebGLThread.");
345 }
346
347 if let Ok((sender, receiver)) = ipc::channel() {
349 self.time_profiler_chan
350 .send(profile_time::ProfilerMsg::Exit(sender));
351 let _ = receiver.recv();
352 }
353 }
354
355 fn handle_browser_message(&self, msg: PaintMessage) {
356 trace_msg_from_constellation!(msg, "{msg:?}");
357
358 match self.shutdown_state() {
359 ShutdownState::NotShuttingDown => {},
360 ShutdownState::ShuttingDown => {
361 self.handle_browser_message_while_shutting_down(msg);
362 return;
363 },
364 ShutdownState::FinishedShuttingDown => {
365 return;
367 },
368 }
369
370 match msg {
371 PaintMessage::CollectMemoryReport(sender) => {
372 self.collect_memory_report(sender);
373 },
374 PaintMessage::ChangeRunningAnimationsState(
375 webview_id,
376 pipeline_id,
377 animation_state,
378 ) => {
379 if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
380 painter.change_running_animations_state(
381 webview_id,
382 pipeline_id,
383 animation_state,
384 );
385 }
386 },
387 PaintMessage::SetFrameTreeForWebView(webview_id, frame_tree) => {
388 if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
389 painter.set_frame_tree_for_webview(&frame_tree);
390 }
391 },
392 PaintMessage::SetThrottled(webview_id, pipeline_id, throttled) => {
393 if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
394 painter.set_throttled(webview_id, pipeline_id, throttled);
395 }
396 },
397 PaintMessage::PipelineExited(webview_id, pipeline_id, pipeline_exit_source) => {
398 if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
399 painter.notify_pipeline_exited(webview_id, pipeline_id, pipeline_exit_source);
400 }
401 },
402 PaintMessage::NewWebRenderFrameReady(..) => {
403 unreachable!("New WebRender frames should be handled in the caller.");
404 },
405 PaintMessage::SendInitialTransaction(webview_id, pipeline_id) => {
406 if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
407 painter.send_initial_pipeline_transaction(webview_id, pipeline_id);
408 }
409 },
410 PaintMessage::ScrollNodeByDelta(
411 webview_id,
412 pipeline_id,
413 offset,
414 external_scroll_id,
415 ) => {
416 if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
417 painter.scroll_node_by_delta(
418 webview_id,
419 pipeline_id,
420 offset,
421 external_scroll_id,
422 );
423 }
424 },
425 PaintMessage::ScrollViewportByDelta(webview_id, delta) => {
426 if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
427 painter.scroll_viewport_by_delta(webview_id, delta);
428 }
429 },
430 PaintMessage::UpdateEpoch {
431 webview_id,
432 pipeline_id,
433 epoch,
434 } => {
435 if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
436 painter.update_epoch(webview_id, pipeline_id, epoch);
437 }
438 },
439 PaintMessage::SendDisplayList {
440 webview_id,
441 display_list_descriptor,
442 display_list_info_receiver,
443 display_list_data_receiver,
444 } => {
445 if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
446 painter.handle_new_display_list(
447 webview_id,
448 display_list_descriptor,
449 display_list_info_receiver,
450 display_list_data_receiver,
451 );
452 }
453 },
454 PaintMessage::GenerateFrame(painter_ids) => {
455 for painter_id in painter_ids {
456 if let Some(mut painter) = self.maybe_painter_mut(painter_id) {
457 painter.generate_frame_for_script();
458 }
459 }
460 },
461 PaintMessage::GenerateImageKey(webview_id, result_sender) => {
462 self.handle_generate_image_key(webview_id, result_sender);
463 },
464 PaintMessage::GenerateImageKeysForPipeline(webview_id, pipeline_id) => {
465 self.handle_generate_image_keys_for_pipeline(webview_id, pipeline_id);
466 },
467 PaintMessage::UpdateImages(painter_id, updates) => {
468 if let Some(mut painter) = self.maybe_painter_mut(painter_id) {
469 painter.update_images(updates);
470 }
471 },
472 PaintMessage::DelayNewFrameForCanvas(
473 webview_id,
474 pipeline_id,
475 canvas_epoch,
476 image_keys,
477 ) => {
478 if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
479 painter.delay_new_frames_for_canvas(pipeline_id, canvas_epoch, image_keys);
480 }
481 },
482 PaintMessage::AddFont(painter_id, font_key, data, index) => {
483 debug_assert!(painter_id == font_key.into());
484
485 if let Some(mut painter) = self.maybe_painter_mut(painter_id) {
486 painter.add_font(font_key, data, index);
487 }
488 },
489 PaintMessage::AddSystemFont(painter_id, font_key, native_handle) => {
490 debug_assert!(painter_id == font_key.into());
491
492 if let Some(mut painter) = self.maybe_painter_mut(painter_id) {
493 painter.add_system_font(font_key, native_handle);
494 }
495 },
496 PaintMessage::AddFontInstance(
497 painter_id,
498 font_instance_key,
499 font_key,
500 size,
501 flags,
502 variations,
503 ) => {
504 debug_assert!(painter_id == font_key.into());
505 debug_assert!(painter_id == font_instance_key.into());
506
507 if let Some(mut painter) = self.maybe_painter_mut(painter_id) {
508 painter.add_font_instance(font_instance_key, font_key, size, flags, variations);
509 }
510 },
511 PaintMessage::RemoveFonts(painter_id, keys, instance_keys) => {
512 if let Some(mut painter) = self.maybe_painter_mut(painter_id) {
513 painter.remove_fonts(keys, instance_keys);
514 }
515 },
516 PaintMessage::GenerateFontKeys(
517 number_of_font_keys,
518 number_of_font_instance_keys,
519 result_sender,
520 painter_id,
521 ) => {
522 self.handle_generate_font_keys(
523 number_of_font_keys,
524 number_of_font_instance_keys,
525 result_sender,
526 painter_id,
527 );
528 },
529 PaintMessage::Viewport(webview_id, viewport_description) => {
530 if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
531 painter.set_viewport_description(webview_id, viewport_description);
532 }
533 },
534 PaintMessage::ScreenshotReadinessReponse(webview_id, pipelines_and_epochs) => {
535 if let Some(painter) = self.maybe_painter(webview_id.into()) {
536 painter.handle_screenshot_readiness_reply(webview_id, pipelines_and_epochs);
537 }
538 },
539 PaintMessage::SendLCPCandidate(lcp_candidate, webview_id, pipeline_id, epoch) => {
540 if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
541 painter.append_lcp_candidate(lcp_candidate, webview_id, pipeline_id, epoch);
542 }
543 },
544 PaintMessage::EnableLCPCalculation(webview_id) => {
545 if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
546 painter.enable_lcp_calculation(&webview_id);
547 }
548 },
549 }
550 }
551
552 pub fn remove_webview(&mut self, webview_id: WebViewId) {
553 let painter_id = webview_id.into();
554
555 {
556 let mut painter = self.painter_mut(painter_id);
557 painter.remove_webview(webview_id);
558 if !painter.is_empty() {
559 return;
560 }
561 }
562
563 self.remove_painter(painter_id);
564 }
565
566 fn collect_memory_report(&self, sender: profile_traits::mem::ReportsChan) {
567 let mut memory_report = MemoryReport::default();
568 for painter in &self.painters {
569 memory_report += painter.borrow().report_memory();
570 }
571
572 let mut reports = vec![
573 Report {
574 path: path!["webrender", "fonts"],
575 kind: ReportKind::ExplicitJemallocHeapSize,
576 size: memory_report.fonts,
577 },
578 Report {
579 path: path!["webrender", "images"],
580 kind: ReportKind::ExplicitJemallocHeapSize,
581 size: memory_report.images,
582 },
583 Report {
584 path: path!["webrender", "display-list"],
585 kind: ReportKind::ExplicitJemallocHeapSize,
586 size: memory_report.display_list,
587 },
588 ];
589
590 perform_memory_report(|ops| {
591 let scroll_trees_memory_usage = self
592 .painters
593 .iter()
594 .map(|painter| painter.borrow().scroll_trees_memory_usage(ops))
595 .sum();
596 reports.push(Report {
597 path: path!["paint", "scroll-tree"],
598 kind: ReportKind::ExplicitJemallocHeapSize,
599 size: scroll_trees_memory_usage,
600 });
601 });
602
603 sender.send(ProcessReports::new(reports));
604 }
605
606 fn handle_browser_message_while_shutting_down(&self, msg: PaintMessage) {
616 match msg {
617 PaintMessage::PipelineExited(webview_id, pipeline_id, pipeline_exit_source) => {
618 if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
619 painter.notify_pipeline_exited(webview_id, pipeline_id, pipeline_exit_source);
620 }
621 },
622 PaintMessage::GenerateImageKey(webview_id, result_sender) => {
623 self.handle_generate_image_key(webview_id, result_sender);
624 },
625 PaintMessage::GenerateImageKeysForPipeline(webview_id, pipeline_id) => {
626 self.handle_generate_image_keys_for_pipeline(webview_id, pipeline_id);
627 },
628 PaintMessage::GenerateFontKeys(
629 number_of_font_keys,
630 number_of_font_instance_keys,
631 result_sender,
632 painter_id,
633 ) => {
634 self.handle_generate_font_keys(
635 number_of_font_keys,
636 number_of_font_instance_keys,
637 result_sender,
638 painter_id,
639 );
640 },
641 _ => {
642 debug!("Ignoring message ({:?} while shutting down", msg);
643 },
644 }
645 }
646
647 pub fn add_webview(&self, webview: Box<dyn WebViewTrait>, viewport_details: ViewportDetails) {
648 self.painter_mut(webview.id().into())
649 .add_webview(webview, viewport_details);
650 }
651
652 pub fn show_webview(&self, webview_id: WebViewId) -> Result<(), UnknownWebView> {
653 self.painter_mut(webview_id.into())
654 .set_webview_hidden(webview_id, false)
655 }
656
657 pub fn hide_webview(&self, webview_id: WebViewId) -> Result<(), UnknownWebView> {
658 self.painter_mut(webview_id.into())
659 .set_webview_hidden(webview_id, true)
660 }
661
662 pub fn set_hidpi_scale_factor(
663 &self,
664 webview_id: WebViewId,
665 new_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
666 ) {
667 if self.shutdown_state() != ShutdownState::NotShuttingDown {
668 return;
669 }
670 self.painter_mut(webview_id.into())
671 .set_hidpi_scale_factor(webview_id, new_scale_factor);
672 }
673
674 pub fn resize_rendering_context(&self, webview_id: WebViewId, new_size: PhysicalSize<u32>) {
675 if self.shutdown_state() != ShutdownState::NotShuttingDown {
676 return;
677 }
678 self.painter_mut(webview_id.into())
679 .resize_rendering_context(new_size);
680 }
681
682 pub fn set_page_zoom(&self, webview_id: WebViewId, new_zoom: f32) {
683 if self.shutdown_state() != ShutdownState::NotShuttingDown {
684 return;
685 }
686 self.painter_mut(webview_id.into())
687 .set_page_zoom(webview_id, new_zoom);
688 }
689
690 pub fn page_zoom(&self, webview_id: WebViewId) -> f32 {
691 self.painter(webview_id.into()).page_zoom(webview_id)
692 }
693
694 pub fn render(&self, webview_id: WebViewId) {
696 self.painter_mut(webview_id.into())
697 .render(&self.time_profiler_chan);
698 }
699
700 pub fn receiver(&self) -> &RoutedReceiver<PaintMessage> {
702 &self.paint_receiver
703 }
704
705 #[servo_tracing::instrument(skip_all)]
706 pub fn handle_messages(&self, mut messages: Vec<PaintMessage>) {
707 let mut saw_webrender_frame_ready_for_painter = HashMap::new();
712 messages.retain(|message| match message {
713 PaintMessage::NewWebRenderFrameReady(painter_id, _document_id, need_repaint) => {
714 if let Some(painter) = self.maybe_painter(*painter_id) {
715 painter.decrement_pending_frames();
716 *saw_webrender_frame_ready_for_painter
717 .entry(*painter_id)
718 .or_insert(*need_repaint) |= *need_repaint;
719 }
720
721 false
722 },
723 _ => true,
724 });
725
726 for message in messages {
727 self.handle_browser_message(message);
728 if self.shutdown_state() == ShutdownState::FinishedShuttingDown {
729 return;
730 }
731 }
732
733 for (painter_id, repaint_needed) in saw_webrender_frame_ready_for_painter.iter() {
734 if let Some(painter) = self.maybe_painter(*painter_id) {
735 painter.handle_new_webrender_frame_ready(*repaint_needed);
736 }
737 }
738 }
739
740 #[servo_tracing::instrument(skip_all)]
741 pub fn perform_updates(&self) -> bool {
742 if self.shutdown_state() == ShutdownState::FinishedShuttingDown {
743 return false;
744 }
745
746 #[cfg(feature = "webxr")]
748 self.webxr_main_thread.borrow_mut().run_one_frame();
749
750 for painter in &self.painters {
751 painter.borrow_mut().perform_updates();
752 }
753
754 self.shutdown_state() != ShutdownState::FinishedShuttingDown
755 }
756
757 pub fn toggle_webrender_debug(&self, option: WebRenderDebugOption) {
758 for painter in &self.painters {
759 painter.borrow_mut().toggle_webrender_debug(option);
760 }
761 }
762
763 pub fn capture_webrender(&self, webview_id: WebViewId) {
764 let capture_id = SystemTime::now()
765 .duration_since(UNIX_EPOCH)
766 .unwrap_or_default()
767 .as_secs()
768 .to_string();
769 let available_path = [env::current_dir(), Ok(env::temp_dir())]
770 .iter()
771 .filter_map(|val| {
772 val.as_ref()
773 .map(|dir| dir.join("webrender-captures").join(&capture_id))
774 .ok()
775 })
776 .find(|val| create_dir_all(val).is_ok());
777
778 let Some(capture_path) = available_path else {
779 log::error!("Couldn't create a path for WebRender captures.");
780 return;
781 };
782
783 log::info!("Saving WebRender capture to {capture_path:?}");
784 self.painter(webview_id.into())
785 .webrender_api
786 .save_capture(capture_path, CaptureBits::all());
787 }
788
789 pub fn notify_input_event(&self, webview_id: WebViewId, event: InputEventAndId) {
790 if self.shutdown_state() != ShutdownState::NotShuttingDown {
791 return;
792 }
793 self.painter_mut(webview_id.into())
794 .notify_input_event(webview_id, event);
795 }
796
797 pub fn notify_scroll_event(&self, webview_id: WebViewId, scroll: Scroll, point: WebViewPoint) {
798 if self.shutdown_state() != ShutdownState::NotShuttingDown {
799 return;
800 }
801 self.painter_mut(webview_id.into())
802 .notify_scroll_event(webview_id, scroll, point);
803 }
804
805 pub fn pinch_zoom(&self, webview_id: WebViewId, pinch_zoom_delta: f32, center: DevicePoint) {
806 if self.shutdown_state() != ShutdownState::NotShuttingDown {
807 return;
808 }
809 self.painter_mut(webview_id.into())
810 .pinch_zoom(webview_id, pinch_zoom_delta, center);
811 }
812
813 pub fn device_pixels_per_page_pixel(
814 &self,
815 webview_id: WebViewId,
816 ) -> Scale<f32, CSSPixel, DevicePixel> {
817 self.painter_mut(webview_id.into())
818 .device_pixels_per_page_pixel(webview_id)
819 }
820
821 pub(crate) fn shutdown_state(&self) -> ShutdownState {
822 self.shutdown_state.get()
823 }
824
825 pub fn request_screenshot(
826 &self,
827 webview_id: WebViewId,
828 rect: Option<WebViewRect>,
829 callback: Box<dyn FnOnce(Result<RgbaImage, ScreenshotCaptureError>) + 'static>,
830 ) {
831 self.painter(webview_id.into())
832 .request_screenshot(webview_id, rect, callback);
833 }
834
835 pub fn notify_input_event_handled(
836 &self,
837 webview_id: WebViewId,
838 input_event_id: InputEventId,
839 result: InputEventResult,
840 ) {
841 if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
842 painter.notify_input_event_handled(webview_id, input_event_id, result);
843 }
844 }
845
846 fn handle_generate_image_key(
851 &self,
852 webview_id: WebViewId,
853 result_sender: GenericSender<ImageKey>,
854 ) {
855 let painter_id = webview_id.into();
856 let image_key = self.maybe_painter(painter_id).map_or_else(
857 || ImageKey::new(painter_id.into(), 0),
858 |painter| painter.webrender_api.generate_image_key(),
859 );
860 let _ = result_sender.send(image_key);
861 }
862
863 fn handle_generate_image_keys_for_pipeline(
868 &self,
869 webview_id: WebViewId,
870 pipeline_id: PipelineId,
871 ) {
872 let painter_id = webview_id.into();
873 let painter = self.maybe_painter(painter_id);
874 let image_keys = (0..pref!(image_key_batch_size))
875 .map(|_| {
876 painter.as_ref().map_or_else(
877 || ImageKey::new(painter_id.into(), 0),
878 |painter| painter.webrender_api.generate_image_key(),
879 )
880 })
881 .collect();
882
883 let _ = self.embedder_to_constellation_sender.send(
884 EmbedderToConstellationMessage::SendImageKeysForPipeline(pipeline_id, image_keys),
885 );
886 }
887
888 fn handle_generate_font_keys(
893 &self,
894 number_of_font_keys: usize,
895 number_of_font_instance_keys: usize,
896 result_sender: GenericSender<(Vec<FontKey>, Vec<FontInstanceKey>)>,
897 painter_id: PainterId,
898 ) {
899 let painter = self.maybe_painter(painter_id);
900 let font_keys = (0..number_of_font_keys)
901 .map(|_| {
902 painter.as_ref().map_or_else(
903 || FontKey::new(painter_id.into(), 0),
904 |painter| painter.webrender_api.generate_font_key(),
905 )
906 })
907 .collect();
908 let font_instance_keys = (0..number_of_font_instance_keys)
909 .map(|_| {
910 painter.as_ref().map_or_else(
911 || FontInstanceKey::new(painter_id.into(), 0),
912 |painter| painter.webrender_api.generate_font_instance_key(),
913 )
914 })
915 .collect();
916
917 let _ = result_sender.send((font_keys, font_instance_keys));
918 }
919}