gecko-dev/dom/base/Location.cpp

853 строки
24 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "nsIScriptSecurityManager.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsIScriptContext.h"
#include "nsIDocShell.h"
#include "nsDocShellLoadState.h"
#include "nsIWebNavigation.h"
#include "nsCDefaultURIFixup.h"
#include "nsIURIFixup.h"
#include "nsIURL.h"
#include "nsIURIMutator.h"
#include "nsIJARURI.h"
#include "nsNetUtil.h"
#include "nsCOMPtr.h"
#include "nsEscape.h"
#include "nsIDOMWindow.h"
#include "nsIDocument.h"
#include "nsIPresShell.h"
#include "nsPresContext.h"
#include "nsError.h"
#include "nsReadableUtils.h"
#include "nsITextToSubURI.h"
#include "nsJSUtils.h"
#include "nsContentUtils.h"
#include "nsGlobalWindow.h"
#include "mozilla/Likely.h"
#include "nsCycleCollectionParticipant.h"
#include "mozilla/NullPrincipal.h"
#include "mozilla/Unused.h"
#include "mozilla/dom/LocationBinding.h"
#include "mozilla/dom/ScriptSettings.h"
namespace mozilla {
namespace dom {
Location::Location(nsPIDOMWindowInner* aWindow, nsIDocShell* aDocShell)
: mInnerWindow(aWindow) {
// aDocShell can be null if it gets called after nsDocShell::Destory().
mDocShell = do_GetWeakReference(aDocShell);
}
Location::~Location() {}
// 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)
NS_IMPL_CYCLE_COLLECTING_ADDREF(Location)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Location)
already_AddRefed<nsDocShellLoadState> Location::CheckURL(
nsIURI* aURI, nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
if (NS_WARN_IF(!docShell)) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
nsCOMPtr<nsIPrincipal> triggeringPrincipal;
nsCOMPtr<nsIURI> sourceURI;
net::ReferrerPolicy referrerPolicy = net::RP_Unset;
// Get security manager.
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
if (NS_WARN_IF(!ssm)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
// Check to see if URI is allowed.
nsresult rv = ssm->CheckLoadURIWithPrincipal(
&aSubjectPrincipal, aURI, nsIScriptSecurityManager::STANDARD);
if (NS_WARN_IF(NS_FAILED(rv))) {
nsAutoCString spec;
aURI->GetSpec(spec);
aRv.ThrowTypeError<MSG_URL_NOT_LOADABLE>(NS_ConvertUTF8toUTF16(spec));
return nullptr;
}
// Make the load's referrer reflect changes to the document's URI caused by
// push/replaceState, if possible. First, get the document corresponding to
// fp. If the document's original URI (i.e. its URI before
// push/replaceState) matches the principal's URI, use the document's
// current URI as the referrer. If they don't match, use the principal's
// URI.
//
// The triggering principal for this load should be the principal of the
// incumbent document (which matches where the referrer information is
// coming from) when there is an incumbent document, and the subject
// principal otherwise. Note that the URI in the triggering principal
// may not match the referrer URI in various cases, notably including
// the cases when the incumbent document's document URI was modified
// after the document was loaded.
nsCOMPtr<nsPIDOMWindowInner> incumbent =
do_QueryInterface(mozilla::dom::GetIncumbentGlobal());
nsCOMPtr<nsIDocument> doc = incumbent ? incumbent->GetDoc() : nullptr;
if (doc) {
nsCOMPtr<nsIURI> docOriginalURI, docCurrentURI, principalURI;
docOriginalURI = doc->GetOriginalURI();
docCurrentURI = doc->GetDocumentURI();
rv = doc->NodePrincipal()->GetURI(getter_AddRefs(principalURI));
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return nullptr;
}
triggeringPrincipal = doc->NodePrincipal();
referrerPolicy = doc->GetReferrerPolicy();
bool urisEqual = false;
if (docOriginalURI && docCurrentURI && principalURI) {
principalURI->Equals(docOriginalURI, &urisEqual);
}
if (urisEqual) {
sourceURI = docCurrentURI;
} else {
// Use principalURI as long as it is not an NullPrincipalURI. We
// could add a method such as GetReferrerURI to principals to make this
// cleaner, but given that we need to start using Source Browsing
// Context for referrer (see Bug 960639) this may be wasted effort at
// this stage.
if (principalURI) {
bool isNullPrincipalScheme;
rv = principalURI->SchemeIs(NS_NULLPRINCIPAL_SCHEME,
&isNullPrincipalScheme);
if (NS_SUCCEEDED(rv) && !isNullPrincipalScheme) {
sourceURI = principalURI;
}
}
}
} else {
// No document; just use our subject principal as the triggering principal.
triggeringPrincipal = &aSubjectPrincipal;
}
// Create load info
RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
loadState->SetTriggeringPrincipal(triggeringPrincipal);
if (sourceURI) {
loadState->SetReferrer(sourceURI);
loadState->SetReferrerPolicy(referrerPolicy);
}
return loadState.forget();
}
nsresult Location::GetURI(nsIURI** aURI, bool aGetInnermostURI) {
*aURI = nullptr;
nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
if (!docShell) {
return NS_OK;
}
nsresult rv;
nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(docShell, &rv));
if (NS_FAILED(rv)) {
return rv;
}
nsCOMPtr<nsIURI> uri;
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?");
nsCOMPtr<nsIURIFixup> urifixup(do_GetService(NS_URIFIXUP_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
return urifixup->CreateExposableURI(uri, aURI);
}
void Location::SetURI(nsIURI* aURI, nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv, bool aReplace) {
nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
if (docShell) {
RefPtr<nsDocShellLoadState> loadState =
CheckURL(aURI, aSubjectPrincipal, aRv);
if (aRv.Failed()) {
return;
}
if (aReplace) {
loadState->SetLoadType(LOAD_STOP_CONTENT_AND_REPLACE);
} else {
loadState->SetLoadType(LOAD_STOP_CONTENT);
}
// Get the incumbent script's browsing context to set as source.
nsCOMPtr<nsPIDOMWindowInner> sourceWindow =
do_QueryInterface(mozilla::dom::GetIncumbentGlobal());
if (sourceWindow) {
loadState->SetSourceDocShell(sourceWindow->GetDocShell());
}
loadState->SetLoadFlags(nsIWebNavigation::LOAD_FLAGS_NONE);
loadState->SetFirstParty(true);
nsresult rv = docShell->LoadURI(loadState);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
}
}
}
void Location::GetHash(nsAString& aHash, nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv) {
if (!CallerSubsumes(&aSubjectPrincipal)) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return;
}
aHash.SetLength(0);
nsCOMPtr<nsIURI> uri;
aRv = GetURI(getter_AddRefs(uri));
if (NS_WARN_IF(aRv.Failed()) || !uri) {
return;
}
nsAutoCString ref;
nsAutoString unicodeRef;
aRv = uri->GetRef(ref);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
if (!ref.IsEmpty()) {
aHash.Assign(char16_t('#'));
AppendUTF8toUTF16(ref, aHash);
}
if (aHash == mCachedHash) {
// Work around ShareThis stupidly polling location.hash every
// 5ms all the time by handing out the same exact string buffer
// we handed out last time.
aHash = mCachedHash;
} else {
mCachedHash = aHash;
}
}
void Location::SetHash(const nsAString& aHash, nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv) {
if (!CallerSubsumes(&aSubjectPrincipal)) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return;
}
NS_ConvertUTF16toUTF8 hash(aHash);
if (hash.IsEmpty() || hash.First() != char16_t('#')) {
hash.Insert(char16_t('#'), 0);
}
nsCOMPtr<nsIURI> uri;
aRv = GetURI(getter_AddRefs(uri));
if (NS_WARN_IF(aRv.Failed()) || !uri) {
return;
}
aRv = NS_MutateURI(uri).SetRef(hash).Finalize(uri);
if (NS_WARN_IF(aRv.Failed()) || !uri) {
return;
}
SetURI(uri, aSubjectPrincipal, aRv);
}
void Location::GetHost(nsAString& aHost, nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv) {
if (!CallerSubsumes(&aSubjectPrincipal)) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return;
}
aHost.Truncate();
nsCOMPtr<nsIURI> uri;
nsresult result;
result = GetURI(getter_AddRefs(uri), true);
if (uri) {
nsAutoCString hostport;
result = uri->GetHostPort(hostport);
if (NS_SUCCEEDED(result)) {
AppendUTF8toUTF16(hostport, aHost);
}
}
}
void Location::SetHost(const nsAString& 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(NS_ConvertUTF16toUTF8(aHost)).Finalize(uri);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
SetURI(uri, aSubjectPrincipal, aRv);
}
void Location::GetHostname(nsAString& 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 nsAString& 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(NS_ConvertUTF16toUTF8(aHostname)).Finalize(uri);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
SetURI(uri, aSubjectPrincipal, aRv);
}
nsresult Location::GetHref(nsAString& aHref) {
aHref.Truncate();
nsCOMPtr<nsIURI> uri;
nsresult rv = GetURI(getter_AddRefs(uri));
if (NS_WARN_IF(NS_FAILED(rv)) || !uri) {
return rv;
}
nsAutoCString uriString;
rv = uri->GetSpec(uriString);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
AppendUTF8toUTF16(uriString, aHref);
return NS_OK;
}
void Location::SetHref(const nsAString& aHref, nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv) {
DoSetHref(aHref, aSubjectPrincipal, false, aRv);
}
void Location::DoSetHref(const nsAString& aHref,
nsIPrincipal& aSubjectPrincipal, bool aReplace,
ErrorResult& aRv) {
// Get the source of the caller
nsCOMPtr<nsIURI> base = GetSourceBaseURL();
SetHrefWithBase(aHref, base, aSubjectPrincipal, aReplace, aRv);
}
void Location::SetHrefWithBase(const nsAString& aHref, nsIURI* aBase,
nsIPrincipal& aSubjectPrincipal, bool aReplace,
ErrorResult& aRv) {
nsresult result;
nsCOMPtr<nsIURI> newUri;
nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
if (nsIDocument* doc = GetEntryDocument()) {
result = NS_NewURI(getter_AddRefs(newUri), aHref,
doc->GetDocumentCharacterSet(), aBase);
} else {
result = NS_NewURI(getter_AddRefs(newUri), aHref, nullptr, aBase);
}
if (newUri) {
/* Check with the scriptContext if it is currently processing a script tag.
* If so, this must be a <script> tag with a location.href in it.
* we want to do a replace load, in such a situation.
* In other cases, for example if a event handler or a JS timer
* had a location.href in it, we want to do a normal load,
* so that the new url will be appended to Session History.
* This solution is tricky. Hopefully it isn't going to bite
* anywhere else. This is part of solution for bug # 39938, 72197
*/
bool inScriptTag = false;
nsIScriptContext* scriptContext = nullptr;
nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(GetEntryGlobal());
if (win) {
scriptContext = nsGlobalWindowInner::Cast(win)->GetContextInternal();
}
if (scriptContext) {
if (scriptContext->GetProcessingScriptTag()) {
// Now check to make sure that the script is running in our window,
// since we only want to replace if the location is set by a
// <script> tag in the same window. See bug 178729.
nsCOMPtr<nsIScriptGlobalObject> ourGlobal =
docShell ? docShell->GetScriptGlobalObject() : nullptr;
inScriptTag = (ourGlobal == scriptContext->GetGlobalObject());
}
}
SetURI(newUri, aSubjectPrincipal, aRv, aReplace || inScriptTag);
return;
}
aRv.Throw(result);
}
void Location::GetOrigin(nsAString& 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;
}
nsAutoString origin;
aRv = nsContentUtils::GetUTFOrigin(uri, origin);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
aOrigin = origin;
}
void Location::GetPathname(nsAString& 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;
}
nsAutoCString file;
aRv = uri->GetFilePath(file);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
AppendUTF8toUTF16(file, aPathname);
}
void Location::SetPathname(const nsAString& 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(NS_ConvertUTF16toUTF8(aPathname))
.Finalize(uri);
if (NS_FAILED(rv)) {
return;
}
SetURI(uri, aSubjectPrincipal, aRv);
}
void Location::GetPort(nsAString& 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) {
nsAutoString portStr;
portStr.AppendInt(port);
aPort.Append(portStr);
}
}
void Location::SetPort(const nsAString& 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?
NS_ConvertUTF16toUTF8 portStr(aPort);
const char* buf = portStr.get();
int32_t port = -1;
if (!portStr.IsEmpty() && buf) {
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;
}
SetURI(uri, aSubjectPrincipal, aRv);
}
void Location::GetProtocol(nsAString& 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;
}
nsAutoCString protocol;
aRv = uri->GetScheme(protocol);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
CopyASCIItoUTF16(protocol, aProtocol);
aProtocol.Append(char16_t(':'));
}
void Location::SetProtocol(const nsAString& 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;
}
nsAString::const_iterator start, end;
aProtocol.BeginReading(start);
aProtocol.EndReading(end);
nsAString::const_iterator iter(start);
Unused << FindCharInReadable(':', iter, end);
nsresult rv = NS_MutateURI(uri)
.SetScheme(NS_ConvertUTF16toUTF8(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;
}
bool isHttp;
aRv = uri->SchemeIs("http", &isHttp);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
bool isHttps;
aRv = uri->SchemeIs("https", &isHttps);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
if (!isHttp && !isHttps) {
// No-op, per spec.
return;
}
SetURI(uri, aSubjectPrincipal, aRv);
}
void Location::GetSearch(nsAString& aSearch, nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv) {
if (!CallerSubsumes(&aSubjectPrincipal)) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return;
}
aSearch.SetLength(0);
nsCOMPtr<nsIURI> uri;
nsresult result = NS_OK;
result = GetURI(getter_AddRefs(uri));
nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
if (url) {
nsAutoCString search;
result = url->GetQuery(search);
if (NS_SUCCEEDED(result) && !search.IsEmpty()) {
aSearch.Assign(char16_t('?'));
AppendUTF8toUTF16(search, aSearch);
}
}
}
void Location::SetSearch(const nsAString& aSearch,
nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
if (!CallerSubsumes(&aSubjectPrincipal)) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return;
}
nsCOMPtr<nsIURI> uri;
aRv = GetURI(getter_AddRefs(uri));
nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
if (NS_WARN_IF(aRv.Failed()) || !url) {
return;
}
if (nsIDocument* doc = GetEntryDocument()) {
aRv = NS_MutateURI(uri)
.SetQueryWithEncoding(NS_ConvertUTF16toUTF8(aSearch),
doc->GetDocumentCharacterSet())
.Finalize(uri);
} else {
aRv = NS_MutateURI(uri)
.SetQuery(NS_ConvertUTF16toUTF8(aSearch))
.Finalize(uri);
}
if (NS_WARN_IF(aRv.Failed())) {
return;
}
SetURI(uri, aSubjectPrincipal, aRv);
}
nsresult Location::Reload(bool aForceget) {
nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(docShell));
nsCOMPtr<nsPIDOMWindowOuter> window =
docShell ? docShell->GetWindow() : nullptr;
if (window && window->IsHandlingResizeEvent()) {
// location.reload() was called on a window that is handling a
// resize event. Sites do this since Netscape 4.x needed it, but
// we don't, and it's a horrible experience for nothing. In stead
// of reloading the page, just clear style data and reflow the
// page since some sites may use this trick to work around gecko
// reflow bugs, and this should have the same effect.
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
nsPresContext* pcx;
if (doc && (pcx = doc->GetPresContext())) {
pcx->RebuildAllStyleData(NS_STYLE_HINT_REFLOW, eRestyle_Subtree);
}
return NS_OK;
}
if (!webNav) {
return NS_ERROR_FAILURE;
}
uint32_t reloadFlags = nsIWebNavigation::LOAD_FLAGS_NONE;
if (aForceget) {
reloadFlags = nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE |
nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY;
}
nsresult rv = webNav->Reload(reloadFlags);
if (rv == NS_BINDING_ABORTED) {
// This happens 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.
rv = NS_OK;
}
return rv;
}
void Location::Replace(const nsAString& aUrl, nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv) {
DoSetHref(aUrl, aSubjectPrincipal, true, aRv);
}
void Location::Assign(const nsAString& aUrl, nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv) {
if (!CallerSubsumes(&aSubjectPrincipal)) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return;
}
DoSetHref(aUrl, aSubjectPrincipal, false, aRv);
}
already_AddRefed<nsIURI> Location::GetSourceBaseURL() {
nsIDocument* doc = GetEntryDocument();
// If there's no entry document, we either have no Script Entry Point or one
// that isn't a DOM Window. This doesn't generally happen with the DOM, but
// can sometimes happen with extension code in certain IPC configurations. If
// this happens, try falling back on the current document associated with the
// docshell. If that fails, just return null and hope that the caller passed
// an absolute URI.
nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
if (!doc && docShell) {
nsCOMPtr<nsPIDOMWindowOuter> docShellWin =
do_QueryInterface(docShell->GetScriptGlobalObject());
if (docShellWin) {
doc = docShellWin->GetDoc();
}
}
NS_ENSURE_TRUE(doc, nullptr);
return doc->GetBaseURI();
}
bool Location::CallerSubsumes(nsIPrincipal* aSubjectPrincipal) {
MOZ_ASSERT(aSubjectPrincipal);
// 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.
nsCOMPtr<nsPIDOMWindowOuter> outer = mInnerWindow->GetOuterWindow();
if (MOZ_UNLIKELY(!outer)) return false;
nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(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);
}
} // namespace dom
} // namespace mozilla