Bug 1892041 - Part 1: Update test262 features. r=spidermonkey-reviewers,dminor
[gecko.git] / dom / base / Location.cpp
blob9edb9e0b6f8e067167c82aac61732732158bcd9e
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "Location.h"
8 #include "nsIScriptObjectPrincipal.h"
9 #include "nsIScriptContext.h"
10 #include "nsDocShellLoadState.h"
11 #include "nsIWebNavigation.h"
12 #include "nsIOService.h"
13 #include "nsIURL.h"
14 #include "nsIJARURI.h"
15 #include "nsIURIMutator.h"
16 #include "nsNetUtil.h"
17 #include "nsCOMPtr.h"
18 #include "nsEscape.h"
19 #include "nsPresContext.h"
20 #include "nsError.h"
21 #include "nsReadableUtils.h"
22 #include "nsJSUtils.h"
23 #include "nsContentUtils.h"
24 #include "nsDocShell.h"
25 #include "nsGlobalWindowOuter.h"
26 #include "nsPIDOMWindowInlines.h"
27 #include "mozilla/Likely.h"
28 #include "nsCycleCollectionParticipant.h"
29 #include "mozilla/BasePrincipal.h"
30 #include "mozilla/Components.h"
31 #include "mozilla/NullPrincipal.h"
32 #include "mozilla/ServoStyleConsts.h"
33 #include "mozilla/StaticPrefs_dom.h"
34 #include "mozilla/Unused.h"
35 #include "mozilla/dom/Document.h"
36 #include "mozilla/dom/DocumentInlines.h"
37 #include "mozilla/dom/FragmentDirective.h"
38 #include "mozilla/dom/LocationBinding.h"
39 #include "mozilla/dom/ScriptSettings.h"
40 #include "ReferrerInfo.h"
42 namespace mozilla::dom {
44 Location::Location(nsPIDOMWindowInner* aWindow)
45 : mCachedHash(VoidString()), mInnerWindow(aWindow) {
46 BrowsingContext* bc = GetBrowsingContext();
47 if (bc) {
48 bc->LocationCreated(this);
52 Location::~Location() {
53 if (isInList()) {
54 remove();
58 // QueryInterface implementation for Location
59 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Location)
60 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
61 NS_INTERFACE_MAP_ENTRY(nsISupports)
62 NS_INTERFACE_MAP_END
64 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Location, mInnerWindow)
66 NS_IMPL_CYCLE_COLLECTING_ADDREF(Location)
67 NS_IMPL_CYCLE_COLLECTING_RELEASE(Location)
69 BrowsingContext* Location::GetBrowsingContext() {
70 return mInnerWindow ? mInnerWindow->GetBrowsingContext() : nullptr;
73 nsIDocShell* Location::GetDocShell() {
74 if (BrowsingContext* bc = GetBrowsingContext()) {
75 return bc->GetDocShell();
77 return nullptr;
80 nsresult Location::GetURI(nsIURI** aURI, bool aGetInnermostURI) {
81 *aURI = nullptr;
83 nsIDocShell* docShell = GetDocShell();
84 if (!docShell) {
85 return NS_OK;
88 nsIWebNavigation* webNav = nsDocShell::Cast(docShell);
90 nsCOMPtr<nsIURI> uri;
91 nsresult rv = webNav->GetCurrentURI(getter_AddRefs(uri));
92 NS_ENSURE_SUCCESS(rv, rv);
94 // It is valid for docshell to return a null URI. Don't try to fixup
95 // if this happens.
96 if (!uri) {
97 return NS_OK;
100 if (aGetInnermostURI) {
101 nsCOMPtr<nsIJARURI> jarURI(do_QueryInterface(uri));
102 while (jarURI) {
103 jarURI->GetJARFile(getter_AddRefs(uri));
104 jarURI = do_QueryInterface(uri);
108 NS_ASSERTION(uri, "nsJARURI screwed up?");
110 // Remove the fragment directive from the url hash.
111 FragmentDirective::ParseAndRemoveFragmentDirectiveFromFragment(uri);
112 nsCOMPtr<nsIURI> exposableURI = net::nsIOService::CreateExposableURI(uri);
113 exposableURI.forget(aURI);
114 return NS_OK;
117 void Location::GetHash(nsAString& aHash, nsIPrincipal& aSubjectPrincipal,
118 ErrorResult& aRv) {
119 if (!CallerSubsumes(&aSubjectPrincipal)) {
120 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
121 return;
124 if (!mCachedHash.IsVoid()) {
125 aHash = mCachedHash;
126 return;
129 aHash.SetLength(0);
131 nsCOMPtr<nsIURI> uri;
132 aRv = GetURI(getter_AddRefs(uri));
133 if (NS_WARN_IF(aRv.Failed()) || !uri) {
134 return;
137 nsAutoCString ref;
139 aRv = uri->GetRef(ref);
140 if (NS_WARN_IF(aRv.Failed())) {
141 return;
144 if (!ref.IsEmpty()) {
145 aHash.Assign(char16_t('#'));
146 AppendUTF8toUTF16(ref, aHash);
149 mCachedHash = aHash;
152 void Location::SetHash(const nsAString& aHash, nsIPrincipal& aSubjectPrincipal,
153 ErrorResult& aRv) {
154 if (!CallerSubsumes(&aSubjectPrincipal)) {
155 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
156 return;
159 NS_ConvertUTF16toUTF8 hash(aHash);
160 if (hash.IsEmpty() || hash.First() != char16_t('#')) {
161 hash.Insert(char16_t('#'), 0);
164 nsCOMPtr<nsIURI> uri;
165 aRv = GetURI(getter_AddRefs(uri));
166 if (NS_WARN_IF(aRv.Failed()) || !uri) {
167 return;
170 aRv = NS_MutateURI(uri).SetRef(hash).Finalize(uri);
171 if (NS_WARN_IF(aRv.Failed()) || !uri) {
172 return;
175 SetURI(uri, aSubjectPrincipal, aRv);
178 void Location::GetHost(nsAString& aHost, nsIPrincipal& aSubjectPrincipal,
179 ErrorResult& aRv) {
180 if (!CallerSubsumes(&aSubjectPrincipal)) {
181 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
182 return;
185 aHost.Truncate();
187 nsCOMPtr<nsIURI> uri;
188 nsresult result;
190 result = GetURI(getter_AddRefs(uri), true);
192 if (uri) {
193 nsAutoCString hostport;
195 result = uri->GetHostPort(hostport);
197 if (NS_SUCCEEDED(result)) {
198 AppendUTF8toUTF16(hostport, aHost);
203 void Location::SetHost(const nsAString& aHost, nsIPrincipal& aSubjectPrincipal,
204 ErrorResult& aRv) {
205 if (!CallerSubsumes(&aSubjectPrincipal)) {
206 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
207 return;
210 nsCOMPtr<nsIURI> uri;
211 aRv = GetURI(getter_AddRefs(uri));
212 if (NS_WARN_IF(aRv.Failed()) || !uri) {
213 return;
216 aRv =
217 NS_MutateURI(uri).SetHostPort(NS_ConvertUTF16toUTF8(aHost)).Finalize(uri);
218 if (NS_WARN_IF(aRv.Failed())) {
219 return;
222 SetURI(uri, aSubjectPrincipal, aRv);
225 void Location::GetHostname(nsAString& aHostname,
226 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
227 if (!CallerSubsumes(&aSubjectPrincipal)) {
228 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
229 return;
232 aHostname.Truncate();
234 nsCOMPtr<nsIURI> uri;
235 GetURI(getter_AddRefs(uri), true);
236 if (uri) {
237 nsContentUtils::GetHostOrIPv6WithBrackets(uri, aHostname);
241 void Location::SetHostname(const nsAString& aHostname,
242 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
243 if (!CallerSubsumes(&aSubjectPrincipal)) {
244 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
245 return;
248 nsCOMPtr<nsIURI> uri;
249 aRv = GetURI(getter_AddRefs(uri));
250 if (NS_WARN_IF(aRv.Failed()) || !uri) {
251 return;
254 aRv =
255 NS_MutateURI(uri).SetHost(NS_ConvertUTF16toUTF8(aHostname)).Finalize(uri);
256 if (NS_WARN_IF(aRv.Failed())) {
257 return;
260 SetURI(uri, aSubjectPrincipal, aRv);
263 nsresult Location::GetHref(nsAString& aHref) {
264 aHref.Truncate();
266 nsCOMPtr<nsIURI> uri;
267 nsresult rv = GetURI(getter_AddRefs(uri));
268 if (NS_WARN_IF(NS_FAILED(rv)) || !uri) {
269 return rv;
272 nsAutoCString uriString;
273 rv = uri->GetSpec(uriString);
274 if (NS_WARN_IF(NS_FAILED(rv))) {
275 return rv;
278 AppendUTF8toUTF16(uriString, aHref);
279 return NS_OK;
282 void Location::GetOrigin(nsAString& aOrigin, nsIPrincipal& aSubjectPrincipal,
283 ErrorResult& aRv) {
284 if (!CallerSubsumes(&aSubjectPrincipal)) {
285 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
286 return;
289 aOrigin.Truncate();
291 nsCOMPtr<nsIURI> uri;
292 aRv = GetURI(getter_AddRefs(uri), true);
293 if (NS_WARN_IF(aRv.Failed()) || !uri) {
294 return;
297 nsAutoString origin;
298 aRv = nsContentUtils::GetWebExposedOriginSerialization(uri, origin);
299 if (NS_WARN_IF(aRv.Failed())) {
300 return;
303 aOrigin = origin;
306 void Location::GetPathname(nsAString& aPathname,
307 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
308 if (!CallerSubsumes(&aSubjectPrincipal)) {
309 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
310 return;
313 aPathname.Truncate();
315 nsCOMPtr<nsIURI> uri;
316 aRv = GetURI(getter_AddRefs(uri));
317 if (NS_WARN_IF(aRv.Failed()) || !uri) {
318 return;
321 nsAutoCString file;
323 aRv = uri->GetFilePath(file);
324 if (NS_WARN_IF(aRv.Failed())) {
325 return;
328 AppendUTF8toUTF16(file, aPathname);
331 void Location::SetPathname(const nsAString& aPathname,
332 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
333 if (!CallerSubsumes(&aSubjectPrincipal)) {
334 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
335 return;
338 nsCOMPtr<nsIURI> uri;
339 aRv = GetURI(getter_AddRefs(uri));
340 if (NS_WARN_IF(aRv.Failed()) || !uri) {
341 return;
344 nsresult rv = NS_MutateURI(uri)
345 .SetFilePath(NS_ConvertUTF16toUTF8(aPathname))
346 .Finalize(uri);
347 if (NS_FAILED(rv)) {
348 return;
351 SetURI(uri, aSubjectPrincipal, aRv);
354 void Location::GetPort(nsAString& aPort, nsIPrincipal& aSubjectPrincipal,
355 ErrorResult& aRv) {
356 if (!CallerSubsumes(&aSubjectPrincipal)) {
357 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
358 return;
361 aPort.SetLength(0);
363 nsCOMPtr<nsIURI> uri;
364 aRv = GetURI(getter_AddRefs(uri), true);
365 if (NS_WARN_IF(aRv.Failed()) || !uri) {
366 return;
369 int32_t port;
370 nsresult result = uri->GetPort(&port);
372 // Don't propagate this exception to caller
373 if (NS_SUCCEEDED(result) && -1 != port) {
374 nsAutoString portStr;
375 portStr.AppendInt(port);
376 aPort.Append(portStr);
380 void Location::SetPort(const nsAString& aPort, nsIPrincipal& aSubjectPrincipal,
381 ErrorResult& aRv) {
382 if (!CallerSubsumes(&aSubjectPrincipal)) {
383 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
384 return;
387 nsCOMPtr<nsIURI> uri;
388 aRv = GetURI(getter_AddRefs(uri));
389 if (NS_WARN_IF(aRv.Failed() || !uri)) {
390 return;
393 // perhaps use nsReadingIterators at some point?
394 NS_ConvertUTF16toUTF8 portStr(aPort);
395 const char* buf = portStr.get();
396 int32_t port = -1;
398 if (!portStr.IsEmpty() && buf) {
399 if (*buf == ':') {
400 port = atol(buf + 1);
401 } else {
402 port = atol(buf);
406 aRv = NS_MutateURI(uri).SetPort(port).Finalize(uri);
407 if (NS_WARN_IF(aRv.Failed())) {
408 return;
411 SetURI(uri, aSubjectPrincipal, aRv);
414 void Location::GetProtocol(nsAString& aProtocol,
415 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
416 if (!CallerSubsumes(&aSubjectPrincipal)) {
417 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
418 return;
421 aProtocol.SetLength(0);
423 nsCOMPtr<nsIURI> uri;
424 aRv = GetURI(getter_AddRefs(uri));
425 if (NS_WARN_IF(aRv.Failed()) || !uri) {
426 return;
429 nsAutoCString protocol;
431 aRv = uri->GetScheme(protocol);
432 if (NS_WARN_IF(aRv.Failed())) {
433 return;
436 CopyASCIItoUTF16(protocol, aProtocol);
437 aProtocol.Append(char16_t(':'));
440 void Location::SetProtocol(const nsAString& aProtocol,
441 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
442 if (!CallerSubsumes(&aSubjectPrincipal)) {
443 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
444 return;
447 nsCOMPtr<nsIURI> uri;
448 aRv = GetURI(getter_AddRefs(uri));
449 if (NS_WARN_IF(aRv.Failed()) || !uri) {
450 return;
453 nsAString::const_iterator start, end;
454 aProtocol.BeginReading(start);
455 aProtocol.EndReading(end);
456 nsAString::const_iterator iter(start);
457 Unused << FindCharInReadable(':', iter, end);
459 nsresult rv = NS_MutateURI(uri)
460 .SetScheme(NS_ConvertUTF16toUTF8(Substring(start, iter)))
461 .Finalize(uri);
462 if (NS_WARN_IF(NS_FAILED(rv))) {
463 // Oh, I wish nsStandardURL returned NS_ERROR_MALFORMED_URI for _all_ the
464 // malformed cases, not just some of them!
465 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
466 return;
469 nsAutoCString newSpec;
470 aRv = uri->GetSpec(newSpec);
471 if (NS_WARN_IF(aRv.Failed())) {
472 return;
474 // We may want a new URI class for the new URI, so recreate it:
475 rv = NS_NewURI(getter_AddRefs(uri), newSpec);
476 if (NS_FAILED(rv)) {
477 if (rv == NS_ERROR_MALFORMED_URI) {
478 rv = NS_ERROR_DOM_SYNTAX_ERR;
481 aRv.Throw(rv);
482 return;
485 if (!uri->SchemeIs("http") && !uri->SchemeIs("https")) {
486 // No-op, per spec.
487 return;
490 SetURI(uri, aSubjectPrincipal, aRv);
493 void Location::GetSearch(nsAString& aSearch, nsIPrincipal& aSubjectPrincipal,
494 ErrorResult& aRv) {
495 if (!CallerSubsumes(&aSubjectPrincipal)) {
496 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
497 return;
500 aSearch.SetLength(0);
502 nsCOMPtr<nsIURI> uri;
503 nsresult result = NS_OK;
505 result = GetURI(getter_AddRefs(uri));
507 nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
509 if (url) {
510 nsAutoCString search;
512 result = url->GetQuery(search);
514 if (NS_SUCCEEDED(result) && !search.IsEmpty()) {
515 aSearch.Assign(char16_t('?'));
516 AppendUTF8toUTF16(search, aSearch);
521 void Location::SetSearch(const nsAString& aSearch,
522 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
523 if (!CallerSubsumes(&aSubjectPrincipal)) {
524 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
525 return;
528 nsCOMPtr<nsIURI> uri;
529 aRv = GetURI(getter_AddRefs(uri));
530 nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
531 if (NS_WARN_IF(aRv.Failed()) || !url) {
532 return;
535 aRv =
536 NS_MutateURI(uri).SetQuery(NS_ConvertUTF16toUTF8(aSearch)).Finalize(uri);
537 if (NS_WARN_IF(aRv.Failed())) {
538 return;
541 SetURI(uri, aSubjectPrincipal, aRv);
544 void Location::Reload(bool aForceget, nsIPrincipal& aSubjectPrincipal,
545 ErrorResult& aRv) {
546 if (!CallerSubsumes(&aSubjectPrincipal)) {
547 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
548 return;
551 RefPtr<nsDocShell> docShell(nsDocShell::Cast(GetDocShell()));
552 if (!docShell) {
553 return aRv.Throw(NS_ERROR_FAILURE);
556 RefPtr<BrowsingContext> bc = GetBrowsingContext();
557 if (!bc || bc->IsDiscarded()) {
558 return;
561 CallerType callerType = aSubjectPrincipal.IsSystemPrincipal()
562 ? CallerType::System
563 : CallerType::NonSystem;
565 nsresult rv = bc->CheckLocationChangeRateLimit(callerType);
566 if (NS_FAILED(rv)) {
567 aRv.Throw(rv);
568 return;
571 uint32_t reloadFlags = nsIWebNavigation::LOAD_FLAGS_NONE;
573 if (aForceget) {
574 reloadFlags = nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE |
575 nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY;
578 rv = docShell->Reload(reloadFlags);
579 if (NS_FAILED(rv) && rv != NS_BINDING_ABORTED) {
580 // NS_BINDING_ABORTED is returned when we attempt to reload a POST result
581 // and the user says no at the "do you want to reload?" prompt. Don't
582 // propagate this one back to callers.
583 return aRv.Throw(rv);
587 void Location::Assign(const nsAString& aUrl, nsIPrincipal& aSubjectPrincipal,
588 ErrorResult& aRv) {
589 if (!CallerSubsumes(&aSubjectPrincipal)) {
590 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
591 return;
594 DoSetHref(aUrl, aSubjectPrincipal, false, aRv);
597 bool Location::CallerSubsumes(nsIPrincipal* aSubjectPrincipal) {
598 MOZ_ASSERT(aSubjectPrincipal);
600 BrowsingContext* bc = GetBrowsingContext();
601 if (MOZ_UNLIKELY(!bc) || MOZ_UNLIKELY(bc->IsDiscarded())) {
602 // Per spec, operations on a Location object with a discarded BC are no-ops,
603 // not security errors, so we need to return true from the access check and
604 // let the caller do its own discarded docShell check.
605 return true;
607 if (MOZ_UNLIKELY(!bc->IsInProcess())) {
608 return false;
611 // Get the principal associated with the location object. Note that this is
612 // the principal of the page which will actually be navigated, not the
613 // principal of the Location object itself. This is why we need this check
614 // even though we only allow limited cross-origin access to Location objects
615 // in general.
616 nsPIDOMWindowOuter* outer = bc->GetDOMWindow();
617 MOZ_DIAGNOSTIC_ASSERT(outer);
618 if (MOZ_UNLIKELY(!outer)) return false;
620 nsIScriptObjectPrincipal* sop = nsGlobalWindowOuter::Cast(outer);
621 bool subsumes = false;
622 nsresult rv = aSubjectPrincipal->SubsumesConsideringDomain(
623 sop->GetPrincipal(), &subsumes);
624 NS_ENSURE_SUCCESS(rv, false);
625 return subsumes;
628 JSObject* Location::WrapObject(JSContext* aCx,
629 JS::Handle<JSObject*> aGivenProto) {
630 return Location_Binding::Wrap(aCx, this, aGivenProto);
633 void Location::ClearCachedValues() { mCachedHash = VoidString(); }
635 } // namespace mozilla::dom