Bug 903022 - Support passing dialog parents to DoContent. r=bz
authorJim Mathies <jmathies@mozilla.com>
Mon, 25 Aug 2014 11:54:16 -0500
changeset 223118 bdc2c5dbf79619911a9d6fb3a4f19d896128ad91
parent 223117 b62223a2c5778fd30a57dc26f1e38285f999a565
child 223119 b793b16ee15c8bb985d9c32dccfd9d4f3cd7f777
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs903022
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 903022 - Support passing dialog parents to DoContent. r=bz
uriloader/base/nsURILoader.cpp
uriloader/exthandler/ExternalHelperAppParent.cpp
uriloader/exthandler/nsExternalHelperAppService.cpp
uriloader/exthandler/nsExternalHelperAppService.h
uriloader/exthandler/nsIExternalHelperAppService.idl
--- a/uriloader/base/nsURILoader.cpp
+++ b/uriloader/base/nsURILoader.cpp
@@ -583,16 +583,17 @@ nsresult nsDocumentOpenInfo::DispatchCon
       mContentType = APPLICATION_GUESS_FROM_EXT;
       aChannel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_GUESS_FROM_EXT));
     }
 
     rv = helperAppService->DoContent(mContentType,
                                      request,
                                      m_originalContext,
                                      false,
+                                     nullptr,
                                      getter_AddRefs(m_targetStreamListener));
     if (NS_FAILED(rv)) {
       request->SetLoadFlags(loadFlags);
       m_targetStreamListener = nullptr;
     }
   }
       
   NS_ASSERTION(m_targetStreamListener || NS_FAILED(rv),
--- a/uriloader/exthandler/ExternalHelperAppParent.cpp
+++ b/uriloader/exthandler/ExternalHelperAppParent.cpp
@@ -82,17 +82,18 @@ ExternalHelperAppParent::Init(ContentPar
   nsCOMPtr<nsIInterfaceRequestor> window;
   if (aBrowser) {
     TabParent* tabParent = static_cast<TabParent*>(aBrowser);
     if (tabParent->GetOwnerElement())
       window = do_QueryInterface(tabParent->GetOwnerElement()->OwnerDoc()->GetWindow());
   }
 
   helperAppService->DoContent(aMimeContentType, this, window,
-                              aForceSave, getter_AddRefs(mListener));
+                              aForceSave, nullptr,
+                              getter_AddRefs(mListener));
 }
 
 void
 ExternalHelperAppParent::ActorDestroy(ActorDestroyReason why)
 {
   mIPCClosed = true;
 }
 
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -611,21 +611,22 @@ nsresult nsExternalHelperAppService::Ini
 nsExternalHelperAppService::~nsExternalHelperAppService()
 {
 }
 
 
 nsresult
 nsExternalHelperAppService::DoContentContentProcessHelper(const nsACString& aMimeContentType,
                                                           nsIRequest *aRequest,
+                                                          nsIInterfaceRequestor *aContentContext,
+                                                          bool aForceSave,
                                                           nsIInterfaceRequestor *aWindowContext,
-                                                          bool aForceSave,
                                                           nsIStreamListener ** aStreamListener)
 {
-  nsCOMPtr<nsIDOMWindow> window = do_GetInterface(aWindowContext);
+  nsCOMPtr<nsIDOMWindow> window = do_GetInterface(aContentContext);
   NS_ENSURE_STATE(window);
 
   // We need to get a hold of a ContentChild so that we can begin forwarding
   // this data to the parent.  In the HTTP case, this is unfortunate, since
   // we're actually passing data from parent->child->parent wastefully, but
   // the Right Fix will eventually be to short-circuit those channels on the
   // parent side based on some sort of subscription concept.
   using mozilla::dom::ContentChild;
@@ -670,35 +671,36 @@ nsExternalHelperAppService::DoContentCon
                                               mozilla::dom::TabChild::GetFrom(window));
   ExternalHelperAppChild *childListener = static_cast<ExternalHelperAppChild *>(pc);
 
   NS_ADDREF(*aStreamListener = childListener);
 
   uint32_t reason = nsIHelperAppLauncherDialog::REASON_CANTHANDLE;
 
   nsRefPtr<nsExternalAppHandler> handler =
