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};
66use constellation_traits::{EmbedderToConstellationMessage, ScriptToConstellationChan};
67use crossbeam_channel::{Receiver, Sender, unbounded};
68use embedder_traits::FormControl as EmbedderFormControl;
69use embedder_traits::user_content_manager::UserContentManager;
70pub use embedder_traits::*;
71use env_logger::Builder as EnvLoggerBuilder;
72use fonts::SystemFontService;
73#[cfg(all(
74 not(target_os = "windows"),
75 not(target_os = "ios"),
76 not(target_os = "android"),
77 not(target_arch = "arm"),
78 not(target_arch = "aarch64"),
79 not(target_env = "ohos"),
80))]
81use gaol::sandbox::{ChildSandbox, ChildSandboxMethods};
82pub use gleam::gl;
83use gleam::gl::RENDERER;
84use ipc_channel::ipc::{self, IpcSender};
85use javascript_evaluator::JavaScriptEvaluator;
86pub use keyboard_types::{
87 Code, CompositionEvent, CompositionState, Key, KeyState, Location, Modifiers, NamedKey,
88};
89use layout::LayoutFactoryImpl;
90use log::{Log, Metadata, Record, debug, error, warn};
91use media::{GlApi, NativeDisplay, WindowGLContext};
92use net::protocols::ProtocolRegistry;
93use net::resource_thread::new_resource_threads;
94use net_traits::{exit_fetch_thread, start_fetch_thread};
95use profile::{mem as profile_mem, time as profile_time};
96use profile_traits::mem::MemoryReportResult;
97use profile_traits::{mem, time};
98use rustc_hash::FxHashMap;
99use script::{JSEngineSetup, ServiceWorkerManager};
100use servo_config::opts::Opts;
101pub use servo_config::prefs::PrefValue;
102use servo_config::prefs::Preferences;
103use servo_config::{opts, pref, prefs};
104use servo_delegate::DefaultServoDelegate;
105use servo_geometry::{
106 DeviceIndependentIntRect, convert_rect_to_css_pixel, convert_size_to_css_pixel,
107};
108use servo_media::ServoMedia;
109use servo_media::player::context::GlContext;
110use servo_url::ServoUrl;
111use style::global_style_data::StyleThreadPool;
112use webgl::WebGLComm;
113#[cfg(feature = "webgpu")]
114pub use webgpu;
115#[cfg(feature = "webgpu")]
116use webgpu::canvas_context::WGPUImageMap;
117use webrender::{ONE_TIME_USAGE_HINT, RenderApiSender, ShaderPrecacheFlags, UploadMethod};
118use webrender_api::{ColorF, DocumentId, FramePublishId};
119use webview::WebViewInner;
120#[cfg(feature = "webxr")]
121pub use webxr;
122pub use {
123 background_hang_monitor, base, canvas_traits, devtools, devtools_traits, euclid, fonts,
124 ipc_channel, layout_api, media, net, net_traits, profile, profile_traits, script,
125 script_traits, servo_config as config, servo_config, servo_geometry, servo_url, style,
126 style_traits, webrender_api,
127};
128#[cfg(feature = "bluetooth")]
129pub use {bluetooth, bluetooth_traits};
130
131use crate::proxies::ConstellationProxy;
132use crate::responders::ServoErrorChannel;
133pub use crate::servo_delegate::{ServoDelegate, ServoError};
134use crate::webrender_api::FrameReadyParams;
135use crate::webview::MINIMUM_WEBVIEW_SIZE;
136pub use crate::webview::{WebView, WebViewBuilder};
137pub use crate::webview_delegate::{
138 AllowOrDenyRequest, AuthenticationRequest, ColorPicker, FormControl, NavigationRequest,
139 PermissionRequest, SelectElement, WebResourceLoad, WebViewDelegate,
140};
141
142#[cfg(feature = "media-gstreamer")]
143mod media_platform {
144 #[cfg(any(windows, target_os = "macos"))]
145 mod gstreamer_plugins {
146 include!(concat!(env!("OUT_DIR"), "/gstreamer_plugins.rs"));
147 }
148
149 use servo_media_gstreamer::GStreamerBackend;
150
151 use super::ServoMedia;
152
153 #[cfg(any(windows, target_os = "macos"))]
154 pub fn init() {
155 ServoMedia::init_with_backend(|| {
156 let mut plugin_dir = std::env::current_exe().unwrap();
157 plugin_dir.pop();
158
159 if cfg!(target_os = "macos") {
160 plugin_dir.push("lib");
161 }
162
163 match GStreamerBackend::init_with_plugins(
164 plugin_dir,
165 gstreamer_plugins::GSTREAMER_PLUGINS,
166 ) {
167 Ok(b) => b,
168 Err(e) => {
169 eprintln!("Error initializing GStreamer: {:?}", e);
170 std::process::exit(1);
171 },
172 }
173 });
174 }
175
176 #[cfg(not(any(windows, target_os = "macos")))]
177 pub fn init() {
178 ServoMedia::init::<GStreamerBackend>();
179 }
180}
181
182#[cfg(not(feature = "media-gstreamer"))]
183mod media_platform {
184 use super::ServoMedia;
185 pub fn init() {
186 ServoMedia::init::<servo_media_dummy::DummyBackend>();
187 }
188}
189
190pub struct Servo {
199 delegate: RefCell<Rc<dyn ServoDelegate>>,
200 compositor: Rc<RefCell<IOCompositor>>,
201 constellation_proxy: ConstellationProxy,
202 embedder_receiver: Receiver<EmbedderMsg>,
203 javascript_evaluator: Rc<RefCell<JavaScriptEvaluator>>,
206 shutdown_state: Rc<Cell<ShutdownState>>,
209 webviews: RefCell<FxHashMap<WebViewId, Weak<RefCell<WebViewInner>>>>,
214 servo_errors: ServoErrorChannel,
215 _js_engine_setup: Option<JSEngineSetup>,
219 animating: Cell<bool>,
221}
222
223#[derive(Clone)]
224struct RenderNotifier {
225 compositor_proxy: CompositorProxy,
226}
227
228impl RenderNotifier {
229 pub fn new(compositor_proxy: CompositorProxy) -> RenderNotifier {
230 RenderNotifier { compositor_proxy }
231 }
232}
233
234impl webrender_api::RenderNotifier for RenderNotifier {
235 fn clone(&self) -> Box<dyn webrender_api::RenderNotifier> {
236 Box::new(RenderNotifier::new(self.compositor_proxy.clone()))
237 }
238
239 fn wake_up(&self, _composite_needed: bool) {}
240
241 fn new_frame_ready(
242 &self,
243 document_id: DocumentId,
244 _: FramePublishId,
245 frame_ready_params: &FrameReadyParams,
246 ) {
247 self.compositor_proxy
248 .send(CompositorMsg::NewWebRenderFrameReady(
249 document_id,
250 frame_ready_params.render,
251 ));
252 }
253}
254
255impl Servo {
256 #[servo_tracing::instrument(skip(builder))]
257 fn new(builder: ServoBuilder) -> Self {
258 let opts = builder.opts.map(|opts| *opts);
260 opts::initialize_options(opts.unwrap_or_default());
261 let opts = opts::get();
262
263 let preferences = builder.preferences.map(|opts| *opts);
266 servo_config::prefs::set(preferences.unwrap_or_default());
267
268 use std::sync::atomic::Ordering;
269
270 style::context::DEFAULT_DISABLE_STYLE_SHARING_CACHE
271 .store(opts.debug.disable_share_style_cache, Ordering::Relaxed);
272 style::context::DEFAULT_DUMP_STYLE_STATISTICS
273 .store(opts.debug.dump_style_statistics, Ordering::Relaxed);
274 style::traversal::IS_SERVO_NONINCREMENTAL_LAYOUT
275 .store(opts.nonincremental_layout, Ordering::Relaxed);
276
277 if !opts.multiprocess {
278 media_platform::init();
279 }
280
281 let rendering_context = builder.rendering_context;
283 let webrender_gl = rendering_context.gleam_gl_api();
284
285 if let Err(err) = rendering_context.make_current() {
287 warn!("Failed to make the rendering context current: {:?}", err);
288 }
289 debug_assert_eq!(webrender_gl.get_error(), gleam::gl::NO_ERROR,);
290
291 PipelineNamespace::install(PipelineNamespaceId(0));
293
294 let event_loop_waker = builder.event_loop_waker;
299 let (compositor_proxy, compositor_receiver) =
300 create_compositor_channel(event_loop_waker.clone());
301 let (embedder_proxy, embedder_receiver) = create_embedder_channel(event_loop_waker.clone());
302 let time_profiler_chan = profile_time::Profiler::create(
303 &opts.time_profiling,
304 opts.time_profiler_trace_path.clone(),
305 );
306 let mem_profiler_chan = profile_mem::Profiler::create();
307
308 let devtools_sender = if pref!(devtools_server_enabled) {
309 Some(devtools::start_server(
310 pref!(devtools_server_port) as u16,
311 embedder_proxy.clone(),
312 ))
313 } else {
314 None
315 };
316
317 let (mut webrender, webrender_api_sender) = {
318 rendering_context.prepare_for_rendering();
319 let render_notifier = Box::new(RenderNotifier::new(compositor_proxy.clone()));
320 let clear_color = servo_config::pref!(shell_background_color_rgba);
321 let clear_color = ColorF::new(
322 clear_color[0] as f32,
323 clear_color[1] as f32,
324 clear_color[2] as f32,
325 clear_color[3] as f32,
326 );
327
328 let upload_method = if webrender_gl.get_string(RENDERER).starts_with("ANGLE") {
331 UploadMethod::Immediate
332 } else {
333 UploadMethod::PixelBuffer(ONE_TIME_USAGE_HINT)
334 };
335 let worker_threads = thread::available_parallelism()
336 .map(|i| i.get())
337 .unwrap_or(pref!(threadpools_fallback_worker_num) as usize)
338 .min(pref!(threadpools_webrender_workers_max).max(1) as usize);
339 let workers = Some(Arc::new(
340 rayon::ThreadPoolBuilder::new()
341 .num_threads(worker_threads)
342 .thread_name(|idx| format!("WRWorker#{}", idx))
343 .build()
344 .unwrap(),
345 ));
346 webrender::create_webrender_instance(
347 webrender_gl.clone(),
348 render_notifier,
349 webrender::WebRenderOptions {
350 use_optimized_shaders: true,
355 resource_override_path: opts.shaders_dir.clone(),
356 debug_flags: webrender::DebugFlags::empty(),
357 precache_flags: if pref!(gfx_precache_shaders) {
358 ShaderPrecacheFlags::FULL_COMPILE
359 } else {
360 ShaderPrecacheFlags::empty()
361 },
362 enable_aa: pref!(gfx_text_antialiasing_enabled),
363 enable_subpixel_aa: pref!(gfx_subpixel_text_antialiasing_enabled),
364 allow_texture_swizzling: pref!(gfx_texture_swizzling_enabled),
365 clear_color,
366 upload_method,
367 workers,
368 size_of_op: Some(servo_allocator::usable_size),
369 ..Default::default()
370 },
371 None,
372 )
373 .expect("Unable to initialize webrender!")
374 };
375
376 let webrender_api = webrender_api_sender.create_api();
377 let webrender_document = webrender_api.add_document(rendering_context.size2d().to_i32());
378
379 let js_engine_setup = if !opts.multiprocess {
382 Some(script::init())
383 } else {
384 None
385 };
386
387 let gl_type = match webrender_gl.get_type() {
389 gleam::gl::GlType::Gl => GlType::Gl,
390 gleam::gl::GlType::Gles => GlType::Gles,
391 };
392
393 let (external_image_handlers, external_images) = WebrenderExternalImageHandlers::new();
394 let mut external_image_handlers = Box::new(external_image_handlers);
395
396 let WebGLComm {
397 webgl_threads,
398 #[cfg(feature = "webxr")]
399 webxr_layer_grand_manager,
400 image_handler,
401 } = WebGLComm::new(
402 rendering_context.clone(),
403 compositor_proxy.cross_process_compositor_api.clone(),
404 webrender_api.create_sender(),
405 external_images.clone(),
406 gl_type,
407 );
408
409 external_image_handlers.set_handler(image_handler, WebrenderImageHandlerType::WebGL);
411
412 #[cfg(feature = "webxr")]
414 let mut webxr_main_thread =
415 webxr::MainThreadRegistry::new(event_loop_waker.clone(), webxr_layer_grand_manager)
416 .expect("Failed to create WebXR device registry");
417 #[cfg(feature = "webxr")]
418 if pref!(dom_webxr_enabled) {
419 builder.webxr_registry.register(&mut webxr_main_thread);
420 }
421
422 #[cfg(feature = "webgpu")]
423 let wgpu_image_handler = webgpu::WGPUExternalImages::default();
424 #[cfg(feature = "webgpu")]
425 let wgpu_image_map = wgpu_image_handler.images.clone();
426 #[cfg(feature = "webgpu")]
427 external_image_handlers.set_handler(
428 Box::new(wgpu_image_handler),
429 WebrenderImageHandlerType::WebGPU,
430 );
431
432 WindowGLContext::initialize_image_handler(
433 &mut external_image_handlers,
434 external_images.clone(),
435 );
436
437 webrender.set_external_image_handler(external_image_handlers);
438
439 let mut protocols = ProtocolRegistry::with_internal_protocols();
442 protocols.merge(builder.protocol_registry);
443
444 let constellation_chan = create_constellation(
445 opts.config_dir.clone(),
446 embedder_proxy,
447 compositor_proxy.clone(),
448 time_profiler_chan.clone(),
449 mem_profiler_chan.clone(),
450 devtools_sender,
451 webrender_document,
452 webrender_api_sender,
453 #[cfg(feature = "webxr")]
454 webxr_main_thread.registry(),
455 Some(webgl_threads),
456 external_images,
457 #[cfg(feature = "webgpu")]
458 wgpu_image_map,
459 protocols,
460 builder.user_content_manager,
461 );
462
463 let shutdown_state = Rc::new(Cell::new(ShutdownState::NotShuttingDown));
466 let compositor = IOCompositor::new(
467 InitialCompositorState {
468 sender: compositor_proxy,
469 receiver: compositor_receiver,
470 constellation_chan: constellation_chan.clone(),
471 time_profiler_chan,
472 mem_profiler_chan,
473 webrender,
474 webrender_document,
475 webrender_api,
476 rendering_context,
477 webrender_gl,
478 #[cfg(feature = "webxr")]
479 webxr_main_thread,
480 shutdown_state: shutdown_state.clone(),
481 event_loop_waker,
482 },
483 opts.debug.convert_mouse_to_touch,
484 );
485
486 let constellation_proxy = ConstellationProxy::new(constellation_chan);
487 Self {
488 delegate: RefCell::new(Rc::new(DefaultServoDelegate)),
489 compositor: Rc::new(RefCell::new(compositor)),
490 javascript_evaluator: Rc::new(RefCell::new(JavaScriptEvaluator::new(
491 constellation_proxy.clone(),
492 ))),
493 constellation_proxy,
494 embedder_receiver,
495 shutdown_state,
496 webviews: Default::default(),
497 servo_errors: ServoErrorChannel::default(),
498 _js_engine_setup: js_engine_setup,
499 animating: Cell::new(false),
500 }
501 }
502
503 pub fn delegate(&self) -> Rc<dyn ServoDelegate> {
504 self.delegate.borrow().clone()
505 }
506
507 pub fn set_delegate(&self, delegate: Rc<dyn ServoDelegate>) {
508 *self.delegate.borrow_mut() = delegate;
509 }
510
511 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::Keyboard(webview_id, keyboard_event) => {
768 if let Some(webview) = self.get_webview_handle(webview_id) {
769 webview
770 .delegate()
771 .notify_keyboard_event(webview, keyboard_event);
772 }
773 },
774 EmbedderMsg::ClearClipboard(webview_id) => {
775 if let Some(webview) = self.get_webview_handle(webview_id) {
776 webview.clipboard_delegate().clear(webview);
777 }
778 },
779 EmbedderMsg::GetClipboardText(webview_id, result_sender) => {
780 if let Some(webview) = self.get_webview_handle(webview_id) {
781 webview
782 .clipboard_delegate()
783 .get_text(webview, StringRequest::from(result_sender));
784 }
785 },
786 EmbedderMsg::SetClipboardText(webview_id, string) => {
787 if let Some(webview) = self.get_webview_handle(webview_id) {
788 webview.clipboard_delegate().set_text(webview, string);
789 }
790 },
791 EmbedderMsg::SetCursor(webview_id, cursor) => {
792 if let Some(webview) = self.get_webview_handle(webview_id) {
793 webview.set_cursor(cursor);
794 }
795 },
796 EmbedderMsg::NewFavicon(webview_id, image) => {
797 if let Some(webview) = self.get_webview_handle(webview_id) {
798 webview.set_favicon(image);
799 }
800 },
801 EmbedderMsg::NotifyLoadStatusChanged(webview_id, load_status) => {
802 if let Some(webview) = self.get_webview_handle(webview_id) {
803 webview.set_load_status(load_status);
804 }
805 },
806 EmbedderMsg::HistoryTraversalComplete(webview_id, traversal_id) => {
807 if let Some(webview) = self.get_webview_handle(webview_id) {
808 webview
809 .delegate()
810 .notify_traversal_complete(webview.clone(), traversal_id);
811 }
812 },
813 EmbedderMsg::HistoryChanged(webview_id, urls, current_index) => {
814 if let Some(webview) = self.get_webview_handle(webview_id) {
815 let urls: Vec<_> = urls.into_iter().map(ServoUrl::into_url).collect();
816 let current_url = urls[current_index].clone();
817
818 webview
819 .delegate()
820 .notify_history_changed(webview.clone(), urls, current_index);
821 webview.set_url(current_url);
822 }
823 },
824 EmbedderMsg::NotifyFullscreenStateChanged(webview_id, fullscreen) => {
825 if let Some(webview) = self.get_webview_handle(webview_id) {
826 webview
827 .delegate()
828 .notify_fullscreen_state_changed(webview, fullscreen);
829 }
830 },
831 EmbedderMsg::WebResourceRequested(
832 webview_id,
833 web_resource_request,
834 response_sender,
835 ) => {
836 if let Some(webview) =
837 webview_id.and_then(|webview_id| self.get_webview_handle(webview_id))
838 {
839 let web_resource_load = WebResourceLoad::new(
840 web_resource_request,
841 response_sender,
842 self.servo_errors.sender(),
843 );
844 webview
845 .delegate()
846 .load_web_resource(webview, web_resource_load);
847 } else {
848 let web_resource_load = WebResourceLoad::new(
849 web_resource_request,
850 response_sender,
851 self.servo_errors.sender(),
852 );
853 self.delegate().load_web_resource(web_resource_load);
854 }
855 },
856 EmbedderMsg::Panic(webview_id, reason, backtrace) => {
857 if let Some(webview) = self.get_webview_handle(webview_id) {
858 webview
859 .delegate()
860 .notify_crashed(webview, reason, backtrace);
861 }
862 },
863 EmbedderMsg::GetSelectedBluetoothDevice(webview_id, items, response_sender) => {
864 if let Some(webview) = self.get_webview_handle(webview_id) {
865 webview.delegate().show_bluetooth_device_dialog(
866 webview,
867 items,
868 response_sender,
869 );
870 }
871 },
872 EmbedderMsg::SelectFiles(
873 webview_id,
874 filter_patterns,
875 allow_select_multiple,
876 response_sender,
877 ) => {
878 if let Some(webview) = self.get_webview_handle(webview_id) {
879 webview.delegate().show_file_selection_dialog(
880 webview,
881 filter_patterns,
882 allow_select_multiple,
883 response_sender,
884 );
885 }
886 },
887 EmbedderMsg::RequestAuthentication(webview_id, url, for_proxy, response_sender) => {
888 if let Some(webview) = self.get_webview_handle(webview_id) {
889 let authentication_request = AuthenticationRequest::new(
890 url.into_url(),
891 for_proxy,
892 response_sender,
893 self.servo_errors.sender(),
894 );
895 webview
896 .delegate()
897 .request_authentication(webview, authentication_request);
898 }
899 },
900 EmbedderMsg::PromptPermission(webview_id, requested_feature, response_sender) => {
901 if let Some(webview) = self.get_webview_handle(webview_id) {
902 let permission_request = PermissionRequest {
903 requested_feature,
904 allow_deny_request: AllowOrDenyRequest::new(
905 response_sender,
906 AllowOrDeny::Deny,
907 self.servo_errors.sender(),
908 ),
909 };
910 webview
911 .delegate()
912 .request_permission(webview, permission_request);
913 }
914 },
915 EmbedderMsg::ShowIME(webview_id, input_method_type, text, multiline, position) => {
916 if let Some(webview) = self.get_webview_handle(webview_id) {
917 webview.delegate().show_ime(
918 webview,
919 input_method_type,
920 text,
921 multiline,
922 position,
923 );
924 }
925 },
926 EmbedderMsg::HideIME(webview_id) => {
927 if let Some(webview) = self.get_webview_handle(webview_id) {
928 webview.delegate().hide_ime(webview);
929 }
930 },
931 EmbedderMsg::ReportProfile(_items) => {},
932 EmbedderMsg::MediaSessionEvent(webview_id, media_session_event) => {
933 if let Some(webview) = self.get_webview_handle(webview_id) {
934 webview
935 .delegate()
936 .notify_media_session_event(webview, media_session_event);
937 }
938 },
939 EmbedderMsg::OnDevtoolsStarted(port, token) => match port {
940 Ok(port) => self
941 .delegate()
942 .notify_devtools_server_started(self, port, token),
943 Err(()) => self
944 .delegate()
945 .notify_error(self, ServoError::DevtoolsFailedToStart),
946 },
947 EmbedderMsg::RequestDevtoolsConnection(response_sender) => {
948 self.delegate().request_devtools_connection(
949 self,
950 AllowOrDenyRequest::new(
951 response_sender,
952 AllowOrDeny::Deny,
953 self.servo_errors.sender(),
954 ),
955 );
956 },
957 EmbedderMsg::PlayGamepadHapticEffect(
958 webview_id,
959 gamepad_index,
960 gamepad_haptic_effect_type,
961 ipc_sender,
962 ) => {
963 if let Some(webview) = self.get_webview_handle(webview_id) {
964 webview.delegate().play_gamepad_haptic_effect(
965 webview,
966 gamepad_index,
967 gamepad_haptic_effect_type,
968 ipc_sender,
969 );
970 }
971 },
972 EmbedderMsg::StopGamepadHapticEffect(webview_id, gamepad_index, ipc_sender) => {
973 if let Some(webview) = self.get_webview_handle(webview_id) {
974 webview.delegate().stop_gamepad_haptic_effect(
975 webview,
976 gamepad_index,
977 ipc_sender,
978 );
979 }
980 },
981 EmbedderMsg::ShowNotification(webview_id, notification) => {
982 match webview_id.and_then(|webview_id| self.get_webview_handle(webview_id)) {
983 Some(webview) => webview.delegate().show_notification(webview, notification),
984 None => self.delegate().show_notification(notification),
985 }
986 },
987 EmbedderMsg::ShowFormControl(webview_id, position, form_control) => {
988 if let Some(webview) = self.get_webview_handle(webview_id) {
989 let form_control = match form_control {
990 EmbedderFormControl::SelectElement(
991 options,
992 selected_option,
993 ipc_sender,
994 ) => FormControl::SelectElement(SelectElement::new(
995 options,
996 selected_option,
997 position,
998 ipc_sender,
999 )),
1000 EmbedderFormControl::ColorPicker(current_color, ipc_sender) => {
1001 FormControl::ColorPicker(ColorPicker::new(
1002 current_color,
1003 position,
1004 ipc_sender,
1005 self.servo_errors.sender(),
1006 ))
1007 },
1008 };
1009
1010 webview.delegate().show_form_control(webview, form_control);
1011 }
1012 },
1013 EmbedderMsg::GetWindowRect(webview_id, response_sender) => {
1014 let window_rect = || {
1015 let Some(webview) = self.get_webview_handle(webview_id) else {
1016 return DeviceIndependentIntRect::default();
1017 };
1018 let hidpi_scale_factor = webview.hidpi_scale_factor();
1019 let Some(screen_geometry) = webview.delegate().screen_geometry(webview) else {
1020 return DeviceIndependentIntRect::default();
1021 };
1022
1023 convert_rect_to_css_pixel(screen_geometry.window_rect, hidpi_scale_factor)
1024 };
1025
1026 if let Err(error) = response_sender.send(window_rect()) {
1027 warn!("Failed to respond to GetWindowRect: {error}");
1028 }
1029 },
1030 EmbedderMsg::GetScreenMetrics(webview_id, response_sender) => {
1031 let screen_metrics = || {
1032 let Some(webview) = self.get_webview_handle(webview_id) else {
1033 return ScreenMetrics::default();
1034 };
1035 let hidpi_scale_factor = webview.hidpi_scale_factor();
1036 let Some(screen_geometry) = webview.delegate().screen_geometry(webview) else {
1037 return ScreenMetrics::default();
1038 };
1039
1040 ScreenMetrics {
1041 screen_size: convert_size_to_css_pixel(
1042 screen_geometry.size,
1043 hidpi_scale_factor,
1044 ),
1045 available_size: convert_size_to_css_pixel(
1046 screen_geometry.available_size,
1047 hidpi_scale_factor,
1048 ),
1049 }
1050 };
1051 if let Err(error) = response_sender.send(screen_metrics()) {
1052 warn!("Failed to respond to GetScreenMetrics: {error}");
1053 }
1054 },
1055 }
1056 }
1057
1058 pub fn constellation_sender(&self) -> Sender<EmbedderToConstellationMessage> {
1059 self.constellation_proxy.sender()
1060 }
1061
1062 pub fn execute_webdriver_command(&self, command: WebDriverCommandMsg) {
1063 if let WebDriverCommandMsg::TakeScreenshot(webview_id, page_rect, response_sender) = command
1064 {
1065 let res = self
1066 .compositor
1067 .borrow_mut()
1068 .render_to_shared_memory(webview_id, page_rect);
1069 if let Err(ref e) = res {
1070 error!("Error retrieving PNG: {:?}", e);
1071 }
1072 let img = res.unwrap_or(None);
1073 if let Err(e) = response_sender.send(img) {
1074 error!("Sending reply to create png failed ({:?}).", e);
1075 }
1076 } else {
1077 self.constellation_proxy
1078 .send(EmbedderToConstellationMessage::WebDriverCommand(command));
1079 }
1080 }
1081
1082 pub fn set_preference(&self, name: &str, value: PrefValue) {
1083 let mut preferences = prefs::get().clone();
1084 preferences.set_value(name, value);
1085 prefs::set(preferences);
1086 }
1087}
1088
1089fn create_embedder_channel(
1090 event_loop_waker: Box<dyn EventLoopWaker>,
1091) -> (EmbedderProxy, Receiver<EmbedderMsg>) {
1092 let (sender, receiver) = unbounded();
1093 (
1094 EmbedderProxy {
1095 sender,
1096 event_loop_waker,
1097 },
1098 receiver,
1099 )
1100}
1101
1102fn create_compositor_channel(
1103 event_loop_waker: Box<dyn EventLoopWaker>,
1104) -> (CompositorProxy, RoutedReceiver<CompositorMsg>) {
1105 let (sender, receiver) = unbounded();
1106 let sender_clone = sender.clone();
1107 let event_loop_waker_clone = event_loop_waker.clone();
1108 let result_callback = move |msg: Result<CompositorMsg, ipc_channel::Error>| {
1110 if let Err(err) = sender_clone.send(msg) {
1111 warn!("Failed to send response ({:?}).", err);
1112 }
1113 event_loop_waker_clone.wake();
1114 };
1115
1116 let generic_callback =
1117 GenericCallback::new(result_callback).expect("Failed to create callback");
1118 let cross_process_compositor_api = CrossProcessCompositorApi::new(generic_callback);
1119 let compositor_proxy = CompositorProxy {
1120 sender,
1121 cross_process_compositor_api,
1122 event_loop_waker,
1123 };
1124
1125 (compositor_proxy, receiver)
1126}
1127
1128#[allow(clippy::too_many_arguments)]
1129fn create_constellation(
1130 config_dir: Option<PathBuf>,
1131 embedder_proxy: EmbedderProxy,
1132 compositor_proxy: CompositorProxy,
1133 time_profiler_chan: time::ProfilerChan,
1134 mem_profiler_chan: mem::ProfilerChan,
1135 devtools_sender: Option<Sender<devtools_traits::DevtoolsControlMsg>>,
1136 webrender_document: DocumentId,
1137 webrender_api_sender: RenderApiSender,
1138 #[cfg(feature = "webxr")] webxr_registry: webxr_api::Registry,
1139 webgl_threads: Option<WebGLThreads>,
1140 external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
1141 #[cfg(feature = "webgpu")] wgpu_image_map: WGPUImageMap,
1142 protocols: ProtocolRegistry,
1143 user_content_manager: UserContentManager,
1144) -> Sender<EmbedderToConstellationMessage> {
1145 let opts = opts::get();
1147
1148 #[cfg(feature = "bluetooth")]
1149 let bluetooth_thread: IpcSender<BluetoothRequest> =
1150 BluetoothThreadFactory::new(embedder_proxy.clone());
1151
1152 let privileged_urls = protocols.privileged_urls();
1153
1154 let (public_resource_threads, private_resource_threads, async_runtime) = new_resource_threads(
1155 devtools_sender.clone(),
1156 time_profiler_chan.clone(),
1157 mem_profiler_chan.clone(),
1158 embedder_proxy.clone(),
1159 config_dir,
1160 opts.certificate_path.clone(),
1161 opts.ignore_certificate_errors,
1162 Arc::new(protocols),
1163 );
1164
1165 let system_font_service = Arc::new(
1166 SystemFontService::spawn(
1167 compositor_proxy.cross_process_compositor_api.clone(),
1168 mem_profiler_chan.clone(),
1169 )
1170 .to_proxy(),
1171 );
1172
1173 let initial_state = InitialConstellationState {
1174 compositor_proxy,
1175 embedder_proxy,
1176 devtools_sender,
1177 #[cfg(feature = "bluetooth")]
1178 bluetooth_thread,
1179 system_font_service,
1180 public_resource_threads,
1181 private_resource_threads,
1182 time_profiler_chan,
1183 mem_profiler_chan,
1184 webrender_document,
1185 webrender_api_sender,
1186 #[cfg(feature = "webxr")]
1187 webxr_registry: Some(webxr_registry),
1188 #[cfg(not(feature = "webxr"))]
1189 webxr_registry: None,
1190 webgl_threads,
1191 webrender_external_images: external_images,
1192 #[cfg(feature = "webgpu")]
1193 wgpu_image_map,
1194 user_content_manager,
1195 async_runtime,
1196 privileged_urls,
1197 };
1198
1199 let layout_factory = Arc::new(LayoutFactoryImpl());
1200
1201 Constellation::<script::ScriptThread, script::ServiceWorkerManager>::start(
1202 initial_state,
1203 layout_factory,
1204 opts.random_pipeline_closure_probability,
1205 opts.random_pipeline_closure_seed,
1206 opts.hard_fail,
1207 )
1208}
1209
1210struct BothLogger<Log1, Log2>(Log1, Log2);
1213
1214impl<Log1, Log2> Log for BothLogger<Log1, Log2>
1215where
1216 Log1: Log,
1217 Log2: Log,
1218{
1219 fn enabled(&self, metadata: &Metadata) -> bool {
1220 self.0.enabled(metadata) || self.1.enabled(metadata)
1221 }
1222
1223 fn log(&self, record: &Record) {
1224 self.0.log(record);
1225 self.1.log(record);
1226 }
1227
1228 fn flush(&self) {
1229 self.0.flush();
1230 self.1.flush();
1231 }
1232}
1233
1234pub fn set_logger(script_to_constellation_chan: ScriptToConstellationChan) {
1235 let con_logger = FromScriptLogger::new(script_to_constellation_chan);
1236 let env = env_logger::Env::default();
1237 let env_logger = EnvLoggerBuilder::from_env(env).build();
1238
1239 let filter = max(env_logger.filter(), con_logger.filter());
1240 let logger = BothLogger(env_logger, con_logger);
1241
1242 log::set_boxed_logger(Box::new(logger)).expect("Failed to set logger.");
1243 log::set_max_level(filter);
1244}
1245
1246pub fn run_content_process(token: String) {
1248 let (unprivileged_content_sender, unprivileged_content_receiver) =
1249 ipc::channel::<UnprivilegedContent>().unwrap();
1250 let connection_bootstrap: IpcSender<IpcSender<UnprivilegedContent>> =
1251 IpcSender::connect(token).unwrap();
1252 connection_bootstrap
1253 .send(unprivileged_content_sender)
1254 .unwrap();
1255
1256 let unprivileged_content = unprivileged_content_receiver.recv().unwrap();
1257 opts::initialize_options(unprivileged_content.opts());
1258 prefs::set(unprivileged_content.prefs().clone());
1259
1260 if opts::get().sandbox {
1262 create_sandbox();
1263 }
1264
1265 let _js_engine_setup = script::init();
1266
1267 match unprivileged_content {
1268 UnprivilegedContent::Pipeline(mut content) => {
1269 media_platform::init();
1270
1271 let fetch_thread_join_handle = start_fetch_thread();
1273
1274 set_logger(content.script_to_constellation_chan().clone());
1275
1276 let (background_hang_monitor_register, join_handle) =
1277 content.register_with_background_hang_monitor();
1278 let layout_factory = Arc::new(LayoutFactoryImpl());
1279
1280 content.register_system_memory_reporter();
1281
1282 let script_join_handle = content.start_all::<script::ScriptThread>(
1283 true,
1284 layout_factory,
1285 background_hang_monitor_register,
1286 None,
1287 );
1288
1289 script_join_handle
1294 .join()
1295 .expect("Failed to join on the script thread.");
1296 join_handle
1297 .join()
1298 .expect("Failed to join on the BHM background thread.");
1299
1300 StyleThreadPool::shutdown();
1301
1302 exit_fetch_thread();
1304 fetch_thread_join_handle
1305 .join()
1306 .expect("Failed to join on the fetch thread in the constellation");
1307 },
1308 UnprivilegedContent::ServiceWorker(content) => {
1309 content.start::<ServiceWorkerManager>();
1310 },
1311 }
1312}
1313
1314#[cfg(all(
1315 not(target_os = "windows"),
1316 not(target_os = "ios"),
1317 not(target_os = "android"),
1318 not(target_arch = "arm"),
1319 not(target_arch = "aarch64"),
1320 not(target_env = "ohos"),
1321))]
1322fn create_sandbox() {
1323 ChildSandbox::new(content_process_sandbox_profile())
1324 .activate()
1325 .expect("Failed to activate sandbox!");
1326}
1327
1328#[cfg(any(
1329 target_os = "windows",
1330 target_os = "ios",
1331 target_os = "android",
1332 target_arch = "arm",
1333 target_arch = "aarch64",
1334 target_env = "ohos",
1335))]
1336fn create_sandbox() {
1337 panic!("Sandboxing is not supported on Windows, iOS, ARM targets and android.");
1338}
1339
1340struct DefaultEventLoopWaker;
1341
1342impl EventLoopWaker for DefaultEventLoopWaker {
1343 fn clone_box(&self) -> Box<dyn EventLoopWaker> {
1344 Box::new(DefaultEventLoopWaker)
1345 }
1346}
1347
1348#[cfg(feature = "webxr")]
1349struct DefaultWebXrRegistry;
1350#[cfg(feature = "webxr")]
1351impl webxr::WebXrRegistry for DefaultWebXrRegistry {}
1352
1353pub struct ServoBuilder {
1354 rendering_context: Rc<dyn RenderingContext>,
1355 opts: Option<Box<Opts>>,
1356 preferences: Option<Box<Preferences>>,
1357 event_loop_waker: Box<dyn EventLoopWaker>,
1358 user_content_manager: UserContentManager,
1359 protocol_registry: ProtocolRegistry,
1360 #[cfg(feature = "webxr")]
1361 webxr_registry: Box<dyn webxr::WebXrRegistry>,
1362}
1363
1364impl ServoBuilder {
1365 pub fn new(rendering_context: Rc<dyn RenderingContext>) -> Self {
1366 Self {
1367 rendering_context,
1368 opts: None,
1369 preferences: None,
1370 event_loop_waker: Box::new(DefaultEventLoopWaker),
1371 user_content_manager: UserContentManager::default(),
1372 protocol_registry: ProtocolRegistry::default(),
1373 #[cfg(feature = "webxr")]
1374 webxr_registry: Box::new(DefaultWebXrRegistry),
1375 }
1376 }
1377
1378 pub fn build(self) -> Servo {
1379 Servo::new(self)
1380 }
1381
1382 pub fn opts(mut self, opts: Opts) -> Self {
1383 self.opts = Some(Box::new(opts));
1384 self
1385 }
1386
1387 pub fn preferences(mut self, preferences: Preferences) -> Self {
1388 self.preferences = Some(Box::new(preferences));
1389 self
1390 }
1391
1392 pub fn event_loop_waker(mut self, event_loop_waker: Box<dyn EventLoopWaker>) -> Self {
1393 self.event_loop_waker = event_loop_waker;
1394 self
1395 }
1396
1397 pub fn user_content_manager(mut self, user_content_manager: UserContentManager) -> Self {
1398 self.user_content_manager = user_content_manager;
1399 self
1400 }
1401
1402 pub fn protocol_registry(mut self, protocol_registry: ProtocolRegistry) -> Self {
1403 self.protocol_registry = protocol_registry;
1404 self
1405 }
1406
1407 #[cfg(feature = "webxr")]
1408 pub fn webxr_registry(mut self, webxr_registry: Box<dyn webxr::WebXrRegistry>) -> Self {
1409 self.webxr_registry = webxr_registry;
1410 self
1411 }
1412}