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