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::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::NoOpWakeLockProvider;
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
856 .store(opts.debug.style_statistics, Ordering::Relaxed);
857
858 if !opts.multiprocess {
859 media_platform::init();
860 }
861
862 PipelineNamespace::install(EMBEDDER_PIPELINE_NAMESPACE_ID);
864
865 let event_loop_waker = builder.event_loop_waker;
870 let (paint_proxy, paint_receiver) = create_paint_channel(event_loop_waker.clone());
871 let (constellation_proxy, embedder_to_constellation_receiver) = ConstellationProxy::new();
872 let (embedder_proxy, embedder_receiver) = create_embedder_channel(event_loop_waker.clone());
873 let (net_embedder_proxy, net_embedder_receiver) =
874 create_generic_embedder_channel::<NetToEmbedderMsg>(event_loop_waker.clone());
875 let (constellation_embedder_proxy, constellation_embedder_receiver) =
876 create_generic_embedder_channel::<ConstellationToEmbedderMsg>(event_loop_waker.clone());
877 let time_profiler_chan = profile_time::Profiler::create(
878 &opts.time_profiling,
879 opts.time_profiler_trace_path.clone(),
880 );
881 let mem_profiler_chan = profile_mem::Profiler::create();
882
883 let devtools_sender = if pref!(devtools_server_enabled) {
884 Some(devtools::start_server(
885 embedder_proxy.clone(),
886 mem_profiler_chan.clone(),
887 ))
888 } else {
889 None
890 };
891
892 let js_engine_setup = if !opts.multiprocess {
895 Some(script::init())
896 } else {
897 None
898 };
899
900 let mut protocols = ProtocolRegistry::with_internal_protocols();
903 protocols.merge(builder.protocol_registry);
904
905 let shutdown_state = Rc::new(Cell::new(ShutdownState::NotShuttingDown));
908 let paint = Paint::new(InitialPaintState {
909 paint_proxy: paint_proxy.clone(),
910 receiver: paint_receiver,
911 embedder_to_constellation_sender: constellation_proxy.sender(),
912 time_profiler_chan: time_profiler_chan.clone(),
913 mem_profiler_chan: mem_profiler_chan.clone(),
914 shutdown_state: shutdown_state.clone(),
915 event_loop_waker: event_loop_waker.clone(),
916 #[cfg(feature = "webxr")]
917 webxr_registry: builder.webxr_registry,
918 });
919
920 let protocols = Arc::new(protocols);
921 let (public_resource_threads, private_resource_threads, async_runtime) =
922 new_resource_threads(
923 devtools_sender.clone(),
924 time_profiler_chan.clone(),
925 mem_profiler_chan.clone(),
926 net_embedder_proxy,
927 opts.config_dir.clone(),
928 opts.certificate_path.clone(),
929 opts.ignore_certificate_errors,
930 protocols.clone(),
931 );
932
933 let (private_storage_threads, public_storage_threads) =
934 new_storage_threads(mem_profiler_chan.clone(), opts.config_dir.clone());
935
936 create_constellation(
937 embedder_to_constellation_receiver,
938 &paint.borrow(),
939 embedder_proxy,
940 constellation_embedder_proxy,
941 paint_proxy,
942 time_profiler_chan,
943 mem_profiler_chan,
944 devtools_sender,
945 protocols,
946 public_resource_threads.clone(),
947 private_resource_threads.clone(),
948 async_runtime,
949 public_storage_threads.clone(),
950 private_storage_threads.clone(),
951 );
952
953 net::connector::prewarm_tls();
954
955 if opts::get().multiprocess {
956 prefs::add_observer(Box::new(constellation_proxy.clone()));
957 }
958
959 Servo(Rc::new(ServoInner {
960 delegate: RefCell::new(Rc::new(DefaultServoDelegate)),
961 paint,
962 network_manager: Rc::new(RefCell::new(NetworkManager::new(
963 public_resource_threads.clone(),
964 private_resource_threads.clone(),
965 ))),
966 site_data_manager: SiteDataManager::new(
967 public_resource_threads,
968 private_resource_threads,
969 public_storage_threads,
970 private_storage_threads,
971 ),
972 javascript_evaluator: Rc::new(RefCell::new(JavaScriptEvaluator::new(
973 constellation_proxy.clone(),
974 ))),
975 constellation_proxy,
976 embedder_receiver,
977 net_embedder_receiver,
978 constellation_embedder_receiver,
979 shutdown_state,
980 webviews: Default::default(),
981 servo_errors: ServoErrorChannel::default(),
982 _js_engine_setup: js_engine_setup,
983 pending_handled_input_events: Default::default(),
984 event_loop_waker,
985 }))
986 }
987
988 pub fn delegate(&self) -> Rc<dyn ServoDelegate> {
989 self.0.delegate.borrow().clone()
990 }
991
992 pub fn set_delegate(&self, delegate: Rc<dyn ServoDelegate>) {
993 *self.0.delegate.borrow_mut() = delegate;
994 }
995
996 pub fn initialize_gl_accelerated_media(display: NativeDisplay, api: GlApi, context: GlContext) {
999 WindowGLContext::initialize(display, api, context)
1000 }
1001
1002 pub fn spin_event_loop(&self) {
1008 self.0.spin_event_loop();
1009 }
1010
1011 pub fn setup_logging(&self) {
1012 let constellation_chan = self.0.constellation_proxy.sender();
1013 let env = env_logger::Env::default();
1014 let env_logger = EnvLoggerBuilder::from_env(env).build();
1015 let con_logger = FromEmbedderLogger::new(constellation_chan);
1016
1017 let filter = max(env_logger.filter(), con_logger.filter());
1018 let logger = BothLogger(env_logger, con_logger);
1019
1020 log::set_boxed_logger(Box::new(logger)).expect("Failed to set logger.");
1021 log::set_max_level(filter);
1022 }
1023
1024 pub fn create_memory_report(&self, snd: GenericCallback<MemoryReportResult>) {
1025 self.0
1026 .constellation_proxy
1027 .send(EmbedderToConstellationMessage::CreateMemoryReport(snd));
1028 }
1029
1030 pub fn execute_webdriver_command(&self, command: WebDriverCommandMsg) {
1031 self.0
1032 .constellation_proxy
1033 .send(EmbedderToConstellationMessage::WebDriverCommand(command));
1034 }
1035
1036 pub fn set_preference(&self, name: &str, value: PrefValue) {
1037 let mut preferences = prefs::get().clone();
1038 preferences.set_value(name, value);
1039 prefs::set(preferences);
1040 }
1041
1042 pub fn network_manager<'a>(&'a self) -> Ref<'a, NetworkManager> {
1043 self.0.network_manager.borrow()
1044 }
1045
1046 pub fn site_data_manager(&self) -> &SiteDataManager {
1047 &self.0.site_data_manager
1048 }
1049
1050 pub(crate) fn paint<'a>(&'a self) -> Ref<'a, Paint> {
1051 self.0.paint.borrow()
1052 }
1053
1054 pub(crate) fn paint_mut<'a>(&'a self) -> RefMut<'a, Paint> {
1055 self.0.paint.borrow_mut()
1056 }
1057
1058 pub(crate) fn event_loop_waker(&self) -> &dyn EventLoopWaker {
1059 &*self.0.event_loop_waker
1060 }
1061
1062 pub(crate) fn webviews_mut<'a>(
1063 &'a self,
1064 ) -> RefMut<'a, FxHashMap<WebViewId, Weak<RefCell<WebViewInner>>>> {
1065 self.0.webviews.borrow_mut()
1066 }
1067
1068 pub(crate) fn constellation_proxy(&self) -> &ConstellationProxy {
1069 &self.0.constellation_proxy
1070 }
1071
1072 pub(crate) fn javascript_evaluator_mut<'a>(&'a self) -> RefMut<'a, JavaScriptEvaluator> {
1073 self.0.javascript_evaluator.borrow_mut()
1074 }
1075
1076 pub(crate) fn add_pending_handled_input_event(&self, residue_event: PendingHandledInputEvent) {
1077 self.0
1078 .pending_handled_input_events
1079 .borrow_mut()
1080 .push(residue_event);
1081 }
1082}
1083
1084fn create_embedder_channel(
1085 event_loop_waker: Box<dyn EventLoopWaker>,
1086) -> (EmbedderProxy, Receiver<EmbedderMsg>) {
1087 let (sender, receiver) = unbounded();
1088 (
1089 EmbedderProxy {
1090 sender,
1091 event_loop_waker,
1092 },
1093 receiver,
1094 )
1095}
1096
1097fn create_generic_embedder_channel<T>(
1098 event_loop_waker: Box<dyn EventLoopWaker>,
1099) -> (GenericEmbedderProxy<T>, Receiver<T>) {
1100 let (sender, receiver) = unbounded();
1101 (
1102 GenericEmbedderProxy {
1103 sender,
1104 event_loop_waker,
1105 },
1106 receiver,
1107 )
1108}
1109
1110fn create_paint_channel(
1111 event_loop_waker: Box<dyn EventLoopWaker>,
1112) -> (PaintProxy, RoutedReceiver<PaintMessage>) {
1113 let (sender, receiver) = unbounded();
1114 let sender_clone = sender.clone();
1115 let event_loop_waker_clone = event_loop_waker.clone();
1116 let result_callback = move |msg: Result<PaintMessage, ipc_channel::IpcError>| {
1118 if let Err(err) = sender_clone.send(msg) {
1119 warn!("Failed to send response ({:?}).", err);
1120 }
1121 event_loop_waker_clone.wake();
1122 };
1123
1124 let generic_callback =
1125 GenericCallback::new(result_callback).expect("Failed to create callback");
1126 let cross_process_paint_api = CrossProcessPaintApi::new(generic_callback);
1127 let paint_proxy = PaintProxy {
1128 sender,
1129 cross_process_paint_api,
1130 event_loop_waker,
1131 };
1132
1133 (paint_proxy, receiver)
1134}
1135
1136#[expect(clippy::too_many_arguments)]
1137fn create_constellation(
1138 embedder_to_constellation_receiver: Receiver<EmbedderToConstellationMessage>,
1139 paint: &Paint,
1140 embedder_proxy: EmbedderProxy,
1141 constellation_to_embedder_proxy: GenericEmbedderProxy<ConstellationToEmbedderMsg>,
1142 paint_proxy: PaintProxy,
1143 time_profiler_chan: time::ProfilerChan,
1144 mem_profiler_chan: mem::ProfilerChan,
1145 devtools_sender: Option<Sender<devtools_traits::DevtoolsControlMsg>>,
1146 protocols: Arc<ProtocolRegistry>,
1147 public_resource_threads: ResourceThreads,
1148 private_resource_threads: ResourceThreads,
1149 async_runtime: Box<dyn net_traits::AsyncRuntime>,
1150 public_storage_threads: StorageThreads,
1151 private_storage_threads: StorageThreads,
1152) {
1153 let opts = opts::get();
1155
1156 #[cfg(feature = "bluetooth")]
1157 let bluetooth_thread: GenericSender<BluetoothRequest> =
1158 BluetoothThreadFactory::new(embedder_proxy.clone());
1159
1160 let privileged_urls = protocols.privileged_urls();
1161
1162 let system_font_service = Arc::new(
1163 SystemFontService::spawn(
1164 paint_proxy.cross_process_paint_api.clone(),
1165 mem_profiler_chan.clone(),
1166 )
1167 .to_proxy(),
1168 );
1169
1170 let initial_state = InitialConstellationState {
1171 paint_proxy,
1172 embedder_proxy,
1173 constellation_to_embedder_proxy,
1174 devtools_sender,
1175 #[cfg(feature = "bluetooth")]
1176 bluetooth_thread,
1177 system_font_service,
1178 public_resource_threads,
1179 private_resource_threads,
1180 public_storage_threads,
1181 private_storage_threads,
1182 time_profiler_chan,
1183 mem_profiler_chan,
1184 #[cfg(feature = "webxr")]
1185 webxr_registry: Some(paint.webxr_main_thread_registry()),
1186 #[cfg(not(feature = "webxr"))]
1187 webxr_registry: None,
1188 webgl_threads: Some(paint.webgl_threads()),
1189 webrender_external_image_id_manager: paint.webrender_external_image_id_manager(),
1190 #[cfg(feature = "webgpu")]
1191 wgpu_image_map: paint.webgpu_image_map(),
1192 async_runtime,
1193 privileged_urls,
1194 wake_lock_provider: Box::new(NoOpWakeLockProvider),
1195 };
1196
1197 let layout_factory = Arc::new(LayoutFactoryImpl());
1198
1199 Constellation::<script::ScriptThread, script::ServiceWorkerManager>::start(
1200 embedder_to_constellation_receiver,
1201 initial_state,
1202 layout_factory,
1203 opts.random_pipeline_closure_probability,
1204 opts.random_pipeline_closure_seed,
1205 opts.hard_fail,
1206 );
1207}
1208
1209struct BothLogger<Log1, Log2>(Log1, Log2);
1212
1213impl<Log1, Log2> Log for BothLogger<Log1, Log2>
1214where
1215 Log1: Log,
1216 Log2: Log,
1217{
1218 fn enabled(&self, metadata: &Metadata) -> bool {
1219 self.0.enabled(metadata) || self.1.enabled(metadata)
1220 }
1221
1222 fn log(&self, record: &Record) {
1223 self.0.log(record);
1224 self.1.log(record);
1225 }
1226
1227 fn flush(&self) {
1228 self.0.flush();
1229 self.1.flush();
1230 }
1231}
1232
1233fn set_logger(script_to_constellation_sender: ScriptToConstellationSender) {
1234 let con_logger = FromScriptLogger::new(script_to_constellation_sender);
1235 let env = env_logger::Env::default();
1236 let env_logger = EnvLoggerBuilder::from_env(env).build();
1237
1238 let filter = max(env_logger.filter(), con_logger.filter());
1239 let logger = BothLogger(env_logger, con_logger);
1240
1241 log::set_boxed_logger(Box::new(logger)).expect("Failed to set logger.");
1242 log::set_max_level(filter);
1243}
1244
1245pub fn run_content_process(token: String) {
1247 let (unprivileged_content_sender, unprivileged_content_receiver) =
1248 ipc::channel::<UnprivilegedContent>().unwrap();
1249 let connection_bootstrap: IpcSender<IpcSender<UnprivilegedContent>> =
1250 IpcSender::connect(token).unwrap();
1251 connection_bootstrap
1252 .send(unprivileged_content_sender)
1253 .unwrap();
1254
1255 let unprivileged_content = unprivileged_content_receiver.recv().unwrap();
1256 opts::initialize_options(unprivileged_content.opts());
1257 prefs::set(unprivileged_content.prefs().clone());
1258
1259 if opts::get().sandbox {
1261 create_sandbox();
1262 }
1263
1264 let _js_engine_setup = script::init();
1265
1266 match unprivileged_content {
1267 UnprivilegedContent::ScriptEventLoop(new_event_loop_info) => {
1268 media_platform::init();
1269
1270 let fetch_thread_join_handle = start_fetch_thread();
1272
1273 set_logger(
1274 new_event_loop_info
1275 .initial_script_state
1276 .script_to_constellation_sender
1277 .clone(),
1278 );
1279
1280 register_system_memory_reporter_for_event_loop(&new_event_loop_info);
1281
1282 let (background_hang_monitor_register, background_hang_monitor_join_handle) =
1283 HangMonitorRegister::init(
1284 new_event_loop_info.bhm_to_constellation_sender.clone(),
1285 new_event_loop_info.constellation_to_bhm_receiver,
1286 opts::get().background_hang_monitor,
1287 );
1288
1289 let layout_factory = Arc::new(LayoutFactoryImpl());
1290 let script_join_handle = script::ScriptThread::create(
1291 new_event_loop_info.initial_script_state,
1292 layout_factory,
1293 Arc::new(ImageCacheFactoryImpl::new(
1294 new_event_loop_info.broken_image_icon_data,
1295 )),
1296 background_hang_monitor_register,
1297 );
1298
1299 script_join_handle
1300 .join()
1301 .expect("Failed to join on the script thread.");
1302 background_hang_monitor_join_handle
1303 .join()
1304 .expect("Failed to join on the BHM background thread.");
1305
1306 StyleThreadPool::shutdown();
1307
1308 exit_fetch_thread();
1310 fetch_thread_join_handle
1311 .join()
1312 .expect("Failed to join on the fetch thread in the constellation");
1313 },
1314 UnprivilegedContent::ServiceWorker(content) => {
1315 content.start::<ServiceWorkerManager>();
1316 },
1317 }
1318}
1319
1320#[cfg(all(
1321 not(target_os = "windows"),
1322 not(target_os = "ios"),
1323 not(target_os = "android"),
1324 not(target_arch = "arm"),
1325 not(target_arch = "aarch64"),
1326 not(target_env = "ohos"),
1327))]
1328fn create_sandbox() {
1329 ChildSandbox::new(content_process_sandbox_profile())
1330 .activate()
1331 .expect("Failed to activate sandbox!");
1332}
1333
1334#[cfg(any(
1335 target_os = "windows",
1336 target_os = "ios",
1337 target_os = "android",
1338 target_arch = "arm",
1339 target_arch = "aarch64",
1340 target_env = "ohos",
1341))]
1342fn create_sandbox() {
1343 panic!("Sandboxing is not supported on Windows, iOS, ARM targets and android.");
1344}
1345
1346struct DefaultEventLoopWaker;
1347
1348impl EventLoopWaker for DefaultEventLoopWaker {
1349 fn clone_box(&self) -> Box<dyn EventLoopWaker> {
1350 Box::new(DefaultEventLoopWaker)
1351 }
1352
1353 fn wake(&self) {}
1354}
1355
1356#[cfg(feature = "webxr")]
1357struct DefaultWebXrRegistry;
1358#[cfg(feature = "webxr")]
1359impl webxr::WebXrRegistry for DefaultWebXrRegistry {}
1360
1361pub struct ServoBuilder {
1363 opts: Option<Box<Opts>>,
1364 preferences: Option<Box<Preferences>>,
1365 event_loop_waker: Box<dyn EventLoopWaker>,
1366 protocol_registry: ProtocolRegistry,
1367 #[cfg(feature = "webxr")]
1368 webxr_registry: Box<dyn webxr::WebXrRegistry>,
1369}
1370
1371impl Default for ServoBuilder {
1372 fn default() -> Self {
1373 Self {
1374 opts: Default::default(),
1375 preferences: Default::default(),
1376 event_loop_waker: Box::new(DefaultEventLoopWaker),
1377 protocol_registry: Default::default(),
1378 #[cfg(feature = "webxr")]
1379 webxr_registry: Box::new(DefaultWebXrRegistry),
1380 }
1381 }
1382}
1383
1384impl ServoBuilder {
1385 pub fn build(self) -> Servo {
1386 Servo::new(self)
1387 }
1388
1389 pub fn opts(mut self, opts: Opts) -> Self {
1390 self.opts = Some(Box::new(opts));
1391 self
1392 }
1393
1394 pub fn preferences(mut self, preferences: Preferences) -> Self {
1395 self.preferences = Some(Box::new(preferences));
1396 self
1397 }
1398
1399 pub fn event_loop_waker(mut self, event_loop_waker: Box<dyn EventLoopWaker>) -> Self {
1400 self.event_loop_waker = event_loop_waker;
1401 self
1402 }
1403
1404 pub fn protocol_registry(mut self, protocol_registry: ProtocolRegistry) -> Self {
1405 self.protocol_registry = protocol_registry;
1406 self
1407 }
1408
1409 #[cfg(feature = "webxr")]
1410 pub fn webxr_registry(mut self, webxr_registry: Box<dyn webxr::WebXrRegistry>) -> Self {
1411 self.webxr_registry = webxr_registry;
1412 self
1413 }
1414}
1415
1416fn register_system_memory_reporter_for_event_loop(
1417 new_event_loop_info: &NewScriptEventLoopProcessInfo,
1418) {
1419 let callback = GenericCallback::new(|message| {
1423 if let Ok(request) = message {
1424 system_reporter::collect_reports(request);
1425 }
1426 })
1427 .expect("Could not create memory reporter callback");
1428 new_event_loop_info
1429 .initial_script_state
1430 .memory_profiler_sender
1431 .send(ProfilerMsg::RegisterReporter(
1432 format!("system-content-{}", std::process::id()),
1433 Reporter(callback),
1434 ));
1435}