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_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::{DiagnosticsLoggingOption, 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::DefaultWakeLockDelegate;
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.store(
856            opts.debug
857                .is_enabled(DiagnosticsLoggingOption::StyleStatistics),
858            Ordering::Relaxed,
859        );
860
861        if !opts.multiprocess {
862            media_platform::init();
863        }
864
865        // Reserving a namespace to create WebViewId.
866        PipelineNamespace::install(EMBEDDER_PIPELINE_NAMESPACE_ID);
867
868        // Get both endpoints of a special channel for communication between
869        // the client window and `Paint`. This channel is unique because
870        // messages to client may need to pump a platform-specific event loop
871        // to deliver the message.
872        let event_loop_waker = builder.event_loop_waker;
873        let (paint_proxy, paint_receiver) = create_paint_channel(event_loop_waker.clone());
874        let (constellation_proxy, embedder_to_constellation_receiver) = ConstellationProxy::new();
875        let (embedder_proxy, embedder_receiver) = create_embedder_channel(event_loop_waker.clone());
876        let (net_embedder_proxy, net_embedder_receiver) =
877            create_generic_embedder_channel::<NetToEmbedderMsg>(event_loop_waker.clone());
878        let (constellation_embedder_proxy, constellation_embedder_receiver) =
879            create_generic_embedder_channel::<ConstellationToEmbedderMsg>(event_loop_waker.clone());
880        let time_profiler_chan = profile_time::Profiler::create(
881            &opts.time_profiling,
882            opts.time_profiler_trace_path.clone(),
883        );
884        let mem_profiler_chan = profile_mem::Profiler::create();
885
886        let devtools_sender = if pref!(devtools_server_enabled) {
887            Some(devtools::start_server(
888                embedder_proxy.clone(),
889                mem_profiler_chan.clone(),
890            ))
891        } else {
892            None
893        };
894
895        // Important that this call is done in a single-threaded fashion, we
896        // can't defer it after `create_constellation` has started.
897        let js_engine_setup = if !opts.multiprocess {
898            Some(script::init())
899        } else {
900            None
901        };
902
903        // Create the constellation, which maintains the engine pipelines, including script and
904        // layout, as well as the navigation context.
905        let mut protocols = ProtocolRegistry::with_internal_protocols();
906        protocols.merge(builder.protocol_registry);
907
908        // The `Paint` coordinates with the client window to create the final
909        // rendered page and display it somewhere.
910        let shutdown_state = Rc::new(Cell::new(ShutdownState::NotShuttingDown));
911        let paint = Paint::new(InitialPaintState {
912            paint_proxy: paint_proxy.clone(),
913            receiver: paint_receiver,
914            embedder_to_constellation_sender: constellation_proxy.sender(),
915            time_profiler_chan: time_profiler_chan.clone(),
916            mem_profiler_chan: mem_profiler_chan.clone(),
917            shutdown_state: shutdown_state.clone(),
918            event_loop_waker: event_loop_waker.clone(),
919            #[cfg(feature = "webxr")]
920            webxr_registry: builder.webxr_registry,
921        });
922
923        let protocols = Arc::new(protocols);
924        let (public_resource_threads, private_resource_threads, async_runtime) =
925            new_resource_threads(
926                devtools_sender.clone(),
927                time_profiler_chan.clone(),
928                mem_profiler_chan.clone(),
929                net_embedder_proxy,
930                opts.config_dir.clone(),
931                opts.certificate_path.clone(),
932                opts.ignore_certificate_errors,
933                protocols.clone(),
934            );
935
936        let (private_storage_threads, public_storage_threads) = new_storage_threads(
937            mem_profiler_chan.clone(),
938            opts.config_dir.clone(),
939            opts.temporary_storage,
940        );
941
942        create_constellation(
943            embedder_to_constellation_receiver,
944            &paint.borrow(),
945            embedder_proxy,
946            constellation_embedder_proxy,
947            paint_proxy,
948            time_profiler_chan,
949            mem_profiler_chan,
950            devtools_sender,
951            protocols,
952            public_resource_threads.clone(),
953            private_resource_threads.clone(),
954            async_runtime,
955            public_storage_threads.clone(),
956            private_storage_threads.clone(),
957        );
958
959        net::connector::prewarm_tls();
960
961        if opts::get().multiprocess {
962            prefs::add_observer(Box::new(constellation_proxy.clone()));
963        }
964
965        Servo(Rc::new(ServoInner {
966            delegate: RefCell::new(Rc::new(DefaultServoDelegate)),
967            paint,
968            network_manager: Rc::new(RefCell::new(NetworkManager::new(
969                public_resource_threads.clone(),
970                private_resource_threads.clone(),
971            ))),
972            site_data_manager: SiteDataManager::new(
973                public_resource_threads,
974                private_resource_threads,
975                public_storage_threads,
976                private_storage_threads,
977            ),
978            javascript_evaluator: Rc::new(RefCell::new(JavaScriptEvaluator::new(
979                constellation_proxy.clone(),
980            ))),
981            constellation_proxy,
982            embedder_receiver,
983            net_embedder_receiver,
984            constellation_embedder_receiver,
985            shutdown_state,
986            webviews: Default::default(),
987            servo_errors: ServoErrorChannel::default(),
988            _js_engine_setup: js_engine_setup,
989            pending_handled_input_events: Default::default(),
990            event_loop_waker,
991        }))
992    }
993
994    pub fn delegate(&self) -> Rc<dyn ServoDelegate> {
995        self.0.delegate.borrow().clone()
996    }
997
998    pub fn set_delegate(&self, delegate: Rc<dyn ServoDelegate>) {
999        *self.0.delegate.borrow_mut() = delegate;
1000    }
1001
1002    /// **EXPERIMENTAL:** Intialize GL accelerated media playback. This currently only works on a limited number
1003    /// of platforms. This should be run *before* creating [`Servo`] and its first [`WebView`].
1004    pub fn initialize_gl_accelerated_media(display: NativeDisplay, api: GlApi, context: GlContext) {
1005        WindowGLContext::initialize(display, api, context)
1006    }
1007
1008    /// Spin the Servo event loop, which:
1009    ///
1010    ///   - Performs updates in `Paint`, such as queued pinch zoom events
1011    ///   - Runs delegate methods on all `WebView`s and `Servo` itself
1012    ///   - Maybe update the rendered `Paint` output, but *without* swapping buffers.
1013    pub fn spin_event_loop(&self) {
1014        self.0.spin_event_loop();
1015    }
1016
1017    pub fn setup_logging(&self) {
1018        let constellation_chan = self.0.constellation_proxy.sender();
1019        let env = env_logger::Env::default();
1020        let env_logger = EnvLoggerBuilder::from_env(env).build();
1021        let con_logger = FromEmbedderLogger::new(constellation_chan);
1022
1023        let filter = max(env_logger.filter(), con_logger.filter());
1024        let logger = BothLogger(env_logger, con_logger);
1025
1026        log::set_boxed_logger(Box::new(logger)).expect("Failed to set logger.");
1027        log::set_max_level(filter);
1028    }
1029
1030    pub fn create_memory_report(&self, snd: GenericCallback<MemoryReportResult>) {
1031        self.0
1032            .constellation_proxy
1033            .send(EmbedderToConstellationMessage::CreateMemoryReport(snd));
1034    }
1035
1036    pub fn execute_webdriver_command(&self, command: WebDriverCommandMsg) {
1037        self.0
1038            .constellation_proxy
1039            .send(EmbedderToConstellationMessage::WebDriverCommand(command));
1040    }
1041
1042    pub fn set_preference(&self, name: &str, value: PrefValue) {
1043        let mut preferences = prefs::get().clone();
1044        preferences.set_value(name, value);
1045        prefs::set(preferences);
1046    }
1047
1048    pub fn network_manager<'a>(&'a self) -> Ref<'a, NetworkManager> {
1049        self.0.network_manager.borrow()
1050    }
1051
1052    pub fn site_data_manager(&self) -> &SiteDataManager {
1053        &self.0.site_data_manager
1054    }
1055
1056    pub(crate) fn paint<'a>(&'a self) -> Ref<'a, Paint> {
1057        self.0.paint.borrow()
1058    }
1059
1060    pub(crate) fn paint_mut<'a>(&'a self) -> RefMut<'a, Paint> {
1061        self.0.paint.borrow_mut()
1062    }
1063
1064    pub(crate) fn event_loop_waker(&self) -> &dyn EventLoopWaker {
1065        &*self.0.event_loop_waker
1066    }
1067
1068    pub(crate) fn webviews_mut<'a>(
1069        &'a self,
1070    ) -> RefMut<'a, FxHashMap<WebViewId, Weak<RefCell<WebViewInner>>>> {
1071        self.0.webviews.borrow_mut()
1072    }
1073
1074    pub(crate) fn constellation_proxy(&self) -> &ConstellationProxy {
1075        &self.0.constellation_proxy
1076    }
1077
1078    pub(crate) fn javascript_evaluator_mut<'a>(&'a self) -> RefMut<'a, JavaScriptEvaluator> {
1079        self.0.javascript_evaluator.borrow_mut()
1080    }
1081
1082    pub(crate) fn add_pending_handled_input_event(&self, residue_event: PendingHandledInputEvent) {
1083        self.0
1084            .pending_handled_input_events
1085            .borrow_mut()
1086            .push(residue_event);
1087    }
1088}
1089
1090fn create_embedder_channel(
1091    event_loop_waker: Box<dyn EventLoopWaker>,
1092) -> (EmbedderProxy, Receiver<EmbedderMsg>) {
1093    let (sender, receiver) = unbounded();
1094    (
1095        EmbedderProxy {
1096            sender,
1097            event_loop_waker,
1098        },
1099        receiver,
1100    )
1101}
1102
1103fn create_generic_embedder_channel<T>(
1104    event_loop_waker: Box<dyn EventLoopWaker>,
1105) -> (GenericEmbedderProxy<T>, Receiver<T>) {
1106    let (sender, receiver) = unbounded();
1107    (
1108        GenericEmbedderProxy {
1109            sender,
1110            event_loop_waker,
1111        },
1112        receiver,
1113    )
1114}
1115
1116fn create_paint_channel(
1117    event_loop_waker: Box<dyn EventLoopWaker>,
1118) -> (PaintProxy, RoutedReceiver<PaintMessage>) {
1119    let (sender, receiver) = unbounded();
1120    let sender_clone = sender.clone();
1121    let event_loop_waker_clone = event_loop_waker.clone();
1122    // This callback is equivalent to `PaintProxy::send`
1123    let result_callback = move |msg: Result<PaintMessage, ipc_channel::IpcError>| {
1124        if let Err(err) = sender_clone.send(msg) {
1125            warn!("Failed to send response ({:?}).", err);
1126        }
1127        event_loop_waker_clone.wake();
1128    };
1129
1130    let generic_callback =
1131        GenericCallback::new(result_callback).expect("Failed to create callback");
1132    let cross_process_paint_api = CrossProcessPaintApi::new(generic_callback);
1133    let paint_proxy = PaintProxy {
1134        sender,
1135        cross_process_paint_api,
1136        event_loop_waker,
1137    };
1138
1139    (paint_proxy, receiver)
1140}
1141
1142#[expect(clippy::too_many_arguments)]
1143fn create_constellation(
1144    embedder_to_constellation_receiver: Receiver<EmbedderToConstellationMessage>,
1145    paint: &Paint,
1146    embedder_proxy: EmbedderProxy,
1147    constellation_to_embedder_proxy: GenericEmbedderProxy<ConstellationToEmbedderMsg>,
1148    paint_proxy: PaintProxy,
1149    time_profiler_chan: time::ProfilerChan,
1150    mem_profiler_chan: mem::ProfilerChan,
1151    devtools_sender: Option<Sender<devtools_traits::DevtoolsControlMsg>>,
1152    protocols: Arc<ProtocolRegistry>,
1153    public_resource_threads: ResourceThreads,
1154    private_resource_threads: ResourceThreads,
1155    async_runtime: Box<dyn net_traits::AsyncRuntime>,
1156    public_storage_threads: StorageThreads,
1157    private_storage_threads: StorageThreads,
1158) {
1159    // Global configuration options, parsed from the command line.
1160    let opts = opts::get();
1161
1162    #[cfg(feature = "bluetooth")]
1163    let bluetooth_thread: GenericSender<BluetoothRequest> =
1164        BluetoothThreadFactory::new(embedder_proxy.clone());
1165
1166    let privileged_urls = protocols.privileged_urls();
1167
1168    let system_font_service = Arc::new(
1169        SystemFontService::spawn(
1170            paint_proxy.cross_process_paint_api.clone(),
1171            mem_profiler_chan.clone(),
1172        )
1173        .to_proxy(),
1174    );
1175
1176    let initial_state = InitialConstellationState {
1177        paint_proxy,
1178        embedder_proxy,
1179        constellation_to_embedder_proxy,
1180        devtools_sender,
1181        #[cfg(feature = "bluetooth")]
1182        bluetooth_thread,
1183        system_font_service,
1184        public_resource_threads,
1185        private_resource_threads,
1186        public_storage_threads,
1187        private_storage_threads,
1188        time_profiler_chan,
1189        mem_profiler_chan,
1190        #[cfg(feature = "webxr")]
1191        webxr_registry: Some(paint.webxr_main_thread_registry()),
1192        #[cfg(not(feature = "webxr"))]
1193        webxr_registry: None,
1194        webgl_threads: Some(paint.webgl_threads()),
1195        webrender_external_image_id_manager: paint.webrender_external_image_id_manager(),
1196        #[cfg(feature = "webgpu")]
1197        wgpu_image_map: paint.webgpu_image_map(),
1198        async_runtime,
1199        privileged_urls,
1200        wake_lock_provider: Box::new(DefaultWakeLockDelegate),
1201    };
1202
1203    let layout_factory = Arc::new(LayoutFactoryImpl());
1204
1205    Constellation::<script::ScriptThread, script::ServiceWorkerManager>::start(
1206        embedder_to_constellation_receiver,
1207        initial_state,
1208        layout_factory,
1209        opts.random_pipeline_closure_probability,
1210        opts.random_pipeline_closure_seed,
1211        opts.hard_fail,
1212    );
1213}
1214
1215// A logger that logs to two downstream loggers.
1216// This should probably be in the log crate.
1217struct BothLogger<Log1, Log2>(Log1, Log2);
1218
1219impl<Log1, Log2> Log for BothLogger<Log1, Log2>
1220where
1221    Log1: Log,
1222    Log2: Log,
1223{
1224    fn enabled(&self, metadata: &Metadata) -> bool {
1225        self.0.enabled(metadata) || self.1.enabled(metadata)
1226    }
1227
1228    fn log(&self, record: &Record) {
1229        self.0.log(record);
1230        self.1.log(record);
1231    }
1232
1233    fn flush(&self) {
1234        self.0.flush();
1235        self.1.flush();
1236    }
1237}
1238
1239fn set_logger(script_to_constellation_sender: ScriptToConstellationSender) {
1240    let con_logger = FromScriptLogger::new(script_to_constellation_sender);
1241    let env = env_logger::Env::default();
1242    let env_logger = EnvLoggerBuilder::from_env(env).build();
1243
1244    let filter = max(env_logger.filter(), con_logger.filter());
1245    let logger = BothLogger(env_logger, con_logger);
1246
1247    log::set_boxed_logger(Box::new(logger)).expect("Failed to set logger.");
1248    log::set_max_level(filter);
1249}
1250
1251/// Content process entry point.
1252pub fn run_content_process(token: String) {
1253    let (unprivileged_content_sender, unprivileged_content_receiver) =
1254        ipc::channel::<UnprivilegedContent>().unwrap();
1255    let connection_bootstrap: IpcSender<IpcSender<UnprivilegedContent>> =
1256        IpcSender::connect(token).unwrap();
1257    connection_bootstrap
1258        .send(unprivileged_content_sender)
1259        .unwrap();
1260
1261    let unprivileged_content = unprivileged_content_receiver.recv().unwrap();
1262    opts::initialize_options(unprivileged_content.opts());
1263    prefs::set(unprivileged_content.prefs().clone());
1264
1265    // Enter the sandbox if necessary.
1266    if opts::get().sandbox {
1267        create_sandbox();
1268    }
1269
1270    let _js_engine_setup = script::init();
1271
1272    match unprivileged_content {
1273        UnprivilegedContent::ScriptEventLoop(new_event_loop_info) => {
1274            media_platform::init();
1275
1276            // Start the fetch thread for this content process.
1277            let fetch_thread_join_handle = start_fetch_thread();
1278
1279            set_logger(
1280                new_event_loop_info
1281                    .initial_script_state
1282                    .script_to_constellation_sender
1283                    .clone(),
1284            );
1285
1286            register_system_memory_reporter_for_event_loop(&new_event_loop_info);
1287
1288            let (background_hang_monitor_register, background_hang_monitor_join_handle) =
1289                HangMonitorRegister::init(
1290                    new_event_loop_info.bhm_to_constellation_sender.clone(),
1291                    new_event_loop_info.constellation_to_bhm_receiver,
1292                    opts::get().background_hang_monitor,
1293                );
1294
1295            let layout_factory = Arc::new(LayoutFactoryImpl());
1296            let script_join_handle = script::ScriptThread::create(
1297                new_event_loop_info.initial_script_state,
1298                layout_factory,
1299                Arc::new(ImageCacheFactoryImpl::new(
1300                    new_event_loop_info.broken_image_icon_data,
1301                )),
1302                background_hang_monitor_register,
1303            );
1304
1305            script_join_handle
1306                .join()
1307                .expect("Failed to join on the script thread.");
1308            background_hang_monitor_join_handle
1309                .join()
1310                .expect("Failed to join on the BHM background thread.");
1311
1312            StyleThreadPool::shutdown();
1313
1314            // Shut down the fetch thread started above.
1315            exit_fetch_thread();
1316            fetch_thread_join_handle
1317                .join()
1318                .expect("Failed to join on the fetch thread in the constellation");
1319        },
1320        UnprivilegedContent::ServiceWorker(content) => {
1321            content.start::<ServiceWorkerManager>();
1322        },
1323    }
1324}
1325
1326#[cfg(all(
1327    not(target_os = "windows"),
1328    not(target_os = "ios"),
1329    not(target_os = "android"),
1330    not(target_arch = "arm"),
1331    not(target_arch = "aarch64"),
1332    not(target_env = "ohos"),
1333))]
1334fn create_sandbox() {
1335    ChildSandbox::new(content_process_sandbox_profile())
1336        .activate()
1337        .expect("Failed to activate sandbox!");
1338}
1339
1340#[cfg(any(
1341    target_os = "windows",
1342    target_os = "ios",
1343    target_os = "android",
1344    target_arch = "arm",
1345    target_arch = "aarch64",
1346    target_env = "ohos",
1347))]
1348fn create_sandbox() {
1349    panic!("Sandboxing is not supported on Windows, iOS, ARM targets and android.");
1350}
1351
1352struct DefaultEventLoopWaker;
1353
1354impl EventLoopWaker for DefaultEventLoopWaker {
1355    fn clone_box(&self) -> Box<dyn EventLoopWaker> {
1356        Box::new(DefaultEventLoopWaker)
1357    }
1358
1359    fn wake(&self) {}
1360}
1361
1362#[cfg(feature = "webxr")]
1363struct DefaultWebXrRegistry;
1364#[cfg(feature = "webxr")]
1365impl webxr::WebXrRegistry for DefaultWebXrRegistry {}
1366
1367/// Builder for [`Servo`].
1368pub struct ServoBuilder {
1369    opts: Option<Box<Opts>>,
1370    preferences: Option<Box<Preferences>>,
1371    event_loop_waker: Box<dyn EventLoopWaker>,
1372    protocol_registry: ProtocolRegistry,
1373    #[cfg(feature = "webxr")]
1374    webxr_registry: Box<dyn webxr::WebXrRegistry>,
1375}
1376
1377impl Default for ServoBuilder {
1378    fn default() -> Self {
1379        Self {
1380            opts: Default::default(),
1381            preferences: Default::default(),
1382            event_loop_waker: Box::new(DefaultEventLoopWaker),
1383            protocol_registry: Default::default(),
1384            #[cfg(feature = "webxr")]
1385            webxr_registry: Box::new(DefaultWebXrRegistry),
1386        }
1387    }
1388}
1389
1390impl ServoBuilder {
1391    pub fn build(self) -> Servo {
1392        Servo::new(self)
1393    }
1394
1395    pub fn opts(mut self, opts: Opts) -> Self {
1396        self.opts = Some(Box::new(opts));
1397        self
1398    }
1399
1400    pub fn preferences(mut self, preferences: Preferences) -> Self {
1401        self.preferences = Some(Box::new(preferences));
1402        self
1403    }
1404
1405    pub fn event_loop_waker(mut self, event_loop_waker: Box<dyn EventLoopWaker>) -> Self {
1406        self.event_loop_waker = event_loop_waker;
1407        self
1408    }
1409
1410    pub fn protocol_registry(mut self, protocol_registry: ProtocolRegistry) -> Self {
1411        self.protocol_registry = protocol_registry;
1412        self
1413    }
1414
1415    #[cfg(feature = "webxr")]
1416    pub fn webxr_registry(mut self, webxr_registry: Box<dyn webxr::WebXrRegistry>) -> Self {
1417        self.webxr_registry = webxr_registry;
1418        self
1419    }
1420}
1421
1422fn register_system_memory_reporter_for_event_loop(
1423    new_event_loop_info: &NewScriptEventLoopProcessInfo,
1424) {
1425    // Register the system memory reporter, which will run on its own thread. It never needs to
1426    // be unregistered, because as long as the memory profiler is running the system memory
1427    // reporter can make measurements.
1428    let callback = GenericCallback::new(|message| {
1429        if let Ok(request) = message {
1430            system_reporter::collect_reports(request);
1431        }
1432    })
1433    .expect("Could not create memory reporter callback");
1434    new_event_loop_info
1435        .initial_script_state
1436        .memory_profiler_sender
1437        .send(ProfilerMsg::RegisterReporter(
1438            format!("system-content-{}", std::process::id()),
1439            Reporter(callback),
1440        ));
1441}