Bug 1114507 - Part 2: Add/release the appId's refcnt in oop case. r=kanru
authorchunminchang <cchang@mozilla.com>
Fri, 28 Aug 2015 03:18:00 -0400
changeset 260442 b08257bf7dfed0a202a3b5e0a0d4395f228dcbfa
parent 260441 2beac00f41a16c1a1c42838abea2875f840fca86
child 260443 d3a6c5aa62b2f619acbee16109b49abee0c917c0
push id29311
push usercbook@mozilla.com
push dateWed, 02 Sep 2015 11:53:18 +0000
treeherdermozilla-central@1b687fcb5213 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskanru
bugs1114507
milestone43.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 1114507 - Part 2: Add/release the appId's refcnt in oop case. r=kanru
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/ContentProcessManager.cpp
dom/ipc/ContentProcessManager.h
dom/ipc/PContent.ipdl
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1463,17 +1463,19 @@ ContentParent::Init()
     if (obs) {
         size_t length = ArrayLength(sObserverTopics);
         for (size_t i = 0; i < length; ++i) {
             obs->AddObserver(this, sObserverTopics[i], false);
         }
     }
     Preferences::AddStrongObserver(this, "");
     if (obs) {
-        obs->NotifyObservers(static_cast<nsIObserver*>(this), "ipc:content-created", nullptr);
+        nsAutoString cpId;
+        cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
+        obs->NotifyObservers(static_cast<nsIObserver*>(this), "ipc:content-created", cpId.get());
     }
 
 #ifdef ACCESSIBILITY
     // If accessibility is running in chrome process then start it in content
     // process.
     if (nsIPresShell::IsAccessibilityActive()) {
         unused << SendActivateA11y();
     }
@@ -2052,17 +2054,19 @@ ContentParent::ActorDestroy(ActorDestroy
                     crashReporter->GenerateCrashReport(this, nullptr);
                 }
 
                 nsAutoString dumpID(crashReporter->ChildDumpID());
                 props->SetPropertyAsAString(NS_LITERAL_STRING("dumpID"), dumpID);
             }
 #endif
         }
