Skip to main content

servo/
servo.rs

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