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