From c8758b856c2a9c297fe755c27b43a7886f5de9d7 Mon Sep 17 00:00:00 2001 From: "marria%gmail.com" Date: Tue, 4 Jul 2006 20:48:33 +0000 Subject: [PATCH] Bug 241972 (new window opened by window.open('xxx.exe') or target="_blank" isn't closed automatically when a download begins). Fix stores state on the channel indicating whether a window was opened specifically for the download. The external helper app service then closes the window if there is no refresh header and if the window was opened specifically for the download. r=cbiesinger, sr=darin git-svn-id: svn://10.0.0.236/trunk@201538 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/docshell/base/nsDocShell.cpp | 23 +++++++++- mozilla/docshell/base/nsDocShell.h | 3 +- mozilla/docshell/base/nsIDocShell.idl | 3 ++ mozilla/docshell/base/nsIWebNavigation.idl | 5 +++ .../windowwatcher/src/nsWindowWatcher.cpp | 3 +- mozilla/uriloader/exthandler/Makefile.in | 1 + .../exthandler/nsExternalHelperAppService.cpp | 44 +++++++++++++++++-- .../exthandler/nsExternalHelperAppService.h | 22 +++++++++- mozilla/webshell/public/nsIRefreshURI.idl | 14 ++++++ 9 files changed, 110 insertions(+), 8 deletions(-) diff --git a/mozilla/docshell/base/nsDocShell.cpp b/mozilla/docshell/base/nsDocShell.cpp index 765f4d79663..66fb25dfb40 100644 --- a/mozilla/docshell/base/nsDocShell.cpp +++ b/mozilla/docshell/base/nsDocShell.cpp @@ -819,6 +819,9 @@ nsDocShell::LoadURI(nsIURI * aURI, if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) flags |= INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP; + if (aLoadFlags & LOAD_FLAGS_NEW_WINDOW) + flags |= INTERNAL_LOAD_FLAGS_NEW_WINDOW; + rv = InternalLoad(aURI, referrer, owner, @@ -4530,6 +4533,9 @@ NS_IMETHODIMP nsDocShell::SetupRefreshURI(nsIChannel * aChannel) if (!refreshHeader.IsEmpty()) { SetupReferrerFromChannel(aChannel); rv = SetupRefreshURIFromHeader(mCurrentURI, refreshHeader); + if (NS_SUCCEEDED(rv)) { + return NS_REFRESHURI_HEADER_FOUND; + } } } return rv; @@ -6372,6 +6378,8 @@ nsDocShell::InternalLoad(nsIURI * aURI, NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE); isNewWindow = PR_TRUE; + aFlags |= INTERNAL_LOAD_FLAGS_NEW_WINDOW; + nsDependentString name(aWindowTarget); nsCOMPtr newWin; rv = win->Open(EmptyString(), // URL to load @@ -6718,7 +6726,8 @@ nsDocShell::InternalLoad(nsIURI * aURI, rv = DoURILoad(aURI, aReferrer, !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER), owner, aTypeHint, aPostData, aHeadersData, aFirstParty, - aDocShell, getter_AddRefs(req)); + aDocShell, getter_AddRefs(req), + aFlags & INTERNAL_LOAD_FLAGS_NEW_WINDOW); if (req && aRequest) NS_ADDREF(*aRequest = req); @@ -6778,7 +6787,8 @@ nsDocShell::DoURILoad(nsIURI * aURI, nsIInputStream * aHeadersData, PRBool aFirstParty, nsIDocShell ** aDocShell, - nsIRequest ** aRequest) + nsIRequest ** aRequest, + PRBool aIsNewWindowTarget) { nsresult rv; nsCOMPtr uriLoader; @@ -6948,6 +6958,15 @@ nsDocShell::DoURILoad(nsIURI * aURI, channel->SetOwner(aOwner); } + if (aIsNewWindowTarget) { + nsCOMPtr props = do_QueryInterface(channel); + if (props) { + props->SetPropertyAsBool( + NS_LITERAL_STRING("docshell.newWindowTarget"), + PR_TRUE); + } + } + rv = DoChannelLoad(channel, uriLoader); // diff --git a/mozilla/docshell/base/nsDocShell.h b/mozilla/docshell/base/nsDocShell.h index bb8125ff1fa..b333c375eaf 100644 --- a/mozilla/docshell/base/nsDocShell.h +++ b/mozilla/docshell/base/nsDocShell.h @@ -241,7 +241,8 @@ protected: nsIInputStream * aHeadersData, PRBool firstParty, nsIDocShell ** aDocShell, - nsIRequest ** aRequest); + nsIRequest ** aRequest, + PRBool aIsNewWindowTarget); NS_IMETHOD AddHeadersToChannel(nsIInputStream * aHeadersData, nsIChannel * aChannel); virtual nsresult DoChannelLoad(nsIChannel * aChannel, diff --git a/mozilla/docshell/base/nsIDocShell.idl b/mozilla/docshell/base/nsIDocShell.idl index 9e864d3ee6d..234e1b022da 100644 --- a/mozilla/docshell/base/nsIDocShell.idl +++ b/mozilla/docshell/base/nsIDocShell.idl @@ -122,6 +122,9 @@ interface nsIDocShell : nsISupports const long INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER = 0x2; const long INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP = 0x4; + // This flag marks the first load in a new window. + const long INTERNAL_LOAD_FLAGS_NEW_WINDOW = 0x8; + /** * Loads the given URI. This method is identical to loadURI(...) except * that its parameter list is broken out instead of being packaged inside diff --git a/mozilla/docshell/base/nsIWebNavigation.idl b/mozilla/docshell/base/nsIWebNavigation.idl index 823a9b877b9..e988652aa05 100644 --- a/mozilla/docshell/base/nsIWebNavigation.idl +++ b/mozilla/docshell/base/nsIWebNavigation.idl @@ -183,6 +183,11 @@ interface nsIWebNavigation : nsISupports */ const unsigned long LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP = 0x2000; + /** + * This flag specifies that this load is the first load in a new window. + */ + const unsigned long LOAD_FLAGS_NEW_WINDOW = 0x4000; + /** * Loads a given URI. This will give priority to loading the requested URI * in the object implementing this interface. If it can't be loaded here diff --git a/mozilla/embedding/components/windowwatcher/src/nsWindowWatcher.cpp b/mozilla/embedding/components/windowwatcher/src/nsWindowWatcher.cpp index d4a067dddca..1b85832fe2d 100644 --- a/mozilla/embedding/components/windowwatcher/src/nsWindowWatcher.cpp +++ b/mozilla/embedding/components/windowwatcher/src/nsWindowWatcher.cpp @@ -844,7 +844,8 @@ nsWindowWatcher::OpenWindowJSInternal(nsIDOMWindow *aParent, } newDocShell->LoadURI(uriToLoad, loadInfo, - nsIWebNavigation::LOAD_FLAGS_NONE, PR_TRUE); + windowIsNew ? nsIWebNavigation::LOAD_FLAGS_NEW_WINDOW : + nsIWebNavigation::LOAD_FLAGS_NONE, PR_TRUE); } if (isNewToplevelWindow) diff --git a/mozilla/uriloader/exthandler/Makefile.in b/mozilla/uriloader/exthandler/Makefile.in index d26c2e62f5d..81f79edb182 100644 --- a/mozilla/uriloader/exthandler/Makefile.in +++ b/mozilla/uriloader/exthandler/Makefile.in @@ -79,6 +79,7 @@ REQUIRES = xpcom \ docshell \ windowwatcher \ embed_base \ + dom \ $(NULL) ifdef MOZ_PHOENIX diff --git a/mozilla/uriloader/exthandler/nsExternalHelperAppService.cpp b/mozilla/uriloader/exthandler/nsExternalHelperAppService.cpp index 9e6e223edcb..2187e224f5d 100644 --- a/mozilla/uriloader/exthandler/nsExternalHelperAppService.cpp +++ b/mozilla/uriloader/exthandler/nsExternalHelperAppService.cpp @@ -112,6 +112,10 @@ #include "nsIGlobalHistory.h" // to mark downloads as visited #include "nsIGlobalHistory2.h" // to mark downloads as visited +#include "nsIDOMWindow.h" +#include "nsIDOMWindowInternal.h" +#include "nsIDocShell.h" + #include "nsCRT.h" #ifdef PR_LOGGING @@ -1343,6 +1347,8 @@ nsExternalAppHandler::nsExternalAppHandler(nsIMIMEInfo * aMIMEInfo, , mWindowContext(aWindowContext) , mSuggestedFileName(aSuggestedFilename) , mCanceled(PR_FALSE) +, mHasRefreshHeader(PR_FALSE) +, mShouldCloseWindow(PR_FALSE) , mReceivedDispositionInfo(PR_FALSE) , mStopRequestIssued(PR_FALSE) , mProgressListenerInitialized(PR_FALSE) @@ -1610,6 +1616,12 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest *request, nsISuppo mContentLength = len; } + // Determine whether a new window was opened specifically for this request + if (props) { + props->GetPropertyAsBool(NS_LITERAL_STRING("docshell.newWindowTarget"), + &mShouldCloseWindow); + } + // Now get the URI if (aChannel) { @@ -2066,6 +2078,10 @@ nsresult nsExternalAppHandler::ExecuteDesiredAction() } mWebProgressListener->OnStateChange(nsnull, nsnull, nsIWebProgressListener::STATE_STOP, NS_OK); } + + // Close the underlying DOMWindow if there is no refresh header + // and it was opened specifically for the download + MaybeCloseWindow(); } return rv; @@ -2452,6 +2468,10 @@ NS_IMETHODIMP nsExternalAppHandler::Cancel(nsresult aReason) NS_ENSURE_ARG(NS_FAILED(aReason)); // XXX should not ignore the reason + // Close the underlying DOMWindow if there is no refresh header + // and it was opened specifically for the download + MaybeCloseWindow(); + mCanceled = PR_TRUE; // Break our reference cycle with the helper app dialog (set up in // OnStartRequest) @@ -2497,8 +2517,12 @@ void nsExternalAppHandler::ProcessAnyRefreshTags() if (mWindowContext && mOriginalChannel) { nsCOMPtr refreshHandler (do_GetInterface(mWindowContext)); - if (refreshHandler) - refreshHandler->SetupRefreshURI(mOriginalChannel); + if (refreshHandler) { + nsresult rv = refreshHandler->SetupRefreshURI(mOriginalChannel); + if (rv == NS_REFRESHURI_HEADER_FOUND) { + mHasRefreshHeader = PR_TRUE; + } + } mOriginalChannel = nsnull; } } @@ -2530,6 +2554,21 @@ PRBool nsExternalAppHandler::GetNeverAskFlagFromPref(const char * prefName, cons return PR_TRUE; } +nsresult nsExternalAppHandler::MaybeCloseWindow() +{ + nsCOMPtr window(do_GetInterface(mWindowContext)); + nsCOMPtr internalWindow = do_QueryInterface(window); + NS_ENSURE_STATE(internalWindow); + + // Only close the page if there is no refresh header and if the window + // was opened specifically for the download + if (!mHasRefreshHeader && mShouldCloseWindow) { + internalWindow->Close(); + } + + return NS_OK; +} + ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // The following section contains our nsIMIMEService implementation and related methods. // @@ -2871,4 +2910,3 @@ PRBool nsExternalHelperAppService::GetTypeFromExtras(const nsACString& aExtensio return PR_FALSE; } - diff --git a/mozilla/uriloader/exthandler/nsExternalHelperAppService.h b/mozilla/uriloader/exthandler/nsExternalHelperAppService.h index 803fecb1db1..5d80eeb219e 100644 --- a/mozilla/uriloader/exthandler/nsExternalHelperAppService.h +++ b/mozilla/uriloader/exthandler/nsExternalHelperAppService.h @@ -367,6 +367,19 @@ protected: */ PRPackedBool mCanceled; + /** + * This flag is set if a refresh header was found. In this case, we + * don't want to close the dom window after handling the content. + */ + PRPackedBool mHasRefreshHeader; + + /** + * This is set based on whether the channel indicates that a new window + * was opened specifically for this download. If so, then we + * close it. + */ + PRBool mShouldCloseWindow; + /** * have we received information from the user about how they want to * dispose of this content @@ -468,7 +481,14 @@ protected: * Utility function to send proper error notification to web progress listener */ void SendStatusChange(ErrorType type, nsresult aStatus, nsIRequest *aRequest, const nsAFlatString &path); - + + /** + * Closes the window context if it does not have a refresh header + * and it never displayed content before the external helper app + * service was invoked. + */ + nsresult MaybeCloseWindow(); + nsCOMPtr mWebProgressListener; nsCOMPtr mOriginalChannel; /**< in the case of a redirect, this will be the pre-redirect channel. */ nsCOMPtr mDialog; diff --git a/mozilla/webshell/public/nsIRefreshURI.idl b/mozilla/webshell/public/nsIRefreshURI.idl index 01538a5aa88..c00a5298ddd 100644 --- a/mozilla/webshell/public/nsIRefreshURI.idl +++ b/mozilla/webshell/public/nsIRefreshURI.idl @@ -62,6 +62,9 @@ interface nsIRefreshURI : nsISupports { * request will be queued and executed when the current page * finishes loading. * + * Returns the NS_REFRESHURI_HEADER_FOUND success code if a refresh + * header was found and successfully setup. + * * @param aChannel The channel to be parsed. */ void setupRefreshURI(in nsIChannel aChannel); @@ -82,3 +85,14 @@ interface nsIRefreshURI : nsISupports { */ void cancelRefreshURITimers(); }; + + +/** + * This success code indicates that a refresh header was found and + * successfully setup. + */ +%{C++ +#define NS_REFRESHURI_HEADER_FOUND \ + NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_URILOADER, \ + 2) +%}