diff -r 000000000000 -r 6474c204b198 dom/base/nsLocation.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dom/base/nsLocation.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,927 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 sw=2 et 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 "nsLocation.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 "mozilla/Likely.h" +#include "nsCycleCollectionParticipant.h" +#include "nsNullPrincipal.h" +#include "ScriptSettings.h" + +static nsresult +GetDocumentCharacterSetForURI(const nsAString& aHref, nsACString& aCharset) +{ + aCharset.Truncate(); + + JSContext *cx = nsContentUtils::GetCurrentJSContext(); + if (cx) { + nsCOMPtr window = + do_QueryInterface(nsJSUtils::GetDynamicScriptGlobal(cx)); + NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); + + if (nsIDocument* doc = window->GetDoc()) { + aCharset = doc->GetDocumentCharacterSet(); + } + } + + return NS_OK; +} + +nsLocation::nsLocation(nsIDocShell *aDocShell) +{ + mDocShell = do_GetWeakReference(aDocShell); + nsCOMPtr outer = do_GetInterface(aDocShell); + mOuter = do_GetWeakReference(outer); +} + +nsLocation::~nsLocation() +{ +} + +DOMCI_DATA(Location, nsLocation) + +// QueryInterface implementation for nsLocation +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsLocation) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsIDOMLocation) + NS_INTERFACE_MAP_ENTRY(nsISupports) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Location) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsLocation) +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsLocation) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsLocation) + +void +nsLocation::SetDocShell(nsIDocShell *aDocShell) +{ + mDocShell = do_GetWeakReference(aDocShell); +} + +nsIDocShell * +nsLocation::GetDocShell() +{ + nsCOMPtr docshell(do_QueryReferent(mDocShell)); + return docshell; +} + +nsresult +nsLocation::CheckURL(nsIURI* aURI, nsIDocShellLoadInfo** aLoadInfo) +{ + *aLoadInfo = nullptr; + + nsCOMPtr docShell(do_QueryReferent(mDocShell)); + NS_ENSURE_TRUE(docShell, NS_ERROR_NOT_AVAILABLE); + + nsCOMPtr owner; + nsCOMPtr sourceURI; + + 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. + + nsCOMPtr doc; + nsCOMPtr docOriginalURI, docCurrentURI, principalURI; + nsCOMPtr incumbent = + do_QueryInterface(mozilla::dom::GetIncumbentGlobal()); + if (incumbent) { + doc = incumbent->GetDoc(); + } + if (doc) { + docOriginalURI = doc->GetOriginalURI(); + docCurrentURI = doc->GetDocumentURI(); + rv = doc->NodePrincipal()->GetURI(getter_AddRefs(principalURI)); + NS_ENSURE_SUCCESS(rv, rv); + } + + 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; + } + } + } + + owner = do_QueryInterface(ssm->GetCxSubjectPrincipal(cx)); + } + + // Create load info + nsCOMPtr loadInfo; + docShell->CreateLoadInfo(getter_AddRefs(loadInfo)); + NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE); + + loadInfo->SetOwner(owner); + + if (sourceURI) { + loadInfo->SetReferrer(sourceURI); + } + + loadInfo.swap(*aLoadInfo); + + return NS_OK; +} + +nsresult +nsLocation::GetURI(nsIURI** aURI, bool aGetInnermostURI) +{ + *aURI = nullptr; + + nsresult rv; + nsCOMPtr docShell(do_QueryReferent(mDocShell)); + nsCOMPtr webNav(do_QueryInterface(docShell, &rv)); + if (NS_FAILED(rv)) { + return rv; + } + + nsCOMPtr 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 jarURI(do_QueryInterface(uri)); + while (jarURI) { + jarURI->GetJARFile(getter_AddRefs(uri)); + jarURI = do_QueryInterface(uri); + } + } + + NS_ASSERTION(uri, "nsJARURI screwed up?"); + + nsCOMPtr urifixup(do_GetService(NS_URIFIXUP_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + return urifixup->CreateExposableURI(uri, aURI); +} + +nsresult +nsLocation::GetWritableURI(nsIURI** aURI) +{ + *aURI = nullptr; + + nsCOMPtr uri; + + nsresult rv = GetURI(getter_AddRefs(uri)); + if (NS_FAILED(rv) || !uri) { + return rv; + } + + return uri->Clone(aURI); +} + +nsresult +nsLocation::SetURI(nsIURI* aURI, bool aReplace) +{ + nsCOMPtr docShell(do_QueryReferent(mDocShell)); + if (docShell) { + nsCOMPtr loadInfo; + nsCOMPtr webNav(do_QueryInterface(docShell)); + + 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 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 +nsLocation::GetHash(nsAString& aHash) +{ + if (!CallerSubsumes()) + return NS_ERROR_DOM_SECURITY_ERR; + + aHash.SetLength(0); + + nsCOMPtr uri; + nsresult rv = GetURI(getter_AddRefs(uri)); + if (NS_FAILED(rv) || !uri) { + return rv; + } + + nsAutoCString ref; + nsAutoString unicodeRef; + + rv = uri->GetRef(ref); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr 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); + } + + 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 +nsLocation::SetHash(const nsAString& aHash) +{ + nsCOMPtr uri; + nsresult rv = GetWritableURI(getter_AddRefs(uri)); + if (NS_FAILED(rv) || !uri) { + return rv; + } + + NS_ConvertUTF16toUTF8 hash(aHash); + if (hash.IsEmpty() || hash.First() != char16_t('#')) { + hash.Insert(char16_t('#'), 0); + } + rv = uri->SetRef(hash); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return SetURI(uri); +} + +NS_IMETHODIMP +nsLocation::GetHost(nsAString& aHost) +{ + if (!CallerSubsumes()) + return NS_ERROR_DOM_SECURITY_ERR; + + aHost.Truncate(); + + nsCOMPtr 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 +nsLocation::SetHost(const nsAString& aHost) +{ + if (!CallerSubsumes()) + return NS_ERROR_DOM_SECURITY_ERR; + + nsCOMPtr 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 +nsLocation::GetHostname(nsAString& aHostname) +{ + if (!CallerSubsumes()) + return NS_ERROR_DOM_SECURITY_ERR; + + aHostname.Truncate(); + + nsCOMPtr uri; + nsresult result; + + result = GetURI(getter_AddRefs(uri), true); + + if (uri) { + nsAutoCString host; + + result = uri->GetHost(host); + + if (NS_SUCCEEDED(result)) { + AppendUTF8toUTF16(host, aHostname); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsLocation::SetHostname(const nsAString& aHostname) +{ + if (!CallerSubsumes()) + return NS_ERROR_DOM_SECURITY_ERR; + + nsCOMPtr 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 +nsLocation::GetHref(nsAString& aHref) +{ + if (!CallerSubsumes()) + return NS_ERROR_DOM_SECURITY_ERR; + + aHref.Truncate(); + + nsCOMPtr 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 +nsLocation::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 oldUri; + + rv = NS_NewURI(getter_AddRefs(oldUri), oldHref); + + if (oldUri) { + rv = SetHrefWithBase(aHref, oldUri, false); + } + } + } + + return rv; +} + +nsresult +nsLocation::SetHrefWithContext(JSContext* cx, const nsAString& aHref, + bool aReplace) +{ + nsCOMPtr 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 +nsLocation::SetHrefWithBase(const nsAString& aHref, nsIURI* aBase, + bool aReplace) +{ + nsresult result; + nsCOMPtr newUri; + + nsCOMPtr 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