/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 sw=2 et tw=80: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Travis Bogard * Pierre Phaneuf * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsGlobalWindow.h" #include "nsIScriptSecurityManager.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 "nsIIOService.h" #include "nsIServiceManager.h" #include "nsNetUtil.h" #include "plstr.h" #include "prprf.h" #include "prmem.h" #include "nsCOMPtr.h" #include "nsEscape.h" #include "nsJSUtils.h" #include "nsIDOMWindow.h" #include "nsIDOMDocument.h" #include "nsIDocument.h" #include "nsIPresShell.h" #include "nsPresContext.h" #include "nsIJSContextStack.h" #include "nsXPIDLString.h" #include "nsDOMError.h" #include "nsDOMClassInfo.h" #include "nsCRT.h" #include "nsIProtocolHandler.h" #include "nsReadableUtils.h" #include "nsITextToSubURI.h" #include "nsContentUtils.h" static nsresult GetContextFromStack(nsIJSContextStack *aStack, JSContext **aContext) { nsCOMPtr iterator(do_CreateInstance("@mozilla.org/js/xpc/ContextStackIterator;1")); NS_ENSURE_TRUE(iterator, NS_ERROR_FAILURE); nsresult rv = iterator->Reset(aStack); NS_ENSURE_SUCCESS(rv, rv); PRBool done; while (NS_SUCCEEDED(iterator->Done(&done)) && !done) { rv = iterator->Prev(aContext); NS_ASSERTION(NS_SUCCEEDED(rv), "Broken iterator implementation"); // Consider a null context the end of the line. if (!*aContext) { break; } if (nsJSUtils::GetDynamicScriptContext(*aContext)) { return NS_OK; } } *aContext = nsnull; return NS_OK; } static nsresult GetDocumentCharacterSetForURI(const nsAString& aHref, nsACString& aCharset) { aCharset.Truncate(); nsresult rv; nsCOMPtr stack(do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv)); NS_ENSURE_SUCCESS(rv, rv); JSContext *cx; rv = GetContextFromStack(stack, &cx); NS_ENSURE_SUCCESS(rv, rv); if (cx) { nsCOMPtr window = do_QueryInterface(nsJSUtils::GetDynamicScriptGlobal(cx)); NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); nsCOMPtr domDoc; rv = window->GetDocument(getter_AddRefs(domDoc)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr doc(do_QueryInterface(domDoc)); if (doc) { aCharset = doc->GetDocumentCharacterSet(); } } return NS_OK; } nsLocation::nsLocation(nsIDocShell *aDocShell) { mDocShell = do_GetWeakReference(aDocShell); } nsLocation::~nsLocation() { } // QueryInterface implementation for nsLocation NS_INTERFACE_MAP_BEGIN(nsLocation) NS_INTERFACE_MAP_ENTRY(nsIDOMLocation) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMLocation) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Location) NS_INTERFACE_MAP_END NS_IMPL_ADDREF(nsLocation) NS_IMPL_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 = nsnull; nsCOMPtr docShell(do_QueryReferent(mDocShell)); if (!docShell) { return NS_ERROR_NOT_AVAILABLE; } nsresult result; // Get JSContext from stack. nsCOMPtr stack(do_GetService("@mozilla.org/js/xpc/ContextStack;1", &result)); if (NS_FAILED(result)) return NS_ERROR_FAILURE; JSContext *cx; if (NS_FAILED(GetContextFromStack(stack, &cx))) return NS_ERROR_FAILURE; nsCOMPtr owner; nsCOMPtr sourceURI; if (cx) { // 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. nsCOMPtr secMan(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &result)); if (NS_FAILED(result)) return NS_ERROR_FAILURE; // Check to see if URI is allowed. result = secMan->CheckLoadURIFromScript(cx, aURI); if (NS_FAILED(result)) return result; // Now get the principal to use when loading the URI nsCOMPtr principal; if (NS_FAILED(secMan->GetSubjectPrincipal(getter_AddRefs(principal))) || !principal) return NS_ERROR_FAILURE; owner = do_QueryInterface(principal); principal->GetURI(getter_AddRefs(sourceURI)); } // Create load info nsCOMPtr loadInfo; docShell->CreateLoadInfo(getter_AddRefs(loadInfo)); NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE); loadInfo->SetOwner(owner); // now set the referrer on the loadinfo if (sourceURI) { loadInfo->SetReferrer(sourceURI); } loadInfo.swap(*aLoadInfo); return NS_OK; } nsresult nsLocation::GetURI(nsIURI** aURI, PRBool aGetInnermostURI) { *aURI = nsnull; 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 = nsnull; nsCOMPtr uri; nsresult rv = GetURI(getter_AddRefs(uri)); if (NS_FAILED(rv) || !uri) { return rv; } return uri->Clone(aURI); } nsresult nsLocation::SetURI(nsIURI* aURI, PRBool 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); } return docShell->LoadURI(aURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, PR_TRUE); } return NS_OK; } NS_IMETHODIMP nsLocation::GetHash(nsAString& aHash) { aHash.SetLength(0); nsCOMPtr uri; nsresult rv = GetURI(getter_AddRefs(uri)); nsCOMPtr url(do_QueryInterface(uri)); if (url) { nsCAutoString ref; nsAutoString unicodeRef; rv = url->GetRef(ref); if (NS_SUCCEEDED(rv)) { nsCOMPtr textToSubURI( do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv)); if (NS_SUCCEEDED(rv)) { nsCAutoString charset; url->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(PRUnichar('#')); aHash.Append(unicodeRef); } } return rv; } NS_IMETHODIMP nsLocation::SetHash(const nsAString& aHash) { nsCOMPtr uri; nsresult rv = GetWritableURI(getter_AddRefs(uri)); nsCOMPtr url(do_QueryInterface(uri)); if (url) { NS_ConvertUTF16toUTF8 hash(aHash); if (hash.IsEmpty() || hash.First() != PRUnichar('#')) { hash.Insert(PRUnichar('#'), 0); } rv = url->SetRef(hash); if (NS_SUCCEEDED(rv)) { SetURI(url); } } return rv; } NS_IMETHODIMP nsLocation::GetHost(nsAString& aHost) { aHost.Truncate(); nsCOMPtr uri; nsresult result; result = GetURI(getter_AddRefs(uri), PR_TRUE); if (uri) { nsCAutoString hostport; result = uri->GetHostPort(hostport); if (NS_SUCCEEDED(result)) { AppendUTF8toUTF16(hostport, aHost); } } return result; } NS_IMETHODIMP nsLocation::SetHost(const nsAString& aHost) { nsCOMPtr uri; nsresult rv = GetWritableURI(getter_AddRefs(uri)); if (uri) { rv = uri->SetHostPort(NS_ConvertUTF16toUTF8(aHost)); if (NS_SUCCEEDED(rv)) { SetURI(uri); } } return rv; } NS_IMETHODIMP nsLocation::GetHostname(nsAString& aHostname) { aHostname.Truncate(); nsCOMPtr uri; nsresult result; result = GetURI(getter_AddRefs(uri), PR_TRUE); if (uri) { nsCAutoString host; result = uri->GetHost(host); if (NS_SUCCEEDED(result)) { AppendUTF8toUTF16(host, aHostname); } } return result; } NS_IMETHODIMP nsLocation::SetHostname(const nsAString& aHostname) { nsCOMPtr uri; nsresult rv = GetWritableURI(getter_AddRefs(uri)); if (uri) { rv = uri->SetHost(NS_ConvertUTF16toUTF8(aHostname)); if (NS_SUCCEEDED(rv)) { SetURI(uri); } } return rv; } NS_IMETHODIMP nsLocation::GetHref(nsAString& aHref) { aHref.Truncate(); nsCOMPtr uri; nsresult result; result = GetURI(getter_AddRefs(uri)); if (uri) { nsCAutoString 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; // Get JSContext from stack. nsCOMPtr stack(do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv)); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; JSContext *cx; if (NS_FAILED(GetContextFromStack(stack, &cx))) return NS_ERROR_FAILURE; if (cx) { rv = SetHrefWithContext(cx, aHref, PR_FALSE); } else { rv = GetHref(oldHref); if (NS_SUCCEEDED(rv)) { nsCOMPtr oldUri; rv = NS_NewURI(getter_AddRefs(oldUri), oldHref); if (oldUri) { rv = SetHrefWithBase(aHref, oldUri, PR_FALSE); } } } return rv; } nsresult nsLocation::SetHrefWithContext(JSContext* cx, const nsAString& aHref, PRBool 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, PRBool aReplace) { nsresult result; nsCOMPtr newUri; nsCOMPtr docShell(do_QueryReferent(mDocShell)); nsCAutoString 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, nsnull, aBase); if (newUri) { /* Check with the scriptContext if it is currently processing a script tag. * If so, this must be a