-    new nsExternalAppHandler(nullptr, EmptyCString(), aWindowContext, this,
+    new nsExternalAppHandler(nullptr, EmptyCString(), aContentContext, aWindowContext, this,
                              fileName, reason, aForceSave);
   if (!handler) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   childListener->SetHandler(handler);
   return NS_OK;
 }
 
 NS_IMETHODIMP nsExternalHelperAppService::DoContent(const nsACString& aMimeContentType,
                                                     nsIRequest *aRequest,
+                                                    nsIInterfaceRequestor *aContentContext,
+                                                    bool aForceSave,
                                                     nsIInterfaceRequestor *aWindowContext,
-                                                    bool aForceSave,
                                                     nsIStreamListener ** aStreamListener)
 {
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
-    return DoContentContentProcessHelper(aMimeContentType, aRequest, aWindowContext,
-                                         aForceSave, aStreamListener);
+    return DoContentContentProcessHelper(aMimeContentType, aRequest, aContentContext,
+                                         aForceSave, aWindowContext, aStreamListener);
   }
 
   nsAutoString fileName;
   nsAutoCString fileExtension;
   uint32_t reason = nsIHelperAppLauncherDialog::REASON_CANTHANDLE;
   uint32_t contentDisposition = -1;
 
   // Get the file extension and name that we will need later
@@ -815,16 +817,17 @@ NS_IMETHODIMP nsExternalHelperAppService
   *aStreamListener = nullptr;
   // We want the mimeInfo's primary extension to pass it to
   // nsExternalAppHandler
   nsAutoCString buf;
   mimeInfo->GetPrimaryExtension(buf);
 
   nsExternalAppHandler * handler = new nsExternalAppHandler(mimeInfo,
                                                             buf,
+                                                            aContentContext,
                                                             aWindowContext,
                                                             this,
                                                             fileName,
                                                             reason,
                                                             aForceSave);
   if (!handler) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
