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