Bug 1350058 - Update the content context if we close the downloading window. r=mconley draft
authorBlake Kaplan <mrbkap@gmail.com>
Thu, 23 Mar 2017 17:01:30 -0700
changeset 555824 6e3c79b86bb2393260a9267686ecdcf2c0399fe2
parent 555725 b043233ec04f06768d59dcdfb9e928142280f3cc
child 622716 bbead1780c303380bf6a873310fda0da170c5091
push id52359
push userbmo:mrbkap@mozilla.com
push dateWed, 05 Apr 2017 01:44:48 +0000
reviewersmconley
bugs1350058
milestone55.0a1
Bug 1350058 - Update the content context if we close the downloading window. r=mconley MozReview-Commit-ID: A5LsapsljMY
uriloader/exthandler/ExternalHelperAppChild.cpp
uriloader/exthandler/ExternalHelperAppChild.h
uriloader/exthandler/ExternalHelperAppParent.cpp
uriloader/exthandler/ExternalHelperAppParent.h
uriloader/exthandler/PExternalHelperApp.ipdl
uriloader/exthandler/nsExternalHelperAppService.cpp
uriloader/exthandler/nsExternalHelperAppService.h
--- a/uriloader/exthandler/ExternalHelperAppChild.cpp
+++ b/uriloader/exthandler/ExternalHelperAppChild.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 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 "ExternalHelperAppChild.h"
 #include "mozilla/net/ChannelDiverterChild.h"
