1mod 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
193pub struct Servo {
202 delegate: RefCell<Rc<dyn ServoDelegate>>,
203 compositor: Rc<RefCell<IOCompositor>>,
204 constellation_proxy: ConstellationProxy,
205 embedder_receiver: Receiver<EmbedderMsg>,
206 javascript_evaluator: Rc<RefCell<JavaScriptEvaluator>>,
209 shutdown_state: Rc<Cell<ShutdownState>>,
212 webviews: RefCell<FxHashMap<WebViewId, Weak<RefCell<WebViewInner>>>>,
217 servo_errors: ServoErrorChannel,
218 _js_engine_setup: Option<JSEngineSetup>,
222 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 let opts = builder.opts.map(|opts| *opts);
263 opts::initialize_options(opts.unwrap_or_default());
264 let opts = opts::get();
265
266 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 let rendering_context = builder.rendering_context;
286 let webrender_gl = rendering_context.gleam_gl_api();
287
288 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 PipelineNamespace::install(PipelineNamespaceId(0));
296
297 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 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 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 let js_engine_setup = if !opts.multiprocess {
385 Some(script::init())
386 } else {
387 None
388 };
389
390 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 external_image_handlers.set_handler(image_handler, WebrenderImageHandlerType::WebGL);
414
415 #[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 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 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 pub fn animating(&self) -> bool {
517 self.animating.get()
518 }
519
520 pub fn initialize_gl_accelerated_media(display: NativeDisplay, api: GlApi, context: GlContext) {
523 WindowGLContext::initialize(display, api, context)
524 }
525
526 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 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 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 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 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
1202struct 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
1238pub 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 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 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 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 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}