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