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::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
188pub struct Servo {
197 delegate: RefCell<Rc<dyn ServoDelegate>>,
198 compositor: Rc<RefCell<IOCompositor>>,
199 constellation_proxy: ConstellationProxy,
200 embedder_receiver: Receiver<EmbedderMsg>,
201 javascript_evaluator: Rc<RefCell<JavaScriptEvaluator>>,
204 shutdown_state: Rc<Cell<ShutdownState>>,
207 webviews: RefCell<HashMap<WebViewId, Weak<RefCell<WebViewInner>>>>,
212 servo_errors: ServoErrorChannel,
213 _js_engine_setup: Option<JSEngineSetup>,
217 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 let opts = builder.opts.map(|opts| *opts);
258 opts::initialize_options(opts.unwrap_or_default());
259 let opts = opts::get();
260
261 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 let rendering_context = builder.rendering_context;
281 let webrender_gl = rendering_context.gleam_gl_api();
282
283 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 PipelineNamespace::install(PipelineNamespaceId(0));
291
292 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 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 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 let js_engine_setup = if !opts.multiprocess {
386 Some(script::init())
387 } else {
388 None
389 };
390
391 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 external_image_handlers.set_handler(image_handler, WebrenderImageHandlerType::WebGL);
415
416 #[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 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 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 pub fn animating(&self) -> bool {
521 self.animating.get()
522 }
523
524 pub fn initialize_gl_accelerated_media(display: NativeDisplay, api: GlApi, context: GlContext) {
527 WindowGLContext::initialize(display, api, context)
528 }
529
530 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 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 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 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 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
1209struct 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
1245pub 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 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 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 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 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}