-        obs->NotifyObservers((nsIPropertyBag2*) props, "ipc:content-shutdown", nullptr);
+        nsAutoString cpId;
+        cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
+        obs->NotifyObservers((nsIPropertyBag2*) props, "ipc:content-shutdown", cpId.get());
     }
 
     // Remove any and all idle listeners.
     nsCOMPtr<nsIIdleService> idleService =
         do_GetService("@mozilla.org/widget/idleservice;1");
     MOZ_ASSERT(idleService);
     nsRefPtr<ParentIdleListener> listener;
     for (int32_t i = mIdleListeners.Length() - 1; i >= 0; --i) {
@@ -2080,52 +2084,87 @@ ContentParent::ActorDestroy(ActorDestroy
     // may be that the kungFuDeathGrip above is the last reference to
     // |this|.  If so, when we go out of scope here, we're deleted and
     // all hell breaks loose.
     //
     // This runnable ensures that a reference to |this| lives on at
     // least until after the current task finishes running.
     NS_DispatchToCurrentThread(new DelayedDeleteContentParentTask(this));
 
-    // Destroy any processes created by this ContentParent
-    ContentProcessManager *cpm = ContentProcessManager::GetSingleton();
+    // Release the appId's reference count of any processes
+    // created by this ContentParent and the frame opened by this ContentParent
+    // if this ContentParent crashes.
+    ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
     nsTArray<ContentParentId> childIDArray =
         cpm->GetAllChildProcessById(this->ChildID());
+    if (why == AbnormalShutdown) {
+      nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
+      if(permMgr) {
+        // Release the appId's reference count of its child-processes
+        for (uint32_t i = 0; i < childIDArray.Length(); i++) {
+          nsTArray<TabContext> tabCtxs = cpm->GetTabContextByContentProcess(childIDArray[i]);
+          for (uint32_t j = 0 ; j < tabCtxs.Length() ; j++) {
+            if (tabCtxs[j].OwnOrContainingAppId() != nsIScriptSecurityManager::NO_APP_ID) {
+              permMgr->ReleaseAppId(tabCtxs[j].OwnOrContainingAppId());
+            }
+          }
+        }
+        // Release the appId's reference count belong to itself
+        nsTArray<TabContext> tabCtxs = cpm->GetTabContextByContentProcess(mChildID);
+        for (uint32_t i = 0; i < tabCtxs.Length() ; i++) {
+          if (tabCtxs[i].OwnOrContainingAppId()!= nsIScriptSecurityManager::NO_APP_ID) {
+            permMgr->ReleaseAppId(tabCtxs[i].OwnOrContainingAppId());
+          }
+        }
+      }
+    }
+
+    // Destroy any processes created by this ContentParent
     for(uint32_t i = 0; i < childIDArray.Length(); i++) {
         ContentParent* cp = cpm->GetContentProcessById(childIDArray[i]);
         MessageLoop::current()->PostTask(
             FROM_HERE,
             NewRunnableMethod(cp, &ContentParent::ShutDownProcess,
                               SEND_SHUTDOWN_MESSAGE));
     }
     cpm->RemoveContentProcess(this->ChildID());
 
     if (mDriverCrashGuard) {
       mDriverCrashGuard->NotifyCrashed();
     }
 }
 
 void
-ContentParent::NotifyTabDestroying(PBrowserParent* aTab)
-{
+ContentParent::NotifyTabDestroying(const TabId& aTabId,
+                                   const ContentParentId& aCpId)
+{
+  if (XRE_IsParentProcess()) {
     // There can be more than one PBrowser for a given app process
     // because of popup windows.  PBrowsers can also destroy
     // concurrently.  When all the PBrowsers are destroying, kick off
     // another task to ensure the child process *really* shuts down,
     // even if the PBrowsers themselves never finish destroying.
-    int32_t numLiveTabs = ManagedPBrowserParent().Length();
-    ++mNumDestroyingTabs;
-    if (mNumDestroyingTabs != numLiveTabs) {
+    ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+    ContentParent* cp = cpm->GetContentProcessById(aCpId);
+    if (!cp) {
+        return;
+    }
+    ++cp->mNumDestroyingTabs;
+    nsTArray<TabId> tabIds = cpm->GetTabParentsByProcessId(aCpId);
+    if (static_cast<size_t>(cp->mNumDestroyingTabs) != tabIds.Length()) {
         return;
     }
 
     // We're dying now, so prevent this content process from being
     // recycled during its shutdown procedure.
-    MarkAsDead();
-    StartForceKillTimer();
+    cp->MarkAsDead();
+    cp->StartForceKillTimer();
+  } else {
+    ContentChild::GetSingleton()->SendNotifyTabDestroying(aTabId, aCpId);
+  }
 }
 
 void
 ContentParent::StartForceKillTimer()
 {
     if (mForceKillTimer || !mIPCOpen) {
         return;
     }
@@ -2137,39 +2176,40 @@ ContentParent::StartForceKillTimer()
         mForceKillTimer->InitWithFuncCallback(ContentParent::ForceKillTimerCallback,
                                               this,
                                               timeoutSecs * 1000,
                                               nsITimer::TYPE_ONE_SHOT);
     }
 }
 
 void
-ContentParent::NotifyTabDestroyed(PBrowserParent* aTab,
+ContentParent::NotifyTabDestroyed(const TabId& aTabId,
                                   bool aNotifiedDestroying)
 {
     if (aNotifiedDestroying) {
         --mNumDestroyingTabs;
     }
 
-    TabId id = static_cast<TabParent*>(aTab)->GetTabId();
     nsTArray<PContentPermissionRequestParent*> parentArray =
-        nsContentPermissionUtils::GetContentPermissionRequestParentById(id);
+        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);
     }
 
     // There can be more than one PBrowser for a given app process
     // because of popup windows.  When the last one closes, shut
     // us down.
-    if (ManagedPBrowserParent().Length() == 1) {
+    ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+    nsTArray<TabId> tabIds = cpm->GetTabParentsByProcessId(this->ChildID());
+    if (tabIds.Length() == 1) {
         // In the case of normal shutdown, send a shutdown message to child to
         // allow it to perform shutdown tasks.
         MessageLoop::current()->PostTask(
             FROM_HERE,
             NewRunnableMethod(this, &ContentParent::ShutDownProcess,
                               SEND_SHUTDOWN_MESSAGE));
     }
 }
@@ -4894,38 +4934,55 @@ ContentParent::NotifyUpdatedDictionaries
 
 /*static*/ TabId
 ContentParent::AllocateTabId(const TabId& aOpenerTabId,
                              const IPCTabContext& aContext,
                              const ContentParentId& aCpId)
 {
     TabId tabId;
     if (XRE_IsParentProcess()) {
-        ContentProcessManager *cpm = ContentProcessManager::GetSingleton();
+        ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
         tabId = cpm->AllocateTabId(aOpenerTabId, aContext, aCpId);
+        // Add appId's reference count in oop case
+        if (tabId) {
+          PermissionManagerAddref(aCpId, tabId);
+        }
     }
     else {
         ContentChild::GetSingleton()->SendAllocateTabId(aOpenerTabId,
                                                         aContext,
                                                         aCpId,
                                                         &tabId);
     }
     return tabId;
 }
 
 /*static*/ void
 ContentParent::DeallocateTabId(const TabId& aTabId,
-                               const ContentParentId& aCpId)
+                               const ContentParentId& aCpId,
+                               bool aMarkedDestroying)
 {
     if (XRE_IsParentProcess()) {
+        // Release appId's reference count in oop case
+        if (aTabId) {
+          PermissionManagerRelease(aCpId, aTabId);
+        }
+
+        ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+        ContentParent* cp = cpm->GetContentProcessById(aCpId);
+
+        cp->NotifyTabDestroyed(aTabId, aMarkedDestroying);
+
         ContentProcessManager::GetSingleton()->DeallocateTabId(aCpId,
                                                                aTabId);
     }
     else {
-        ContentChild::GetSingleton()->SendDeallocateTabId(aTabId);
+        ContentChild::GetSingleton()->SendDeallocateTabId(aTabId,
+                                                          aCpId,
+                                                          aMarkedDestroying);
     }
 }
 
 bool
 ContentParent::RecvAllocateTabId(const TabId& aOpenerTabId,
                                  const IPCTabContext& aContext,
                                  const ContentParentId& aCpId,
                                  TabId* aTabId)
@@ -4933,19 +4990,29 @@ ContentParent::RecvAllocateTabId(const T
     *aTabId = AllocateTabId(aOpenerTabId, aContext, aCpId);
     if (!(*aTabId)) {
         return false;
     }
     return true;
 }
 
 bool
-ContentParent::RecvDeallocateTabId(const TabId& aTabId)
-{
-    DeallocateTabId(aTabId, this->ChildID());
+ContentParent::RecvDeallocateTabId(const TabId& aTabId,
+                                   const ContentParentId& aCpId,
+                                   const bool& aMarkedDestroying)
+{
+    DeallocateTabId(aTabId, aCpId, aMarkedDestroying);
+    return true;
+}
+
+bool
+ContentParent::RecvNotifyTabDestroying(const TabId& aTabId,
+                                       const ContentParentId& aCpId)
+{
+    NotifyTabDestroying(aTabId, aCpId);
     return true;
 }
 
 nsTArray<TabContext>
 ContentParent::GetManagedTabContext()
 {
     return Move(ContentProcessManager::GetSingleton()->
         GetTabContextByContentProcess(this->ChildID()));
@@ -5106,16 +5173,48 @@ ContentParent::AllocPContentPermissionRe
 bool
 ContentParent::DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor)
 {
     nsContentPermissionUtils::NotifyRemoveContentPermissionRequestParent(actor);
     delete actor;
     return true;
 }
 
+/* static */ bool
+ContentParent::PermissionManagerAddref(const ContentParentId& aCpId,
+                                       const TabId& aTabId)
+{
+  MOZ_ASSERT(XRE_IsParentProcess(),
+             "Call PermissionManagerAddref in content process!");
+  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+  uint32_t appId = cpm->GetAppIdByProcessAndTabId(aCpId, aTabId);
+  nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
+  if (appId != nsIScriptSecurityManager::NO_APP_ID && permMgr) {
+    permMgr->AddrefAppId(appId);
+    return true;
+  }
+  return false;
+}
+
+/* static */ bool
+ContentParent::PermissionManagerRelease(const ContentParentId& aCpId,
+                                        const TabId& aTabId)
+{
+  MOZ_ASSERT(XRE_IsParentProcess(),
+             "Call PermissionManagerRelease in content process!");
+  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+  uint32_t appId = cpm->GetAppIdByProcessAndTabId(aCpId, aTabId);
+  nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
+  if (appId != nsIScriptSecurityManager::NO_APP_ID && permMgr) {
+    permMgr->ReleaseAppId(appId);
+    return true;
+  }
+  return false;
+}
+
 bool
 ContentParent::RecvGetBrowserConfiguration(const nsCString& aURI, BrowserConfiguration* aConfig)
 {
     MOZ_ASSERT(XRE_IsParentProcess());
 
     return GetBrowserConfiguration(aURI, *aConfig);;
 }
 
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -207,32 +207,47 @@ public:
                                     nsIPrincipal* aPrincipal) override;
     virtual bool CheckPermission(const nsAString& aPermission) override;
     virtual bool CheckManifestURL(const nsAString& aManifestURL) override;
     virtual bool CheckAppHasPermission(const nsAString& aPermission) override;
     virtual bool CheckAppHasStatus(unsigned short aStatus) override;
     virtual bool KillChild() override;
 
     /** Notify that a tab is beginning its destruction sequence. */
-    void NotifyTabDestroying(PBrowserParent* aTab);
+    static void NotifyTabDestroying(const TabId& aTabId,
+                                    const ContentParentId& aCpId);
     /** Notify that a tab was destroyed during normal operation. */
-    void NotifyTabDestroyed(PBrowserParent* aTab,
+    void NotifyTabDestroyed(const TabId& aTabId,
                             bool aNotifiedDestroying);
 
     TestShellParent* CreateTestShell();
     bool DestroyTestShell(TestShellParent* aTestShell);
     TestShellParent* GetTestShellSingleton();
     jsipc::CPOWManager* GetCPOWManager() override;
 
     static TabId
     AllocateTabId(const TabId& aOpenerTabId,
                   const IPCTabContext& aContext,
                   const ContentParentId& aCpId);
     static void
-    DeallocateTabId(const TabId& aTabId, const ContentParentId& aCpId);
+    DeallocateTabId(const TabId& aTabId,
+                    const ContentParentId& aCpId,
+                    bool aMarkedDestroying);
+
+    /*
+     * Add the appId's reference count by the given ContentParentId and TabId
+     */
+    static bool
+    PermissionManagerAddref(const ContentParentId& aCpId, const TabId& aTabId);
+
+    /*
+     * Release the appId's reference count by the given ContentParentId and TabId
+     */
+    static bool
+    PermissionManagerRelease(const ContentParentId& aCpId, const TabId& aTabId);
 
     static bool
     GetBrowserConfiguration(const nsCString& aURI, BrowserConfiguration& aConfig);
 
     void ReportChildAlreadyBlocked();
     bool RequestRunToCompletion();
 
     bool IsAlive();
@@ -351,17 +366,22 @@ public:
         PBlobParent* aActor,
         const BlobConstructorParams& aParams) override;
 
     virtual bool RecvAllocateTabId(const TabId& aOpenerTabId,
                                    const IPCTabContext& aContext,
                                    const ContentParentId& aCpId,
                                    TabId* aTabId) override;
 
-    virtual bool RecvDeallocateTabId(const TabId& aTabId) override;
+    virtual bool RecvDeallocateTabId(const TabId& aTabId,
+                                     const ContentParentId& aCpId,
+                                     const bool& aMarkedDestroying) override;
+
+    virtual bool RecvNotifyTabDestroying(const TabId& aTabId,
+                                         const ContentParentId& aCpId) override;
 
     nsTArray<TabContext> GetManagedTabContext();
 
     virtual POfflineCacheUpdateParent*
     AllocPOfflineCacheUpdateParent(const URIParams& aManifestURI,
                                    const URIParams& aDocumentURI,
                                    const bool& aStickDocument,
                                    const TabId& aTabId) override;
--- a/dom/ipc/ContentProcessManager.cpp
+++ b/dom/ipc/ContentProcessManager.cpp
@@ -7,16 +7,17 @@
 #include "ContentProcessManager.h"
 #include "ContentParent.h"
 #include "mozilla/dom/TabParent.h"
 
 #include "mozilla/StaticPtr.h"
 #include "mozilla/ClearOnShutdown.h"
 
 #include "nsPrintfCString.h"
+#include "nsIScriptSecurityManager.h"
 
 // XXX need another bug to move this to a common header.
 #ifdef DISABLE_ASSERTS_FOR_FUZZING
 #define ASSERT_UNLESS_FUZZING(...) do { } while (0)
 #else
 #define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
 #endif
 
@@ -329,10 +330,45 @@ ContentProcessManager::GetTopLevelTabPar
       return nullptr;
     }
   } while (parentCpId);
 
   // Get the top level TabParent by the current ContentParentId and TabId
   return GetTabParentByProcessAndTabId(currentCpId, currentTabId);
 }
 
