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