Bug 1189195 - Fix PContentPermissionRequest shutdown () r=fabrice
authorSotaro Ikeda <sotaro.ikeda.g@gmail.com>
Sun, 08 Nov 2015 16:55:08 -0800
changeset 307659 4d69fc402b8653416eedc893787e63f674985eee
parent 307658 f8fa4680e7321306c8ed56c4881851fd78226bc8
child 307660 b346a16f9d061d04be68104d4e8ec2ff72f20c34
push id1040
push userraliiev@mozilla.com
push dateMon, 29 Feb 2016 17:11:22 +0000
treeherdermozilla-release@8c3167321162 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfabrice
bugs1189195
milestone45.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 1189195 - Fix PContentPermissionRequest shutdown () r=fabrice
dom/base/nsContentPermissionHelper.cpp
dom/base/nsContentPermissionHelper.h
dom/ipc/ContentChild.cpp
dom/ipc/ContentParent.cpp
dom/ipc/PContentPermissionRequest.ipdl
dom/ipc/TabChild.cpp
--- a/dom/base/nsContentPermissionHelper.cpp
+++ b/dom/base/nsContentPermissionHelper.cpp
@@ -122,16 +122,17 @@ class ContentPermissionRequestParent : p
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<Element> mElement;
   RefPtr<nsContentPermissionRequestProxy> mProxy;
   nsTArray<PermissionRequest> mRequests;
 
  private:
   virtual bool Recvprompt();
   virtual bool RecvNotifyVisibility(const bool& aIsVisible);
+  virtual bool RecvDestroy();
   virtual void ActorDestroy(ActorDestroyReason why);
 };
 
 ContentPermissionRequestParent::ContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests,
                                                                Element* aElement,
                                                                const IPC::Principal& aPrincipal)
 {
   MOZ_COUNT_CTOR(ContentPermissionRequestParent);
@@ -162,16 +163,23 @@ ContentPermissionRequestParent::RecvNoti
 {
   if (!mProxy) {
     return false;
   }
   mProxy->NotifyVisibility(aIsVisible);
   return true;
 }
 
+bool
+ContentPermissionRequestParent::RecvDestroy()
+{
+  Unused << PContentPermissionRequestParent::Send__delete__(this);
+  return true;
+}
+
 void
 ContentPermissionRequestParent::ActorDestroy(ActorDestroyReason why)
 {
   if (mProxy) {
     mProxy->OnParentDestroyed();
   }
 }
 
@@ -296,16 +304,24 @@ nsContentPermissionUtils::ConvertArrayTo
 static std::map<PContentPermissionRequestParent*, TabId>&
 ContentPermissionRequestParentMap()
 {
   MOZ_ASSERT(NS_IsMainThread());
   static std::map<PContentPermissionRequestParent*, TabId> sPermissionRequestParentMap;
   return sPermissionRequestParentMap;
 }
 
+static std::map<PContentPermissionRequestChild*, TabId>&
+ContentPermissionRequestChildMap()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  static std::map<PContentPermissionRequestChild*, TabId> sPermissionRequestChildMap;
+  return sPermissionRequestChildMap;
+}
+
 /* static */ nsresult
 nsContentPermissionUtils::CreatePermissionArray(const nsACString& aType,
                                                 const nsACString& aAccess,
                                                 const nsTArray<nsString>& aOptions,
                                                 nsIArray** aTypesArray)
 {
   nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID);
   RefPtr<ContentPermissionType> permType = new ContentPermissionType(aType,
@@ -359,16 +375,17 @@ nsContentPermissionUtils::AskPermission(
     NS_ENSURE_SUCCESS(rv, rv);
 
     req->IPDLAddRef();
     ContentChild::GetSingleton()->SendPContentPermissionRequestConstructor(
       req,
       permArray,
       IPC::Principal(principal),
       child->GetTabId());
+    ContentPermissionRequestChildMap()[req.get()] = child->GetTabId();
 
     req->Sendprompt();
     return NS_OK;
   }
 
   // for chrome process
   nsCOMPtr<nsIContentPermissionPrompt> prompt =
     do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
@@ -398,16 +415,39 @@ nsContentPermissionUtils::NotifyRemoveCo
   PContentPermissionRequestParent* aParent)
 {
   auto it = ContentPermissionRequestParentMap().find(aParent);
   MOZ_ASSERT(it != ContentPermissionRequestParentMap().end());
 
   ContentPermissionRequestParentMap().erase(it);
 }
 
