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/. */
8 #include "nsIScriptObjectPrincipal.h"
9 #include "nsIScriptContext.h"
10 #include "nsDocShellLoadState.h"
11 #include "nsIWebNavigation.h"
12 #include "nsIOService.h"
14 #include "nsIJARURI.h"
15 #include "nsIURIMutator.h"
16 #include "nsNetUtil.h"
19 #include "nsPresContext.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/LocationBinding.h"
38 #include "mozilla/dom/ScriptSettings.h"
39 #include "ReferrerInfo.h"
41 namespace mozilla::dom
{
43 Location::Location(nsPIDOMWindowInner
* aWindow
)
44 : mCachedHash(VoidString()), mInnerWindow(aWindow
) {
45 BrowsingContext
* bc
= GetBrowsingContext();
47 bc
->LocationCreated(this);
51 Location::~Location() {
57 // QueryInterface implementation for Location
58 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Location
)
59 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
60 NS_INTERFACE_MAP_ENTRY(nsISupports
)
63 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Location
, mInnerWindow
)
65 NS_IMPL_CYCLE_COLLECTING_ADDREF(Location
)
66 NS_IMPL_CYCLE_COLLECTING_RELEASE(Location
)
68 BrowsingContext
* Location::GetBrowsingContext() {
69 return mInnerWindow
? mInnerWindow
->GetBrowsingContext() : nullptr;
72 nsIDocShell
* Location::GetDocShell() {
73 if (BrowsingContext
* bc
= GetBrowsingContext()) {
74 return bc
->GetDocShell();
79 nsresult
Location::GetURI(nsIURI
** aURI
, bool aGetInnermostURI
) {
82 nsIDocShell
* docShell
= GetDocShell();
87 nsIWebNavigation
* webNav
= nsDocShell::Cast(docShell
);
90 nsresult rv
= webNav
->GetCurrentURI(getter_AddRefs(uri
));
91 NS_ENSURE_SUCCESS(rv
, rv
);
93 // It is valid for docshell to return a null URI. Don't try to fixup
99 if (aGetInnermostURI
) {
100 nsCOMPtr
<nsIJARURI
> jarURI(do_QueryInterface(uri
));
102 jarURI
->GetJARFile(getter_AddRefs(uri
));
103 jarURI
= do_QueryInterface(uri
);
107 NS_ASSERTION(uri
, "nsJARURI screwed up?");
108 nsCOMPtr
<nsIURI
> exposableURI
= net::nsIOService::CreateExposableURI(uri
);
109 exposableURI
.forget(aURI
);
113 void Location::GetHash(nsAString
& aHash
, nsIPrincipal
& aSubjectPrincipal
,
115 if (!CallerSubsumes(&aSubjectPrincipal
)) {
116 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
120 if (!mCachedHash
.IsVoid()) {
127 nsCOMPtr
<nsIURI
> uri
;
128 aRv
= GetURI(getter_AddRefs(uri
));
129 if (NS_WARN_IF(aRv
.Failed()) || !uri
) {
135 aRv
= uri
->GetRef(ref
);
136 if (NS_WARN_IF(aRv
.Failed())) {
140 if (!ref
.IsEmpty()) {
141 aHash
.Assign(char16_t('#'));
142 AppendUTF8toUTF16(ref
, aHash
);
148 void Location::SetHash(const nsAString
& aHash
, nsIPrincipal
& aSubjectPrincipal
,
150 if (!CallerSubsumes(&aSubjectPrincipal
)) {
151 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
155 NS_ConvertUTF16toUTF8
hash(aHash
);
156 if (hash
.IsEmpty() || hash
.First() != char16_t('#')) {
157 hash
.Insert(char16_t('#'), 0);
160 nsCOMPtr
<nsIURI
> uri
;
161 aRv
= GetURI(getter_AddRefs(uri
));
162 if (NS_WARN_IF(aRv
.Failed()) || !uri
) {
166 aRv
= NS_MutateURI(uri
).SetRef(hash
).Finalize(uri
);
167 if (NS_WARN_IF(aRv
.Failed()) || !uri
) {
171 SetURI(uri
, aSubjectPrincipal
, aRv
);
174 void Location::GetHost(nsAString
& aHost
, nsIPrincipal
& aSubjectPrincipal
,
176 if (!CallerSubsumes(&aSubjectPrincipal
)) {
177 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
183 nsCOMPtr
<nsIURI
> uri
;
186 result
= GetURI(getter_AddRefs(uri
), true);
189 nsAutoCString hostport
;
191 result
= uri
->GetHostPort(hostport
);
193 if (NS_SUCCEEDED(result
)) {
194 AppendUTF8toUTF16(hostport
, aHost
);
199 void Location::SetHost(const nsAString
& aHost
, nsIPrincipal
& aSubjectPrincipal
,
201 if (!CallerSubsumes(&aSubjectPrincipal
)) {
202 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
206 nsCOMPtr
<nsIURI
> uri
;
207 aRv
= GetURI(getter_AddRefs(uri
));
208 if (NS_WARN_IF(aRv
.Failed()) || !uri
) {
213 NS_MutateURI(uri
).SetHostPort(NS_ConvertUTF16toUTF8(aHost
)).Finalize(uri
);
214 if (NS_WARN_IF(aRv
.Failed())) {
218 SetURI(uri
, aSubjectPrincipal
, aRv
);
221 void Location::GetHostname(nsAString
& aHostname
,
222 nsIPrincipal
& aSubjectPrincipal
, ErrorResult
& aRv
) {
223 if (!CallerSubsumes(&aSubjectPrincipal
)) {
224 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
228 aHostname
.Truncate();
230 nsCOMPtr
<nsIURI
> uri
;
231 GetURI(getter_AddRefs(uri
), true);
233 nsContentUtils::GetHostOrIPv6WithBrackets(uri
, aHostname
);
237 void Location::SetHostname(const nsAString
& aHostname
,
238 nsIPrincipal
& aSubjectPrincipal
, ErrorResult
& aRv
) {
239 if (!CallerSubsumes(&aSubjectPrincipal
)) {
240 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
244 nsCOMPtr
<nsIURI
> uri
;
245 aRv
= GetURI(getter_AddRefs(uri
));
246 if (NS_WARN_IF(aRv
.Failed()) || !uri
) {
251 NS_MutateURI(uri
).SetHost(NS_ConvertUTF16toUTF8(aHostname
)).Finalize(uri
);
252 if (NS_WARN_IF(aRv
.Failed())) {
256 SetURI(uri
, aSubjectPrincipal
, aRv
);
259 nsresult
Location::GetHref(nsAString
& aHref
) {
262 nsCOMPtr
<nsIURI
> uri
;
263 nsresult rv
= GetURI(getter_AddRefs(uri
));
264 if (NS_WARN_IF(NS_FAILED(rv
)) || !uri
) {
268 nsAutoCString uriString
;
269 rv
= uri
->GetSpec(uriString
);
270 if (NS_WARN_IF(NS_FAILED(rv
))) {
274 AppendUTF8toUTF16(uriString
, aHref
);
278 void Location::GetOrigin(nsAString
& aOrigin
, nsIPrincipal
& aSubjectPrincipal
,
280 if (!CallerSubsumes(&aSubjectPrincipal
)) {
281 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
287 nsCOMPtr
<nsIURI
> uri
;
288 aRv
= GetURI(getter_AddRefs(uri
), true);
289 if (NS_WARN_IF(aRv
.Failed()) || !uri
) {
294 aRv
= nsContentUtils::GetWebExposedOriginSerialization(uri
, origin
);
295 if (NS_WARN_IF(aRv
.Failed())) {
302 void Location::GetPathname(nsAString
& aPathname
,
303 nsIPrincipal
& aSubjectPrincipal
, ErrorResult
& aRv
) {
304 if (!CallerSubsumes(&aSubjectPrincipal
)) {
305 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
309 aPathname
.Truncate();
311 nsCOMPtr
<nsIURI
> uri
;
312 aRv
= GetURI(getter_AddRefs(uri
));
313 if (NS_WARN_IF(aRv
.Failed()) || !uri
) {
319 aRv
= uri
->GetFilePath(file
);
320 if (NS_WARN_IF(aRv
.Failed())) {
324 AppendUTF8toUTF16(file
, aPathname
);
327 void Location::SetPathname(const nsAString
& aPathname
,
328 nsIPrincipal
& aSubjectPrincipal
, ErrorResult
& aRv
) {
329 if (!CallerSubsumes(&aSubjectPrincipal
)) {
330 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
334 nsCOMPtr
<nsIURI
> uri
;
335 aRv
= GetURI(getter_AddRefs(uri
));
336 if (NS_WARN_IF(aRv
.Failed()) || !uri
) {
340 nsresult rv
= NS_MutateURI(uri
)
341 .SetFilePath(NS_ConvertUTF16toUTF8(aPathname
))
347 SetURI(uri
, aSubjectPrincipal
, aRv
);
350 void Location::GetPort(nsAString
& aPort
, nsIPrincipal
& aSubjectPrincipal
,
352 if (!CallerSubsumes(&aSubjectPrincipal
)) {
353 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
359 nsCOMPtr
<nsIURI
> uri
;
360 aRv
= GetURI(getter_AddRefs(uri
), true);
361 if (NS_WARN_IF(aRv
.Failed()) || !uri
) {
366 nsresult result
= uri
->GetPort(&port
);
368 // Don't propagate this exception to caller
369 if (NS_SUCCEEDED(result
) && -1 != port
) {
370 nsAutoString portStr
;
371 portStr
.AppendInt(port
);
372 aPort
.Append(portStr
);
376 void Location::SetPort(const nsAString
& aPort
, nsIPrincipal
& aSubjectPrincipal
,
378 if (!CallerSubsumes(&aSubjectPrincipal
)) {
379 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
383 nsCOMPtr
<nsIURI
> uri
;
384 aRv
= GetURI(getter_AddRefs(uri
));
385 if (NS_WARN_IF(aRv
.Failed() || !uri
)) {
389 // perhaps use nsReadingIterators at some point?
390 NS_ConvertUTF16toUTF8
portStr(aPort
);
391 const char* buf
= portStr
.get();
394 if (!portStr
.IsEmpty() && buf
) {
396 port
= atol(buf
+ 1);
402 aRv
= NS_MutateURI(uri
).SetPort(port
).Finalize(uri
);
403 if (NS_WARN_IF(aRv
.Failed())) {
407 SetURI(uri
, aSubjectPrincipal
, aRv
);
410 void Location::GetProtocol(nsAString
& aProtocol
,
411 nsIPrincipal
& aSubjectPrincipal
, ErrorResult
& aRv
) {
412 if (!CallerSubsumes(&aSubjectPrincipal
)) {
413 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
417 aProtocol
.SetLength(0);
419 nsCOMPtr
<nsIURI
> uri
;
420 aRv
= GetURI(getter_AddRefs(uri
));
421 if (NS_WARN_IF(aRv
.Failed()) || !uri
) {
425 nsAutoCString protocol
;
427 aRv
= uri
->GetScheme(protocol
);
428 if (NS_WARN_IF(aRv
.Failed())) {
432 CopyASCIItoUTF16(protocol
, aProtocol
);
433 aProtocol
.Append(char16_t(':'));
436 void Location::SetProtocol(const nsAString
& aProtocol
,
437 nsIPrincipal
& aSubjectPrincipal
, ErrorResult
& aRv
) {
438 if (!CallerSubsumes(&aSubjectPrincipal
)) {
439 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
443 nsCOMPtr
<nsIURI
> uri
;
444 aRv
= GetURI(getter_AddRefs(uri
));
445 if (NS_WARN_IF(aRv
.Failed()) || !uri
) {
449 nsAString::const_iterator start
, end
;
450 aProtocol
.BeginReading(start
);
451 aProtocol
.EndReading(end
);
452 nsAString::const_iterator
iter(start
);
453 Unused
<< FindCharInReadable(':', iter
, end
);
455 nsresult rv
= NS_MutateURI(uri
)
456 .SetScheme(NS_ConvertUTF16toUTF8(Substring(start
, iter
)))
458 if (NS_WARN_IF(NS_FAILED(rv
))) {
459 // Oh, I wish nsStandardURL returned NS_ERROR_MALFORMED_URI for _all_ the
460 // malformed cases, not just some of them!
461 aRv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
465 nsAutoCString newSpec
;
466 aRv
= uri
->GetSpec(newSpec
);
467 if (NS_WARN_IF(aRv
.Failed())) {
470 // We may want a new URI class for the new URI, so recreate it:
471 rv
= NS_NewURI(getter_AddRefs(uri
), newSpec
);
473 if (rv
== NS_ERROR_MALFORMED_URI
) {
474 rv
= NS_ERROR_DOM_SYNTAX_ERR
;
481 if (!uri
->SchemeIs("http") && !uri
->SchemeIs("https")) {
486 SetURI(uri
, aSubjectPrincipal
, aRv
);
489 void Location::GetSearch(nsAString
& aSearch
, nsIPrincipal
& aSubjectPrincipal
,
491 if (!CallerSubsumes(&aSubjectPrincipal
)) {
492 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
496 aSearch
.SetLength(0);
498 nsCOMPtr
<nsIURI
> uri
;
499 nsresult result
= NS_OK
;
501 result
= GetURI(getter_AddRefs(uri
));
503 nsCOMPtr
<nsIURL
> url(do_QueryInterface(uri
));
506 nsAutoCString search
;
508 result
= url
->GetQuery(search
);
510 if (NS_SUCCEEDED(result
) && !search
.IsEmpty()) {
511 aSearch
.Assign(char16_t('?'));
512 AppendUTF8toUTF16(search
, aSearch
);
517 void Location::SetSearch(const nsAString
& aSearch
,
518 nsIPrincipal
& aSubjectPrincipal
, ErrorResult
& aRv
) {
519 if (!CallerSubsumes(&aSubjectPrincipal
)) {
520 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
524 nsCOMPtr
<nsIURI
> uri
;
525 aRv
= GetURI(getter_AddRefs(uri
));
526 nsCOMPtr
<nsIURL
> url(do_QueryInterface(uri
));
527 if (NS_WARN_IF(aRv
.Failed()) || !url
) {
532 NS_MutateURI(uri
).SetQuery(NS_ConvertUTF16toUTF8(aSearch
)).Finalize(uri
);
533 if (NS_WARN_IF(aRv
.Failed())) {
537 SetURI(uri
, aSubjectPrincipal
, aRv
);
540 void Location::Reload(bool aForceget
, nsIPrincipal
& aSubjectPrincipal
,
542 if (!CallerSubsumes(&aSubjectPrincipal
)) {
543 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
547 RefPtr
<nsDocShell
> docShell(nsDocShell::Cast(GetDocShell()));
549 return aRv
.Throw(NS_ERROR_FAILURE
);
552 if (StaticPrefs::dom_block_reload_from_resize_event_handler()) {
553 nsCOMPtr
<nsPIDOMWindowOuter
> window
= docShell
->GetWindow();
554 if (window
&& window
->IsHandlingResizeEvent()) {
555 // location.reload() was called on a window that is handling a
556 // resize event. Sites do this since Netscape 4.x needed it, but
557 // we don't, and it's a horrible experience for nothing. In stead
558 // of reloading the page, just clear style data and reflow the
559 // page since some sites may use this trick to work around gecko
560 // reflow bugs, and this should have the same effect.
561 RefPtr
<Document
> doc
= window
->GetExtantDoc();
564 if (doc
&& (pcx
= doc
->GetPresContext())) {
565 pcx
->RebuildAllStyleData(NS_STYLE_HINT_REFLOW
,
566 RestyleHint::RestyleSubtree());
572 RefPtr
<BrowsingContext
> bc
= GetBrowsingContext();
573 if (!bc
|| bc
->IsDiscarded()) {
577 CallerType callerType
= aSubjectPrincipal
.IsSystemPrincipal()
579 : CallerType::NonSystem
;
581 nsresult rv
= bc
->CheckLocationChangeRateLimit(callerType
);
587 uint32_t reloadFlags
= nsIWebNavigation::LOAD_FLAGS_NONE
;
590 reloadFlags
= nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE
|
591 nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY
;
594 rv
= docShell
->Reload(reloadFlags
);
595 if (NS_FAILED(rv
) && rv
!= NS_BINDING_ABORTED
) {
596 // NS_BINDING_ABORTED is returned when we attempt to reload a POST result
597 // and the user says no at the "do you want to reload?" prompt. Don't
598 // propagate this one back to callers.
599 return aRv
.Throw(rv
);
603 void Location::Assign(const nsAString
& aUrl
, nsIPrincipal
& aSubjectPrincipal
,
605 if (!CallerSubsumes(&aSubjectPrincipal
)) {
606 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
610 DoSetHref(aUrl
, aSubjectPrincipal
, false, aRv
);
613 bool Location::CallerSubsumes(nsIPrincipal
* aSubjectPrincipal
) {
614 MOZ_ASSERT(aSubjectPrincipal
);
616 BrowsingContext
* bc
= GetBrowsingContext();
617 if (MOZ_UNLIKELY(!bc
) || MOZ_UNLIKELY(bc
->IsDiscarded())) {
618 // Per spec, operations on a Location object with a discarded BC are no-ops,
619 // not security errors, so we need to return true from the access check and
620 // let the caller do its own discarded docShell check.
623 if (MOZ_UNLIKELY(!bc
->IsInProcess())) {
627 // Get the principal associated with the location object. Note that this is
628 // the principal of the page which will actually be navigated, not the
629 // principal of the Location object itself. This is why we need this check
630 // even though we only allow limited cross-origin access to Location objects
632 nsPIDOMWindowOuter
* outer
= bc
->GetDOMWindow();
633 MOZ_DIAGNOSTIC_ASSERT(outer
);
634 if (MOZ_UNLIKELY(!outer
)) return false;
636 nsIScriptObjectPrincipal
* sop
= nsGlobalWindowOuter::Cast(outer
);
637 bool subsumes
= false;
638 nsresult rv
= aSubjectPrincipal
->SubsumesConsideringDomain(
639 sop
->GetPrincipal(), &subsumes
);
640 NS_ENSURE_SUCCESS(rv
, false);
644 JSObject
* Location::WrapObject(JSContext
* aCx
,
645 JS::Handle
<JSObject
*> aGivenProto
) {
646 return Location_Binding::Wrap(aCx
, this, aGivenProto
);
649 void Location::ClearCachedValues() { mCachedHash
= VoidString(); }
651 } // namespace mozilla::dom