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