Bug 532751 - Notify the nsNPAPIPlugin and the related nsNPAPIPluginInstances when a plugin crashes so that reloading will create a new plugin process, r=josh
authorBenjamin Smedberg <benjamin@smedbergs.us>
Wed, 16 Dec 2009 15:08:45 -0500
changeset 36304 755a66faccf5bc3c79faf026557c463390f7a71a
parent 36303 f34a319078c1f512113100b082b0e12b7e989753
child 36305 a1022a154520ac1aeee1ee6f2023d60153319abd
push idunknown
push userunknown
push dateunknown
reviewersjosh
bugs532751
milestone1.9.3a1pre
Bug 532751 - Notify the nsNPAPIPlugin and the related nsNPAPIPluginInstances when a plugin crashes so that reloading will create a new plugin process, r=josh
dom/plugins/PluginLibrary.h
dom/plugins/PluginModuleParent.cpp
dom/plugins/PluginModuleParent.h
dom/plugins/PluginPRLibrary.h
modules/plugin/base/src/nsNPAPIPlugin.cpp
modules/plugin/base/src/nsNPAPIPlugin.h
modules/plugin/base/src/nsPluginHost.cpp
modules/plugin/base/src/nsPluginHost.h
modules/plugin/test/mochitest/Makefile.in
--- a/dom/plugins/PluginLibrary.h
+++ b/dom/plugins/PluginLibrary.h
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: sw=4 ts=4 et :
  * ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
@@ -38,23 +38,31 @@
 
 #ifndef mozilla_PluginLibrary_h
 #define mozilla_PluginLibrary_h 1
 
 #include "prlink.h"
 #include "npapi.h"
 #include "nscore.h"
 
+class nsNPAPIPlugin;
+
 namespace mozilla {
 
 class PluginLibrary
 {
 public:
   virtual ~PluginLibrary() { }
 
+  /**
+   * Inform this library about the nsNPAPIPlugin which owns it. This
+   * object will hold a weak pointer to the plugin.
+   */
+  virtual void SetPlugin(nsNPAPIPlugin* plugin) = 0;
+
   virtual bool HasRequiredFunctions() = 0;
 
 #if defined(XP_UNIX) && !defined(XP_MACOSX)
   virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error) = 0;
 #else
   virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) = 0;
 #endif
   virtual nsresult NP_Shutdown(NPError* error) = 0;
--- a/dom/plugins/PluginModuleParent.cpp
+++ b/dom/plugins/PluginModuleParent.cpp
@@ -64,16 +64,17 @@ PluginModuleParent::LoadModule(const cha
     return parent;
 }
 
 
 PluginModuleParent::PluginModuleParent(const char* aFilePath)
     : mSubprocess(new PluginProcessParent(aFilePath))
     , mShutdown(false)
     , mNPNIface(NULL)
+    , mPlugin(NULL)
 {
     NS_ASSERTION(mSubprocess, "Out of memory!");
 
     if (!mValidIdentifiers.Init()) {
         NS_ERROR("Out of memory");
     }
 }
 
@@ -92,19 +93,26 @@ PluginModuleParent::~PluginModuleParent(
     }
 }
 
 void
 PluginModuleParent::ActorDestroy(ActorDestroyReason why)
 {
     switch (why) {
     case AbnormalShutdown:
-        // TODObsmedberg: notify the plugin host to forget this plugin module
-        // and instantiate us again.
-        // FALL THROUGH
+        mShutdown = true;
+        // Defer the PluginCrashed method so that we don't re-enter
+        // and potentially modify the actor child list while enumerating it.
+        if (mPlugin) {
+            nsCOMPtr<nsIRunnable> r =
+                new nsRunnableMethod<nsNPAPIPlugin>(
+                    mPlugin, &nsNPAPIPlugin::PluginCrashed);
+            NS_DispatchToMainThread(r);
+        }
+        break;
 
     case NormalShutdown:
         mShutdown = true;
         break;
 
     default:
         NS_ERROR("Unexpected shutdown reason for toplevel actor.");
     }
--- a/dom/plugins/PluginModuleParent.h
+++ b/dom/plugins/PluginModuleParent.h
@@ -92,18 +92,22 @@ protected:
                          const nsTArray<nsCString>& aValues,
                          NPError* rv);
 
     virtual bool
     DeallocPPluginInstance(PPluginInstanceParent* aActor);
 
 public:
     PluginModuleParent(const char* aFilePath);
+    virtual ~PluginModuleParent();
 
