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::{FetchThread, ResourceThreads};
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#[cfg(feature = "webxr")]
81use webxr::WebXrRegistry;
82
83use crate::clipboard_delegate::StringRequest;
84#[cfg(feature = "gamepad")]
85use crate::gamepad_delegate::{GamepadHapticEffectRequest, GamepadHapticEffectRequestType};
86use crate::javascript_evaluator::JavaScriptEvaluator;
87use crate::network_manager::NetworkManager;
88use crate::proxies::ConstellationProxy;
89use crate::responders::ServoErrorChannel;
90use crate::servo_delegate::{DefaultServoDelegate, ServoDelegate, ServoError};
91use crate::site_data_manager::{CookieOperationResponse, SiteDataManager};
92use crate::webview::{MINIMUM_WEBVIEW_SIZE, WebView, WebViewInner};
93use crate::webview_delegate::{
94 AllowOrDenyRequest, AuthenticationRequest, BluetoothDeviceSelectionRequest, EmbedderControl,
95 FilePicker, NavigationRequest, PermissionRequest, ProtocolHandlerRegistration, WebResourceLoad,
96};
97
98#[cfg(feature = "media-gstreamer")]
99mod media_platform {
100 use servo_media_gstreamer::GStreamerBackend;
101
102 use super::ServoMedia;
103
104 #[cfg(any(windows, target_os = "macos"))]
105 pub fn init() {
106 ServoMedia::init_with_backend(|| {
107 let mut plugin_dir = std::env::current_exe().unwrap();
108 plugin_dir.pop();
109
110 if cfg!(target_os = "macos") {
111 plugin_dir.push("lib");
112 }
113
114 let plugin_list = crate::gstreamer_plugins::gstreamer_plugins();
115 match GStreamerBackend::init_with_plugins(plugin_dir, &plugin_list) {
116 Ok(b) => b,
117 Err(e) => {
118 log::error!("Error initializing GStreamer: {:?}", e);
119 std::process::exit(1);
120 },
121 }
122 });
123 }
124
125 #[cfg(not(any(windows, target_os = "macos")))]
126 pub fn init() {
127 ServoMedia::init::<GStreamerBackend>();
128 }
129}
130
131#[cfg(all(not(feature = "media-gstreamer"), target_env = "ohos"))]
132mod media_platform {
133 use servo_media_ohos::OhosBackend;
134
135 use super::ServoMedia;
136 pub fn init() {
137 ServoMedia::init::<OhosBackend>();
138 }
139}
140
141#[cfg(all(not(feature = "media-gstreamer"), not(target_env = "ohos")))]
142mod media_platform {
143 use super::ServoMedia;
144 pub fn init() {
145 ServoMedia::init::<servo_media_dummy::DummyBackend>();
146 }
147}
148
149#[allow(clippy::enum_variant_names)]
150enum Message {
151 FromNet(NetToEmbedderMsg),
152 FromConstellation(ConstellationToEmbedderMsg),
153 FromUnknown(EmbedderMsg),
154}
155
156struct EmbedderMessageSelector<'a> {
162 select: crossbeam_channel::Select<'a>,
163 receivers: (
164 &'a Receiver<EmbedderMsg>,
165 &'a Receiver<NetToEmbedderMsg>,
166 &'a Receiver<ConstellationToEmbedderMsg>,
167 ),
168}
169
170impl<'a> EmbedderMessageSelector<'a> {
171 fn new(
172 embedder_receiver: &'a Receiver<EmbedderMsg>,
173 net_embedder_receiver: &'a Receiver<NetToEmbedderMsg>,
174 constellation_embedder_receiver: &'a Receiver<ConstellationToEmbedderMsg>,
175 ) -> Self {
176 let mut select = crossbeam_channel::Select::new();
177 let embedder_index = select.recv(embedder_receiver);
180 debug_assert_eq!(embedder_index, 0);
181 let net_embedder_index = select.recv(net_embedder_receiver);
182 debug_assert_eq!(net_embedder_index, 1);
183 let constellation_embedder_index = select.recv(constellation_embedder_receiver);
184 debug_assert_eq!(constellation_embedder_index, 2);
185 Self {
186 select,
187 receivers: (
188 embedder_receiver,
189 net_embedder_receiver,
190 constellation_embedder_receiver,
191 ),
192 }
193 }
194
195 #[servo_tracing::instrument(
196 level = "debug",
197 name = "EmbedderMessageSelector::try_recv_one_message",
198 skip_all
199 )]
200 fn try_recv_one_message(&mut self) -> Option<Message> {
201 let operation = self.select.try_select().ok()?;
202 let index = operation.index();
203 if index == 0 {
204 let message = operation.recv(self.receivers.0).ok()?;
205 Some(Message::FromUnknown(message))
206 } else if index == 1 {
207 let message = operation.recv(self.receivers.1).ok()?;
208 Some(Message::FromNet(message))
209 } else if index == 2 {
210 let message = operation.recv(self.receivers.2).ok()?;
211 Some(Message::FromConstellation(message))
212 } else {
213 log::error!("No select operation registered for {index:?}");
214 None
215 }
216 }
217}
218
219pub struct PendingHandledInputEvent {
220 pub event_id: InputEventId,
221 pub webview_id: WebViewId,
222}
223
224struct ServoInner {
225 delegate: RefCell<Rc<dyn ServoDelegate>>,
226 paint: Rc<RefCell<Paint>>,
227 constellation_proxy: ConstellationProxy,
228 embedder_receiver: Receiver<EmbedderMsg>,
229 net_embedder_receiver: Receiver<NetToEmbedderMsg>,
230 constellation_embedder_receiver: Receiver<ConstellationToEmbedderMsg>,
231 network_manager: Rc<RefCell<NetworkManager>>,
232 site_data_manager: SiteDataManager,
233 javascript_evaluator: Rc<RefCell<JavaScriptEvaluator>>,
236 shutdown_state: Rc<Cell<ShutdownState>>,
239 webviews: RefCell<FxHashMap<WebViewId, Weak<RefCell<WebViewInner>>>>,
244 servo_errors: ServoErrorChannel,
245 _js_engine_setup: Option<JSEngineSetup>,
249 pending_handled_input_events: RefCell<Vec<PendingHandledInputEvent>>,
252 event_loop_waker: Box<dyn EventLoopWaker>,
254}
255
256impl ServoInner {
257 fn get_webview_handle(&self, id: WebViewId) -> Option<WebView> {
258 self.webviews
259 .borrow()
260 .get(&id)
261 .and_then(WebView::from_weak_handle)
262 }
263
264 #[servo_tracing::instrument(level = "debug", skip_all)]
265 fn spin_event_loop(&self) -> bool {
266 if self.shutdown_state.get() == ShutdownState::FinishedShuttingDown {
267 return false;
268 }
269
270 {
271 let paint = self.paint.borrow();
272 let mut messages = Vec::new();
273 while let Ok(message) = paint.receiver().try_recv() {
274 match message {
275 Ok(message) => messages.push(message),
276 Err(error) => {
277 warn!("Router deserialization error: {error}. Ignoring this PaintMessage.")
278 },
279 }
280 }
281 paint.handle_messages(messages);
282 }
283
284 let mut selector = EmbedderMessageSelector::new(
285 &self.embedder_receiver,
286 &self.net_embedder_receiver,
287 &self.constellation_embedder_receiver,
288 );
289 while let Some(message) = selector.try_recv_one_message() {
291 match message {
292 Message::FromUnknown(message) => self.handle_embedder_message(message),
293 Message::FromNet(message) => self.handle_net_embedder_message(message),
294 Message::FromConstellation(message) => {
295 self.handle_constellation_embedder_message(message)
296 },
297 }
298 if self.shutdown_state.get() == ShutdownState::FinishedShuttingDown {
299 break;
300 }
301 }
302 let pending_handled_input_events =
303 std::mem::take(&mut *self.pending_handled_input_events.borrow_mut());
304 for PendingHandledInputEvent {
305 event_id,
306 webview_id,
307 } in pending_handled_input_events
308 {
309 self.paint.borrow_mut().notify_input_event_handled(
310 webview_id,
311 event_id,
312 InputEventResult::DispatchFailed,
313 );
314 if let Some(webview) = self.get_webview_handle(webview_id) {
315 webview.delegate().notify_input_event_handled(
316 webview,
317 event_id,
318 InputEventResult::DispatchFailed,
319 );
320 }
321 }
322
323 if self.constellation_proxy.disconnected() {
324 self.delegate
325 .borrow()
326 .notify_error(ServoError::LostConnectionWithBackend);
327 }
328
329 self.paint.borrow_mut().perform_updates();
330 self.send_new_frame_ready_messages();
331 self.handle_delegate_errors();
332 self.clean_up_destroyed_webview_handles();
333
334 if self.shutdown_state.get() == ShutdownState::FinishedShuttingDown {
335 return false;
336 }
337
338 true
339 }
340
341 fn send_new_frame_ready_messages(&self) {
342 let webviews_needing_repaint = self.paint.borrow().webviews_needing_repaint();
343
344 for webview in webviews_needing_repaint
345 .iter()
346 .filter_map(|webview_id| self.get_webview_handle(*webview_id))
347 {
348 webview.delegate().notify_new_frame_ready(webview);
349 }
350 }
351
352 fn handle_delegate_errors(&self) {
353 while let Some(error) = self.servo_errors.try_recv() {
354 self.delegate.borrow().notify_error(error);
355 }
356 }
357
358 fn clean_up_destroyed_webview_handles(&self) {
359 self.webviews
364 .borrow_mut()
365 .retain(|_webview_id, webview| webview.strong_count() > 0);
366 }
367
368 fn finish_shutting_down(&self) {
369 debug!("Servo received message that Constellation shutdown is complete");
370 self.shutdown_state.set(ShutdownState::FinishedShuttingDown);
371 self.paint.borrow_mut().finish_shutting_down();
372 }
373
374 fn handle_net_embedder_message(&self, message: NetToEmbedderMsg) {
375 match message {
376 NetToEmbedderMsg::SelectFiles(control_id, file_picker_request, response_sender) => {
377 if file_picker_request.accept_current_paths_for_testing {
378 let _ = response_sender.send(Some(file_picker_request.current_paths));
379 return;
380 }
381 if let Some(webview) = self.get_webview_handle(control_id.webview_id) {
382 webview.delegate().show_embedder_control(
383 webview,
384 EmbedderControl::FilePicker(FilePicker {
385 id: control_id,
386 file_picker_request,
387 response_sender: Some(response_sender),
388 }),
389 );
390 }
391 },
392 NetToEmbedderMsg::WebResourceRequested(
393 webview_id,
394 web_resource_request,
395 response_sender,
396 ) => {
397 if let Some(webview) =
398 webview_id.and_then(|webview_id| self.get_webview_handle(webview_id))
399 {
400 let web_resource_load = WebResourceLoad::new(
401 web_resource_request,
402 response_sender,
403 self.servo_errors.sender(),
404 );
405 webview
406 .delegate()
407 .load_web_resource(webview, web_resource_load);
408 } else {
409 let web_resource_load = WebResourceLoad::new(
410 web_resource_request,
411 response_sender,
412 self.servo_errors.sender(),
413 );
414 self.delegate.borrow().load_web_resource(web_resource_load);
415 }
416 },
417 NetToEmbedderMsg::RequestAuthentication(
418 webview_id,
419 url,
420 for_proxy,
421 response_sender,
422 ) => {
423 if let Some(webview) = self.get_webview_handle(webview_id) {
424 let authentication_request = AuthenticationRequest::new(
425 url.into_url(),
426 for_proxy,
427 response_sender,
428 self.servo_errors.sender(),
429 );
430 webview
431 .delegate()
432 .request_authentication(webview, authentication_request);
433 }
434 },
435 NetToEmbedderMsg::EmbedderCookieOperationResponseWithCookies(operation_id, cookies) => {
436 self.site_data_manager.handle_cookie_response(
437 operation_id,
438 CookieOperationResponse::Cookies(cookies),
439 );
440 },
441 NetToEmbedderMsg::EmbedderCookieOperationResponse(operation_id) => {
442 self.site_data_manager
443 .handle_cookie_response(operation_id, CookieOperationResponse::Done);
444 },
445 }
446 }
447
448 fn handle_embedder_message(&self, message: EmbedderMsg) {
449 match message {
450 EmbedderMsg::Status(webview_id, status_text) => {
451 if let Some(webview) = self.get_webview_handle(webview_id) {
452 webview.set_status_text(status_text);
453 }
454 },
455 EmbedderMsg::ChangePageTitle(webview_id, title) => {
456 if let Some(webview) = self.get_webview_handle(webview_id) {
457 webview.set_page_title(title);
458 }
459 },
460 EmbedderMsg::MoveTo(webview_id, position) => {
461 if let Some(webview) = self.get_webview_handle(webview_id) {
462 webview.delegate().request_move_to(webview, position);
463 }
464 },
465 EmbedderMsg::ResizeTo(webview_id, size) => {
466 if let Some(webview) = self.get_webview_handle(webview_id) {
467 webview
468 .delegate()
469 .request_resize_to(webview, size.max(MINIMUM_WEBVIEW_SIZE));
470 }
471 },
472 EmbedderMsg::ShowSimpleDialog(webview_id, simple_dialog) => {
473 if let Some(webview) = self.get_webview_handle(webview_id) {
474 webview.delegate().show_embedder_control(
475 webview,
476 EmbedderControl::SimpleDialog(simple_dialog.into()),
477 );
478 }
479 },
480 EmbedderMsg::AllowProtocolHandlerRequest(
481 webview_id,
482 registration_update,
483 response_sender,
484 ) => {
485 if let Some(webview) = self.get_webview_handle(webview_id) {
486 let ProtocolHandlerUpdateRegistration {
487 scheme,
488 url,
489 register_or_unregister,
490 } = registration_update;
491 let protocol_handler_registration = ProtocolHandlerRegistration {
492 scheme,
493 url: url.into_url(),
494 register_or_unregister,
495 };
496 let allow_deny_request = AllowOrDenyRequest::new(
497 response_sender,
498 AllowOrDeny::Deny,
499 self.servo_errors.sender(),
500 );
501 webview.delegate().request_protocol_handler(
502 webview,
503 protocol_handler_registration,
504 allow_deny_request,
505 );
506 }
507 },
508 EmbedderMsg::AllowUnload(webview_id, response_sender) => {
509 if let Some(webview) = self.get_webview_handle(webview_id) {
510 let request = AllowOrDenyRequest::new(
511 response_sender,
512 AllowOrDeny::Allow,
513 self.servo_errors.sender(),
514 );
515 webview.delegate().request_unload(webview, request);
516 }
517 },
518 EmbedderMsg::InputEventsHandled(webview_id, event_outcomes) => {
519 let webview = self.get_webview_handle(webview_id);
520 for InputEventOutcome {
521 id: input_event_id,
522 result,
523 } in event_outcomes
524 {
525 self.paint.borrow_mut().notify_input_event_handled(
526 webview_id,
527 input_event_id,
528 result,
529 );
530 if let Some(ref webview) = webview {
531 webview.delegate().notify_input_event_handled(
532 webview.clone(),
533 input_event_id,
534 result,
535 );
536 }
537 }
538 },
539 EmbedderMsg::ClearClipboard(webview_id) => {
540 if let Some(webview) = self.get_webview_handle(webview_id) {
541 webview.clipboard_delegate().clear(webview);
542 }
543 },
544 EmbedderMsg::GetClipboardText(webview_id, result_sender) => {
545 if let Some(webview) = self.get_webview_handle(webview_id) {
546 webview
547 .clipboard_delegate()
548 .get_text(webview, StringRequest::from(result_sender));
549 }
550 },
551 EmbedderMsg::SetClipboardText(webview_id, string) => {
552 if let Some(webview) = self.get_webview_handle(webview_id) {
553 webview.clipboard_delegate().set_text(webview, string);
554 }
555 },
556 EmbedderMsg::SetCursor(webview_id, cursor) => {
557 if let Some(webview) = self.get_webview_handle(webview_id) {
558 webview.set_cursor(cursor);
559 }
560 },
561 EmbedderMsg::NewFavicon(webview_id, image) => {
562 if let Some(webview) = self.get_webview_handle(webview_id) {
563 webview.set_favicon(image);
564 }
565 },
566 EmbedderMsg::NotifyLoadStatusChanged(webview_id, load_status) => {
567 if let Some(webview) = self.get_webview_handle(webview_id) {
568 webview.set_load_status(load_status);
569 }
570 },
571 EmbedderMsg::NotifyFullscreenStateChanged(webview_id, fullscreen) => {
572 if let Some(webview) = self.get_webview_handle(webview_id) {
573 webview
574 .delegate()
575 .notify_fullscreen_state_changed(webview, fullscreen);
576 }
577 },
578 EmbedderMsg::GetSelectedBluetoothDevice(webview_id, items, response_sender) => {
579 if let Some(webview) = self.get_webview_handle(webview_id) {
580 webview.delegate().show_bluetooth_device_dialog(
581 webview,
582 BluetoothDeviceSelectionRequest::new(items, response_sender),
583 );
584 }
585 },
586 EmbedderMsg::PromptPermission(webview_id, requested_feature, response_sender) => {
587 if let Some(webview) = self.get_webview_handle(webview_id) {
588 let permission_request = PermissionRequest {
589 requested_feature,
590 allow_deny_request: AllowOrDenyRequest::new(
591 response_sender,
592 AllowOrDeny::Deny,
593 self.servo_errors.sender(),
594 ),
595 };
596 webview
597 .delegate()
598 .request_permission(webview, permission_request);
599 }
600 },
601 EmbedderMsg::RequestWakeLockPermission(webview_id, callback, type_) => {
602 if let Some(webview) = self.get_webview_handle(webview_id) {
603 let permission_request = PermissionRequest {
604 requested_feature: PermissionFeature::ScreenWakeLock(type_),
605 allow_deny_request: AllowOrDenyRequest::new_from_callback(
606 callback,
607 AllowOrDeny::Deny,
608 self.servo_errors.sender(),
609 ),
610 };
611 webview
612 .delegate()
613 .request_permission(webview, permission_request);
614 }
615 },
616 EmbedderMsg::OnDevtoolsStarted(port, token) => match port {
617 Ok(port) => self
618 .delegate
619 .borrow()
620 .notify_devtools_server_started(port, token),
621 Err(()) => self
622 .delegate
623 .borrow()
624 .notify_error(ServoError::DevtoolsFailedToStart),
625 },
626 EmbedderMsg::RequestDevtoolsConnection(response_sender) => {
627 self.delegate
628 .borrow()
629 .request_devtools_connection(AllowOrDenyRequest::new(
630 response_sender,
631 AllowOrDeny::Deny,
632 self.servo_errors.sender(),
633 ));
634 },
635 #[cfg(feature = "gamepad")]
636 EmbedderMsg::PlayGamepadHapticEffect(
637 webview_id,
638 gamepad_index,
639 gamepad_haptic_effect_type,
640 callback,
641 ) => {
642 if let Some(webview) = self.get_webview_handle(webview_id) {
643 let request = GamepadHapticEffectRequest::new(
644 gamepad_index,
645 GamepadHapticEffectRequestType::Play(gamepad_haptic_effect_type),
646 Box::new(move |success| {
647 callback
648 .send(success)
649 .expect("Could not send message via callback")
650 }),
651 );
652 webview
653 .gamepad_delegate()
654 .handle_haptic_effect_request(request);
655 }
656 },
657 #[cfg(feature = "gamepad")]
658 EmbedderMsg::StopGamepadHapticEffect(webview_id, gamepad_index, callback) => {
659 if let Some(webview) = self.get_webview_handle(webview_id) {
660 let request = GamepadHapticEffectRequest::new(
661 gamepad_index,
662 GamepadHapticEffectRequestType::Stop,
663 Box::new(move |success| {
664 callback
665 .send(success)
666 .expect("Could not send message via callback")
667 }),
668 );
669 webview
670 .gamepad_delegate()
671 .handle_haptic_effect_request(request);
672 }
673 },
674 EmbedderMsg::ShowNotification(webview_id, notification) => {
675 match webview_id.and_then(|webview_id| self.get_webview_handle(webview_id)) {
676 Some(webview) => webview.delegate().show_notification(webview, notification),
677 None => self.delegate.borrow().show_notification(notification),
678 }
679 },
680 EmbedderMsg::ShowConsoleApiMessage(webview_id, level, message) => {
681 match webview_id.and_then(|webview_id| self.get_webview_handle(webview_id)) {
682 Some(webview) => webview
683 .delegate()
684 .show_console_message(webview, level, message),
685 None => self.delegate.borrow().show_console_message(level, message),
686 }
687 },
688 EmbedderMsg::ShowEmbedderControl(control_id, position, embedder_control_request) => {
689 if let Some(webview) = self.get_webview_handle(control_id.webview_id) {
690 webview.show_embedder_control(control_id, position, embedder_control_request);
691 }
692 },
693 EmbedderMsg::HideEmbedderControl(control_id) => {
694 if let Some(webview) = self.get_webview_handle(control_id.webview_id) {
695 webview
696 .delegate()
697 .hide_embedder_control(webview, control_id);
698 }
699 },
700 EmbedderMsg::GetWindowRect(webview_id, response_sender) => {
701 let window_rect = || {
702 let Some(webview) = self.get_webview_handle(webview_id) else {
703 return DeviceIndependentIntRect::default();
704 };
705 let hidpi_scale_factor = webview.hidpi_scale_factor();
706 let Some(screen_geometry) = webview.delegate().screen_geometry(webview) else {
707 return DeviceIndependentIntRect::default();
708 };
709
710 convert_rect_to_css_pixel(screen_geometry.window_rect, hidpi_scale_factor)
711 };
712
713 if let Err(error) = response_sender.send(window_rect()) {
714 warn!("Failed to respond to GetWindowRect: {error}");
715 }
716 },
717 EmbedderMsg::GetScreenMetrics(webview_id, response_sender) => {
718 let screen_metrics = || {
719 let Some(webview) = self.get_webview_handle(webview_id) else {
720 return ScreenMetrics::default();
721 };
722 let hidpi_scale_factor = webview.hidpi_scale_factor();
723 let Some(screen_geometry) = webview.delegate().screen_geometry(webview) else {
724 return ScreenMetrics::default();
725 };
726
727 ScreenMetrics {
728 screen_size: convert_size_to_css_pixel(
729 screen_geometry.size,
730 hidpi_scale_factor,
731 ),
732 available_size: convert_size_to_css_pixel(
733 screen_geometry.available_size,
734 hidpi_scale_factor,
735 ),
736 }
737 };
738 if let Err(error) = response_sender.send(screen_metrics()) {
739 warn!("Failed to respond to GetScreenMetrics: {error}");
740 }
741 },
742 EmbedderMsg::AccessibilityTreeUpdate(webview_id, tree_update, epoch) => {
743 if let Some(webview) = self.get_webview_handle(webview_id) {
744 webview.process_accessibility_tree_update(tree_update, epoch);
745 }
746 },
747 }
748 }
749
750 fn handle_constellation_embedder_message(&self, message: ConstellationToEmbedderMsg) {
751 match message {
752 ConstellationToEmbedderMsg::ShutdownComplete => self.finish_shutting_down(),
753 ConstellationToEmbedderMsg::AllowNavigationRequest(
754 webview_id,
755 pipeline_id,
756 servo_url,
757 ) => {
758 if let Some(webview) = self.get_webview_handle(webview_id) {
759 let request = NavigationRequest {
760 url: servo_url.into_url(),
761 pipeline_id,
762 constellation_proxy: self.constellation_proxy.clone(),
763 response_sent: false,
764 };
765 webview.delegate().request_navigation(webview, request);
766 }
767 },
768 ConstellationToEmbedderMsg::AllowOpeningWebView(webview_id, response_sender) => {
769 if let Some(webview) = self.get_webview_handle(webview_id) {
770 webview.request_create_new(response_sender);
771 }
772 },
773 ConstellationToEmbedderMsg::WebViewClosed(webview_id) => {
774 if let Some(webview) = self.get_webview_handle(webview_id) {
775 webview.delegate().notify_closed(webview);
776 }
777 },
778 ConstellationToEmbedderMsg::WebViewFocused(webview_id, focus_result) => {
779 if focus_result {
780 for id in self.webviews.borrow().keys() {
781 if let Some(webview) = self.get_webview_handle(*id) {
782 let focused = webview.id() == webview_id;
783 webview.set_focused(focused);
784 }
785 }
786 }
787 },
788 ConstellationToEmbedderMsg::WebViewBlurred => {
789 for id in self.webviews.borrow().keys() {
790 if let Some(webview) = self.get_webview_handle(*id) {
791 webview.set_focused(false);
792 }
793 }
794 },
795 ConstellationToEmbedderMsg::FinishJavaScriptEvaluation(evaluation_id, result) => {
796 self.javascript_evaluator
797 .borrow_mut()
798 .finish_evaluation(evaluation_id, result);
799 },
800 ConstellationToEmbedderMsg::InputEventsHandled(webview_id, event_outcomes) => {
801 let webview = self.get_webview_handle(webview_id);
802 for InputEventOutcome {
803 id: input_event_id,
804 result,
805 } in event_outcomes
806 {
807 self.paint.borrow_mut().notify_input_event_handled(
808 webview_id,
809 input_event_id,
810 result,
811 );
812 if let Some(ref webview) = webview {
813 webview.delegate().notify_input_event_handled(
814 webview.clone(),
815 input_event_id,
816 result,
817 );
818 }
819 }
820 },
821 ConstellationToEmbedderMsg::HistoryTraversalComplete(webview_id, traversal_id) => {
822 if let Some(webview) = self.get_webview_handle(webview_id) {
823 webview
824 .delegate()
825 .notify_traversal_complete(webview.clone(), traversal_id);
826 }
827 },
828 ConstellationToEmbedderMsg::HistoryChanged(
829 webview_id,
830 new_back_forward_list,
831 current_list_index,
832 ) => {
833 if let Some(webview) = self.get_webview_handle(webview_id) {
834 webview.set_history(new_back_forward_list, current_list_index);
835 }
836 },
837 ConstellationToEmbedderMsg::Panic(webview_id, reason, backtrace) => {
838 if let Some(webview) = self.get_webview_handle(webview_id) {
839 webview
840 .delegate()
841 .notify_crashed(webview, reason, backtrace);
842 }
843 },
844 ConstellationToEmbedderMsg::ReportProfile(_items) => {},
845 ConstellationToEmbedderMsg::MediaSessionEvent(webview_id, media_session_event) => {
846 if let Some(webview) = self.get_webview_handle(webview_id) {
847 webview
848 .delegate()
849 .notify_media_session_event(webview, media_session_event);
850 }
851 },
852 }
853 }
854}
855
856impl Drop for ServoInner {
857 fn drop(&mut self) {
858 self.constellation_proxy
859 .send(EmbedderToConstellationMessage::Exit);
860 self.shutdown_state.set(ShutdownState::ShuttingDown);
861 while self.spin_event_loop() {
862 std::thread::sleep(Duration::from_micros(500));
863 }
864 }
865}
866
867#[derive(Clone)]
876pub struct Servo(Rc<ServoInner>);
877
878impl Servo {
879 #[servo_tracing::instrument(name = "Servo::new", skip(builder))]
880 fn new(builder: ServoBuilder) -> Self {
881 let opts = builder.opts.map(|opts| *opts);
883 opts::initialize_options(opts.unwrap_or_default());
884 let opts = opts::get();
885
886 let preferences = builder.preferences.map(|opts| *opts);
889 servo_config::prefs::set(preferences.unwrap_or_default());
890
891 use std::sync::atomic::Ordering;
892
893 style::context::DEFAULT_DISABLE_STYLE_SHARING_CACHE.store(
894 !pref!(layout_style_sharing_cache_enabled),
895 Ordering::Relaxed,
896 );
897 style::context::DEFAULT_DUMP_STYLE_STATISTICS.store(
898 opts.debug
899 .is_enabled(DiagnosticsLoggingOption::StyleStatistics),
900 Ordering::Relaxed,
901 );
902
903 if !opts.multiprocess {
904 media_platform::init();
905 }
906
907 PipelineNamespace::install(EMBEDDER_PIPELINE_NAMESPACE_ID);
909
910 let event_loop_waker = builder.event_loop_waker;
915 let (paint_proxy, paint_receiver) = create_paint_channel(event_loop_waker.clone());
916 let (constellation_proxy, embedder_to_constellation_receiver) = ConstellationProxy::new();
917 let (embedder_proxy, embedder_receiver) = create_embedder_channel(event_loop_waker.clone());
918 let (net_embedder_proxy, net_embedder_receiver) =
919 create_generic_embedder_channel::<NetToEmbedderMsg>(event_loop_waker.clone());
920 let (constellation_embedder_proxy, constellation_embedder_receiver) =
921 create_generic_embedder_channel::<ConstellationToEmbedderMsg>(event_loop_waker.clone());
922 let time_profiler_chan = profile_time::Profiler::create(
923 &opts.time_profiling,
924 opts.time_profiler_trace_path.clone(),
925 );
926 let mem_profiler_chan = profile_mem::Profiler::create();
927
928 let devtools_sender = if pref!(devtools_server_enabled) {
929 Some(devtools::start_server(
930 embedder_proxy.clone(),
931 mem_profiler_chan.clone(),
932 ))
933 } else {
934 None
935 };
936
937 let js_engine_setup = if !opts.multiprocess {
940 Some(script::init())
941 } else {
942 None
943 };
944
945 let mut protocols = ProtocolRegistry::with_internal_protocols();
948 protocols.merge(builder.protocol_registry);
949
950 let shutdown_state = Rc::new(Cell::new(ShutdownState::NotShuttingDown));
953 let paint = Paint::new(InitialPaintState {
954 paint_proxy: paint_proxy.clone(),
955 receiver: paint_receiver,
956 embedder_to_constellation_sender: constellation_proxy.sender(),
957 time_profiler_chan: time_profiler_chan.clone(),
958 mem_profiler_chan: mem_profiler_chan.clone(),
959 shutdown_state: shutdown_state.clone(),
960 event_loop_waker: event_loop_waker.clone(),
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 #[cfg(feature = "webxr")]
1130 pub fn register_webxr_registry(&self, registry: Box<dyn WebXrRegistry>) {
1132 self.0.paint.borrow().register_webxr_registry(registry);
1133 }
1134}
1135
1136fn create_embedder_channel(
1137 event_loop_waker: Box<dyn EventLoopWaker>,
1138) -> (EmbedderProxy, Receiver<EmbedderMsg>) {
1139 let (sender, receiver) = unbounded();
1140 (
1141 EmbedderProxy {
1142 sender,
1143 event_loop_waker,
1144 },
1145 receiver,
1146 )
1147}
1148
1149fn create_generic_embedder_channel<T>(
1150 event_loop_waker: Box<dyn EventLoopWaker>,
1151) -> (GenericEmbedderProxy<T>, Receiver<T>) {
1152 let (sender, receiver) = unbounded();
1153 (
1154 GenericEmbedderProxy {
1155 sender,
1156 event_loop_waker,
1157 },
1158 receiver,
1159 )
1160}
1161
1162fn create_paint_channel(
1163 event_loop_waker: Box<dyn EventLoopWaker>,
1164) -> (PaintProxy, RoutedReceiver<PaintMessage>) {
1165 let (sender, receiver) = unbounded();
1166 let sender_clone = sender.clone();
1167 let event_loop_waker_clone = event_loop_waker.clone();
1168 let result_callback = move |msg: Result<PaintMessage, ipc_channel::IpcError>| {
1170 if let Err(err) = sender_clone.send(msg) {
1171 warn!("Failed to send response ({:?}).", err);
1172 }
1173 event_loop_waker_clone.wake();
1174 };
1175
1176 let generic_callback =
1177 GenericCallback::new(result_callback).expect("Failed to create callback");
1178 let cross_process_paint_api = CrossProcessPaintApi::new(generic_callback);
1179 let paint_proxy = PaintProxy {
1180 sender,
1181 cross_process_paint_api,
1182 event_loop_waker,
1183 };
1184
1185 (paint_proxy, receiver)
1186}
1187
1188#[expect(clippy::too_many_arguments)]
1189fn create_constellation(
1190 embedder_to_constellation_receiver: Receiver<EmbedderToConstellationMessage>,
1191 paint: &Paint,
1192 embedder_proxy: EmbedderProxy,
1193 constellation_to_embedder_proxy: GenericEmbedderProxy<ConstellationToEmbedderMsg>,
1194 paint_proxy: PaintProxy,
1195 time_profiler_chan: time::ProfilerChan,
1196 mem_profiler_chan: mem::ProfilerChan,
1197 devtools_sender: Option<Sender<devtools_traits::DevtoolsControlMsg>>,
1198 protocols: Arc<ProtocolRegistry>,
1199 public_resource_threads: ResourceThreads,
1200 private_resource_threads: ResourceThreads,
1201 async_runtime: Box<dyn net_traits::AsyncRuntime>,
1202 public_storage_threads: StorageThreads,
1203 private_storage_threads: StorageThreads,
1204) {
1205 let opts = opts::get();
1207
1208 #[cfg(feature = "bluetooth")]
1209 let bluetooth_thread: GenericSender<BluetoothRequest> =
1210 BluetoothThreadFactory::new(embedder_proxy.clone());
1211
1212 let privileged_urls = protocols.privileged_urls();
1213
1214 let system_font_service = Arc::new(
1215 SystemFontService::spawn(
1216 paint_proxy.cross_process_paint_api.clone(),
1217 mem_profiler_chan.clone(),
1218 )
1219 .to_proxy(),
1220 );
1221
1222 let initial_state = InitialConstellationState {
1223 paint_proxy,
1224 embedder_proxy,
1225 constellation_to_embedder_proxy,
1226 devtools_sender,
1227 #[cfg(feature = "bluetooth")]
1228 bluetooth_thread,
1229 system_font_service,
1230 public_resource_threads,
1231 private_resource_threads,
1232 public_storage_threads,
1233 private_storage_threads,
1234 time_profiler_chan,
1235 mem_profiler_chan,
1236 #[cfg(feature = "webxr")]
1237 webxr_registry: Some(paint.webxr_main_thread_registry()),
1238 #[cfg(not(feature = "webxr"))]
1239 webxr_registry: None,
1240 webgl_threads: Some(paint.webgl_threads()),
1241 webrender_external_image_id_manager: paint.webrender_external_image_id_manager(),
1242 #[cfg(feature = "webgpu")]
1243 wgpu_image_map: paint.webgpu_image_map(),
1244 async_runtime,
1245 privileged_urls,
1246 wake_lock_provider: Box::new(DefaultWakeLockDelegate),
1247 };
1248
1249 let layout_factory = Arc::new(LayoutFactoryImpl());
1250
1251 Constellation::<script::ScriptThread, script::ServiceWorkerManager>::start(
1252 embedder_to_constellation_receiver,
1253 initial_state,
1254 layout_factory,
1255 opts.random_pipeline_closure_probability,
1256 opts.random_pipeline_closure_seed,
1257 opts.hard_fail,
1258 );
1259}
1260
1261struct BothLogger<Log1, Log2>(Log1, Log2);
1264
1265impl<Log1, Log2> Log for BothLogger<Log1, Log2>
1266where
1267 Log1: Log,
1268 Log2: Log,
1269{
1270 fn enabled(&self, metadata: &Metadata) -> bool {
1271 self.0.enabled(metadata) || self.1.enabled(metadata)
1272 }
1273
1274 fn log(&self, record: &Record) {
1275 self.0.log(record);
1276 self.1.log(record);
1277 }
1278
1279 fn flush(&self) {
1280 self.0.flush();
1281 self.1.flush();
1282 }
1283}
1284
1285fn set_logger(script_to_constellation_sender: ScriptToConstellationSender) {
1286 let con_logger = FromScriptLogger::new(script_to_constellation_sender);
1287 let env = env_logger::Env::default();
1288 let env_logger = EnvLoggerBuilder::from_env(env).build();
1289
1290 let filter = max(env_logger.filter(), con_logger.filter());
1291 let logger = BothLogger(env_logger, con_logger);
1292
1293 log::set_boxed_logger(Box::new(logger)).expect("Failed to set logger.");
1294 log::set_max_level(filter);
1295}
1296
1297pub fn run_content_process(token: String) {
1299 let (unprivileged_content_sender, unprivileged_content_receiver) =
1300 ipc::channel::<UnprivilegedContent>().unwrap();
1301 let connection_bootstrap: IpcSender<IpcSender<UnprivilegedContent>> =
1302 IpcSender::connect(token).unwrap();
1303 connection_bootstrap
1304 .send(unprivileged_content_sender)
1305 .unwrap();
1306
1307 let unprivileged_content = unprivileged_content_receiver.recv().unwrap();
1308 opts::initialize_options(unprivileged_content.opts());
1309 prefs::set(unprivileged_content.prefs().clone());
1310
1311 if opts::get().sandbox {
1313 create_sandbox();
1314 }
1315
1316 let _js_engine_setup = script::init();
1317
1318 match unprivileged_content {
1319 UnprivilegedContent::ScriptEventLoop(new_event_loop_info) => {
1320 media_platform::init();
1321
1322 set_logger(
1323 new_event_loop_info
1324 .initial_script_state
1325 .script_to_constellation_sender
1326 .clone(),
1327 );
1328
1329 register_system_memory_reporter_for_event_loop(&new_event_loop_info);
1330
1331 let (background_hang_monitor_register, background_hang_monitor_join_handle) =
1332 HangMonitorRegister::init(
1333 new_event_loop_info.bhm_to_constellation_sender.clone(),
1334 new_event_loop_info.constellation_to_bhm_receiver,
1335 opts::get().background_hang_monitor,
1336 );
1337
1338 let layout_factory = Arc::new(LayoutFactoryImpl());
1339 let script_join_handle = script::ScriptThread::create(
1340 new_event_loop_info.initial_script_state,
1341 layout_factory,
1342 Arc::new(ImageCacheFactoryImpl::new(
1343 new_event_loop_info.broken_image_icon_data,
1344 )),
1345 background_hang_monitor_register,
1346 );
1347
1348 script_join_handle
1349 .join()
1350 .expect("Failed to join on the script thread.");
1351 background_hang_monitor_join_handle
1352 .join()
1353 .expect("Failed to join on the BHM background thread.");
1354
1355 StyleThreadPool::shutdown();
1356
1357 FetchThread::exit();
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
1406pub struct ServoBuilder {
1408 opts: Option<Box<Opts>>,
1409 preferences: Option<Box<Preferences>>,
1410 event_loop_waker: Box<dyn EventLoopWaker>,
1411 protocol_registry: ProtocolRegistry,
1412}
1413
1414impl Default for ServoBuilder {
1415 fn default() -> Self {
1416 Self {
1417 opts: Default::default(),
1418 preferences: Default::default(),
1419 event_loop_waker: Box::new(DefaultEventLoopWaker),
1420 protocol_registry: Default::default(),
1421 }
1422 }
1423}
1424
1425impl ServoBuilder {
1426 pub fn build(self) -> Servo {
1427 Servo::new(self)
1428 }
1429
1430 pub fn opts(mut self, opts: Opts) -> Self {
1431 self.opts = Some(Box::new(opts));
1432 self
1433 }
1434
1435 pub fn preferences(mut self, preferences: Preferences) -> Self {
1436 self.preferences = Some(Box::new(preferences));
1437 self
1438 }
1439
1440 pub fn event_loop_waker(mut self, event_loop_waker: Box<dyn EventLoopWaker>) -> Self {
1441 self.event_loop_waker = event_loop_waker;
1442 self
1443 }
1444
1445 pub fn protocol_registry(mut self, protocol_registry: ProtocolRegistry) -> Self {
1446 self.protocol_registry = protocol_registry;
1447 self
1448 }
1449}
1450
1451fn register_system_memory_reporter_for_event_loop(
1452 new_event_loop_info: &NewScriptEventLoopProcessInfo,
1453) {
1454 let callback = GenericCallback::new(|message| {
1458 if let Ok(request) = message {
1459 system_reporter::collect_reports(request);
1460 }
1461 })
1462 .expect("Could not create memory reporter callback");
1463 new_event_loop_info
1464 .initial_script_state
1465 .memory_profiler_sender
1466 .send(ProfilerMsg::RegisterReporter(
1467 format!("system-content-{}", std::process::id()),
1468 Reporter(callback),
1469 ));
1470}