+nsTArray<TabId>
+ContentProcessManager::GetTabParentsByProcessId(const ContentParentId& aChildCpId)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsTArray<TabId> tabIdList;
+  auto iter = mContentParentMap.find(aChildCpId);
+  if (NS_WARN_IF(iter == mContentParentMap.end())) {
+    ASSERT_UNLESS_FUZZING();
+    return Move(tabIdList);
+  }
+
+  for (auto remoteFrameIter = iter->second.mRemoteFrames.begin();
+      remoteFrameIter != iter->second.mRemoteFrames.end();
+      ++remoteFrameIter) {
+    tabIdList.AppendElement(remoteFrameIter->first);
+  }
+
+  return Move(tabIdList);
+}
+
+uint32_t
+ContentProcessManager::GetAppIdByProcessAndTabId(const ContentParentId& aChildCpId,
+                                                 const TabId& aChildTabId)
+{
+  uint32_t appId = nsIScriptSecurityManager::NO_APP_ID;
+  if (aChildCpId && aChildTabId) {
+    TabContext tabContext;
+    if (GetTabContextByProcessAndTabId(aChildCpId, aChildTabId, &tabContext)) {
+      appId = tabContext.OwnOrContainingAppId();
+    }
+  }
+  return appId;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/ContentProcessManager.h
+++ b/dom/ipc/ContentProcessManager.h
@@ -106,16 +106,23 @@ public:
    * Query a tab's opener id by the given process and tab id.
    * XXX Currently not used. Plan to be used for bug 1020179.
    */
   bool GetRemoteFrameOpenerTabId(const ContentParentId& aChildCpId,
                                  const TabId& aChildTabId,
                                  /*out*/ TabId* aOpenerTabId);
 
   /**
+   * Get all TabParents' Ids managed by the givent content process.
+   * Return empty array when TabParent couldn't be found via aChildCpId
+   */
+  nsTArray<TabId>
+  GetTabParentsByProcessId(const ContentParentId& aChildCpId);
+
+  /**
    * Get the TabParent by the given content process and tab id.
    * Return nullptr when TabParent couldn't be found via aChildCpId
    * and aChildTabId.
    * (or probably because the TabParent is not in the chrome process)
    */
   already_AddRefed<TabParent>
   GetTabParentByProcessAndTabId(const ContentParentId& aChildCpId,
                                 const TabId& aChildTabId);
@@ -130,16 +137,25 @@ public:
    *  always the opener tab of the given tab in content process. This function
    *  will call GetTabParentByProcessAndTabId iteratively until the Tab returned
    *  is belong to the chrome process.
    */
   already_AddRefed<TabParent>
   GetTopLevelTabParentByProcessAndTabId(const ContentParentId& aChildCpId,
                                         const TabId& aChildTabId);
 
+  /**
+   * Return appId by given TabId and ContentParentId.
+   * It will return nsIScriptSecurityManager::NO_APP_ID
+   * if the given tab is not an app.
+   */
+  uint32_t
+  GetAppIdByProcessAndTabId(const ContentParentId& aChildCpId,
+                            const TabId& aChildTabId);
+
 private:
   static StaticAutoPtr<ContentProcessManager> sSingleton;
   TabId mUniqueId;
   std::map<ContentParentId, ContentProcessInfo> mContentParentMap;
 
   ContentProcessManager() {MOZ_COUNT_CTOR(ContentProcessManager);};
 };
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -986,19 +986,26 @@ parent:
     FlushPendingFileDeletions();
 
     /**
      * Tell the chrome process there is an creation of PBrowser.
      * return a system-wise unique Id.
      */
     sync AllocateTabId(TabId openerTabId, IPCTabContext context, ContentParentId cpId)
         returns (TabId tabId);
