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