mirror of
https://github.com/servo/servo
synced 2026-04-25 17:15:48 +02:00
Follow-up to #44443. This helps investigating the cold-start timeline, and could be used by tooling to A/B compare branches affecting the cold-start time. Additionally also change the `handle_request::select` span, so that we can see the blocked time (which was probably what was intended), since the actual time spent on recv after select is insignificant. Testing: Tracing output is not covered by automatic tests. --------- Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
230 lines
7.4 KiB
Rust
230 lines
7.4 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
|
|
//! Application entry point, runs the event loop.
|
|
|
|
use std::rc::Rc;
|
|
use std::time::Instant;
|
|
use std::{env, fs};
|
|
|
|
use servo::protocol_handler::ProtocolRegistry;
|
|
use servo::{EventLoopWaker, Opts, Preferences, ServoBuilder, ServoUrl, UserContentManager};
|
|
use url::Url;
|
|
use winit::application::ApplicationHandler;
|
|
use winit::event::WindowEvent;
|
|
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoopProxy};
|
|
use winit::window::WindowId;
|
|
|
|
use super::event_loop::AppEvent;
|
|
use crate::desktop::event_loop::ServoShellEventLoop;
|
|
use crate::desktop::headed_window::HeadedWindow;
|
|
use crate::desktop::headless_window::HeadlessWindow;
|
|
use crate::desktop::protocols;
|
|
use crate::desktop::tracing::trace_winit_event;
|
|
use crate::parser::get_default_url;
|
|
use crate::prefs::ServoShellPreferences;
|
|
use crate::running_app_state::RunningAppState;
|
|
#[cfg(feature = "gamepad")]
|
|
use crate::running_app_state::ServoshellGamepadDelegate;
|
|
use crate::window::{PlatformWindow, ServoShellWindowId};
|
|
|
|
pub(crate) enum AppState {
|
|
Initializing,
|
|
Running(Rc<RunningAppState>),
|
|
ShuttingDown,
|
|
}
|
|
|
|
pub struct App {
|
|
opts: Opts,
|
|
preferences: Preferences,
|
|
servoshell_preferences: ServoShellPreferences,
|
|
waker: Box<dyn EventLoopWaker>,
|
|
event_loop_proxy: Option<EventLoopProxy<AppEvent>>,
|
|
initial_url: ServoUrl,
|
|
t_start: Instant,
|
|
t: Instant,
|
|
state: AppState,
|
|
}
|
|
|
|
impl App {
|
|
pub fn new(
|
|
opts: Opts,
|
|
preferences: Preferences,
|
|
servo_shell_preferences: ServoShellPreferences,
|
|
event_loop: &ServoShellEventLoop,
|
|
) -> Self {
|
|
let initial_url = get_default_url(
|
|
servo_shell_preferences.url.as_deref(),
|
|
env::current_dir().unwrap(),
|
|
|path| fs::metadata(path).is_ok(),
|
|
&servo_shell_preferences,
|
|
);
|
|
|
|
let t = Instant::now();
|
|
App {
|
|
opts,
|
|
preferences,
|
|
servoshell_preferences: servo_shell_preferences,
|
|
waker: event_loop.create_event_loop_waker(),
|
|
event_loop_proxy: event_loop.event_loop_proxy(),
|
|
initial_url,
|
|
t_start: t,
|
|
t,
|
|
state: AppState::Initializing,
|
|
}
|
|
}
|
|
|
|
/// Initialize Application once event loop start running.
|
|
pub fn init(&mut self, active_event_loop: Option<&ActiveEventLoop>) {
|
|
let mut protocol_registry = ProtocolRegistry::default();
|
|
let _ = protocol_registry.register(
|
|
"urlinfo",
|
|
protocols::urlinfo::UrlInfoProtocolHander::default(),
|
|
);
|
|
let _ =
|
|
protocol_registry.register("servo", protocols::servo::ServoProtocolHandler::default());
|
|
let _ = protocol_registry.register(
|
|
"resource",
|
|
protocols::resource::ResourceProtocolHandler::default(),
|
|
);
|
|
|
|
let servo_builder = ServoBuilder::default()
|
|
.opts(self.opts.clone())
|
|
.preferences(self.preferences.clone())
|
|
.protocol_registry(protocol_registry)
|
|
.event_loop_waker(self.waker.clone());
|
|
|
|
let url = self.initial_url.as_url().clone();
|
|
let platform_window = self.create_platform_window(url, active_event_loop);
|
|
|
|
#[cfg(feature = "webxr")]
|
|
let servo_builder =
|
|
servo_builder.webxr_registry(super::webxr::XrDiscoveryWebXrRegistry::new_boxed(
|
|
platform_window.clone(),
|
|
active_event_loop,
|
|
&self.preferences,
|
|
));
|
|
|
|
let servo = servo_builder.build();
|
|
servo.setup_logging();
|
|
|
|
let user_content_manager = Rc::new(UserContentManager::new(&servo));
|
|
|
|
for user_stylesheet in &self.servoshell_preferences.user_stylesheets {
|
|
user_content_manager.add_stylesheet(user_stylesheet.clone());
|
|
}
|
|
|
|
let running_state = Rc::new(RunningAppState::new(
|
|
servo,
|
|
self.servoshell_preferences.clone(),
|
|
self.waker.clone(),
|
|
user_content_manager,
|
|
self.preferences.clone(),
|
|
#[cfg(feature = "gamepad")]
|
|
ServoshellGamepadDelegate::maybe_new().map(Rc::new),
|
|
));
|
|
running_state.open_window(platform_window, self.initial_url.as_url().clone());
|
|
|
|
self.state = AppState::Running(running_state);
|
|
}
|
|
|
|
#[servo::servo_tracing::instrument(level = "debug", skip_all)]
|
|
fn create_platform_window(
|
|
&self,
|
|
url: Url,
|
|
active_event_loop: Option<&ActiveEventLoop>,
|
|
) -> Rc<dyn PlatformWindow> {
|
|
assert_eq!(
|
|
self.servoshell_preferences.headless,
|
|
active_event_loop.is_none()
|
|
);
|
|
|
|
let Some(active_event_loop) = active_event_loop else {
|
|
return HeadlessWindow::new(&self.servoshell_preferences);
|
|
};
|
|
|
|
HeadedWindow::new(
|
|
&self.servoshell_preferences,
|
|
active_event_loop,
|
|
self.event_loop_proxy
|
|
.clone()
|
|
.expect("Should always have event loop proxy in headed mode."),
|
|
url,
|
|
)
|
|
}
|
|
|
|
pub fn pump_servo_event_loop(&mut self, active_event_loop: Option<&ActiveEventLoop>) -> bool {
|
|
let AppState::Running(state) = &self.state else {
|
|
return false;
|
|
};
|
|
|
|
let create_platform_window = |url: Url| self.create_platform_window(url, active_event_loop);
|
|
if !state.spin_event_loop(Some(&create_platform_window)) {
|
|
self.state = AppState::ShuttingDown;
|
|
return false;
|
|
}
|
|
true
|
|
}
|
|
}
|
|
|
|
impl ApplicationHandler<AppEvent> for App {
|
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
|
self.init(Some(event_loop));
|
|
}
|
|
|
|
fn window_event(
|
|
&mut self,
|
|
event_loop: &ActiveEventLoop,
|
|
window_id: WindowId,
|
|
window_event: WindowEvent,
|
|
) {
|
|
let now = Instant::now();
|
|
trace_winit_event!(
|
|
window_event,
|
|
"@{:?} (+{:?}) {window_event:?}",
|
|
now - self.t_start,
|
|
now - self.t
|
|
);
|
|
self.t = now;
|
|
|
|
let AppState::Running(state) = &self.state else {
|
|
return;
|
|
};
|
|
|
|
if let Some(window) = state.window(ServoShellWindowId::from(u64::from(window_id))) {
|
|
if let Some(headed_window) = window.platform_window().as_headed_window() {
|
|
headed_window.handle_winit_window_event(state.clone(), window, window_event);
|
|
}
|
|
}
|
|
|
|
if !self.pump_servo_event_loop(event_loop.into()) {
|
|
event_loop.exit();
|
|
}
|
|
// Block until the window gets an event
|
|
event_loop.set_control_flow(ControlFlow::Wait);
|
|
}
|
|
|
|
fn user_event(&mut self, event_loop: &ActiveEventLoop, app_event: AppEvent) {
|
|
let AppState::Running(state) = &self.state else {
|
|
return;
|
|
};
|
|
|
|
if let Some(window) = app_event
|
|
.window_id()
|
|
.and_then(|window_id| state.window(ServoShellWindowId::from(u64::from(window_id))))
|
|
{
|
|
if let Some(headed_window) = window.platform_window().as_headed_window() {
|
|
headed_window.handle_winit_app_event(state.clone(), app_event);
|
|
}
|
|
}
|
|
|
|
if !self.pump_servo_event_loop(event_loop.into()) {
|
|
event_loop.exit();
|
|
}
|
|
|
|
// Block until the window gets an event
|
|
event_loop.set_control_flow(ControlFlow::Wait);
|
|
}
|
|
}
|