Make Location operate on the correct objects

This PR updates the implementation of the "Location-object navigate"
algorithm to use the correct browsing context as the source browsing
context for navigation. This affects the determination of a referrer and
referrer policy and the treatment of javascript: URLs.

This PR also fixes the derivation of a Location object's relevant
Document to match the specified behavior.
This commit is contained in:
yvt 2021-08-21 00:26:05 +09:00 committed by Martin Robinson
parent 918557ad6d
commit b0a99c8c67
11 changed files with 270 additions and 223 deletions

View file

@ -5,10 +5,10 @@
use crate::dom::bindings::codegen::Bindings::LocationBinding::LocationMethods; use crate::dom::bindings::codegen::Bindings::LocationBinding::LocationMethods;
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods; use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::USVString; use crate::dom::bindings::str::USVString;
use crate::dom::document::Document;
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
use crate::dom::urlhelper::UrlHelper; use crate::dom::urlhelper::UrlHelper;
use crate::dom::window::Window; use crate::dom::window::Window;
@ -17,6 +17,25 @@ use net_traits::request::Referrer;
use script_traits::{HistoryEntryReplacement, LoadData, LoadOrigin}; use script_traits::{HistoryEntryReplacement, LoadData, LoadOrigin};
use servo_url::{MutableOrigin, ServoUrl}; use servo_url::{MutableOrigin, ServoUrl};
#[derive(PartialEq)]
enum NavigationType {
/// The "[`Location`-object navigate][1]" steps.
///
/// [1]: https://html.spec.whatwg.org/multipage/#location-object-navigate
Normal,
/// The last step of [`reload()`][1] (`reload_triggered == true`)
///
/// [1]: https://html.spec.whatwg.org/multipage/#dom-location-reload
ReloadByScript,
/// User-requested navigation (the unlabeled paragraph after
/// [`reload()`][1]).
///
/// [1]: https://html.spec.whatwg.org/multipage/#dom-location-reload
ReloadByConstellation,
}
#[dom_struct] #[dom_struct]
pub struct Location { pub struct Location {
reflector_: Reflector, reflector_: Reflector,
@ -35,56 +54,188 @@ impl Location {
reflect_dom_object(Box::new(Location::new_inherited(window)), window) reflect_dom_object(Box::new(Location::new_inherited(window)), window)
} }
/// https://html.spec.whatwg.org/multipage/#location-object-navigate /// Navigate the relevant `Document`'s browsing context.
fn navigate( fn navigate(
&self, &self,
url: ServoUrl, url: ServoUrl,
referrer: Referrer,
replacement_flag: HistoryEntryReplacement, replacement_flag: HistoryEntryReplacement,
reload_triggered: bool, navigation_type: NavigationType,
) { ) {
let document = self.window.Document(); fn incumbent_window() -> DomRoot<Window> {
let referrer_policy = document.get_referrer_policy(); let incumbent_global = GlobalScope::incumbent().expect("no incumbent global object");
let pipeline_id = self.window.upcast::<GlobalScope>().pipeline_id(); DomRoot::downcast(incumbent_global).expect("global object is not a Window")
}
// The active document of the source browsing context used for
// navigation determines the request's referrer and referrer policy.
let source_window = match navigation_type {
NavigationType::ReloadByScript | NavigationType::ReloadByConstellation => {
// > Navigate the browsing context [...] the source browsing context
// > set to the browsing context being navigated.
DomRoot::from_ref(&*self.window)
},
NavigationType::Normal => {
// > 2. Let `sourceBrowsingContext` be the incumbent global object's
// > browsing context.
incumbent_window()
},
};
let source_document = source_window.Document();
let referrer = Referrer::ReferrerUrl(source_document.url());
let referrer_policy = source_document.get_referrer_policy();
// <https://html.spec.whatwg.org/multipage/#navigate>
// > Let `incumbentNavigationOrigin` be the origin of the incumbent
// > settings object, or if no script was involved, the origin of the
// > node document of the element that initiated the navigation.
let navigation_origin_window = match navigation_type {
NavigationType::Normal | NavigationType::ReloadByScript => incumbent_window(),
NavigationType::ReloadByConstellation => DomRoot::from_ref(&*self.window),
};
let (load_origin, creator_pipeline_id) = (
navigation_origin_window.origin().immutable().clone(),
Some(navigation_origin_window.pipeline_id()),
);
// Is `historyHandling` `reload`?
let reload_triggered = match navigation_type {
NavigationType::ReloadByScript | NavigationType::ReloadByConstellation => true,
NavigationType::Normal => false,
};
// Initiate navigation
// TODO: rethrow exceptions, set exceptions enabled flag.
let load_data = LoadData::new( let load_data = LoadData::new(
LoadOrigin::Script(document.origin().immutable().clone()), LoadOrigin::Script(load_origin),
url, url,
Some(pipeline_id), creator_pipeline_id,
referrer, referrer,
referrer_policy, referrer_policy,
None, // Top navigation doesn't inherit secure context None, // Top navigation doesn't inherit secure context
); );
// TODO: rethrow exceptions, set exceptions enabled flag.
self.window self.window
.load_url(replacement_flag, reload_triggered, load_data); .load_url(replacement_flag, reload_triggered, load_data);
} }
fn get_url(&self) -> ServoUrl { /// Get if this `Location`'s [relevant `Document`][1] is non-null.
self.window.get_url() ///
/// [1]: https://html.spec.whatwg.org/multipage/#relevant-document
fn has_document(&self) -> bool {
// <https://html.spec.whatwg.org/multipage/#relevant-document>
//
// > A `Location` object has an associated relevant `Document`, which is
// > this `Location` object's relevant global object's browsing
// > context's active document, if this `Location` object's relevant
// > global object's browsing context is non-null, and null otherwise.
self.window.Document().browsing_context().is_some()
} }
fn check_same_origin_domain(&self) -> ErrorResult { /// Get this `Location` object's [relevant `Document`][1], or
let this_document = self.window.Document(); /// `Err(Error::Security)` if it's non-null and its origin is not same
if self /// origin-domain with the entry setting object's origin.
.entry_settings_object() ///
/// In the specification's terms:
///
/// 1. If this `Location` object's relevant `Document` is null, then return
/// null.
///
/// 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`.
///
/// 3. Return this `Location` object's relevant `Document`.
///
/// [1]: https://html.spec.whatwg.org/multipage/#relevant-document
fn document_if_same_origin(&self) -> Fallible<Option<DomRoot<Document>>> {
// <https://html.spec.whatwg.org/multipage/#relevant-document>
//
// > A `Location` object has an associated relevant `Document`, which is
// > this `Location` object's relevant global object's browsing
// > context's active document, if this `Location` object's relevant
// > global object's browsing context is non-null, and null otherwise.
if let Some(window_proxy) = self.window.Document().browsing_context() {
// `Location`'s many other operations:
//
// > If this `Location` object's relevant `Document` is non-null and
// > its origin is not same origin-domain with the entry settings
// > object's origin, then throw a "SecurityError" `DOMException`.
//
// FIXME: We should still return the active document if it's same
// origin but not fully active. `WindowProxy::document`
// currently returns `None` in this case.
if let Some(document) = window_proxy.document().filter(|document| {
self.entry_settings_object()
.origin() .origin()
.same_origin_domain(this_document.origin()) .same_origin_domain(document.origin())
{ }) {
Ok(()) Ok(Some(document))
} else { } else {
Err(Error::Security) Err(Error::Security)
} }
} else {
// The browsing context is null
Ok(None)
}
}
/// Get this `Location` object's [relevant url][1] or
/// `Err(Error::Security)` if the [relevant `Document`][2] if it's non-null
/// and its origin is not same origin-domain with the entry setting object's
/// origin.
///
/// [1]: https://html.spec.whatwg.org/multipage/#concept-location-url
/// [2]: https://html.spec.whatwg.org/multipage/#relevant-document
fn get_url_if_same_origin(&self) -> Fallible<ServoUrl> {
Ok(if let Some(document) = self.document_if_same_origin()? {
document.url()
} else {
ServoUrl::parse("about:blank").unwrap()
})
} }
fn entry_settings_object(&self) -> DomRoot<GlobalScope> { fn entry_settings_object(&self) -> DomRoot<GlobalScope> {
GlobalScope::entry() GlobalScope::entry()
} }
// https://html.spec.whatwg.org/multipage/#dom-location-reload /// The common algorithm for `Location`'s setters and `Location::Assign`.
#[inline]
fn setter_common(&self, f: impl FnOnce(ServoUrl) -> Fallible<Option<ServoUrl>>) -> ErrorResult {
// Step 1: If this Location object's relevant Document is null, then return.
// 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.
if let Some(document) = self.document_if_same_origin()? {
// Step 3: Let copyURL be a copy of this Location object's url.
// Step 4: Assign the result of running f(copyURL) to copyURL.
if let Some(copy_url) = f(document.url())? {
// Step 5: Terminate these steps if copyURL is null.
// Step 6: Location-object navigate to copyURL.
self.navigate(
copy_url,
HistoryEntryReplacement::Disabled,
NavigationType::Normal,
);
}
}
Ok(())
}
/// Perform a user-requested reload (the unlabeled paragraph after
/// [`reload()`][1]).
///
/// [1]: https://html.spec.whatwg.org/multipage/#dom-location-reload
pub fn reload_without_origin_check(&self) { pub fn reload_without_origin_check(&self) {
let url = self.get_url(); // > When a user requests that the active document of a browsing context
let referrer = Referrer::ReferrerUrl(url.clone()); // > be reloaded through a user interface element, the user agent should
self.navigate(url, referrer, HistoryEntryReplacement::Enabled, true); // > navigate the browsing context to the same resource as that
// > `Document`, with `historyHandling` set to "reload".
let url = self.window.get_url();
self.navigate(
url,
HistoryEntryReplacement::Enabled,
NavigationType::ReloadByConstellation,
);
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -96,12 +247,7 @@ impl Location {
impl LocationMethods for Location { impl LocationMethods for Location {
// https://html.spec.whatwg.org/multipage/#dom-location-assign // https://html.spec.whatwg.org/multipage/#dom-location-assign
fn Assign(&self, url: USVString) -> ErrorResult { fn Assign(&self, url: USVString) -> ErrorResult {
// Step 1: If this Location object's relevant Document is null, then return. self.setter_common(|_copy_url| {
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, // Step 3: Parse url relative to the entry settings object. If that failed,
// throw a "SyntaxError" DOMException. // throw a "SyntaxError" DOMException.
let base_url = self.entry_settings_object().api_base_url(); let base_url = self.entry_settings_object().api_base_url();
@ -109,26 +255,26 @@ impl LocationMethods for Location {
Ok(url) => url, Ok(url) => url,
Err(_) => return Err(Error::Syntax), Err(_) => return Err(Error::Syntax),
}; };
// Step 4: Location-object navigate to the resulting URL record.
let referrer = Referrer::ReferrerUrl(self.get_url()); Ok(Some(url))
self.navigate(url, referrer, HistoryEntryReplacement::Disabled, false); })
}
Ok(())
} }
// https://html.spec.whatwg.org/multipage/#dom-location-reload // https://html.spec.whatwg.org/multipage/#dom-location-reload
fn Reload(&self) -> ErrorResult { fn Reload(&self) -> ErrorResult {
self.check_same_origin_domain()?; let url = self.get_url_if_same_origin()?;
let url = self.get_url(); self.navigate(
let referrer = Referrer::ReferrerUrl(url.clone()); url,
self.navigate(url, referrer, HistoryEntryReplacement::Enabled, true); HistoryEntryReplacement::Enabled,
NavigationType::ReloadByScript,
);
Ok(()) Ok(())
} }
// https://html.spec.whatwg.org/multipage/#dom-location-replace // https://html.spec.whatwg.org/multipage/#dom-location-replace
fn Replace(&self, url: USVString) -> ErrorResult { fn Replace(&self, url: USVString) -> ErrorResult {
// Step 1: If this Location object's relevant Document is null, then return. // Step 1: If this Location object's relevant Document is null, then return.
if self.window.has_document() { if self.has_document() {
// Step 2: Parse url relative to the entry settings object. If that failed, // Step 2: Parse url relative to the entry settings object. If that failed,
// throw a "SyntaxError" DOMException. // throw a "SyntaxError" DOMException.
let base_url = self.entry_settings_object().api_base_url(); let base_url = self.entry_settings_object().api_base_url();
@ -138,28 +284,23 @@ impl LocationMethods for Location {
}; };
// Step 3: Location-object navigate to the resulting URL record with // Step 3: Location-object navigate to the resulting URL record with
// the replacement flag set. // the replacement flag set.
let referrer = Referrer::ReferrerUrl(self.get_url()); self.navigate(
self.navigate(url, referrer, HistoryEntryReplacement::Enabled, false); url,
HistoryEntryReplacement::Enabled,
NavigationType::Normal,
);
} }
Ok(()) Ok(())
} }
// https://html.spec.whatwg.org/multipage/#dom-location-hash // https://html.spec.whatwg.org/multipage/#dom-location-hash
fn GetHash(&self) -> Fallible<USVString> { fn GetHash(&self) -> Fallible<USVString> {
self.check_same_origin_domain()?; Ok(UrlHelper::Hash(&self.get_url_if_same_origin()?))
Ok(UrlHelper::Hash(&self.get_url()))
} }
// https://html.spec.whatwg.org/multipage/#dom-location-hash // https://html.spec.whatwg.org/multipage/#dom-location-hash
fn SetHash(&self, value: USVString) -> ErrorResult { fn SetHash(&self, value: USVString) -> ErrorResult {
// Step 1: If this Location object's relevant Document is null, then return. self.setter_common(|mut copy_url| {
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 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 5: Set copyURL's fragment to the empty string.
// Step 6: Basic URL parse input, with copyURL as url and fragment state as // Step 6: Basic URL parse input, with copyURL as url and fragment state as
@ -169,87 +310,67 @@ impl LocationMethods for Location {
_ if value.0.starts_with('#') => Some(&value.0[1..]), _ if value.0.starts_with('#') => Some(&value.0[1..]),
_ => Some(&value.0), _ => Some(&value.0),
}); });
// Step 7: Location-object-setter navigate to copyURL.
let referrer = Referrer::ReferrerUrl(self.get_url()); Ok(Some(copy_url))
self.navigate(copy_url, referrer, HistoryEntryReplacement::Disabled, false); })
}
Ok(())
} }
// https://html.spec.whatwg.org/multipage/#dom-location-host // https://html.spec.whatwg.org/multipage/#dom-location-host
fn GetHost(&self) -> Fallible<USVString> { fn GetHost(&self) -> Fallible<USVString> {
self.check_same_origin_domain()?; Ok(UrlHelper::Host(&self.get_url_if_same_origin()?))
Ok(UrlHelper::Host(&self.get_url()))
} }
// https://html.spec.whatwg.org/multipage/#dom-location-host // https://html.spec.whatwg.org/multipage/#dom-location-host
fn SetHost(&self, value: USVString) -> ErrorResult { fn SetHost(&self, value: USVString) -> ErrorResult {
// Step 1: If this Location object's relevant Document is null, then return. self.setter_common(|mut copy_url| {
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. // Step 4: If copyURL's cannot-be-a-base-URL flag is set, terminate these steps.
if !copy_url.cannot_be_a_base() { if copy_url.cannot_be_a_base() {
return Ok(None);
}
// Step 5: Basic URL parse the given value, with copyURL as url and host state // Step 5: Basic URL parse the given value, with copyURL as url and host state
// as state override. // as state override.
let _ = copy_url.as_mut_url().set_host(Some(&value.0)); 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()); Ok(Some(copy_url))
self.navigate(copy_url, referrer, HistoryEntryReplacement::Disabled, false); })
}
}
Ok(())
} }
// https://html.spec.whatwg.org/multipage/#dom-location-origin // https://html.spec.whatwg.org/multipage/#dom-location-origin
fn GetOrigin(&self) -> Fallible<USVString> { fn GetOrigin(&self) -> Fallible<USVString> {
self.check_same_origin_domain()?; Ok(UrlHelper::Origin(&self.get_url_if_same_origin()?))
Ok(UrlHelper::Origin(&self.get_url()))
} }
// https://html.spec.whatwg.org/multipage/#dom-location-hostname // https://html.spec.whatwg.org/multipage/#dom-location-hostname
fn GetHostname(&self) -> Fallible<USVString> { fn GetHostname(&self) -> Fallible<USVString> {
self.check_same_origin_domain()?; Ok(UrlHelper::Hostname(&self.get_url_if_same_origin()?))
Ok(UrlHelper::Hostname(&self.get_url()))
} }
// https://html.spec.whatwg.org/multipage/#dom-location-hostname // https://html.spec.whatwg.org/multipage/#dom-location-hostname
fn SetHostname(&self, value: USVString) -> ErrorResult { fn SetHostname(&self, value: USVString) -> ErrorResult {
// Step 1: If this Location object's relevant Document is null, then return. self.setter_common(|mut copy_url| {
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. // Step 4: If copyURL's cannot-be-a-base-URL flag is set, terminate these steps.
if !copy_url.cannot_be_a_base() { if copy_url.cannot_be_a_base() {
return Ok(None);
}
// Step 5: Basic URL parse the given value, with copyURL as url and hostname // Step 5: Basic URL parse the given value, with copyURL as url and hostname
// state as state override. // state as state override.
let _ = copy_url.as_mut_url().set_host(Some(&value.0)); 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()); Ok(Some(copy_url))
self.navigate(copy_url, referrer, HistoryEntryReplacement::Disabled, false); })
}
}
Ok(())
} }
// https://html.spec.whatwg.org/multipage/#dom-location-href // https://html.spec.whatwg.org/multipage/#dom-location-href
fn GetHref(&self) -> Fallible<USVString> { fn GetHref(&self) -> Fallible<USVString> {
self.check_same_origin_domain()?; Ok(UrlHelper::Href(&self.get_url_if_same_origin()?))
Ok(UrlHelper::Href(&self.get_url()))
} }
// https://html.spec.whatwg.org/multipage/#dom-location-href // https://html.spec.whatwg.org/multipage/#dom-location-href
fn SetHref(&self, value: USVString) -> ErrorResult { fn SetHref(&self, value: USVString) -> ErrorResult {
// Step 1: If this Location object's relevant Document is null, then return. // Step 1: If this Location object's relevant Document is null, then return.
if self.window.has_document() { if self.has_document() {
// Note: no call to self.check_same_origin_domain() // Note: no call to self.check_same_origin_domain()
// Step 2: Parse the given value relative to the entry settings object. // Step 2: Parse the given value relative to the entry settings object.
// If that failed, throw a TypeError exception. // If that failed, throw a TypeError exception.
@ -258,94 +379,73 @@ impl LocationMethods for Location {
Ok(url) => url, Ok(url) => url,
Err(e) => return Err(Error::Type(format!("Couldn't parse URL: {}", e))), Err(e) => return Err(Error::Type(format!("Couldn't parse URL: {}", e))),
}; };
// Step 3: Location-object-setter navigate to the resulting URL record. // Step 3: Location-object navigate to the resulting URL record.
let referrer = Referrer::ReferrerUrl(self.get_url()); self.navigate(
self.navigate(url, referrer, HistoryEntryReplacement::Disabled, false); url,
HistoryEntryReplacement::Disabled,
NavigationType::Normal,
);
} }
Ok(()) Ok(())
} }
// https://html.spec.whatwg.org/multipage/#dom-location-pathname // https://html.spec.whatwg.org/multipage/#dom-location-pathname
fn GetPathname(&self) -> Fallible<USVString> { fn GetPathname(&self) -> Fallible<USVString> {
self.check_same_origin_domain()?; Ok(UrlHelper::Pathname(&self.get_url_if_same_origin()?))
Ok(UrlHelper::Pathname(&self.get_url()))
} }
// https://html.spec.whatwg.org/multipage/#dom-location-pathname // https://html.spec.whatwg.org/multipage/#dom-location-pathname
fn SetPathname(&self, value: USVString) -> ErrorResult { fn SetPathname(&self, value: USVString) -> ErrorResult {
// Step 1: If this Location object's relevant Document is null, then return. self.setter_common(|mut copy_url| {
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. // Step 4: If copyURL's cannot-be-a-base-URL flag is set, terminate these steps.
if !copy_url.cannot_be_a_base() { if copy_url.cannot_be_a_base() {
return Ok(None);
}
// Step 5: Set copyURL's path to the empty list. // Step 5: Set copyURL's path to the empty list.
// Step 6: Basic URL parse the given value, with copyURL as url and path // Step 6: Basic URL parse the given value, with copyURL as url and path
// start state as state override. // start state as state override.
copy_url.as_mut_url().set_path(&value.0); copy_url.as_mut_url().set_path(&value.0);
// Step 7: Location-object-setter navigate to copyURL.
let referrer = Referrer::ReferrerUrl(self.get_url()); Ok(Some(copy_url))
self.navigate(copy_url, referrer, HistoryEntryReplacement::Disabled, false); })
}
}
Ok(())
} }
// https://html.spec.whatwg.org/multipage/#dom-location-port // https://html.spec.whatwg.org/multipage/#dom-location-port
fn GetPort(&self) -> Fallible<USVString> { fn GetPort(&self) -> Fallible<USVString> {
self.check_same_origin_domain()?; Ok(UrlHelper::Port(&self.get_url_if_same_origin()?))
Ok(UrlHelper::Port(&self.get_url()))
} }
// https://html.spec.whatwg.org/multipage/#dom-location-port // https://html.spec.whatwg.org/multipage/#dom-location-port
fn SetPort(&self, value: USVString) -> ErrorResult { fn SetPort(&self, value: USVString) -> ErrorResult {
// Step 1: If this Location object's relevant Document is null, then return. self.setter_common(|mut copy_url| {
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. // Step 4: If copyURL cannot have a username/password/port, then return.
// https://url.spec.whatwg.org/#cannot-have-a-username-password-port // https://url.spec.whatwg.org/#cannot-have-a-username-password-port
if copy_url.host().is_some() && if copy_url.host().is_none() ||
!copy_url.cannot_be_a_base() && copy_url.cannot_be_a_base() ||
copy_url.scheme() != "file" copy_url.scheme() == "file"
{ {
return Ok(None);
}
// Step 5: If the given value is the empty string, then set copyURL's // Step 5: If the given value is the empty string, then set copyURL's
// port to null. // port to null.
// Step 6: Otherwise, basic URL parse the given value, with copyURL as url // Step 6: Otherwise, basic URL parse the given value, with copyURL as url
// and port state as state override. // and port state as state override.
let _ = url::quirks::set_port(copy_url.as_mut_url(), &value.0); 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()); Ok(Some(copy_url))
self.navigate(copy_url, referrer, HistoryEntryReplacement::Disabled, false); })
}
}
Ok(())
} }
// https://html.spec.whatwg.org/multipage/#dom-location-protocol // https://html.spec.whatwg.org/multipage/#dom-location-protocol
fn GetProtocol(&self) -> Fallible<USVString> { fn GetProtocol(&self) -> Fallible<USVString> {
self.check_same_origin_domain()?; Ok(UrlHelper::Protocol(&self.get_url_if_same_origin()?))
Ok(UrlHelper::Protocol(&self.get_url()))
} }
// https://html.spec.whatwg.org/multipage/#dom-location-protocol // https://html.spec.whatwg.org/multipage/#dom-location-protocol
fn SetProtocol(&self, value: USVString) -> ErrorResult { fn SetProtocol(&self, value: USVString) -> ErrorResult {
// Step 1: If this Location object's relevant Document is null, then return. self.setter_common(|mut copy_url| {
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 // 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 // value, followed by ":", with copyURL as url and scheme start state as
// state override. // state override.
@ -353,38 +453,31 @@ impl LocationMethods for Location {
Some(position) => &value.0[..position], Some(position) => &value.0[..position],
None => &value.0, None => &value.0,
}; };
if let Err(_) = copy_url.as_mut_url().set_scheme(scheme) { if let Err(_) = copy_url.as_mut_url().set_scheme(scheme) {
// Step 5: If possibleFailure is failure, then throw a "SyntaxError" DOMException. // Step 5: If possibleFailure is failure, then throw a "SyntaxError" DOMException.
return Err(Error::Syntax); return Err(Error::Syntax);
} }
// Step 6: If copyURL's scheme is not an HTTP(S) scheme, then terminate these steps. // 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") || if !copy_url.scheme().eq_ignore_ascii_case("http") &&
copy_url.scheme().eq_ignore_ascii_case("https") !copy_url.scheme().eq_ignore_ascii_case("https")
{ {
// Step 7: Location-object-setter navigate to copyURL. return Ok(None);
let referrer = Referrer::ReferrerUrl(self.get_url());
self.navigate(copy_url, referrer, HistoryEntryReplacement::Disabled, false);
} }
}
Ok(()) Ok(Some(copy_url))
})
} }
// https://html.spec.whatwg.org/multipage/#dom-location-search // https://html.spec.whatwg.org/multipage/#dom-location-search
fn GetSearch(&self) -> Fallible<USVString> { fn GetSearch(&self) -> Fallible<USVString> {
self.check_same_origin_domain()?; Ok(UrlHelper::Search(&self.get_url_if_same_origin()?))
Ok(UrlHelper::Search(&self.get_url()))
} }
// https://html.spec.whatwg.org/multipage/#dom-location-search // https://html.spec.whatwg.org/multipage/#dom-location-search
fn SetSearch(&self, value: USVString) -> ErrorResult { fn SetSearch(&self, value: USVString) -> ErrorResult {
// Step 1: If this Location object's relevant Document is null, then return. self.setter_common(|mut copy_url| {
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 4: If the given value is the empty string, set copyURL's query to null.
// Step 5: Otherwise, run these substeps: // Step 5: Otherwise, run these substeps:
// 1. Let input be the given value with a single leading "?" removed, if any. // 1. Let input be the given value with a single leading "?" removed, if any.
@ -397,10 +490,8 @@ impl LocationMethods for Location {
_ if value.0.starts_with('?') => Some(&value.0[1..]), _ if value.0.starts_with('?') => Some(&value.0[1..]),
_ => Some(&value.0), _ => Some(&value.0),
}); });
// Step 6: Location-object-setter navigate to copyURL.
let referrer = Referrer::ReferrerUrl(self.get_url()); Ok(Some(copy_url))
self.navigate(copy_url, referrer, HistoryEntryReplacement::Disabled, false); })
}
Ok(())
} }
} }

