script/dom/window/location.rs
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use dom_struct::dom_struct;
6use js::context::JSContext;
7use net_traits::request::Referrer;
8use script_bindings::dom::MutNullableDom;
9use script_bindings::reflector::{Reflector, reflect_dom_object};
10use servo_constellation_traits::{LoadData, LoadOrigin, NavigationHistoryBehavior};
11use servo_url::ServoUrl;
12
13use crate::dom::bindings::codegen::Bindings::LocationBinding::LocationMethods;
14use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
15use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
16use crate::dom::bindings::inheritance::Castable;
17use crate::dom::bindings::root::{Dom, DomRoot};
18use crate::dom::bindings::str::USVString;
19use crate::dom::dissimilaroriginwindow::DissimilarOriginWindow;
20use crate::dom::document::Document;
21use crate::dom::domstringlist::DOMStringList;
22use crate::dom::globalscope::GlobalScope;
23use crate::dom::urlhelper::UrlHelper;
24use crate::dom::window::Window;
25use crate::navigation::navigate;
26
27#[derive(PartialEq)]
28pub(crate) enum NavigationType {
29 /// The "[`Location`-object navigate][1]" steps.
30 ///
31 /// [1]: https://html.spec.whatwg.org/multipage/#location-object-navigate
32 Normal,
33
34 /// The last step of [`reload()`][1] (`reload_triggered == true`)
35 ///
36 /// [1]: https://html.spec.whatwg.org/multipage/#dom-location-reload
37 ReloadByScript,
38
39 /// User-requested navigation (the unlabeled paragraph after
40 /// [`reload()`][1]).
41 ///
42 /// [1]: https://html.spec.whatwg.org/multipage/#dom-location-reload
43 ReloadByConstellation,
44}
45
46#[derive(JSTraceable, MallocSizeOf)]
47#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
48enum LocationWindow {
49 SameOrigin(Dom<Window>),
50 DissimilarOrigin(Dom<DissimilarOriginWindow>),
51}
52
53impl LocationWindow {
54 fn same_origin_window(&self) -> Fallible<&Window> {
55 match self {
56 LocationWindow::SameOrigin(window) => Ok(window),
57 LocationWindow::DissimilarOrigin(..) => Err(Error::Security(Some(
58 "Tried to access same origin window on cross-origin object".into(),
59 ))),
60 }
61 }
62}
63
64#[dom_struct]
65pub(crate) struct Location {
66 reflector_: Reflector,
67 window: LocationWindow,
68 /// <https://html.spec.whatwg.org/multipage/#concept-location-empty-domstringlist>
69 empty_dom_string_list: MutNullableDom<DOMStringList>,
70}
71
72impl Location {
73 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
74 fn new_inherited(window: LocationWindow) -> Self {
75 Location {
76 reflector_: Reflector::new(),
77 window,
78 empty_dom_string_list: Default::default(),
79 }
80 }
81
82 pub(crate) fn new(cx: &mut JSContext, window: &Window) -> DomRoot<Self> {
83 reflect_dom_object(
84 cx,
85 Box::new(Location::new_inherited(LocationWindow::SameOrigin(
86 Dom::from_ref(window),
87 ))),
88 window,
89 )
90 }
91
92 pub(crate) fn new_dissimilar_origin(
93 cx: &mut JSContext,
94 window: &DissimilarOriginWindow,
95 ) -> DomRoot<Self> {
96 reflect_dom_object(
97 cx,
98 Box::new(Location::new_inherited(LocationWindow::DissimilarOrigin(
99 Dom::from_ref(window),
100 ))),
101 window,
102 )
103 }
104
105 /// <https://html.spec.whatwg.org/multipage/#location-object-navigate>
106 fn navigate_a_location(
107 &self,
108 cx: &mut JSContext,
109 navigable: &Window,
110 url: ServoUrl,
111 history_handling: NavigationHistoryBehavior,
112 ) {
113 // Step 1. Let navigable be location's relevant global object's navigable.
114 let navigable_document = navigable.Document();
115 // Step 2. Let sourceDocument be the incumbent global object's associated Document.
116 let incumbent_global = GlobalScope::incumbent().expect("no incumbent global object");
117 let mut load_data = incumbent_global
118 .as_window()
119 .load_data_for_document(url, incumbent_global.pipeline_id());
120 load_data.about_base_url = navigable_document.about_base_url();
121 // Step 3. If location's relevant Document is not yet completely loaded,
122 // and the incumbent global object does not have transient activation, then set historyHandling to "replace".
123 //
124 // TODO: check for transient activation
125 let history_handling = if !navigable_document.completely_loaded() {
126 NavigationHistoryBehavior::Replace
127 } else {
128 history_handling
129 };
130 // Step 4. Navigate navigable to url using sourceDocument, with exceptionsEnabled set to true and historyHandling set to historyHandling.
131 navigate(cx, navigable, history_handling, false, load_data);
132 }
133
134 /// Navigate the relevant `Document`'s browsing context.
135 ///
136 /// This is ostensibly an implementation of
137 /// <https://html.spec.whatwg.org/multipage/#navigate>, but the specification has
138 /// greatly deviated from our code.
139 fn navigate(
140 &self,
141 cx: &mut JSContext,
142 window: &Window,
143 url: ServoUrl,
144 history_handling: NavigationHistoryBehavior,
145 navigation_type: NavigationType,
146 ) {
147 fn incumbent_window() -> DomRoot<Window> {
148 let incumbent_global = GlobalScope::incumbent().expect("no incumbent global object");
149 DomRoot::downcast(incumbent_global).expect("global object is not a Window")
150 }
151
152 // The active document of the source browsing context used for
153 // navigation determines the request's referrer and referrer policy.
154 let source_window = match navigation_type {
155 NavigationType::ReloadByScript | NavigationType::ReloadByConstellation => {
156 // > Navigate the browsing context [...] the source browsing context
157 // > set to the browsing context being navigated.
158 DomRoot::from_ref(window)
159 },
160 NavigationType::Normal => {
161 // > 2. Let `sourceBrowsingContext` be the incumbent global object's
162 // > browsing context.
163 incumbent_window()
164 },
165 };
166 let source_document = source_window.Document();
167
168 let referrer = Referrer::ReferrerUrl(source_document.url());
169 let referrer_policy = source_document.get_referrer_policy();
170
171 // <https://html.spec.whatwg.org/multipage/#navigate>
172 // > Let `incumbentNavigationOrigin` be the origin of the incumbent
173 // > settings object, or if no script was involved, the origin of the
174 // > node document of the element that initiated the navigation.
175 let navigation_origin_window = match navigation_type {
176 NavigationType::Normal | NavigationType::ReloadByScript => incumbent_window(),
177 NavigationType::ReloadByConstellation => DomRoot::from_ref(window),
178 };
179 let (load_origin, creator_pipeline_id) = (
180 navigation_origin_window.origin().snapshot(),
181 Some(navigation_origin_window.pipeline_id()),
182 );
183
184 // Is `historyHandling` `reload`?
185 let reload_triggered = match navigation_type {
186 NavigationType::ReloadByScript | NavigationType::ReloadByConstellation => true,
187 NavigationType::Normal => false,
188 };
189
190 // Initiate navigation
191 // TODO: rethrow exceptions, set exceptions enabled flag.
192 let load_data = LoadData::new(
193 LoadOrigin::Script(load_origin),
194 url,
195 source_document.about_base_url(),
196 creator_pipeline_id,
197 referrer,
198 referrer_policy,
199 None, // Top navigation doesn't inherit secure context
200 Some(source_document.insecure_requests_policy()),
201 source_document.has_trustworthy_ancestor_origin(),
202 source_document.creation_sandboxing_flag_set_considering_parent_iframe(),
203 );
204 navigate(cx, window, history_handling, reload_triggered, load_data);
205 }
206
207 /// Get if this `Location`'s [relevant `Document`][1] is non-null.
208 ///
209 /// [1]: https://html.spec.whatwg.org/multipage/#relevant-document
210 fn has_document(&self) -> bool {
211 // <https://html.spec.whatwg.org/multipage/#relevant-document>
212 //
213 // > A `Location` object has an associated relevant `Document`, which is
214 // > this `Location` object's relevant global object's browsing
215 // > context's active document, if this `Location` object's relevant
216 // > global object's browsing context is non-null, and null otherwise.
217 self.window
218 .same_origin_window()
219 .is_ok_and(|window| window.Document().browsing_context().is_some())
220 }
221
222 /// Get this `Location` object's [relevant `Document`][1], or
223 /// `Err(Error::Security(..))` if it's non-null and its origin is not same
224 /// origin-domain with the entry setting object's origin.
225 ///
226 /// In the specification's terms:
227 ///
228 /// 1. If this `Location` object's relevant `Document` is null, then return
229 /// null.
230 ///
231 /// 2. If this `Location` object's relevant `Document`'s origin is not same
232 /// origin-domain with the entry settings object's origin, then throw a
233 /// "`SecurityError`" `DOMException`.
234 ///
235 /// 3. Return this `Location` object's relevant `Document`.
236 ///
237 /// [1]: https://html.spec.whatwg.org/multipage/#relevant-document
238 fn document_if_same_origin(&self) -> Fallible<Option<DomRoot<Document>>> {
239 let window = self.window.same_origin_window()?;
240
241 // <https://html.spec.whatwg.org/multipage/#relevant-document>
242 //
243 // > A `Location` object has an associated relevant `Document`, which is
244 // > this `Location` object's relevant global object's browsing
245 // > context's active document, if this `Location` object's relevant
246 // > global object's browsing context is non-null, and null otherwise.
247 if let Some(window_proxy) = window.Document().browsing_context() {
248 // `Location`'s many other operations:
249 //
250 // > If this `Location` object's relevant `Document` is non-null and
251 // > its origin is not same origin-domain with the entry settings
252 // > object's origin, then throw a "SecurityError" `DOMException`.
253 //
254 // FIXME: We should still return the active document if it's same
255 // origin but not fully active. `WindowProxy::document`
256 // currently returns `None` in this case.
257 if let Some(document) = window_proxy.document().filter(|document| {
258 self.entry_settings_object()
259 .origin()
260 .same_origin_domain(&document.origin())
261 }) {
262 Ok(Some(document))
263 } else {
264 Err(Error::Security("Location's relevant Document is not same origin-domain with the entry settings object's origin".to_string().into()))
265 }
266 } else {
267 // The browsing context is null
268 Ok(None)
269 }
270 }
271
272 /// Get this `Location` object's [relevant url][1] or
273 /// `Err(Error::Security(..))` if the [relevant `Document`][2] if it's non-null
274 /// and its origin is not same origin-domain with the entry setting object's
275 /// origin.
276 ///
277 /// [1]: https://html.spec.whatwg.org/multipage/#concept-location-url
278 /// [2]: https://html.spec.whatwg.org/multipage/#relevant-document
279 fn get_url_if_same_origin(&self) -> Fallible<ServoUrl> {
280 Ok(if let Some(document) = self.document_if_same_origin()? {
281 document.url()
282 } else {
283 ServoUrl::parse("about:blank").unwrap()
284 })
285 }
286
287 fn entry_settings_object(&self) -> DomRoot<GlobalScope> {
288 GlobalScope::entry()
289 }
290
291 /// The common algorithm for `Location`'s setters and `Location::Assign`.
292 #[inline]
293 fn setter_common(
294 &self,
295 cx: &mut JSContext,
296 f: impl FnOnce(ServoUrl) -> Fallible<Option<ServoUrl>>,
297 ) -> ErrorResult {
298 // Step 1: If this Location object's relevant Document is null, then return.
299 // Step 2: If this Location object's relevant Document's origin is not
300 // same origin-domain with the entry settings object's origin, then
301 // throw a "SecurityError" DOMException.
302 if let Some(document) = self.document_if_same_origin()? {
303 // Step 3: Let copyURL be a copy of this Location object's url.
304 // Step 4: Assign the result of running f(copyURL) to copyURL.
305 if let Some(copy_url) = f(document.url())? {
306 // Step 5: Terminate these steps if copyURL is null.
307 // Step 6: Location-object navigate to copyURL.
308 self.navigate(
309 cx,
310 document.window(),
311 copy_url,
312 NavigationHistoryBehavior::Push,
313 NavigationType::Normal,
314 );
315 }
316 }
317 Ok(())
318 }
319
320 /// Perform a user-requested reload (the unlabeled paragraph after
321 /// [`reload()`][1]).
322 ///
323 /// [1]: https://html.spec.whatwg.org/multipage/#dom-location-reload
324 pub(crate) fn reload_without_origin_check(&self, cx: &mut JSContext, window: &Window) {
325 // > When a user requests that the active document of a browsing context
326 // > be reloaded through a user interface element, the user agent should
327 // > navigate the browsing context to the same resource as that
328 // > `Document`, with `historyHandling` set to "reload".
329 let url = window.get_url();
330 self.navigate(
331 cx,
332 window,
333 url,
334 NavigationHistoryBehavior::Replace,
335 NavigationType::ReloadByConstellation,
336 );
337 }
338}
339
340impl LocationMethods<crate::DomTypeHolder> for Location {
341 /// <https://html.spec.whatwg.org/multipage/#dom-location-assign>
342 fn Assign(&self, cx: &mut JSContext, url: USVString) -> ErrorResult {
343 self.setter_common(cx, |_copy_url| {
344 // Step 3: Let urlRecord be the result of encoding-parsing a URL given url, relative to the entry settings object. If that failed,
345 // throw a "SyntaxError" DOMException.
346 let url = match self.entry_settings_object().encoding_parse_a_url(&url.0) {
347 Ok(url) => url,
348 Err(e) => return Err(Error::Syntax(Some(format!("Couldn't parse URL: {}", e)))),
349 };
350
351 Ok(Some(url))
352 })
353 }
354
355 /// <https://html.spec.whatwg.org/multipage/#dom-location-reload>
356 fn Reload(&self, cx: &mut JSContext) -> ErrorResult {
357 let window = self.window.same_origin_window()?;
358 let url = self.get_url_if_same_origin()?;
359 self.navigate(
360 cx,
361 window,
362 url,
363 NavigationHistoryBehavior::Replace,
364 NavigationType::ReloadByScript,
365 );
366 Ok(())
367 }
368
369 /// <https://html.spec.whatwg.org/multipage/#dom-location-replace>
370 fn Replace(&self, cx: &mut JSContext, url: USVString) -> ErrorResult {
371 // TODO: This should eventually support cross-origin operation.
372 let window = self.window.same_origin_window()?;
373
374 // Step 1: If this Location object's relevant Document is null, then return.
375 if self.has_document() {
376 // Step 2. Let urlRecord be the result of encoding-parsing a URL given url, relative to the entry settings object.
377 let url = match self.entry_settings_object().encoding_parse_a_url(&url.0) {
378 Ok(url) => url,
379 // Step 3. If urlRecord is failure, then throw a "SyntaxError" DOMException.
380 Err(e) => return Err(Error::Syntax(Some(format!("Couldn't parse URL: {}", e)))),
381 };
382 // Step 4. Location-object navigate this to urlRecord given "replace".
383 self.navigate_a_location(cx, window, url, NavigationHistoryBehavior::Replace);
384 }
385 Ok(())
386 }
387
388 /// <https://html.spec.whatwg.org/multipage/#dom-location-hash>
389 fn GetHash(&self) -> Fallible<USVString> {
390 Ok(UrlHelper::Hash(&self.get_url_if_same_origin()?))
391 }
392
393 /// <https://html.spec.whatwg.org/multipage/#dom-location-hash>
394 fn SetHash(&self, cx: &mut JSContext, value: USVString) -> ErrorResult {
395 let window = self.window.same_origin_window()?;
396
397 // Step 1. If this's relevant Document is null, then return.
398 if self.has_document() {
399 // Step 2. If this's relevant Document's origin is not same origin-domain
400 // with the entry settings object's origin, then throw a "SecurityError" DOMException.
401 // Step 3. Let copyURL be a copy of this's url.
402 let mut copy_url = self.get_url_if_same_origin()?;
403 // Step 4. Let thisURLFragment be copyURL's fragment if it is non-null; otherwise the empty string.
404 let this_url_fragment = copy_url.fragment().map(str::to_owned).unwrap_or_default();
405 // Step 6. Set copyURL's fragment to the empty string.
406 // Step 7. Basic URL parse input, with copyURL as url and fragment state as state override.
407 let input = &value.0;
408 // Note that if the hash is the empty string, we shouldn't then set the fragment to `None`.
409 // That's because the empty string is a valid hash target and should then scroll to the
410 // top of the document. Therefore, we don't use `UrlHelpers::SetHash` here, which would
411 // set it to `None`.
412 copy_url.set_fragment(match input {
413 // Step 5. Let input be the given value with a single leading "#" removed, if any.
414 _ if input.starts_with('#') => Some(&input[1..]),
415 _ => Some(input),
416 });
417 // Step 8. If copyURL's fragment is thisURLFragment, then return.
418 if copy_url.fragment() != Some(&this_url_fragment) {
419 // Step 9. Location-object navigate this to copyURL.
420 self.navigate_a_location(cx, window, copy_url, NavigationHistoryBehavior::Auto);
421 }
422 }
423 Ok(())
424 }
425
426 /// <https://html.spec.whatwg.org/multipage/#dom-location-host>
427 fn GetHost(&self) -> Fallible<USVString> {
428 Ok(UrlHelper::Host(&self.get_url_if_same_origin()?))
429 }
430
431 /// <https://html.spec.whatwg.org/multipage/#dom-location-host>
432 fn SetHost(&self, cx: &mut JSContext, value: USVString) -> ErrorResult {
433 self.setter_common(cx, |mut copy_url| {
434 // Step 4: If copyURL's cannot-be-a-base-URL flag is set, terminate these steps.
435 if copy_url.cannot_be_a_base() {
436 return Ok(None);
437 }
438
439 // Step 5: Basic URL parse the given value, with copyURL as url and host state
440 // as state override.
441 let _ = copy_url.as_mut_url().set_host(Some(&value.0));
442
443 Ok(Some(copy_url))
444 })
445 }
446
447 /// <https://html.spec.whatwg.org/multipage/#dom-location-origin>
448 fn GetOrigin(&self) -> Fallible<USVString> {
449 Ok(UrlHelper::Origin(&self.get_url_if_same_origin()?))
450 }
451
452 /// <https://html.spec.whatwg.org/multipage/#dom-location-hostname>
453 fn GetHostname(&self) -> Fallible<USVString> {
454 Ok(UrlHelper::Hostname(&self.get_url_if_same_origin()?))
455 }
456
457 /// <https://html.spec.whatwg.org/multipage/#dom-location-hostname>
458 fn SetHostname(&self, cx: &mut JSContext, value: USVString) -> ErrorResult {
459 self.setter_common(cx, |mut copy_url| {
460 // Step 4: If copyURL's cannot-be-a-base-URL flag is set, terminate these steps.
461 if copy_url.cannot_be_a_base() {
462 return Ok(None);
463 }
464
465 // Step 5: Basic URL parse the given value, with copyURL as url and hostname
466 // state as state override.
467 let _ = copy_url.as_mut_url().set_host(Some(&value.0));
468
469 Ok(Some(copy_url))
470 })
471 }
472
473 /// <https://html.spec.whatwg.org/multipage/#dom-location-href>
474 fn GetHref(&self) -> Fallible<USVString> {
475 Ok(UrlHelper::Href(&self.get_url_if_same_origin()?))
476 }
477
478 /// <https://html.spec.whatwg.org/multipage/#dom-location-href>
479 fn SetHref(&self, cx: &mut JSContext, value: USVString) -> ErrorResult {
480 let window = self.window.same_origin_window()?;
481
482 // Step 1. If this's relevant Document is null, then return.
483 if self.has_document() {
484 // Note: no call to self.check_same_origin_domain()
485 // Step 2: Let url be the result of encoding-parsing a URL given the given value, relative to the entry settings object.
486 let url = match self.entry_settings_object().encoding_parse_a_url(&value.0) {
487 Ok(url) => url,
488 // Step 3: If url is failure, then throw a "SyntaxError" DOMException.
489 Err(e) => return Err(Error::Syntax(Some(format!("Couldn't parse URL: {}", e)))),
490 };
491 // Step 4: Location-object navigate this to url.
492 self.navigate_a_location(cx, window, url, NavigationHistoryBehavior::Auto);
493 }
494 Ok(())
495 }
496
497 /// <https://html.spec.whatwg.org/multipage/#dom-location-pathname>
498 fn GetPathname(&self) -> Fallible<USVString> {
499 Ok(UrlHelper::Pathname(&self.get_url_if_same_origin()?))
500 }
501
502 /// <https://html.spec.whatwg.org/multipage/#dom-location-pathname>
503 fn SetPathname(&self, cx: &mut JSContext, value: USVString) -> ErrorResult {
504 self.setter_common(cx, |mut copy_url| {
505 // Step 4: If copyURL's cannot-be-a-base-URL flag is set, terminate these steps.
506 if copy_url.cannot_be_a_base() {
507 return Ok(None);
508 }
509
510 // Step 5: Set copyURL's path to the empty list.
511 // Step 6: Basic URL parse the given value, with copyURL as url and path
512 // start state as state override.
513 copy_url.as_mut_url().set_path(&value.0);
514
515 Ok(Some(copy_url))
516 })
517 }
518
519 /// <https://html.spec.whatwg.org/multipage/#dom-location-port>
520 fn GetPort(&self) -> Fallible<USVString> {
521 Ok(UrlHelper::Port(&self.get_url_if_same_origin()?))
522 }
523
524 /// <https://html.spec.whatwg.org/multipage/#dom-location-port>
525 fn SetPort(&self, cx: &mut JSContext, value: USVString) -> ErrorResult {
526 self.setter_common(cx, |mut copy_url| {
527 // Step 4: If copyURL cannot have a username/password/port, then return.
528 // https://url.spec.whatwg.org/#cannot-have-a-username-password-port
529 if copy_url.host().is_none() ||
530 copy_url.cannot_be_a_base() ||
531 copy_url.scheme() == "file"
532 {
533 return Ok(None);
534 }
535
536 // Step 5: If the given value is the empty string, then set copyURL's
537 // port to null.
538 // Step 6: Otherwise, basic URL parse the given value, with copyURL as url
539 // and port state as state override.
540 let _ = url::quirks::set_port(copy_url.as_mut_url(), &value.0);
541
542 Ok(Some(copy_url))
543 })
544 }
545
546 /// <https://html.spec.whatwg.org/multipage/#dom-location-protocol>
547 fn GetProtocol(&self) -> Fallible<USVString> {
548 Ok(UrlHelper::Protocol(&self.get_url_if_same_origin()?))
549 }
550
551 /// <https://html.spec.whatwg.org/multipage/#dom-location-protocol>
552 fn SetProtocol(&self, cx: &mut JSContext, value: USVString) -> ErrorResult {
553 self.setter_common(cx, |mut copy_url| {
554 // Step 4: Let possibleFailure be the result of basic URL parsing the given
555 // value, followed by ":", with copyURL as url and scheme start state as
556 // state override.
557 let scheme = match value.0.find(':') {
558 Some(position) => &value.0[..position],
559 None => &value.0,
560 };
561
562 if copy_url.as_mut_url().set_scheme(scheme).is_err() {
563 // Step 5: If possibleFailure is failure, then throw a "SyntaxError" DOMException.
564 return Err(Error::Syntax(
565 "Couldn't parse URL scheme".to_string().into(),
566 ));
567 }
568
569 // Step 6: If copyURL's scheme is not an HTTP(S) scheme, then terminate these steps.
570 if !copy_url.scheme().eq_ignore_ascii_case("http") &&
571 !copy_url.scheme().eq_ignore_ascii_case("https")
572 {
573 return Ok(None);
574 }
575
576 Ok(Some(copy_url))
577 })
578 }
579
580 /// <https://html.spec.whatwg.org/multipage/#dom-location-search>
581 fn GetSearch(&self) -> Fallible<USVString> {
582 Ok(UrlHelper::Search(&self.get_url_if_same_origin()?))
583 }
584
585 /// <https://html.spec.whatwg.org/multipage/#dom-location-search>
586 fn SetSearch(&self, cx: &mut JSContext, value: USVString) -> ErrorResult {
587 self.setter_common(cx, |mut copy_url| {
588 // Step 4: If the given value is the empty string, set copyURL's query to null.
589 // Step 5: Otherwise, run these substeps:
590 // 1. Let input be the given value with a single leading "?" removed, if any.
591 // 2. Set copyURL's query to the empty string.
592 // 3. Basic URL parse input, with copyURL as url and query state as state
593 // override, and the relevant Document's document's character encoding as
594 // encoding override.
595 copy_url.as_mut_url().set_query(match value.0.as_str() {
596 "" => None,
597 _ if value.0.starts_with('?') => Some(&value.0[1..]),
598 _ => Some(&value.0),
599 });
600
601 Ok(Some(copy_url))
602 })
603 }
604
605 /// <https://html.spec.whatwg.org/multipage/#dom-location-ancestororigins>
606 fn GetAncestorOrigins(&self, cx: &mut JSContext) -> Fallible<DomRoot<DOMStringList>> {
607 let window = self.window.same_origin_window()?;
608
609 // Step 1. If this's relevant Document is null, then return this's empty DOMStringList.
610 if !self.has_document() {
611 return Ok(self
612 .empty_dom_string_list
613 .or_init(|| DOMStringList::new(cx, window.upcast(), vec![])));
614 }
615 // Step 2. If this's relevant Document's origin is not same origin-domain
616 // with the entry settings object's origin, then throw a "SecurityError" DOMException.
617 let document = window.Document();
618 if !document
619 .origin()
620 .same_origin_domain(&self.entry_settings_object().origin())
621 {
622 return Err(Error::Security("Location's relevant Document is not \
623 same origin-domain with the entry settings object's \
624 origin".to_string().into()));
625 }
626 // Step 3. Assert: this's relevant Document's ancestor origins list is not null.
627 // Step 4. Otherwise, return this's relevant Document's ancestor origins list.
628 Ok(document
629 .ancestor_origins_list()
630 .expect("Must always have ancestor origins initialized"))
631 }
632}