diff options
Diffstat (limited to 'vendor/url')
-rw-r--r-- | vendor/url/.cargo-checksum.json | 2 | ||||
-rw-r--r-- | vendor/url/Cargo.toml | 16 | ||||
-rw-r--r-- | vendor/url/src/host.rs | 10 | ||||
-rw-r--r-- | vendor/url/src/lib.rs | 114 | ||||
-rw-r--r-- | vendor/url/src/parser.rs | 80 | ||||
-rw-r--r-- | vendor/url/src/slicing.rs | 32 | ||||
-rw-r--r-- | vendor/url/tests/data.rs | 30 | ||||
-rw-r--r-- | vendor/url/tests/unit.rs | 136 | ||||
-rw-r--r-- | vendor/url/tests/urltestdata.json | 1013 |
9 files changed, 1377 insertions, 56 deletions
diff --git a/vendor/url/.cargo-checksum.json b/vendor/url/.cargo-checksum.json index 8a659101a..df3624ea0 100644 --- a/vendor/url/.cargo-checksum.json +++ b/vendor/url/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"c861c99782bfd4be160167bf659e87792f00d7e1eb1ee072a59546143fa980f4","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"76e972ac0f4ddb116e86e10100132a783931a596e7b9872eaa31be15cd4d751d","README.md":"71b01ec6f2f4ce47235ee430ba0c41afac563403a9dbcda23a584c3e915395ac","src/host.rs":"60cd21e0b4cbb208c36eec5d40628a8bbe1d7ef13e481479b33f89cef96277ec","src/lib.rs":"c48655bc0455efecb7f7e36dfee8b92f6d96d9a1fbc5b24890ef255fb7843956","src/origin.rs":"19a4b451e8615bfef7239d2fc719c489398fe5044edb0df7c84b54eef4ceba1b","src/parser.rs":"5c05f233722560fd21899e982b9defc828ba13fbe70e7cf157a439968716c6ae","src/path_segments.rs":"dd6b637245b2ad77ce96221df3f80c8b4ad858cd52aecc86b97166dec386882a","src/quirks.rs":"82903ff9c95604409cded330e36ebba0ede91c469d1aa40fb646186e2bef0769","src/slicing.rs":"25425fc5c4100a3a5da49d1e0b497f6536066afcc91c4ba4dff5ace0860acd40","tests/data.rs":"dbd795b25fe7869b242aeeb822c736167d5b4be1f6a867f563e23977d07c8fc5","tests/debugger_visualizer.rs":"d3a9466c636ee80ec9181817a8d30ea3e0cee699776ee4b805175981e4775448","tests/setters_tests.json":"486f6d129960d0d0d99b533caf9bef21113b31adcdb83296dfc4a59cd8431715","tests/unit.rs":"9707676cd80d7c8131b9c805f03b387a36ca2b1dda84a4d567788814c8afe59f","tests/urltestdata.json":"9dca78fbbcb6fceb0584f2fae7b3bb4e8a868750f2426c359c6694aed25f2682"},"package":"0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"}
\ No newline at end of file +{"files":{"Cargo.toml":"81ec036a51f251210a8469d004611867b1b18cc5e6a5e27abefa86c80b057d75","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"76e972ac0f4ddb116e86e10100132a783931a596e7b9872eaa31be15cd4d751d","README.md":"71b01ec6f2f4ce47235ee430ba0c41afac563403a9dbcda23a584c3e915395ac","src/host.rs":"5e25476aaec0153b64d35b53940a72a1ec58e29a0e1fde36944f52eeb945c5f6","src/lib.rs":"fe1a969023cf7fca36cbd58f72b6cdb988d0ac58c0555d8ecc376dfd78d19a69","src/origin.rs":"19a4b451e8615bfef7239d2fc719c489398fe5044edb0df7c84b54eef4ceba1b","src/parser.rs":"8a928f10387b39fefbba9b22048452b1b1ae9cb46b3c349dea2a59d169e7aed8","src/path_segments.rs":"dd6b637245b2ad77ce96221df3f80c8b4ad858cd52aecc86b97166dec386882a","src/quirks.rs":"82903ff9c95604409cded330e36ebba0ede91c469d1aa40fb646186e2bef0769","src/slicing.rs":"39f4e624adbdbf952b7da8bfe6abdfa6e344193d6e1bbca1b0ccbfc821573f10","tests/data.rs":"35c8f1ab545c3e20a3895712ef235aed07dd8226bf9be6e7b21674fc0125c2be","tests/debugger_visualizer.rs":"d3a9466c636ee80ec9181817a8d30ea3e0cee699776ee4b805175981e4775448","tests/setters_tests.json":"486f6d129960d0d0d99b533caf9bef21113b31adcdb83296dfc4a59cd8431715","tests/unit.rs":"ff61fd7640cc84f45f6dd73d8785576d070506a62c48475bcbf927e9ae97edfd","tests/urltestdata.json":"4df7b990a7624da4fd94bee862fe1c3c1ded59fc27fb1cdf08d4a20b655aa3b7"},"package":"50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb"}
\ No newline at end of file diff --git a/vendor/url/Cargo.toml b/vendor/url/Cargo.toml index 4363ae58f..311354ca2 100644 --- a/vendor/url/Cargo.toml +++ b/vendor/url/Cargo.toml @@ -11,9 +11,9 @@ [package] edition = "2018" -rust-version = "1.51" +rust-version = "1.56" name = "url" -version = "2.3.1" +version = "2.4.0" authors = ["The rust-url developers"] include = [ "src/**/*", @@ -36,6 +36,12 @@ categories = [ license = "MIT OR Apache-2.0" repository = "https://github.com/servo/rust-url" +[package.metadata.docs.rs] +features = ["serde"] + +[package.metadata.playground] +features = ["serde"] + [[test]] name = "debugger_visualizer" path = "tests/debugger_visualizer.rs" @@ -48,13 +54,13 @@ path = "benches/parse_url.rs" harness = false [dependencies.form_urlencoded] -version = "1.1.0" +version = "1.2.0" [dependencies.idna] -version = "0.3.0" +version = "0.4.0" [dependencies.percent-encoding] -version = "2.2.0" +version = "2.3.0" [dependencies.serde] version = "1.0" diff --git a/vendor/url/src/host.rs b/vendor/url/src/host.rs index f1921c654..9931c2f87 100644 --- a/vendor/url/src/host.rs +++ b/vendor/url/src/host.rs @@ -269,7 +269,7 @@ fn ends_in_a_number(input: &str) -> bool { } else { last }; - if !last.is_empty() && last.chars().all(|c| ('0'..='9').contains(&c)) { + if !last.is_empty() && last.as_bytes().iter().all(|c| c.is_ascii_digit()) { return true; } @@ -297,11 +297,9 @@ fn parse_ipv4number(mut input: &str) -> Result<Option<u32>, ()> { } let valid_number = match r { - 8 => input.chars().all(|c| ('0'..='7').contains(&c)), - 10 => input.chars().all(|c| ('0'..='9').contains(&c)), - 16 => input.chars().all(|c| { - ('0'..='9').contains(&c) || ('a'..='f').contains(&c) || ('A'..='F').contains(&c) - }), + 8 => input.as_bytes().iter().all(|c| (b'0'..=b'7').contains(c)), + 10 => input.as_bytes().iter().all(|c| c.is_ascii_digit()), + 16 => input.as_bytes().iter().all(|c| c.is_ascii_hexdigit()), _ => false, }; if !valid_number { diff --git a/vendor/url/src/lib.rs b/vendor/url/src/lib.rs index 6dc09d12f..ad3c89001 100644 --- a/vendor/url/src/lib.rs +++ b/vendor/url/src/lib.rs @@ -121,7 +121,7 @@ url = { version = "2", features = ["serde"] } */ -#![doc(html_root_url = "https://docs.rs/url/2.3.1")] +#![doc(html_root_url = "https://docs.rs/url/2.4.0")] #![cfg_attr( feature = "debugger_visualizer", feature(debugger_visualizer), @@ -322,6 +322,32 @@ 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`]. @@ -601,7 +627,7 @@ impl Url { } assert!(self.scheme_end >= 1); - assert!(matches!(self.byte_at(0), b'a'..=b'z' | b'A'..=b'Z')); + assert!(self.byte_at(0).is_ascii_alphabetic()); assert!(self .slice(1..self.scheme_end) .chars() @@ -657,7 +683,14 @@ impl Url { assert_eq!(self.host_end, self.scheme_end + 1); assert_eq!(self.host, HostInternal::None); assert_eq!(self.port, None); - assert_eq!(self.path_start, self.scheme_end + 1); + if self.path().starts_with("//") { + // special case when first path segment is empty + assert_eq!(self.byte_at(self.scheme_end + 1), b'/'); + assert_eq!(self.byte_at(self.scheme_end + 2), b'.'); + assert_eq!(self.path_start, self.scheme_end + 3); + } else { + assert_eq!(self.path_start, self.scheme_end + 1); + } } if let Some(start) = self.query_start { assert!(start >= self.path_start); @@ -786,12 +819,35 @@ impl Url { self.slice(..self.scheme_end) } + /// Return whether the URL is special (has a special scheme) + /// + /// # Examples + /// + /// ``` + /// use url::Url; + /// # use url::ParseError; + /// + /// # fn run() -> Result<(), ParseError> { + /// assert!(Url::parse("http:///tmp/foo")?.is_special()); + /// assert!(Url::parse("file:///tmp/foo")?.is_special()); + /// assert!(!Url::parse("moz:///tmp/foo")?.is_special()); + /// # Ok(()) + /// # } + /// # run().unwrap(); + /// ``` + pub fn is_special(&self) -> bool { + let scheme_type = SchemeType::from(self.scheme()); + scheme_type.is_special() + } + /// Return whether the URL has an 'authority', /// which can contain a username, password, host, and port number. /// /// URLs that do *not* are either path-only like `unix:/run/foo.socket` /// or cannot-be-a-base like `data:text/plain,Stuff`. /// + /// See also the `authority` method. + /// /// # Examples /// /// ``` @@ -817,6 +873,47 @@ impl Url { self.slice(self.scheme_end..).starts_with("://") } + /// Return the authority of this URL as an ASCII string. + /// + /// Non-ASCII domains are punycode-encoded per IDNA if this is the host + /// of a special URL, or percent encoded for non-special URLs. + /// IPv6 addresses are given between `[` and `]` brackets. + /// Ports are omitted if they match the well known port of a special URL. + /// + /// Username and password are percent-encoded. + /// + /// See also the `has_authority` method. + /// + /// # Examples + /// + /// ``` + /// use url::Url; + /// # use url::ParseError; + /// + /// # fn run() -> Result<(), ParseError> { + /// let url = Url::parse("unix:/run/foo.socket")?; + /// assert_eq!(url.authority(), ""); + /// let url = Url::parse("file:///tmp/foo")?; + /// assert_eq!(url.authority(), ""); + /// let url = Url::parse("https://user:password@example.com/tmp/foo")?; + /// assert_eq!(url.authority(), "user:password@example.com"); + /// let url = Url::parse("irc://àlex.рф.example.com:6667/foo")?; + /// assert_eq!(url.authority(), "%C3%A0lex.%D1%80%D1%84.example.com:6667"); + /// let url = Url::parse("http://àlex.рф.example.com:80/foo")?; + /// assert_eq!(url.authority(), "xn--lex-8ka.xn--p1ai.example.com"); + /// # Ok(()) + /// # } + /// # run().unwrap(); + /// ``` + pub fn authority(&self) -> &str { + let scheme_separator_len = "://".len() as u32; + if self.has_authority() && self.path_start > self.scheme_end + scheme_separator_len { + self.slice(self.scheme_end + scheme_separator_len..self.path_start) + } else { + "" + } + } + /// Return whether this URL is a cannot-be-a-base URL, /// meaning that parsing a relative URL string with this URL as the base will return an error. /// @@ -1391,7 +1488,8 @@ impl Url { self.serialization.push('#'); self.mutate(|parser| parser.parse_fragment(parser::Input::no_trim(input))) } else { - self.fragment_start = None + self.fragment_start = None; + self.strip_trailing_spaces_from_opaque_path(); } } @@ -1454,6 +1552,9 @@ impl Url { parser::Input::trim_tab_and_newlines(input, vfn), ) }); + } else { + self.query_start = None; + self.strip_trailing_spaces_from_opaque_path(); } self.restore_already_parsed_fragment(fragment); @@ -1989,7 +2090,8 @@ impl Url { if !self.has_host() || self.host() == Some(Host::Domain("")) || self.scheme() == "file" { return Err(()); } - if let Some(password) = password { + let password = password.unwrap_or_default(); + if !password.is_empty() { let host_and_after = self.slice(self.host_start..).to_owned(); self.serialization.truncate(self.username_end as usize); self.serialization.push(':'); @@ -2805,7 +2907,7 @@ fn file_url_segments_to_pathbuf( // A windows drive letter must end with a slash. if bytes.len() > 2 - && matches!(bytes[bytes.len() - 2], b'a'..=b'z' | b'A'..=b'Z') + && bytes[bytes.len() - 2].is_ascii_alphabetic() && matches!(bytes[bytes.len() - 1], b':' | b'|') { bytes.push(b'/'); diff --git a/vendor/url/src/parser.rs b/vendor/url/src/parser.rs index f5438c505..765cc027c 100644 --- a/vendor/url/src/parser.rs +++ b/vendor/url/src/parser.rs @@ -157,9 +157,11 @@ impl SchemeType { pub fn is_file(&self) -> bool { matches!(*self, SchemeType::File) } +} - pub fn from(s: &str) -> Self { - match s { +impl<T: AsRef<str>> From<T> for SchemeType { + fn from(s: T) -> Self { + match s.as_ref() { "http" | "https" | "ws" | "wss" | "ftp" => SchemeType::SpecialNotFile, "file" => SchemeType::File, _ => SchemeType::NotSpecial, @@ -176,7 +178,7 @@ pub fn default_port(scheme: &str) -> Option<u16> { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Input<'i> { chars: str::Chars<'i>, } @@ -474,9 +476,8 @@ impl<'a> Parser<'a> { let host = HostInternal::None; let port = None; let remaining = if let Some(input) = input.split_prefix('/') { - let path_start = self.serialization.len(); self.serialization.push('/'); - self.parse_path(scheme_type, &mut false, path_start, input) + self.parse_path(scheme_type, &mut false, path_start as usize, input) } else { self.parse_cannot_be_a_base_path(input) }; @@ -1156,7 +1157,7 @@ impl<'a> Parser<'a> { return input; } - if maybe_c != None && maybe_c != Some('/') { + if maybe_c.is_some() && maybe_c != Some('/') { self.serialization.push('/'); } // Otherwise, if c is not the EOF code point: @@ -1172,7 +1173,7 @@ impl<'a> Parser<'a> { ) -> Input<'i> { // Relative path state loop { - let segment_start = self.serialization.len(); + let mut segment_start = self.serialization.len(); let mut ends_with_slash = false; loop { let input_before_c = input.clone(); @@ -1201,6 +1202,14 @@ impl<'a> Parser<'a> { } _ => { self.check_url_code_point(c, &input); + if scheme_type.is_file() + && is_normalized_windows_drive_letter( + &self.serialization[path_start + 1..], + ) + { + self.serialization.push('/'); + segment_start += 1; + } if self.context == Context::PathSegmentSetter { if scheme_type.is_special() { self.serialization @@ -1248,7 +1257,10 @@ impl<'a> Parser<'a> { } _ => { // If url’s scheme is "file", url’s path is empty, and buffer is a Windows drive letter, then - if scheme_type.is_file() && is_windows_drive_letter(segment_before_slash) { + if scheme_type.is_file() + && segment_start == path_start + 1 + && is_windows_drive_letter(segment_before_slash) + { // Replace the second code point in buffer with U+003A (:). if let Some(c) = segment_before_slash.chars().next() { self.serialization.truncate(segment_start); @@ -1354,9 +1366,50 @@ impl<'a> Parser<'a> { host_end: u32, host: HostInternal, port: Option<u16>, - path_start: u32, + mut path_start: u32, remaining: Input<'_>, ) -> ParseResult<Url> { + // Special case for anarchist URL's with a leading empty path segment + // This prevents web+demo:/.//not-a-host/ or web+demo:/path/..//not-a-host/, + // when parsed and then serialized, from ending up as web+demo://not-a-host/ + // (they end up as web+demo:/.//not-a-host/). + // + // If url’s host is null, url does not have an opaque path, + // url’s path’s size is greater than 1, and url’s path[0] is the empty string, + // then append U+002F (/) followed by U+002E (.) to output. + let scheme_end_as_usize = scheme_end as usize; + let path_start_as_usize = path_start as usize; + if path_start_as_usize == scheme_end_as_usize + 1 { + // Anarchist URL + if self.serialization[path_start_as_usize..].starts_with("//") { + // Case 1: The base URL did not have an empty path segment, but the resulting one does + // Insert the "/." prefix + self.serialization.insert_str(path_start_as_usize, "/."); + path_start += 2; + } + assert!(!self.serialization[scheme_end_as_usize..].starts_with("://")); + } else if path_start_as_usize == scheme_end_as_usize + 3 + && &self.serialization[scheme_end_as_usize..path_start_as_usize] == ":/." + { + // Anarchist URL with leading empty path segment + // The base URL has a "/." between the host and the path + assert_eq!(self.serialization.as_bytes()[path_start_as_usize], b'/'); + if self + .serialization + .as_bytes() + .get(path_start_as_usize + 1) + .copied() + != Some(b'/') + { + // Case 2: The base URL had an empty path segment, but the resulting one does not + // Remove the "/." prefix + self.serialization + .replace_range(scheme_end_as_usize..path_start_as_usize, ":"); + path_start -= 2; + } + assert!(!self.serialization[scheme_end_as_usize..].starts_with("://")); + } + let (query_start, fragment_start) = self.parse_query_and_fragment(scheme_type, scheme_end, remaining)?; Ok(Url { @@ -1477,7 +1530,7 @@ impl<'a> Parser<'a> { if c == '%' { let mut input = input.clone(); if !matches!((input.next(), input.next()), (Some(a), Some(b)) - if is_ascii_hex_digit(a) && is_ascii_hex_digit(b)) + if a.is_ascii_hexdigit() && b.is_ascii_hexdigit()) { vfn(SyntaxViolation::PercentDecode) } @@ -1488,11 +1541,6 @@ impl<'a> Parser<'a> { } } -#[inline] -fn is_ascii_hex_digit(c: char) -> bool { - matches!(c, 'a'..='f' | 'A'..='F' | '0'..='9') -} - // Non URL code points: // U+0000 to U+0020 (space) // " # % < > [ \ ] ^ ` { | } @@ -1534,7 +1582,7 @@ fn ascii_tab_or_new_line(ch: char) -> bool { /// https://url.spec.whatwg.org/#ascii-alpha #[inline] pub fn ascii_alpha(ch: char) -> bool { - matches!(ch, 'a'..='z' | 'A'..='Z') + ch.is_ascii_alphabetic() } #[inline] diff --git a/vendor/url/src/slicing.rs b/vendor/url/src/slicing.rs index a90337bb6..c061fee84 100644 --- a/vendor/url/src/slicing.rs +++ b/vendor/url/src/slicing.rs @@ -37,6 +37,29 @@ impl Index<Range<Position>> for Url { } } +// Counts how many base-10 digits are required to represent n in the given base +fn count_digits(n: u16) -> usize { + match n { + 0..=9 => 1, + 10..=99 => 2, + 100..=999 => 3, + 1000..=9999 => 4, + 10000..=65535 => 5, + } +} + +#[test] +fn test_count_digits() { + assert_eq!(count_digits(0), 1); + assert_eq!(count_digits(1), 1); + assert_eq!(count_digits(9), 1); + assert_eq!(count_digits(10), 2); + assert_eq!(count_digits(99), 2); + assert_eq!(count_digits(100), 3); + assert_eq!(count_digits(9999), 4); + assert_eq!(count_digits(65535), 5); +} + /// Indicates a position within a URL based on its components. /// /// A range of positions can be used for slicing `Url`: @@ -149,7 +172,14 @@ impl Url { } } - Position::AfterPort => self.path_start as usize, + Position::AfterPort => { + if let Some(port) = self.port { + debug_assert!(self.byte_at(self.host_end) == b':'); + self.host_end as usize + ":".len() + count_digits(port) + } else { + self.host_end as usize + } + } Position::BeforePath => self.path_start as usize, diff --git a/vendor/url/tests/data.rs b/vendor/url/tests/data.rs index f4001ca2b..dc4660b9b 100644 --- a/vendor/url/tests/data.rs +++ b/vendor/url/tests/data.rs @@ -15,29 +15,25 @@ use url::{quirks, Url}; #[test] fn urltestdata() { - let idna_skip_inputs = [ - "http://www.foo。bar.com", - "http://Go.com", - "http://你好你好", - "https://faß.ExAmPlE/", - "http://0Xc0.0250.01", - "ftp://%e2%98%83", - "https://%e2%98%83", - "file://a\u{ad}b/p", - "file://a%C2%ADb/p", - "http://GOO\u{200b}\u{2060}\u{feff}goo.com", - ]; - // Copied from https://github.com/web-platform-tests/wpt/blob/master/url/ let mut json = Value::from_str(include_str!("urltestdata.json")) .expect("JSON parse error in urltestdata.json"); let mut passed = true; + let mut skip_next = false; for entry in json.as_array_mut().unwrap() { if entry.is_string() { + if entry.as_str().unwrap() == "skip next" { + skip_next = true; + } continue; // ignore comments } + if skip_next { + skip_next = false; + continue; + } + let maybe_base = entry .take_key("base") .expect("missing base key") @@ -45,12 +41,6 @@ fn urltestdata() { let input = entry.take_string("input"); let failure = entry.take_key("failure").is_some(); - { - if idna_skip_inputs.contains(&input.as_str()) { - continue; - } - } - let res = if let Some(base) = maybe_base { let base = match Url::parse(&base) { Ok(base) => base, @@ -228,7 +218,7 @@ fn get<'a>(url: &'a Url, attr: &str) -> &'a str { } #[allow(clippy::unit_arg)] -fn set<'a>(url: &'a mut Url, attr: &str, new: &str) { +fn set(url: &mut Url, attr: &str, new: &str) { let _ = match attr { "protocol" => quirks::set_protocol(url, new), "username" => quirks::set_username(url, new), diff --git a/vendor/url/tests/unit.rs b/vendor/url/tests/unit.rs index 55ff59ada..6cb0f37fe 100644 --- a/vendor/url/tests/unit.rs +++ b/vendor/url/tests/unit.rs @@ -35,6 +35,17 @@ fn test_relative_empty() { } #[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(); base.set_username("").unwrap(); @@ -54,6 +65,30 @@ fn test_set_empty_host() { } #[test] +fn test_set_empty_username_and_password() { + let mut base: Url = "moz://foo:bar@servo/baz".parse().unwrap(); + base.set_username("").unwrap(); + assert_eq!(base.as_str(), "moz://:bar@servo/baz"); + + base.set_password(Some("")).unwrap(); + assert_eq!(base.as_str(), "moz://servo/baz"); + + base.set_password(None).unwrap(); + assert_eq!(base.as_str(), "moz://servo/baz"); +} + +#[test] +fn test_set_empty_password() { + let mut base: Url = "moz://foo:bar@servo/baz".parse().unwrap(); + + base.set_password(Some("")).unwrap(); + assert_eq!(base.as_str(), "moz://foo@servo/baz"); + + base.set_password(None).unwrap(); + assert_eq!(base.as_str(), "moz://foo@servo/baz"); +} + +#[test] fn test_set_empty_hostname() { use url::quirks; let mut base: Url = "moz://foo@servo/baz".parse().unwrap(); @@ -71,6 +106,17 @@ fn test_set_empty_hostname() { assert_eq!(base.as_str(), "moz:///baz"); } +#[test] +fn test_set_empty_query() { + let mut base: Url = "moz://example.com/path?query".parse().unwrap(); + + base.set_query(Some("")); + assert_eq!(base.as_str(), "moz://example.com/path?"); + + base.set_query(None); + assert_eq!(base.as_str(), "moz://example.com/path"); +} + macro_rules! assert_from_file_path { ($path: expr) => { assert_from_file_path!($path, $path) @@ -944,6 +990,16 @@ fn no_panic() { } #[test] +fn test_null_host_with_leading_empty_path_segment() { + // since Note in item 3 of URL serializing in the URL Standard + // https://url.spec.whatwg.org/#url-serializing + let url = Url::parse("m:/.//\\").unwrap(); + let encoded = url.as_str(); + let reparsed = Url::parse(encoded).unwrap(); + assert_eq!(reparsed, url); +} + +#[test] fn pop_if_empty_in_bounds() { let mut url = Url::parse("m://").unwrap(); let mut segments = url.path_segments_mut().unwrap(); @@ -1162,3 +1218,83 @@ fn test_make_relative() { assert_eq!(make_relative, None, "base: {}, uri: {}", base, uri); } } + +#[test] +fn test_has_authority() { + let url = Url::parse("mailto:joe@example.com").unwrap(); + assert!(!url.has_authority()); + let url = Url::parse("unix:/run/foo.socket").unwrap(); + assert!(!url.has_authority()); + let url = Url::parse("file:///tmp/foo").unwrap(); + assert!(url.has_authority()); + let url = Url::parse("http://example.com/tmp/foo").unwrap(); + assert!(url.has_authority()); +} + +#[test] +fn test_authority() { + let url = Url::parse("mailto:joe@example.com").unwrap(); + assert_eq!(url.authority(), ""); + let url = Url::parse("unix:/run/foo.socket").unwrap(); + assert_eq!(url.authority(), ""); + let url = Url::parse("file:///tmp/foo").unwrap(); + assert_eq!(url.authority(), ""); + let url = Url::parse("http://example.com/tmp/foo").unwrap(); + assert_eq!(url.authority(), "example.com"); + let url = Url::parse("ftp://127.0.0.1:21/").unwrap(); + assert_eq!(url.authority(), "127.0.0.1"); + let url = Url::parse("ftp://user@127.0.0.1:2121/").unwrap(); + assert_eq!(url.authority(), "user@127.0.0.1:2121"); + let url = Url::parse("https://:@example.com/").unwrap(); + assert_eq!(url.authority(), "example.com"); + let url = Url::parse("https://:password@[::1]:8080/").unwrap(); + assert_eq!(url.authority(), ":password@[::1]:8080"); + let url = Url::parse("gopher://user:@àlex.example.com:70").unwrap(); + assert_eq!(url.authority(), "user@%C3%A0lex.example.com:70"); + let url = Url::parse("irc://àlex:àlex@àlex.рф.example.com:6667/foo").unwrap(); + assert_eq!( + url.authority(), + "%C3%A0lex:%C3%A0lex@%C3%A0lex.%D1%80%D1%84.example.com:6667" + ); + let url = Url::parse("https://àlex:àlex@àlex.рф.example.com:443/foo").unwrap(); + assert_eq!( + url.authority(), + "%C3%A0lex:%C3%A0lex@xn--lex-8ka.xn--p1ai.example.com" + ); +} + +#[test] +/// https://github.com/servo/rust-url/issues/838 +fn test_file_with_drive() { + let s1 = "fIlE:p:?../"; + let url = url::Url::parse(s1).unwrap(); + assert_eq!(url.to_string(), "file:///p:?../"); + assert_eq!(url.path(), "/p:"); + + let testcases = [ + ("a", "file:///p:/a"), + ("", "file:///p:?../"), + ("?x", "file:///p:?x"), + (".", "file:///p:/"), + ("..", "file:///p:/"), + ("../", "file:///p:/"), + ]; + + for case in &testcases { + let url2 = url::Url::join(&url, case.0).unwrap(); + assert_eq!(url2.to_string(), case.1); + } +} + +#[test] +/// Similar to test_file_with_drive, but with a path +/// that could be confused for a drive. +fn test_file_with_drive_and_path() { + let s1 = "fIlE:p:/x|?../"; + let url = url::Url::parse(s1).unwrap(); + assert_eq!(url.to_string(), "file:///p:/x|?../"); + assert_eq!(url.path(), "/p:/x|"); + let s2 = "a"; + let url2 = url::Url::join(&url, s2).unwrap(); + assert_eq!(url2.to_string(), "file:///p:/a"); +} diff --git a/vendor/url/tests/urltestdata.json b/vendor/url/tests/urltestdata.json index 4265f5928..53d036886 100644 --- a/vendor/url/tests/urltestdata.json +++ b/vendor/url/tests/urltestdata.json @@ -1,6 +1,5 @@ [ "# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/script-tests/segments.js", - "# AS OF https://github.com/web-platform-tests/wpt/commit/2a64dae4641fbd61bd4257df460e188f425b492e", { "input": "http://example\t.\norg", "base": "http://example.org/foo/bar", @@ -966,6 +965,11 @@ "hash": "" }, { + "input": "http://[::127.0.0.1.]", + "base": "http://example.org/foo/bar", + "failure": true + }, + { "input": "http://[0:0:0:0:0:0:13.1.68.3]", "base": "http://example.org/foo/bar", "href": "http://[::d01:4403]/", @@ -3918,6 +3922,22 @@ "search": "", "hash": "" }, + "Non-special domains with empty labels", + { + "input": "h://.", + "base": "about:blank", + "href": "h://.", + "origin": "null", + "protocol": "h:", + "username": "", + "password": "", + "host": ".", + "hostname": ".", + "port": "", + "pathname": "", + "search": "", + "hash": "" + }, "Broken IPv6", { "input": "http://[www.google.com]/", @@ -3945,10 +3965,30 @@ "failure": true }, { + "input": "http://[::.1.2]", + "base": "http://other.com/", + "failure": true + }, + { "input": "http://[::1.]", "base": "http://other.com/", "failure": true }, + { + "input": "http://[::.1]", + "base": "http://other.com/", + "failure": true + }, + { + "input": "http://[::%31]", + "base": "http://other.com/", + "failure": true + }, + { + "input": "http://%5B::1]", + "base": "http://other.com/", + "failure": true + }, "Misc Unicode", { "input": "http://foo:💩@example.com/bar", @@ -5895,6 +5935,21 @@ "search": "", "hash": "" }, + "skip next", + { + "input": "/", + "base": "file://h/C:/a/b", + "href": "file://h/C:/", + "protocol": "file:", + "username": "", + "password": "", + "host": "h", + "hostname": "h", + "port": "", + "pathname": "/C:/", + "search": "", + "hash": "" + }, { "input": "/", "base": "file://h/a/b", @@ -6050,6 +6105,96 @@ "hash": "#x" }, "# File URLs and many (back)slashes", + "skip next", + { + "input": "file:\\\\//", + "base": "about:blank", + "href": "file:////", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//", + "search": "", + "hash": "" + }, + "skip next", + { + "input": "file:\\\\\\\\", + "base": "about:blank", + "href": "file:////", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//", + "search": "", + "hash": "" + }, + "skip next", + { + "input": "file:\\\\\\\\?fox", + "base": "about:blank", + "href": "file:////?fox", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//", + "search": "?fox", + "hash": "" + }, + "skip next", + { + "input": "file:\\\\\\\\#guppy", + "base": "about:blank", + "href": "file:////#guppy", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//", + "search": "", + "hash": "#guppy" + }, + "skip next", + { + "input": "file://spider///", + "base": "about:blank", + "href": "file://spider///", + "protocol": "file:", + "username": "", + "password": "", + "host": "spider", + "hostname": "spider", + "port": "", + "pathname": "///", + "search": "", + "hash": "" + }, + "skip next", + { + "input": "file:\\\\localhost//", + "base": "about:blank", + "href": "file:////", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//", + "search": "", + "hash": "" + }, { "input": "file:///localhost//cat", "base": "about:blank", @@ -6064,6 +6209,51 @@ "search": "", "hash": "" }, + "skip next", + { + "input": "file://\\/localhost//cat", + "base": "about:blank", + "href": "file:////localhost//cat", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//localhost//cat", + "search": "", + "hash": "" + }, + "skip next", + { + "input": "file://localhost//a//../..//", + "base": "about:blank", + "href": "file://///", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "///", + "search": "", + "hash": "" + }, + "skip next", + { + "input": "/////mouse", + "base": "file:///elephant", + "href": "file://///mouse", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "///mouse", + "search": "", + "hash": "" + }, { "input": "\\//pig", "base": "file://lion/", @@ -6078,6 +6268,51 @@ "search": "", "hash": "" }, + "skip next", + { + "input": "\\/localhost//pig", + "base": "file://lion/", + "href": "file:////pig", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//pig", + "search": "", + "hash": "" + }, + "skip next", + { + "input": "//localhost//pig", + "base": "file://lion/", + "href": "file:////pig", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//pig", + "search": "", + "hash": "" + }, + "skip next", + { + "input": "/..//localhost//pig", + "base": "file://lion/", + "href": "file://lion//localhost//pig", + "protocol": "file:", + "username": "", + "password": "", + "host": "lion", + "hostname": "lion", + "port": "", + "pathname": "//localhost//pig", + "search": "", + "hash": "" + }, { "input": "file://", "base": "file://ape/", @@ -6122,6 +6357,111 @@ "hash": "" }, "# Windows drive letter handling with the 'file:' base URL", + "skip next", + { + "input": "C|", + "base": "file://host/dir/file", + "href": "file://host/C:", + "protocol": "file:", + "username": "", + "password": "", + "host": "host", + "hostname": "host", + "port": "", + "pathname": "/C:", + "search": "", + "hash": "" + }, + "skip next", + { + "input": "C|", + "base": "file://host/D:/dir1/dir2/file", + "href": "file://host/C:", + "protocol": "file:", + "username": "", + "password": "", + "host": "host", + "hostname": "host", + "port": "", + "pathname": "/C:", + "search": "", + "hash": "" + }, + "skip next", + { + "input": "C|#", + "base": "file://host/dir/file", + "href": "file://host/C:#", + "protocol": "file:", + "username": "", + "password": "", + "host": "host", + "hostname": "host", + "port": "", + "pathname": "/C:", + "search": "", + "hash": "" + }, + "skip next", + { + "input": "C|?", + "base": "file://host/dir/file", + "href": "file://host/C:?", + "protocol": "file:", + "username": "", + "password": "", + "host": "host", + "hostname": "host", + "port": "", + "pathname": "/C:", + "search": "", + "hash": "" + }, + "skip next", + { + "input": "C|/", + "base": "file://host/dir/file", + "href": "file://host/C:/", + "protocol": "file:", + "username": "", + "password": "", + "host": "host", + "hostname": "host", + "port": "", + "pathname": "/C:/", + "search": "", + "hash": "" + }, + "skip next", + { + "input": "C|\n/", + "base": "file://host/dir/file", + "href": "file://host/C:/", + "protocol": "file:", + "username": "", + "password": "", + "host": "host", + "hostname": "host", + "port": "", + "pathname": "/C:/", + "search": "", + "hash": "" + }, + "skip next", + { + "input": "C|\\", + "base": "file://host/dir/file", + "href": "file://host/C:/", + "protocol": "file:", + "username": "", + "password": "", + "host": "host", + "hostname": "host", + "port": "", + "pathname": "/C:/", + "search": "", + "hash": "" + }, { "input": "C", "base": "file://host/dir/file", @@ -6193,7 +6533,130 @@ "search": "", "hash": "" }, + "skip next", + { + "input": "/c:/foo/bar", + "base": "file://host/path", + "href": "file://host/c:/foo/bar", + "protocol": "file:", + "username": "", + "password": "", + "host": "host", + "hostname": "host", + "port": "", + "pathname": "/c:/foo/bar", + "search": "", + "hash": "" + }, + "# Do not drop the host in the presence of a drive letter", + "skip next", + { + "input": "file://example.net/C:/", + "base": "about:blank", + "href": "file://example.net/C:/", + "protocol": "file:", + "username": "", + "password": "", + "host": "example.net", + "hostname": "example.net", + "port": "", + "pathname": "/C:/", + "search": "", + "hash": "" + }, + "skip next", + { + "input": "file://1.2.3.4/C:/", + "base": "about:blank", + "href": "file://1.2.3.4/C:/", + "protocol": "file:", + "username": "", + "password": "", + "host": "1.2.3.4", + "hostname": "1.2.3.4", + "port": "", + "pathname": "/C:/", + "search": "", + "hash": "" + }, + "skip next", + { + "input": "file://[1::8]/C:/", + "base": "about:blank", + "href": "file://[1::8]/C:/", + "protocol": "file:", + "username": "", + "password": "", + "host": "[1::8]", + "hostname": "[1::8]", + "port": "", + "pathname": "/C:/", + "search": "", + "hash": "" + }, + "# Copy the host from the base URL in the following cases", + "skip next", + { + "input": "C|/", + "base": "file://host/", + "href": "file://host/C:/", + "protocol": "file:", + "username": "", + "password": "", + "host": "host", + "hostname": "host", + "port": "", + "pathname": "/C:/", + "search": "", + "hash": "" + }, + "skip next", + { + "input": "/C:/", + "base": "file://host/", + "href": "file://host/C:/", + "protocol": "file:", + "username": "", + "password": "", + "host": "host", + "hostname": "host", + "port": "", + "pathname": "/C:/", + "search": "", + "hash": "" + }, + "skip next", + { + "input": "file:C:/", + "base": "file://host/", + "href": "file://host/C:/", + "protocol": "file:", + "username": "", + "password": "", + "host": "host", + "hostname": "host", + "port": "", + "pathname": "/C:/", + "search": "", + "hash": "" + }, + "skip next", + { + "input": "file:/C:/", + "base": "file://host/", + "href": "file://host/C:/", + "protocol": "file:", + "username": "", + "password": "", + "host": "host", + "hostname": "host", + "port": "", + "pathname": "/C:/", + "search": "", + "hash": "" + }, "# Copy the empty host from the input in the following cases", + "skip next", { "input": "//C:/", "base": "file://host/", @@ -6449,6 +6912,51 @@ "inputCanBeRelative": true }, "# Additional file URL tests for (https://github.com/whatwg/url/issues/405)", + "skip next", + { + "input": "file://localhost//a//../..//foo", + "base": "about:blank", + "href": "file://///foo", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "///foo", + "search": "", + "hash": "" + }, + "skip next", + { + "input": "file://localhost////foo", + "base": "about:blank", + "href": "file://////foo", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "////foo", + "search": "", + "hash": "" + }, + "skip next", + { + "input": "file:////foo", + "base": "about:blank", + "href": "file:////foo", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//foo", + "search": "", + "hash": "" + }, { "input": "file:///one/two", "base": "file:///", @@ -6463,6 +6971,21 @@ "search": "", "hash": "" }, + "skip next", + { + "input": "file:////one/two", + "base": "file:///", + "href": "file:////one/two", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//one/two", + "search": "", + "hash": "" + }, { "input": "//one/two", "base": "file:///", @@ -6491,6 +7014,67 @@ "search": "", "hash": "" }, + "skip next", + { + "input": "////one/two", + "base": "file:///", + "href": "file:////one/two", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//one/two", + "search": "", + "hash": "" + }, + "skip next", + { + "input": "file:///.//", + "base": "file:////", + "href": "file:////", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//", + "search": "", + "hash": "" + }, + "File URL tests for https://github.com/whatwg/url/issues/549", + "skip next", + { + "input": "file:.//p", + "base": "about:blank", + "href": "file:////p", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//p", + "search": "", + "hash": "" + }, + "skip next", + { + "input": "file:/.//p", + "base": "about:blank", + "href": "file:////p", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//p", + "search": "", + "hash": "" + }, "# IPv6 tests", { "input": "http://[1:0::]", @@ -6902,6 +7486,191 @@ "search": "", "hash": "" }, + "Serialize /. in path", + { + "input": "non-spec:/.//", + "base": "about:blank", + "href": "non-spec:/.//", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//", + "search": "", + "hash": "" + }, + { + "input": "non-spec:/..//", + "base": "about:blank", + "href": "non-spec:/.//", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//", + "search": "", + "hash": "" + }, + { + "input": "non-spec:/a/..//", + "base": "about:blank", + "href": "non-spec:/.//", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//", + "search": "", + "hash": "" + }, + { + "input": "non-spec:/.//path", + "base": "about:blank", + "href": "non-spec:/.//path", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//path", + "search": "", + "hash": "" + }, + { + "input": "non-spec:/..//path", + "base": "about:blank", + "href": "non-spec:/.//path", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//path", + "search": "", + "hash": "" + }, + { + "input": "non-spec:/a/..//path", + "base": "about:blank", + "href": "non-spec:/.//path", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//path", + "search": "", + "hash": "" + }, + "skip next", + { + "input": "/.//path", + "base": "non-spec:/p", + "href": "non-spec:/.//path", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//path", + "search": "", + "hash": "" + }, + { + "input": "/..//path", + "base": "non-spec:/p", + "href": "non-spec:/.//path", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//path", + "search": "", + "hash": "" + }, + { + "input": "..//path", + "base": "non-spec:/p", + "href": "non-spec:/.//path", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//path", + "search": "", + "hash": "" + }, + { + "input": "a/..//path", + "base": "non-spec:/p", + "href": "non-spec:/.//path", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//path", + "search": "", + "hash": "" + }, + { + "input": "", + "base": "non-spec:/..//p", + "href": "non-spec:/.//p", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//p", + "search": "", + "hash": "" + }, + { + "input": "path", + "base": "non-spec:/..//p", + "href": "non-spec:/.//path", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "//path", + "search": "", + "hash": "" + }, + "Do not serialize /. in path", + { + "input": "../path", + "base": "non-spec:/.//p", + "href": "non-spec:/path", + "protocol": "non-spec:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "/path", + "search": "", + "hash": "" + }, "# percent encoded hosts in non-special-URLs", { "input": "non-special://%E2%80%A0/", @@ -7419,6 +8188,21 @@ "search": "", "hash": "" }, + "IDNA hostnames which get mapped to 'localhost'", + { + "input": "file://loC𝐀𝐋𝐇𝐨𝐬𝐭/usr/bin", + "base": "about:blank", + "href": "file:///usr/bin", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "/usr/bin", + "search": "", + "hash": "" + }, "Empty host after the domain to ASCII", { "input": "file://\u00ad/p", @@ -7904,5 +8688,232 @@ "input": "http://💩.123/", "base": "about:blank", "failure": true + }, + "U+0000 and U+FFFF in various places", + { + "input": "https://\u0000y", + "base": "about:blank", + "failure": true + }, + { + "input": "https://x/\u0000y", + "base": "about:blank", + "hash": "", + "host": "x", + "hostname": "x", + "href": "https://x/%00y", + "password": "", + "pathname": "/%00y", + "port": "", + "protocol": "https:", + "search": "", + "username": "" + }, + { + "input": "https://x/?\u0000y", + "base": "about:blank", + "hash": "", + "host": "x", + "hostname": "x", + "href": "https://x/?%00y", + "password": "", + "pathname": "/", + "port": "", + "protocol": "https:", + "search": "?%00y", + "username": "" + }, + { + "input": "https://x/?#\u0000y", + "base": "about:blank", + "hash": "#%00y", + "host": "x", + "hostname": "x", + "href": "https://x/?#%00y", + "password": "", + "pathname": "/", + "port": "", + "protocol": "https:", + "search": "", + "username": "" + }, + { + "input": "https://\uFFFFy", + "base": "about:blank", + "failure": true + }, + { + "input": "https://x/\uFFFFy", + "base": "about:blank", + "hash": "", + "host": "x", + "hostname": "x", + "href": "https://x/%EF%BF%BFy", + "password": "", + "pathname": "/%EF%BF%BFy", + "port": "", + "protocol": "https:", + "search": "", + "username": "" + }, + { + "input": "https://x/?\uFFFFy", + "base": "about:blank", + "hash": "", + "host": "x", + "hostname": "x", + "href": "https://x/?%EF%BF%BFy", + "password": "", + "pathname": "/", + "port": "", + "protocol": "https:", + "search": "?%EF%BF%BFy", + "username": "" + }, + { + "input": "https://x/?#\uFFFFy", + "base": "about:blank", + "hash": "#%EF%BF%BFy", + "host": "x", + "hostname": "x", + "href": "https://x/?#%EF%BF%BFy", + "password": "", + "pathname": "/", + "port": "", + "protocol": "https:", + "search": "", + "username": "" + }, + { + "input": "non-special:\u0000y", + "base": "about:blank", + "hash": "", + "host": "", + "hostname": "", + "href": "non-special:%00y", + "password": "", + "pathname": "%00y", + "port": "", + "protocol": "non-special:", + "search": "", + "username": "" + }, + { + "input": "non-special:x/\u0000y", + "base": "about:blank", + "hash": "", + "host": "", + "hostname": "", + "href": "non-special:x/%00y", + "password": "", + "pathname": "x/%00y", + "port": "", + "protocol": "non-special:", + "search": "", + "username": "" + }, + { + "input": "non-special:x/?\u0000y", + "base": "about:blank", + "hash": "", + "host": "", + "hostname": "", + "href": "non-special:x/?%00y", + "password": "", + "pathname": "x/", + "port": "", + "protocol": "non-special:", + "search": "?%00y", + "username": "" + }, + { + "input": "non-special:x/?#\u0000y", + "base": "about:blank", + "hash": "#%00y", + "host": "", + "hostname": "", + "href": "non-special:x/?#%00y", + "password": "", + "pathname": "x/", + "port": "", + "protocol": "non-special:", + "search": "", + "username": "" + }, + { + "input": "non-special:\uFFFFy", + "base": "about:blank", + "hash": "", + "host": "", + "hostname": "", + "href": "non-special:%EF%BF%BFy", + "password": "", + "pathname": "%EF%BF%BFy", + "port": "", + "protocol": "non-special:", + "search": "", + "username": "" + }, + { + "input": "non-special:x/\uFFFFy", + "base": "about:blank", + "hash": "", + "host": "", + "hostname": "", + "href": "non-special:x/%EF%BF%BFy", + "password": "", + "pathname": "x/%EF%BF%BFy", + "port": "", + "protocol": "non-special:", + "search": "", + "username": "" + }, + { + "input": "non-special:x/?\uFFFFy", + "base": "about:blank", + "hash": "", + "host": "", + "hostname": "", + "href": "non-special:x/?%EF%BF%BFy", + "password": "", + "pathname": "x/", + "port": "", + "protocol": "non-special:", + "search": "?%EF%BF%BFy", + "username": "" + }, + { + "input": "non-special:x/?#\uFFFFy", + "base": "about:blank", + "hash": "#%EF%BF%BFy", + "host": "", + "hostname": "", + "href": "non-special:x/?#%EF%BF%BFy", + "password": "", + "pathname": "x/", + "port": "", + "protocol": "non-special:", + "search": "", + "username": "" + }, + { + "input": "", + "base": "about:blank", + "failure": true + }, + { + "input": "https://example.com/\"quoted\"", + "base": "about:blank", + "hash": "", + "host": "example.com", + "hostname": "example.com", + "href": "https://example.com/%22quoted%22", + "origin": "https://example.com", + "password": "", + "pathname": "/%22quoted%22", + "port": "", + "protocol": "https:", + "search": "", + "username": "" } ] |