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