+#include "mozilla/dom/TabChild.h"
 #include "nsIDivertableChannel.h"
 #include "nsIInputStream.h"
 #include "nsIFTPChannel.h"
 #include "nsIRequest.h"
 #include "nsIResumableChannel.h"
 #include "nsNetUtil.h"
 
 namespace mozilla {
@@ -58,27 +59,35 @@ ExternalHelperAppChild::OnDataAvailable(
 //////////////////////////////////////////////////////////////////////////////
 
 NS_IMETHODIMP
 ExternalHelperAppChild::OnStartRequest(nsIRequest *request, nsISupports *ctx)
 {
   nsresult rv = mHandler->OnStartRequest(request, ctx);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
 
+  // Calling OnStartRequest could cause mHandler to close the window it was
+  // loaded for. In that case, the TabParent in the parent context might then
+  // point to the wrong window. Re-send the window context along with either
+  // DivertToParent or SendOnStartRequest just in case.
+  nsCOMPtr<nsPIDOMWindowOuter> window =
+    do_GetInterface(mHandler->GetDialogParent());
+  TabChild *tabChild = window ? mozilla::dom::TabChild::GetFrom(window) : nullptr;
+
   nsCOMPtr<nsIDivertableChannel> divertable = do_QueryInterface(request);
   if (divertable) {
-    return DivertToParent(divertable, request);
+    return DivertToParent(divertable, request, tabChild);
   }
 
   nsCString entityID;
   nsCOMPtr<nsIResumableChannel> resumable(do_QueryInterface(request));
   if (resumable) {
     resumable->GetEntityID(entityID);
   }
-  SendOnStartRequest(entityID);
+  SendOnStartRequest(entityID, tabChild);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ExternalHelperAppChild::OnStopRequest(nsIRequest *request,
                                       nsISupports *ctx,
                                       nsresult status)
 {
@@ -89,30 +98,31 @@ ExternalHelperAppChild::OnStopRequest(ns
     NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
   }
 
   return NS_OK;
 }
 
 nsresult
 ExternalHelperAppChild::DivertToParent(nsIDivertableChannel *divertable,
-                                       nsIRequest *request)
+                                       nsIRequest *request,
+                                       TabChild *tabChild)
 {
   // nsIDivertable must know about content conversions before being diverted.
   MOZ_ASSERT(mHandler);
   mHandler->MaybeApplyDecodingForExtension(request);
 
   mozilla::net::ChannelDiverterChild *diverter = nullptr;
   nsresult rv = divertable->DivertToParent(&diverter);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   MOZ_ASSERT(diverter);
 
-  if (SendDivertToParentUsing(diverter)) {
+  if (SendDivertToParentUsing(diverter, tabChild)) {
     mHandler->DidDivertRequest(request);
     mHandler = nullptr;
     return NS_OK;
   }
 
   return NS_ERROR_FAILURE;
 }
 
--- a/uriloader/exthandler/ExternalHelperAppChild.h
+++ b/uriloader/exthandler/ExternalHelperAppChild.h
@@ -11,16 +11,18 @@
 #include "nsExternalHelperAppService.h"
 #include "nsIStreamListener.h"
 
 class nsIDivertableChannel;
 
 namespace mozilla {
 namespace dom {
 
+class TabChild;
+
 class ExternalHelperAppChild : public PExternalHelperAppChild
                              , public nsIStreamListener
 {
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSISTREAMLISTENER
     NS_DECL_NSIREQUESTOBSERVER
 
@@ -28,17 +30,19 @@ public:
 
     // Give the listener a real nsExternalAppHandler to complete processing on
     // the child.
     void SetHandler(nsExternalAppHandler *handler) { mHandler = handler; }
 
     virtual mozilla::ipc::IPCResult RecvCancel(const nsresult& aStatus) override;
 private:
     virtual ~ExternalHelperAppChild();
-    MOZ_MUST_USE nsresult DivertToParent(nsIDivertableChannel *divertable, nsIRequest *request);
+    MOZ_MUST_USE nsresult DivertToParent(nsIDivertableChannel *divertable,
+                                         nsIRequest *request,
+                                         TabChild *tabChild);
 
     RefPtr<nsExternalAppHandler> mHandler;
     nsresult mStatus;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/uriloader/exthandler/ExternalHelperAppParent.cpp
+++ b/uriloader/exthandler/ExternalHelperAppParent.cpp
@@ -49,16 +49,40 @@ ExternalHelperAppParent::ExternalHelperA
   , mIPCClosed(false)
   , mLoadFlags(0)
   , mStatus(NS_OK)
   , mContentLength(aContentLength)
   , mWasFileChannel(aWasFileChannel)
 {
 }
 
+already_AddRefed<nsIInterfaceRequestor>
+GetWindowFromTabParent(PBrowserParent* aBrowser)
+{
+  if (!aBrowser) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIInterfaceRequestor> window;
+  TabParent* tabParent = TabParent::GetFrom(aBrowser);
+  if (tabParent->GetOwnerElement()) {
+    window = do_QueryInterface(tabParent->GetOwnerElement()->OwnerDoc()->GetWindow());
+  }
+
+  return window.forget();
+}
+
+void
+UpdateContentContext(nsIStreamListener* aListener, PBrowserParent* aBrowser)
+{
+  MOZ_ASSERT(aListener);
+  nsCOMPtr<nsIInterfaceRequestor> window = GetWindowFromTabParent(aBrowser);
+  static_cast<nsExternalAppHandler *>(aListener)->SetContentContext(window);
+}
+
 void
 ExternalHelperAppParent::Init(ContentParent *parent,
                               const nsCString& aMimeContentType,
                               const nsCString& aContentDispositionHeader,
                               const uint32_t& aContentDispositionHint,
                               const nsString& aContentDispositionFilename,
                               const bool& aForceSave,
                               const OptionalURIParams& aReferrer,
@@ -112,20 +136,23 @@ void
 ExternalHelperAppParent::Delete()
 {
   if (!mIPCClosed) {
     Unused << Send__delete__(this);
   }
 }
 
 mozilla::ipc::IPCResult
-ExternalHelperAppParent::RecvOnStartRequest(const nsCString& entityID)
+ExternalHelperAppParent::RecvOnStartRequest(const nsCString& entityID,
+                                            PBrowserParent* contentContext)
 {
   MOZ_ASSERT(!mDiverted, "child forwarding callbacks after request was diverted");
 
+  UpdateContentContext(mListener, contentContext);
+
   mEntityID = entityID;
   mPending = true;
   mStatus = mListener->OnStartRequest(this, nullptr);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ExternalHelperAppParent::RecvOnDataAvailable(const nsCString& data,
@@ -154,19 +181,21 @@ ExternalHelperAppParent::RecvOnStopReque
   mPending = false;
   mListener->OnStopRequest(this, nullptr,
                            (NS_SUCCEEDED(code) && NS_FAILED(mStatus)) ? mStatus : code);
   Delete();
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-ExternalHelperAppParent::RecvDivertToParentUsing(PChannelDiverterParent* diverter)
+ExternalHelperAppParent::RecvDivertToParentUsing(PChannelDiverterParent* diverter,
+                                                 PBrowserParent* contentContext)
 {
   MOZ_ASSERT(diverter);
+  UpdateContentContext(mListener, contentContext);
   auto p = static_cast<mozilla::net::ChannelDiverterParent*>(diverter);
   p->DivertTo(this);
 #ifdef DEBUG
   mDiverted = true;
 #endif
   Unused << p->Send__delete__(p);
   return IPC_OK();
 }
--- a/uriloader/exthandler/ExternalHelperAppParent.h
+++ b/uriloader/exthandler/ExternalHelperAppParent.h
@@ -63,23 +63,25 @@ public:
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIREQUEST
     NS_DECL_NSICHANNEL
     NS_DECL_NSIMULTIPARTCHANNEL
     NS_DECL_NSIRESUMABLECHANNEL
     NS_DECL_NSISTREAMLISTENER
     NS_DECL_NSIREQUESTOBSERVER
 
-    mozilla::ipc::IPCResult RecvOnStartRequest(const nsCString& entityID) override;
+    mozilla::ipc::IPCResult RecvOnStartRequest(const nsCString& entityID,
+                                               PBrowserParent* aBrowser) override;
     mozilla::ipc::IPCResult RecvOnDataAvailable(const nsCString& data,
                                                 const uint64_t& offset,
                                                 const uint32_t& count) override;
     mozilla::ipc::IPCResult RecvOnStopRequest(const nsresult& code) override;
 
-    mozilla::ipc::IPCResult RecvDivertToParentUsing(PChannelDiverterParent* diverter) override;
+    mozilla::ipc::IPCResult RecvDivertToParentUsing(PChannelDiverterParent* diverter,
+                                                    PBrowserParent* aBrowser) override;
 
     bool WasFileChannel() override {
       return mWasFileChannel;
     }
 
     ExternalHelperAppParent(const OptionalURIParams& uri, const int64_t& contentLength,
                             const bool& wasFileChannel);
     void Init(ContentParent *parent,
--- a/uriloader/exthandler/PExternalHelperApp.ipdl
+++ b/uriloader/exthandler/PExternalHelperApp.ipdl
@@ -1,28 +1,30 @@
+/* vim: set ft=cpp: */
 /* 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 protocol PBrowser;
 include protocol PContent;
 include protocol PChannelDiverter;
 
 namespace mozilla {
 namespace dom {
 
 protocol PExternalHelperApp
 {
   manager PContent;
 
 parent:
-  async OnStartRequest(nsCString entityID);
+  async OnStartRequest(nsCString entityID, PBrowser windowContext);
   async OnDataAvailable(nsCString data, uint64_t offset, uint32_t count);
   async OnStopRequest(nsresult code);
 
-  async DivertToParentUsing(PChannelDiverter diverter);
+  async DivertToParentUsing(PChannelDiverter diverter, PBrowser windowContext);
 
 child:
   async Cancel(nsresult aStatus);
   async __delete__();
 };
 
 
 } // namespace dom
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -835,16 +835,18 @@ NS_IMETHODIMP nsExternalHelperAppService
   }
 
   *aStreamListener = nullptr;
   // We want the mimeInfo's primary extension to pass it to
   // nsExternalAppHandler
   nsAutoCString buf;
   mimeInfo->GetPrimaryExtension(buf);
 
+  // NB: ExternalHelperAppParent depends on this listener always being an
+  // nsExternalAppHandler. If this changes, make sure to update that code.
   nsExternalAppHandler * handler = new nsExternalAppHandler(mimeInfo,
                                                             buf,
                                                             aContentContext,
                                                             aWindowContext,
                                                             this,
                                                             fileName,
                                                             reason,
                                                             aForceSave);
--- a/uriloader/exthandler/nsExternalHelperAppService.h
+++ b/uriloader/exthandler/nsExternalHelperAppService.h
@@ -240,23 +240,31 @@ public:
    */
   void DidDivertRequest(nsIRequest *request);
 
   /**
    * Apply content conversions if needed.
    */
   void MaybeApplyDecodingForExtension(nsIRequest *request);
 
-protected:
-  ~nsExternalAppHandler();
-
+  /**
+   * Get the dialog parent. Public for ExternalHelperAppChild::OnStartRequest.
+   */
   nsIInterfaceRequestor* GetDialogParent() {
     return mWindowContext ? mWindowContext : mContentContext;
   }
 
+  void SetContentContext(nsIInterfaceRequestor* context) {
+    MOZ_ASSERT(!mWindowContext);
+    mContentContext = context;
+  }
+
+protected:
+  ~nsExternalAppHandler();
+
   nsCOMPtr<nsIFile> mTempFile;
   nsCOMPtr<nsIURI> mSourceUrl;
   nsString mTempFileExtension;
   nsString mTempLeafName;
 
   /**
    * The MIME Info for this load. Will never be null.
    */