From 5982fb6110f1ccb92aba02a5996c81b7d289a220 Mon Sep 17 00:00:00 2001 From: "bzbarsky%mit.edu" Date: Fri, 25 Jan 2008 20:31:44 +0000 Subject: [PATCH] Allow certain restricted loads in unload events. Bug 409888, r+sr=jst git-svn-id: svn://10.0.0.236/trunk@243999 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/docshell/base/nsDocShell.cpp | 122 +++++++++++++++++++++++++-- mozilla/docshell/base/nsDocShell.h | 12 +++ mozilla/docshell/base/nsWebShell.cpp | 4 +- 3 files changed, 129 insertions(+), 9 deletions(-) diff --git a/mozilla/docshell/base/nsDocShell.cpp b/mozilla/docshell/base/nsDocShell.cpp index bdecfd038bd..57c77ff542c 100644 --- a/mozilla/docshell/base/nsDocShell.cpp +++ b/mozilla/docshell/base/nsDocShell.cpp @@ -678,7 +678,9 @@ nsDocShell::LoadURI(nsIURI * aURI, PRUint32 aLoadFlags, PRBool aFirstParty) { - if (!IsNavigationAllowed()) { + // Note: we allow loads to get through here even if mFiredUnloadEvent is + // true; that case will get handled in LoadInternal or LoadHistoryEntry. + if (IsPrintingOrPP()) { return NS_OK; // JS may not handle returning of an error code } nsresult rv; @@ -3603,6 +3605,10 @@ nsDocShell::Destroy() } } + // Make sure to blow away our mLoadingURI just in case. No loads + // from inside this pagehide. + mLoadingURI = nsnull; + // Fire unload event before we blow anything away. (void) FirePageHideNotification(PR_TRUE); @@ -5138,6 +5144,10 @@ nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal) mSavingOldViewer = CanSavePresentation(LOAD_NORMAL, nsnull, nsnull); + // Make sure to blow away our mLoadingURI just in case. No loads + // from inside this pagehide. + mLoadingURI = nsnull; + // Notify the current document that it is about to be unloaded!! // // It is important to fire the unload() notification *before* any state @@ -5554,6 +5564,10 @@ nsDocShell::RestoreFromHistory() // pagehide or unload. nsCOMPtr origLSHE = mLSHE; + // Make sure to blow away our mLoadingURI just in case. No loads + // from inside this pagehide. + mLoadingURI = nsnull; + // Notify the old content viewer that it's being hidden. FirePageHideNotification(!mSavingOldViewer); @@ -5916,7 +5930,14 @@ nsDocShell::CreateContentViewer(const char *aContentType, mSavingOldViewer = CanSavePresentation(mLoadType, request, doc); } + NS_ASSERTION(!mLoadingURI, "Re-entering unload?"); + + nsCOMPtr aOpenedChannel = do_QueryInterface(request); + if (aOpenedChannel) { + aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI)); + } FirePageHideNotification(!mSavingOldViewer); + mLoadingURI = nsnull; // Set mFiredUnloadEvent = PR_FALSE so that the unload handler for the // *new* document will fire. @@ -5927,8 +5948,6 @@ nsDocShell::CreateContentViewer(const char *aContentType, // notifications before we've called Embed(). See bug 284993. mURIResultedInDocument = PR_TRUE; - nsCOMPtr aOpenedChannel = do_QueryInterface(request); - PRBool onLocationChangeNeeded = OnLoadingSite(aOpenedChannel, PR_FALSE); // let's try resetting the load group if we need to... @@ -6450,6 +6469,60 @@ nsDocShell::CheckLoadingPermissions() //***************************************************************************** // nsDocShell: Site Loading //***************************************************************************** +class InternalLoadEvent : public nsRunnable +{ +public: + InternalLoadEvent(nsDocShell* aDocShell, nsIURI * aURI, nsIURI * aReferrer, + nsISupports * aOwner, PRUint32 aFlags, + const PRUnichar *aWindowTarget, const char* aTypeHint, + nsIInputStream * aPostData, + nsIInputStream * aHeadersData, PRUint32 aLoadType, + nsISHEntry * aSHEntry, PRBool aFirstParty) : + mDocShell(aDocShell), + mURI(aURI), + mReferrer(aReferrer), + mOwner(aOwner), + mFlags(aFlags), + mPostData(aPostData), + mHeadersData(aHeadersData), + mLoadType(aLoadType), + mSHEntry(aSHEntry), + mFirstParty(aFirstParty) + { + // Make sure to keep null things null as needed + if (aWindowTarget) { + mWindowTarget = aWindowTarget; + } + if (aTypeHint) { + mTypeHint = aTypeHint; + } + } + + NS_IMETHOD Run() { + return mDocShell->InternalLoad(mURI, mReferrer, mOwner, mFlags, + mWindowTarget.get(), mTypeHint.get(), + mPostData, mHeadersData, mLoadType, + mSHEntry, mFirstParty, nsnull, nsnull); + } + +private: + nsRefPtr mDocShell; + nsCOMPtr mURI; + nsCOMPtr mReferrer; + nsCOMPtr mOwner; + PRUint32 mFlags; + + // Use IDL strings so .get() returns null by default + nsXPIDLString mWindowTarget; + nsXPIDLCString mTypeHint; + + nsCOMPtr mPostData; + nsCOMPtr mHeadersData; + PRUint32 mLoadType; + nsCOMPtr mSHEntry; + PRBool mFirstParty; +}; + NS_IMETHODIMP nsDocShell::InternalLoad(nsIURI * aURI, nsIURI * aReferrer, @@ -6465,10 +6538,6 @@ nsDocShell::InternalLoad(nsIURI * aURI, nsIDocShell** aDocShell, nsIRequest** aRequest) { - if (mFiredUnloadEvent) { - return NS_OK; // JS may not handle returning of an error code - } - nsresult rv = NS_OK; #ifdef PR_LOGGING @@ -6496,6 +6565,21 @@ nsDocShell::InternalLoad(nsIURI * aURI, NS_ENSURE_TRUE(!mIsBeingDestroyed, NS_ERROR_NOT_AVAILABLE); + if (mFiredUnloadEvent) { + if (IsOKToLoadURI(aURI)) { + // Do this asynchronously + nsCOMPtr ev = + new InternalLoadEvent(this, aURI, aReferrer, aOwner, aFlags, + aWindowTarget, aTypeHint, + aPostData, aHeadersData, aLoadType, + aSHEntry, aFirstParty); + return NS_DispatchToCurrentThread(ev); + } + + // Just ignore this load attempt + return NS_OK; + } + // wyciwyg urls can only be loaded through history. Any normal load of // wyciwyg through docshell is illegal. Disallow such loads. if (aLoadType & LOAD_CMD_NORMAL) { @@ -8064,6 +8148,10 @@ nsDocShell::AddToSessionHistory(nsIURI * aURI, NS_IMETHODIMP nsDocShell::LoadHistoryEntry(nsISHEntry * aEntry, PRUint32 aLoadType) { + if (!IsNavigationAllowed()) { + return NS_OK; + } + nsCOMPtr uri; nsCOMPtr postData; nsCOMPtr referrerURI; @@ -9211,6 +9299,26 @@ nsDocShell::IsAboutBlank(nsIURI* aURI) return str.EqualsLiteral("about:blank"); } +PRBool +nsDocShell::IsOKToLoadURI(nsIURI* aURI) +{ + NS_PRECONDITION(aURI, "Must have a URI!"); + + if (!mFiredUnloadEvent) { + return PR_TRUE; + } + + if (!mLoadingURI) { + return PR_FALSE; + } + + nsCOMPtr secMan = + do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); + return + secMan && + NS_SUCCEEDED(secMan->CheckSameOriginURI(aURI, mLoadingURI, PR_FALSE)); +} + //***************************************************************************** // nsClassifierCallback //***************************************************************************** diff --git a/mozilla/docshell/base/nsDocShell.h b/mozilla/docshell/base/nsDocShell.h index 687f70c9aa7..6b1a58941da 100644 --- a/mozilla/docshell/base/nsDocShell.h +++ b/mozilla/docshell/base/nsDocShell.h @@ -512,6 +512,11 @@ protected: // Check whether aURI is about:blank static PRBool IsAboutBlank(nsIURI* aURI); + + // Call this when a URI load is handed to us (via OnLinkClick or + // InternalLoad). This makes sure that we're not inside unload, or that if + // we are it's still OK to load this URI. + PRBool IsOKToLoadURI(nsIURI* aURI); protected: // Override the parent setter from nsDocLoader @@ -642,6 +647,13 @@ protected: // Suspends/resumes channels based on the URI classifier. nsRefPtr mClassifier; + // The URI we're currently loading. This is only relevant during the + // firing of a pagehide/unload. The caller of FirePageHideNotification() + // is responsible for setting it and unsetting it. It may be null if the + // pagehide/unload is happening for some reason other than just loading a + // new URI. + nsCOMPtr mLoadingURI; + // WEAK REFERENCES BELOW HERE. // Note these are intentionally not addrefd. Doing so will create a cycle. // For that reasons don't use nsCOMPtr. diff --git a/mozilla/docshell/base/nsWebShell.cpp b/mozilla/docshell/base/nsWebShell.cpp index 522160b8db2..d8444426b51 100644 --- a/mozilla/docshell/base/nsWebShell.cpp +++ b/mozilla/docshell/base/nsWebShell.cpp @@ -766,7 +766,7 @@ nsWebShell::OnLinkClick(nsIContent* aContent, { NS_ASSERTION(NS_IsMainThread(), "wrong thread"); - if (mFiredUnloadEvent) { + if (!IsOKToLoadURI(aURI)) { return NS_OK; } @@ -797,7 +797,7 @@ nsWebShell::OnLinkClickSync(nsIContent *aContent, *aRequest = nsnull; } - if (mFiredUnloadEvent) { + if (!IsOKToLoadURI(aURI)) { return NS_OK; }