From 6ff4f7f8cd9e210ed42dd4404221297fb7893ba2 Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 17 Oct 2024 18:05:45 +0800 Subject: [PATCH 1/3] Allow changing initial url from cli --- src/compositor.rs | 4 +-- src/config.rs | 40 +++++++++++++++++++++++++++++- src/verso.rs | 6 +++-- src/webview.rs | 11 ++++++--- src/window.rs | 62 ++++++++++++++++++++++++++++++++--------------- 5 files changed, 96 insertions(+), 27 deletions(-) diff --git a/src/compositor.rs b/src/compositor.rs index 9850007..f791d90 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -1236,8 +1236,8 @@ impl IOCompositor { if let Some(panel) = &mut window.panel { let rect = DeviceIntRect::from_size(size); - panel.rect = rect; - self.on_resize_webview_event(panel.webview_id, rect); + panel.webview.rect = rect; + self.on_resize_webview_event(panel.webview.webview_id, rect); } let rect = DeviceIntRect::from_size(size); diff --git a/src/config.rs b/src/config.rs index 3a75ab0..41aa49b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,6 +10,13 @@ use net_traits::{ }; use servo_config::opts::{default_opts, set_options, Opts}; +/// Command line arguments. +#[derive(Clone, Debug, Default)] +pub struct CliArgs { + /// Url to load initially. + pub initial_url: Option, +} + /// Configuration of Verso instance. #[derive(Clone, Debug)] pub struct Config { @@ -17,6 +24,33 @@ pub struct Config { pub opts: Opts, /// Path to resources directory. pub resource_dir: PathBuf, + /// Command line arguments. + pub cli_args: CliArgs, +} + +fn parse_cli_args() -> Result { + let args: Vec = std::env::args().collect(); + + let mut opts = getopts::Options::new(); + opts.optopt("", "initial-url", "load this url on start", "url"); + + let matches = opts.parse(&args[1..])?; + let initial_url = matches + .opt_str("initial-url") + .and_then(|initial_url| match url::Url::parse(&initial_url) { + Ok(url_parsed) => Some(url_parsed), + Err(e) => { + if e == url::ParseError::RelativeUrlWithoutBase { + if let Ok(url_parsed) = url::Url::parse(&format!("https://{initial_url}")) { + return Some(url_parsed); + } + } + log::error!("Invalid initial url: {initial_url}"); + None + } + }); + + Ok(CliArgs { initial_url }) } impl Config { @@ -24,7 +58,11 @@ impl Config { /// resources directory. pub fn new(resource_dir: PathBuf) -> Self { let opts = default_opts(); - Self { opts, resource_dir } + Self { + opts, + resource_dir, + cli_args: parse_cli_args().unwrap_or_default(), + } } /// Register URL scheme protocols diff --git a/src/verso.rs b/src/verso.rs index 305c046..fdafb75 100644 --- a/src/verso.rs +++ b/src/verso.rs @@ -78,10 +78,12 @@ impl Verso { pub fn new(evl: &ActiveEventLoop, proxy: EventLoopProxy<()>, config: Config) -> Self { // Initialize configurations and Verso window let protocols = config.create_protocols(); + let initial_url = config.cli_args.initial_url.clone(); config.init(); // Reserving a namespace to create TopLevelBrowsingContextId. PipelineNamespace::install(PipelineNamespaceId(0)); let (mut window, rendering_context) = Window::new(evl); + let event_loop_waker = Box::new(Waker(proxy)); let opts = opts::get(); @@ -359,7 +361,7 @@ impl Verso { opts.debug.convert_mouse_to_touch, ); - window.create_panel(&constellation_sender); + window.create_panel(&constellation_sender, initial_url); let mut windows = HashMap::new(); windows.insert(window.id(), (window, webrender_document)); @@ -428,7 +430,7 @@ impl Verso { ) { let mut window = Window::new_with_compositor(evl, compositor); - window.create_panel(&self.constellation_sender); + window.create_panel(&self.constellation_sender, None); let webrender_document = document.clone(); self.windows .insert(window.id(), (window, webrender_document)); diff --git a/src/webview.rs b/src/webview.rs index 7331d28..4a3f737 100644 --- a/src/webview.rs +++ b/src/webview.rs @@ -105,7 +105,7 @@ impl Window { send_to_constellation( sender, ConstellationMsg::WebDriverCommand(WebDriverCommandMsg::ScriptCommand( - BrowsingContextId::from(panel.webview_id), + BrowsingContextId::from(panel.webview.webview_id), WebDriverScriptCommand::ExecuteScript( format!("window.navbar.setNavbarUrl('{}')", url.as_str()), tx, @@ -155,14 +155,19 @@ impl Window { self.window.request_redraw(); send_to_constellation(sender, ConstellationMsg::FocusWebView(panel_id)); - let demo_url = ServoUrl::parse("https://example.com").unwrap(); let demo_id = WebViewId::new(); let size = self.size(); let rect = DeviceIntRect::from_size(size); let mut webview = WebView::new(demo_id, rect); webview.set_size(self.get_content_size(rect)); self.webview = Some(webview); - send_to_constellation(sender, ConstellationMsg::NewWebView(demo_url, demo_id)); + send_to_constellation( + sender, + ConstellationMsg::NewWebView( + self.panel.as_ref().unwrap().initial_url.clone(), + demo_id, + ), + ); log::debug!("Verso Window {:?} adds webview {}", self.id(), demo_id); } EmbedderMsg::AllowNavigationRequest(id, _url) => { diff --git a/src/window.rs b/src/window.rs index d581361..07daca0 100644 --- a/src/window.rs +++ b/src/window.rs @@ -34,24 +34,30 @@ use crate::{ use arboard::Clipboard; +/// A panel is a special web view that focus on controlling states around window. +/// It could be treated as the control panel or navigation bar of the window depending on usages. +/// +/// At the moment, following Web API is supported: +/// - Close window: `window.close()` +/// - Navigate to previous page: `window.prompt('PREV')` +/// - Navigate to next page: `window.prompt('FORWARD')` +/// - Refresh the page: `window.prompt('REFRESH')` +/// - Minimize the window: `window.prompt('MINIMIZE')` +/// - Maximize the window: `window.prompt('MAXIMIZE')` +/// - Navigate to a specific URL: `window.prompt('NAVIGATE_TO:${url}')` +pub struct Panel { + pub(crate) webview: WebView, + pub(crate) initial_url: servo_url::ServoUrl, +} + /// A Verso window is a Winit window containing several web views. pub struct Window { /// Access to Winit window pub(crate) window: WinitWindow, /// GL surface of the window pub(crate) surface: Surface, - /// The main panel of this window. A panel is a special web view that focus on controlling states around window. - /// It could be treated as the control panel or navigation bar of the window depending on usages. - /// - /// At the moment, following Web API is supported: - /// - Close window: `window.close()` - /// - Navigate to previous page: `window.prompt('PREV')` - /// - Navigate to next page: `window.prompt('FORWARD')` - /// - Refresh the page: `window.prompt('REFRESH')` - /// - Minimize the window: `window.prompt('MINIMIZE')` - /// - Maximize the window: `window.prompt('MAXIMIZE')` - /// - Navigate to a specific URL: `window.prompt('NAVIGATE_TO:${url}')` - pub(crate) panel: Option, + /// The main panel of this window. + pub(crate) panel: Option, /// The WebView of this window. pub(crate) webview: Option, /// The mouse physical position in the web view. @@ -154,11 +160,22 @@ impl Window { } /// Send the constellation message to start Panel UI - pub fn create_panel(&mut self, constellation_sender: &Sender) { + pub fn create_panel( + &mut self, + constellation_sender: &Sender, + initial_url: Option, + ) { let size = self.window.inner_size(); let size = Size2D::new(size.width as i32, size.height as i32); let panel_id = WebViewId::new(); - self.panel = Some(WebView::new(panel_id, DeviceIntRect::from_size(size))); + self.panel = Some(Panel { + webview: WebView::new(panel_id, DeviceIntRect::from_size(size)), + initial_url: if let Some(initial_url) = initial_url { + servo_url::ServoUrl::from_url(initial_url) + } else { + ServoUrl::parse("https://example.com").unwrap() + }, + }); let url = ServoUrl::parse("verso://panel.html").unwrap(); send_to_constellation( @@ -302,7 +319,7 @@ impl Window { ) -> bool { // // Handle message in Verso Panel if let Some(panel) = &self.panel { - if panel.webview_id == webview_id { + if panel.webview.webview_id == webview_id { return self.handle_servo_messages_with_panel( webview_id, message, sender, clipboard, compositor, ); @@ -336,7 +353,9 @@ impl Window { /// Check if the window has such webview. pub fn has_webview(&self, id: WebViewId) -> bool { - self.panel.as_ref().map_or(false, |w| w.webview_id == id) + self.panel + .as_ref() + .map_or(false, |w| w.webview.webview_id == id) || self.webview.as_ref().map_or(false, |w| w.webview_id == id) } @@ -347,14 +366,19 @@ impl Window { id: WebViewId, compositor: &mut IOCompositor, ) -> (Option, bool) { - if self.panel.as_ref().filter(|w| w.webview_id == id).is_some() { + if self + .panel + .as_ref() + .filter(|w| w.webview.webview_id == id) + .is_some() + { if let Some(w) = self.webview.as_ref() { send_to_constellation( &compositor.constellation_chan, ConstellationMsg::CloseWebView(w.webview_id), ) } - (self.panel.take(), false) + (self.panel.take().map(|panel| panel.webview), false) } else if self .webview .as_ref() @@ -371,7 +395,7 @@ impl Window { pub fn painting_order(&self) -> Vec<&WebView> { let mut order = vec![]; if let Some(panel) = &self.panel { - order.push(panel); + order.push(&panel.webview); } if let Some(webview) = &self.webview { order.push(webview); From 517ea96ffff379d23ac7e221784f3f09a95e4fcb Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 18 Oct 2024 11:16:02 +0800 Subject: [PATCH 2/3] Apply suggestions from code review --- src/config.rs | 12 ++++++------ src/verso.rs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/config.rs b/src/config.rs index 41aa49b..8805506 100644 --- a/src/config.rs +++ b/src/config.rs @@ -13,8 +13,8 @@ use servo_config::opts::{default_opts, set_options, Opts}; /// Command line arguments. #[derive(Clone, Debug, Default)] pub struct CliArgs { - /// Url to load initially. - pub initial_url: Option, + /// URL to load initially. + pub url: Option, } /// Configuration of Verso instance. @@ -25,14 +25,14 @@ pub struct Config { /// Path to resources directory. pub resource_dir: PathBuf, /// Command line arguments. - pub cli_args: CliArgs, + pub args: CliArgs, } fn parse_cli_args() -> Result { let args: Vec = std::env::args().collect(); let mut opts = getopts::Options::new(); - opts.optopt("", "initial-url", "load this url on start", "url"); + opts.optopt("", "url", "URL to load on start", "URL"); let matches = opts.parse(&args[1..])?; let initial_url = matches @@ -50,7 +50,7 @@ fn parse_cli_args() -> Result { } }); - Ok(CliArgs { initial_url }) + Ok(CliArgs { url: initial_url }) } impl Config { @@ -61,7 +61,7 @@ impl Config { Self { opts, resource_dir, - cli_args: parse_cli_args().unwrap_or_default(), + args: parse_cli_args().unwrap_or_default(), } } diff --git a/src/verso.rs b/src/verso.rs index fdafb75..1b78b6b 100644 --- a/src/verso.rs +++ b/src/verso.rs @@ -78,7 +78,7 @@ impl Verso { pub fn new(evl: &ActiveEventLoop, proxy: EventLoopProxy<()>, config: Config) -> Self { // Initialize configurations and Verso window let protocols = config.create_protocols(); - let initial_url = config.cli_args.initial_url.clone(); + let initial_url = config.args.url.clone(); config.init(); // Reserving a namespace to create TopLevelBrowsingContextId. PipelineNamespace::install(PipelineNamespaceId(0)); From 60c4e6b18a3ae07f1580e1abbe7113d988d8f6ca Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 18 Oct 2024 11:46:34 +0800 Subject: [PATCH 3/3] Move panel to webview.rs --- src/webview.rs | 18 ++++++++++++++++++ src/window.rs | 18 +----------------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/webview.rs b/src/webview.rs index 4a3f737..721bac7 100644 --- a/src/webview.rs +++ b/src/webview.rs @@ -35,6 +35,24 @@ impl WebView { } } +/// A panel is a special web view that focus on controlling states around window. +/// It could be treated as the control panel or navigation bar of the window depending on usages. +/// +/// At the moment, following Web API is supported: +/// - Close window: `window.close()` +/// - Navigate to previous page: `window.prompt('PREV')` +/// - Navigate to next page: `window.prompt('FORWARD')` +/// - Refresh the page: `window.prompt('REFRESH')` +/// - Minimize the window: `window.prompt('MINIMIZE')` +/// - Maximize the window: `window.prompt('MAXIMIZE')` +/// - Navigate to a specific URL: `window.prompt('NAVIGATE_TO:${url}')` +pub struct Panel { + /// The panel's webview + pub(crate) webview: WebView, + /// The URL to load when the panel gets loaded + pub(crate) initial_url: servo_url::ServoUrl, +} + impl Window { /// Handle servo messages with corresponding web view ID. pub fn handle_servo_messages_with_webview( diff --git a/src/window.rs b/src/window.rs index 07daca0..764f39a 100644 --- a/src/window.rs +++ b/src/window.rs @@ -29,27 +29,11 @@ use crate::{ keyboard::keyboard_event_from_winit, rendering::{gl_config_picker, RenderingContext}, verso::send_to_constellation, - webview::WebView, + webview::{Panel, WebView}, }; use arboard::Clipboard; -/// A panel is a special web view that focus on controlling states around window. -/// It could be treated as the control panel or navigation bar of the window depending on usages. -/// -/// At the moment, following Web API is supported: -/// - Close window: `window.close()` -/// - Navigate to previous page: `window.prompt('PREV')` -/// - Navigate to next page: `window.prompt('FORWARD')` -/// - Refresh the page: `window.prompt('REFRESH')` -/// - Minimize the window: `window.prompt('MINIMIZE')` -/// - Maximize the window: `window.prompt('MAXIMIZE')` -/// - Navigate to a specific URL: `window.prompt('NAVIGATE_TO:${url}')` -pub struct Panel { - pub(crate) webview: WebView, - pub(crate) initial_url: servo_url::ServoUrl, -} - /// A Verso window is a Winit window containing several web views. pub struct Window { /// Access to Winit window