-    virtual ~PluginModuleParent();
+    NS_OVERRIDE virtual void SetPlugin(nsNPAPIPlugin* plugin)
+    {
+        mPlugin = plugin;
+    }
 
     NS_OVERRIDE virtual void ActorDestroy(ActorDestroyReason why);
 
     /**
      * LoadModule
      *
      * This may or may not launch a plugin child process,
      * and may or may not be very expensive.
@@ -208,14 +212,15 @@ private:
                              uint16_t mode, int16_t argc, char* argn[],
                              char* argv[], NPSavedData* saved,
                              NPError* error);
 private:
     PluginProcessParent* mSubprocess;
     bool mShutdown;
     const NPNetscapeFuncs* mNPNIface;
     nsTHashtable<nsVoidPtrHashKey> mValidIdentifiers;
+    nsNPAPIPlugin* mPlugin;
 };
 
 } // namespace plugins
 } // namespace mozilla
 
 #endif  // ifndef dom_plugins_PluginModuleParent_h
--- a/dom/plugins/PluginPRLibrary.h
+++ b/dom/plugins/PluginPRLibrary.h
@@ -78,16 +78,18 @@ public:
         // addref here??
     }
 
     virtual ~PluginPRLibrary()
     {
         // unref here??
     }
 
+    virtual void SetPlugin(nsNPAPIPlugin*) { }
+
     virtual bool HasRequiredFunctions() {
         mNP_Initialize = (NP_InitializeFunc)
             PR_FindFunctionSymbol(mLibrary, "NP_Initialize");
         if (!mNP_Initialize)
             return false;
 
         mNP_Shutdown = (NP_ShutdownFunc)
             PR_FindFunctionSymbol(mLibrary, "NP_Shutdown");
--- a/modules/plugin/base/src/nsNPAPIPlugin.cpp
+++ b/modules/plugin/base/src/nsNPAPIPlugin.cpp
@@ -275,16 +275,17 @@ nsNPAPIPlugin::nsNPAPIPlugin(NPPluginFun
   fCallbacks.urlnotify = (NPP_URLNotifyProcPtr)np_callbacks.urlnotify;
   fCallbacks.getvalue = (NPP_GetValueProcPtr)np_callbacks.getvalue;
   fCallbacks.setvalue = (NPP_SetValueProcPtr)np_callbacks.setvalue;
 #else // for everyone else
   memcpy((void*) &fCallbacks, (void*) callbacks, sizeof(fCallbacks));
 #endif
 
   fLibrary = aLibrary;
+  fLibrary->SetPlugin(this);
 }
 
 nsNPAPIPlugin::~nsNPAPIPlugin()
 {
   // reset the callbacks list
   memset((void*) &fCallbacks, 0, sizeof(fCallbacks));
   delete fLibrary;
   fLibrary = NULL;
@@ -294,16 +295,25 @@ nsNPAPIPlugin::~nsNPAPIPlugin()
 #if defined(XP_MACOSX)
 void
 nsNPAPIPlugin::SetPluginRefNum(short aRefNum)
 {
   fPluginRefNum = aRefNum;
 }
 #endif
 
+#ifdef MOZ_IPC
+void
+nsNPAPIPlugin::PluginCrashed()
+{
+  nsRefPtr<nsPluginHost> host = dont_AddRef(nsPluginHost::GetInst());
+  host->PluginCrashed(this);
+}
+#endif
+
 namespace {
 
 #ifdef MOZ_IPC
 
 inline PRBool
 OOPPluginsEnabled()
 {
   if (PR_GetEnv("MOZ_DISABLE_OOP_PLUGINS")) {
--- a/modules/plugin/base/src/nsNPAPIPlugin.h
+++ b/modules/plugin/base/src/nsNPAPIPlugin.h
@@ -89,16 +89,22 @@ public:
   // Constructs and initializes an nsNPAPIPlugin object. A NULL file path
   // will prevent this from calling NP_Initialize.
   static nsresult CreatePlugin(const char* aFilePath, PRLibrary* aLibrary,
                                nsIPlugin** aResult);
 #ifdef XP_MACOSX
   void SetPluginRefNum(short aRefNum);
 #endif
 
+#ifdef MOZ_IPC
+  // The IPC mechanism notifies the nsNPAPIPlugin if the plugin crashes and is
+  // no longer usable.
+  void PluginCrashed();
+#endif
+
 protected:
   // Ensures that the static CALLBACKS is properly initialized
   static void CheckClassInitialized(void);
 
 #ifdef XP_MACOSX
   short fPluginRefNum;
 #endif
 
--- a/modules/plugin/base/src/nsPluginHost.cpp
+++ b/modules/plugin/base/src/nsPluginHost.cpp
@@ -5175,16 +5175,54 @@ NS_IMETHODIMP nsPluginHost::Notify(nsITi
       iter.GetNext()->SendIdleEvent();
     }
     return NS_OK;
   }
 #endif
   return NS_ERROR_FAILURE;
 }
 
+#ifdef MOZ_IPC
+void
+nsPluginHost::PluginCrashed(nsNPAPIPlugin* aPlugin)
+{
+  // Find the nsPluginTag corresponding to this plugin
+
+  nsPluginTag* plugin;
+  for (plugin = mPlugins; plugin; plugin = plugin->mNext) {
+    if (plugin->mEntryPoint == aPlugin)
+      break;
+  }
+  if (!plugin) {
+    NS_WARNING("nsPluginTag not found in nsPluginHost::PluginCrashed");
+    return;
+  }
+
+  // Invalidate each nsPluginInstanceTag for the crashed plugin
+
+  nsPluginInstanceTag** pinstancetag = &mPluginInstanceTagList.mFirst;
+  while (*pinstancetag) {
+    nsPluginInstanceTag* instancetag = *pinstancetag;
+    if (instancetag->mPluginTag == plugin) {
+      *pinstancetag = (*pinstancetag)->mNext;
+      delete instancetag;
+    }
+    else {
+      pinstancetag = &(*pinstancetag)->mNext;
+    }
+  }
+
+  // Only after all instances have been invalidated is it safe to null
+  // out nsPluginTag.mEntryPoint. The next time we try to create an
+  // instance of this plugin we reload it (launch a new plugin process).
+
+  plugin->mEntryPoint = nsnull;
+}
+#endif
+
 nsresult nsPluginStreamListenerPeer::ServeStreamAsFile(nsIRequest *request,
                                                        nsISupports* aContext)
 {
   if (!mInstance)
     return NS_ERROR_FAILURE;
 
   // mInstance->Stop calls mPStreamListener->CleanUpStream(), so stream will be properly clean up
   mInstance->Stop();
--- a/modules/plugin/base/src/nsPluginHost.h
+++ b/modules/plugin/base/src/nsPluginHost.h
@@ -156,17 +156,21 @@ public:
   static PRBool IsJavaMIMEType(const char *aType);
 
   static nsresult GetPrompt(nsIPluginInstanceOwner *aOwner, nsIPrompt **aPrompt);
 
   static nsresult PostPluginUnloadEvent(PRLibrary* aLibrary);
 
   void AddIdleTimeTarget(nsIPluginInstanceOwner* objectFrame, PRBool isVisible);
   void RemoveIdleTimeTarget(nsIPluginInstanceOwner* objectFrame);
-  
+
+#ifdef MOZ_IPC
+  void PluginCrashed(nsNPAPIPlugin* plugin);
+#endif
+
 private:
   nsresult
   TrySetUpPluginInstance(const char *aMimeType, nsIURI *aURL, nsIPluginInstanceOwner *aOwner);
 
   nsresult
   NewEmbeddedPluginStreamListener(nsIURI* aURL, nsIPluginInstanceOwner *aOwner,
                                   nsIPluginInstance* aInstance,
                                   nsIStreamListener** aListener);
--- a/modules/plugin/test/mochitest/Makefile.in
+++ b/modules/plugin/test/mochitest/Makefile.in
@@ -64,22 +64,22 @@ include $(topsrcdir)/config/rules.mk
 		test_pluginstream_poststream.html \
 		test_pluginstream_seek.html \
 		test_pluginstream_newstream.html \
 		test_streamNotify.html \
 		$(NULL)
 
 #		test_npruntime_npnsetexception.html \ Disabled for e10s
 
-#ifdef MOZ_IPC
-#_MOCHITEST_FILES += \
-#		test_crashing.html \
-#		crashing_subpage.html \
-#		$(NULL)
-#endif
+ifdef MOZ_IPC
+_MOCHITEST_FILES += \
+		test_crashing.html \
+		crashing_subpage.html \
+		$(NULL)
+endif
 
 ifeq ($(OS_ARCH),WINNT)
 _MOCHITEST_FILES += \
  		test_windowed_invalidate.html \
         $(NULL)
 endif
 
 _MOCHICHROME_FILES = \