@@ -1179,21 +1182,23 @@ NS_INTERFACE_MAP_BEGIN(nsExternalAppHand
    NS_INTERFACE_MAP_ENTRY(nsIHelperAppLauncher)
    NS_INTERFACE_MAP_ENTRY(nsICancelable)
    NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
    NS_INTERFACE_MAP_ENTRY(nsIBackgroundFileSaverObserver)
 NS_INTERFACE_MAP_END_THREADSAFE
 
 nsExternalAppHandler::nsExternalAppHandler(nsIMIMEInfo * aMIMEInfo,
                                            const nsCSubstring& aTempFileExtension,
+                                           nsIInterfaceRequestor* aContentContext,
                                            nsIInterfaceRequestor* aWindowContext,
                                            nsExternalHelperAppService *aExtProtSvc,
                                            const nsAString& aSuggestedFilename,
                                            uint32_t aReason, bool aForceSave)
 : mMimeInfo(aMIMEInfo)
+, mContentContext(aContentContext)
 , mWindowContext(aWindowContext)
 , mWindowToClose(nullptr)
 , mSuggestedFileName(aSuggestedFilename)
 , mForceSave(aForceSave)
 , mCanceled(false)
 , mShouldCloseWindow(false)
 , mStopRequestIssued(false)
 , mReason(aReason)
@@ -1311,17 +1316,17 @@ void nsExternalAppHandler::RetargetLoadN
   // ideally we should be able to just use mChannel (the channel we are extracting content from) or
   // the default load channel associated with the original load group. Unfortunately because
   // a redirect may have occurred, the doc loader is the only one with a ptr to the original channel 
   // which is what we really want....
 
   // Note that we need to do this before removing aChannel from the loadgroup,
   // since that would mess with the original channel on the loader.
   nsCOMPtr<nsIDocumentLoader> origContextLoader =
-    do_GetInterface(mWindowContext);
+    do_GetInterface(mContentContext);
   if (origContextLoader)
     origContextLoader->GetDocumentChannel(getter_AddRefs(mOriginalChannel));
 
   bool isPrivate = NS_UsePrivateBrowsing(aChannel);
 
   nsCOMPtr<nsILoadGroup> oldLoadGroup;
   aChannel->GetLoadGroup(getter_AddRefs(oldLoadGroup));
 
@@ -1685,23 +1690,23 @@ NS_IMETHODIMP nsExternalAppHandler::OnSt
   // before this is irrelevant; override it
   if (mForceSave) {
     alwaysAsk = false;
     action = nsIMIMEInfo::saveToDisk;
   }
   
   if (alwaysAsk)
   {
-    // invoke the dialog!!!!! use mWindowContext as the window context parameter for the dialog request
-    mDialog = do_CreateInstance( NS_HELPERAPPLAUNCHERDLG_CONTRACTID, &rv );
+    // Display the dialog
+    mDialog = do_CreateInstance(NS_HELPERAPPLAUNCHERDLG_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // this will create a reference cycle (the dialog holds a reference to us as
     // nsIHelperAppLauncher), which will be broken in Cancel or CreateTransfer.
-    rv = mDialog->Show( this, mWindowContext, mReason );
+    rv = mDialog->Show(this, GetDialogParent(), mReason);
 
     // what do we do if the dialog failed? I guess we should call Cancel and abort the load....
   }
   else
   {
 
     // We need to do the save/open immediately, then.
 #ifdef XP_WIN
@@ -1844,45 +1849,45 @@ void nsExternalAppHandler::SendStatusCha
                 mDialogProgressListener->OnStatusChange(nullptr, (type == kReadError) ? aRequest : nullptr, rv, msgText);
               } else if (mTransfer) {
                 mTransfer->OnStatusChange(nullptr, (type == kReadError) ? aRequest : nullptr, rv, msgText);
               }
               else
               if (XRE_GetProcessType() == GeckoProcessType_Default) {
                 // We don't have a listener.  Simply show the alert ourselves.
                 nsresult qiRv;
-                nsCOMPtr<nsIPrompt> prompter(do_GetInterface(mWindowContext, &qiRv));
+                nsCOMPtr<nsIPrompt> prompter(do_GetInterface(GetDialogParent(), &qiRv));
                 nsXPIDLString title;
                 bundle->FormatStringFromName(MOZ_UTF16("title"),
                                              strings,
                                              1,
                                              getter_Copies(title));
 
                 PR_LOG(nsExternalHelperAppService::mLog, PR_LOG_DEBUG,
-                       ("mWindowContext=0x%p, prompter=0x%p, qi rv=0x%08X, title='%s', msg='%s'",
-                       mWindowContext.get(),
+                       ("mContentContext=0x%p, prompter=0x%p, qi rv=0x%08X, title='%s', msg='%s'",
+                       mContentContext.get(),
                        prompter.get(),
                        qiRv,
                        NS_ConvertUTF16toUTF8(title).get(),
                        NS_ConvertUTF16toUTF8(msgText).get()));
 
                 // If we didn't have a prompter we will try and get a window
                 // instead, get it's docshell and use it to alert the user.
                 if (!prompter)
                 {
-                  nsCOMPtr<nsPIDOMWindow> window(do_GetInterface(mWindowContext));
+                  nsCOMPtr<nsPIDOMWindow> window(do_GetInterface(GetDialogParent()));
                   if (!window || !window->GetDocShell())
                   {
                     return;
                   }
 
                   prompter = do_GetInterface(window->GetDocShell(), &qiRv);
 
                   PR_LOG(nsExternalHelperAppService::mLog, PR_LOG_DEBUG,
-                         ("No prompter from mWindowContext, using DocShell, " \
+                         ("No prompter from mContentContext, using DocShell, " \
                           "window=0x%p, docShell=0x%p, " \
                           "prompter=0x%p, qi rv=0x%08X",
                           window.get(),
                           window->GetDocShell(),
                           prompter.get(),
                           qiRv));
 
                   // If we still don't have a prompter, there's nothing else we
@@ -2208,24 +2213,23 @@ nsresult nsExternalAppHandler::SaveDesti
   else
     Cancel(NS_BINDING_ABORTED);
 
   return NS_OK;
 }
 
 void nsExternalAppHandler::RequestSaveDestination(const nsAFlatString &aDefaultFile, const nsAFlatString &aFileExtension)
 {
-  // invoke the dialog!!!!! use mWindowContext as the window context parameter for the dialog request
-  // Convert to use file picker? No, then embeddors could not do any sort of
+  // Display the dialog
+  // XXX Convert to use file picker? No, then embeddors could not do any sort of
   // "AutoDownload" w/o showing a prompt
   nsresult rv = NS_OK;
-  if (!mDialog)
-  {
+  if (!mDialog) {
     // Get helper app launcher dialog.
-    mDialog = do_CreateInstance( NS_HELPERAPPLAUNCHERDLG_CONTRACTID, &rv );
+    mDialog = do_CreateInstance(NS_HELPERAPPLAUNCHERDLG_CONTRACTID, &rv);
     if (rv != NS_OK) {
       Cancel(NS_BINDING_ABORTED);
       return;
     }
   }
 
   // we want to explicitly unescape aDefaultFile b4 passing into the dialog. we can't unescape
   // it because the dialog is implemented by a JS component which doesn't have a window so no unescape routine is defined...
@@ -2234,25 +2238,25 @@ void nsExternalAppHandler::RequestSaveDe
   // If we don't do this, users that close the helper app dialog while the file
   // picker is up would cause Cancel() to be called, and the dialog would be
   // released, which would release this object too, which would crash.
   // See Bug 249143
   nsIFile* fileToUse;
   nsRefPtr<nsExternalAppHandler> kungFuDeathGrip(this);
   nsCOMPtr<nsIHelperAppLauncherDialog> dlg(mDialog);
   rv = mDialog->PromptForSaveToFile(this,
-                                    mWindowContext,
+                                    GetDialogParent(),
                                     aDefaultFile.get(),
                                     aFileExtension.get(),
                                     mForceSave, &fileToUse);
 
   if (rv == NS_ERROR_NOT_AVAILABLE) {
     // we need to use the async version -> nsIHelperAppLauncherDialog.promptForSaveToFileAsync.
     rv = mDialog->PromptForSaveToFileAsync(this, 
-                                           mWindowContext,
+                                           GetDialogParent(),
                                            aDefaultFile.get(),
                                            aFileExtension.get(),
                                            mForceSave);
   } else {
     SaveDestinationAvailable(rv == NS_OK ? fileToUse : nullptr);
   }
 }
 
@@ -2476,19 +2480,19 @@ void nsExternalAppHandler::ProcessAnyRef
 {
    // one last thing, try to see if the original window context supports a refresh interface...
    // Sometimes, when you download content that requires an external handler, there is
    // a refresh header associated with the download. This refresh header points to a page
    // the content provider wants the user to see after they download the content. How do we
    // pass this refresh information back to the caller? For now, try to get the refresh URI
    // interface. If the window context where the request originated came from supports this
    // then we can force it to process the refresh information (if there is any) from this channel.
-   if (mWindowContext && mOriginalChannel)
+   if (mContentContext && mOriginalChannel)
    {
-     nsCOMPtr<nsIRefreshURI> refreshHandler (do_GetInterface(mWindowContext));
+     nsCOMPtr<nsIRefreshURI> refreshHandler (do_GetInterface(mContentContext));
      if (refreshHandler) {
         refreshHandler->SetupRefreshURI(mOriginalChannel);
      }
      mOriginalChannel = nullptr;
    }
 }
 
 bool nsExternalAppHandler::GetNeverAskFlagFromPref(const char * prefName, const char * aContentType)
@@ -2505,28 +2509,28 @@ bool nsExternalAppHandler::GetNeverAskFl
   prefCString.BeginReading(start);
   prefCString.EndReading(end);
   return !CaseInsensitiveFindInReadable(nsDependentCString(aContentType),
                                         start, end);
 }
 
 nsresult nsExternalAppHandler::MaybeCloseWindow()
 {
-  nsCOMPtr<nsIDOMWindow> window = do_GetInterface(mWindowContext);
+  nsCOMPtr<nsIDOMWindow> window = do_GetInterface(mContentContext);
   NS_ENSURE_STATE(window);
 
   if (mShouldCloseWindow) {
     // Reset the window context to the opener window so that the dependent
     // dialogs have a parent
     nsCOMPtr<nsIDOMWindow> opener;
     window->GetOpener(getter_AddRefs(opener));
 
     bool isClosed;
     if (opener && NS_SUCCEEDED(opener->GetClosed(&isClosed)) && !isClosed) {
-      mWindowContext = do_GetInterface(opener);
+      mContentContext = do_GetInterface(opener);
 
       // Now close the old window.  Do it on a timer so that we don't run
       // into issues trying to close the window before it has fully opened.
       NS_ASSERTION(!mTimer, "mTimer was already initialized once!");
       mTimer = do_CreateInstance("@mozilla.org/timer;1");
       if (!mTimer) {
         return NS_ERROR_FAILURE;
       }
--- a/uriloader/exthandler/nsExternalHelperAppService.h
+++ b/uriloader/exthandler/nsExternalHelperAppService.h
@@ -185,18 +185,19 @@ protected:
    * Array for the files that should be deleted (for the temporary files
    * added during the private browsing mode)
    */
   nsCOMArray<nsIFile> mTemporaryPrivateFilesList;
 
 private:
   nsresult DoContentContentProcessHelper(const nsACString& aMimeContentType,
                                          nsIRequest *aRequest,
+                                         nsIInterfaceRequestor *aContentContext,
+                                         bool aForceSave,
                                          nsIInterfaceRequestor *aWindowContext,
-                                         bool aForceSave,
                                          nsIStreamListener ** aStreamListener);
 };
 
 /**
  * An external app handler is just a small little class that presents itself as
  * a nsIStreamListener. It saves the incoming data into a temp file. The handler
  * is bound to an application when it is created. When it receives an
  * OnStopRequest it launches the application using the temp file it has
@@ -213,49 +214,68 @@ public:
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSIHELPERAPPLAUNCHER
   NS_DECL_NSICANCELABLE
   NS_DECL_NSITIMERCALLBACK
   NS_DECL_NSIBACKGROUNDFILESAVEROBSERVER
 
   /**
-   * @param aMIMEInfo      MIMEInfo object, representing the type of the
-   *                       content that should be handled
-   * @param aFileExtension The extension we need to append to our temp file,
-   *                       INCLUDING the ".". e.g. .mp3
-   * @param aWindowContext Window context, as passed to DoContent
-   * @param mExtProtSvc    nsExternalHelperAppService on creation
-   * @param aFileName      The filename to use
-   * @param aReason        A constant from nsIHelperAppLauncherDialog indicating
-   *                       why the request is handled by a helper app.
+   * @param aMIMEInfo       MIMEInfo object, representing the type of the
+   *                        content that should be handled
+   * @param aFileExtension  The extension we need to append to our temp file,
+   *                        INCLUDING the ".". e.g. .mp3
+   * @param aContentContext dom Window context, as passed to DoContent.
+   * @param aWindowContext  Top level window context used in dialog parenting,
+   *                        as passed to DoContent. This parameter may be null,
+   *                        in which case dialogs will be parented to
+   *                        aContentContext.
+   * @param mExtProtSvc     nsExternalHelperAppService on creation
+   * @param aFileName       The filename to use
+   * @param aReason         A constant from nsIHelperAppLauncherDialog indicating
+   *                        why the request is handled by a helper app.
    */
   nsExternalAppHandler(nsIMIMEInfo * aMIMEInfo, const nsCSubstring& aFileExtension,
+                       nsIInterfaceRequestor * aContentContext,
                        nsIInterfaceRequestor * aWindowContext,
                        nsExternalHelperAppService * aExtProtSvc,
                        const nsAString& aFilename,
                        uint32_t aReason, bool aForceSave);
 
   /**
    * Clean up after the request was diverted to the parent process.
    */
   void DidDivertRequest(nsIRequest *request);
 
 protected:
   ~nsExternalAppHandler();
 
+  nsIInterfaceRequestor* GetDialogParent() {
+    return mWindowContext ? mWindowContext : mContentContext;
+  }
+
   nsCOMPtr<nsIFile> mTempFile;
   nsCOMPtr<nsIURI> mSourceUrl;
   nsString mTempFileExtension;
   nsString mTempLeafName;
 
   /**
    * The MIME Info for this load. Will never be null.
    */
   nsCOMPtr<nsIMIMEInfo> mMimeInfo;
+
+  /**
+   * The dom window associated with this request to handle content.
+   */
+  nsCOMPtr<nsIInterfaceRequestor> mContentContext;
+
+  /**
+   * If set, the parent window helper app dialogs and file pickers
+   * should use in parenting. If null, we use mContentContext.
+   */
   nsCOMPtr<nsIInterfaceRequestor> mWindowContext;
 
   /**
    * Used to close the window on a timer, to avoid any exceptions that are
    * thrown if we try to close the window before it's fully loaded.
    */
   nsCOMPtr<nsIDOMWindow> mWindowToClose;
   nsCOMPtr<nsITimer> mTimer;
--- a/uriloader/exthandler/nsIExternalHelperAppService.idl
+++ b/uriloader/exthandler/nsIExternalHelperAppService.idl
@@ -13,36 +13,41 @@ interface nsIFile;
 interface nsIMIMEInfo;
 interface nsIWebProgressListener2;
 interface nsIInterfaceRequestor;
 
 /**
  * The external helper app service is used for finding and launching
  * platform specific external applications for a given mime content type.
  */
-[scriptable, uuid(9e456297-ba3e-42b1-92bd-b7db014268cb)]
+[scriptable, uuid(1E4F3AE1-B737-431F-A95D-31FA8DA70199)]
 interface nsIExternalHelperAppService : nsISupports
 {
   /**
    * Binds an external helper application to a stream listener. The caller
    * should pump data into the returned stream listener. When the OnStopRequest
    * is issued, the stream listener implementation will launch the helper app
    * with this data.
    * @param aMimeContentType The content type of the incoming data
    * @param aRequest The request corresponding to the incoming data
-   * @param aWindowContext Use GetInterface to retrieve properties like the
-   *                       dom window or parent window...
-   *                       The service might need this in order to bring up dialogs.
+   * @param aContentContext Used in processing content document refresh
+   *  headers after target content is downloaded. Note in e10s land
+   *  this is likely a CPOW that points to a window in the child process.
    * @param aForceSave True to always save this content to disk, regardless of
-   *                   nsIMIMEInfo and other such influences.
+   *  nsIMIMEInfo and other such influences.
+   * @param aWindowContext Used in parenting helper app dialogs, usually
+   *  points to the parent browser window. This parameter may be null,
+   *  in which case dialogs will be parented to aContentContext.
    * @return A nsIStreamListener which the caller should pump the data into.
    */
-  nsIStreamListener doContent (in ACString aMimeContentType, in nsIRequest aRequest,
-                               in nsIInterfaceRequestor aWindowContext,
-                               in boolean aForceSave); 
+  nsIStreamListener doContent (in ACString aMimeContentType,
+                               in nsIRequest aRequest,
+                               in nsIInterfaceRequestor aContentContext,
+                               in boolean aForceSave,
+                               [optional] in nsIInterfaceRequestor aWindowContext); 
 
   /**
    * Returns true if data from a URL with this extension combination
    * is to be decoded from aEncodingType prior to saving or passing
    * off to helper apps, false otherwise.
    */
   boolean applyDecodingForExtension(in AUTF8String aExtension,
                                     in ACString aEncodingType);