servo/
servo.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::{Cell, Ref, RefCell, RefMut};
6use std::cmp::max;
7use std::rc::{Rc, Weak};
8use std::sync::Arc;
9use std::time::Duration;
10
11use background_hang_monitor::HangMonitorRegister;
12use base::generic_channel::{GenericCallback, GenericSender, RoutedReceiver};
13pub use base::id::WebViewId;
14use base::id::{PipelineNamespace, PipelineNamespaceId};
15#[cfg(feature = "bluetooth")]
16use bluetooth::BluetoothThreadFactory;
17#[cfg(feature = "bluetooth")]
18use bluetooth_traits::BluetoothRequest;
19#[cfg(all(
20    not(target_os = "windows"),
21    not(target_os = "ios"),
22    not(target_os = "android"),
23    not(target_arch = "arm"),
24    not(target_arch = "aarch64"),
25    not(target_env = "ohos"),
26))]
27use constellation::content_process_sandbox_profile;
28use constellation::{
29    Constellation, FromEmbedderLogger, FromScriptLogger, InitialConstellationState,
30    NewScriptEventLoopProcessInfo, UnprivilegedContent,
31};
32use constellation_traits::{EmbedderToConstellationMessage, ScriptToConstellationSender};
33use crossbeam_channel::{Receiver, Sender, unbounded};
34pub use embedder_traits::*;
35use env_logger::Builder as EnvLoggerBuilder;
36use fonts::SystemFontService;
37#[cfg(all(
38    not(target_os = "windows"),
39    not(target_os = "ios"),
40    not(target_os = "android"),
41    not(target_arch = "arm"),
42    not(target_arch = "aarch64"),
43    not(target_env = "ohos"),
44))]
45use gaol::sandbox::{ChildSandbox, ChildSandboxMethods};
46use ipc_channel::ipc::{self, IpcSender};
47use layout::LayoutFactoryImpl;
48use layout_api::ScriptThreadFactory;
49use log::{Log, Metadata, Record, debug, warn};
50use media::{GlApi, NativeDisplay, WindowGLContext};
51use net::embedder::NetToEmbedderMsg;
52use net::image_cache::ImageCacheFactoryImpl;
53use net::protocols::ProtocolRegistry;
54use net::resource_thread::new_resource_threads;
55use net_traits::{ResourceThreads, exit_fetch_thread, start_fetch_thread};
56use paint::{InitialPaintState, Paint};
57pub use paint_api::rendering_context::RenderingContext;
58use paint_api::{CrossProcessPaintApi, PaintMessage, PaintProxy};
59use profile::{mem as profile_mem, system_reporter, time as profile_time};
60use profile_traits::mem::{MemoryReportResult, ProfilerMsg, Reporter};
61use profile_traits::{mem, time};
62use rustc_hash::FxHashMap;
63use script::{JSEngineSetup, ServiceWorkerManager};
64use servo_config::opts::Opts;
65use servo_config::prefs::{PrefValue, Preferences};
66use servo_config::{opts, pref, prefs};
67use servo_geometry::{
68    DeviceIndependentIntRect, convert_rect_to_css_pixel, convert_size_to_css_pixel,
69};
70use servo_media::ServoMedia;
71use servo_media::player::context::GlContext;
72use storage::new_storage_threads;
73use storage_traits::StorageThreads;
74use style::global_style_data::StyleThreadPool;
75
76use crate::clipboard_delegate::StringRequest;
77#[cfg(feature = "gamepad")]
78use crate::gamepad_provider::{GamepadHapticEffectRequest, GamepadHapticEffectRequestType};
79use crate::javascript_evaluator::JavaScriptEvaluator;
80use crate::network_manager::NetworkManager;
81use crate::proxies::ConstellationProxy;
82use crate::responders::ServoErrorChannel;
83use crate::servo_delegate::{DefaultServoDelegate, ServoDelegate, ServoError};
84use crate::site_data_manager::SiteDataManager;
85use crate::webview::{MINIMUM_WEBVIEW_SIZE, WebView, WebViewInner};
86use crate::webview_delegate::{
87    AllowOrDenyRequest, AuthenticationRequest, EmbedderControl, FilePicker, NavigationRequest,
88    PermissionRequest, ProtocolHandlerRegistration, WebResourceLoad,
89};
90
91#[cfg(feature = "media-gstreamer")]
92mod media_platform {
93    #[cfg(any(windows, target_os = "macos"))]
94    mod gstreamer_plugins {
95        include!(concat!(env!("OUT_DIR"), "/gstreamer_plugins.rs"));
96    }
97
98    use servo_media_gstreamer::GStreamerBackend;
99
100    use super::ServoMedia;
101
102    #[cfg(any(windows, target_os = "macos"))]
103    pub fn init() {
104        ServoMedia::init_with_backend(|| {
105            let mut plugin_dir = std::env::current_exe().unwrap();
106            plugin_dir.pop();
107
108            if cfg!(target_os = "macos") {
109                plugin_dir.push("lib");
110            }
111
112            match GStreamerBackend::init_with_plugins(
113                plugin_dir,
114                gstreamer_plugins::GSTREAMER_PLUGINS,
115            ) {
116                Ok(b) => b,
117                Err(e) => {
118                    log::error!("Error initializing GStreamer: {:?}", e);
119                    std::process::exit(1);
120                },
121            }
122        });
123    }
124
125    #[cfg(not(any(windows, target_os = "macos")))]
126    pub fn init() {
127        ServoMedia::init::<GStreamerBackend>();
128    }
129}
130
131#[cfg(not(feature = "media-gstreamer"))]
132mod media_platform {
133    use super::ServoMedia;
134    pub fn init() {
135        ServoMedia::init::<servo_media_dummy::DummyBackend>();
136    }
137}
138
139enum Message {
140    FromNet(NetToEmbedderMsg),
141    FromUnknown(EmbedderMsg),
142}
143
144struct ServoInner {
145    delegate: RefCell<Rc<dyn ServoDelegate>>,
146    paint: Rc<RefCell<Paint>>,
147    constellation_proxy: ConstellationProxy,
148    embedder_receiver: Receiver<EmbedderMsg>,
149    net_embedder_receiver: Receiver<NetToEmbedderMsg>,
150    network_manager: Rc<RefCell<NetworkManager>>,
151    site_data_manager: Rc<RefCell<SiteDataManager>>,
152    /// A struct that tracks ongoing JavaScript evaluations and is responsible for
153    /// calling the callback when the evaluation is complete.
154    javascript_evaluator: Rc<RefCell<JavaScriptEvaluator>>,
155    /// Tracks whether we are in the process of shutting down, or have shut down.
156    /// This is shared with `WebView`s and the `ServoRenderer`.
157    shutdown_state: Rc<Cell<ShutdownState>>,
158    /// A map  [`WebView`]s that are managed by this [`Servo`] instance. These are stored
159    /// as `Weak` references so that the embedding application can control their lifetime.
160    /// When accessed, `Servo` will be reponsible for cleaning up the invalid `Weak`
161    /// references.
162    webviews: RefCell<FxHashMap<WebViewId, Weak<RefCell<WebViewInner>>>>,
163    servo_errors: ServoErrorChannel,
164    /// For single-process Servo instances, this field controls the initialization
165    /// and deinitialization of the JS Engine. Multiprocess Servo instances have their
166    /// own instance that exists in the content process instead.
167    _js_engine_setup: Option<JSEngineSetup>,
168}
169
170impl ServoInner {
171    fn get_webview_handle(&self, id: WebViewId) -> Option<WebView> {
172        self.webviews
173            .borrow()
174            .get(&id)
175            .and_then(WebView::from_weak_handle)
176    }
177
178    fn spin_event_loop(&self) -> bool {
179        if self.shutdown_state.get() == ShutdownState::FinishedShuttingDown {
180            return false;
181        }
182
183        {
184            let paint = self.paint.borrow();
185            let mut messages = Vec::new();
186            while let Ok(message) = paint.receiver().try_recv() {
187                match message {
188                    Ok(message) => messages.push(message),
189                    Err(error) => {
190                        warn!("Router deserialization error: {error}. Ignoring this PaintMessage.")
191                    },
192                }
193            }
194            paint.handle_messages(messages);
195        }
196
197        // Only handle incoming embedder messages if `Paint` hasn't already started shutting down.
198        while let Some(message) = self.receive_one_message() {
199            match message {
200                Message::FromUnknown(message) => self.handle_embedder_message(message),
201                Message::FromNet(message) => self.handle_net_embedder_message(message),
202            }
203            if self.shutdown_state.get() == ShutdownState::FinishedShuttingDown {
204                break;
205            }
206        }
207
208        if self.constellation_proxy.disconnected() {
209            self.delegate
210                .borrow()
211                .notify_error(ServoError::LostConnectionWithBackend);
212        }
213
214        self.paint.borrow_mut().perform_updates();
215        self.send_new_frame_ready_messages();
216        self.handle_delegate_errors();
217        self.clean_up_destroyed_webview_handles();
218
219        if self.shutdown_state.get() == ShutdownState::FinishedShuttingDown {
220            return false;
221        }
222
223        true
224    }
225
226    fn receive_one_message(&self) -> Option<Message> {
227        let mut select = crossbeam_channel::Select::new();
228        let embedder_receiver_index = select.recv(&self.embedder_receiver);
229        let net_embedder_receiver_index = select.recv(&self.net_embedder_receiver);
230        let Ok(operation) = select.try_select() else {
231            return None;
232        };
233        let index = operation.index();
234        if index == embedder_receiver_index {
235            let Ok(message) = operation.recv(&self.embedder_receiver) else {
236                return None;
237            };
238            Some(Message::FromUnknown(message))
239        } else if index == net_embedder_receiver_index {
240            let Ok(message) = operation.recv(&self.net_embedder_receiver) else {
241                return None;
242            };
243            Some(Message::FromNet(message))
244        } else {
245            log::error!("No select operation registered for {index:?}");
246            None
247        }
248    }
249
250    fn send_new_frame_ready_messages(&self) {
251        let webviews_needing_repaint = self.paint.borrow().webviews_needing_repaint();
252
253        for webview in webviews_needing_repaint
254            .iter()
255            .filter_map(|webview_id| self.get_webview_handle(*webview_id))
256        {
257            webview.delegate().notify_new_frame_ready(webview);
258        }
259    }
260
261    fn handle_delegate_errors(&self) {
262        while let Some(error) = self.servo_errors.try_recv() {
263            self.delegate.borrow().notify_error(error);
264        }
265    }
266
267    fn clean_up_destroyed_webview_handles(&self) {
268        // Remove any webview handles that have been destroyed and would not be upgradable.
269        // Note that `retain` is O(capacity) because it visits empty buckets, so it may be worth
270        // calling `shrink_to_fit` at some point to deal with cases where a long-running Servo
271        // instance goes from many open webviews to only a few.
272        self.webviews
273            .borrow_mut()
274            .retain(|_webview_id, webview| webview.strong_count() > 0);
275    }
276
277    fn finish_shutting_down(&self) {
278        debug!("Servo received message that Constellation shutdown is complete");
279        self.shutdown_state.set(ShutdownState::FinishedShuttingDown);
280        self.paint.borrow_mut().finish_shutting_down();
281    }
282
283    fn handle_net_embedder_message(&self, message: NetToEmbedderMsg) {
284        match message {
285            NetToEmbedderMsg::SelectFiles(control_id, file_picker_request, response_sender) => {
286                if file_picker_request.accept_current_paths_for_testing {
287                    let _ = response_sender.send(Some(file_picker_request.current_paths));
288                    return;
289                }
290                if let Some(webview) = self.get_webview_handle(control_id.webview_id) {
291                    webview.delegate().show_embedder_control(
292                        webview,
293                        EmbedderControl::FilePicker(FilePicker {
294                            id: control_id,
295                            file_picker_request,
296                            response_sender: Some(response_sender),
297                        }),
298                    );
299                }
300            },
301            NetToEmbedderMsg::WebResourceRequested(
302                webview_id,
303                web_resource_request,
304                response_sender,
305            ) => {
306                if let Some(webview) =
307                    webview_id.and_then(|webview_id| self.get_webview_handle(webview_id))
308                {
309                    let web_resource_load = WebResourceLoad::new(
310                        web_resource_request,
311                        response_sender,
312                        self.servo_errors.sender(),
313                    );
314                    webview
315                        .delegate()
316                        .load_web_resource(webview, web_resource_load);
317                } else {
318                    let web_resource_load = WebResourceLoad::new(
319                        web_resource_request,
320                        response_sender,
321                        self.servo_errors.sender(),
322                    );
323                    self.delegate.borrow().load_web_resource(web_resource_load);
324                }
325            },
326            NetToEmbedderMsg::RequestAuthentication(
327                webview_id,
328                url,
329                for_proxy,
330                response_sender,
331            ) => {
332                if let Some(webview) = self.get_webview_handle(webview_id) {
333                    let authentication_request = AuthenticationRequest::new(
334                        url.into_url(),
335                        for_proxy,
336                        response_sender,
337                        self.servo_errors.sender(),
338                    );
339                    webview
340                        .delegate()
341                        .request_authentication(webview, authentication_request);
342                }
343            },
344        }
345    }
346
347    fn handle_embedder_message(&self, message: EmbedderMsg) {
348        match message {
349            EmbedderMsg::ShutdownComplete => self.finish_shutting_down(),
350            EmbedderMsg::Status(webview_id, status_text) => {
351                if let Some(webview) = self.get_webview_handle(webview_id) {
352                    webview.set_status_text(status_text);
353                }
354            },
355            EmbedderMsg::ChangePageTitle(webview_id, title) => {
356                if let Some(webview) = self.get_webview_handle(webview_id) {
357                    webview.set_page_title(title);
358                }
359            },
360            EmbedderMsg::MoveTo(webview_id, position) => {
361                if let Some(webview) = self.get_webview_handle(webview_id) {
362                    webview.delegate().request_move_to(webview, position);
363                }
364            },
365            EmbedderMsg::ResizeTo(webview_id, size) => {
366                if let Some(webview) = self.get_webview_handle(webview_id) {
367                    webview
368                        .delegate()
369                        .request_resize_to(webview, size.max(MINIMUM_WEBVIEW_SIZE));
370                }
371            },
372            EmbedderMsg::ShowSimpleDialog(webview_id, simple_dialog) => {
373                if let Some(webview) = self.get_webview_handle(webview_id) {
374                    webview.delegate().show_embedder_control(
375                        webview,
376                        EmbedderControl::SimpleDialog(simple_dialog.into()),
377                    );
378                }
379            },
380            EmbedderMsg::AllowNavigationRequest(webview_id, pipeline_id, servo_url) => {
381                if let Some(webview) = self.get_webview_handle(webview_id) {
382                    let request = NavigationRequest {
383                        url: servo_url.into_url(),
384                        pipeline_id,
385                        constellation_proxy: self.constellation_proxy.clone(),
386                        response_sent: false,
387                    };
388                    webview.delegate().request_navigation(webview, request);
389                }
390            },
391            EmbedderMsg::AllowProtocolHandlerRequest(
392                webview_id,
393                registration_update,
394                response_sender,
395            ) => {
396                if let Some(webview) = self.get_webview_handle(webview_id) {
397                    let ProtocolHandlerUpdateRegistration {
398                        scheme,
399                        url,
400                        register_or_unregister,
401                    } = registration_update;
402                    let protocol_handler_registration = ProtocolHandlerRegistration {
403                        scheme,
404                        url: url.into_url(),
405                        register_or_unregister,
406                    };
407                    let allow_deny_request = AllowOrDenyRequest::new(
408                        response_sender,
409                        AllowOrDeny::Deny,
410                        self.servo_errors.sender(),
411                    );
412                    webview.delegate().request_protocol_handler(
413                        webview,
414                        protocol_handler_registration,
415                        allow_deny_request,
416                    );
417                }
418            },
419            EmbedderMsg::AllowOpeningWebView(webview_id, response_sender) => {
420                if let Some(webview) = self.get_webview_handle(webview_id) {
421                    webview.request_create_new(response_sender);
422                }
423            },
424            EmbedderMsg::WebViewClosed(webview_id) => {
425                if let Some(webview) = self.get_webview_handle(webview_id) {
426                    webview.delegate().notify_closed(webview);
427                }
428            },
429            EmbedderMsg::WebViewFocused(webview_id, focus_result) => {
430                if focus_result {
431                    for id in self.webviews.borrow().keys() {
432                        if let Some(webview) = self.get_webview_handle(*id) {
433                            let focused = webview.id() == webview_id;
434                            webview.set_focused(focused);
435                        }
436                    }
437                }
438            },
439            EmbedderMsg::WebViewBlurred => {
440                for id in self.webviews.borrow().keys() {
441                    if let Some(webview) = self.get_webview_handle(*id) {
442                        webview.set_focused(false);
443                    }
444                }
445            },
446            EmbedderMsg::AllowUnload(webview_id, response_sender) => {
447                if let Some(webview) = self.get_webview_handle(webview_id) {
448                    let request = AllowOrDenyRequest::new(
449                        response_sender,
450                        AllowOrDeny::Allow,
451                        self.servo_errors.sender(),
452                    );
453                    webview.delegate().request_unload(webview, request);
454                }
455            },
456            EmbedderMsg::FinishJavaScriptEvaluation(evaluation_id, result) => {
457                self.javascript_evaluator
458                    .borrow_mut()
459                    .finish_evaluation(evaluation_id, result);
460            },
461            EmbedderMsg::InputEventHandled(webview_id, input_event_id, result) => {
462                self.paint.borrow_mut().notify_input_event_handled(
463                    webview_id,
464                    input_event_id,
465                    result,
466                );
467
468                if let Some(webview) = self.get_webview_handle(webview_id) {
469                    webview
470                        .delegate()
471                        .notify_input_event_handled(webview, input_event_id, result);
472                }
473            },
474            EmbedderMsg::ClearClipboard(webview_id) => {
475                if let Some(webview) = self.get_webview_handle(webview_id) {
476                    webview.clipboard_delegate().clear(webview);
477                }
478            },
479            EmbedderMsg::GetClipboardText(webview_id, result_sender) => {
480                if let Some(webview) = self.get_webview_handle(webview_id) {
481                    webview
482                        .clipboard_delegate()
483                        .get_text(webview, StringRequest::from(result_sender));
484                }
485            },
486            EmbedderMsg::SetClipboardText(webview_id, string) => {
487                if let Some(webview) = self.get_webview_handle(webview_id) {
488                    webview.clipboard_delegate().set_text(webview, string);
489                }
490            },
491            EmbedderMsg::SetCursor(webview_id, cursor) => {
492                if let Some(webview) = self.get_webview_handle(webview_id) {
493                    webview.set_cursor(cursor);
494                }
495            },
496            EmbedderMsg::NewFavicon(webview_id, image) => {
497                if let Some(webview) = self.get_webview_handle(webview_id) {
498                    webview.set_favicon(image);
499                }
500            },
501            EmbedderMsg::NotifyLoadStatusChanged(webview_id, load_status) => {
502                if let Some(webview) = self.get_webview_handle(webview_id) {
503                    webview.set_load_status(load_status);
504                }
505            },
506            EmbedderMsg::HistoryTraversalComplete(webview_id, traversal_id) => {
507                if let Some(webview) = self.get_webview_handle(webview_id) {
508                    webview
509                        .delegate()
510                        .notify_traversal_complete(webview.clone(), traversal_id);
511                }
512            },
513            EmbedderMsg::HistoryChanged(webview_id, new_back_forward_list, current_list_index) => {
514                if let Some(webview) = self.get_webview_handle(webview_id) {
515                    webview.set_history(new_back_forward_list, current_list_index);
516                }
517            },
518            EmbedderMsg::NotifyFullscreenStateChanged(webview_id, fullscreen) => {
519                if let Some(webview) = self.get_webview_handle(webview_id) {
520                    webview
521                        .delegate()
522                        .notify_fullscreen_state_changed(webview, fullscreen);
523                }
524            },
525            EmbedderMsg::Panic(webview_id, reason, backtrace) => {
526                if let Some(webview) = self.get_webview_handle(webview_id) {
527                    webview
528                        .delegate()
529                        .notify_crashed(webview, reason, backtrace);
530                }
531            },
532            EmbedderMsg::GetSelectedBluetoothDevice(webview_id, items, response_sender) => {
533                if let Some(webview) = self.get_webview_handle(webview_id) {
534                    webview.delegate().show_bluetooth_device_dialog(
535                        webview,
536                        items,
537                        response_sender,
538                    );
539                }
540            },
541            EmbedderMsg::PromptPermission(webview_id, requested_feature, response_sender) => {
542                if let Some(webview) = self.get_webview_handle(webview_id) {
543                    let permission_request = PermissionRequest {
544                        requested_feature,
545                        allow_deny_request: AllowOrDenyRequest::new(
546                            response_sender,
547                            AllowOrDeny::Deny,
548                            self.servo_errors.sender(),
549                        ),
550                    };
551                    webview
552                        .delegate()
553                        .request_permission(webview, permission_request);
554                }
555            },
556            EmbedderMsg::ReportProfile(_items) => {},
557            EmbedderMsg::MediaSessionEvent(webview_id, media_session_event) => {
558                if let Some(webview) = self.get_webview_handle(webview_id) {
559                    webview
560                        .delegate()
561                        .notify_media_session_event(webview, media_session_event);
562                }
563            },
564            EmbedderMsg::OnDevtoolsStarted(port, token) => match port {
565                Ok(port) => self
566                    .delegate
567                    .borrow()
568                    .notify_devtools_server_started(port, token),
569                Err(()) => self
570                    .delegate
571                    .borrow()
572                    .notify_error(ServoError::DevtoolsFailedToStart),
573            },
574            EmbedderMsg::RequestDevtoolsConnection(response_sender) => {
575                self.delegate
576                    .borrow()
577                    .request_devtools_connection(AllowOrDenyRequest::new(
578                        response_sender,
579                        AllowOrDeny::Deny,
580                        self.servo_errors.sender(),
581                    ));
582            },
583            #[cfg(feature = "gamepad")]
584            EmbedderMsg::PlayGamepadHapticEffect(
585                webview_id,
586                gamepad_index,
587                gamepad_haptic_effect_type,
588                callback,
589            ) => {
590                if let Some(webview) = self.get_webview_handle(webview_id) {
591                    let request = GamepadHapticEffectRequest::new(
592                        gamepad_index,
593                        GamepadHapticEffectRequestType::Play(gamepad_haptic_effect_type),
594                        Box::new(move |success| {
595                            callback
596                                .send(success)
597                                .expect("Could not send message via callback")
598                        }),
599                    );
600                    webview
601                        .gamepad_provider()
602                        .handle_haptic_effect_request(request);
603                }
604            },
605            #[cfg(feature = "gamepad")]
606            EmbedderMsg::StopGamepadHapticEffect(webview_id, gamepad_index, callback) => {
607                if let Some(webview) = self.get_webview_handle(webview_id) {
608                    let request = GamepadHapticEffectRequest::new(
609                        gamepad_index,
610                        GamepadHapticEffectRequestType::Stop,
611                        Box::new(move |success| {
612                            callback
613                                .send(success)
614                                .expect("Could not send message via callback")
615                        }),
616                    );
617                    webview
618                        .gamepad_provider()
619                        .handle_haptic_effect_request(request);
620                }
621            },
622            EmbedderMsg::ShowNotification(webview_id, notification) => {
623                match webview_id.and_then(|webview_id| self.get_webview_handle(webview_id)) {
624                    Some(webview) => webview.delegate().show_notification(webview, notification),
625                    None => self.delegate.borrow().show_notification(notification),
626                }
627            },
628            EmbedderMsg::ShowConsoleApiMessage(webview_id, level, message) => {
629                match webview_id.and_then(|webview_id| self.get_webview_handle(webview_id)) {
630                    Some(webview) => webview
631                        .delegate()
632                        .show_console_message(webview, level, message),
633                    None => self.delegate.borrow().show_console_message(level, message),
634                }
635            },
636            EmbedderMsg::ShowEmbedderControl(control_id, position, embedder_control_request) => {
637                if let Some(webview) = self.get_webview_handle(control_id.webview_id) {
638                    webview.show_embedder_control(control_id, position, embedder_control_request);
639                }
640            },
641            EmbedderMsg::HideEmbedderControl(control_id) => {
642                if let Some(webview) = self.get_webview_handle(control_id.webview_id) {
643                    webview
644                        .delegate()
645                        .hide_embedder_control(webview, control_id);
646                }
647            },
648            EmbedderMsg::GetWindowRect(webview_id, response_sender) => {
649                let window_rect = || {
650                    let Some(webview) = self.get_webview_handle(webview_id) else {
651                        return DeviceIndependentIntRect::default();
652                    };
653                    let hidpi_scale_factor = webview.hidpi_scale_factor();
654                    let Some(screen_geometry) = webview.delegate().screen_geometry(webview) else {
655                        return DeviceIndependentIntRect::default();
656                    };
657
658                    convert_rect_to_css_pixel(screen_geometry.window_rect, hidpi_scale_factor)
659                };
660
661                if let Err(error) = response_sender.send(window_rect()) {
662                    warn!("Failed to respond to GetWindowRect: {error}");
663                }
664            },
665            EmbedderMsg::GetScreenMetrics(webview_id, response_sender) => {
666                let screen_metrics = || {
667                    let Some(webview) = self.get_webview_handle(webview_id) else {
668                        return ScreenMetrics::default();
669                    };
670                    let hidpi_scale_factor = webview.hidpi_scale_factor();
671                    let Some(screen_geometry) = webview.delegate().screen_geometry(webview) else {
672                        return ScreenMetrics::default();
673                    };
674
675                    ScreenMetrics {
676                        screen_size: convert_size_to_css_pixel(
677                            screen_geometry.size,
678                            hidpi_scale_factor,
679                        ),
680                        available_size: convert_size_to_css_pixel(
681                            screen_geometry.available_size,
682                            hidpi_scale_factor,
683                        ),
684                    }
685                };
686                if let Err(error) = response_sender.send(screen_metrics()) {
687                    warn!("Failed to respond to GetScreenMetrics: {error}");
688                }
689            },
690            EmbedderMsg::AccessibilityTreeUpdate(webview_id, tree_update) => {
691                if let Some(webview) = self.get_webview_handle(webview_id) {
692                    webview
693                        .delegate()
694                        .notify_accessibility_tree_update(webview, tree_update);
695                }
696            },
697        }
698    }
699}
700
701impl Drop for ServoInner {
702    fn drop(&mut self) {
703        self.constellation_proxy
704            .send(EmbedderToConstellationMessage::Exit);
705        self.shutdown_state.set(ShutdownState::ShuttingDown);
706        while self.spin_event_loop() {
707            std::thread::sleep(Duration::from_micros(500));
708        }
709    }
710}
711
712/// An in-process handle to a `Servo` instance. Cloning the handle does not create a new instance
713/// of `Servo`.
714///
715/// A `Servo` instance does everything necessary to render the web, primarily orchestrating the
716/// interaction between JavaScript, CSS layout, rendering, and the client window.
717///
718/// Clients create an event loop to pump messages between the embedding
719/// application and various browser components.
720#[derive(Clone)]
721pub struct Servo(Rc<ServoInner>);
722
723impl Servo {
724    #[servo_tracing::instrument(skip(builder))]
725    fn new(builder: ServoBuilder) -> Self {
726        // Global configuration options, parsed from the command line.
727        let opts = builder.opts.map(|opts| *opts);
728        opts::initialize_options(opts.unwrap_or_default());
729        let opts = opts::get();
730
731        // Set the preferences globally.
732        // TODO: It would be better to make these private to a particular Servo instance.
733        let preferences = builder.preferences.map(|opts| *opts);
734        servo_config::prefs::set(preferences.unwrap_or_default());
735
736        use std::sync::atomic::Ordering;
737
738        style::context::DEFAULT_DISABLE_STYLE_SHARING_CACHE.store(
739            !pref!(layout_style_sharing_cache_enabled),
740            Ordering::Relaxed,
741        );
742        style::context::DEFAULT_DUMP_STYLE_STATISTICS
743            .store(opts.debug.style_statistics, Ordering::Relaxed);
744        style::traversal::IS_SERVO_NONINCREMENTAL_LAYOUT
745            .store(opts.nonincremental_layout, Ordering::Relaxed);
746
747        if !opts.multiprocess {
748            media_platform::init();
749        }
750
751        // Reserving a namespace to create WebViewId.
752        PipelineNamespace::install(PipelineNamespaceId(0));
753
754        // Get both endpoints of a special channel for communication between
755        // the client window and `Paint`. This channel is unique because
756        // messages to client may need to pump a platform-specific event loop
757        // to deliver the message.
758        let event_loop_waker = builder.event_loop_waker;
759        let (paint_proxy, paint_receiver) = create_paint_channel(event_loop_waker.clone());
760        let (constellation_proxy, embedder_to_constellation_receiver) = ConstellationProxy::new();
761        let (embedder_proxy, embedder_receiver) = create_embedder_channel(event_loop_waker.clone());
762        let (net_embedder_proxy, net_embedder_receiver) =
763            create_generic_embedder_channel::<NetToEmbedderMsg>(event_loop_waker.clone());
764        let time_profiler_chan = profile_time::Profiler::create(
765            &opts.time_profiling,
766            opts.time_profiler_trace_path.clone(),
767        );
768        let mem_profiler_chan = profile_mem::Profiler::create();
769
770        let devtools_sender = if pref!(devtools_server_enabled) {
771            Some(devtools::start_server(
772                embedder_proxy.clone(),
773                mem_profiler_chan.clone(),
774            ))
775        } else {
776            None
777        };
778
779        // Important that this call is done in a single-threaded fashion, we
780        // can't defer it after `create_constellation` has started.
781        let js_engine_setup = if !opts.multiprocess {
782            Some(script::init())
783        } else {
784            None
785        };
786
787        // Create the constellation, which maintains the engine pipelines, including script and
788        // layout, as well as the navigation context.
789        let mut protocols = ProtocolRegistry::with_internal_protocols();
790        protocols.merge(builder.protocol_registry);
791
792        // The `Paint` coordinates with the client window to create the final
793        // rendered page and display it somewhere.
794        let shutdown_state = Rc::new(Cell::new(ShutdownState::NotShuttingDown));
795        let paint = Paint::new(InitialPaintState {
796            paint_proxy: paint_proxy.clone(),
797            receiver: paint_receiver,
798            embedder_to_constellation_sender: constellation_proxy.sender().clone(),
799            time_profiler_chan: time_profiler_chan.clone(),
800            mem_profiler_chan: mem_profiler_chan.clone(),
801            shutdown_state: shutdown_state.clone(),
802            event_loop_waker,
803            #[cfg(feature = "webxr")]
804            webxr_registry: builder.webxr_registry,
805        });
806
807        let protocols = Arc::new(protocols);
808        let (public_resource_threads, private_resource_threads, async_runtime) =
809            new_resource_threads(
810                devtools_sender.clone(),
811                time_profiler_chan.clone(),
812                mem_profiler_chan.clone(),
813                net_embedder_proxy,
814                opts.config_dir.clone(),
815                opts.certificate_path.clone(),
816                opts.ignore_certificate_errors,
817                protocols.clone(),
818            );
819
820        let (private_storage_threads, public_storage_threads) =
821            new_storage_threads(mem_profiler_chan.clone(), opts.config_dir.clone());
822
823        create_constellation(
824            embedder_to_constellation_receiver,
825            &paint.borrow(),
826            embedder_proxy,
827            paint_proxy.clone(),
828            time_profiler_chan,
829            mem_profiler_chan,
830            devtools_sender,
831            protocols,
832            public_resource_threads.clone(),
833            private_resource_threads.clone(),
834            async_runtime,
835            public_storage_threads.clone(),
836            private_storage_threads.clone(),
837        );
838
839        if opts::get().multiprocess {
840            prefs::add_observer(Box::new(constellation_proxy.clone()));
841        }
842
843        Servo(Rc::new(ServoInner {
844            delegate: RefCell::new(Rc::new(DefaultServoDelegate)),
845            paint,
846            network_manager: Rc::new(RefCell::new(NetworkManager::new(
847                public_resource_threads.clone(),
848                private_resource_threads.clone(),
849            ))),
850            site_data_manager: Rc::new(RefCell::new(SiteDataManager::new(
851                public_resource_threads,
852                private_resource_threads,
853                public_storage_threads,
854                private_storage_threads,
855            ))),
856            javascript_evaluator: Rc::new(RefCell::new(JavaScriptEvaluator::new(
857                constellation_proxy.clone(),
858            ))),
859            constellation_proxy,
860            embedder_receiver,
861            net_embedder_receiver,
862            shutdown_state,
863            webviews: Default::default(),
864            servo_errors: ServoErrorChannel::default(),
865            _js_engine_setup: js_engine_setup,
866        }))
867    }
868
869    pub fn delegate(&self) -> Rc<dyn ServoDelegate> {
870        self.0.delegate.borrow().clone()
871    }
872
873    pub fn set_delegate(&self, delegate: Rc<dyn ServoDelegate>) {
874        *self.0.delegate.borrow_mut() = delegate;
875    }
876
877    /// **EXPERIMENTAL:** Intialize GL accelerated media playback. This currently only works on a limited number
878    /// of platforms. This should be run *before* calling [`Servo::new`] and creating the first [`WebView`].
879    pub fn initialize_gl_accelerated_media(display: NativeDisplay, api: GlApi, context: GlContext) {
880        WindowGLContext::initialize(display, api, context)
881    }
882
883    /// Spin the Servo event loop, which:
884    ///
885    ///   - Performs updates in `Paint`, such as queued pinch zoom events
886    ///   - Runs delebgate methods on all `WebView`s and `Servo` itself
887    ///   - Maybe update the rendered `Paint` output, but *without* swapping buffers.
888    pub fn spin_event_loop(&self) {
889        self.0.spin_event_loop();
890    }
891
892    pub fn setup_logging(&self) {
893        let constellation_chan = self.0.constellation_proxy.sender();
894        let env = env_logger::Env::default();
895        let env_logger = EnvLoggerBuilder::from_env(env).build();
896        let con_logger = FromEmbedderLogger::new(constellation_chan);
897
898        let filter = max(env_logger.filter(), con_logger.filter());
899        let logger = BothLogger(env_logger, con_logger);
900
901        log::set_boxed_logger(Box::new(logger)).expect("Failed to set logger.");
902        log::set_max_level(filter);
903    }
904
905    pub fn create_memory_report(&self, snd: GenericCallback<MemoryReportResult>) {
906        self.0
907            .constellation_proxy
908            .send(EmbedderToConstellationMessage::CreateMemoryReport(snd));
909    }
910
911    pub fn execute_webdriver_command(&self, command: WebDriverCommandMsg) {
912        self.0
913            .constellation_proxy
914            .send(EmbedderToConstellationMessage::WebDriverCommand(command));
915    }
916
917    pub fn set_preference(&self, name: &str, value: PrefValue) {
918        let mut preferences = prefs::get().clone();
919        preferences.set_value(name, value);
920        prefs::set(preferences);
921    }
922
923    pub fn network_manager<'a>(&'a self) -> Ref<'a, NetworkManager> {
924        self.0.network_manager.borrow()
925    }
926
927    pub fn site_data_manager<'a>(&'a self) -> Ref<'a, SiteDataManager> {
928        self.0.site_data_manager.borrow()
929    }
930
931    pub fn set_accessibility_active(&self, active: bool) {
932        self.0
933            .constellation_proxy
934            .send(EmbedderToConstellationMessage::SetAccessibilityActive(
935                active,
936            ));
937    }
938
939    pub(crate) fn paint<'a>(&'a self) -> Ref<'a, Paint> {
940        self.0.paint.borrow()
941    }
942
943    pub(crate) fn paint_mut<'a>(&'a self) -> RefMut<'a, Paint> {
944        self.0.paint.borrow_mut()
945    }
946
947    pub(crate) fn webviews_mut<'a>(
948        &'a self,
949    ) -> RefMut<'a, FxHashMap<WebViewId, Weak<RefCell<WebViewInner>>>> {
950        self.0.webviews.borrow_mut()
951    }
952
953    pub(crate) fn constellation_proxy(&self) -> &ConstellationProxy {
954        &self.0.constellation_proxy
955    }
956
957    pub(crate) fn javascript_evaluator_mut<'a>(&'a self) -> RefMut<'a, JavaScriptEvaluator> {
958        self.0.javascript_evaluator.borrow_mut()
959    }
960}
961
962fn create_embedder_channel(
963    event_loop_waker: Box<dyn EventLoopWaker>,
964) -> (EmbedderProxy, Receiver<EmbedderMsg>) {
965    let (sender, receiver) = unbounded();
966    (
967        EmbedderProxy {
968            sender,
969            event_loop_waker,
970        },
971        receiver,
972    )
973}
974
975fn create_generic_embedder_channel<T>(
976    event_loop_waker: Box<dyn EventLoopWaker>,
977) -> (GenericEmbedderProxy<T>, Receiver<T>) {
978    let (sender, receiver) = unbounded();
979    (
980        GenericEmbedderProxy {
981            sender,
982            event_loop_waker,
983        },
984        receiver,
985    )
986}
987
988fn create_paint_channel(
989    event_loop_waker: Box<dyn EventLoopWaker>,
990) -> (PaintProxy, RoutedReceiver<PaintMessage>) {
991    let (sender, receiver) = unbounded();
992    let sender_clone = sender.clone();
993    let event_loop_waker_clone = event_loop_waker.clone();
994    // This callback is equivalent to `PaintProxy::send`
995    let result_callback = move |msg: Result<PaintMessage, ipc_channel::IpcError>| {
996        if let Err(err) = sender_clone.send(msg) {
997            warn!("Failed to send response ({:?}).", err);
998        }
999        event_loop_waker_clone.wake();
1000    };
1001
1002    let generic_callback =
1003        GenericCallback::new(result_callback).expect("Failed to create callback");
1004    let cross_process_paint_api = CrossProcessPaintApi::new(generic_callback);
1005    let paint_proxy = PaintProxy {
1006        sender,
1007        cross_process_paint_api,
1008        event_loop_waker,
1009    };
1010
1011    (paint_proxy, receiver)
1012}
1013
1014#[expect(clippy::too_many_arguments)]
1015fn create_constellation(
1016    embedder_to_constellation_receiver: Receiver<EmbedderToConstellationMessage>,
1017    paint: &Paint,
1018    embedder_proxy: EmbedderProxy,
1019    paint_proxy: PaintProxy,
1020    time_profiler_chan: time::ProfilerChan,
1021    mem_profiler_chan: mem::ProfilerChan,
1022    devtools_sender: Option<Sender<devtools_traits::DevtoolsControlMsg>>,
1023    protocols: Arc<ProtocolRegistry>,
1024    public_resource_threads: ResourceThreads,
1025    private_resource_threads: ResourceThreads,
1026    async_runtime: Box<dyn net_traits::AsyncRuntime>,
1027    public_storage_threads: StorageThreads,
1028    private_storage_threads: StorageThreads,
1029) {
1030    // Global configuration options, parsed from the command line.
1031    let opts = opts::get();
1032
1033    #[cfg(feature = "bluetooth")]
1034    let bluetooth_thread: GenericSender<BluetoothRequest> =
1035        BluetoothThreadFactory::new(embedder_proxy.clone());
1036
1037    let privileged_urls = protocols.privileged_urls();
1038
1039    let system_font_service = Arc::new(
1040        SystemFontService::spawn(
1041            paint_proxy.cross_process_paint_api.clone(),
1042            mem_profiler_chan.clone(),
1043        )
1044        .to_proxy(),
1045    );
1046
1047    let initial_state = InitialConstellationState {
1048        paint_proxy,
1049        embedder_proxy,
1050        devtools_sender,
1051        #[cfg(feature = "bluetooth")]
1052        bluetooth_thread,
1053        system_font_service,
1054        public_resource_threads,
1055        private_resource_threads,
1056        public_storage_threads,
1057        private_storage_threads,
1058        time_profiler_chan,
1059        mem_profiler_chan,
1060        #[cfg(feature = "webxr")]
1061        webxr_registry: Some(paint.webxr_main_thread_registry()),
1062        #[cfg(not(feature = "webxr"))]
1063        webxr_registry: None,
1064        webgl_threads: Some(paint.webgl_threads()),
1065        webrender_external_image_id_manager: paint.webrender_external_image_id_manager(),
1066        #[cfg(feature = "webgpu")]
1067        wgpu_image_map: paint.webgpu_image_map(),
1068        async_runtime,
1069        privileged_urls,
1070    };
1071
1072    let layout_factory = Arc::new(LayoutFactoryImpl());
1073
1074    Constellation::<script::ScriptThread, script::ServiceWorkerManager>::start(
1075        embedder_to_constellation_receiver,
1076        initial_state,
1077        layout_factory,
1078        opts.random_pipeline_closure_probability,
1079        opts.random_pipeline_closure_seed,
1080        opts.hard_fail,
1081    );
1082}
1083
1084// A logger that logs to two downstream loggers.
1085// This should probably be in the log crate.
1086struct BothLogger<Log1, Log2>(Log1, Log2);
1087
1088impl<Log1, Log2> Log for BothLogger<Log1, Log2>
1089where
1090    Log1: Log,
1091    Log2: Log,
1092{
1093    fn enabled(&self, metadata: &Metadata) -> bool {
1094        self.0.enabled(metadata) || self.1.enabled(metadata)
1095    }
1096
1097    fn log(&self, record: &Record) {
1098        self.0.log(record);
1099        self.1.log(record);
1100    }
1101
1102    fn flush(&self) {
1103        self.0.flush();
1104        self.1.flush();
1105    }
1106}
1107
1108fn set_logger(script_to_constellation_sender: ScriptToConstellationSender) {
1109    let con_logger = FromScriptLogger::new(script_to_constellation_sender);
1110    let env = env_logger::Env::default();
1111    let env_logger = EnvLoggerBuilder::from_env(env).build();
1112
1113    let filter = max(env_logger.filter(), con_logger.filter());
1114    let logger = BothLogger(env_logger, con_logger);
1115
1116    log::set_boxed_logger(Box::new(logger)).expect("Failed to set logger.");
1117    log::set_max_level(filter);
1118}
1119
1120/// Content process entry point.
1121pub fn run_content_process(token: String) {
1122    let (unprivileged_content_sender, unprivileged_content_receiver) =
1123        ipc::channel::<UnprivilegedContent>().unwrap();
1124    let connection_bootstrap: IpcSender<IpcSender<UnprivilegedContent>> =
1125        IpcSender::connect(token).unwrap();
1126    connection_bootstrap
1127        .send(unprivileged_content_sender)
1128        .unwrap();
1129
1130    let unprivileged_content = unprivileged_content_receiver.recv().unwrap();
1131    opts::initialize_options(unprivileged_content.opts());
1132    prefs::set(unprivileged_content.prefs().clone());
1133
1134    // Enter the sandbox if necessary.
1135    if opts::get().sandbox {
1136        create_sandbox();
1137    }
1138
1139    let _js_engine_setup = script::init();
1140
1141    match unprivileged_content {
1142        UnprivilegedContent::ScriptEventLoop(new_event_loop_info) => {
1143            media_platform::init();
1144
1145            // Start the fetch thread for this content process.
1146            let fetch_thread_join_handle = start_fetch_thread();
1147
1148            set_logger(
1149                new_event_loop_info
1150                    .initial_script_state
1151                    .script_to_constellation_sender
1152                    .clone(),
1153            );
1154
1155            register_system_memory_reporter_for_event_loop(&new_event_loop_info);
1156
1157            let (background_hang_monitor_register, background_hang_monitor_join_handle) =
1158                HangMonitorRegister::init(
1159                    new_event_loop_info.bhm_to_constellation_sender.clone(),
1160                    new_event_loop_info.constellation_to_bhm_receiver,
1161                    opts::get().background_hang_monitor,
1162                );
1163
1164            let layout_factory = Arc::new(LayoutFactoryImpl());
1165            let script_join_handle = script::ScriptThread::create(
1166                new_event_loop_info.initial_script_state,
1167                layout_factory,
1168                Arc::new(ImageCacheFactoryImpl::new(
1169                    new_event_loop_info.broken_image_icon_data,
1170                )),
1171                background_hang_monitor_register,
1172            );
1173
1174            script_join_handle
1175                .join()
1176                .expect("Failed to join on the script thread.");
1177            background_hang_monitor_join_handle
1178                .join()
1179                .expect("Failed to join on the BHM background thread.");
1180
1181            StyleThreadPool::shutdown();
1182
1183            // Shut down the fetch thread started above.
1184            exit_fetch_thread();
1185            fetch_thread_join_handle
1186                .join()
1187                .expect("Failed to join on the fetch thread in the constellation");
1188        },
1189        UnprivilegedContent::ServiceWorker(content) => {
1190            content.start::<ServiceWorkerManager>();
1191        },
1192    }
1193}
1194
1195#[cfg(all(
1196    not(target_os = "windows"),
1197    not(target_os = "ios"),
1198    not(target_os = "android"),
1199    not(target_arch = "arm"),
1200    not(target_arch = "aarch64"),
1201    not(target_env = "ohos"),
1202))]
1203fn create_sandbox() {
1204    ChildSandbox::new(content_process_sandbox_profile())
1205        .activate()
1206        .expect("Failed to activate sandbox!");
1207}
1208
1209#[cfg(any(
1210    target_os = "windows",
1211    target_os = "ios",
1212    target_os = "android",
1213    target_arch = "arm",
1214    target_arch = "aarch64",
1215    target_env = "ohos",
1216))]
1217fn create_sandbox() {
1218    panic!("Sandboxing is not supported on Windows, iOS, ARM targets and android.");
1219}
1220
1221struct DefaultEventLoopWaker;
1222
1223impl EventLoopWaker for DefaultEventLoopWaker {
1224    fn clone_box(&self) -> Box<dyn EventLoopWaker> {
1225        Box::new(DefaultEventLoopWaker)
1226    }
1227}
1228
1229#[cfg(feature = "webxr")]
1230struct DefaultWebXrRegistry;
1231#[cfg(feature = "webxr")]
1232impl webxr::WebXrRegistry for DefaultWebXrRegistry {}
1233
1234pub struct ServoBuilder {
1235    opts: Option<Box<Opts>>,
1236    preferences: Option<Box<Preferences>>,
1237    event_loop_waker: Box<dyn EventLoopWaker>,
1238    protocol_registry: ProtocolRegistry,
1239    #[cfg(feature = "webxr")]
1240    webxr_registry: Box<dyn webxr::WebXrRegistry>,
1241}
1242
1243impl Default for ServoBuilder {
1244    fn default() -> Self {
1245        Self {
1246            opts: Default::default(),
1247            preferences: Default::default(),
1248            event_loop_waker: Box::new(DefaultEventLoopWaker),
1249            protocol_registry: Default::default(),
1250            #[cfg(feature = "webxr")]
1251            webxr_registry: Box::new(DefaultWebXrRegistry),
1252        }
1253    }
1254}
1255
1256impl ServoBuilder {
1257    pub fn build(self) -> Servo {
1258        Servo::new(self)
1259    }
1260
1261    pub fn opts(mut self, opts: Opts) -> Self {
1262        self.opts = Some(Box::new(opts));
1263        self
1264    }
1265
1266    pub fn preferences(mut self, preferences: Preferences) -> Self {
1267        self.preferences = Some(Box::new(preferences));
1268        self
1269    }
1270
1271    pub fn event_loop_waker(mut self, event_loop_waker: Box<dyn EventLoopWaker>) -> Self {
1272        self.event_loop_waker = event_loop_waker;
1273        self
1274    }
1275
1276    pub fn protocol_registry(mut self, protocol_registry: ProtocolRegistry) -> Self {
1277        self.protocol_registry = protocol_registry;
1278        self
1279    }
1280
1281    #[cfg(feature = "webxr")]
1282    pub fn webxr_registry(mut self, webxr_registry: Box<dyn webxr::WebXrRegistry>) -> Self {
1283        self.webxr_registry = webxr_registry;
1284        self
1285    }
1286}
1287
1288fn register_system_memory_reporter_for_event_loop(
1289    new_event_loop_info: &NewScriptEventLoopProcessInfo,
1290) {
1291    // Register the system memory reporter, which will run on its own thread. It never needs to
1292    // be unregistered, because as long as the memory profiler is running the system memory
1293    // reporter can make measurements.
1294    let callback = GenericCallback::new(|message| {
1295        if let Ok(request) = message {
1296            system_reporter::collect_reports(request);
1297        }
1298    })
1299    .expect("Could not create memory reporter callback");
1300    new_event_loop_info
1301        .initial_script_state
1302        .memory_profiler_sender
1303        .send(ProfilerMsg::RegisterReporter(
1304            format!("system-content-{}", std::process::id()),
1305            Reporter(callback),
1306        ));
1307}