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};
66pub use constellation_traits::EmbedderToConstellationMessage;
67use constellation_traits::ScriptToConstellationChan;
68use crossbeam_channel::{Receiver, Sender, unbounded};
69use embedder_traits::FormControlRequest as EmbedderFormControl;
70use embedder_traits::user_content_manager::UserContentManager;
71pub use embedder_traits::{WebDriverSenders, *};
72use env_logger::Builder as EnvLoggerBuilder;
73use fonts::SystemFontService;
74#[cfg(all(
75    not(target_os = "windows"),
76    not(target_os = "ios"),
77    not(target_os = "android"),
78    not(target_arch = "arm"),
79    not(target_arch = "aarch64"),
80    not(target_env = "ohos"),
81))]
82use gaol::sandbox::{ChildSandbox, ChildSandboxMethods};
83pub use gleam::gl;
84use gleam::gl::RENDERER;
85pub use image::RgbaImage;
86use ipc_channel::ipc::{self, IpcSender};
87use javascript_evaluator::JavaScriptEvaluator;
88pub use keyboard_types::{
89    Code, CompositionEvent, CompositionState, Key, KeyState, Location, Modifiers, NamedKey,
90};
91use layout::LayoutFactoryImpl;
92use log::{Log, Metadata, Record, debug, warn};
93use media::{GlApi, NativeDisplay, WindowGLContext};
94use net::protocols::ProtocolRegistry;
95use net::resource_thread::new_resource_threads;
96use net_traits::{exit_fetch_thread, start_fetch_thread};
97use profile::{mem as profile_mem, time as profile_time};
98use profile_traits::mem::MemoryReportResult;
99use profile_traits::{mem, time};
100use rustc_hash::FxHashMap;
101use script::{JSEngineSetup, ServiceWorkerManager};
102use servo_config::opts::Opts;
103pub use servo_config::prefs::PrefValue;
104use servo_config::prefs::Preferences;
105use servo_config::{opts, pref, prefs};
106use servo_delegate::DefaultServoDelegate;
107use servo_geometry::{
108    DeviceIndependentIntRect, convert_rect_to_css_pixel, convert_size_to_css_pixel,
109};
110use servo_media::ServoMedia;
111use servo_media::player::context::GlContext;
112use servo_url::ServoUrl;
113use storage::new_storage_threads;
114use style::global_style_data::StyleThreadPool;
115use webgl::WebGLComm;
116#[cfg(feature = "webgpu")]
117pub use webgpu;
118#[cfg(feature = "webgpu")]
119use webgpu::canvas_context::WGPUImageMap;
120use webrender::{ONE_TIME_USAGE_HINT, RenderApiSender, ShaderPrecacheFlags, UploadMethod};
121use webrender_api::{ColorF, DocumentId, FramePublishId};
122use webview::WebViewInner;
123#[cfg(feature = "webxr")]
124pub use webxr;
125pub use {
126    background_hang_monitor, base, canvas_traits, devtools, devtools_traits, euclid, fonts,
127    ipc_channel, layout_api, media, net, net_traits, profile, profile_traits, script,
128    script_traits, servo_config as config, servo_config, servo_geometry, servo_url, style,
129    style_traits, webrender_api,
130};
131#[cfg(feature = "bluetooth")]
132pub use {bluetooth, bluetooth_traits};
133
134use crate::proxies::ConstellationProxy;
135use crate::responders::ServoErrorChannel;
136pub use crate::servo_delegate::{ServoDelegate, ServoError};
137use crate::webrender_api::FrameReadyParams;
138use crate::webview::MINIMUM_WEBVIEW_SIZE;
139pub use crate::webview::{WebView, WebViewBuilder};
140pub use crate::webview_delegate::{
141    AllowOrDenyRequest, AuthenticationRequest, ColorPicker, FormControl, NavigationRequest,
142    PermissionRequest, SelectElement, WebResourceLoad, WebViewDelegate,
143};
144
145#[cfg(feature = "media-gstreamer")]
146mod media_platform {
147    #[cfg(any(windows, target_os = "macos"))]
148    mod gstreamer_plugins {
149        include!(concat!(env!("OUT_DIR"), "/gstreamer_plugins.rs"));
150    }
151
152    use servo_media_gstreamer::GStreamerBackend;
153
154    use super::ServoMedia;
155
156    #[cfg(any(windows, target_os = "macos"))]
157    pub fn init() {
158        ServoMedia::init_with_backend(|| {
159            let mut plugin_dir = std::env::current_exe().unwrap();
160            plugin_dir.pop();
161
162            if cfg!(target_os = "macos") {
163                plugin_dir.push("lib");
164            }
165
166            match GStreamerBackend::init_with_plugins(
167                plugin_dir,
168                gstreamer_plugins::GSTREAMER_PLUGINS,
169            ) {
170                Ok(b) => b,
171                Err(e) => {
172                    eprintln!("Error initializing GStreamer: {:?}", e);
173                    std::process::exit(1);
174                },
175            }
176        });
177    }
178
179    #[cfg(not(any(windows, target_os = "macos")))]
180    pub fn init() {
181        ServoMedia::init::<GStreamerBackend>();
182    }
183}
184
185#[cfg(not(feature = "media-gstreamer"))]
186mod media_platform {
187    use super::ServoMedia;
188    pub fn init() {
189        ServoMedia::init::<servo_media_dummy::DummyBackend>();
190    }
191}
192
193/// The in-process interface to Servo.
194///
195/// It does everything necessary to render the web, primarily
196/// orchestrating the interaction between JavaScript, CSS layout,
197/// rendering, and the client window.
198///
199// Clients create an event loop to pump messages between the embedding
200// application and various browser components.
201pub struct Servo {
202    delegate: RefCell<Rc<dyn ServoDelegate>>,
203    compositor: Rc<RefCell<IOCompositor>>,
204    constellation_proxy: ConstellationProxy,
205    embedder_receiver: Receiver<EmbedderMsg>,
206    /// A struct that tracks ongoing JavaScript evaluations and is responsible for
207    /// calling the callback when the evaluation is complete.
208    javascript_evaluator: Rc<RefCell<JavaScriptEvaluator>>,
209    /// Tracks whether we are in the process of shutting down, or have shut down.
210    /// This is shared with `WebView`s and the `ServoRenderer`.
211    shutdown_state: Rc<Cell<ShutdownState>>,
212    /// A map  [`WebView`]s that are managed by this [`Servo`] instance. These are stored
213    /// as `Weak` references so that the embedding application can control their lifetime.
214    /// When accessed, `Servo` will be reponsible for cleaning up the invalid `Weak`
215    /// references.
216    webviews: RefCell<FxHashMap<WebViewId, Weak<RefCell<WebViewInner>>>>,
217    servo_errors: ServoErrorChannel,
218    /// For single-process Servo instances, this field controls the initialization
219    /// and deinitialization of the JS Engine. Multiprocess Servo instances have their
220    /// own instance that exists in the content process instead.
221    _js_engine_setup: Option<JSEngineSetup>,
222    /// Whether or not any WebView in this instance is animating or WebXR is enabled.
223    animating: Cell<bool>,
224}
225
226#[derive(Clone)]
227struct RenderNotifier {
228    compositor_proxy: CompositorProxy,
229}
230
231impl RenderNotifier {
232    pub fn new(compositor_proxy: CompositorProxy) -> RenderNotifier {
233        RenderNotifier { compositor_proxy }
234    }
235}
236
237impl webrender_api::RenderNotifier for RenderNotifier {
238    fn clone(&self) -> Box<dyn webrender_api::RenderNotifier> {
239        Box::new(RenderNotifier::new(self.compositor_proxy.clone()))
240    }
241
242    fn wake_up(&self, _composite_needed: bool) {}
243
244    fn new_frame_ready(
245        &self,
246        document_id: DocumentId,
247        _: FramePublishId,
248        frame_ready_params: &FrameReadyParams,
249    ) {
250        self.compositor_proxy
251            .send(CompositorMsg::NewWebRenderFrameReady(
252                document_id,
253                frame_ready_params.render,
254            ));
255    }
256}
257
258impl Servo {
259    #[servo_tracing::instrument(skip(builder))]
260    fn new(builder: ServoBuilder) -> Self {
261        // Global configuration options, parsed from the command line.
262        let opts = builder.opts.map(|opts| *opts);
263        opts::initialize_options(opts.unwrap_or_default());
264        let opts = opts::get();
265
266        // Set the preferences globally.
267        // TODO: It would be better to make these private to a particular Servo instance.
268        let preferences = builder.preferences.map(|opts| *opts);
269        servo_config::prefs::set(preferences.unwrap_or_default());
270
271        use std::sync::atomic::Ordering;
272
273        style::context::DEFAULT_DISABLE_STYLE_SHARING_CACHE
274            .store(opts.debug.disable_share_style_cache, Ordering::Relaxed);
275        style::context::DEFAULT_DUMP_STYLE_STATISTICS
276            .store(opts.debug.dump_style_statistics, Ordering::Relaxed);
277        style::traversal::IS_SERVO_NONINCREMENTAL_LAYOUT
278            .store(opts.nonincremental_layout, Ordering::Relaxed);
279
280        if !opts.multiprocess {
281            media_platform::init();
282        }
283
284        // Get GL bindings
285        let rendering_context = builder.rendering_context;
286        let webrender_gl = rendering_context.gleam_gl_api();
287
288        // Make sure the gl context is made current.
289        if let Err(err) = rendering_context.make_current() {
290            warn!("Failed to make the rendering context current: {:?}", err);
291        }
292        debug_assert_eq!(webrender_gl.get_error(), gleam::gl::NO_ERROR,);
293
294        // Reserving a namespace to create WebViewId.
295        PipelineNamespace::install(PipelineNamespaceId(0));
296
297        // Get both endpoints of a special channel for communication between
298        // the client window and the compositor. This channel is unique because
299        // messages to client may need to pump a platform-specific event loop
300        // to deliver the message.
301        let event_loop_waker = builder.event_loop_waker;
302        let (compositor_proxy, compositor_receiver) =
303            create_compositor_channel(event_loop_waker.clone());
304        let (embedder_proxy, embedder_receiver) = create_embedder_channel(event_loop_waker.clone());
305        let time_profiler_chan = profile_time::Profiler::create(
306            &opts.time_profiling,
307            opts.time_profiler_trace_path.clone(),
308        );
309        let mem_profiler_chan = profile_mem::Profiler::create();
310
311        let devtools_sender = if pref!(devtools_server_enabled) {
312            Some(devtools::start_server(
313                pref!(devtools_server_port) as u16,
314                embedder_proxy.clone(),
315            ))
316        } else {
317            None
318        };
319
320        let (mut webrender, webrender_api_sender) = {
321            rendering_context.prepare_for_rendering();
322            let render_notifier = Box::new(RenderNotifier::new(compositor_proxy.clone()));
323            let clear_color = servo_config::pref!(shell_background_color_rgba);
324            let clear_color = ColorF::new(
325                clear_color[0] as f32,
326                clear_color[1] as f32,
327                clear_color[2] as f32,
328                clear_color[3] as f32,
329            );
330
331            // Use same texture upload method as Gecko with ANGLE:
332            // https://searchfox.org/mozilla-central/source/gfx/webrender_bindings/src/bindings.rs#1215-1219
333            let upload_method = if webrender_gl.get_string(RENDERER).starts_with("ANGLE") {
334                UploadMethod::Immediate
335            } else {
336                UploadMethod::PixelBuffer(ONE_TIME_USAGE_HINT)
337            };
338            let worker_threads = thread::available_parallelism()
339                .map(|i| i.get())
340                .unwrap_or(pref!(threadpools_fallback_worker_num) as usize)
341                .min(pref!(threadpools_webrender_workers_max).max(1) as usize);
342            let workers = Some(Arc::new(
343                rayon::ThreadPoolBuilder::new()
344                    .num_threads(worker_threads)
345                    .thread_name(|idx| format!("WRWorker#{}", idx))
346                    .build()
347                    .unwrap(),
348            ));
349            webrender::create_webrender_instance(
350                webrender_gl.clone(),
351                render_notifier,
352                webrender::WebRenderOptions {
353                    // We force the use of optimized shaders here because rendering is broken
354                    // on Android emulators with unoptimized shaders. This is due to a known
355                    // issue in the emulator's OpenGL emulation layer.
356                    // See: https://github.com/servo/servo/issues/31726
357                    use_optimized_shaders: true,
358                    resource_override_path: opts.shaders_dir.clone(),
359                    debug_flags: webrender::DebugFlags::empty(),
360                    precache_flags: if pref!(gfx_precache_shaders) {
361                        ShaderPrecacheFlags::FULL_COMPILE
362                    } else {
363                        ShaderPrecacheFlags::empty()
364                    },
365                    enable_aa: pref!(gfx_text_antialiasing_enabled),
366                    enable_subpixel_aa: pref!(gfx_subpixel_text_antialiasing_enabled),
367                    allow_texture_swizzling: pref!(gfx_texture_swizzling_enabled),
368                    clear_color,
369                    upload_method,
370                    workers,
371                    size_of_op: Some(servo_allocator::usable_size),
372                    ..Default::default()
373                },
374                None,
375            )
376            .expect("Unable to initialize webrender!")
377        };
378
379        let webrender_api = webrender_api_sender.create_api();
380        let webrender_document = webrender_api.add_document(rendering_context.size2d().to_i32());
381
382        // Important that this call is done in a single-threaded fashion, we
383        // can't defer it after `create_constellation` has started.
384        let js_engine_setup = if !opts.multiprocess {
385            Some(script::init())
386        } else {
387            None
388        };
389
390        // Create the webgl thread
391        let gl_type = match webrender_gl.get_type() {
392            gleam::gl::GlType::Gl => GlType::Gl,
393            gleam::gl::GlType::Gles => GlType::Gles,
394        };
395
396        let (external_image_handlers, external_images) = WebrenderExternalImageHandlers::new();
397        let mut external_image_handlers = Box::new(external_image_handlers);
398
399        let WebGLComm {
400            webgl_threads,
401            #[cfg(feature = "webxr")]
402            webxr_layer_grand_manager,
403            image_handler,
404        } = WebGLComm::new(
405            rendering_context.clone(),
406            compositor_proxy.cross_process_compositor_api.clone(),
407            webrender_api.create_sender(),
408            external_images.clone(),
409            gl_type,
410        );
411
412        // Set webrender external image handler for WebGL textures
413        external_image_handlers.set_handler(image_handler, WebrenderImageHandlerType::WebGL);
414
415        // Create the WebXR main thread
416        #[cfg(feature = "webxr")]
417        let mut webxr_main_thread =
418            webxr::MainThreadRegistry::new(event_loop_waker.clone(), webxr_layer_grand_manager)
419                .expect("Failed to create WebXR device registry");
420        #[cfg(feature = "webxr")]
421        if pref!(dom_webxr_enabled) {
422            builder.webxr_registry.register(&mut webxr_main_thread);
423        }
424
425        #[cfg(feature = "webgpu")]
426        let wgpu_image_handler = webgpu::WGPUExternalImages::default();
427        #[cfg(feature = "webgpu")]
428        let wgpu_image_map = wgpu_image_handler.images.clone();
429        #[cfg(feature = "webgpu")]
430        external_image_handlers.set_handler(
431            Box::new(wgpu_image_handler),
432            WebrenderImageHandlerType::WebGPU,
433        );
434
435        WindowGLContext::initialize_image_handler(
436            &mut external_image_handlers,
437            external_images.clone(),
438        );
439
440        webrender.set_external_image_handler(external_image_handlers);
441
442        // Create the constellation, which maintains the engine pipelines, including script and
443        // layout, as well as the navigation context.
444        let mut protocols = ProtocolRegistry::with_internal_protocols();
445        protocols.merge(builder.protocol_registry);
446
447        let constellation_chan = create_constellation(
448            opts.config_dir.clone(),
449            embedder_proxy,
450            compositor_proxy.clone(),
451            time_profiler_chan.clone(),
452            mem_profiler_chan.clone(),
453            devtools_sender,
454            webrender_document,
455            webrender_api_sender,
456            #[cfg(feature = "webxr")]
457            webxr_main_thread.registry(),
458            Some(webgl_threads),
459            external_images,
460            #[cfg(feature = "webgpu")]
461            wgpu_image_map,
462            protocols,
463            builder.user_content_manager,
464        );
465
466        // The compositor coordinates with the client window to create the final
467        // rendered page and display it somewhere.
468        let shutdown_state = Rc::new(Cell::new(ShutdownState::NotShuttingDown));
469        let compositor = IOCompositor::new(InitialCompositorState {
470            sender: compositor_proxy,
471            receiver: compositor_receiver,
472            constellation_chan: constellation_chan.clone(),
473            time_profiler_chan,
474            mem_profiler_chan,
475            webrender,
476            webrender_document,
477            webrender_api,
478            rendering_context,
479            webrender_gl,
480            #[cfg(feature = "webxr")]
481            webxr_main_thread,
482            shutdown_state: shutdown_state.clone(),
483            event_loop_waker,
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::InputEventHandled(webview_id, input_event_id, result) => {
768                if let Some(webview) = self.get_webview_handle(webview_id) {
769                    webview
770                        .delegate()
771                        .notify_input_event_handled(webview, input_event_id, result);
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::ShowEmbedderControl(control_id, position, form_control) => {
988                if let Some(webview) = self.get_webview_handle(control_id.webview_id) {
989                    let constellation_proxy = self.constellation_proxy.clone();
990                    let form_control = match form_control {
991                        EmbedderFormControl::SelectElement(options, selected_option) => {
992                            FormControl::SelectElement(SelectElement {
993                                id: control_id,
994                                options,
995                                selected_option,
996                                position,
997                                constellation_proxy,
998                                response_sent: false,
999                            })
1000                        },
1001                        EmbedderFormControl::ColorPicker(current_color) => {
1002                            FormControl::ColorPicker(ColorPicker {
1003                                id: control_id,
1004                                current_color: Some(current_color),
1005                                position,
1006                                constellation_proxy,
1007                                response_sent: false,
1008                            })
1009                        },
1010                    };
1011
1012                    webview.delegate().show_form_control(webview, form_control);
1013                }
1014            },
1015            EmbedderMsg::GetWindowRect(webview_id, response_sender) => {
1016                let window_rect = || {
1017                    let Some(webview) = self.get_webview_handle(webview_id) else {
1018                        return DeviceIndependentIntRect::default();
1019                    };
1020                    let hidpi_scale_factor = webview.hidpi_scale_factor();
1021                    let Some(screen_geometry) = webview.delegate().screen_geometry(webview) else {
1022                        return DeviceIndependentIntRect::default();
1023                    };
1024
1025                    convert_rect_to_css_pixel(screen_geometry.window_rect, hidpi_scale_factor)
1026                };
1027
1028                if let Err(error) = response_sender.send(window_rect()) {
1029                    warn!("Failed to respond to GetWindowRect: {error}");
1030                }
1031            },
1032            EmbedderMsg::GetScreenMetrics(webview_id, response_sender) => {
1033                let screen_metrics = || {
1034                    let Some(webview) = self.get_webview_handle(webview_id) else {
1035                        return ScreenMetrics::default();
1036                    };
1037                    let hidpi_scale_factor = webview.hidpi_scale_factor();
1038                    let Some(screen_geometry) = webview.delegate().screen_geometry(webview) else {
1039                        return ScreenMetrics::default();
1040                    };
1041
1042                    ScreenMetrics {
1043                        screen_size: convert_size_to_css_pixel(
1044                            screen_geometry.size,
1045                            hidpi_scale_factor,
1046                        ),
1047                        available_size: convert_size_to_css_pixel(
1048                            screen_geometry.available_size,
1049                            hidpi_scale_factor,
1050                        ),
1051                    }
1052                };
1053                if let Err(error) = response_sender.send(screen_metrics()) {
1054                    warn!("Failed to respond to GetScreenMetrics: {error}");
1055                }
1056            },
1057        }
1058    }
1059
1060    pub fn constellation_sender(&self) -> Sender<EmbedderToConstellationMessage> {
1061        self.constellation_proxy.sender()
1062    }
1063
1064    pub fn execute_webdriver_command(&self, command: WebDriverCommandMsg) {
1065        self.constellation_proxy
1066            .send(EmbedderToConstellationMessage::WebDriverCommand(command));
1067    }
1068
1069    pub fn set_preference(&self, name: &str, value: PrefValue) {
1070        let mut preferences = prefs::get().clone();
1071        preferences.set_value(name, value);
1072        prefs::set(preferences);
1073    }
1074}
1075
1076fn create_embedder_channel(
1077    event_loop_waker: Box<dyn EventLoopWaker>,
1078) -> (EmbedderProxy, Receiver<EmbedderMsg>) {
1079    let (sender, receiver) = unbounded();
1080    (
1081        EmbedderProxy {
1082            sender,
1083            event_loop_waker,
1084        },
1085        receiver,
1086    )
1087}
1088
1089fn create_compositor_channel(
1090    event_loop_waker: Box<dyn EventLoopWaker>,
1091) -> (CompositorProxy, RoutedReceiver<CompositorMsg>) {
1092    let (sender, receiver) = unbounded();
1093    let sender_clone = sender.clone();
1094    let event_loop_waker_clone = event_loop_waker.clone();
1095    // This callback is equivalent to `CompositorProxy::send`
1096    let result_callback = move |msg: Result<CompositorMsg, ipc_channel::Error>| {
1097        if let Err(err) = sender_clone.send(msg) {
1098            warn!("Failed to send response ({:?}).", err);
1099        }
1100        event_loop_waker_clone.wake();
1101    };
1102
1103    let generic_callback =
1104        GenericCallback::new(result_callback).expect("Failed to create callback");
1105    let cross_process_compositor_api = CrossProcessCompositorApi::new(generic_callback);
1106    let compositor_proxy = CompositorProxy {
1107        sender,
1108        cross_process_compositor_api,
1109        event_loop_waker,
1110    };
1111
1112    (compositor_proxy, receiver)
1113}
1114
1115#[allow(clippy::too_many_arguments)]
1116fn create_constellation(
1117    config_dir: Option<PathBuf>,
1118    embedder_proxy: EmbedderProxy,
1119    compositor_proxy: CompositorProxy,
1120    time_profiler_chan: time::ProfilerChan,
1121    mem_profiler_chan: mem::ProfilerChan,
1122    devtools_sender: Option<Sender<devtools_traits::DevtoolsControlMsg>>,
1123    webrender_document: DocumentId,
1124    webrender_api_sender: RenderApiSender,
1125    #[cfg(feature = "webxr")] webxr_registry: webxr_api::Registry,
1126    webgl_threads: Option<WebGLThreads>,
1127    external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
1128    #[cfg(feature = "webgpu")] wgpu_image_map: WGPUImageMap,
1129    protocols: ProtocolRegistry,
1130    user_content_manager: UserContentManager,
1131) -> Sender<EmbedderToConstellationMessage> {
1132    // Global configuration options, parsed from the command line.
1133    let opts = opts::get();
1134
1135    #[cfg(feature = "bluetooth")]
1136    let bluetooth_thread: IpcSender<BluetoothRequest> =
1137        BluetoothThreadFactory::new(embedder_proxy.clone());
1138
1139    let privileged_urls = protocols.privileged_urls();
1140
1141    let (public_resource_threads, private_resource_threads, async_runtime) = new_resource_threads(
1142        devtools_sender.clone(),
1143        time_profiler_chan.clone(),
1144        mem_profiler_chan.clone(),
1145        embedder_proxy.clone(),
1146        config_dir.clone(),
1147        opts.certificate_path.clone(),
1148        opts.ignore_certificate_errors,
1149        Arc::new(protocols),
1150    );
1151
1152    let (private_storage_threads, public_storage_threads) =
1153        new_storage_threads(mem_profiler_chan.clone(), config_dir);
1154
1155    let system_font_service = Arc::new(
1156        SystemFontService::spawn(
1157            compositor_proxy.cross_process_compositor_api.clone(),
1158            mem_profiler_chan.clone(),
1159        )
1160        .to_proxy(),
1161    );
1162
1163    let initial_state = InitialConstellationState {
1164        compositor_proxy,
1165        embedder_proxy,
1166        devtools_sender,
1167        #[cfg(feature = "bluetooth")]
1168        bluetooth_thread,
1169        system_font_service,
1170        public_resource_threads,
1171        private_resource_threads,
1172        public_storage_threads,
1173        private_storage_threads,
1174        time_profiler_chan,
1175        mem_profiler_chan,
1176        webrender_document,
1177        webrender_api_sender,
1178        #[cfg(feature = "webxr")]
1179        webxr_registry: Some(webxr_registry),
1180        #[cfg(not(feature = "webxr"))]
1181        webxr_registry: None,
1182        webgl_threads,
1183        webrender_external_images: external_images,
1184        #[cfg(feature = "webgpu")]
1185        wgpu_image_map,
1186        user_content_manager,
1187        async_runtime,
1188        privileged_urls,
1189    };
1190
1191    let layout_factory = Arc::new(LayoutFactoryImpl());
1192
1193    Constellation::<script::ScriptThread, script::ServiceWorkerManager>::start(
1194        initial_state,
1195        layout_factory,
1196        opts.random_pipeline_closure_probability,
1197        opts.random_pipeline_closure_seed,
1198        opts.hard_fail,
1199    )
1200}
1201
1202// A logger that logs to two downstream loggers.
1203// This should probably be in the log crate.
1204struct BothLogger<Log1, Log2>(Log1, Log2);
1205
1206impl<Log1, Log2> Log for BothLogger<Log1, Log2>
1207where
1208    Log1: Log,
1209    Log2: Log,
1210{
1211    fn enabled(&self, metadata: &Metadata) -> bool {
1212        self.0.enabled(metadata) || self.1.enabled(metadata)
1213    }
1214
1215    fn log(&self, record: &Record) {
1216        self.0.log(record);
1217        self.1.log(record);
1218    }
1219
1220    fn flush(&self) {
1221        self.0.flush();
1222        self.1.flush();
1223    }
1224}
1225
1226pub fn set_logger(script_to_constellation_chan: ScriptToConstellationChan) {
1227    let con_logger = FromScriptLogger::new(script_to_constellation_chan);
1228    let env = env_logger::Env::default();
1229    let env_logger = EnvLoggerBuilder::from_env(env).build();
1230
1231    let filter = max(env_logger.filter(), con_logger.filter());
1232    let logger = BothLogger(env_logger, con_logger);
1233
1234    log::set_boxed_logger(Box::new(logger)).expect("Failed to set logger.");
1235    log::set_max_level(filter);
1236}
1237
1238/// Content process entry point.
1239pub fn run_content_process(token: String) {
1240    let (unprivileged_content_sender, unprivileged_content_receiver) =
1241        ipc::channel::<UnprivilegedContent>().unwrap();
1242    let connection_bootstrap: IpcSender<IpcSender<UnprivilegedContent>> =
1243        IpcSender::connect(token).unwrap();
1244    connection_bootstrap
1245        .send(unprivileged_content_sender)
1246        .unwrap();
1247
1248    let unprivileged_content = unprivileged_content_receiver.recv().unwrap();
1249    opts::initialize_options(unprivileged_content.opts());
1250    prefs::set(unprivileged_content.prefs().clone());
1251
1252    // Enter the sandbox if necessary.
1253    if opts::get().sandbox {
1254        create_sandbox();
1255    }
1256
1257    let _js_engine_setup = script::init();
1258
1259    match unprivileged_content {
1260        UnprivilegedContent::Pipeline(mut content) => {
1261            media_platform::init();
1262
1263            // Start the fetch thread for this content process.
1264            let fetch_thread_join_handle = start_fetch_thread();
1265
1266            set_logger(content.script_to_constellation_chan().clone());
1267
1268            let (background_hang_monitor_register, join_handle) =
1269                content.register_with_background_hang_monitor();
1270            let layout_factory = Arc::new(LayoutFactoryImpl());
1271
1272            content.register_system_memory_reporter();
1273
1274            let script_join_handle = content.start_all::<script::ScriptThread>(
1275                true,
1276                layout_factory,
1277                background_hang_monitor_register,
1278                None,
1279            );
1280
1281            // Since wait_for_completion is true,
1282            // here we know that the script-thread
1283            // will exit(or already has),
1284            // and so we can join first on the script, and then on the BHM worker, threads.
1285            script_join_handle
1286                .join()
1287                .expect("Failed to join on the script thread.");
1288            join_handle
1289                .join()
1290                .expect("Failed to join on the BHM background thread.");
1291
1292            StyleThreadPool::shutdown();
1293
1294            // Shut down the fetch thread started above.
1295            exit_fetch_thread();
1296            fetch_thread_join_handle
1297                .join()
1298                .expect("Failed to join on the fetch thread in the constellation");
1299        },
1300        UnprivilegedContent::ServiceWorker(content) => {
1301            content.start::<ServiceWorkerManager>();
1302        },
1303    }
1304}
1305
1306#[cfg(all(
1307    not(target_os = "windows"),
1308    not(target_os = "ios"),
1309    not(target_os = "android"),
1310    not(target_arch = "arm"),
1311    not(target_arch = "aarch64"),
1312    not(target_env = "ohos"),
1313))]
1314fn create_sandbox() {
1315    ChildSandbox::new(content_process_sandbox_profile())
1316        .activate()
1317        .expect("Failed to activate sandbox!");
1318}
1319
1320#[cfg(any(
1321    target_os = "windows",
1322    target_os = "ios",
1323    target_os = "android",
1324    target_arch = "arm",
1325    target_arch = "aarch64",
1326    target_env = "ohos",
1327))]
1328fn create_sandbox() {
1329    panic!("Sandboxing is not supported on Windows, iOS, ARM targets and android.");
1330}
1331
1332struct DefaultEventLoopWaker;
1333
1334impl EventLoopWaker for DefaultEventLoopWaker {
1335    fn clone_box(&self) -> Box<dyn EventLoopWaker> {
1336        Box::new(DefaultEventLoopWaker)
1337    }
1338}
1339
1340#[cfg(feature = "webxr")]
1341struct DefaultWebXrRegistry;
1342#[cfg(feature = "webxr")]
1343impl webxr::WebXrRegistry for DefaultWebXrRegistry {}
1344
1345pub struct ServoBuilder {
1346    rendering_context: Rc<dyn RenderingContext>,
1347    opts: Option<Box<Opts>>,
1348    preferences: Option<Box<Preferences>>,
1349    event_loop_waker: Box<dyn EventLoopWaker>,
1350    user_content_manager: UserContentManager,
1351    protocol_registry: ProtocolRegistry,
1352    #[cfg(feature = "webxr")]
1353    webxr_registry: Box<dyn webxr::WebXrRegistry>,
1354}
1355
1356impl ServoBuilder {
1357    pub fn new(rendering_context: Rc<dyn RenderingContext>) -> Self {
1358        Self {
1359            rendering_context,
1360            opts: None,
1361            preferences: None,
1362            event_loop_waker: Box::new(DefaultEventLoopWaker),
1363            user_content_manager: UserContentManager::default(),
1364            protocol_registry: ProtocolRegistry::default(),
1365            #[cfg(feature = "webxr")]
1366            webxr_registry: Box::new(DefaultWebXrRegistry),
1367        }
1368    }
1369
1370    pub fn build(self) -> Servo {
1371        Servo::new(self)
1372    }
1373
1374    pub fn opts(mut self, opts: Opts) -> Self {
1375        self.opts = Some(Box::new(opts));
1376        self
1377    }
1378
1379    pub fn preferences(mut self, preferences: Preferences) -> Self {
1380        self.preferences = Some(Box::new(preferences));
1381        self
1382    }
1383
1384    pub fn event_loop_waker(mut self, event_loop_waker: Box<dyn EventLoopWaker>) -> Self {
1385        self.event_loop_waker = event_loop_waker;
1386        self
1387    }
1388
1389    pub fn user_content_manager(mut self, user_content_manager: UserContentManager) -> Self {
1390        self.user_content_manager = user_content_manager;
1391        self
1392    }
1393
1394    pub fn protocol_registry(mut self, protocol_registry: ProtocolRegistry) -> Self {
1395        self.protocol_registry = protocol_registry;
1396        self
1397    }
1398
1399    #[cfg(feature = "webxr")]
1400    pub fn webxr_registry(mut self, webxr_registry: Box<dyn webxr::WebXrRegistry>) -> Self {
1401        self.webxr_registry = webxr_registry;
1402        self
1403    }
1404}