diff --git a/ports/servoshell/app.rs b/ports/servoshell/app.rs index 2f44453dc46..fbd7d5bc313 100644 --- a/ports/servoshell/app.rs +++ b/ports/servoshell/app.rs @@ -8,6 +8,7 @@ use std::cell::{Cell, RefCell, RefMut}; use std::collections::HashMap; use std::rc::Rc; use std::time::Instant; +use std::{env, fs}; use gleam::gl; use log::{info, trace, warn}; @@ -71,7 +72,11 @@ impl App { // Handle browser state. let browser = Browser::new(window.clone()); - let initial_url = get_default_url(url); + let initial_url = get_default_url( + url.as_ref().map(String::as_str), + env::current_dir().unwrap(), + |path| fs::metadata(path).is_ok(), + ); let mut app = App { event_queue: RefCell::new(vec![]), diff --git a/ports/servoshell/browser.rs b/ports/servoshell/browser.rs index 752d194bd64..ee707795d13 100644 --- a/ports/servoshell/browser.rs +++ b/ports/servoshell/browser.rs @@ -25,7 +25,7 @@ use servo::webrender_api::ScrollLocation; use tinyfiledialogs::{self, MessageBoxIcon, OkCancel, YesNo}; use crate::keyutils::{CMD_OR_ALT, CMD_OR_CONTROL}; -use crate::parser::sanitize_url; +use crate::parser::location_bar_input_to_url; use crate::window_trait::{WindowPortsMethods, LINE_HEIGHT}; pub struct Browser { @@ -122,7 +122,7 @@ where let title = "URL or search query"; let input = tinyfiledialogs::input_box(title, title, &tiny_dialog_escape(&url)); if let Some(input) = input { - if let Some(url) = sanitize_url(&input) { + if let Some(url) = location_bar_input_to_url(&input) { if let Some(id) = self.browser_id { self.event_queue.push(EmbedderEvent::LoadUrl(id, url)); } diff --git a/ports/servoshell/minibrowser.rs b/ports/servoshell/minibrowser.rs index 75adfbe79d5..16bf72ec34e 100644 --- a/ports/servoshell/minibrowser.rs +++ b/ports/servoshell/minibrowser.rs @@ -11,12 +11,12 @@ use euclid::Length; use log::{trace, warn}; use servo::compositing::windowing::EmbedderEvent; use servo::servo_geometry::DeviceIndependentPixel; -use servo::servo_url::ServoUrl; use servo::webrender_surfman::WebrenderSurfman; use crate::browser::Browser; use crate::egui_glue::EguiGlow; use crate::events_loop::EventsLoop; +use crate::parser::location_bar_input_to_url; use crate::window_trait::WindowPortsMethods; pub struct Minibrowser { @@ -130,11 +130,12 @@ impl Minibrowser { MinibrowserEvent::Go => { let browser_id = browser.browser_id().unwrap(); let location = self.location.borrow(); - let Ok(url) = ServoUrl::parse(&location) else { + if let Some(url) = location_bar_input_to_url(&location.clone()) { + app_event_queue.push(EmbedderEvent::LoadUrl(browser_id, url)); + } else { warn!("failed to parse location"); break; - }; - app_event_queue.push(EmbedderEvent::LoadUrl(browser_id, url)); + } }, } } diff --git a/ports/servoshell/parser.rs b/ports/servoshell/parser.rs index 95333d7cb89..860fe5a9484 100644 --- a/ports/servoshell/parser.rs +++ b/ports/servoshell/parser.rs @@ -2,8 +2,7 @@ * 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/. */ -use std::env; -use std::path::Path; +use std::path::{Path, PathBuf}; use log::warn; use servo::net_traits::pub_domains::is_reg_domain; @@ -21,35 +20,57 @@ pub fn parse_url_or_filename(cwd: &Path, input: &str) -> Result { } } -pub fn get_default_url(url_opt: Option) -> ServoUrl { +pub fn get_default_url( + url_opt: Option<&str>, + cwd: impl AsRef, + exists: impl FnOnce(&PathBuf) -> bool, +) -> ServoUrl { // If the url is not provided, we fallback to the homepage in prefs, // or a blank page in case the homepage is not set either. - let cwd = env::current_dir().unwrap(); + let mut new_url = None; + let cmdline_url = url_opt + .clone() + .map(|s| s.to_string()) + .and_then(|url_string| { + parse_url_or_filename(cwd.as_ref(), &url_string) + .map_err(|error| { + warn!("URL parsing failed ({:?}).", error); + error + }) + .ok() + }); - let cmdline_url = url_opt.map(|s| s.to_string()).and_then(|url_string| { - parse_url_or_filename(&cwd, &url_string) - .map_err(|error| { - warn!("URL parsing failed ({:?}).", error); - error - }) - .ok() - }); + if let Some(url) = cmdline_url.clone() { + // Check if the URL path corresponds to a file + match (url.scheme(), url.host(), url.to_file_path()) { + ("file", None, Ok(ref path)) if exists(path) => { + new_url = cmdline_url; + }, + _ => {}, + } + } + + if new_url.is_none() && !url_opt.is_none() { + new_url = location_bar_input_to_url(url_opt.unwrap()); + } let pref_url = { let homepage_url = pref!(shell.homepage); - parse_url_or_filename(&cwd, &homepage_url).ok() + parse_url_or_filename(cwd.as_ref(), &homepage_url).ok() }; let blank_url = ServoUrl::parse("about:blank").ok(); - cmdline_url.or(pref_url).or(blank_url).unwrap() + new_url.or(pref_url).or(blank_url).unwrap() } -pub fn sanitize_url(request: &str) -> Option { +pub fn location_bar_input_to_url(request: &str) -> Option { let request = request.trim(); ServoUrl::parse(request) .ok() .or_else(|| { - if request.contains('/') || is_reg_domain(request) { + if request.starts_with('/') { + ServoUrl::parse(&format!("file://{}", request)).ok() + } else if request.contains('/') || is_reg_domain(request) { ServoUrl::parse(&format!("https://{}", request)).ok() } else { None diff --git a/ports/servoshell/test.rs b/ports/servoshell/test.rs index 0f870fec995..c715350c7b6 100644 --- a/ports/servoshell/test.rs +++ b/ports/servoshell/test.rs @@ -4,7 +4,7 @@ use std::path::Path; -use crate::parser::parse_url_or_filename; +use crate::parser::{get_default_url, location_bar_input_to_url, parse_url_or_filename}; #[cfg(not(target_os = "windows"))] const FAKE_CWD: &'static str = "/fake/cwd"; @@ -85,3 +85,136 @@ fn test_argument_parsing_special() { assert_eq!(url.query(), None); assert_eq!(url.fragment(), None); } + +// Helper function to test url +fn test_url(input: &str, location: &str, cmdline_if_exists: &str, cmdline_otherwise: &str) { + assert_eq!( + location_bar_input_to_url(input).unwrap().into_string(), + location + ); + assert_eq!( + get_default_url(Some(input), FAKE_CWD, |_| true).into_string(), + cmdline_if_exists + ); + assert_eq!( + get_default_url(Some(input), FAKE_CWD, |_| false).into_string(), + cmdline_otherwise + ); +} + +#[test] +#[cfg(not(target_os = "windows"))] +fn test_cmdline_and_location_bar_url() { + test_url( + "data:text/html,a", + "data:text/html,a", + "data:text/html,a", + "data:text/html,a", + ); + test_url( + "README.md", + "https://readme.md/", + "file:///fake/cwd/README.md", + "https://readme.md/", + ); + test_url( + "nic.md", + "https://nic.md/", + "file:///fake/cwd/nic.md", + "https://nic.md/", + ); + test_url( + "nic.md/ro", + "https://nic.md/ro", + "file:///fake/cwd/nic.md/ro", + "https://nic.md/ro", + ); + test_url( + "foo.txt", + "https://foo.txt/", + "file:///fake/cwd/foo.txt", + "https://foo.txt/", + ); + test_url( + "foo.txt/ro", + "https://foo.txt/ro", + "file:///fake/cwd/foo.txt/ro", + "https://foo.txt/ro", + ); + test_url( + "resources/public_domains.txt", + "https://resources/public_domains.txt", + "file:///fake/cwd/resources/public_domains.txt", + "https://resources/public_domains.txt", + ); + test_url( + "dragonfruit", + "https://duckduckgo.com/html/?q=dragonfruit", + "file:///fake/cwd/dragonfruit", + "https://duckduckgo.com/html/?q=dragonfruit", + ); +} + +#[test] +#[cfg(target_os = "windows")] +fn test_cmdline_and_location_bar_url() { + test_url( + "data:text/html,a", + "data:text/html,a", + "data:text/html,a", + "data:text/html,a", + ); + test_url( + "README.md", + "https://readme.md/", + "file:///C:/fake/cwd/README.md", + "https://readme.md/", + ); + test_url( + "nic.md", + "https://nic.md/", + "file:///C:/fake/cwd/nic.md", + "https://nic.md/", + ); + test_url( + "nic.md/ro", + "https://nic.md/ro", + "file:///C:/fake/cwd/nic.md/ro", + "https://nic.md/ro", + ); + test_url( + "foo.txt", + "https://foo.txt/", + "file:///C:/fake/cwd/foo.txt", + "https://foo.txt/", + ); + test_url( + "foo.txt/ro", + "https://foo.txt/ro", + "file:///C:/fake/cwd/foo.txt/ro", + "https://foo.txt/ro", + ); + test_url( + "resources/public_domains.txt", + "https://resources/public_domains.txt", + "file:///C:/fake/cwd/resources/public_domains.txt", + "https://resources/public_domains.txt", + ); + test_url( + "dragonfruit", + "https://duckduckgo.com/html/?q=dragonfruit", + "file:///C:/fake/cwd/dragonfruit", + "https://duckduckgo.com/html/?q=dragonfruit", + ); +} + +#[cfg(target_os = "linux")] +#[test] +fn test_cmd_and_location_bar_url() { + test_url( + "/dev/null", + "file:///dev/null", + "file:///dev/null", + "file:///dev/null", + ); +}