DOMStringList::GetParentObject() returned nullptr, so FindAssociatedGlobal in BindingUtils.h:1845 fell back to JS::CurrentGlobalOrNull(cx), stamping the JS wrapper into the first caller's compartment. If an extension content script got there first, subsequent page access required a content→extension CCW, which security policy blocks, breaking sites like Gmail. Fix by passing the Document (or inner window) as the DOMStringList parent, so FindAssociatedGlobal always anchors the wrapper to the content realm — the same mechanism that makes things like nsContentList immune to this problem. Differential Revision: https://phabricator.services.mozilla.com/D296198
674 lines
17 KiB
C++
674 lines
17 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "Location.h"
|
|
|
|
#include "ReferrerInfo.h"
|
|
#include "mozilla/BasePrincipal.h"
|
|
#include "mozilla/Components.h"
|
|
#include "mozilla/Likely.h"
|
|
#include "mozilla/NullPrincipal.h"
|
|
#include "mozilla/ServoStyleConsts.h"
|
|
#include "mozilla/StaticPrefs_dom.h"
|
|
#include "mozilla/dom/Document.h"
|
|
#include "mozilla/dom/DocumentInlines.h"
|
|
#include "mozilla/dom/FragmentDirective.h"
|
|
#include "mozilla/dom/LocationBinding.h"
|
|
#include "mozilla/dom/ScriptSettings.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsCycleCollectionParticipant.h"
|
|
#include "nsDocShell.h"
|
|
#include "nsDocShellLoadState.h"
|
|
#include "nsError.h"
|
|
#include "nsEscape.h"
|
|
#include "nsGlobalWindowOuter.h"
|
|
#include "nsIJARURI.h"
|
|
#include "nsIOService.h"
|
|
#include "nsIScriptContext.h"
|
|
#include "nsIScriptObjectPrincipal.h"
|
|
#include "nsIURIMutator.h"
|
|
#include "nsIWebNavigation.h"
|
|
#include "nsJSUtils.h"
|
|
#include "nsNetUtil.h"
|
|
#include "nsPIDOMWindowInlines.h"
|
|
#include "nsPresContext.h"
|
|
#include "nsReadableUtils.h"
|
|
|
|
namespace mozilla::dom {
|
|
|
|
nsTArray<nsString> ProduceAncestorOriginsList(
|
|
const nsTArray<nsCOMPtr<nsIPrincipal>>& aPrincipals) {
|
|
nsTArray<nsString> result;
|
|
|
|
for (const auto& principal : aPrincipals) {
|
|
nsString origin;
|
|
if (principal == nullptr) {
|
|
origin.AssignLiteral(u"null");
|
|
} else {
|
|
nsAutoCString originNoSuffix;
|
|
if (NS_WARN_IF(NS_FAILED(principal->GetOriginNoSuffix(originNoSuffix)))) {
|
|
origin.AssignLiteral(u"null");
|
|
} else {
|
|
CopyUTF8toUTF16(originNoSuffix, origin);
|
|
}
|
|
}
|
|
result.AppendElement(std::move(origin));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
Location::Location(nsPIDOMWindowInner* aWindow)
|
|
: mCachedHash(VoidCString()), mInnerWindow(aWindow) {
|
|
BrowsingContext* bc = GetBrowsingContext();
|
|
if (bc) {
|
|
bc->LocationCreated(this);
|
|
}
|
|
}
|
|
|
|
Location::~Location() {
|
|
if (isInList()) {
|
|
remove();
|
|
}
|
|
}
|
|
|
|
// QueryInterface implementation for Location
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Location)
|
|
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Location, mInnerWindow,
|
|
mRelevantDocNullAncestorOriginsList)
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(Location)
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(Location)
|
|
|
|
BrowsingContext* Location::GetBrowsingContext() {
|
|
return mInnerWindow ? mInnerWindow->GetBrowsingContext() : nullptr;
|
|
}
|
|
|
|
nsIDocShell* Location::GetDocShell() {
|
|
if (BrowsingContext* bc = GetBrowsingContext()) {
|
|
return bc->GetDocShell();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
nsresult Location::GetURI(nsIURI** aURI, bool aGetInnermostURI) {
|
|
*aURI = nullptr;
|
|
|
|
nsIDocShell* docShell = GetDocShell();
|
|
if (!docShell) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsIWebNavigation* webNav = nsDocShell::Cast(docShell);
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
nsresult rv = webNav->GetCurrentURI(getter_AddRefs(uri));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// It is valid for docshell to return a null URI. Don't try to fixup
|
|
// if this happens.
|
|
if (!uri) {
|
|
return NS_OK;
|
|
}
|
|
|
|
if (aGetInnermostURI) {
|
|
nsCOMPtr<nsIJARURI> jarURI(do_QueryInterface(uri));
|
|
while (jarURI) {
|
|
jarURI->GetJARFile(getter_AddRefs(uri));
|
|
jarURI = do_QueryInterface(uri);
|
|
}
|
|
}
|
|
|
|
NS_ASSERTION(uri, "nsJARURI screwed up?");
|
|
|
|
// Remove the fragment directive from the url hash.
|
|
FragmentDirective::ParseAndRemoveFragmentDirectiveFromFragment(uri);
|
|
nsCOMPtr<nsIURI> exposableURI = net::nsIOService::CreateExposableURI(uri);
|
|
exposableURI.forget(aURI);
|
|
return NS_OK;
|
|
}
|
|
|
|
void Location::GetHash(nsACString& aHash, nsIPrincipal& aSubjectPrincipal,
|
|
ErrorResult& aRv) {
|
|
if (!CallerSubsumes(&aSubjectPrincipal)) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return;
|
|
}
|
|
|
|
if (!mCachedHash.IsVoid()) {
|
|
aHash = mCachedHash;
|
|
return;
|
|
}
|
|
|
|
aHash.SetLength(0);
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
aRv = GetURI(getter_AddRefs(uri));
|
|
if (NS_WARN_IF(aRv.Failed()) || !uri) {
|
|
return;
|
|
}
|
|
|
|
nsAutoCString ref;
|
|
|
|
aRv = uri->GetRef(ref);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return;
|
|
}
|
|
|
|
if (!ref.IsEmpty()) {
|
|
aHash.SetCapacity(ref.Length() + 1);
|
|
aHash.Assign('#');
|
|
aHash.Append(ref);
|
|
}
|
|
|
|
mCachedHash = aHash;
|
|
}
|
|
|
|
void Location::SetHash(const nsACString& aHash, nsIPrincipal& aSubjectPrincipal,
|
|
ErrorResult& aRv) {
|
|
if (!CallerSubsumes(&aSubjectPrincipal)) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
aRv = GetURI(getter_AddRefs(uri));
|
|
if (NS_WARN_IF(aRv.Failed()) || !uri) {
|
|
return;
|
|
}
|
|
|
|
nsAutoCString currentHash;
|
|
aRv = uri->GetRef(currentHash);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return;
|
|
}
|
|
|
|
if (aHash.IsEmpty() || aHash.First() != '#') {
|
|
aRv = NS_MutateURI(uri).SetRef("#"_ns + aHash).Finalize(uri);
|
|
} else {
|
|
aRv = NS_MutateURI(uri).SetRef(aHash).Finalize(uri);
|
|
}
|
|
if (NS_WARN_IF(aRv.Failed()) || !uri) {
|
|
return;
|
|
}
|
|
|
|
// If the new hash is the same as the current hash, then return without
|
|
// navigating to the anchor again. This bailout is necessary for
|
|
// compatibility with deployed content, which redundantly sets
|
|
// location.hash on scroll. https://github.com/whatwg/html/issues/7386
|
|
nsAutoCString newHash;
|
|
aRv = uri->GetRef(newHash);
|
|
if (NS_WARN_IF(aRv.Failed()) || newHash == currentHash) {
|
|
return;
|
|
}
|
|
|
|
Navigate(uri, aSubjectPrincipal, aRv);
|
|
}
|
|
|
|
// https://html.spec.whatwg.org/#dom-location-ancestororigins
|
|
RefPtr<DOMStringList> Location::GetAncestorOrigins(
|
|
nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
|
|
Document* doc = mInnerWindow->GetExtantDoc();
|
|
// Step 1. If this's relevant Document is null, then return an empty list.
|
|
if (!doc || !doc->IsActive()) {
|
|
if (!mRelevantDocNullAncestorOriginsList) {
|
|
mRelevantDocNullAncestorOriginsList =
|
|
MakeRefPtr<DOMStringList>(mInnerWindow);
|
|
}
|
|
return mRelevantDocNullAncestorOriginsList;
|
|
}
|
|
|
|
// Step 2. If this's relevant Document's origin is not same origin-domain with
|
|
// the entry settings object's origin, then throw a "SecurityError"
|
|
// DOMException.
|
|
if (!CallerSubsumes(&aSubjectPrincipal)) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
// Step 3. Otherwise, return this's ancestor origins list.
|
|
return doc->AncestorOrigins();
|
|
}
|
|
|
|
void Location::GetHost(nsACString& aHost, nsIPrincipal& aSubjectPrincipal,
|
|
ErrorResult& aRv) {
|
|
if (!CallerSubsumes(&aSubjectPrincipal)) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return;
|
|
}
|
|
|
|
aHost.Truncate();
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
(void)GetURI(getter_AddRefs(uri), true);
|
|
|
|
if (uri) {
|
|
(void)uri->GetHostPort(aHost);
|
|
}
|
|
}
|
|
|
|
void Location::SetHost(const nsACString& aHost, nsIPrincipal& aSubjectPrincipal,
|
|
ErrorResult& aRv) {
|
|
if (!CallerSubsumes(&aSubjectPrincipal)) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
aRv = GetURI(getter_AddRefs(uri));
|
|
if (NS_WARN_IF(aRv.Failed()) || !uri) {
|
|
return;
|
|
}
|
|
|
|
aRv = NS_MutateURI(uri).SetHostPort(aHost).Finalize(uri);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return;
|
|
}
|
|
|
|
Navigate(uri, aSubjectPrincipal, aRv);
|
|
}
|
|
|
|
void Location::GetHostname(nsACString& aHostname,
|
|
nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
|
|
if (!CallerSubsumes(&aSubjectPrincipal)) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return;
|
|
}
|
|
|
|
aHostname.Truncate();
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
GetURI(getter_AddRefs(uri), true);
|
|
if (uri) {
|
|
nsContentUtils::GetHostOrIPv6WithBrackets(uri, aHostname);
|
|
}
|
|
}
|
|
|
|
void Location::SetHostname(const nsACString& aHostname,
|
|
nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
|
|
if (!CallerSubsumes(&aSubjectPrincipal)) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
aRv = GetURI(getter_AddRefs(uri));
|
|
if (NS_WARN_IF(aRv.Failed()) || !uri) {
|
|
return;
|
|
}
|
|
|
|
aRv = NS_MutateURI(uri).SetHost(aHostname).Finalize(uri);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return;
|
|
}
|
|
|
|
Navigate(uri, aSubjectPrincipal, aRv);
|
|
}
|
|
|
|
nsresult Location::GetHref(nsACString& aHref) {
|
|
aHref.Truncate();
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
nsresult rv = GetURI(getter_AddRefs(uri));
|
|
if (NS_WARN_IF(NS_FAILED(rv)) || !uri) {
|
|
return rv;
|
|
}
|
|
|
|
rv = uri->GetSpec(aHref);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
void Location::GetOrigin(nsACString& aOrigin, nsIPrincipal& aSubjectPrincipal,
|
|
ErrorResult& aRv) {
|
|
if (!CallerSubsumes(&aSubjectPrincipal)) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return;
|
|
}
|
|
|
|
aOrigin.Truncate();
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
aRv = GetURI(getter_AddRefs(uri), true);
|
|
if (NS_WARN_IF(aRv.Failed()) || !uri) {
|
|
return;
|
|
}
|
|
|
|
aRv = nsContentUtils::GetWebExposedOriginSerialization(uri, aOrigin);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
aOrigin.Truncate();
|
|
return;
|
|
}
|
|
}
|
|
|
|
void Location::GetPathname(nsACString& aPathname,
|
|
nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
|
|
if (!CallerSubsumes(&aSubjectPrincipal)) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return;
|
|
}
|
|
|
|
aPathname.Truncate();
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
aRv = GetURI(getter_AddRefs(uri));
|
|
if (NS_WARN_IF(aRv.Failed()) || !uri) {
|
|
return;
|
|
}
|
|
|
|
aRv = uri->GetFilePath(aPathname);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
void Location::SetPathname(const nsACString& aPathname,
|
|
nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
|
|
if (!CallerSubsumes(&aSubjectPrincipal)) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
aRv = GetURI(getter_AddRefs(uri));
|
|
if (NS_WARN_IF(aRv.Failed()) || !uri) {
|
|
return;
|
|
}
|
|
|
|
nsresult rv = NS_MutateURI(uri).SetFilePath(aPathname).Finalize(uri);
|
|
if (NS_FAILED(rv)) {
|
|
return;
|
|
}
|
|
|
|
Navigate(uri, aSubjectPrincipal, aRv);
|
|
}
|
|
|
|
void Location::GetPort(nsACString& aPort, nsIPrincipal& aSubjectPrincipal,
|
|
ErrorResult& aRv) {
|
|
if (!CallerSubsumes(&aSubjectPrincipal)) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return;
|
|
}
|
|
|
|
aPort.SetLength(0);
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
aRv = GetURI(getter_AddRefs(uri), true);
|
|
if (NS_WARN_IF(aRv.Failed()) || !uri) {
|
|
return;
|
|
}
|
|
|
|
int32_t port;
|
|
nsresult result = uri->GetPort(&port);
|
|
|
|
// Don't propagate this exception to caller
|
|
if (NS_SUCCEEDED(result) && -1 != port) {
|
|
aPort.AppendInt(port);
|
|
}
|
|
}
|
|
|
|
void Location::SetPort(const nsACString& aPort, nsIPrincipal& aSubjectPrincipal,
|
|
ErrorResult& aRv) {
|
|
if (!CallerSubsumes(&aSubjectPrincipal)) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
aRv = GetURI(getter_AddRefs(uri));
|
|
if (NS_WARN_IF(aRv.Failed() || !uri)) {
|
|
return;
|
|
}
|
|
|
|
// perhaps use nsReadingIterators at some point?
|
|
nsAutoCString portStr(aPort);
|
|
const char* buf = portStr.get();
|
|
int32_t port = -1;
|
|
|
|
if (!portStr.IsEmpty() && buf) {
|
|
// Sadly, ToInteger() on nsACString does not have the right semantics.
|
|
if (*buf == ':') {
|
|
port = atol(buf + 1);
|
|
} else {
|
|
port = atol(buf);
|
|
}
|
|
}
|
|
|
|
aRv = NS_MutateURI(uri).SetPort(port).Finalize(uri);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return;
|
|
}
|
|
|
|
Navigate(uri, aSubjectPrincipal, aRv);
|
|
}
|
|
|
|
void Location::GetProtocol(nsACString& aProtocol,
|
|
nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
|
|
if (!CallerSubsumes(&aSubjectPrincipal)) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return;
|
|
}
|
|
|
|
aProtocol.SetLength(0);
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
aRv = GetURI(getter_AddRefs(uri));
|
|
if (NS_WARN_IF(aRv.Failed()) || !uri) {
|
|
return;
|
|
}
|
|
|
|
aRv = uri->GetScheme(aProtocol);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return;
|
|
}
|
|
|
|
aProtocol.Append(':');
|
|
}
|
|
|
|
void Location::SetProtocol(const nsACString& aProtocol,
|
|
nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
|
|
if (!CallerSubsumes(&aSubjectPrincipal)) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
aRv = GetURI(getter_AddRefs(uri));
|
|
if (NS_WARN_IF(aRv.Failed()) || !uri) {
|
|
return;
|
|
}
|
|
|
|
nsACString::const_iterator start, end;
|
|
aProtocol.BeginReading(start);
|
|
aProtocol.EndReading(end);
|
|
nsACString::const_iterator iter(start);
|
|
(void)FindCharInReadable(':', iter, end);
|
|
|
|
nsresult rv =
|
|
NS_MutateURI(uri).SetScheme(Substring(start, iter)).Finalize(uri);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
// Oh, I wish nsStandardURL returned NS_ERROR_MALFORMED_URI for _all_ the
|
|
// malformed cases, not just some of them!
|
|
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
|
return;
|
|
}
|
|
|
|
nsAutoCString newSpec;
|
|
aRv = uri->GetSpec(newSpec);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return;
|
|
}
|
|
// We may want a new URI class for the new URI, so recreate it:
|
|
rv = NS_NewURI(getter_AddRefs(uri), newSpec);
|
|
if (NS_FAILED(rv)) {
|
|
if (rv == NS_ERROR_MALFORMED_URI) {
|
|
rv = NS_ERROR_DOM_SYNTAX_ERR;
|
|
}
|
|
|
|
aRv.Throw(rv);
|
|
return;
|
|
}
|
|
|
|
if (!net::SchemeIsHttpOrHttps(uri)) {
|
|
// No-op, per spec.
|
|
return;
|
|
}
|
|
|
|
Navigate(uri, aSubjectPrincipal, aRv);
|
|
}
|
|
|
|
void Location::GetSearch(nsACString& aSearch, nsIPrincipal& aSubjectPrincipal,
|
|
ErrorResult& aRv) {
|
|
if (!CallerSubsumes(&aSubjectPrincipal)) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return;
|
|
}
|
|
|
|
aSearch.SetLength(0);
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
aRv = GetURI(getter_AddRefs(uri));
|
|
if (NS_WARN_IF(aRv.Failed()) || !uri) {
|
|
return;
|
|
}
|
|
|
|
nsAutoCString search;
|
|
aRv = uri->GetQuery(search);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return;
|
|
}
|
|
|
|
if (!search.IsEmpty()) {
|
|
aSearch.SetCapacity(search.Length() + 1);
|
|
aSearch.Assign('?');
|
|
aSearch.Append(search);
|
|
}
|
|
}
|
|
|
|
void Location::SetSearch(const nsACString& aSearch,
|
|
nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
|
|
if (!CallerSubsumes(&aSubjectPrincipal)) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
aRv = GetURI(getter_AddRefs(uri));
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return;
|
|
}
|
|
|
|
aRv = NS_MutateURI(uri).SetQuery(aSearch).Finalize(uri);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return;
|
|
}
|
|
|
|
Navigate(uri, aSubjectPrincipal, aRv);
|
|
}
|
|
|
|
void Location::Reload(JSContext* aCx, bool aForceget,
|
|
nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
|
|
if (!CallerSubsumes(&aSubjectPrincipal)) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return;
|
|
}
|
|
|
|
RefPtr<nsDocShell> docShell(nsDocShell::Cast(GetDocShell()));
|
|
if (!docShell) {
|
|
return aRv.Throw(NS_ERROR_FAILURE);
|
|
}
|
|
|
|
RefPtr<BrowsingContext> bc = GetBrowsingContext();
|
|
if (!bc || bc->IsDiscarded()) {
|
|
return;
|
|
}
|
|
|
|
CallerType callerType = aSubjectPrincipal.IsSystemPrincipal()
|
|
? CallerType::System
|
|
: CallerType::NonSystem;
|
|
|
|
nsresult rv = bc->CheckNavigationRateLimit(callerType);
|
|
if (NS_FAILED(rv)) {
|
|
aRv.Throw(rv);
|
|
return;
|
|
}
|
|
|
|
uint32_t reloadFlags = nsIWebNavigation::LOAD_FLAGS_NONE;
|
|
|
|
if (aForceget) {
|
|
reloadFlags = nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE |
|
|
nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY;
|
|
}
|
|
|
|
UserNavigationInvolvement userInvolvement =
|
|
callerType == CallerType::System ? UserNavigationInvolvement::BrowserUI
|
|
: UserNavigationInvolvement::None;
|
|
|
|
rv = docShell->ReloadNavigable(Some(WrapNotNull(aCx)), reloadFlags, nullptr,
|
|
userInvolvement);
|
|
if (NS_FAILED(rv) && rv != NS_BINDING_ABORTED) {
|
|
// NS_BINDING_ABORTED is returned when we attempt to reload a POST result
|
|
// and the user says no at the "do you want to reload?" prompt. Don't
|
|
// propagate this one back to callers.
|
|
return aRv.Throw(rv);
|
|
}
|
|
}
|
|
|
|
void Location::Assign(const nsACString& aUrl, nsIPrincipal& aSubjectPrincipal,
|
|
ErrorResult& aRv) {
|
|
if (!CallerSubsumes(&aSubjectPrincipal)) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return;
|
|
}
|
|
|
|
DoSetHref(aUrl, aSubjectPrincipal, false, aRv);
|
|
}
|
|
|
|
bool Location::CallerSubsumes(nsIPrincipal* aSubjectPrincipal) {
|
|
MOZ_ASSERT(aSubjectPrincipal);
|
|
|
|
BrowsingContext* bc = GetBrowsingContext();
|
|
if (MOZ_UNLIKELY(!bc) || MOZ_UNLIKELY(bc->IsDiscarded())) {
|
|
// Per spec, operations on a Location object with a discarded BC are no-ops,
|
|
// not security errors, so we need to return true from the access check and
|
|
// let the caller do its own discarded docShell check.
|
|
return true;
|
|
}
|
|
if (MOZ_UNLIKELY(!bc->IsInProcess())) {
|
|
return false;
|
|
}
|
|
|
|
// Get the principal associated with the location object. Note that this is
|
|
// the principal of the page which will actually be navigated, not the
|
|
// principal of the Location object itself. This is why we need this check
|
|
// even though we only allow limited cross-origin access to Location objects
|
|
// in general.
|
|
nsPIDOMWindowOuter* outer = bc->GetDOMWindow();
|
|
MOZ_DIAGNOSTIC_ASSERT(outer);
|
|
if (MOZ_UNLIKELY(!outer)) return false;
|
|
|
|
nsIScriptObjectPrincipal* sop = nsGlobalWindowOuter::Cast(outer);
|
|
bool subsumes = false;
|
|
nsresult rv = aSubjectPrincipal->SubsumesConsideringDomain(
|
|
sop->GetPrincipal(), &subsumes);
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
return subsumes;
|
|
}
|
|
|
|
JSObject* Location::WrapObject(JSContext* aCx,
|
|
JS::Handle<JSObject*> aGivenProto) {
|
|
return Location_Binding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
void Location::ClearCachedValues() { mCachedHash = VoidCString(); }
|
|
|
|
} // namespace mozilla::dom
|