Skip to main content

servo/
servo.rs

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