Skip to main content

servo/
servo.rs

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