-    async DeallocateTabId(TabId tabId);
+    async DeallocateTabId(TabId tabId,
+                          ContentParentId cpId,
+                          bool aMarkedDestroying);
 
     /**
+     * Tell the chrome process there is a destruction of PBrowser(Tab)
+     */
+    async NotifyTabDestroying(TabId tabId,
+                              ContentParentId cpId);
+    /**
      * Starts an offline application cache update.
      * @param manifestURI
      *   URI of the manifest to fetch, the application cache group ID
      * @param documentURI
      *   URI of the document that referred the manifest
      * @param stickDocument
      *   True if the update was initiated by a document load that referred
      *   a manifest.
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -431,74 +431,102 @@ TabParent::IsVisible()
   }
 
   bool visible = false;
   frameLoader->GetVisible(&visible);
   return visible;
 }
 
 void
-TabParent::Destroy()
+TabParent::DestroyInternal()
 {
-  if (mIsDestroyed) {
-    return;
-  }
-
   IMEStateManager::OnTabParentDestroying(this);
 
   RemoveWindowListeners();
 
   // If this fails, it's most likely due to a content-process crash,
   // and auto-cleanup will kick in.  Otherwise, the child side will
   // destroy itself and send back __delete__().
   unused << SendDestroy();
 
   if (RenderFrameParent* frame = GetRenderFrame()) {
     RemoveTabParentFromTable(frame->GetLayersId());
     frame->Destroy();
   }
-  mIsDestroyed = true;
-
-  if (XRE_IsParentProcess()) {
-    Manager()->AsContentParent()->NotifyTabDestroying(this);
-  }
 
   // Let all PluginWidgets know we are tearing down. Prevents
   // these objects from sending async events after the child side
   // is shut down.
   const nsTArray<PPluginWidgetParent*>& kids = ManagedPPluginWidgetParent();
   for (uint32_t idx = 0; idx < kids.Length(); ++idx) {
       static_cast<mozilla::plugins::PluginWidgetParent*>(kids[idx])->ParentDestroy();
   }
+}
+
+void
+TabParent::Destroy()
+{
+  if (mIsDestroyed) {
+    return;
+  }
+
+  DestroyInternal();
+
+  mIsDestroyed = true;
+
+  if (XRE_IsParentProcess()) {
+    ContentParent::NotifyTabDestroying(this->GetTabId(), Manager()->AsContentParent()->ChildID());
+  } else {
+    ContentParent::NotifyTabDestroying(this->GetTabId(), Manager()->ChildID());
+  }
 
   mMarkedDestroying = true;
 }
 
 bool
 TabParent::Recv__delete__()
 {
   if (XRE_IsParentProcess()) {
-    Manager()->AsContentParent()->NotifyTabDestroyed(this, mMarkedDestroying);
     ContentParent::DeallocateTabId(mTabId,
-                                   Manager()->AsContentParent()->ChildID());
+                                   Manager()->AsContentParent()->ChildID(),
+                                   mMarkedDestroying);
   }
   else {
-    ContentParent::DeallocateTabId(mTabId, ContentParentId(0));
+    Manager()->AsContentBridgeParent()->NotifyTabDestroyed();
+    ContentParent::DeallocateTabId(mTabId,
+                                   Manager()->ChildID(),
+                                   mMarkedDestroying);
   }
 
   return true;
 }
 
 void
 TabParent::ActorDestroy(ActorDestroyReason why)
 {
   // Even though TabParent::Destroy calls this, we need to do it here too in
   // case of a crash.
   IMEStateManager::OnTabParentDestroying(this);
 
+  // Prevent executing ContentParent::NotifyTabDestroying in
+  // TabParent::Destroy() called by frameLoader->DestroyComplete() below
+  // when tab crashes in contentprocess because ContentParent::ActorDestroy()
+  // in main process will be triggered before this function
+  // and remove the process information that
+  // ContentParent::NotifyTabDestroying need from mContentParentMap.
+
+  // When tab crashes in content process,
+  // there is no need to call ContentParent::NotifyTabDestroying
+  // because the jobs in ContentParent::NotifyTabDestroying
+  // will be done by ContentParent::ActorDestroy.
+  if (XRE_IsContentProcess() && why == AbnormalShutdown && !mIsDestroyed) {
+    DestroyInternal();
+    mIsDestroyed = true;
+  }
+
   nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader(true);
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (frameLoader) {
     nsCOMPtr<Element> frameElement(mFrameElement);
     ReceiveMessage(CHILD_PROCESS_SHUTDOWN_MESSAGE, false, nullptr, nullptr,
                    nullptr);
     frameLoader->DestroyComplete();
 
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -486,16 +486,17 @@ protected:
     ScreenIntSize mDimensions;
     ScreenOrientationInternal mOrientation;
     float mDPI;
     CSSToLayoutDeviceScale mDefaultScale;
     bool mUpdatedDimensions;
     LayoutDeviceIntPoint mChromeOffset;
 
 private:
+    void DestroyInternal();
     already_AddRefed<nsFrameLoader> GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy = false) const;
     nsRefPtr<nsIContentParent> mManager;
     void TryCacheDPIAndScale();
 
     nsresult UpdatePosition();
 
     CSSPoint AdjustTapToChildWidget(const CSSPoint& aPoint);