From c83cbbd9b75ed0baea9200eb09d0c1b51fde8e99 Mon Sep 17 00:00:00 2001 From: "James C. Wise" Date: Fri, 22 May 2026 23:04:03 -0400 Subject: [PATCH] Fix #1123 --- url/src/lib.rs | 37 +++++++-------------------------- url/src/parser.rs | 4 ++++ url/tests/expected_failures.txt | 13 ------------ url/tests/unit.rs | 11 ---------- 4 files changed, 11 insertions(+), 54 deletions(-) diff --git a/url/src/lib.rs b/url/src/lib.rs index fa2803681..3e3a4d42d 100644 --- a/url/src/lib.rs +++ b/url/src/lib.rs @@ -383,32 +383,6 @@ impl Url { url } - /// https://url.spec.whatwg.org/#potentially-strip-trailing-spaces-from-an-opaque-path - fn strip_trailing_spaces_from_opaque_path(&mut self) { - if !self.cannot_be_a_base() { - return; - } - - if self.fragment_start.is_some() { - return; - } - - if self.query_start.is_some() { - return; - } - - let trailing_space_count = self - .serialization - .chars() - .rev() - .take_while(|c| *c == ' ') - .count(); - - let start = self.serialization.len() - trailing_space_count; - - self.serialization.truncate(start); - } - /// Parse a string as an URL, with this URL as the base URL. /// /// The inverse of this is [`make_relative`]. @@ -1591,7 +1565,6 @@ impl Url { self.mutate(|parser| parser.parse_fragment(parser::Input::new_no_trim(input))) } else { self.fragment_start = None; - self.strip_trailing_spaces_from_opaque_path(); } } @@ -1657,9 +1630,6 @@ impl Url { }); } else { self.query_start = None; - if fragment.is_none() { - self.strip_trailing_spaces_from_opaque_path(); - } } self.restore_already_parsed_fragment(fragment); @@ -1759,6 +1729,13 @@ impl Url { /// assert_eq!(url.as_str(), "https://example.com/api/some%20comments"); /// assert_eq!(url.path(), "/api/some%20comments"); /// + /// // `set_path` will encode leading `/` and trailing ` ` in cannot-be-a-base paths. + /// let mut url = Url::parse("non-special: ?")?; + /// assert_eq!(url.path(), " %20"); + /// url.set_query(None); + /// url.set_path("/ "); + /// assert_eq!(url.path(), "%2F %20"); + /// /// # Ok(()) /// # } /// # run().unwrap(); diff --git a/url/src/parser.rs b/url/src/parser.rs index dbdf9b906..2bd7963af 100644 --- a/url/src/parser.rs +++ b/url/src/parser.rs @@ -1431,6 +1431,10 @@ impl Parser<'_> { Some(('?', _)) | Some(('#', _)) if self.context == Context::UrlParser => { return input_before_c } + Some((' ', _)) if input.starts_with('?') || input.starts_with('#') || input.is_empty() => { + self.serialization.push_str("%20"); + return input; + } Some((c, utf8_c)) => { self.check_url_code_point(c, &input); self.serialization diff --git a/url/tests/expected_failures.txt b/url/tests/expected_failures.txt index 8d4407c45..5fb63d339 100644 --- a/url/tests/expected_failures.txt +++ b/url/tests/expected_failures.txt @@ -46,11 +46,6 @@ set pathname to set pathname to

- - - - - @[\\]^_`{|}~> @[\\]^_`{|}~> against @@ -62,12 +57,4 @@ against against set pathname to <\u{0}\u{1}\t\n\r\u{1f} !\"#$%&\'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u{7f}\u{80}\u{81}\u{c9}\u{e9}> - set hash to <> - set hash to <> - set hash to <> - set hash to <> - set search to <> - set search to <> - set search to <> - set search to <> set port to <\n\n\t\t> diff --git a/url/tests/unit.rs b/url/tests/unit.rs index 828f79756..d3be4f27f 100644 --- a/url/tests/unit.rs +++ b/url/tests/unit.rs @@ -67,17 +67,6 @@ fn test_relative_empty() { assert_eq!(url.as_str(), "sc://%C3%B1"); } -#[test] -fn test_strip_trailing_spaces_from_opaque_path() { - let mut url: Url = "data:space ?query".parse().unwrap(); - url.set_query(None); - assert_eq!(url.as_str(), "data:space"); - - let mut url: Url = "data:space #hash".parse().unwrap(); - url.set_fragment(None); - assert_eq!(url.as_str(), "data:space"); -} - #[test] fn test_set_empty_host() { let mut base: Url = "moz://foo:bar@servo/baz".parse().unwrap();