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