summaryrefslogtreecommitdiff
path: root/dom/base/Location.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/base/Location.cpp')
-rw-r--r--dom/base/Location.cpp941
1 files changed, 941 insertions, 0 deletions
diff --git a/dom/base/Location.cpp b/dom/base/Location.cpp
new file mode 100644
index 0000000000..e3b614931a
--- /dev/null
+++ b/dom/base/Location.cpp
@@ -0,0 +1,941 @@
+/* -*- 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 "nsIDocShellLoadInfo.h"
+#include "nsIWebNavigation.h"
+#include "nsCDefaultURIFixup.h"
+#include "nsIURIFixup.h"
+#include "nsIURL.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 "nsDOMClassInfoID.h"
+#include "nsReadableUtils.h"
+#include "nsITextToSubURI.h"
+#include "nsJSUtils.h"
+#include "nsContentUtils.h"
+#include "nsGlobalWindow.h"
+#include "mozilla/Likely.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsNullPrincipal.h"
+#include "ScriptSettings.h"
+#include "mozilla/dom/LocationBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+static nsresult
+GetDocumentCharacterSetForURI(const nsAString& aHref, nsACString& aCharset)
+{
+ aCharset.Truncate();
+
+ if (nsIDocument* doc = GetEntryDocument()) {
+ aCharset = doc->GetDocumentCharacterSet();
+ }
+
+ return NS_OK;
+}
+
+Location::Location(nsPIDOMWindowInner* aWindow, nsIDocShell *aDocShell)
+ : mInnerWindow(aWindow)
+{
+ MOZ_ASSERT(aDocShell);
+ MOZ_ASSERT(mInnerWindow->IsInnerWindow());
+
+ 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(nsIDOMLocation)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMLocation)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(Location)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Location)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mInnerWindow);
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Location)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInnerWindow)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Location)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(Location)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(Location)
+
+void
+Location::SetDocShell(nsIDocShell *aDocShell)
+{
+ mDocShell = do_GetWeakReference(aDocShell);
+}
+
+nsIDocShell *
+Location::GetDocShell()
+{
+ nsCOMPtr<nsIDocShell> docshell(do_QueryReferent(mDocShell));
+ return docshell;
+}
+
+nsresult
+Location::CheckURL(nsIURI* aURI, nsIDocShellLoadInfo** aLoadInfo)
+{
+ *aLoadInfo = nullptr;
+
+ nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
+ NS_ENSURE_TRUE(docShell, NS_ERROR_NOT_AVAILABLE);
+
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal;
+ nsCOMPtr<nsIURI> sourceURI;
+ net::ReferrerPolicy referrerPolicy = net::RP_Default;
+
+ if (JSContext *cx = nsContentUtils::GetCurrentJSContext()) {
+ // No cx means that there's no JS running, or at least no JS that
+ // was run through code that properly pushed a context onto the
+ // context stack (as all code that runs JS off of web pages
+ // does). We won't bother with security checks in this case, but
+ // we need to create the loadinfo etc.
+
+ // Get security manager.
+ nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+ NS_ENSURE_STATE(ssm);
+
+ // Check to see if URI is allowed.
+ nsresult rv = ssm->CheckLoadURIFromScript(cx, aURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // 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));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ 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 nsNullPrincipalURI. 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; determine triggeringPrincipal by quering the
+ // subjectPrincipal, wich is the principal of the current JS
+ // compartment, or a null principal in case there is no
+ // compartment yet.
+ triggeringPrincipal = nsContentUtils::SubjectPrincipal();
+ }
+ }
+
+ // Create load info
+ nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
+ docShell->CreateLoadInfo(getter_AddRefs(loadInfo));
+ NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE);
+
+ loadInfo->SetTriggeringPrincipal(triggeringPrincipal);
+
+ if (sourceURI) {
+ loadInfo->SetReferrer(sourceURI);
+ loadInfo->SetReferrerPolicy(referrerPolicy);
+ }
+
+ loadInfo.swap(*aLoadInfo);
+
+ return NS_OK;
+}
+
+nsresult
+Location::GetURI(nsIURI** aURI, bool aGetInnermostURI)
+{
+ *aURI = nullptr;
+
+ nsresult rv;
+ nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
+ 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);
+}
+
+nsresult
+Location::GetWritableURI(nsIURI** aURI, const nsACString* aNewRef)
+{
+ *aURI = nullptr;
+
+ nsCOMPtr<nsIURI> uri;
+
+ nsresult rv = GetURI(getter_AddRefs(uri));
+ if (NS_FAILED(rv) || !uri) {
+ return rv;
+ }
+
+ if (!aNewRef) {
+ return uri->Clone(aURI);
+ }
+
+ return uri->CloneWithNewRef(*aNewRef, aURI);
+}
+
+nsresult
+Location::SetURI(nsIURI* aURI, bool aReplace)
+{
+ nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
+ if (docShell) {
+ nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
+
+ if(NS_FAILED(CheckURL(aURI, getter_AddRefs(loadInfo))))
+ return NS_ERROR_FAILURE;
+
+ if (aReplace) {
+ loadInfo->SetLoadType(nsIDocShellLoadInfo::loadStopContentAndReplace);
+ } else {
+ loadInfo->SetLoadType(nsIDocShellLoadInfo::loadStopContent);
+ }
+
+ // Get the incumbent script's browsing context to set as source.
+ nsCOMPtr<nsPIDOMWindowInner> sourceWindow =
+ do_QueryInterface(mozilla::dom::GetIncumbentGlobal());
+ if (sourceWindow) {
+ loadInfo->SetSourceDocShell(sourceWindow->GetDocShell());
+ }
+
+ return docShell->LoadURI(aURI, loadInfo,
+ nsIWebNavigation::LOAD_FLAGS_NONE, true);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Location::GetHash(nsAString& aHash)
+{
+ aHash.SetLength(0);
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = GetURI(getter_AddRefs(uri));
+ if (NS_FAILED(rv) || !uri) {
+ return rv;
+ }
+
+ nsAutoCString ref;
+ nsAutoString unicodeRef;
+
+ rv = uri->GetRef(ref);
+
+ if (nsContentUtils::GettersDecodeURLHash()) {
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsITextToSubURI> textToSubURI(
+ do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv));
+
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString charset;
+ uri->GetOriginCharset(charset);
+
+ rv = textToSubURI->UnEscapeURIForUI(charset, ref, unicodeRef);
+ }
+
+ if (NS_FAILED(rv)) {
+ // Oh, well. No intl here!
+ NS_UnescapeURL(ref);
+ CopyASCIItoUTF16(ref, unicodeRef);
+ rv = NS_OK;
+ }
+ }
+
+ if (NS_SUCCEEDED(rv) && !unicodeRef.IsEmpty()) {
+ aHash.Assign(char16_t('#'));
+ aHash.Append(unicodeRef);
+ }
+ } else { // URL Hash should simply return the value of the Ref segment
+ if (NS_SUCCEEDED(rv) && !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;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+Location::SetHash(const nsAString& aHash)
+{
+ NS_ConvertUTF16toUTF8 hash(aHash);
+ if (hash.IsEmpty() || hash.First() != char16_t('#')) {
+ hash.Insert(char16_t('#'), 0);
+ }
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = GetWritableURI(getter_AddRefs(uri), &hash);
+ if (NS_FAILED(rv) || !uri) {
+ return rv;
+ }
+
+ return SetURI(uri);
+}
+
+NS_IMETHODIMP
+Location::GetHost(nsAString& aHost)
+{
+ 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);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Location::SetHost(const nsAString& aHost)
+{
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = GetWritableURI(getter_AddRefs(uri));
+ if (NS_WARN_IF(NS_FAILED(rv) || !uri)) {
+ return rv;
+ }
+
+ rv = uri->SetHostPort(NS_ConvertUTF16toUTF8(aHost));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return SetURI(uri);
+}
+
+NS_IMETHODIMP
+Location::GetHostname(nsAString& aHostname)
+{
+ aHostname.Truncate();
+
+ nsCOMPtr<nsIURI> uri;
+ GetURI(getter_AddRefs(uri), true);
+ if (uri) {
+ nsContentUtils::GetHostOrIPv6WithBrackets(uri, aHostname);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Location::SetHostname(const nsAString& aHostname)
+{
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = GetWritableURI(getter_AddRefs(uri));
+ if (NS_WARN_IF(NS_FAILED(rv) || !uri)) {
+ return rv;
+ }
+
+ rv = uri->SetHost(NS_ConvertUTF16toUTF8(aHostname));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return SetURI(uri);
+}
+
+NS_IMETHODIMP
+Location::GetHref(nsAString& aHref)
+{
+ aHref.Truncate();
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult result;
+
+ result = GetURI(getter_AddRefs(uri));
+
+ if (uri) {
+ nsAutoCString uriString;
+
+ result = uri->GetSpec(uriString);
+
+ if (NS_SUCCEEDED(result)) {
+ AppendUTF8toUTF16(uriString, aHref);
+ }
+ }
+
+ return result;
+}
+
+NS_IMETHODIMP
+Location::SetHref(const nsAString& aHref)
+{
+ nsAutoString oldHref;
+ nsresult rv = NS_OK;
+
+ JSContext *cx = nsContentUtils::GetCurrentJSContext();
+ if (cx) {
+ rv = SetHrefWithContext(cx, aHref, false);
+ } else {
+ rv = GetHref(oldHref);
+
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIURI> oldUri;
+
+ rv = NS_NewURI(getter_AddRefs(oldUri), oldHref);
+
+ if (oldUri) {
+ rv = SetHrefWithBase(aHref, oldUri, false);
+ }
+ }
+ }
+
+ return rv;
+}
+
+nsresult
+Location::SetHrefWithContext(JSContext* cx, const nsAString& aHref,
+ bool aReplace)
+{
+ nsCOMPtr<nsIURI> base;
+
+ // Get the source of the caller
+ nsresult result = GetSourceBaseURL(cx, getter_AddRefs(base));
+
+ if (NS_FAILED(result)) {
+ return result;
+ }
+
+ return SetHrefWithBase(aHref, base, aReplace);
+}
+
+nsresult
+Location::SetHrefWithBase(const nsAString& aHref, nsIURI* aBase,
+ bool aReplace)
+{
+ nsresult result;
+ nsCOMPtr<nsIURI> newUri;
+
+ nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
+
+ nsAutoCString docCharset;
+ if (NS_SUCCEEDED(GetDocumentCharacterSetForURI(aHref, docCharset)))
+ result = NS_NewURI(getter_AddRefs(newUri), aHref, docCharset.get(), 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 = nsGlobalWindow::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());
+ }
+ }
+
+ return SetURI(newUri, aReplace || inScriptTag);
+ }
+
+ return result;
+}
+
+NS_IMETHODIMP
+Location::GetOrigin(nsAString& aOrigin)
+{
+ aOrigin.Truncate();
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = GetURI(getter_AddRefs(uri), true);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(uri, NS_OK);
+
+ nsAutoString origin;
+ rv = nsContentUtils::GetUTFOrigin(uri, origin);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aOrigin = origin;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Location::GetPathname(nsAString& aPathname)
+{
+ aPathname.Truncate();
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult result = NS_OK;
+
+ result = GetURI(getter_AddRefs(uri));
+
+ nsCOMPtr<nsIURIWithQuery> url(do_QueryInterface(uri));
+ if (url) {
+ nsAutoCString file;
+
+ result = url->GetFilePath(file);
+
+ if (NS_SUCCEEDED(result)) {
+ AppendUTF8toUTF16(file, aPathname);
+ }
+ }
+
+ return result;
+}
+
+NS_IMETHODIMP
+Location::SetPathname(const nsAString& aPathname)
+{
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = GetWritableURI(getter_AddRefs(uri));
+ if (NS_WARN_IF(NS_FAILED(rv) || !uri)) {
+ return rv;
+ }
+
+ nsCOMPtr<nsIURIWithQuery> url(do_QueryInterface(uri));
+ if (url && NS_SUCCEEDED(url->SetFilePath(NS_ConvertUTF16toUTF8(aPathname)))) {
+ return SetURI(uri);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Location::GetPort(nsAString& aPort)
+{
+ aPort.SetLength(0);
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult result = NS_OK;
+
+ result = GetURI(getter_AddRefs(uri), true);
+
+ if (uri) {
+ int32_t port;
+ result = uri->GetPort(&port);
+
+ if (NS_SUCCEEDED(result) && -1 != port) {
+ nsAutoString portStr;
+ portStr.AppendInt(port);
+ aPort.Append(portStr);
+ }
+
+ // Don't propagate this exception to caller
+ result = NS_OK;
+ }
+
+ return result;
+}
+
+NS_IMETHODIMP
+Location::SetPort(const nsAString& aPort)
+{
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = GetWritableURI(getter_AddRefs(uri));
+ if (NS_WARN_IF(NS_FAILED(rv) || !uri)) {
+ return rv;
+ }
+
+ // 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);
+ }
+ }
+
+ rv = uri->SetPort(port);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return SetURI(uri);
+}
+
+NS_IMETHODIMP
+Location::GetProtocol(nsAString& aProtocol)
+{
+ aProtocol.SetLength(0);
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult result = NS_OK;
+
+ result = GetURI(getter_AddRefs(uri));
+
+ if (uri) {
+ nsAutoCString protocol;
+
+ result = uri->GetScheme(protocol);
+
+ if (NS_SUCCEEDED(result)) {
+ CopyASCIItoUTF16(protocol, aProtocol);
+ aProtocol.Append(char16_t(':'));
+ }
+ }
+
+ return result;
+}
+
+NS_IMETHODIMP
+Location::SetProtocol(const nsAString& aProtocol)
+{
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = GetWritableURI(getter_AddRefs(uri));
+ if (NS_WARN_IF(NS_FAILED(rv) || !uri)) {
+ return rv;
+ }
+
+ rv = uri->SetScheme(NS_ConvertUTF16toUTF8(aProtocol));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ nsAutoCString newSpec;
+ rv = uri->GetSpec(newSpec);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ // 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)) {
+ return rv;
+ }
+
+ return SetURI(uri);
+}
+
+NS_IMETHODIMP
+Location::GetSearch(nsAString& aSearch)
+{
+ 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);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Location::SetSearch(const nsAString& aSearch)
+{
+ nsresult rv = SetSearchInternal(aSearch);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+Location::SetSearchInternal(const nsAString& aSearch)
+{
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = GetWritableURI(getter_AddRefs(uri));
+
+ nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
+ if (NS_WARN_IF(NS_FAILED(rv) || !url)) {
+ return rv;
+ }
+
+ rv = url->SetQuery(NS_ConvertUTF16toUTF8(aSearch));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return SetURI(uri);
+}
+
+NS_IMETHODIMP
+Location::Reload(bool aForceget)
+{
+ nsresult rv;
+ 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();
+
+ nsIPresShell *shell;
+ nsPresContext *pcx;
+ if (doc && (shell = doc->GetShell()) && (pcx = shell->GetPresContext())) {
+ pcx->RebuildAllStyleData(NS_STYLE_HINT_REFLOW, eRestyle_Subtree);
+ }
+
+ return NS_OK;
+ }
+
+ if (webNav) {
+ uint32_t reloadFlags = nsIWebNavigation::LOAD_FLAGS_NONE;
+
+ if (aForceget) {
+ reloadFlags = nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE |
+ nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY;
+ }
+ 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;
+ }
+ } else {
+ rv = NS_ERROR_FAILURE;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+Location::Replace(const nsAString& aUrl)
+{
+ nsresult rv = NS_OK;
+ if (JSContext *cx = nsContentUtils::GetCurrentJSContext()) {
+ return SetHrefWithContext(cx, aUrl, true);
+ }
+
+ nsAutoString oldHref;
+
+ rv = GetHref(oldHref);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIURI> oldUri;
+
+ rv = NS_NewURI(getter_AddRefs(oldUri), oldHref);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return SetHrefWithBase(aUrl, oldUri, true);
+}
+
+NS_IMETHODIMP
+Location::Assign(const nsAString& aUrl)
+{
+ if (JSContext *cx = nsContentUtils::GetCurrentJSContext()) {
+ return SetHrefWithContext(cx, aUrl, false);
+ }
+
+ nsAutoString oldHref;
+ nsresult result = NS_OK;
+
+ result = GetHref(oldHref);
+
+ if (NS_SUCCEEDED(result)) {
+ nsCOMPtr<nsIURI> oldUri;
+
+ result = NS_NewURI(getter_AddRefs(oldUri), oldHref);
+
+ if (oldUri) {
+ result = SetHrefWithBase(aUrl, oldUri, false);
+ }
+ }
+
+ return result;
+}
+
+NS_IMETHODIMP
+Location::ToString(nsAString& aReturn)
+{
+ return GetHref(aReturn);
+}
+
+NS_IMETHODIMP
+Location::ValueOf(nsIDOMLocation** aReturn)
+{
+ nsCOMPtr<nsIDOMLocation> loc(this);
+ loc.forget(aReturn);
+ return NS_OK;
+}
+
+nsresult
+Location::GetSourceBaseURL(JSContext* cx, nsIURI** sourceURL)
+{
+ *sourceURL = nullptr;
+ 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.
+ if (!doc && GetDocShell()) {
+ nsCOMPtr<nsPIDOMWindowOuter> docShellWin =
+ do_QueryInterface(GetDocShell()->GetScriptGlobalObject());
+ if (docShellWin) {
+ doc = docShellWin->GetDoc();
+ }
+ }
+ NS_ENSURE_TRUE(doc, NS_OK);
+ *sourceURL = doc->GetBaseURI().take();
+ return NS_OK;
+}
+
+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 LocationBinding::Wrap(aCx, this, aGivenProto);
+}
+
+} // dom namespace
+} // mozilla namespace