diff --git a/components/script/dom/location.rs b/components/script/dom/location.rs index aac4cc30b97b..e57ecf6cd39b 100644 --- a/components/script/dom/location.rs +++ b/components/script/dom/location.rs @@ -9,6 +9,7 @@ use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::USVString; +use crate::dom::document::Document; use crate::dom::globalscope::GlobalScope; use crate::dom::urlhelper::UrlHelper; use crate::dom::window::Window; @@ -62,17 +63,10 @@ impl Location { self.window.get_url() } - fn set_url_component(&self, value: USVString, setter: fn(&mut ServoUrl, USVString)) { - let mut url = self.window.get_url(); - let referrer = Referrer::ReferrerUrl(url.clone()); - setter(&mut url, value); - self.navigate(url, referrer, HistoryEntryReplacement::Disabled, false); - } - fn check_same_origin_domain(&self) -> ErrorResult { - let entry_document = GlobalScope::entry().as_window().Document(); let this_document = self.window.Document(); - if entry_document + if self + .entry_document() .origin() .same_origin_domain(this_document.origin()) { @@ -82,6 +76,10 @@ impl Location { } } + fn entry_document(&self) -> DomRoot { + GlobalScope::entry().as_window().Document() + } + // https://html.spec.whatwg.org/multipage/#dom-location-reload pub fn reload_without_origin_check(&self) { let url = self.get_url(); @@ -98,17 +96,24 @@ impl Location { impl LocationMethods for Location { // https://html.spec.whatwg.org/multipage/#dom-location-assign fn Assign(&self, url: USVString) -> ErrorResult { - self.check_same_origin_domain()?; - // TODO: per spec, we should use the _API base URL_ specified by the - // _entry settings object_. - let base_url = self.window.get_url(); - if let Ok(url) = base_url.join(&url.0) { - let referrer = Referrer::ReferrerUrl(base_url.clone()); + // Step 1: If this Location object's relevant Document is null, then return. + if self.window.has_document() { + // Step 2: If this Location object's relevant Document's origin is not same + // origin-domain with the entry settings object's origin, then throw a + // "SecurityError" DOMException. + self.check_same_origin_domain()?; + // Step 3: Parse url relative to the entry settings object. If that failed, + // throw a "SyntaxError" DOMException. + let base_url = self.entry_document().url(); + let url = match base_url.join(&url.0) { + Ok(url) => url, + Err(_) => return Err(Error::Syntax), + }; + // Step 4: Location-object navigate to the resulting URL record. + let referrer = Referrer::ReferrerUrl(self.get_url()); self.navigate(url, referrer, HistoryEntryReplacement::Disabled, false); - Ok(()) - } else { - Err(Error::Syntax) } + Ok(()) } // https://html.spec.whatwg.org/multipage/#dom-location-reload @@ -122,17 +127,21 @@ impl LocationMethods for Location { // https://html.spec.whatwg.org/multipage/#dom-location-replace fn Replace(&self, url: USVString) -> ErrorResult { - // Note: no call to self.check_same_origin_domain() - // TODO: per spec, we should use the _API base URL_ specified by the - // _entry settings object_. - let base_url = self.window.get_url(); - if let Ok(url) = base_url.join(&url.0) { - let referrer = Referrer::ReferrerUrl(base_url.clone()); + // Step 1: If this Location object's relevant Document is null, then return. + if self.window.has_document() { + // Step 2: Parse url relative to the entry settings object. If that failed, + // throw a "SyntaxError" DOMException. + let base_url = self.entry_document().url(); + let url = match base_url.join(&url.0) { + Ok(url) => url, + Err(_) => return Err(Error::Syntax), + }; + // Step 3: Location-object navigate to the resulting URL record with + // the replacement flag set. + let referrer = Referrer::ReferrerUrl(self.get_url()); self.navigate(url, referrer, HistoryEntryReplacement::Enabled, false); - Ok(()) - } else { - Err(Error::Syntax) } + Ok(()) } // https://html.spec.whatwg.org/multipage/#dom-location-hash @@ -142,12 +151,28 @@ impl LocationMethods for Location { } // https://html.spec.whatwg.org/multipage/#dom-location-hash - fn SetHash(&self, mut value: USVString) -> ErrorResult { - if value.0.is_empty() { - value = USVString("#".to_owned()); + fn SetHash(&self, value: USVString) -> ErrorResult { + // Step 1: If this Location object's relevant Document is null, then return. + if self.window.has_document() { + // Step 2: If this Location object's relevant Document's origin is not + // same origin-domain with the entry settings object's origin, then + // throw a "SecurityError" DOMException. + self.check_same_origin_domain()?; + // Step 3: Let copyURL be a copy of this Location object's url. + let mut copy_url = self.get_url(); + // Step 4: Let input be the given value with a single leading "#" removed, if any. + // Step 5: Set copyURL's fragment to the empty string. + // Step 6: Basic URL parse input, with copyURL as url and fragment state as + // state override. + copy_url.as_mut_url().set_fragment(match value.0.as_str() { + "" => Some("#"), + _ if value.0.starts_with('#') => Some(&value.0[1..]), + _ => Some(&value.0), + }); + // Step 7: Location-object-setter navigate to copyURL. + let referrer = Referrer::ReferrerUrl(self.get_url()); + self.navigate(copy_url, referrer, HistoryEntryReplacement::Disabled, false); } - self.check_same_origin_domain()?; - self.set_url_component(value, UrlHelper::SetHash); Ok(()) } @@ -159,8 +184,24 @@ impl LocationMethods for Location { // https://html.spec.whatwg.org/multipage/#dom-location-host fn SetHost(&self, value: USVString) -> ErrorResult { - self.check_same_origin_domain()?; - self.set_url_component(value, UrlHelper::SetHost); + // Step 1: If this Location object's relevant Document is null, then return. + if self.window.has_document() { + // Step 2: If this Location object's relevant Document's origin is not + // same origin-domain with the entry settings object's origin, then + // throw a "SecurityError" DOMException. + self.check_same_origin_domain()?; + // Step 3: Let copyURL be a copy of this Location object's url. + let mut copy_url = self.get_url(); + // Step 4: If copyURL's cannot-be-a-base-URL flag is set, terminate these steps. + if !copy_url.cannot_be_a_base() { + // Step 5: Basic URL parse the given value, with copyURL as url and host state + // as state override. + let _ = copy_url.as_mut_url().set_host(Some(&value.0)); + // Step 6: Location-object-setter navigate to copyURL. + let referrer = Referrer::ReferrerUrl(self.get_url()); + self.navigate(copy_url, referrer, HistoryEntryReplacement::Disabled, false); + } + } Ok(()) } @@ -178,8 +219,24 @@ impl LocationMethods for Location { // https://html.spec.whatwg.org/multipage/#dom-location-hostname fn SetHostname(&self, value: USVString) -> ErrorResult { - self.check_same_origin_domain()?; - self.set_url_component(value, UrlHelper::SetHostname); + // Step 1: If this Location object's relevant Document is null, then return. + if self.window.has_document() { + // Step 2: If this Location object's relevant Document's origin is not + // same origin-domain with the entry settings object's origin, then + // throw a "SecurityError" DOMException. + self.check_same_origin_domain()?; + // Step 3: Let copyURL be a copy of this Location object's url. + let mut copy_url = self.get_url(); + // Step 4: If copyURL's cannot-be-a-base-URL flag is set, terminate these steps. + if !copy_url.cannot_be_a_base() { + // Step 5: Basic URL parse the given value, with copyURL as url and hostname + // state as state override. + let _ = copy_url.as_mut_url().set_host(Some(&value.0)); + // Step 6: Location-object-setter navigate to copyURL. + let referrer = Referrer::ReferrerUrl(self.get_url()); + self.navigate(copy_url, referrer, HistoryEntryReplacement::Disabled, false); + } + } Ok(()) } @@ -191,14 +248,20 @@ impl LocationMethods for Location { // https://html.spec.whatwg.org/multipage/#dom-location-href fn SetHref(&self, value: USVString) -> ErrorResult { - // Note: no call to self.check_same_origin_domain() - let current_url = self.window.get_url(); - let url = match current_url.join(&value.0) { - Ok(url) => url, - Err(e) => return Err(Error::Type(format!("Couldn't parse URL: {}", e))), - }; - let referrer = Referrer::ReferrerUrl(current_url.clone()); - self.navigate(url, referrer, HistoryEntryReplacement::Disabled, false); + // Step 1: If this Location object's relevant Document is null, then return. + if self.window.has_document() { + // Note: no call to self.check_same_origin_domain() + // Step 2: Parse the given value relative to the entry settings object. + // If that failed, throw a TypeError exception. + let base_url = self.entry_document().url(); + let url = match base_url.join(&value.0) { + Ok(url) => url, + Err(e) => return Err(Error::Type(format!("Couldn't parse URL: {}", e))), + }; + // Step 3: Location-object-setter navigate to the resulting URL record. + let referrer = Referrer::ReferrerUrl(self.get_url()); + self.navigate(url, referrer, HistoryEntryReplacement::Disabled, false); + } Ok(()) } @@ -210,8 +273,25 @@ impl LocationMethods for Location { // https://html.spec.whatwg.org/multipage/#dom-location-pathname fn SetPathname(&self, value: USVString) -> ErrorResult { - self.check_same_origin_domain()?; - self.set_url_component(value, UrlHelper::SetPathname); + // Step 1: If this Location object's relevant Document is null, then return. + if self.window.has_document() { + // Step 2: If this Location object's relevant Document's origin is not + // same origin-domain with the entry settings object's origin, then + // throw a "SecurityError" DOMException. + self.check_same_origin_domain()?; + // Step 3: Let copyURL be a copy of this Location object's url. + let mut copy_url = self.get_url(); + // Step 4: If copyURL's cannot-be-a-base-URL flag is set, terminate these steps. + if !copy_url.cannot_be_a_base() { + // Step 5: Set copyURL's path to the empty list. + // Step 6: Basic URL parse the given value, with copyURL as url and path + // start state as state override. + copy_url.as_mut_url().set_path(&value.0); + // Step 7: Location-object-setter navigate to copyURL. + let referrer = Referrer::ReferrerUrl(self.get_url()); + self.navigate(copy_url, referrer, HistoryEntryReplacement::Disabled, false); + } + } Ok(()) } @@ -223,8 +303,30 @@ impl LocationMethods for Location { // https://html.spec.whatwg.org/multipage/#dom-location-port fn SetPort(&self, value: USVString) -> ErrorResult { - self.check_same_origin_domain()?; - self.set_url_component(value, UrlHelper::SetPort); + // Step 1: If this Location object's relevant Document is null, then return. + if self.window.has_document() { + // Step 2: If this Location object's relevant Document's origin is not + // same origin-domain with the entry settings object's origin, then + // throw a "SecurityError" DOMException. + self.check_same_origin_domain()?; + // Step 3: Let copyURL be a copy of this Location object's url. + let mut copy_url = self.get_url(); + // Step 4: If copyURL cannot have a username/password/port, then return. + // https://url.spec.whatwg.org/#cannot-have-a-username-password-port + if copy_url.host().is_some() && + !copy_url.cannot_be_a_base() && + copy_url.scheme() != "file" + { + // Step 5: If the given value is the empty string, then set copyURL's + // port to null. + // Step 6: Otherwise, basic URL parse the given value, with copyURL as url + // and port state as state override. + let _ = url::quirks::set_port(copy_url.as_mut_url(), &value.0); + // Step 7: Location-object-setter navigate to copyURL. + let referrer = Referrer::ReferrerUrl(self.get_url()); + self.navigate(copy_url, referrer, HistoryEntryReplacement::Disabled, false); + } + } Ok(()) } @@ -236,8 +338,34 @@ impl LocationMethods for Location { // https://html.spec.whatwg.org/multipage/#dom-location-protocol fn SetProtocol(&self, value: USVString) -> ErrorResult { - self.check_same_origin_domain()?; - self.set_url_component(value, UrlHelper::SetProtocol); + // Step 1: If this Location object's relevant Document is null, then return. + if self.window.has_document() { + // Step 2: If this Location object's relevant Document's origin is not + // same origin-domain with the entry settings object's origin, then + // throw a "SecurityError" DOMException. + self.check_same_origin_domain()?; + // Step 3: Let copyURL be a copy of this Location object's url. + let mut copy_url = self.get_url(); + // Step 4: Let possibleFailure be the result of basic URL parsing the given + // value, followed by ":", with copyURL as url and scheme start state as + // state override. + let scheme = match value.0.find(':') { + Some(position) => &value.0[..position], + None => &value.0, + }; + if let Err(_) = copy_url.as_mut_url().set_scheme(scheme) { + // Step 5: If possibleFailure is failure, then throw a "SyntaxError" DOMException. + return Err(Error::Syntax); + } + // Step 6: If copyURL's scheme is not an HTTP(S) scheme, then terminate these steps. + if copy_url.scheme().eq_ignore_ascii_case("http") || + copy_url.scheme().eq_ignore_ascii_case("https") + { + // Step 7: Location-object-setter navigate to copyURL. + let referrer = Referrer::ReferrerUrl(self.get_url()); + self.navigate(copy_url, referrer, HistoryEntryReplacement::Disabled, false); + } + } Ok(()) } @@ -249,8 +377,30 @@ impl LocationMethods for Location { // https://html.spec.whatwg.org/multipage/#dom-location-search fn SetSearch(&self, value: USVString) -> ErrorResult { - self.check_same_origin_domain()?; - self.set_url_component(value, UrlHelper::SetSearch); + // Step 1: If this Location object's relevant Document is null, then return. + if self.window.has_document() { + // Step 2: If this Location object's relevant Document's origin is not + // same origin-domain with the entry settings object's origin, then + // throw a "SecurityError" DOMException. + self.check_same_origin_domain()?; + // Step 3: Let copyURL be a copy of this Location object's url. + let mut copy_url = self.get_url(); + // Step 4: If the given value is the empty string, set copyURL's query to null. + // Step 5: Otherwise, run these substeps: + // 1. Let input be the given value with a single leading "?" removed, if any. + // 2. Set copyURL's query to the empty string. + // 3. Basic URL parse input, with copyURL as url and query state as state + // override, and the relevant Document's document's character encoding as + // encoding override. + copy_url.as_mut_url().set_query(match value.0.as_str() { + "" => None, + _ if value.0.starts_with('?') => Some(&value.0[1..]), + _ => Some(&value.0), + }); + // Step 6: Location-object-setter navigate to copyURL. + let referrer = Referrer::ReferrerUrl(self.get_url()); + self.navigate(copy_url, referrer, HistoryEntryReplacement::Disabled, false); + } Ok(()) } } diff --git a/tests/wpt/metadata/fullscreen/api/document-exit-fullscreen-active-document.html.ini b/tests/wpt/metadata/fullscreen/api/document-exit-fullscreen-active-document.html.ini deleted file mode 100644 index 160a13ef6bca..000000000000 --- a/tests/wpt/metadata/fullscreen/api/document-exit-fullscreen-active-document.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[document-exit-fullscreen-active-document.html] - type: testharness - [Document#exitFullscreen() when the document is not the active document] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/browsing-the-web/navigating-across-documents/005.html.ini b/tests/wpt/metadata/html/browsers/browsing-the-web/navigating-across-documents/005.html.ini index 20ee90039207..7c2cc7c76522 100644 --- a/tests/wpt/metadata/html/browsers/browsing-the-web/navigating-across-documents/005.html.ini +++ b/tests/wpt/metadata/html/browsers/browsing-the-web/navigating-across-documents/005.html.ini @@ -1,3 +1,4 @@ [005.html] - type: testharness - expected: ERROR + [Link with onclick navigation and href navigation ] + expected: FAIL + diff --git a/tests/wpt/metadata/html/browsers/browsing-the-web/unloading-documents/beforeunload-synchronous.html.ini b/tests/wpt/metadata/html/browsers/browsing-the-web/unloading-documents/beforeunload-synchronous.html.ini deleted file mode 100644 index efa045bd18ee..000000000000 --- a/tests/wpt/metadata/html/browsers/browsing-the-web/unloading-documents/beforeunload-synchronous.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[beforeunload-synchronous.html] - [beforeunload event is emitted synchronously] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/history/the-location-interface/location-protocol-setter-non-broken-weird.html.ini b/tests/wpt/metadata/html/browsers/history/the-location-interface/location-protocol-setter-non-broken-weird.html.ini deleted file mode 100644 index a30c51097ab4..000000000000 --- a/tests/wpt/metadata/html/browsers/history/the-location-interface/location-protocol-setter-non-broken-weird.html.ini +++ /dev/null @@ -1,16 +0,0 @@ -[location-protocol-setter-non-broken-weird.html] - [Set location.protocol to data] - expected: FAIL - - [Set location.protocol to ftp] - expected: FAIL - - [Set location.protocol to gopher] - expected: FAIL - - [Set location.protocol to x] - expected: FAIL - - [Set location.protocol to http+x] - expected: FAIL - diff --git a/tests/wpt/metadata/html/browsers/history/the-location-interface/location-protocol-setter-non-broken.html.ini b/tests/wpt/metadata/html/browsers/history/the-location-interface/location-protocol-setter-non-broken.html.ini index f8222ab132b4..b95a9339bc09 100644 --- a/tests/wpt/metadata/html/browsers/history/the-location-interface/location-protocol-setter-non-broken.html.ini +++ b/tests/wpt/metadata/html/browsers/history/the-location-interface/location-protocol-setter-non-broken.html.ini @@ -1,29 +1,5 @@ [location-protocol-setter-non-broken.html] type: testharness - [Set data URL frame location.protocol to gopher] - expected: FAIL - - [Set HTTP URL frame location.protocol to gopher] - expected: FAIL - - [Set HTTP URL frame location.protocol to data] - expected: FAIL - - [Set HTTP URL frame location.protocol to x] - expected: FAIL - - [Set HTTP URL frame location.protocol to http+x] - expected: FAIL - - [Set HTTP URL frame location.protocol to ftp] - expected: FAIL - - [Set data URL frame location.protocol to x] - expected: FAIL - - [Set data URL frame location.protocol to data] - expected: FAIL - [Set data URL frame location.protocol to ftp] expected: FAIL diff --git a/tests/wpt/metadata/html/browsers/history/the-location-interface/location-protocol-setter.html.ini b/tests/wpt/metadata/html/browsers/history/the-location-interface/location-protocol-setter.html.ini index bc813d0c1f3d..327544a548f2 100644 --- a/tests/wpt/metadata/html/browsers/history/the-location-interface/location-protocol-setter.html.ini +++ b/tests/wpt/metadata/html/browsers/history/the-location-interface/location-protocol-setter.html.ini @@ -1,35 +1,5 @@ [location-protocol-setter.html] type: testharness - [%00 (percent-encoded) is not a scheme] - expected: FAIL - - [%01 (percent-encoded) is not a scheme] - expected: FAIL - - [%0A (percent-encoded) is not a scheme] - expected: FAIL - - [%20 (percent-encoded) is not a scheme] - expected: FAIL - - [! (percent-encoded) is not a scheme] - expected: FAIL - - [%7F (percent-encoded) is not a scheme] - expected: FAIL - - [%C2%80 (percent-encoded) is not a scheme] - expected: FAIL - - [%C3%BF (percent-encoded) is not a scheme] - expected: FAIL - - [: (percent-encoded) is not a scheme] - expected: FAIL - - [%E2%80%A0 (percent-encoded) is not a scheme] - expected: FAIL - [%00x (percent-encoded) is not a scheme] expected: FAIL @@ -42,21 +12,6 @@ [%20x (percent-encoded) is not a scheme] expected: FAIL - [!x (percent-encoded) is not a scheme] - expected: FAIL - - [%7Fx (percent-encoded) is not a scheme] - expected: FAIL - - [%C2%80x (percent-encoded) is not a scheme] - expected: FAIL - - [%C3%BFx (percent-encoded) is not a scheme] - expected: FAIL - - [:x (percent-encoded) is not a scheme] - expected: FAIL - [%EF%BF%BD%EF%BF%BD%EF%BF%BDx (percent-encoded) is not a scheme] expected: FAIL @@ -72,24 +27,6 @@ [%20X (percent-encoded) is not a scheme] expected: FAIL - [!X (percent-encoded) is not a scheme] - expected: FAIL - - [%7FX (percent-encoded) is not a scheme] - expected: FAIL - - [%C2%80X (percent-encoded) is not a scheme] - expected: FAIL - - [%C3%BFX (percent-encoded) is not a scheme] - expected: FAIL - - [:X (percent-encoded) is not a scheme] - expected: FAIL - - [%E2%80%A0X (percent-encoded) is not a scheme] - expected: FAIL - [x%00 (percent-encoded) is not a scheme] expected: FAIL @@ -102,21 +39,6 @@ [x%20 (percent-encoded) is not a scheme] expected: FAIL - [x! (percent-encoded) is not a scheme] - expected: FAIL - - [x%7F (percent-encoded) is not a scheme] - expected: FAIL - - [x%C2%80 (percent-encoded) is not a scheme] - expected: FAIL - - [x%C3%BF (percent-encoded) is not a scheme] - expected: FAIL - - [x%E2%80%A0 (percent-encoded) is not a scheme] - expected: FAIL - [X%00 (percent-encoded) is not a scheme] expected: FAIL @@ -129,21 +51,6 @@ [X%20 (percent-encoded) is not a scheme] expected: FAIL - [X! (percent-encoded) is not a scheme] - expected: FAIL - - [X%7F (percent-encoded) is not a scheme] - expected: FAIL - - [X%C2%80 (percent-encoded) is not a scheme] - expected: FAIL - - [X%C3%BF (percent-encoded) is not a scheme] - expected: FAIL - - [X%E2%80%A0 (percent-encoded) is not a scheme] - expected: FAIL - [a%0A (percent-encoded) is not a scheme] expected: FAIL @@ -153,6 +60,3 @@ [Equivalent tests for data URL and srcdoc