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