Allow domain-like as URL location input (#35756)

* Allow domain-like as URL location input

Before this patch, domain with subdomain (e.g. book.servo.org) won't be
treated as URL location.

This patch retifies that by adding Firefox's location bar behavior:

  - book.servo.org is URL location
  - book.servo.org. is URL location
  - .book.servo.org is not URL location

Fixes #35754.

Signed-off-by: Kafji <k@kafji.net>

* Chain location input interpretation attempts

Signed-off-by: Kafji <k@kafji.net>

---------

Signed-off-by: Kafji <k@kafji.net>
This commit is contained in:
Kafji 2025-03-10 10:24:48 +07:00 committed by GitHub
parent 48aacc43b7
commit 34047f8da8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 94 additions and 9 deletions

View file

@ -63,13 +63,35 @@ pub fn get_default_url(
/// interpret the string as a search term.
pub(crate) fn location_bar_input_to_url(request: &str, searchpage: &str) -> Option<ServoUrl> {
let request = request.trim();
ServoUrl::parse(request).ok().or_else(|| {
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 {
ServoUrl::parse(&searchpage.replace("%s", request)).ok()
}
})
ServoUrl::parse(request)
.ok()
.or_else(|| try_as_file(request))
.or_else(|| try_as_domain(request))
.or_else(|| try_as_search_page(request, searchpage))
}
fn try_as_file(request: &str) -> Option<ServoUrl> {
if request.starts_with('/') {
return ServoUrl::parse(&format!("file://{}", request)).ok();
}
None
}
fn try_as_domain(request: &str) -> Option<ServoUrl> {
fn is_domain_like(s: &str) -> bool {
!s.starts_with('/') && s.contains('/') ||
(!s.contains(' ') && !s.starts_with('.') && s.split('.').count() > 1)
}
if !request.contains(' ') && is_reg_domain(request) || is_domain_like(request) {
return ServoUrl::parse(&format!("https://{}", request)).ok();
}
None
}
fn try_as_search_page(request: &str, searchpage: &str) -> Option<ServoUrl> {
if request.is_empty() {
return None;
}
ServoUrl::parse(&searchpage.replace("%s", request)).ok()
}

View file

@ -220,3 +220,66 @@ fn test_cmd_and_location_bar_url() {
"file:///dev/null",
);
}
/// Like [test_url] but will produce test for Windows or non Windows using `#[cfg(target_os)]` internally.
fn test_url_any_os(
input: &str,
location: &str,
#[allow(unused)] if_exists: &str,
#[allow(unused)] if_exists_windows: &str,
otherwise: &str,
) {
#[cfg(not(target_os = "windows"))]
test_url(input, location, if_exists, otherwise);
#[cfg(target_os = "windows")]
test_url(input, location, if_exists_windows, otherwise);
}
// https://github.com/servo/servo/issues/35754
#[test]
fn test_issue_35754() {
test_url_any_os(
"leah.chromebooks.lol",
"https://leah.chromebooks.lol/",
"file:///fake/cwd/leah.chromebooks.lol",
"file:///C:/fake/cwd/leah.chromebooks.lol",
"https://leah.chromebooks.lol/",
);
// ends with dot
test_url_any_os(
"leah.chromebooks.lol.",
"https://leah.chromebooks.lol./",
"file:///fake/cwd/leah.chromebooks.lol.",
"file:///C:/fake/cwd/leah.chromebooks.lol.",
"https://leah.chromebooks.lol./",
);
// starts with dot
test_url_any_os(
".leah.chromebooks.lol",
"https://duckduckgo.com/html/?q=.leah.chromebooks.lol",
"file:///fake/cwd/.leah.chromebooks.lol",
"file:///C:/fake/cwd/.leah.chromebooks.lol",
"https://duckduckgo.com/html/?q=.leah.chromebooks.lol",
);
// contains spaces
test_url_any_os(
"3.5 kg in lb",
"https://duckduckgo.com/html/?q=3.5%20kg%20in%20lb",
"file:///fake/cwd/3.5%20kg%20in%20lb",
"file:///C:/fake/cwd/3.5%20kg%20in%20lb",
"https://duckduckgo.com/html/?q=3.5%20kg%20in%20lb",
);
// user-local domain
test_url_any_os(
"foo/bar",
"https://foo/bar",
"file:///fake/cwd/foo/bar",
"file:///C:/fake/cwd/foo/bar",
"https://foo/bar",
);
}