1use std::cell::{Cell, Ref, RefCell, RefMut};
6use std::cmp::max;
7use std::rc::{Rc, Weak};
8use std::sync::Arc;
9use std::time::Duration;
10
11use crossbeam_channel::{Receiver, Sender, unbounded};
12pub use embedder_traits::*;
13use env_logger::Builder as EnvLoggerBuilder;
14use fonts::SystemFontService;
15#[cfg(all(
16 not(target_os = "windows"),
17 not(target_os = "ios"),
18 not(target_os = "android"),
19 not(target_arch = "arm"),
20 not(target_arch = "aarch64"),
21 not(target_env = "ohos"),
22))]
23use gaol::sandbox::{ChildSandbox, ChildSandboxMethods};
24use ipc_channel::ipc::{self, IpcSender};
25use layout::LayoutFactoryImpl;
26use layout_api::ScriptThreadFactory;
27use log::{Log, Metadata, Record, debug, warn};
28use media::{GlApi, NativeDisplay, WindowGLContext};
29use net::embedder::NetToEmbedderMsg;
30use net::image_cache::ImageCacheFactoryImpl;
31use net::protocols::ProtocolRegistry;
32use net::resource_thread::new_resource_threads;
33use net_traits::{ResourceThreads, exit_fetch_thread, start_fetch_thread};
34use paint::{InitialPaintState, Paint};
35pub use paint_api::rendering_context::RenderingContext;
36use paint_api::{CrossProcessPaintApi, PaintMessage, PaintProxy};
37use profile::{mem as profile_mem, system_reporter, time as profile_time};
38use profile_traits::mem::{MemoryReportResult, ProfilerMsg, Reporter};
39use profile_traits::{mem, time};
40use rustc_hash::FxHashMap;
41use script::{JSEngineSetup, ServiceWorkerManager};
42use servo_background_hang_monitor::HangMonitorRegister;
43use servo_base::generic_channel::{GenericCallback, GenericSender, RoutedReceiver};
44pub use servo_base::id::WebViewId;
45use servo_base::id::{EMBEDDER_PIPELINE_NAMESPACE_ID, PipelineNamespace};
46#[cfg(feature = "bluetooth")]
47use servo_bluetooth::BluetoothThreadFactory;
48#[cfg(feature = "bluetooth")]
49use servo_bluetooth_traits::BluetoothRequest;
50use servo_config::opts::{DiagnosticsLoggingOption, Opts};
51use servo_config::prefs::{PrefValue, Preferences};
52use servo_config::{opts, pref, prefs};
53#[cfg(all(
54 not(target_os = "windows"),
55 not(target_os = "ios"),
56 not(target_os = "android"),
57 not(target_arch = "arm"),
58 not(target_arch = "aarch64"),
59 not(target_env = "ohos"),
60))]
61use servo_constellation::content_process_sandbox_profile;
62use servo_constellation::{
63 Constellation, ConstellationToEmbedderMsg, FromEmbedderLogger, FromScriptLogger,
64 InitialConstellationState, NewScriptEventLoopProcessInfo, UnprivilegedContent,
65};
66use servo_constellation_traits::{EmbedderToConstellationMessage, ScriptToConstellationSender};
67use servo_geometry::{
68 DeviceIndependentIntRect, convert_rect_to_css_pixel, convert_size_to_css_pixel,
69};
70use servo_media::ServoMedia;
71use servo_media::player::context::GlContext;
72use servo_wakelock::DefaultWakeLockDelegate;
73use storage::new_storage_threads;
74use storage_traits::StorageThreads;
75use style::global_style_data::StyleThreadPool;
76
77use crate::clipboard_delegate::StringRequest;
78#[cfg(feature = "gamepad")]
79use crate::gamepad_delegate::{GamepadHapticEffectRequest, GamepadHapticEffectRequestType};
80use crate::javascript_evaluator::JavaScriptEvaluator;
81use crate::network_manager::NetworkManager;
82use crate::proxies::ConstellationProxy;
83use crate::responders::ServoErrorChannel;
84use crate::servo_delegate::{DefaultServoDelegate, ServoDelegate, ServoError};
85use crate::site_data_manager::{CookieOperationResponse, SiteDataManager};
86use crate::webview::{MINIMUM_WEBVIEW_SIZE, WebView, WebViewInner};
87use crate::webview_delegate::{
88 AllowOrDenyRequest, AuthenticationRequest, BluetoothDeviceSelectionRequest, EmbedderControl,
89 FilePicker, NavigationRequest, PermissionRequest, ProtocolHandlerRegistration, WebResourceLoad,
90};
91
92#[cfg(feature = "media-gstreamer")]
93mod media_platform {
94 use servo_media_gstreamer::GStreamerBackend;
95
96 use super::ServoMedia;
97
98 #[cfg(any(windows, target_os = "macos"))]
99 pub fn init() {
100 ServoMedia::init_with_backend(|| {
101 let mut plugin_dir = std::env::current_exe().unwrap();
102 plugin_dir.pop();
103
104 if cfg!(target_os = "macos") {
105 plugin_dir.push("lib");
106 }
107
108 let plugin_list = crate::gstreamer_plugins::gstreamer_plugins();
109 match GStreamerBackend::init_with_plugins(plugin_dir, &plugin_list) {
110 Ok(b) => b,
111 Err(e) => {
112 log::error!("Error initializing GStreamer: {:?}", e);
113 std::process::exit(1);
114 },
115 }
116 });
117 }
118
119 #[cfg(not(any(windows, target_os = "macos")))]
120 pub fn init() {
121 ServoMedia::init::<GStreamerBackend>();
122 }
123}
124
125#[cfg(all(not(feature = "media-gstreamer"), target_env = "ohos"))]
126mod media_platform {
127 use servo_media_ohos::OhosBackend;
128
129 use super::ServoMedia;
130 pub fn init() {
131 ServoMedia::init::<OhosBackend>();
132 }
133}
134
135#[cfg(all(not(feature = "media-gstreamer"), not(target_env = "ohos")))]
136mod media_platform {
137 use super::ServoMedia;
138 pub fn init() {
139 ServoMedia::init::<servo_media_dummy::DummyBackend>();
140 }
141}
142
143#[allow(clippy::enum_variant_names)]
144enum Message {
145 FromNet(NetToEmbedderMsg),
146 FromConstellation(ConstellationToEmbedderMsg),
147 FromUnknown(EmbedderMsg),
148}
149
150pub struct PendingHandledInputEvent {
151 pub event_id: InputEventId,
152 pub webview_id: WebViewId,
153}
154
155struct ServoInner {
156 delegate: RefCell<Rc<dyn ServoDelegate>>,
157 paint: Rc<RefCell<Paint>>,
158 constellation_proxy: ConstellationProxy,
159 embedder_receiver: Receiver<EmbedderMsg>,
160 net_embedder_receiver: Receiver<NetToEmbedderMsg>,
161 constellation_embedder_receiver: Receiver<ConstellationToEmbedderMsg>,
162 network_manager: Rc<RefCell<NetworkManager>>,
163 site_data_manager: SiteDataManager,
164 javascript_evaluator: Rc<RefCell<JavaScriptEvaluator>>,
167 shutdown_state: Rc<Cell<ShutdownState>>,
170 webviews: RefCell<FxHashMap<WebViewId, Weak<RefCell<WebViewInner>>>>,
175 servo_errors: ServoErrorChannel,
176 _js_engine_setup: Option<JSEngineSetup>,
180 pending_handled_input_events: RefCell<Vec<PendingHandledInputEvent>>,
183 event_loop_waker: Box<dyn EventLoopWaker>,
185}
186
187impl ServoInner {
188 fn get_webview_handle(&self, id: WebViewId) -> Option<WebView> {
189 self.webviews
190 .borrow()
191 .get(&id)
192 .and_then(WebView::from_weak_handle)
193 }
194
195 #[servo_tracing::instrument(level = "debug", skip_all)]
196 fn spin_event_loop(&self) -> bool {
197 if self.shutdown_state.get() == ShutdownState::FinishedShuttingDown {
198 return false;
199 }
200
201 {
202 let paint = self.paint.borrow();
203 let mut messages = Vec::new();
204 while let Ok(message) = paint.receiver().try_recv() {
205 match message {
206 Ok(message) => messages.push(message),
207 Err(error) => {
208 warn!("Router deserialization error: {error}. Ignoring this PaintMessage.")
209 },
210 }
211 }
212 paint.handle_messages(messages);
213 }
214
215 while let Some(message) = self.receive_one_message() {
217 match message {
218 Message::FromUnknown(message) => self.handle_embedder_message(message),
219 Message::FromNet(message) => self.handle_net_embedder_message(message),
220 Message::FromConstellation(message) => {
221 self.handle_constellation_embedder_message(message)
222 },
223 }
224 if self.shutdown_state.get() == ShutdownState::FinishedShuttingDown {
225 break;
226 }
227 }
228 let pending_handled_input_events =
229 std::mem::take(&mut *self.pending_handled_input_events.borrow_mut());
230 for PendingHandledInputEvent {
231 event_id,
232 webview_id,
233 } in pending_handled_input_events
234 {
235 self.paint.borrow_mut().notify_input_event_handled(
236 webview_id,
237 event_id,
238 InputEventResult::DispatchFailed,
239 );
240 if let Some(webview) = self.get_webview_handle(webview_id) {
241 webview.delegate().notify_input_event_handled(
242 webview,
243 event_id,
244 InputEventResult::DispatchFailed,
245 );
246 }
247 }
248
249 if self.constellation_proxy.disconnected() {
250 self.delegate
251 .borrow()
252 .notify_error(ServoError::LostConnectionWithBackend);
253 }
254
255 self.paint.borrow_mut().perform_updates();
256 self.send_new_frame_ready_messages();
257 self.handle_delegate_errors();
258 self.clean_up_destroyed_webview_handles();
259
260 if self.shutdown_state.get() == ShutdownState::FinishedShuttingDown {
261 return false;
262 }
263
264 true
265 }
266
267 #[servo_tracing::instrument(level = "debug", skip_all)]
268 fn receive_one_message(&self) -> Option<Message> {
269 let mut select = crossbeam_channel::Select::new();
270 let embedder_receiver_index = select.recv(&self.embedder_receiver);
271 let net_embedder_receiver_index = select.recv(&self.net_embedder_receiver);
272 let constellation_embedder_receiver_index =
273 select.recv(&self.constellation_embedder_receiver);
274 let Ok(operation) = select.try_select() else {
275 return None;
276 };
277 let index = operation.index();
278 if index == embedder_receiver_index {
279 let Ok(message) = operation.recv(&self.embedder_receiver) else {
280 return None;
281 };
282 Some(Message::FromUnknown(message))
283 } else if index == net_embedder_receiver_index {
284 let Ok(message) = operation.recv(&self.net_embedder_receiver) else {
285 return None;
286 };
287 Some(Message::FromNet(message))
288 } else if index == constellation_embedder_receiver_index {
289 let Ok(message) = operation.recv(&self.constellation_embedder_receiver) else {
290 return None;
291 };
292 Some(Message::FromConstellation(message))
293 } else {
294 log::error!("No select operation registered for {index:?}");
295 None
296 }
297 }
298
299 fn send_new_frame_ready_messages(&self) {
300 let webviews_needing_repaint = self.paint.borrow().webviews_needing_repaint();
301
302 for webview in webviews_needing_repaint
303 .iter()
304 .filter_map(|webview_id| self.get_webview_handle(*webview_id))
305 {
306 webview.delegate().notify_new_frame_ready(webview);
307 }
308 }
309
310 fn handle_delegate_errors(&self) {
311 while let Some(error) = self.servo_errors.try_recv() {
312 self.delegate.borrow().notify_error(error);
313 }
314 }
315
316 fn clean_up_destroyed_webview_handles(&self) {
317 self.webviews
322 .borrow_mut()
323 .retain(|_webview_id, webview| webview.strong_count() > 0);
324 }
325
326 fn finish_shutting_down(&self) {
327 debug!("Servo received message that Constellation shutdown is complete");
328 self.shutdown_state.set(ShutdownState::FinishedShuttingDown);
329 self.paint.borrow_mut().finish_shutting_down();
330 }
331
332 fn handle_net_embedder_message(&self, message: NetToEmbedderMsg) {
333 match message {
334 NetToEmbedderMsg::SelectFiles(control_id, file_picker_request, response_sender) => {
335 if file_picker_request.accept_current_paths_for_testing {
336 let _ = response_sender.send(Some(file_picker_request.current_paths));
337 return;
338 }
339 if let Some(webview) = self.get_webview_handle(control_id.webview_id) {
340 webview.delegate().show_embedder_control(
341 webview,
342 EmbedderControl::FilePicker(FilePicker {
343 id: control_id,
344 file_picker_request,
345 response_sender: Some(response_sender),
346 }),
347 );
348 }
349 },
350 NetToEmbedderMsg::WebResourceRequested(
351 webview_id,
352 web_resource_request,
353 response_sender,
354 ) => {
355 if let Some(webview) =
356 webview_id.and_then(|webview_id| self.get_webview_handle(webview_id))
357 {
358 let web_resource_load = WebResourceLoad::new(
359 web_resource_request,
360 response_sender,
361 self.servo_errors.sender(),
362 );
363 webview
364 .delegate()
365 .load_web_resource(webview, web_resource_load);
366 } else {
367 let web_resource_load = WebResourceLoad::new(
368 web_resource_request,
369 response_sender,
370 self.servo_errors.sender(),
371 );
372 self.delegate.borrow().load_web_resource(web_resource_load);
373 }
374 },
375 NetToEmbedderMsg::RequestAuthentication(
376 webview_id,
377 url,
378 for_proxy,
379 response_sender,
380 ) => {
381 if let Some(webview) = self.get_webview_handle(webview_id) {
382 let authentication_request = AuthenticationRequest::new(
383 url.into_url(),
384 for_proxy,
385 response_sender,
386 self.servo_errors.sender(),
387 );
388 webview
389 .delegate()
390 .request_authentication(webview, authentication_request);
391 }
392 },
393 NetToEmbedderMsg::EmbedderGetCookiesForUrlResponse(operation_id, cookies) => {
394 self.site_data_manager.handle_cookie_response(
395 operation_id,
396 CookieOperationResponse::Cookies(cookies),
397 );
398 },
399 NetToEmbedderMsg::EmbedderSetCookieForUrlResponse(operation_id) => {
400 self.site_data_manager
401 .handle_cookie_response(operation_id, CookieOperationResponse::Done);
402 },
403 }
404 }
405
406 fn handle_embedder_message(&self, message: EmbedderMsg) {
407 match message {
408 EmbedderMsg::Status(webview_id, status_text) => {
409 if let Some(webview) = self.get_webview_handle(webview_id) {
410 webview.set_status_text(status_text);
411 }
412 },
413 EmbedderMsg::ChangePageTitle(webview_id, title) => {
414 if let Some(webview) = self.get_webview_handle(webview_id) {
415 webview.set_page_title(title);
416 }
417 },
418 EmbedderMsg::MoveTo(webview_id, position) => {
419 if let Some(webview) = self.get_webview_handle(webview_id) {
420 webview.delegate().request_move_to(webview, position);
421 }
422 },
423 EmbedderMsg::ResizeTo(webview_id, size) => {
424 if let Some(webview) = self.get_webview_handle(webview_id) {
425 webview
426 .delegate()
427 .request_resize_to(webview, size.max(MINIMUM_WEBVIEW_SIZE));
428 }
429 },
430 EmbedderMsg::ShowSimpleDialog(webview_id, simple_dialog) => {
431 if let Some(webview) = self.get_webview_handle(webview_id) {
432 webview.delegate().show_embedder_control(
433 webview,
434 EmbedderControl::SimpleDialog(simple_dialog.into()),
435 );
436 }
437 },
438 EmbedderMsg::AllowProtocolHandlerRequest(
439 webview_id,
440 registration_update,
441 response_sender,
442 ) => {
443 if let Some(webview) = self.get_webview_handle(webview_id) {
444 let ProtocolHandlerUpdateRegistration {
445 scheme,
446 url,
447 register_or_unregister,
448 } = registration_update;
449 let protocol_handler_registration = ProtocolHandlerRegistration {
450 scheme,
451 url: url.into_url(),
452 register_or_unregister,
453 };
454 let allow_deny_request = AllowOrDenyRequest::new(
455 response_sender,
456 AllowOrDeny::Deny,
457 self.servo_errors.sender(),
458 );
459 webview.delegate().request_protocol_handler(
460 webview,
461 protocol_handler_registration,
462 allow_deny_request,
463 );
464 }
465 },
466 EmbedderMsg::AllowUnload(webview_id, response_sender) => {
467 if let Some(webview) = self.get_webview_handle(webview_id) {
468 let request = AllowOrDenyRequest::new(
469 response_sender,
470 AllowOrDeny::Allow,
471 self.servo_errors.sender(),
472 );
473 webview.delegate().request_unload(webview, request);
474 }
475 },
476 EmbedderMsg::InputEventsHandled(webview_id, event_outcomes) => {
477 let webview = self.get_webview_handle(webview_id);
478 for InputEventOutcome {
479 id: input_event_id,
480 result,
481 } in event_outcomes
482 {
483 self.paint.borrow_mut().notify_input_event_handled(
484 webview_id,
485 input_event_id,
486 result,
487 );
488 if let Some(ref webview) = webview {
489 webview.delegate().notify_input_event_handled(
490 webview.clone(),
491 input_event_id,
492 result,
493 );
494 }
495 }
496 },
497 EmbedderMsg::ClearClipboard(webview_id) => {
498 if let Some(webview) = self.get_webview_handle(webview_id) {
499 webview.clipboard_delegate().clear(webview);
500 }
501 },
502 EmbedderMsg::GetClipboardText(webview_id, result_sender) => {
503 if let Some(webview) = self.get_webview_handle(webview_id) {
504 webview
505 .clipboard_delegate()
506 .get_text(webview, StringRequest::from(result_sender));
507 }
508 },
509 EmbedderMsg::SetClipboardText(webview_id, string) => {
510 if let Some(webview) = self.get_webview_handle(webview_id) {
511 webview.clipboard_delegate().set_text(webview, string);
512 }
513 },
514 EmbedderMsg::SetCursor(webview_id, cursor) => {
515 if let Some(webview) = self.get_webview_handle(webview_id) {
516 webview.set_cursor(cursor);
517 }
518 },
519 EmbedderMsg::NewFavicon(webview_id, image) => {
520 if let Some(webview) = self.get_webview_handle(webview_id) {
521 webview.set_favicon(image);
522 }
523 },
524 EmbedderMsg::NotifyLoadStatusChanged(webview_id, load_status) => {
525 if let Some(webview) = self.get_webview_handle(webview_id) {
526 webview.set_load_status(load_status);
527 }
528 },
529 EmbedderMsg::NotifyFullscreenStateChanged(webview_id, fullscreen) => {
530 if let Some(webview) = self.get_webview_handle(webview_id) {
531 webview
532 .delegate()
533 .notify_fullscreen_state_changed(webview, fullscreen);
534 }
535 },
536 EmbedderMsg::GetSelectedBluetoothDevice(webview_id, items, response_sender) => {
537 if let Some(webview) = self.get_webview_handle(webview_id) {
538 webview.delegate().show_bluetooth_device_dialog(
539 webview,
540 BluetoothDeviceSelectionRequest::new(items, response_sender),
541 );
542 }
543 },
544 EmbedderMsg::PromptPermission(webview_id, requested_feature, response_sender) => {
545 if let Some(webview) = self.get_webview_handle(webview_id) {
546 let permission_request = PermissionRequest {
547 requested_feature,
548 allow_deny_request: AllowOrDenyRequest::new(
549 response_sender,
550 AllowOrDeny::Deny,
551 self.servo_errors.sender(),
552 ),
553 };
554 webview
555 .delegate()
556 .request_permission(webview, permission_request);
557 }
558 },
559 EmbedderMsg::RequestWakeLockPermission(webview_id, callback) => {
560 if let Some(webview) = self.get_webview_handle(webview_id) {
561 let permission_request = PermissionRequest {
562 requested_feature: PermissionFeature::ScreenWakeLock,
563 allow_deny_request: AllowOrDenyRequest::new_from_callback(
564 callback,
565 AllowOrDeny::Deny,
566 self.servo_errors.sender(),
567 ),
568 };
569 webview
570 .delegate()
571 .request_permission(webview, permission_request);
572 }
573 },
574 EmbedderMsg::OnDevtoolsStarted(port, token) => match port {
575 Ok(port) => self
576 .delegate
577 .borrow()
578 .notify_devtools_server_started(port, token),
579 Err(()) => self
580 .delegate
581 .borrow()
582 .notify_error(ServoError::DevtoolsFailedToStart),
583 },
584 EmbedderMsg::RequestDevtoolsConnection(response_sender) => {
585 self.delegate
586 .borrow()
587 .request_devtools_connection(AllowOrDenyRequest::new(
588 response_sender,
589 AllowOrDeny::Deny,
590 self.servo_errors.sender(),
591 ));
592 },
593 #[cfg(feature = "gamepad")]
594 EmbedderMsg::PlayGamepadHapticEffect(
595 webview_id,
596 gamepad_index,
597 gamepad_haptic_effect_type,
598 callback,
599 ) => {
600 if let Some(webview) = self.get_webview_handle(webview_id) {
601 let request = GamepadHapticEffectRequest::new(
602 gamepad_index,
603 GamepadHapticEffectRequestType::Play(gamepad_haptic_effect_type),
604 Box::new(move |success| {
605 callback
606 .send(success)
607 .expect("Could not send message via callback")
608 }),
609 );
610 webview
611 .gamepad_delegate()
612 .handle_haptic_effect_request(request);
613 }
614 },
615 #[cfg(feature = "gamepad")]
616 EmbedderMsg::StopGamepadHapticEffect(webview_id, gamepad_index, callback) => {
617 if let Some(webview) = self.get_webview_handle(webview_id) {
618 let request = GamepadHapticEffectRequest::new(
619 gamepad_index,
620 GamepadHapticEffectRequestType::Stop,
621 Box::new(move |success| {
622 callback
623 .send(success)
624 .expect("Could not send message via callback")
625 }),
626 );
627 webview
628 .gamepad_delegate()
629 .handle_haptic_effect_request(request);
630 }
631 },
632 EmbedderMsg::ShowNotification(webview_id, notification) => {
633 match webview_id.and_then(|webview_id| self.get_webview_handle(webview_id)) {
634 Some(webview) => webview.delegate().show_notification(webview, notification),
635 None => self.delegate.borrow().show_notification(notification),
636 }
637 },
638 EmbedderMsg::ShowConsoleApiMessage(webview_id, level, message) => {
639 match webview_id.and_then(|webview_id| self.get_webview_handle(webview_id)) {
640 Some(webview) => webview
641 .delegate()
642 .show_console_message(webview, level, message),
643 None => self.delegate.borrow().show_console_message(level, message),
644 }
645 },
646 EmbedderMsg::ShowEmbedderControl(control_id, position, embedder_control_request) => {
647 if let Some(webview) = self.get_webview_handle(control_id.webview_id) {
648 webview.show_embedder_control(control_id, position, embedder_control_request);
649 }
650 },
651 EmbedderMsg::HideEmbedderControl(control_id) => {
652 if let Some(webview) = self.get_webview_handle(control_id.webview_id) {
653 webview
654 .delegate()
655 .hide_embedder_control(webview, control_id);
656 }
657 },
658 EmbedderMsg::GetWindowRect(webview_id, response_sender) => {
659 let window_rect = || {
660 let Some(webview) = self.get_webview_handle(webview_id) else {
661 return DeviceIndependentIntRect::default();
662 };
663 let hidpi_scale_factor = webview.hidpi_scale_factor();
664 let Some(screen_geometry) = webview.delegate().screen_geometry(webview) else {
665 return DeviceIndependentIntRect::default();
666 };
667
668 convert_rect_to_css_pixel(screen_geometry.window_rect, hidpi_scale_factor)
669 };
670
671 if let Err(error) = response_sender.send(window_rect()) {
672 warn!("Failed to respond to GetWindowRect: {error}");
673 }
674 },
675 EmbedderMsg::GetScreenMetrics(webview_id, response_sender) => {
676 let screen_metrics = || {
677 let Some(webview) = self.get_webview_handle(webview_id) else {
678 return ScreenMetrics::default();
679 };
680 let hidpi_scale_factor = webview.hidpi_scale_factor();
681 let Some(screen_geometry) = webview.delegate().screen_geometry(webview) else {
682 return ScreenMetrics::default();
683 };
684
685 ScreenMetrics {
686 screen_size: convert_size_to_css_pixel(
687 screen_geometry.size,
688 hidpi_scale_factor,
689 ),
690 available_size: convert_size_to_css_pixel(
691 screen_geometry.available_size,
692 hidpi_scale_factor,
693 ),
694 }
695 };
696 if let Err(error) = response_sender.send(screen_metrics()) {
697 warn!("Failed to respond to GetScreenMetrics: {error}");
698 }
699 },
700 EmbedderMsg::AccessibilityTreeUpdate(webview_id, tree_update, epoch) => {
701 if let Some(webview) = self.get_webview_handle(webview_id) {
702 webview.process_accessibility_tree_update(tree_update, epoch);
703 }
704 },
705 }
706 }
707
708 fn handle_constellation_embedder_message(&self, message: ConstellationToEmbedderMsg) {
709 match message {
710 ConstellationToEmbedderMsg::ShutdownComplete => self.finish_shutting_down(),
711 ConstellationToEmbedderMsg::AllowNavigationRequest(
712 webview_id,
713 pipeline_id,
714 servo_url,
715 ) => {
716 if let Some(webview) = self.get_webview_handle(webview_id) {
717 let request = NavigationRequest {
718 url: servo_url.into_url(),
719 pipeline_id,
720 constellation_proxy: self.constellation_proxy.clone(),
721 response_sent: false,
722 };
723 webview.delegate().request_navigation(webview, request);
724 }
725 },
726 ConstellationToEmbedderMsg::AllowOpeningWebView(webview_id, response_sender) => {
727 if let Some(webview) = self.get_webview_handle(webview_id) {
728 webview.request_create_new(response_sender);
729 }
730 },
731 ConstellationToEmbedderMsg::WebViewClosed(webview_id) => {
732 if let Some(webview) = self.get_webview_handle(webview_id) {
733 webview.delegate().notify_closed(webview);
734 }
735 },
736 ConstellationToEmbedderMsg::WebViewFocused(webview_id, focus_result) => {
737 if focus_result {
738 for id in self.webviews.borrow().keys() {
739 if let Some(webview) = self.get_webview_handle(*id) {
740 let focused = webview.id() == webview_id;
741 webview.set_focused(focused);
742 }
743 }
744 }
745 },
746 ConstellationToEmbedderMsg::WebViewBlurred => {
747 for id in self.webviews.borrow().keys() {
748 if let Some(webview) = self.get_webview_handle(*id) {
749 webview.set_focused(false);
750 }
751 }
752 },
753 ConstellationToEmbedderMsg::FinishJavaScriptEvaluation(evaluation_id, result) => {
754 self.javascript_evaluator
755 .borrow_mut()
756 .finish_evaluation(evaluation_id, result);
757 },
758 ConstellationToEmbedderMsg::InputEventsHandled(webview_id, event_outcomes) => {
759 let webview = self.get_webview_handle(webview_id);
760 for InputEventOutcome {
761 id: input_event_id,
762 result,
763 } in event_outcomes
764 {
765 self.paint.borrow_mut().notify_input_event_handled(
766 webview_id,
767 input_event_id,
768 result,
769 );
770 if let Some(ref webview) = webview {
771 webview.delegate().notify_input_event_handled(
772 webview.clone(),
773 input_event_id,
774 result,
775 );
776 }
777 }
778 },
779 ConstellationToEmbedderMsg::HistoryTraversalComplete(webview_id, traversal_id) => {
780 if let Some(webview) = self.get_webview_handle(webview_id) {
781 webview
782 .delegate()
783 .notify_traversal_complete(webview.clone(), traversal_id);
784 }
785 },
786 ConstellationToEmbedderMsg::HistoryChanged(
787 webview_id,
788 new_back_forward_list,
789 current_list_index,
790 ) => {
791 if let Some(webview) = self.get_webview_handle(webview_id) {
792 webview.set_history(new_back_forward_list, current_list_index);
793 }
794 },
795 ConstellationToEmbedderMsg::Panic(webview_id, reason, backtrace) => {
796 if let Some(webview) = self.get_webview_handle(webview_id) {
797 webview
798 .delegate()
799 .notify_crashed(webview, reason, backtrace);
800 }
801 },
802 ConstellationToEmbedderMsg::ReportProfile(_items) => {},
803 ConstellationToEmbedderMsg::MediaSessionEvent(webview_id, media_session_event) => {
804 if let Some(webview) = self.get_webview_handle(webview_id) {
805 webview
806 .delegate()
807 .notify_media_session_event(webview, media_session_event);
808 }
809 },
810 }
811 }
812}
813
814impl Drop for ServoInner {
815 fn drop(&mut self) {
816 self.constellation_proxy
817 .send(EmbedderToConstellationMessage::Exit);
818 self.shutdown_state.set(ShutdownState::ShuttingDown);
819 while self.spin_event_loop() {
820 std::thread::sleep(Duration::from_micros(500));
821 }
822 }
823}
824
825#[derive(Clone)]
834pub struct Servo(Rc<ServoInner>);
835
836impl Servo {
837 #[servo_tracing::instrument(name = "Servo::new", skip(builder))]
838 fn new(builder: ServoBuilder) -> Self {
839 let opts = builder.opts.map(|opts| *opts);
841 opts::initialize_options(opts.unwrap_or_default());
842 let opts = opts::get();
843
844 let preferences = builder.preferences.map(|opts| *opts);
847 servo_config::prefs::set(preferences.unwrap_or_default());
848
849 use std::sync::atomic::Ordering;
850
851 style::context::DEFAULT_DISABLE_STYLE_SHARING_CACHE.store(
852 !pref!(layout_style_sharing_cache_enabled),
853 Ordering::Relaxed,
854 );
855 style::context::DEFAULT_DUMP_STYLE_STATISTICS.store(
856 opts.debug
857 .is_enabled(DiagnosticsLoggingOption::StyleStatistics),
858 Ordering::Relaxed,
859 );
860
861 if !opts.multiprocess {
862 media_platform::init();
863 }
864
865 PipelineNamespace::install(EMBEDDER_PIPELINE_NAMESPACE_ID);
867
868 let event_loop_waker = builder.event_loop_waker;
873 let (paint_proxy, paint_receiver) = create_paint_channel(event_loop_waker.clone());
874 let (constellation_proxy, embedder_to_constellation_receiver) = ConstellationProxy::new();
875 let (embedder_proxy, embedder_receiver) = create_embedder_channel(event_loop_waker.clone());
876 let (net_embedder_proxy, net_embedder_receiver) =
877 create_generic_embedder_channel::<NetToEmbedderMsg>(event_loop_waker.clone());
878 let (constellation_embedder_proxy, constellation_embedder_receiver) =
879 create_generic_embedder_channel::<ConstellationToEmbedderMsg>(event_loop_waker.clone());
880 let time_profiler_chan = profile_time::Profiler::create(
881 &opts.time_profiling,
882 opts.time_profiler_trace_path.clone(),
883 );
884 let mem_profiler_chan = profile_mem::Profiler::create();
885
886 let devtools_sender = if pref!(devtools_server_enabled) {
887 Some(devtools::start_server(
888 embedder_proxy.clone(),
889 mem_profiler_chan.clone(),
890 ))
891 } else {
892 None
893 };
894
895 let js_engine_setup = if !opts.multiprocess {
898 Some(script::init())
899 } else {
900 None
901 };
902
903 let mut protocols = ProtocolRegistry::with_internal_protocols();
906 protocols.merge(builder.protocol_registry);
907
908 let shutdown_state = Rc::new(Cell::new(ShutdownState::NotShuttingDown));
911 let paint = Paint::new(InitialPaintState {
912 paint_proxy: paint_proxy.clone(),
913 receiver: paint_receiver,
914 embedder_to_constellation_sender: constellation_proxy.sender(),
915 time_profiler_chan: time_profiler_chan.clone(),
916 mem_profiler_chan: mem_profiler_chan.clone(),
917 shutdown_state: shutdown_state.clone(),
918 event_loop_waker: event_loop_waker.clone(),
919 #[cfg(feature = "webxr")]
920 webxr_registry: builder.webxr_registry,
921 });
922
923 let protocols = Arc::new(protocols);
924 let (public_resource_threads, private_resource_threads, async_runtime) =
925 new_resource_threads(
926 devtools_sender.clone(),
927 time_profiler_chan.clone(),
928 mem_profiler_chan.clone(),
929 net_embedder_proxy,
930 opts.config_dir.clone(),
931 opts.certificate_path.clone(),
932 opts.ignore_certificate_errors,
933 protocols.clone(),
934 );
935
936 let (private_storage_threads, public_storage_threads) = new_storage_threads(
937 mem_profiler_chan.clone(),
938 opts.config_dir.clone(),
939 opts.temporary_storage,
940 );
941
942 create_constellation(
943 embedder_to_constellation_receiver,
944 &paint.borrow(),
945 embedder_proxy,
946 constellation_embedder_proxy,
947 paint_proxy,
948 time_profiler_chan,
949 mem_profiler_chan,
950 devtools_sender,
951 protocols,
952 public_resource_threads.clone(),
953 private_resource_threads.clone(),
954 async_runtime,
955 public_storage_threads.clone(),
956 private_storage_threads.clone(),
957 );
958
959 net::connector::prewarm_tls();
960
961 if opts::get().multiprocess {
962 prefs::add_observer(Box::new(constellation_proxy.clone()));
963 }
964
965 Servo(Rc::new(ServoInner {
966 delegate: RefCell::new(Rc::new(DefaultServoDelegate)),
967 paint,
968 network_manager: Rc::new(RefCell::new(NetworkManager::new(
969 public_resource_threads.clone(),
970 private_resource_threads.clone(),
971 ))),
972 site_data_manager: SiteDataManager::new(
973 public_resource_threads,
974 private_resource_threads,
975 public_storage_threads,
976 private_storage_threads,
977 ),
978 javascript_evaluator: Rc::new(RefCell::new(JavaScriptEvaluator::new(
979 constellation_proxy.clone(),
980 ))),
981 constellation_proxy,
982 embedder_receiver,
983 net_embedder_receiver,
984 constellation_embedder_receiver,
985 shutdown_state,
986 webviews: Default::default(),
987 servo_errors: ServoErrorChannel::default(),
988 _js_engine_setup: js_engine_setup,
989 pending_handled_input_events: Default::default(),
990 event_loop_waker,
991 }))
992 }
993
994 pub fn delegate(&self) -> Rc<dyn ServoDelegate> {
995 self.0.delegate.borrow().clone()
996 }
997
998 pub fn set_delegate(&self, delegate: Rc<dyn ServoDelegate>) {
999 *self.0.delegate.borrow_mut() = delegate;
1000 }
1001
1002 pub fn initialize_gl_accelerated_media(display: NativeDisplay, api: GlApi, context: GlContext) {
1005 WindowGLContext::initialize(display, api, context)
1006 }
1007
1008 pub fn spin_event_loop(&self) {
1014 self.0.spin_event_loop();
1015 }
1016
1017 pub fn setup_logging(&self) {
1018 let constellation_chan = self.0.constellation_proxy.sender();
1019 let env = env_logger::Env::default();
1020 let env_logger = EnvLoggerBuilder::from_env(env).build();
1021 let con_logger = FromEmbedderLogger::new(constellation_chan);
1022
1023 let filter = max(env_logger.filter(), con_logger.filter());
1024 let logger = BothLogger(env_logger, con_logger);
1025
1026 log::set_boxed_logger(Box::new(logger)).expect("Failed to set logger.");
1027 log::set_max_level(filter);
1028 }
1029
1030 pub fn create_memory_report(&self, snd: GenericCallback<MemoryReportResult>) {
1031 self.0
1032 .constellation_proxy
1033 .send(EmbedderToConstellationMessage::CreateMemoryReport(snd));
1034 }
1035
1036 pub fn execute_webdriver_command(&self, command: WebDriverCommandMsg) {
1037 self.0
1038 .constellation_proxy
1039 .send(EmbedderToConstellationMessage::WebDriverCommand(command));
1040 }
1041
1042 pub fn set_preference(&self, name: &str, value: PrefValue) {
1043 let mut preferences = prefs::get().clone();
1044 preferences.set_value(name, value);
1045 prefs::set(preferences);
1046 }
1047
1048 pub fn network_manager<'a>(&'a self) -> Ref<'a, NetworkManager> {
1049 self.0.network_manager.borrow()
1050 }
1051
1052 pub fn site_data_manager(&self) -> &SiteDataManager {
1053 &self.0.site_data_manager
1054 }
1055
1056 pub(crate) fn paint<'a>(&'a self) -> Ref<'a, Paint> {
1057 self.0.paint.borrow()
1058 }
1059
1060 pub(crate) fn paint_mut<'a>(&'a self) -> RefMut<'a, Paint> {
1061 self.0.paint.borrow_mut()
1062 }
1063
1064 pub(crate) fn event_loop_waker(&self) -> &dyn EventLoopWaker {
1065 &*self.0.event_loop_waker
1066 }
1067
1068 pub(crate) fn webviews_mut<'a>(
1069 &'a self,
1070 ) -> RefMut<'a, FxHashMap<WebViewId, Weak<RefCell<WebViewInner>>>> {
1071 self.0.webviews.borrow_mut()
1072 }
1073
1074 pub(crate) fn constellation_proxy(&self) -> &ConstellationProxy {
1075 &self.0.constellation_proxy
1076 }
1077
1078 pub(crate) fn javascript_evaluator_mut<'a>(&'a self) -> RefMut<'a, JavaScriptEvaluator> {
1079 self.0.javascript_evaluator.borrow_mut()
1080 }
1081
1082 pub(crate) fn add_pending_handled_input_event(&self, residue_event: PendingHandledInputEvent) {
1083 self.0
1084 .pending_handled_input_events
1085 .borrow_mut()
1086 .push(residue_event);
1087 }
1088}
1089
1090fn create_embedder_channel(
1091 event_loop_waker: Box<dyn EventLoopWaker>,
1092) -> (EmbedderProxy, Receiver<EmbedderMsg>) {
1093 let (sender, receiver) = unbounded();
1094 (
1095 EmbedderProxy {
1096 sender,
1097 event_loop_waker,
1098 },
1099 receiver,
1100 )
1101}
1102
1103fn create_generic_embedder_channel<T>(
1104 event_loop_waker: Box<dyn EventLoopWaker>,
1105) -> (GenericEmbedderProxy<T>, Receiver<T>) {
1106 let (sender, receiver) = unbounded();
1107 (
1108 GenericEmbedderProxy {
1109 sender,
1110 event_loop_waker,
1111 },
1112 receiver,
1113 )
1114}
1115
1116fn create_paint_channel(
1117 event_loop_waker: Box<dyn EventLoopWaker>,
1118) -> (PaintProxy, RoutedReceiver<PaintMessage>) {
1119 let (sender, receiver) = unbounded();
1120 let sender_clone = sender.clone();
1121 let event_loop_waker_clone = event_loop_waker.clone();
1122 let result_callback = move |msg: Result<PaintMessage, ipc_channel::IpcError>| {
1124 if let Err(err) = sender_clone.send(msg) {
1125 warn!("Failed to send response ({:?}).", err);
1126 }
1127 event_loop_waker_clone.wake();
1128 };
1129
1130 let generic_callback =
1131 GenericCallback::new(result_callback).expect("Failed to create callback");
1132 let cross_process_paint_api = CrossProcessPaintApi::new(generic_callback);
1133 let paint_proxy = PaintProxy {
1134 sender,
1135 cross_process_paint_api,
1136 event_loop_waker,
1137 };
1138
1139 (paint_proxy, receiver)
1140}
1141
1142#[expect(clippy::too_many_arguments)]
1143fn create_constellation(
1144 embedder_to_constellation_receiver: Receiver<EmbedderToConstellationMessage>,
1145 paint: &Paint,
1146 embedder_proxy: EmbedderProxy,
1147 constellation_to_embedder_proxy: GenericEmbedderProxy<ConstellationToEmbedderMsg>,
1148 paint_proxy: PaintProxy,
1149 time_profiler_chan: time::ProfilerChan,
1150 mem_profiler_chan: mem::ProfilerChan,
1151 devtools_sender: Option<Sender<devtools_traits::DevtoolsControlMsg>>,
1152 protocols: Arc<ProtocolRegistry>,
1153 public_resource_threads: ResourceThreads,
1154 private_resource_threads: ResourceThreads,
1155 async_runtime: Box<dyn net_traits::AsyncRuntime>,
1156 public_storage_threads: StorageThreads,
1157 private_storage_threads: StorageThreads,
1158) {
1159 let opts = opts::get();
1161
1162 #[cfg(feature = "bluetooth")]
1163 let bluetooth_thread: GenericSender<BluetoothRequest> =
1164 BluetoothThreadFactory::new(embedder_proxy.clone());
1165
1166 let privileged_urls = protocols.privileged_urls();
1167
1168 let system_font_service = Arc::new(
1169 SystemFontService::spawn(
1170 paint_proxy.cross_process_paint_api.clone(),
1171 mem_profiler_chan.clone(),
1172 )
1173 .to_proxy(),
1174 );
1175
1176 let initial_state = InitialConstellationState {
1177 paint_proxy,
1178 embedder_proxy,
1179 constellation_to_embedder_proxy,
1180 devtools_sender,
1181 #[cfg(feature = "bluetooth")]
1182 bluetooth_thread,
1183 system_font_service,
1184 public_resource_threads,
1185 private_resource_threads,
1186 public_storage_threads,
1187 private_storage_threads,
1188 time_profiler_chan,
1189 mem_profiler_chan,
1190 #[cfg(feature = "webxr")]
1191 webxr_registry: Some(paint.webxr_main_thread_registry()),
1192 #[cfg(not(feature = "webxr"))]
1193 webxr_registry: None,
1194 webgl_threads: Some(paint.webgl_threads()),
1195 webrender_external_image_id_manager: paint.webrender_external_image_id_manager(),
1196 #[cfg(feature = "webgpu")]
1197 wgpu_image_map: paint.webgpu_image_map(),
1198 async_runtime,
1199 privileged_urls,
1200 wake_lock_provider: Box::new(DefaultWakeLockDelegate),
1201 };
1202
1203 let layout_factory = Arc::new(LayoutFactoryImpl());
1204
1205 Constellation::<script::ScriptThread, script::ServiceWorkerManager>::start(
1206 embedder_to_constellation_receiver,
1207 initial_state,
1208 layout_factory,
1209 opts.random_pipeline_closure_probability,
1210 opts.random_pipeline_closure_seed,
1211 opts.hard_fail,
1212 );
1213}
1214
1215struct BothLogger<Log1, Log2>(Log1, Log2);
1218
1219impl<Log1, Log2> Log for BothLogger<Log1, Log2>
1220where
1221 Log1: Log,
1222 Log2: Log,
1223{
1224 fn enabled(&self, metadata: &Metadata) -> bool {
1225 self.0.enabled(metadata) || self.1.enabled(metadata)
1226 }
1227
1228 fn log(&self, record: &Record) {
1229 self.0.log(record);
1230 self.1.log(record);
1231 }
1232
1233 fn flush(&self) {
1234 self.0.flush();
1235 self.1.flush();
1236 }
1237}
1238
1239fn set_logger(script_to_constellation_sender: ScriptToConstellationSender) {
1240 let con_logger = FromScriptLogger::new(script_to_constellation_sender);
1241 let env = env_logger::Env::default();
1242 let env_logger = EnvLoggerBuilder::from_env(env).build();
1243
1244 let filter = max(env_logger.filter(), con_logger.filter());
1245 let logger = BothLogger(env_logger, con_logger);
1246
1247 log::set_boxed_logger(Box::new(logger)).expect("Failed to set logger.");
1248 log::set_max_level(filter);
1249}
1250
1251pub fn run_content_process(token: String) {
1253 let (unprivileged_content_sender, unprivileged_content_receiver) =
1254 ipc::channel::<UnprivilegedContent>().unwrap();
1255 let connection_bootstrap: IpcSender<IpcSender<UnprivilegedContent>> =
1256 IpcSender::connect(token).unwrap();
1257 connection_bootstrap
1258 .send(unprivileged_content_sender)
1259 .unwrap();
1260
1261 let unprivileged_content = unprivileged_content_receiver.recv().unwrap();
1262 opts::initialize_options(unprivileged_content.opts());
1263 prefs::set(unprivileged_content.prefs().clone());
1264
1265 if opts::get().sandbox {
1267 create_sandbox();
1268 }
1269
1270 let _js_engine_setup = script::init();
1271
1272 match unprivileged_content {
1273 UnprivilegedContent::ScriptEventLoop(new_event_loop_info) => {
1274 media_platform::init();
1275
1276 let fetch_thread_join_handle = start_fetch_thread();
1278
1279 set_logger(
1280 new_event_loop_info
1281 .initial_script_state
1282 .script_to_constellation_sender
1283 .clone(),
1284 );
1285
1286 register_system_memory_reporter_for_event_loop(&new_event_loop_info);
1287
1288 let (background_hang_monitor_register, background_hang_monitor_join_handle) =
1289 HangMonitorRegister::init(
1290 new_event_loop_info.bhm_to_constellation_sender.clone(),
1291 new_event_loop_info.constellation_to_bhm_receiver,
1292 opts::get().background_hang_monitor,
1293 );
1294
1295 let layout_factory = Arc::new(LayoutFactoryImpl());
1296 let script_join_handle = script::ScriptThread::create(
1297 new_event_loop_info.initial_script_state,
1298 layout_factory,
1299 Arc::new(ImageCacheFactoryImpl::new(
1300 new_event_loop_info.broken_image_icon_data,
1301 )),
1302 background_hang_monitor_register,
1303 );
1304
1305 script_join_handle
1306 .join()
1307 .expect("Failed to join on the script thread.");
1308 background_hang_monitor_join_handle
1309 .join()
1310 .expect("Failed to join on the BHM background thread.");
1311
1312 StyleThreadPool::shutdown();
1313
1314 exit_fetch_thread();
1316 fetch_thread_join_handle
1317 .join()
1318 .expect("Failed to join on the fetch thread in the constellation");
1319 },
1320 UnprivilegedContent::ServiceWorker(content) => {
1321 content.start::<ServiceWorkerManager>();
1322 },
1323 }
1324}
1325
1326#[cfg(all(
1327 not(target_os = "windows"),
1328 not(target_os = "ios"),
1329 not(target_os = "android"),
1330 not(target_arch = "arm"),
1331 not(target_arch = "aarch64"),
1332 not(target_env = "ohos"),
1333))]
1334fn create_sandbox() {
1335 ChildSandbox::new(content_process_sandbox_profile())
1336 .activate()
1337 .expect("Failed to activate sandbox!");
1338}
1339
1340#[cfg(any(
1341 target_os = "windows",
1342 target_os = "ios",
1343 target_os = "android",
1344 target_arch = "arm",
1345 target_arch = "aarch64",
1346 target_env = "ohos",
1347))]
1348fn create_sandbox() {
1349 panic!("Sandboxing is not supported on Windows, iOS, ARM targets and android.");
1350}
1351
1352struct DefaultEventLoopWaker;
1353
1354impl EventLoopWaker for DefaultEventLoopWaker {
1355 fn clone_box(&self) -> Box<dyn EventLoopWaker> {
1356 Box::new(DefaultEventLoopWaker)
1357 }
1358
1359 fn wake(&self) {}
1360}
1361
1362#[cfg(feature = "webxr")]
1363struct DefaultWebXrRegistry;
1364#[cfg(feature = "webxr")]
1365impl webxr::WebXrRegistry for DefaultWebXrRegistry {}
1366
1367pub struct ServoBuilder {
1369 opts: Option<Box<Opts>>,
1370 preferences: Option<Box<Preferences>>,
1371 event_loop_waker: Box<dyn EventLoopWaker>,
1372 protocol_registry: ProtocolRegistry,
1373 #[cfg(feature = "webxr")]
1374 webxr_registry: Box<dyn webxr::WebXrRegistry>,
1375}
1376
1377impl Default for ServoBuilder {
1378 fn default() -> Self {
1379 Self {
1380 opts: Default::default(),
1381 preferences: Default::default(),
1382 event_loop_waker: Box::new(DefaultEventLoopWaker),
1383 protocol_registry: Default::default(),
1384 #[cfg(feature = "webxr")]
1385 webxr_registry: Box::new(DefaultWebXrRegistry),
1386 }
1387 }
1388}
1389
1390impl ServoBuilder {
1391 pub fn build(self) -> Servo {
1392 Servo::new(self)
1393 }
1394
1395 pub fn opts(mut self, opts: Opts) -> Self {
1396 self.opts = Some(Box::new(opts));
1397 self
1398 }
1399
1400 pub fn preferences(mut self, preferences: Preferences) -> Self {
1401 self.preferences = Some(Box::new(preferences));
1402 self
1403 }
1404
1405 pub fn event_loop_waker(mut self, event_loop_waker: Box<dyn EventLoopWaker>) -> Self {
1406 self.event_loop_waker = event_loop_waker;
1407 self
1408 }
1409
1410 pub fn protocol_registry(mut self, protocol_registry: ProtocolRegistry) -> Self {
1411 self.protocol_registry = protocol_registry;
1412 self
1413 }
1414
1415 #[cfg(feature = "webxr")]
1416 pub fn webxr_registry(mut self, webxr_registry: Box<dyn webxr::WebXrRegistry>) -> Self {
1417 self.webxr_registry = webxr_registry;
1418 self
1419 }
1420}
1421
1422fn register_system_memory_reporter_for_event_loop(
1423 new_event_loop_info: &NewScriptEventLoopProcessInfo,
1424) {
1425 let callback = GenericCallback::new(|message| {
1429 if let Ok(request) = message {
1430 system_reporter::collect_reports(request);
1431 }
1432 })
1433 .expect("Could not create memory reporter callback");
1434 new_event_loop_info
1435 .initial_script_state
1436 .memory_profiler_sender
1437 .send(ProfilerMsg::RegisterReporter(
1438 format!("system-content-{}", std::process::id()),
1439 Reporter(callback),
1440 ));
1441}