View file

@ -2,9 +2,6 @@
[cross-origin, setting src] [cross-origin, setting src]
expected: FAIL expected: FAIL
[cross-origin, setting location.href]
expected: FAIL
[cross-origin-domain but same-origin, setting src] [cross-origin-domain but same-origin, setting src]
expected: FAIL expected: FAIL

View file

@ -1,3 +0,0 @@
[context-for-location-assign.html]
[Fetch client and URL resolution for location.assign()]
expected: FAIL

View file

@ -1,3 +0,0 @@
[context-for-location-href.html]
[Fetch client and URL resolution for location.href setter]
expected: FAIL

View file

@ -1,3 +0,0 @@
[context-for-location.html]
[Fetch client and URL resolution for location setter]
expected: FAIL

View file

@ -2,9 +2,6 @@
[cross-origin, setting src] [cross-origin, setting src]
expected: FAIL expected: FAIL
[cross-origin, setting location.href]
expected: FAIL
[cross-origin-domain but same-origin, setting src] [cross-origin-domain but same-origin, setting src]
expected: FAIL expected: FAIL

View file

@ -1,3 +0,0 @@
[context-for-location-assign.html]
[Fetch client and URL resolution for location.assign()]
expected: FAIL

View file

@ -1,3 +0,0 @@
[context-for-location-href.html]
[Fetch client and URL resolution for location.href setter]
expected: FAIL

View file

@ -1,3 +0,0 @@
[context-for-location.html]
[Fetch client and URL resolution for location setter]
expected: FAIL

View file

@ -1,5 +0,0 @@
[navigate-child-function-parent.html]
type: testharness
[Set location from a parent]
expected: FAIL

View file

@ -1,18 +1,3 @@
[no-browsing-context.window.html] [no-browsing-context.window.html]
[Setting `protocol` to `http` of a `Location` object sans browsing context is a no-op]
expected: FAIL
[Setting `hash` to `test` of a `Location` object sans browsing context is a no-op]
expected: FAIL
[Invoking `replace` with `http://test:test/` on a `Location` object sans browsing context is a no-op]
expected: FAIL
[Getting `ancestorOrigins` of a `Location` object sans browsing context should be [\]] [Getting `ancestorOrigins` of a `Location` object sans browsing context should be [\]]
expected: FAIL expected: FAIL
[Invoking `assign` with `http://test:test/` on a `Location` object sans browsing context is a no-op]
expected: FAIL
[Setting `href` to `http://test:test/` of a `Location` object sans browsing context is a no-op]
expected: FAIL