summaryrefslogtreecommitdiffstats
path: root/vendor/url
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/url')
-rw-r--r--vendor/url/.cargo-checksum.json2
-rw-r--r--vendor/url/Cargo.toml16
-rw-r--r--vendor/url/src/host.rs10
-rw-r--r--vendor/url/src/lib.rs114
-rw-r--r--vendor/url/src/parser.rs80
-rw-r--r--vendor/url/src/slicing.rs32
-rw-r--r--vendor/url/tests/data.rs30
-rw-r--r--vendor/url/tests/unit.rs136
-rw-r--r--vendor/url/tests/urltestdata.json1013
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": ""
}
]