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