servo/
lib.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
5//! Servo, the mighty web browser engine from the future.
6//!
7//! This is a very simple library that wires all of Servo's components
8//! together as type `Servo`, along with a generic client
9//! implementing the `WindowMethods` trait, to create a working web
10//! browser.
11//!
12//! The `Servo` type is responsible for configuring a
13//! `Constellation`, which does the heavy lifting of coordinating all
14//! of Servo's internal subsystems, including the `ScriptThread` and the
15//! `LayoutThread`, as well maintains the navigation context.
16//!
17//! `Servo` is fed events from a generic type that implements the
18//! `WindowMethods` trait.
19
20mod clipboard_delegate;
21mod javascript_evaluator;
22mod proxies;
23mod responders;
24mod servo_delegate;
25mod webview;
26mod webview_delegate;
27
28use std::cell::{Cell, RefCell};
29use std::cmp::max;
30use std::path::PathBuf;
31use std::rc::{Rc, Weak};
32use std::sync::{Arc, Mutex};
33use std::thread;
34
35use base::generic_channel::{GenericCallback, RoutedReceiver};
36pub use base::id::WebViewId;
37use base::id::{PipelineNamespace, PipelineNamespaceId};
38#[cfg(feature = "bluetooth")]
39use bluetooth::BluetoothThreadFactory;
40#[cfg(feature = "bluetooth")]
41use bluetooth_traits::BluetoothRequest;
42use canvas_traits::webgl::{GlType, WebGLThreads};
43use clipboard_delegate::StringRequest;
44pub use compositing::WebRenderDebugOption;
45use compositing::{IOCompositor, InitialCompositorState};
46pub use compositing_traits::rendering_context::{
47    OffscreenRenderingContext, RenderingContext, SoftwareRenderingContext, WindowRenderingContext,
48};
49use compositing_traits::{
50    CompositorMsg, CompositorProxy, CrossProcessCompositorApi, WebrenderExternalImageHandlers,
51    WebrenderExternalImageRegistry, WebrenderImageHandlerType,
52};
53#[cfg(all(
54    not(target_os = "windows"),
55    not(target_os = "ios"),
56    not(target_os = "android"),
57    not(target_arch = "arm"),
58    not(target_arch = "aarch64"),
59    not(target_env = "ohos"),
60))]
61use constellation::content_process_sandbox_profile;
62use constellation::{
63    Constellation, FromEmbedderLogger, FromScriptLogger, InitialConstellationState,
64    UnprivilegedContent,
65};
66use constellation_traits::{EmbedderToConstellationMessage, ScriptToConstellationChan};
67use crossbeam_channel::{Receiver, Sender, unbounded};
68use embedder_traits::FormControl as EmbedderFormControl;
69use embedder_traits::user_content_manager::UserContentManager;
70pub use embedder_traits::*;
71use env_logger::Builder as EnvLoggerBuilder;
72use fonts::SystemFontService;
73#[cfg(all(
74    not(target_os = "windows"),
75    not(target_os = "ios"),
76    not(target_os = "android"),
77    not(target_arch = "arm"),
78    not(target_arch = "aarch64"),
79    not(target_env = "ohos"),
80))]
81use gaol::sandbox::{ChildSandbox, ChildSandboxMethods};
82pub use gleam::gl;
83use gleam::gl::RENDERER;
84use ipc_channel::ipc::{self, IpcSender};
85use javascript_evaluator::JavaScriptEvaluator;
86pub use keyboard_types::{
87    Code, CompositionEvent, CompositionState, Key, KeyState, Location, Modifiers, NamedKey,
88};
89use layout::LayoutFactoryImpl;
90use log::{Log, Metadata, Record, debug, error, warn};
91use media::{GlApi, NativeDisplay, WindowGLContext};
92use net::protocols::ProtocolRegistry;
93use net::resource_thread::new_resource_threads;
94use net_traits::{exit_fetch_thread, start_fetch_thread};
95use profile::{mem as profile_mem, time as profile_time};
96use profile_traits::mem::MemoryReportResult;
97use profile_traits::{mem, time};
98use rustc_hash::FxHashMap;
99use script::{JSEngineSetup, ServiceWorkerManager};
100use servo_config::opts::Opts;
101pub use servo_config::prefs::PrefValue;
102use servo_config::prefs::Preferences;
103use servo_config::{opts, pref, prefs};
104use servo_delegate::DefaultServoDelegate;
105use servo_geometry::{
106    DeviceIndependentIntRect, convert_rect_to_css_pixel, convert_size_to_css_pixel,
107};
108use servo_media::ServoMedia;
109use servo_media::player::context::GlContext;
110use servo_url::ServoUrl;
111use style::global_style_data::StyleThreadPool;
112use webgl::WebGLComm;
113#[cfg(feature = "webgpu")]
114pub use webgpu;
115#[cfg(feature = "webgpu")]
116use webgpu::canvas_context::WGPUImageMap;
117use webrender::{ONE_TIME_USAGE_HINT, RenderApiSender, ShaderPrecacheFlags, UploadMethod};
118use webrender_api::{ColorF, DocumentId, FramePublishId};
119use webview::WebViewInner;
120#[cfg(feature = "webxr")]
121pub use webxr;
122pub use {
123    background_hang_monitor, base, canvas_traits, devtools, devtools_traits, euclid, fonts,
124    ipc_channel, layout_api, media, net, net_traits, profile, profile_traits, script,
125    script_traits, servo_config as config, servo_config, servo_geometry, servo_url, style,
126    style_traits, webrender_api,
127};
128#[cfg(feature = "bluetooth")]
129pub use {bluetooth, bluetooth_traits};
130
131use crate::proxies::ConstellationProxy;
132use crate::responders::ServoErrorChannel;
133pub use crate::servo_delegate::{ServoDelegate, ServoError};
134use crate::webrender_api::FrameReadyParams;
135use crate::webview::MINIMUM_WEBVIEW_SIZE;
136pub use crate::webview::{WebView, WebViewBuilder};
137pub use crate::webview_delegate::{
138    AllowOrDenyRequest, AuthenticationRequest, ColorPicker, FormControl, NavigationRequest,
139    PermissionRequest, SelectElement, WebResourceLoad, WebViewDelegate,
140};
141
142#[cfg(feature = "media-gstreamer")]
143mod media_platform {
144    #[cfg(any(windows, target_os = "macos"))]
145    mod gstreamer_plugins {
146        include!(concat!(env!("OUT_DIR"), "/gstreamer_plugins.rs"));
147    }
148
149    use servo_media_gstreamer::GStreamerBackend;
150
151    use super::ServoMedia;
152
153    #[cfg(any(windows, target_os = "macos"))]
154    pub fn init() {
155        ServoMedia::init_with_backend(|| {
156            let mut plugin_dir = std::env::current_exe().unwrap();
157            plugin_dir.pop();
158
159            if cfg!(target_os = "macos") {
160                plugin_dir.push("lib");
161            }
162
163            match GStreamerBackend::init_with_plugins(
164                plugin_dir,
165                gstreamer_plugins::GSTREAMER_PLUGINS,
166            ) {
167                Ok(b) => b,
168                Err(e) => {
169                    eprintln!("Error initializing GStreamer: {:?}", e);
170                    std::process::exit(1);
171                },
172            }
173        });
174    }
175
176    #[cfg(not(any(windows, target_os = "macos")))]
177    pub fn init() {
178        ServoMedia::init::<GStreamerBackend>();
179    }
180}
181
182#[cfg(not(feature = "media-gstreamer"))]
183mod media_platform {
184    use super::ServoMedia;
185    pub fn init() {
186        ServoMedia::init::<servo_media_dummy::DummyBackend>();
187    }
188}
189
190/// The in-process interface to Servo.
191///
192/// It does everything necessary to render the web, primarily
193/// orchestrating the interaction between JavaScript, CSS layout,
194/// rendering, and the client window.
195///
196// Clients create an event loop to pump messages between the embedding
197// application and various browser components.
198pub struct Servo {
199    delegate: RefCell<Rc<dyn ServoDelegate>>,
200    compositor: Rc<RefCell<IOCompositor>>,
201    constellation_proxy: ConstellationProxy,
202    embedder_receiver: Receiver<EmbedderMsg>,
203    /// A struct that tracks ongoing JavaScript evaluations and is responsible for
204    /// calling the callback when the evaluation is complete.
205    javascript_evaluator: Rc<RefCell<JavaScriptEvaluator>>,
206    /// Tracks whether we are in the process of shutting down, or have shut down.
207    /// This is shared with `WebView`s and the `ServoRenderer`.
208    shutdown_state: Rc<Cell<ShutdownState>>,
209    /// A map  [`WebView`]s that are managed by this [`Servo`] instance. These are stored
210    /// as `Weak` references so that the embedding application can control their lifetime.
211    /// When accessed, `Servo` will be reponsible for cleaning up the invalid `Weak`
212    /// references.
213    webviews: RefCell<FxHashMap<WebViewId, Weak<RefCell<WebViewInner>>>>,
214    servo_errors: ServoErrorChannel,
215    /// For single-process Servo instances, this field controls the initialization
216    /// and deinitialization of the JS Engine. Multiprocess Servo instances have their
217    /// own instance that exists in the content process instead.
218    _js_engine_setup: Option<JSEngineSetup>,
219    /// Whether or not any WebView in this instance is animating or WebXR is enabled.
220    animating: Cell<bool>,
221}
222
223#[derive(Clone)]
224struct RenderNotifier {
225    compositor_proxy: CompositorProxy,
226}
227
228impl RenderNotifier {
229    pub fn new(compositor_proxy: CompositorProxy) -> RenderNotifier {
230        RenderNotifier { compositor_proxy }
231    }
232}
233
234impl webrender_api::RenderNotifier for RenderNotifier {
235    fn clone(&self) -> Box<dyn webrender_api::RenderNotifier> {
236        Box::new(RenderNotifier::new(self.compositor_proxy.clone()))
237    }
238
239    fn wake_up(&self, _composite_needed: bool) {}
240
241    fn new_frame_ready(
242        &self,
243        document_id: DocumentId,
244        _: FramePublishId,
245        frame_ready_params: &FrameReadyParams,
246    ) {
247        self.compositor_proxy
248            .send(CompositorMsg::NewWebRenderFrameReady(
249                document_id,
250                frame_ready_params.render,
251            ));
252    }
253}
254
255impl Servo {
256    #[servo_tracing::instrument(skip(builder))]
257    fn new(builder: ServoBuilder) -> Self {
258        // Global configuration options, parsed from the command line.
259        let opts = builder.opts.map(|opts| *opts);
260        opts::initialize_options(opts.unwrap_or_default());
261        let opts = opts::get();
262
263        // Set the preferences globally.
264        // TODO: It would be better to make these private to a particular Servo instance.
265        let preferences = builder.preferences.map(|opts| *opts);
266        servo_config::prefs::set(preferences.unwrap_or_default());
267
268        use std::sync::atomic::Ordering;
269
270        style::context::DEFAULT_DISABLE_STYLE_SHARING_CACHE
271            .store(opts.debug.disable_share_style_cache, Ordering::Relaxed);
272        style::context::DEFAULT_DUMP_STYLE_STATISTICS
273            .store(opts.debug.dump_style_statistics, Ordering::Relaxed);
274        style::traversal::IS_SERVO_NONINCREMENTAL_LAYOUT
275            .store(opts.nonincremental_layout, Ordering::Relaxed);
276
277        if !opts.multiprocess {
278            media_platform::init();
279        }
280
281        // Get GL bindings
282        let rendering_context = builder.rendering_context;
283        let webrender_gl = rendering_context.gleam_gl_api();
284
285        // Make sure the gl context is made current.
286        if let Err(err) = rendering_context.make_current() {
287            warn!("Failed to make the rendering context current: {:?}", err);
288        }
289        debug_assert_eq!(webrender_gl.get_error(), gleam::gl::NO_ERROR,);
290
291        // Reserving a namespace to create WebViewId.
292        PipelineNamespace::install(PipelineNamespaceId(0));
293
294        // Get both endpoints of a special channel for communication between
295        // the client window and the compositor. This channel is unique because
296        // messages to client may need to pump a platform-specific event loop
297        // to deliver the message.
298        let event_loop_waker = builder.event_loop_waker;
299        let (compositor_proxy, compositor_receiver) =
300            create_compositor_channel(event_loop_waker.clone());
301        let (embedder_proxy, embedder_receiver) = create_embedder_channel(event_loop_waker.clone());
302        let time_profiler_chan = profile_time::Profiler::create(
303            &opts.time_profiling,
304            opts.time_profiler_trace_path.clone(),
305        );
306        let mem_profiler_chan = profile_mem::Profiler::create();
307
308        let devtools_sender = if pref!(devtools_server_enabled) {
309            Some(devtools::start_server(
310                pref!(devtools_server_port) as u16,
311                embedder_proxy.clone(),
312            ))
313        } else {
314            None
315        };
316
317        let (mut webrender, webrender_api_sender) = {
318            rendering_context.prepare_for_rendering();
319            let render_notifier = Box::new(RenderNotifier::new(compositor_proxy.clone()));
320            let clear_color = servo_config::pref!(shell_background_color_rgba);
321            let clear_color = ColorF::new(
322                clear_color[0] as f32,
323                clear_color[1] as f32,
324                clear_color[2] as f32,
325                clear_color[3] as f32,
326            );
327
328            // Use same texture upload method as Gecko with ANGLE:
329            // https://searchfox.org/mozilla-central/source/gfx/webrender_bindings/src/bindings.rs#1215-1219
330            let upload_method = if webrender_gl.get_string(RENDERER).starts_with("ANGLE") {
331                UploadMethod::Immediate
332            } else {
333                UploadMethod::PixelBuffer(ONE_TIME_USAGE_HINT)
334            };
335            let worker_threads = thread::available_parallelism()
336                .map(|i| i.get())
337                .unwrap_or(pref!(threadpools_fallback_worker_num) as usize)
338                .min(pref!(threadpools_webrender_workers_max).max(1) as usize);
339            let workers = Some(Arc::new(
340                rayon::ThreadPoolBuilder::new()
341                    .num_threads(worker_threads)
342                    .thread_name(|idx| format!("WRWorker#{}", idx))
343                    .build()
344                    .unwrap(),
345            ));
346            webrender::create_webrender_instance(
347                webrender_gl.clone(),
348                render_notifier,
349                webrender::WebRenderOptions {
350                    // We force the use of optimized shaders here because rendering is broken
351                    // on Android emulators with unoptimized shaders. This is due to a known
352                    // issue in the emulator's OpenGL emulation layer.
353                    // See: https://github.com/servo/servo/issues/31726
354                    use_optimized_shaders: true,
355                    resource_override_path: opts.shaders_dir.clone(),
356                    debug_flags: webrender::DebugFlags::empty(),
357                    precache_flags: if pref!(gfx_precache_shaders) {
358                        ShaderPrecacheFlags::FULL_COMPILE
359                    } else {
360                        ShaderPrecacheFlags::empty()
361                    },
362                    enable_aa: pref!(gfx_text_antialiasing_enabled),
363                    enable_subpixel_aa: pref!(gfx_subpixel_text_antialiasing_enabled),
364                    allow_texture_swizzling: pref!(gfx_texture_swizzling_enabled),
365                    clear_color,
366                    upload_method,
367                    workers,
368                    size_of_op: Some(servo_allocator::usable_size),
369                    ..Default::default()
370                },
371                None,
372            )
373            .expect("Unable to initialize webrender!")
374        };
375
376        let webrender_api = webrender_api_sender.create_api();
377        let webrender_document = webrender_api.add_document(rendering_context.size2d().to_i32());
378
379        // Important that this call is done in a single-threaded fashion, we
380        // can't defer it after `create_constellation` has started.
381        let js_engine_setup = if !opts.multiprocess {
382            Some(script::init())
383        } else {
384            None
385        };
386
387        // Create the webgl thread
388        let gl_type = match webrender_gl.get_type() {
389            gleam::gl::GlType::Gl => GlType::Gl,
390            gleam::gl::GlType::Gles => GlType::Gles,
391        };
392
393        let (external_image_handlers, external_images) = WebrenderExternalImageHandlers::new();
394        let mut external_image_handlers = Box::new(external_image_handlers);
395
396        let WebGLComm {
397            webgl_threads,
398            #[cfg(feature = "webxr")]
399            webxr_layer_grand_manager,
400            image_handler,
401        } = WebGLComm::new(
402            rendering_context.clone(),
403            compositor_proxy.cross_process_compositor_api.clone(),
404            webrender_api.create_sender(),
405            external_images.clone(),
406            gl_type,
407        );
408
409        // Set webrender external image handler for WebGL textures
410        external_image_handlers.set_handler(image_handler, WebrenderImageHandlerType::WebGL);
411
412        // Create the WebXR main thread
413        #[cfg(feature = "webxr")]
414        let mut webxr_main_thread =
415            webxr::MainThreadRegistry::new(event_loop_waker.clone(), webxr_layer_grand_manager)
416                .expect("Failed to create WebXR device registry");
417        #[cfg(feature = "webxr")]
418        if pref!(dom_webxr_enabled) {
419            builder.webxr_registry.register(&mut webxr_main_thread);
420        }
421
422        #[cfg(feature = "webgpu")]
423        let wgpu_image_handler = webgpu::WGPUExternalImages::default();
424        #[cfg(feature = "webgpu")]
425        let wgpu_image_map = wgpu_image_handler.images.clone();
426        #[cfg(feature = "webgpu")]
427        external_image_handlers.set_handler(
428            Box::new(wgpu_image_handler),
429            WebrenderImageHandlerType::WebGPU,
430        );
431
432        WindowGLContext::initialize_image_handler(
433            &mut external_image_handlers,
434            external_images.clone(),
435        );
436
437        webrender.set_external_image_handler(external_image_handlers);
438
439        // Create the constellation, which maintains the engine pipelines, including script and
440        // layout, as well as the navigation context.
441        let mut protocols = ProtocolRegistry::with_internal_protocols();
442        protocols.merge(builder.protocol_registry);
443
444        let constellation_chan = create_constellation(
445            opts.config_dir.clone(),
446            embedder_proxy,
447            compositor_proxy.clone(),
448            time_profiler_chan.clone(),
449            mem_profiler_chan.clone(),
450            devtools_sender,
451            webrender_document,
452            webrender_api_sender,
453            #[cfg(feature = "webxr")]
454            webxr_main_thread.registry(),
455            Some(webgl_threads),
456            external_images,
457            #[cfg(feature = "webgpu")]
458            wgpu_image_map,
459            protocols,
460            builder.user_content_manager,
461        );
462
463        // The compositor coordinates with the client window to create the final
464        // rendered page and display it somewhere.
465        let shutdown_state = Rc::new(Cell::new(ShutdownState::NotShuttingDown));
466        let compositor = IOCompositor::new(
467            InitialCompositorState {
468                sender: compositor_proxy,
469                receiver: compositor_receiver,
470                constellation_chan: constellation_chan.clone(),
471                time_profiler_chan,
472                mem_profiler_chan,
473                webrender,
474                webrender_document,
475                webrender_api,
476                rendering_context,
477                webrender_gl,
478                #[cfg(feature = "webxr")]
479                webxr_main_thread,
480                shutdown_state: shutdown_state.clone(),
481                event_loop_waker,
482            },
483            opts.debug.convert_mouse_to_touch,
484        );
485
486        let constellation_proxy = ConstellationProxy::new(constellation_chan);
487        Self {
488            delegate: RefCell::new(Rc::new(DefaultServoDelegate)),
489            compositor: Rc::new(RefCell::new(compositor)),
490            javascript_evaluator: Rc::new(RefCell::new(JavaScriptEvaluator::new(
491                constellation_proxy.clone(),
492            ))),
493            constellation_proxy,
494            embedder_receiver,
495            shutdown_state,
496            webviews: Default::default(),
497            servo_errors: ServoErrorChannel::default(),
498            _js_engine_setup: js_engine_setup,
499            animating: Cell::new(false),
500        }
501    }
502
503    pub fn delegate(&self) -> Rc<dyn ServoDelegate> {
504        self.delegate.borrow().clone()
505    }
506
507    pub fn set_delegate(&self, delegate: Rc<dyn ServoDelegate>) {
508        *self.delegate.borrow_mut() = delegate;
509    }
510
511    /// Whether or not any [`WebView`] of this Servo instance has animating content, such as a CSS
512    /// animation or transition or is running `requestAnimationFrame` callbacks. In addition, this
513    /// returns true if WebXR content is running. This indicates that the embedding application
514    /// should be spinning the Servo event loop on regular intervals in order to trigger animation
515    /// updates.
516    pub fn animating(&self) -> bool {
517        self.animating.get()
518    }
519
520    /// **EXPERIMENTAL:** Intialize GL accelerated media playback. This currently only works on a limited number
521    /// of platforms. This should be run *before* calling [`Servo::new`] and creating the first [`WebView`].
522    pub fn initialize_gl_accelerated_media(display: NativeDisplay, api: GlApi, context: GlContext) {
523        WindowGLContext::initialize(display, api, context)
524    }
525
526    /// Spin the Servo event loop, which:
527    ///
528    ///   - Performs updates in the compositor, such as queued pinch zoom events
529    ///   - Runs delebgate methods on all `WebView`s and `Servo` itself
530    ///   - Maybe update the rendered compositor output, but *without* swapping buffers.
531    ///
532    /// The return value of this method indicates whether or not Servo, false indicates that Servo
533    /// has finished shutting down and you should not spin the event loop any longer.
534    pub fn spin_event_loop(&self) -> bool {
535        if self.shutdown_state.get() == ShutdownState::FinishedShuttingDown {
536            return false;
537        }
538
539        {
540            let mut compositor = self.compositor.borrow_mut();
541            let mut messages = Vec::new();
542            while let Ok(message) = compositor.receiver().try_recv() {
543                match message {
544                    Ok(message) => messages.push(message),
545                    Err(error) => {
546                        warn!("Router deserialization error: {error}. Ignoring this CompositorMsg.")
547                    },
548                }
549            }
550            compositor.handle_messages(messages);
551        }
552
553        // Only handle incoming embedder messages if the compositor hasn't already started shutting down.
554        while let Ok(message) = self.embedder_receiver.try_recv() {
555            self.handle_embedder_message(message);
556
557            if self.shutdown_state.get() == ShutdownState::FinishedShuttingDown {
558                break;
559            }
560        }
561
562        if self.constellation_proxy.disconnected() {
563            self.delegate()
564                .notify_error(self, ServoError::LostConnectionWithBackend);
565        }
566
567        self.compositor.borrow_mut().perform_updates();
568        self.send_new_frame_ready_messages();
569        self.send_animating_changed_messages();
570        self.handle_delegate_errors();
571        self.clean_up_destroyed_webview_handles();
572
573        if self.shutdown_state.get() == ShutdownState::FinishedShuttingDown {
574            return false;
575        }
576
577        true
578    }
579
580    fn send_new_frame_ready_messages(&self) {
581        if !self.compositor.borrow().needs_repaint() {
582            return;
583        }
584
585        for webview in self
586            .webviews
587            .borrow()
588            .values()
589            .filter_map(WebView::from_weak_handle)
590        {
591            webview.delegate().notify_new_frame_ready(webview);
592        }
593    }
594
595    fn send_animating_changed_messages(&self) {
596        let animating = self.compositor.borrow().webxr_running() ||
597            self.webviews
598                .borrow()
599                .values()
600                .filter_map(WebView::from_weak_handle)
601                .any(|webview| webview.animating());
602        if animating != self.animating.get() {
603            self.animating.set(animating);
604            self.delegate().notify_animating_changed(animating);
605        }
606    }
607
608    fn handle_delegate_errors(&self) {
609        while let Some(error) = self.servo_errors.try_recv() {
610            self.delegate().notify_error(self, error);
611        }
612    }
613
614    fn clean_up_destroyed_webview_handles(&self) {
615        // Remove any webview handles that have been destroyed and would not be upgradable.
616        // Note that `retain` is O(capacity) because it visits empty buckets, so it may be worth
617        // calling `shrink_to_fit` at some point to deal with cases where a long-running Servo
618        // instance goes from many open webviews to only a few.
619        self.webviews
620            .borrow_mut()
621            .retain(|_webview_id, webview| webview.strong_count() > 0);
622    }
623
624    pub fn setup_logging(&self) {
625        let constellation_chan = self.constellation_proxy.sender();
626        let env = env_logger::Env::default();
627        let env_logger = EnvLoggerBuilder::from_env(env).build();
628        let con_logger = FromEmbedderLogger::new(constellation_chan);
629
630        let filter = max(env_logger.filter(), con_logger.filter());
631        let logger = BothLogger(env_logger, con_logger);
632
633        log::set_boxed_logger(Box::new(logger)).expect("Failed to set logger.");
634        log::set_max_level(filter);
635    }
636
637    pub fn create_memory_report(&self, snd: IpcSender<MemoryReportResult>) {
638        self.constellation_proxy
639            .send(EmbedderToConstellationMessage::CreateMemoryReport(snd));
640    }
641
642    pub fn start_shutting_down(&self) {
643        if self.shutdown_state.get() != ShutdownState::NotShuttingDown {
644            warn!("Requested shutdown while already shutting down");
645            return;
646        }
647
648        debug!("Sending Exit message to Constellation");
649        self.constellation_proxy
650            .send(EmbedderToConstellationMessage::Exit);
651        self.shutdown_state.set(ShutdownState::ShuttingDown);
652    }
653
654    fn finish_shutting_down(&self) {
655        debug!("Servo received message that Constellation shutdown is complete");
656        self.shutdown_state.set(ShutdownState::FinishedShuttingDown);
657        self.compositor.borrow_mut().finish_shutting_down();
658    }
659
660    pub fn deinit(&self) {
661        self.compositor.borrow_mut().deinit();
662    }
663
664    fn get_webview_handle(&self, id: WebViewId) -> Option<WebView> {
665        self.webviews
666            .borrow()
667            .get(&id)
668            .and_then(WebView::from_weak_handle)
669    }
670
671    fn handle_embedder_message(&self, message: EmbedderMsg) {
672        match message {
673            EmbedderMsg::ShutdownComplete => self.finish_shutting_down(),
674            EmbedderMsg::Status(webview_id, status_text) => {
675                if let Some(webview) = self.get_webview_handle(webview_id) {
676                    webview.set_status_text(status_text);
677                }
678            },
679            EmbedderMsg::ChangePageTitle(webview_id, title) => {
680                if let Some(webview) = self.get_webview_handle(webview_id) {
681                    webview.set_page_title(title);
682                }
683            },
684            EmbedderMsg::MoveTo(webview_id, position) => {
685                if let Some(webview) = self.get_webview_handle(webview_id) {
686                    webview.delegate().request_move_to(webview, position);
687                }
688            },
689            EmbedderMsg::ResizeTo(webview_id, size) => {
690                if let Some(webview) = self.get_webview_handle(webview_id) {
691                    webview
692                        .delegate()
693                        .request_resize_to(webview, size.max(MINIMUM_WEBVIEW_SIZE));
694                }
695            },
696            EmbedderMsg::ShowSimpleDialog(webview_id, prompt_definition) => {
697                if let Some(webview) = self.get_webview_handle(webview_id) {
698                    webview
699                        .delegate()
700                        .show_simple_dialog(webview, prompt_definition);
701                }
702            },
703            EmbedderMsg::ShowContextMenu(webview_id, ipc_sender, title, items) => {
704                if let Some(webview) = self.get_webview_handle(webview_id) {
705                    webview
706                        .delegate()
707                        .show_context_menu(webview, ipc_sender, title, items);
708                }
709            },
710            EmbedderMsg::AllowNavigationRequest(webview_id, pipeline_id, servo_url) => {
711                if let Some(webview) = self.get_webview_handle(webview_id) {
712                    let request = NavigationRequest {
713                        url: servo_url.into_url(),
714                        pipeline_id,
715                        constellation_proxy: self.constellation_proxy.clone(),
716                        response_sent: false,
717                    };
718                    webview.delegate().request_navigation(webview, request);
719                }
720            },
721            EmbedderMsg::AllowOpeningWebView(webview_id, response_sender) => {
722                if let Some(webview) = self.get_webview_handle(webview_id) {
723                    let webview_id_and_viewport_details = webview
724                        .delegate()
725                        .request_open_auxiliary_webview(webview)
726                        .map(|webview| (webview.id(), webview.viewport_details()));
727                    let _ = response_sender.send(webview_id_and_viewport_details);
728                }
729            },
730            EmbedderMsg::WebViewClosed(webview_id) => {
731                if let Some(webview) = self.get_webview_handle(webview_id) {
732                    webview.delegate().notify_closed(webview);
733                }
734            },
735            EmbedderMsg::WebViewFocused(webview_id, focus_result) => {
736                if focus_result {
737                    for id in self.webviews.borrow().keys() {
738                        if let Some(webview) = self.get_webview_handle(*id) {
739                            let focused = webview.id() == webview_id;
740                            webview.set_focused(focused);
741                        }
742                    }
743                }
744            },
745            EmbedderMsg::WebViewBlurred => {
746                for id in self.webviews.borrow().keys() {
747                    if let Some(webview) = self.get_webview_handle(*id) {
748                        webview.set_focused(false);
749                    }
750                }
751            },
752            EmbedderMsg::AllowUnload(webview_id, response_sender) => {
753                if let Some(webview) = self.get_webview_handle(webview_id) {
754                    let request = AllowOrDenyRequest::new(
755                        response_sender,
756                        AllowOrDeny::Allow,
757                        self.servo_errors.sender(),
758                    );
759                    webview.delegate().request_unload(webview, request);
760                }
761            },
762            EmbedderMsg::FinishJavaScriptEvaluation(evaluation_id, result) => {
763                self.javascript_evaluator
764                    .borrow_mut()
765                    .finish_evaluation(evaluation_id, result);
766            },
767            EmbedderMsg::Keyboard(webview_id, keyboard_event) => {
768                if let Some(webview) = self.get_webview_handle(webview_id) {
769                    webview
770                        .delegate()
771                        .notify_keyboard_event(webview, keyboard_event);
772                }
773            },
774            EmbedderMsg::ClearClipboard(webview_id) => {
775                if let Some(webview) = self.get_webview_handle(webview_id) {
776                    webview.clipboard_delegate().clear(webview);
777                }
778            },
779            EmbedderMsg::GetClipboardText(webview_id, result_sender) => {
780                if let Some(webview) = self.get_webview_handle(webview_id) {
781                    webview
782                        .clipboard_delegate()
783                        .get_text(webview, StringRequest::from(result_sender));
784                }
785            },
786            EmbedderMsg::SetClipboardText(webview_id, string) => {
787                if let Some(webview) = self.get_webview_handle(webview_id) {
788                    webview.clipboard_delegate().set_text(webview, string);
789                }
790            },
791            EmbedderMsg::SetCursor(webview_id, cursor) => {
792                if let Some(webview) = self.get_webview_handle(webview_id) {
793                    webview.set_cursor(cursor);
794                }
795            },
796            EmbedderMsg::NewFavicon(webview_id, image) => {
797                if let Some(webview) = self.get_webview_handle(webview_id) {
798                    webview.set_favicon(image);
799                }
800            },
801            EmbedderMsg::NotifyLoadStatusChanged(webview_id, load_status) => {
802                if let Some(webview) = self.get_webview_handle(webview_id) {
803                    webview.set_load_status(load_status);
804                }
805            },
806            EmbedderMsg::HistoryTraversalComplete(webview_id, traversal_id) => {
807                if let Some(webview) = self.get_webview_handle(webview_id) {
808                    webview
809                        .delegate()
810                        .notify_traversal_complete(webview.clone(), traversal_id);
811                }
812            },
813            EmbedderMsg::HistoryChanged(webview_id, urls, current_index) => {
814                if let Some(webview) = self.get_webview_handle(webview_id) {
815                    let urls: Vec<_> = urls.into_iter().map(ServoUrl::into_url).collect();
816                    let current_url = urls[current_index].clone();
817
818                    webview
819                        .delegate()
820                        .notify_history_changed(webview.clone(), urls, current_index);
821                    webview.set_url(current_url);
822                }
823            },
824            EmbedderMsg::NotifyFullscreenStateChanged(webview_id, fullscreen) => {
825                if let Some(webview) = self.get_webview_handle(webview_id) {
826                    webview
827                        .delegate()
828                        .notify_fullscreen_state_changed(webview, fullscreen);
829                }
830            },
831            EmbedderMsg::WebResourceRequested(
832                webview_id,
833                web_resource_request,
834                response_sender,
835            ) => {
836                if let Some(webview) =
837                    webview_id.and_then(|webview_id| self.get_webview_handle(webview_id))
838                {
839                    let web_resource_load = WebResourceLoad::new(
840                        web_resource_request,
841                        response_sender,
842                        self.servo_errors.sender(),
843                    );
844                    webview
845                        .delegate()
846                        .load_web_resource(webview, web_resource_load);
847                } else {
848                    let web_resource_load = WebResourceLoad::new(
849                        web_resource_request,
850                        response_sender,
851                        self.servo_errors.sender(),
852                    );
853                    self.delegate().load_web_resource(web_resource_load);
854                }
855            },
856            EmbedderMsg::Panic(webview_id, reason, backtrace) => {
857                if let Some(webview) = self.get_webview_handle(webview_id) {
858                    webview
859                        .delegate()
860                        .notify_crashed(webview, reason, backtrace);
861                }
862            },
863            EmbedderMsg::GetSelectedBluetoothDevice(webview_id, items, response_sender) => {
864                if let Some(webview) = self.get_webview_handle(webview_id) {
865                    webview.delegate().show_bluetooth_device_dialog(
866                        webview,
867                        items,
868                        response_sender,
869                    );
870                }
871            },
872            EmbedderMsg::SelectFiles(
873                webview_id,
874                filter_patterns,
875                allow_select_multiple,
876                response_sender,
877            ) => {
878                if let Some(webview) = self.get_webview_handle(webview_id) {
879                    webview.delegate().show_file_selection_dialog(
880                        webview,
881                        filter_patterns,
882                        allow_select_multiple,
883                        response_sender,
884                    );
885                }
886            },
887            EmbedderMsg::RequestAuthentication(webview_id, url, for_proxy, response_sender) => {
888                if let Some(webview) = self.get_webview_handle(webview_id) {
889                    let authentication_request = AuthenticationRequest::new(
890                        url.into_url(),
891                        for_proxy,
892                        response_sender,
893                        self.servo_errors.sender(),
894                    );
895                    webview
896                        .delegate()
897                        .request_authentication(webview, authentication_request);
898                }
899            },
900            EmbedderMsg::PromptPermission(webview_id, requested_feature, response_sender) => {
901                if let Some(webview) = self.get_webview_handle(webview_id) {
902                    let permission_request = PermissionRequest {
903                        requested_feature,
904                        allow_deny_request: AllowOrDenyRequest::new(
905                            response_sender,
906                            AllowOrDeny::Deny,
907                            self.servo_errors.sender(),
908                        ),
909                    };
910                    webview
911                        .delegate()
912                        .request_permission(webview, permission_request);
913                }
914            },
915            EmbedderMsg::ShowIME(webview_id, input_method_type, text, multiline, position) => {
916                if let Some(webview) = self.get_webview_handle(webview_id) {
917                    webview.delegate().show_ime(
918                        webview,
919                        input_method_type,
920                        text,
921                        multiline,
922                        position,
923                    );
924                }
925            },
926            EmbedderMsg::HideIME(webview_id) => {
927                if let Some(webview) = self.get_webview_handle(webview_id) {
928                    webview.delegate().hide_ime(webview);
929                }
930            },
931            EmbedderMsg::ReportProfile(_items) => {},
932            EmbedderMsg::MediaSessionEvent(webview_id, media_session_event) => {
933                if let Some(webview) = self.get_webview_handle(webview_id) {
934                    webview
935                        .delegate()
936                        .notify_media_session_event(webview, media_session_event);
937                }
938            },
939            EmbedderMsg::OnDevtoolsStarted(port, token) => match port {
940                Ok(port) => self
941                    .delegate()
942                    .notify_devtools_server_started(self, port, token),
943                Err(()) => self
944                    .delegate()
945                    .notify_error(self, ServoError::DevtoolsFailedToStart),
946            },
947            EmbedderMsg::RequestDevtoolsConnection(response_sender) => {
948                self.delegate().request_devtools_connection(
949                    self,
950                    AllowOrDenyRequest::new(
951                        response_sender,
952                        AllowOrDeny::Deny,
953                        self.servo_errors.sender(),
954                    ),
955                );
956            },
957            EmbedderMsg::PlayGamepadHapticEffect(
958                webview_id,
959                gamepad_index,
960                gamepad_haptic_effect_type,
961                ipc_sender,
962            ) => {
963                if let Some(webview) = self.get_webview_handle(webview_id) {
964                    webview.delegate().play_gamepad_haptic_effect(
965                        webview,
966                        gamepad_index,
967                        gamepad_haptic_effect_type,
968                        ipc_sender,
969                    );
970                }
971            },
972            EmbedderMsg::StopGamepadHapticEffect(webview_id, gamepad_index, ipc_sender) => {
973                if let Some(webview) = self.get_webview_handle(webview_id) {
974                    webview.delegate().stop_gamepad_haptic_effect(
975                        webview,
976                        gamepad_index,
977                        ipc_sender,
978                    );
979                }
980            },
981            EmbedderMsg::ShowNotification(webview_id, notification) => {
982                match webview_id.and_then(|webview_id| self.get_webview_handle(webview_id)) {
983                    Some(webview) => webview.delegate().show_notification(webview, notification),
984                    None => self.delegate().show_notification(notification),
985                }
986            },
987            EmbedderMsg::ShowFormControl(webview_id, position, form_control) => {
988                if let Some(webview) = self.get_webview_handle(webview_id) {
989                    let form_control = match form_control {
990                        EmbedderFormControl::SelectElement(
991                            options,
992                            selected_option,
993                            ipc_sender,
994                        ) => FormControl::SelectElement(SelectElement::new(
995                            options,
996                            selected_option,
997                            position,
998                            ipc_sender,
999                        )),
1000                        EmbedderFormControl::ColorPicker(current_color, ipc_sender) => {
1001                            FormControl::ColorPicker(ColorPicker::new(
1002                                current_color,
1003                                position,
1004                                ipc_sender,
1005                                self.servo_errors.sender(),
1006                            ))
1007                        },
1008                    };
1009
1010                    webview.delegate().show_form_control(webview, form_control);
1011                }
1012            },
1013            EmbedderMsg::GetWindowRect(webview_id, response_sender) => {
1014                let window_rect = || {
1015                    let Some(webview) = self.get_webview_handle(webview_id) else {
1016                        return DeviceIndependentIntRect::default();
1017                    };
1018                    let hidpi_scale_factor = webview.hidpi_scale_factor();
1019                    let Some(screen_geometry) = webview.delegate().screen_geometry(webview) else {
1020                        return DeviceIndependentIntRect::default();
1021                    };
1022
1023                    convert_rect_to_css_pixel(screen_geometry.window_rect, hidpi_scale_factor)
1024                };
1025
1026                if let Err(error) = response_sender.send(window_rect()) {
1027                    warn!("Failed to respond to GetWindowRect: {error}");
1028                }
1029            },
1030            EmbedderMsg::GetScreenMetrics(webview_id, response_sender) => {
1031                let screen_metrics = || {
1032                    let Some(webview) = self.get_webview_handle(webview_id) else {
1033                        return ScreenMetrics::default();
1034                    };
1035                    let hidpi_scale_factor = webview.hidpi_scale_factor();
1036                    let Some(screen_geometry) = webview.delegate().screen_geometry(webview) else {
1037                        return ScreenMetrics::default();
1038                    };
1039
1040                    ScreenMetrics {
1041                        screen_size: convert_size_to_css_pixel(
1042                            screen_geometry.size,
1043                            hidpi_scale_factor,
1044                        ),
1045                        available_size: convert_size_to_css_pixel(
1046                            screen_geometry.available_size,
1047                            hidpi_scale_factor,
1048                        ),
1049                    }
1050                };
1051                if let Err(error) = response_sender.send(screen_metrics()) {
1052                    warn!("Failed to respond to GetScreenMetrics: {error}");
1053                }
1054            },
1055        }
1056    }
1057
1058    pub fn constellation_sender(&self) -> Sender<EmbedderToConstellationMessage> {
1059        self.constellation_proxy.sender()
1060    }
1061
1062    pub fn execute_webdriver_command(&self, command: WebDriverCommandMsg) {
1063        if let WebDriverCommandMsg::TakeScreenshot(webview_id, page_rect, response_sender) = command
1064        {
1065            let res = self
1066                .compositor
1067                .borrow_mut()
1068                .render_to_shared_memory(webview_id, page_rect);
1069            if let Err(ref e) = res {
1070                error!("Error retrieving PNG: {:?}", e);
1071            }
1072            let img = res.unwrap_or(None);
1073            if let Err(e) = response_sender.send(img) {
1074                error!("Sending reply to create png failed ({:?}).", e);
1075            }
1076        } else {
1077            self.constellation_proxy
1078                .send(EmbedderToConstellationMessage::WebDriverCommand(command));
1079        }
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
1089fn create_embedder_channel(
1090    event_loop_waker: Box<dyn EventLoopWaker>,
1091) -> (EmbedderProxy, Receiver<EmbedderMsg>) {
1092    let (sender, receiver) = unbounded();
1093    (
1094        EmbedderProxy {
1095            sender,
1096            event_loop_waker,
1097        },
1098        receiver,
1099    )
1100}
1101
1102fn create_compositor_channel(
1103    event_loop_waker: Box<dyn EventLoopWaker>,
1104) -> (CompositorProxy, RoutedReceiver<CompositorMsg>) {
1105    let (sender, receiver) = unbounded();
1106    let sender_clone = sender.clone();
1107    let event_loop_waker_clone = event_loop_waker.clone();
1108    // This callback is equivalent to `CompositorProxy::send`
1109    let result_callback = move |msg: Result<CompositorMsg, ipc_channel::Error>| {
1110        if let Err(err) = sender_clone.send(msg) {
1111            warn!("Failed to send response ({:?}).", err);
1112        }
1113        event_loop_waker_clone.wake();
1114    };
1115
1116    let generic_callback =
1117        GenericCallback::new(result_callback).expect("Failed to create callback");
1118    let cross_process_compositor_api = CrossProcessCompositorApi::new(generic_callback);
1119    let compositor_proxy = CompositorProxy {
1120        sender,
1121        cross_process_compositor_api,
1122        event_loop_waker,
1123    };
1124
1125    (compositor_proxy, receiver)
1126}
1127
1128#[allow(clippy::too_many_arguments)]
1129fn create_constellation(
1130    config_dir: Option<PathBuf>,
1131    embedder_proxy: EmbedderProxy,
1132    compositor_proxy: CompositorProxy,
1133    time_profiler_chan: time::ProfilerChan,
1134    mem_profiler_chan: mem::ProfilerChan,
1135    devtools_sender: Option<Sender<devtools_traits::DevtoolsControlMsg>>,
1136    webrender_document: DocumentId,
1137    webrender_api_sender: RenderApiSender,
1138    #[cfg(feature = "webxr")] webxr_registry: webxr_api::Registry,
1139    webgl_threads: Option<WebGLThreads>,
1140    external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
1141    #[cfg(feature = "webgpu")] wgpu_image_map: WGPUImageMap,
1142    protocols: ProtocolRegistry,
1143    user_content_manager: UserContentManager,
1144) -> Sender<EmbedderToConstellationMessage> {
1145    // Global configuration options, parsed from the command line.
1146    let opts = opts::get();
1147
1148    #[cfg(feature = "bluetooth")]
1149    let bluetooth_thread: IpcSender<BluetoothRequest> =
1150        BluetoothThreadFactory::new(embedder_proxy.clone());
1151
1152    let privileged_urls = protocols.privileged_urls();
1153
1154    let (public_resource_threads, private_resource_threads, async_runtime) = new_resource_threads(
1155        devtools_sender.clone(),
1156        time_profiler_chan.clone(),
1157        mem_profiler_chan.clone(),
1158        embedder_proxy.clone(),
1159        config_dir,
1160        opts.certificate_path.clone(),
1161        opts.ignore_certificate_errors,
1162        Arc::new(protocols),
1163    );
1164
1165    let system_font_service = Arc::new(
1166        SystemFontService::spawn(
1167            compositor_proxy.cross_process_compositor_api.clone(),
1168            mem_profiler_chan.clone(),
1169        )
1170        .to_proxy(),
1171    );
1172
1173    let initial_state = InitialConstellationState {
1174        compositor_proxy,
1175        embedder_proxy,
1176        devtools_sender,
1177        #[cfg(feature = "bluetooth")]
1178        bluetooth_thread,
1179        system_font_service,
1180        public_resource_threads,
1181        private_resource_threads,
1182        time_profiler_chan,
1183        mem_profiler_chan,
1184        webrender_document,
1185        webrender_api_sender,
1186        #[cfg(feature = "webxr")]
1187        webxr_registry: Some(webxr_registry),
1188        #[cfg(not(feature = "webxr"))]
1189        webxr_registry: None,
1190        webgl_threads,
1191        webrender_external_images: external_images,
1192        #[cfg(feature = "webgpu")]
1193        wgpu_image_map,
1194        user_content_manager,
1195        async_runtime,
1196        privileged_urls,
1197    };
1198
1199    let layout_factory = Arc::new(LayoutFactoryImpl());
1200
1201    Constellation::<script::ScriptThread, script::ServiceWorkerManager>::start(
1202        initial_state,
1203        layout_factory,
1204        opts.random_pipeline_closure_probability,
1205        opts.random_pipeline_closure_seed,
1206        opts.hard_fail,
1207    )
1208}
1209
1210// A logger that logs to two downstream loggers.
1211// This should probably be in the log crate.
1212struct BothLogger<Log1, Log2>(Log1, Log2);
1213
1214impl<Log1, Log2> Log for BothLogger<Log1, Log2>
1215where
1216    Log1: Log,
1217    Log2: Log,
1218{
1219    fn enabled(&self, metadata: &Metadata) -> bool {
1220        self.0.enabled(metadata) || self.1.enabled(metadata)
1221    }
1222
1223    fn log(&self, record: &Record) {
1224        self.0.log(record);
1225        self.1.log(record);
1226    }
1227
1228    fn flush(&self) {
1229        self.0.flush();
1230        self.1.flush();
1231    }
1232}
1233
1234pub fn set_logger(script_to_constellation_chan: ScriptToConstellationChan) {
1235    let con_logger = FromScriptLogger::new(script_to_constellation_chan);
1236    let env = env_logger::Env::default();
1237    let env_logger = EnvLoggerBuilder::from_env(env).build();
1238
1239    let filter = max(env_logger.filter(), con_logger.filter());
1240    let logger = BothLogger(env_logger, con_logger);
1241
1242    log::set_boxed_logger(Box::new(logger)).expect("Failed to set logger.");
1243    log::set_max_level(filter);
1244}
1245
1246/// Content process entry point.
1247pub fn run_content_process(token: String) {
1248    let (unprivileged_content_sender, unprivileged_content_receiver) =
1249        ipc::channel::<UnprivilegedContent>().unwrap();
1250    let connection_bootstrap: IpcSender<IpcSender<UnprivilegedContent>> =
1251        IpcSender::connect(token).unwrap();
1252    connection_bootstrap
1253        .send(unprivileged_content_sender)
1254        .unwrap();
1255
1256    let unprivileged_content = unprivileged_content_receiver.recv().unwrap();
1257    opts::initialize_options(unprivileged_content.opts());
1258    prefs::set(unprivileged_content.prefs().clone());
1259
1260    // Enter the sandbox if necessary.
1261    if opts::get().sandbox {
1262        create_sandbox();
1263    }
1264
1265    let _js_engine_setup = script::init();
1266
1267    match unprivileged_content {
1268        UnprivilegedContent::Pipeline(mut content) => {
1269            media_platform::init();
1270
1271            // Start the fetch thread for this content process.
1272            let fetch_thread_join_handle = start_fetch_thread();
1273
1274            set_logger(content.script_to_constellation_chan().clone());
1275
1276            let (background_hang_monitor_register, join_handle) =
1277                content.register_with_background_hang_monitor();
1278            let layout_factory = Arc::new(LayoutFactoryImpl());
1279
1280            content.register_system_memory_reporter();
1281
1282            let script_join_handle = content.start_all::<script::ScriptThread>(
1283                true,
1284                layout_factory,
1285                background_hang_monitor_register,
1286                None,
1287            );
1288
1289            // Since wait_for_completion is true,
1290            // here we know that the script-thread
1291            // will exit(or already has),
1292            // and so we can join first on the script, and then on the BHM worker, threads.
1293            script_join_handle
1294                .join()
1295                .expect("Failed to join on the script thread.");
1296            join_handle
1297                .join()
1298                .expect("Failed to join on the BHM background thread.");
1299
1300            StyleThreadPool::shutdown();
1301
1302            // Shut down the fetch thread started above.
1303            exit_fetch_thread();
1304            fetch_thread_join_handle
1305                .join()
1306                .expect("Failed to join on the fetch thread in the constellation");
1307        },
1308        UnprivilegedContent::ServiceWorker(content) => {
1309            content.start::<ServiceWorkerManager>();
1310        },
1311    }
1312}
1313
1314#[cfg(all(
1315    not(target_os = "windows"),
1316    not(target_os = "ios"),
1317    not(target_os = "android"),
1318    not(target_arch = "arm"),
1319    not(target_arch = "aarch64"),
1320    not(target_env = "ohos"),
1321))]
1322fn create_sandbox() {
1323    ChildSandbox::new(content_process_sandbox_profile())
1324        .activate()
1325        .expect("Failed to activate sandbox!");
1326}
1327
1328#[cfg(any(
1329    target_os = "windows",
1330    target_os = "ios",
1331    target_os = "android",
1332    target_arch = "arm",
1333    target_arch = "aarch64",
1334    target_env = "ohos",
1335))]
1336fn create_sandbox() {
1337    panic!("Sandboxing is not supported on Windows, iOS, ARM targets and android.");
1338}
1339
1340struct DefaultEventLoopWaker;
1341
1342impl EventLoopWaker for DefaultEventLoopWaker {
1343    fn clone_box(&self) -> Box<dyn EventLoopWaker> {
1344        Box::new(DefaultEventLoopWaker)
1345    }
1346}
1347
1348#[cfg(feature = "webxr")]
1349struct DefaultWebXrRegistry;
1350#[cfg(feature = "webxr")]
1351impl webxr::WebXrRegistry for DefaultWebXrRegistry {}
1352
1353pub struct ServoBuilder {
1354    rendering_context: Rc<dyn RenderingContext>,
1355    opts: Option<Box<Opts>>,
1356    preferences: Option<Box<Preferences>>,
1357    event_loop_waker: Box<dyn EventLoopWaker>,
1358    user_content_manager: UserContentManager,
1359    protocol_registry: ProtocolRegistry,
1360    #[cfg(feature = "webxr")]
1361    webxr_registry: Box<dyn webxr::WebXrRegistry>,
1362}
1363
1364impl ServoBuilder {
1365    pub fn new(rendering_context: Rc<dyn RenderingContext>) -> Self {
1366        Self {
1367            rendering_context,
1368            opts: None,
1369            preferences: None,
1370            event_loop_waker: Box::new(DefaultEventLoopWaker),
1371            user_content_manager: UserContentManager::default(),
1372            protocol_registry: ProtocolRegistry::default(),
1373            #[cfg(feature = "webxr")]
1374            webxr_registry: Box::new(DefaultWebXrRegistry),
1375        }
1376    }
1377
1378    pub fn build(self) -> Servo {
1379        Servo::new(self)
1380    }
1381
1382    pub fn opts(mut self, opts: Opts) -> Self {
1383        self.opts = Some(Box::new(opts));
1384        self
1385    }
1386
1387    pub fn preferences(mut self, preferences: Preferences) -> Self {
1388        self.preferences = Some(Box::new(preferences));
1389        self
1390    }
1391
1392    pub fn event_loop_waker(mut self, event_loop_waker: Box<dyn EventLoopWaker>) -> Self {
1393        self.event_loop_waker = event_loop_waker;
1394        self
1395    }
1396
1397    pub fn user_content_manager(mut self, user_content_manager: UserContentManager) -> Self {
1398        self.user_content_manager = user_content_manager;
1399        self
1400    }
1401
1402    pub fn protocol_registry(mut self, protocol_registry: ProtocolRegistry) -> Self {
1403        self.protocol_registry = protocol_registry;
1404        self
1405    }
1406
1407    #[cfg(feature = "webxr")]
1408    pub fn webxr_registry(mut self, webxr_registry: Box<dyn webxr::WebXrRegistry>) -> Self {
1409        self.webxr_registry = webxr_registry;
1410        self
1411    }
1412}