+/* static */ nsTArray<PContentPermissionRequestChild*>
+nsContentPermissionUtils::GetContentPermissionRequestChildById(const TabId& aTabId)
+{
+  nsTArray<PContentPermissionRequestChild*> childArray;
+  for (auto& it : ContentPermissionRequestChildMap()) {
+    if (it.second == aTabId) {
+      childArray.AppendElement(it.first);
+    }
+  }
+
+  return Move(childArray);
+}
+
+/* static */ void
+nsContentPermissionUtils::NotifyRemoveContentPermissionRequestChild(
+  PContentPermissionRequestChild* aChild)
+{
+  auto it = ContentPermissionRequestChildMap().find(aChild);
+  MOZ_ASSERT(it != ContentPermissionRequestChildMap().end());
+
+  ContentPermissionRequestChildMap().erase(it);
+}
+
 NS_IMPL_ISUPPORTS(nsContentPermissionRequester, nsIContentPermissionRequester)
 
 nsContentPermissionRequester::nsContentPermissionRequester(nsPIDOMWindow* aWindow)
   : mWindow(aWindow)
 {
   mListener = new VisibilityChangeListener(mWindow);
 }
 
@@ -604,17 +644,17 @@ nsContentPermissionRequestProxy::Cancel(
   // Don't send out the delete message when the managing protocol (PBrowser) is
   // being destroyed and PContentPermissionRequest will soon be.
   if (mParent->IsBeingDestroyed()) {
     return NS_ERROR_FAILURE;
   }
 
   nsTArray<PermissionChoice> emptyChoices;
 
-  Unused << ContentPermissionRequestParent::Send__delete__(mParent, false, emptyChoices);
+  Unused << mParent->SendNotifyResult(false, emptyChoices);
   mParent = nullptr;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsContentPermissionRequestProxy::Allow(JS::HandleValue aChoices)
 {
   if (mParent == nullptr) {
@@ -673,17 +713,17 @@ nsContentPermissionRequestProxy::Allow(J
         choices.AppendElement(PermissionChoice(type, choice));
       }
     }
   } else {
     MOZ_ASSERT(false, "SelectedChoices should be undefined or an JS object");
     return NS_ERROR_FAILURE;
   }
 
-  Unused << ContentPermissionRequestParent::Send__delete__(mParent, true, choices);
+  Unused << mParent->SendNotifyResult(true, choices);
   mParent = nullptr;
   return NS_OK;
 }
 
 void
 nsContentPermissionRequestProxy::NotifyVisibility(const bool& aIsVisible)
 {
   MOZ_ASSERT(mRequester);
@@ -706,16 +746,17 @@ nsContentPermissionRequestProxy::GetRequ
 NS_IMPL_ISUPPORTS(RemotePermissionRequest, nsIContentPermissionRequestCallback);
 
 RemotePermissionRequest::RemotePermissionRequest(
   nsIContentPermissionRequest* aRequest,
   nsPIDOMWindow* aWindow)
   : mRequest(aRequest)
   , mWindow(aWindow)
   , mIPCOpen(false)
+  , mDestroyed(false)
 {
   mListener = new VisibilityChangeListener(mWindow);
   mListener->SetCallback(this);
 }
 
 void
 RemotePermissionRequest::DoCancel()
 {
@@ -727,21 +768,20 @@ void
 RemotePermissionRequest::DoAllow(JS::HandleValue aChoices)
 {
   NS_ASSERTION(mRequest, "We need a request");
   mRequest->Allow(aChoices);
 }
 
 // PContentPermissionRequestChild
 bool
-RemotePermissionRequest::Recv__delete__(const bool& aAllow,
-                                        InfallibleTArray<PermissionChoice>&& aChoices)
+RemotePermissionRequest::RecvNotifyResult(const bool& aAllow,
+                                          InfallibleTArray<PermissionChoice>&& aChoices)
 {
-  mListener->RemoveListener();
-  mListener = nullptr;
+  Destroy();
 
   if (aAllow && mWindow->IsCurrentInnerWindow()) {
     // Use 'undefined' if no choice is provided.
     if (aChoices.IsEmpty()) {
       DoAllow(JS::UndefinedHandleValue);
       return true;
     }
 
@@ -781,18 +821,30 @@ RemotePermissionRequest::RecvGetVisibili
   }
 
   bool isActive = false;
   docshell->GetIsActive(&isActive);
   Unused << SendNotifyVisibility(isActive);
   return true;
 }
 
+void
+RemotePermissionRequest::Destroy()
+{
+  if (!IPCOpen()) {
+    return;
+  }
+  Unused << this->SendDestroy();
+  mListener->RemoveListener();
+  mListener = nullptr;
+  mDestroyed = true;
+}
+
 NS_IMETHODIMP
 RemotePermissionRequest::NotifyVisibility(bool isVisible)
 {
-  if (!mIPCOpen) {
+  if (!IPCOpen()) {
     return NS_OK;
   }
 
   Unused << SendNotifyVisibility(isVisible);
   return NS_OK;
 }
--- a/dom/base/nsContentPermissionHelper.h
+++ b/dom/base/nsContentPermissionHelper.h
@@ -103,16 +103,22 @@ public:
   static nsresult
   AskPermission(nsIContentPermissionRequest* aRequest, nsPIDOMWindow* aWindow);
 
   static nsTArray<PContentPermissionRequestParent*>
   GetContentPermissionRequestParentById(const TabId& aTabId);
 
   static void
   NotifyRemoveContentPermissionRequestParent(PContentPermissionRequestParent* aParent);
+
+  static nsTArray<PContentPermissionRequestChild*>
+  GetContentPermissionRequestChildById(const TabId& aTabId);
+
+  static void
+  NotifyRemoveContentPermissionRequestChild(PContentPermissionRequestChild* aChild);
 };
 
 class nsContentPermissionRequester final : public nsIContentPermissionRequester
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSICONTENTPERMISSIONREQUESTER
 
@@ -183,42 +189,47 @@ class RemotePermissionRequest final : pu
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSICONTENTPERMISSIONREQUESTCALLBACK
 
   RemotePermissionRequest(nsIContentPermissionRequest* aRequest,
                           nsPIDOMWindow* aWindow);
 
   // It will be called when prompt dismissed.
-  virtual bool Recv__delete__(const bool &aAllow,
-                              InfallibleTArray<PermissionChoice>&& aChoices) override;
+  virtual bool RecvNotifyResult(const bool &aAllow,
+                                InfallibleTArray<PermissionChoice>&& aChoices) override;
 
   virtual bool RecvGetVisibility() override;
 
   void IPDLAddRef()
   {
     mIPCOpen = true;
     AddRef();
   }
 
   void IPDLRelease()
   {
     mIPCOpen = false;
     Release();
   }
 
+  void Destroy();
+
+  bool IPCOpen() const { return mIPCOpen && !mDestroyed; }
+
 private:
   virtual ~RemotePermissionRequest()
   {
     MOZ_ASSERT(!mIPCOpen, "Protocol must not be open when RemotePermissionRequest is destroyed.");
   }
 
   void DoAllow(JS::HandleValue aChoices);
   void DoCancel();
 
   nsCOMPtr<nsIContentPermissionRequest> mRequest;
   nsCOMPtr<nsPIDOMWindow>               mWindow;
   bool                                  mIPCOpen;
+  bool                                  mDestroyed;
   RefPtr<VisibilityChangeListener>    mListener;
 };
 
 #endif // nsContentPermissionHelper_h
 
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -2964,16 +2964,17 @@ ContentChild::AllocPContentPermissionReq
 {
     NS_RUNTIMEABORT("unused");
     return nullptr;
 }
 
 bool
 ContentChild::DeallocPContentPermissionRequestChild(PContentPermissionRequestChild* actor)
 {
+    nsContentPermissionUtils::NotifyRemoveContentPermissionRequestChild(actor);
     auto child = static_cast<RemotePermissionRequest*>(actor);
     child->IPDLRelease();
     return true;
 }
 
 PWebBrowserPersistDocumentChild*
 ContentChild::AllocPWebBrowserPersistDocumentChild(PBrowserChild* aBrowser,
                                                    const uint64_t& aOuterWindowID)
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -2240,20 +2240,17 @@ ContentParent::NotifyTabDestroyed(const 
         --mNumDestroyingTabs;
     }
 
     nsTArray<PContentPermissionRequestParent*> parentArray =
         nsContentPermissionUtils::GetContentPermissionRequestParentById(aTabId);
 
     // Need to close undeleted ContentPermissionRequestParents before tab is closed.
     for (auto& permissionRequestParent : parentArray) {
-        nsTArray<PermissionChoice> emptyChoices;
-        Unused << PContentPermissionRequestParent::Send__delete__(permissionRequestParent,
-                                                                  false,
-                                                                  emptyChoices);
+        Unused << PContentPermissionRequestParent::Send__delete__(permissionRequestParent);
     }
 
     // There can be more than one PBrowser for a given app process
     // because of popup windows.  When the last one closes, shut
     // us down.
     ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
     nsTArray<TabId> tabIds = cpm->GetTabParentsByProcessId(this->ChildID());
     if (tabIds.Length() == 1) {
--- a/dom/ipc/PContentPermissionRequest.ipdl
+++ b/dom/ipc/PContentPermissionRequest.ipdl
@@ -10,17 +10,19 @@ namespace dom {
 
 protocol PContentPermissionRequest
 {
   manager PContent;
 
 parent:
   prompt();
   NotifyVisibility(bool visibility);
+  Destroy();
 
 child:
   GetVisibility();
-  __delete__(bool allow, PermissionChoice[] choices);
+  NotifyResult(bool allow, PermissionChoice[] choices);
+  __delete__();
 };
 
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -91,16 +91,17 @@
 #include "ipc/nsGUIEventIPC.h"
 #include "mozilla/gfx/Matrix.h"
 #include "UnitTransforms.h"
 #include "ClientLayerManager.h"
 #include "LayersLogging.h"
 #include "nsIOService.h"
 #include "nsDOMClassInfoID.h"
 #include "nsColorPickerProxy.h"
+#include "nsContentPermissionHelper.h"
 #include "nsPresShell.h"
 #include "nsIAppsService.h"
 #include "nsNetUtil.h"
 #include "nsIPermissionManager.h"
 #include "nsIScriptError.h"
 #include "mozilla/EventForwards.h"
 #include "nsDeviceContext.h"
 
@@ -2111,17 +2112,16 @@ TabChild::AllocPIndexedDBPermissionReque
             "manually!");
 }
 
 bool
 TabChild::DeallocPIndexedDBPermissionRequestChild(
                                        PIndexedDBPermissionRequestChild* aActor)
 {
   MOZ_ASSERT(aActor);
-
   delete aActor;
   return true;
 }
 
 bool
 TabChild::RecvActivateFrameEvent(const nsString& aType, const bool& capture)
 {
   nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(WebNavigation());
@@ -2223,16 +2223,25 @@ TabChild::RecvHandleAccessKey(nsTArray<u
 }
 
 bool
 TabChild::RecvDestroy()
 {
   MOZ_ASSERT(mDestroyed == false);
   mDestroyed = true;
 
+  nsTArray<PContentPermissionRequestChild*> childArray =
+      nsContentPermissionUtils::GetContentPermissionRequestChildById(GetTabId());
+
+  // Need to close undeleted ContentPermissionRequestChilds before tab is closed.
+  for (auto& permissionRequestChild : childArray) {
+      auto child = static_cast<RemotePermissionRequest*>(permissionRequestChild);
+      child->Destroy();
+  }
+
   while (mActiveSuppressDisplayport > 0) {
     APZCCallbackHelper::SuppressDisplayport(false);
     mActiveSuppressDisplayport--;
   }
 
   if (mTabChildGlobal) {
     // Message handlers are called from the event loop, so it better be safe to
     // run script.