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