Bug 641685 - Start plugins from the chrome process in e10s (r=bsmedberg)
authorBill McCloskey <wmccloskey@mozilla.com>
Wed, 29 Oct 2014 08:05:36 -0700
changeset 212942 0f7fd7a22708cddd947c73b1de162eb1206fea7b
parent 212941 a469d36c6b825de2b5f93490a7f4eeec57ac8f0f
child 212943 52cdf66f6666be1c296ca3d878c117d05047d2ec
push id27736
push userryanvm@gmail.com
push dateWed, 29 Oct 2014 20:49:13 +0000
treeherdermozilla-central@80e18ff7c7b2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbsmedberg
bugs641685, 1090576
milestone36.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 641685 - Start plugins from the chrome process in e10s (r=bsmedberg) This patch has a few side effects: 1. Plugins in the chrome process are "mirrored" to all content processes, although this mirroring is currently imperfect (bug 1090576) 2. Plugins are no longer sorted by modification date in nsPluginHost. 3. Plugin exceptions are no longer propagated to JS code. They are ignored.
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/CrashReporterChild.cpp
dom/ipc/PContent.ipdl
dom/plugins/base/nsNPAPIPlugin.cpp
dom/plugins/base/nsPluginHost.cpp
dom/plugins/base/nsPluginHost.h
dom/plugins/base/nsPluginTags.cpp
dom/plugins/base/nsPluginTags.h
dom/plugins/ipc/PPluginModule.ipdl
dom/plugins/ipc/PluginBridge.h
dom/plugins/ipc/PluginHangUIParent.cpp
dom/plugins/ipc/PluginHangUIParent.h
dom/plugins/ipc/PluginInstanceChild.cpp
dom/plugins/ipc/PluginInstanceChild.h
dom/plugins/ipc/PluginInstanceParent.cpp
dom/plugins/ipc/PluginInterposeOSX.mm
dom/plugins/ipc/PluginModuleChild.cpp
dom/plugins/ipc/PluginModuleChild.h
dom/plugins/ipc/PluginModuleParent.cpp
dom/plugins/ipc/PluginModuleParent.h
dom/plugins/ipc/PluginProcessChild.cpp
dom/plugins/ipc/PluginProcessChild.h
dom/plugins/ipc/PluginScriptableObjectUtils-inl.h
dom/plugins/ipc/PluginScriptableObjectUtils.h
dom/plugins/ipc/PluginTypes.ipdlh
dom/plugins/ipc/moz.build
dom/plugins/test/mochitest/mochitest.ini
dom/plugins/test/mochitest/test_instance_re-parent.html
dom/plugins/test/mochitest/test_npruntime_npnsetexception.html
dom/plugins/test/mochitest/test_secondPlugin.html
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -38,16 +38,17 @@
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/ipc/TestShellChild.h"
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/PCompositorChild.h"
 #include "mozilla/layers/SharedBufferManagerChild.h"
 #include "mozilla/net/NeckoChild.h"
+#include "mozilla/plugins/PluginModuleParent.h"
 
 #if defined(MOZ_CONTENT_SANDBOX)
 #if defined(XP_WIN)
 #define TARGET_SANDBOX_EXPORTS
 #include "mozilla/sandboxTarget.h"
 #include "nsDirectoryServiceDefs.h"
 #elif defined(XP_LINUX)
 #include "mozilla/Sandbox.h"
@@ -927,16 +928,23 @@ ContentChild::DeallocPCycleCollectWithLo
 {
     // ...so when we get here, there's nothing for us to do.
     //
     // Also, we're already in ~CycleCollectWithLogsChild (q.v.) at
     // this point, so we shouldn't touch the actor in any case.
     return true;
 }
 
+mozilla::plugins::PPluginModuleParent*
+ContentChild::AllocPPluginModuleParent(mozilla::ipc::Transport* aTransport,
+                                       base::ProcessId aOtherProcess)
+{
+    return plugins::PluginModuleContentParent::Create(aTransport, aOtherProcess);
+}
+
 PContentBridgeChild*
 ContentChild::AllocPContentBridgeChild(mozilla::ipc::Transport* aTransport,
                                        base::ProcessId aOtherProcess)
 {
     return ContentBridgeChild::Create(aTransport, aOtherProcess);
 }
 
 PContentBridgeParent*
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -96,16 +96,20 @@ public:
     ContentBridgeParent* GetLastBridge() {
         MOZ_ASSERT(mLastBridge);
         ContentBridgeParent* parent = mLastBridge;
         mLastBridge = nullptr;
         return parent;
     }
     nsRefPtr<ContentBridgeParent> mLastBridge;
 
+    PPluginModuleParent *
+    AllocPPluginModuleParent(mozilla::ipc::Transport* transport,
+                             base::ProcessId otherProcess) MOZ_OVERRIDE;
+
     PContentBridgeParent*
     AllocPContentBridgeParent(mozilla::ipc::Transport* transport,
                               base::ProcessId otherProcess) MOZ_OVERRIDE;
     PContentBridgeChild*
     AllocPContentBridgeChild(mozilla::ipc::Transport* transport,
                              base::ProcessId otherProcess) MOZ_OVERRIDE;
 
     PCompositorChild*
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -65,16 +65,17 @@
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "mozilla/ipc/PFileDescriptorSetParent.h"
 #include "mozilla/ipc/TestShellParent.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/layers/CompositorParent.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/SharedBufferManagerParent.h"
 #include "mozilla/net/NeckoParent.h"
+#include "mozilla/plugins/PluginBridge.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/unused.h"
 #include "nsAnonymousTemporaryFile.h"
 #include "nsAppRunner.h"
 #include "nsAutoPtr.h"
 #include "nsCDefaultURIFixup.h"
@@ -917,16 +918,30 @@ ContentParent::AnswerBridgeToChildProces
         return PContentBridge::Bridge(this, cp);
     } else {
         // You can't bridge to a process you didn't open!
         KillHard();
         return false;
     }
 }
 
+bool
+ContentParent::AnswerLoadPlugin(const uint32_t& aPluginId)
+{
+    return mozilla::plugins::SetupBridge(aPluginId, this);
+}
+
+bool
+ContentParent::RecvFindPlugins(const uint32_t& aPluginEpoch,
+                               nsTArray<PluginTag>* aPlugins,
+                               uint32_t* aNewPluginEpoch)
+{
+    return mozilla::plugins::FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch);
+}
+
 /*static*/ TabParent*
 ContentParent::CreateBrowserOrApp(const TabContext& aContext,
                                   Element* aFrameElement,
                                   ContentParent* aOpenerContentParent)
 {
     if (!sCanLaunchSubprocesses) {
         return nullptr;
     }
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -143,16 +143,21 @@ public:
 
     virtual bool RecvCreateChildProcess(const IPCTabContext& aContext,
                                         const hal::ProcessPriority& aPriority,
                                         uint64_t* aId,
                                         bool* aIsForApp,
                                         bool* aIsForBrowser) MOZ_OVERRIDE;
     virtual bool AnswerBridgeToChildProcess(const uint64_t& id) MOZ_OVERRIDE;
 
+    virtual bool AnswerLoadPlugin(const uint32_t& aPluginId) MOZ_OVERRIDE;
+    virtual bool RecvFindPlugins(const uint32_t& aPluginEpoch,
+                                 nsTArray<PluginTag>* aPlugins,
+                                 uint32_t* aNewPluginEpoch) MOZ_OVERRIDE;
+
     NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ContentParent, nsIObserver)
 
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     NS_DECL_NSIOBSERVER
     NS_DECL_NSIDOMGEOPOSITIONCALLBACK
 
     /**
      * MessageManagerCallback methods that we override.
--- a/dom/ipc/CrashReporterChild.cpp
+++ b/dom/ipc/CrashReporterChild.cpp
@@ -20,17 +20,17 @@ CrashReporterChild::GetCrashReporter()
   const InfallibleTArray<PCrashReporterChild*>* reporters = nullptr;
   switch (XRE_GetProcessType()) {
     case GeckoProcessType_Content: {
       ContentChild* child = ContentChild::GetSingleton();
       reporters = &child->ManagedPCrashReporterChild();
       break;
     }
     case GeckoProcessType_Plugin: {
-      PluginModuleChild* child = PluginModuleChild::current();
+      PluginModuleChild* child = PluginModuleChild::GetChrome();
       reporters = &child->ManagedPCrashReporterChild();
       break;
     }
     default:
       break;
   }
   if (reporters && reporters->Length() > 0) {
     return reporters->ElementAt(0);
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -20,32 +20,34 @@ include protocol PDeviceStorageRequest;
 include protocol PFileDescriptorSet;
 include protocol PFMRadio;
 include protocol PFileSystemRequest;
 include protocol PHal;
 include protocol PImageBridge;
 include protocol PMemoryReportRequest;
 include protocol PMobileConnection;
 include protocol PNecko;
+include protocol PPluginModule;
 include protocol PPrinting;
 include protocol PScreenManager;
 include protocol PSharedBufferManager;
 include protocol PSms;
 include protocol PSpeechSynthesis;
 include protocol PStorage;
 include protocol PTelephony;
 include protocol PTestShell;
 include protocol PVoicemail;
 include protocol PJavaScript;
 include protocol PRemoteSpellcheckEngine;
 include DOMTypes;
 include JavaScriptTypes;
 include InputStreamParams;
 include PTabContext;
 include URIParams;
+include PluginTypes;
 include ProtocolTypes;
 
 // Workaround to prevent error if PContentChild.cpp & PContentBridgeParent.cpp
 // are put into different UnifiedProtocolsXX.cpp files.
 // XXX Remove this once bug 1069073 is fixed
 include "mozilla/dom/PContentBridgeParent.h";
 
 include "mozilla/dom/indexedDB/SerializationHelpers.h";
@@ -323,16 +325,18 @@ struct ClipboardCapabilities {
 
 union MaybeFileDesc {
     FileDescriptor;
     void_t;
 };
 
 prio(normal upto high) intr protocol PContent
 {
+    parent spawns PPluginModule;
+
     parent opens PCompositor;
     parent opens PSharedBufferManager;
     parent opens PImageBridge;
     child opens PBackground;
 
     manages PAsmJSCacheEntry;
     manages PBlob;
     manages PBluetooth;
@@ -527,16 +531,39 @@ parent:
         returns (bool isOffline, nsString[] dictionaries,
                  ClipboardCapabilities clipboardCaps);
 
     sync CreateChildProcess(IPCTabContext context,
                             ProcessPriority priority)
         returns (uint64_t id, bool isForApp, bool isForBrowser);
     intr BridgeToChildProcess(uint64_t id);
 
+    /**
+     * This call connects the content process to a plugin process. While this
+     * call runs, a new PluginModuleParent will be created in the ContentChild
+     * via bridging. The corresponding PluginModuleChild will live in the plugin
+     * process. We use intr semantics here to ensure that the PluginModuleParent
+     * allocation message is dispatched before LoadPlugin returns.
+     */
+    intr LoadPlugin(uint32_t pluginId);
+
+    /**
+     * This call returns the set of plugins loaded in the chrome
+     * process. However, in many cases this set will not have changed since the
+     * last FindPlugins message. Consequently, the chrome process increments an
+     * epoch number every time the set of plugins changes. The content process
+     * sends up the last epoch it observed. If the epochs are the same, the
+     * chrome process returns no plugins. Otherwise it returns a complete list.
+     *
+     * |pluginEpoch| is the epoch last observed by the content
+     * process. |newPluginEpoch| is the current epoch in the chrome process. If
+     * |pluginEpoch == newPluginEpoch|, then |plugins| will be left empty.
+     */
+    sync FindPlugins(uint32_t pluginEpoch) returns (PluginTag[] plugins, uint32_t newPluginEpoch);
+
     async PJavaScript();
 
     sync PRemoteSpellcheckEngine();
     PDeviceStorageRequest(DeviceStorageParams params);
 
     PFileSystemRequest(FileSystemParams params);
 
     sync PCrashReporter(NativeThreadId tid, uint32_t processType);
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -75,17 +75,18 @@
 #include "mozilla/Mutex.h"
 #include "mozilla/PluginLibrary.h"
 using mozilla::PluginLibrary;
 
 #include "mozilla/PluginPRLibrary.h"
 using mozilla::PluginPRLibrary;
 
 #include "mozilla/plugins/PluginModuleParent.h"
-using mozilla::plugins::PluginModuleParent;
+using mozilla::plugins::PluginModuleChromeParent;
+using mozilla::plugins::PluginModuleContentParent;
 
 #ifdef MOZ_X11
 #include "mozilla/X11Util.h"
 #endif
 
 #ifdef XP_WIN
 #include <windows.h>
 #include "mozilla/WindowsVersion.h"
@@ -242,16 +243,20 @@ nsNPAPIPlugin::PluginCrashed(const nsASt
 {
   nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
   host->PluginCrashed(this, pluginDumpID, browserDumpID);
 }
 
 bool
 nsNPAPIPlugin::RunPluginOOP(const nsPluginTag *aPluginTag)
 {
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    return true;
+  }
+
 #if (MOZ_WIDGET_GTK == 3)
   // We force OOP on Linux/GTK3 because some plugins use GTK2 and both GTK
   // libraries can't be loaded in the same process.
   return true;
 #else
   if (PR_GetEnv("MOZ_DISABLE_OOP_PLUGINS")) {
     return false;
   }
@@ -383,18 +388,22 @@ nsNPAPIPlugin::RunPluginOOP(const nsPlug
 
 inline PluginLibrary*
 GetNewPluginLibrary(nsPluginTag *aPluginTag)
 {
   if (!aPluginTag) {
     return nullptr;
   }
 
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    return PluginModuleContentParent::LoadModule(aPluginTag->mId);
+  }
+
   if (nsNPAPIPlugin::RunPluginOOP(aPluginTag)) {
-    return PluginModuleParent::LoadModule(aPluginTag->mFullPath.get());
+    return PluginModuleChromeParent::LoadModule(aPluginTag->mFullPath.get(), aPluginTag->mId);
   }
   return new PluginPRLibrary(aPluginTag->mFullPath.get(), aPluginTag->mLibrary);
 }
 
 // Creates an nsNPAPIPlugin object. One nsNPAPIPlugin object exists per plugin (not instance).
 nsresult
 nsNPAPIPlugin::CreatePlugin(nsPluginTag *aPluginTag, nsNPAPIPlugin** aResult)
 {
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -43,17 +43,20 @@
 #include "nsPluginLogging.h"
 #include "nsIScriptChannel.h"
 #include "nsIBlocklistService.h"
 #include "nsVersionComparator.h"
 #include "nsIObjectLoadingContent.h"
 #include "nsIWritablePropertyBag2.h"
 #include "nsICategoryManager.h"
 #include "nsPluginStreamListenerPeer.h"
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/LoadInfo.h"
+#include "mozilla/plugins/PluginBridge.h"
+#include "mozilla/plugins/PluginTypes.h"
 #include "mozilla/Preferences.h"
 
 #include "nsEnumeratorUtils.h"
 #include "nsXPCOM.h"
 #include "nsXPCOMCID.h"
 #include "nsISupportsPrimitives.h"
 
 #include "nsXULAppAPI.h"
@@ -101,16 +104,17 @@
 #endif
 
 #if MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
 
 using namespace mozilla;
 using mozilla::TimeStamp;
+using mozilla::plugins::PluginTag;
 
 // Null out a strong ref to a linked list iteratively to avoid
 // exhausting the stack (bug 486349).
 #define NS_ITERATIVE_UNREF_LIST(type_, list_, mNext_)                \
   {                                                                  \
     while (list_) {                                                  \
       type_ temp = list_->mNext_;                                    \
       list_->mNext_ = nullptr;                                        \
@@ -304,16 +308,20 @@ nsPluginHost::GetInst()
 }
 
 bool nsPluginHost::IsRunningPlugin(nsPluginTag * aPluginTag)
 {
   if (!aPluginTag || !aPluginTag->mPlugin) {
     return false;
   }
 
+  if (aPluginTag->mContentProcessRunningCount) {
+    return true;
+  }
+
   for (uint32_t i = 0; i < mInstances.Length(); i++) {
     nsNPAPIPluginInstance *instance = mInstances[i].get();
     if (instance &&
         instance->GetPlugin() == aPluginTag->mPlugin &&
         instance->IsRunning()) {
       return true;
     }
   }
@@ -1267,16 +1275,86 @@ nsresult nsPluginHost::EnsurePluginLoade
     if (NS_FAILED(rv)) {
       return rv;
     }
     aPluginTag->mPlugin = plugin;
   }
   return NS_OK;
 }
 
+nsresult
+nsPluginHost::GetPluginForContentProcess(uint32_t aPluginId, nsNPAPIPlugin** aPlugin)
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
+  // If plugins haven't been scanned yet, do so now
+  LoadPlugins();
+
+  nsPluginTag* pluginTag = PluginWithId(aPluginId);
+  if (pluginTag) {
+    nsresult rv = EnsurePluginLoaded(pluginTag);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+
+    // We only get here if a content process doesn't have a PluginModuleParent
+    // for the given plugin already. Therefore, this counter is counting the
+    // number of outstanding PluginModuleParents for the plugin, excluding the
+    // one from the chrome process.
+    pluginTag->mContentProcessRunningCount++;
+    NS_ADDREF(*aPlugin = pluginTag->mPlugin);
+    return NS_OK;
+  }
+
+  return NS_ERROR_FAILURE;
+}
+
+class nsPluginUnloadRunnable : public nsRunnable
+{
+public:
+  explicit nsPluginUnloadRunnable(uint32_t aPluginId) : mPluginId(aPluginId) {}
+
+  NS_IMETHOD Run()
+  {
+    nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+    if (!host) {
+      return NS_OK;
+    }
+    nsPluginTag* pluginTag = host->PluginWithId(mPluginId);
+    if (!pluginTag) {
+      return NS_OK;
+    }
+
+    MOZ_ASSERT(pluginTag->mContentProcessRunningCount > 0);
+    pluginTag->mContentProcessRunningCount--;
+
+    if (!pluginTag->mContentProcessRunningCount) {
+      if (!host->IsRunningPlugin(pluginTag)) {
+        pluginTag->TryUnloadPlugin(false);
+      }
+    }
+    return NS_OK;
+  }
+
+protected:
+  uint32_t mPluginId;
+};
+
+void
+nsPluginHost::NotifyContentModuleDestroyed(uint32_t aPluginId)
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
+  // This is called in response to a message from the plugin. Don't unload the
+  // plugin until the message handler is off the stack.
+  nsRefPtr<nsPluginUnloadRunnable> runnable =
+    new nsPluginUnloadRunnable(aPluginId);
+  NS_DispatchToMainThread(runnable);
+}
+
 nsresult nsPluginHost::GetPlugin(const char *aMimeType, nsNPAPIPlugin** aPlugin)
 {
   nsresult rv = NS_ERROR_FAILURE;
   *aPlugin = nullptr;
 
   if (!aMimeType)
     return NS_ERROR_ILLEGAL_VALUE;
 
@@ -1609,16 +1687,27 @@ nsPluginHost::FirstPluginWithPath(const 
   for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
     if (tag->mFullPath.Equals(path)) {
       return tag;
     }
   }
   return nullptr;
 }
 
+nsPluginTag*
+nsPluginHost::PluginWithId(uint32_t aId)
+{
+  for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
+    if (tag->mId == aId) {
+      return tag;
+    }
+  }
+  return nullptr;
+}
+
 namespace {
 
 int64_t GetPluginLastModifiedTime(const nsCOMPtr<nsIFile>& localfile)
 {
   PRTime fileModTime = 0;
 
 #if defined(XP_MACOSX)
   // On OS X the date of a bundle's "contents" (i.e. of its Info.plist file)
@@ -1695,22 +1784,42 @@ struct CompareFilesByTime
   Equals(const nsCOMPtr<nsIFile>& a, const nsCOMPtr<nsIFile>& b) const
   {
     return GetPluginLastModifiedTime(a) == GetPluginLastModifiedTime(b);
   }
 };
 
 } // anonymous namespace
 
+void
+nsPluginHost::AddPluginTag(nsPluginTag* aPluginTag)
+{
+  aPluginTag->mNext = mPlugins;
+  mPlugins = aPluginTag;
+
+  if (aPluginTag->IsActive()) {
+    nsAdoptingCString disableFullPage =
+      Preferences::GetCString(kPrefDisableFullPage);
+    for (uint32_t i = 0; i < aPluginTag->mMimeTypes.Length(); i++) {
+      if (!IsTypeInList(aPluginTag->mMimeTypes[i], disableFullPage)) {
+        RegisterWithCategoryManager(aPluginTag->mMimeTypes[i],
+                                    ePluginRegister);
+      }
+    }
+  }
+}
+
 typedef NS_NPAPIPLUGIN_CALLBACK(char *, NP_GETMIMEDESCRIPTION)(void);
 
 nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir,
                                             bool aCreatePluginList,
                                             bool *aPluginsChanged)
 {
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
   NS_ENSURE_ARG_POINTER(aPluginsChanged);
   nsresult rv;
 
   *aPluginsChanged = false;
 
 #ifdef PLUGIN_LOGGING
   nsAutoCString dirPath;
   pluginsDir->GetNativePath(dirPath);
@@ -1890,65 +1999,32 @@ nsresult nsPluginHost::ScanPluginsDirect
     }
 
     // If we're not creating a plugin list, simply looking for changes,
     // then we're done.
     if (!aCreatePluginList) {
       return NS_OK;
     }
 
-    // Add plugin tags such that the list is ordered by modification date,
-    // newest to oldest. This is ugly, it'd be easier with just about anything
-    // other than a single-directional linked list.
-    if (mPlugins) {
-      nsPluginTag *prev = nullptr;
-      nsPluginTag *next = mPlugins;
-      while (next) {
-        if (pluginTag->mLastModifiedTime >= next->mLastModifiedTime) {
-          pluginTag->mNext = next;
-          if (prev) {
-            prev->mNext = pluginTag;
-          } else {
-            mPlugins = pluginTag;
-          }
-          break;
-        }
-        prev = next;
-        next = prev->mNext;
-        if (!next) {
-          prev->mNext = pluginTag;
-        }
-      }
-    } else {
-      mPlugins = pluginTag;
-    }
-
-    if (pluginTag->IsActive()) {
-      nsAdoptingCString disableFullPage =
-        Preferences::GetCString(kPrefDisableFullPage);
-      for (uint32_t i = 0; i < pluginTag->mMimeTypes.Length(); i++) {
-        if (!IsTypeInList(pluginTag->mMimeTypes[i], disableFullPage)) {
-          RegisterWithCategoryManager(pluginTag->mMimeTypes[i],
-                                      ePluginRegister);
-        }
-      }
-    }
+    AddPluginTag(pluginTag);
   }
 
   if (warnOutdated) {
     Preferences::SetBool("plugins.update.notifyUser", true);
   }
 
   return NS_OK;
 }
 
 nsresult nsPluginHost::ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum,
                                                 bool aCreatePluginList,
                                                 bool *aPluginsChanged)
 {
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
     bool hasMore;
     while (NS_SUCCEEDED(dirEnum->HasMoreElements(&hasMore)) && hasMore) {
       nsCOMPtr<nsISupports> supports;
       nsresult rv = dirEnum->GetNext(getter_AddRefs(supports));
       if (NS_FAILED(rv))
         continue;
       nsCOMPtr<nsIFile> nextDir(do_QueryInterface(supports, &rv));
       if (NS_FAILED(rv))
@@ -1963,16 +2039,44 @@ nsresult nsPluginHost::ScanPluginsDirect
 
       // if changes are detected and we are not creating the list, do not proceed
       if (!aCreatePluginList && *aPluginsChanged)
         break;
     }
     return NS_OK;
 }
 
+void
+nsPluginHost::IncrementChromeEpoch()
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+  mPluginEpoch++;
+}
+
+uint32_t
+nsPluginHost::ChromeEpoch()
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+  return mPluginEpoch;
+}
+
+uint32_t
+nsPluginHost::ChromeEpochForContent()
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
+  return mPluginEpoch;
+}
+
+void
+nsPluginHost::SetChromeEpochForContent(uint32_t aEpoch)
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
+  mPluginEpoch = aEpoch;
+}
+
 nsresult nsPluginHost::LoadPlugins()
 {
 #ifdef ANDROID
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     return NS_OK;
   }
 #endif
   // do not do anything if it is already done
@@ -1985,35 +2089,92 @@ nsresult nsPluginHost::LoadPlugins()
 
   bool pluginschanged;
   nsresult rv = FindPlugins(true, &pluginschanged);
   if (NS_FAILED(rv))
     return rv;
 
   // only if plugins have changed will we notify plugin-change observers
   if (pluginschanged) {
+    if (XRE_GetProcessType() == GeckoProcessType_Default) {
+      IncrementChromeEpoch();
+    }
+
     nsCOMPtr<nsIObserverService> obsService =
       mozilla::services::GetObserverService();
     if (obsService)
       obsService->NotifyObservers(nullptr, "plugins-list-updated", nullptr);
   }
 
   return NS_OK;
 }
 
+nsresult
+nsPluginHost::FindPluginsInContent(bool aCreatePluginList, bool* aPluginsChanged)
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
+
+  dom::ContentChild* cp = dom::ContentChild::GetSingleton();
+  nsTArray<PluginTag> plugins;
+  uint32_t parentEpoch;
+  if (!cp->SendFindPlugins(ChromeEpochForContent(), &plugins, &parentEpoch)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  if (parentEpoch != ChromeEpochForContent()) {
+    SetChromeEpochForContent(parentEpoch);
+    *aPluginsChanged = true;
+    if (!aCreatePluginList) {
+      return NS_OK;
+    }
+
+    for (size_t i = 0; i < plugins.Length(); i++) {
+      PluginTag& tag = plugins[i];
+
+      // Don't add the same plugin again.
+      if (PluginWithId(tag.id())) {
+        continue;
+      }
+
+      nsPluginTag *pluginTag = new nsPluginTag(tag.id(),
+                                               tag.name().get(),
+                                               tag.description().get(),
+                                               tag.filename().get(),
+                                               "", // aFullPath
+                                               tag.version().get(),
+                                               nsTArray<nsCString>(tag.mimeTypes()),
+                                               nsTArray<nsCString>(tag.mimeDescriptions()),
+                                               nsTArray<nsCString>(tag.extensions()),
+                                               tag.isJavaPlugin(),
+                                               tag.isFlashPlugin(),
+                                               tag.lastModifiedTime(),
+                                               tag.isFromExtension());
+      AddPluginTag(pluginTag);
+    }
+  }
+
+  mPluginsLoaded = true;
+  return NS_OK;
+}
+
 // if aCreatePluginList is false we will just scan for plugins
 // and see if any changes have been made to the plugins.
 // This is needed in ReloadPlugins to prevent possible recursive reloads
 nsresult nsPluginHost::FindPlugins(bool aCreatePluginList, bool * aPluginsChanged)
 {
   Telemetry::AutoTimer<Telemetry::FIND_PLUGINS> telemetry;
 
   NS_ENSURE_ARG_POINTER(aPluginsChanged);
 
   *aPluginsChanged = false;
+
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    return FindPluginsInContent(aCreatePluginList, aPluginsChanged);
+  }
+
   nsresult rv;
 
   // Read cached plugins info. If the profile isn't yet available then don't
   // scan for plugins
   if (ReadPluginInfo() == NS_ERROR_NOT_AVAILABLE)
     return NS_OK;
 
 #ifdef XP_WIN
@@ -2162,19 +2323,68 @@ nsresult nsPluginHost::FindPlugins(bool 
 
   // No more need for cached plugins. Clear it up.
   NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
   NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
 
   return NS_OK;
 }
 
+bool
+mozilla::plugins::FindPluginsForContent(uint32_t aPluginEpoch,
+                                        nsTArray<PluginTag>* aPlugins,
+                                        uint32_t* aNewPluginEpoch)
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
+  nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+  host->FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch);
+  return true;
+}
+
+void
+nsPluginHost::FindPluginsForContent(uint32_t aPluginEpoch,
+                                    nsTArray<PluginTag>* aPlugins,
+                                    uint32_t* aNewPluginEpoch)
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
+  // Load plugins so that the epoch is correct.
+  LoadPlugins();
+
+  *aNewPluginEpoch = ChromeEpoch();
+  if (aPluginEpoch == ChromeEpoch()) {
+    return;
+  }
+
+  nsTArray<nsRefPtr<nsPluginTag>> plugins;
+  GetPlugins(plugins);
+
+  for (size_t i = 0; i < plugins.Length(); i++) {
+    nsRefPtr<nsPluginTag> tag = plugins[i];
+    aPlugins->AppendElement(PluginTag(tag->mId,
+                                      tag->mName,
+                                      tag->mDescription,
+                                      tag->mMimeTypes,
+                                      tag->mMimeDescriptions,
+                                      tag->mExtensions,
+                                      tag->mIsJavaPlugin,
+                                      tag->mIsFlashPlugin,
+                                      tag->mFileName,
+                                      tag->mVersion,
+                                      tag->mLastModifiedTime,
+                                      tag->IsFromExtension()));
+  }
+}
+
 nsresult
 nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag)
 {
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
   ReadPluginInfo();
   WritePluginInfo();
   NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
   NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
 
   if (!aPluginTag) {
     return NS_OK;
   }
@@ -2256,16 +2466,17 @@ nsPluginHost::RegisterWithCategoryManage
                                   true);
     }
   }
 }
 
 nsresult
 nsPluginHost::WritePluginInfo()
 {
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
 
   nsresult rv = NS_OK;
   nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv));
   if (NS_FAILED(rv))
     return rv;
 
   directoryService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
                         getter_AddRefs(mPluginRegFile));
@@ -2400,16 +2611,18 @@ nsPluginHost::WritePluginInfo()
   NS_ENSURE_SUCCESS(rv, rv);
   rv = pluginReg->MoveToNative(parent, kPluginRegistryFilename);
   return rv;
 }
 
 nsresult
 nsPluginHost::ReadPluginInfo()
 {
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
   const long PLUGIN_REG_MIMETYPES_ARRAY_SIZE = 12;
   const long PLUGIN_REG_MAX_MIMETYPES = 1000;
 
   // we need to import the legacy flags from the plugin registry once
   const bool pluginStateImported =
     Preferences::GetDefaultBool("plugin.importedState", false);
 
   nsresult rv;
@@ -3537,16 +3750,17 @@ nsPluginHost::PluginCrashed(nsNPAPIPlugi
     }
   }
 
   // Only after all instances have been invalidated is it safe to null
   // out nsPluginTag.mPlugin. The next time we try to create an
   // instance of this plugin we reload it (launch a new plugin process).
 
   crashedPluginTag->mPlugin = nullptr;
+  crashedPluginTag->mContentProcessRunningCount = 0;
 
 #ifdef XP_WIN
   CheckForDisabledWindows();
 #endif
 }
 
 nsNPAPIPluginInstance*
 nsPluginHost::FindInstance(const char *mimetype)
--- a/dom/plugins/base/nsPluginHost.h
+++ b/dom/plugins/base/nsPluginHost.h
@@ -23,24 +23,26 @@
 #include "nsTArray.h"
 #include "nsTObserverArray.h"
 #include "nsITimer.h"
 #include "nsPluginTags.h"
 #include "nsPluginPlayPreviewInfo.h"
 #include "nsIEffectiveTLDService.h"
 #include "nsIIDNService.h"
 #include "nsCRT.h"
+#include "mozilla/plugins/PluginTypes.h"
 
 class nsNPAPIPlugin;
 class nsIComponentManager;
 class nsIFile;
 class nsIChannel;
 class nsPluginNativeWindow;
 class nsObjectLoadingContent;
 class nsPluginInstanceOwner;
+class nsPluginUnloadRunnable;
 class nsNPAPIPluginInstance;
 class nsNPAPIPluginStreamListener;
 class nsIPluginInstanceOwner;
 class nsIInputStream;
 class nsIStreamListener;
 
 class nsInvalidPluginTag : public nsISupports
 {
@@ -83,16 +85,19 @@ public:
   nsresult SetUpPluginInstance(const char *aMimeType,
                                nsIURI *aURL,
                                nsPluginInstanceOwner *aOwner);
   bool PluginExistsForType(const char* aMimeType);
 
   nsresult IsPluginEnabledForExtension(const char* aExtension, const char* &aMimeType);
 
   void GetPlugins(nsTArray<nsRefPtr<nsPluginTag> >& aPluginArray);
+  void FindPluginsForContent(uint32_t aPluginEpoch,
+                             nsTArray<mozilla::plugins::PluginTag>* aPlugins,
+                             uint32_t* aNewPluginEpoch);
 
   nsresult GetURL(nsISupports* pluginInst,
                   const char* url,
                   const char* target,
                   nsNPAPIPluginStreamListener* streamListener,
                   const char* altHost,
                   const char* referrer,
                   bool forceJSEnabled);
@@ -186,22 +191,26 @@ public:
   nsresult InstantiatePluginInstance(const char *aMimeType, nsIURI* aURL,
                                      nsObjectLoadingContent *aContent,
                                      nsPluginInstanceOwner** aOwner);
 
   // Does not accept nullptr and should never fail.
   nsPluginTag* TagForPlugin(nsNPAPIPlugin* aPlugin);
 
   nsresult GetPlugin(const char *aMimeType, nsNPAPIPlugin** aPlugin);
+  nsresult GetPluginForContentProcess(uint32_t aPluginId, nsNPAPIPlugin** aPlugin);
+  void NotifyContentModuleDestroyed(uint32_t aPluginId);
 
   nsresult NewPluginStreamListener(nsIURI* aURL,
                                    nsNPAPIPluginInstance* aInstance,
                                    nsIStreamListener **aStreamListener);
 
 private:
+  friend class nsPluginUnloadRunnable;
+
   nsresult
   TrySetUpPluginInstance(const char *aMimeType, nsIURI *aURL, nsPluginInstanceOwner *aOwner);
 
   nsPluginTag*
   FindPreferredPlugin(const InfallibleTArray<nsPluginTag*>& matches);
 
   // Return an nsPluginTag for this type, if any.  If aCheckEnabled is
   // true, only enabled plugins will be returned.
@@ -209,24 +218,28 @@ private:
   FindPluginForType(const char* aMimeType, bool aCheckEnabled);
 
   nsPluginTag*
   FindPluginEnabledForExtension(const char* aExtension, const char* &aMimeType);
 
   nsresult
   FindStoppedPluginForURL(nsIURI* aURL, nsIPluginInstanceOwner *aOwner);
 
+  nsresult FindPluginsInContent(bool aCreatePluginList, bool * aPluginsChanged);
+
   nsresult
   FindPlugins(bool aCreatePluginList, bool * aPluginsChanged);
 
   // Registers or unregisters the given mime type with the category manager
   // (performs no checks - see UpdateCategoryManager)
   enum nsRegisterType { ePluginRegister, ePluginUnregister };
   void RegisterWithCategoryManager(nsCString &aMimeType, nsRegisterType aType);
 
+  void AddPluginTag(nsPluginTag* aPluginTag);
+
   nsresult
   ScanPluginsDirectory(nsIFile *pluginsDir,
                        bool aCreatePluginList,
                        bool *aPluginsChanged);
 
   nsresult
   ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum,
                            bool aCreatePluginList,
@@ -250,21 +263,33 @@ private:
   // Checks to see if a tag object is in our list of live tags.
   bool IsLiveTag(nsIPluginTag* tag);
   
   // Checks our list of live tags for an equivalent tag.
   nsPluginTag* HaveSamePlugin(const nsPluginTag * aPluginTag);
     
   // Returns the first plugin at |path|
   nsPluginTag* FirstPluginWithPath(const nsCString& path);
+  nsPluginTag* PluginWithId(uint32_t aId);
 
   nsresult EnsurePrivateDirServiceProvider();
 
   void OnPluginInstanceDestroyed(nsPluginTag* aPluginTag);
 
+  // To be used by the chrome process whenever the set of plugins changes.
+  void IncrementChromeEpoch();
+
+  // To be used by the chrome process; returns the current epoch.
+  uint32_t ChromeEpoch();
+
+  // To be used by the content process to get/set the last observed epoch value
+  // from the chrome process.
+  uint32_t ChromeEpochForContent();
+  void SetChromeEpochForContent(uint32_t aEpoch);
+
   nsRefPtr<nsPluginTag> mPlugins;
   nsRefPtr<nsPluginTag> mCachedPlugins;
   nsRefPtr<nsInvalidPluginTag> mInvalidPlugins;
   nsTArray< nsRefPtr<nsPluginPlayPreviewInfo> > mPlayPreviewMimeTypes;
   bool mPluginsLoaded;
 
   // set by pref plugin.override_internal_types
   bool mOverrideInternalTypes;
@@ -290,16 +315,22 @@ private:
   nsresult NormalizeHostname(nsCString& host);
   nsresult EnumerateSiteData(const nsACString& domain,
                              const InfallibleTArray<nsCString>& sites,
                              InfallibleTArray<nsCString>& result,
                              bool firstMatchOnly);
 
   nsWeakPtr mCurrentDocument; // weak reference, we use it to id document only
 
+  // This epoch increases each time we load the list of plugins from disk.
+  // In the chrome process, this stores the actual epoch.
+  // In the content process, this stores the last epoch value observed
+  // when reading plugins from chrome.
+  uint32_t mPluginEpoch;
+
   static nsIFile *sPluginTempDir;
 
   // We need to hold a global ptr to ourselves because we register for
   // two different CIDs for some reason...
   static nsPluginHost* sInst;
 };
 
 class MOZ_STACK_CLASS PluginDestructionGuard : protected PRCList
--- a/dom/plugins/base/nsPluginTags.cpp
+++ b/dom/plugins/base/nsPluginTags.cpp
@@ -61,20 +61,24 @@ MakePrefNameForPlugin(const char* const 
 static nsCString
 GetStatePrefNameForPlugin(nsPluginTag* aTag)
 {
   return MakePrefNameForPlugin("state", aTag);
 }
 
 /* nsPluginTag */
 
+uint32_t nsPluginTag::sNextId;
+
 nsPluginTag::nsPluginTag(nsPluginInfo* aPluginInfo,
                          int64_t aLastModifiedTime,
                          bool fromExtension)
-  : mName(aPluginInfo->fName),
+  : mId(sNextId++),
+    mContentProcessRunningCount(0),
+    mName(aPluginInfo->fName),
     mDescription(aPluginInfo->fDescription),
     mLibrary(nullptr),
     mIsJavaPlugin(false),
     mIsFlashPlugin(false),
     mFileName(aPluginInfo->fFileName),
     mFullPath(aPluginInfo->fFullPath),
     mVersion(aPluginInfo->fVersion),
     mLastModifiedTime(aLastModifiedTime),
@@ -98,17 +102,19 @@ nsPluginTag::nsPluginTag(const char* aNa
                          const char* aVersion,
                          const char* const* aMimeTypes,
                          const char* const* aMimeDescriptions,
                          const char* const* aExtensions,
                          int32_t aVariants,
                          int64_t aLastModifiedTime,
                          bool fromExtension,
                          bool aArgsAreUTF8)
-  : mName(aName),
+  : mId(sNextId++),
+    mContentProcessRunningCount(0),
+    mName(aName),
     mDescription(aDescription),
     mLibrary(nullptr),
     mIsJavaPlugin(false),
     mIsFlashPlugin(false),
     mFileName(aFileName),
     mFullPath(aFullPath),
     mVersion(aVersion),
     mLastModifiedTime(aLastModifiedTime),
@@ -119,16 +125,49 @@ nsPluginTag::nsPluginTag(const char* aNa
 {
   InitMime(aMimeTypes, aMimeDescriptions, aExtensions,
            static_cast<uint32_t>(aVariants));
   if (!aArgsAreUTF8)
     EnsureMembersAreUTF8();
   FixupVersion();
 }
 
+nsPluginTag::nsPluginTag(uint32_t aId,
+                         const char* aName,
+                         const char* aDescription,
+                         const char* aFileName,
+                         const char* aFullPath,
+                         const char* aVersion,
+                         nsTArray<nsCString> aMimeTypes,
+                         nsTArray<nsCString> aMimeDescriptions,
+                         nsTArray<nsCString> aExtensions,
+                         bool aIsJavaPlugin,
+                         bool aIsFlashPlugin,
+                         int64_t aLastModifiedTime,
+                         bool aFromExtension)
+  : mId(aId),
+    mContentProcessRunningCount(0),
+    mName(aName),
+    mDescription(aDescription),
+    mMimeTypes(aMimeTypes),
+    mMimeDescriptions(aMimeDescriptions),
+    mExtensions(aExtensions),
+    mLibrary(nullptr),
+    mIsJavaPlugin(aIsJavaPlugin),
+    mIsFlashPlugin(aIsFlashPlugin),
+    mFileName(aFileName),
+    mVersion(aVersion),
+    mLastModifiedTime(aLastModifiedTime),
+    mNiceFileName(),
+    mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED),
+    mCachedBlocklistStateValid(false),
+    mIsFromExtension(aFromExtension)
+{
+}
+
 nsPluginTag::~nsPluginTag()
 {
   NS_ASSERTION(!mNext, "Risk of exhausting the stack space, bug 486349");
 }
 
 NS_IMPL_ISUPPORTS(nsPluginTag, nsIPluginTag)
 
 void nsPluginTag::InitMime(const char* const* aMimeTypes,
--- a/dom/plugins/base/nsPluginTags.h
+++ b/dom/plugins/base/nsPluginTags.h
@@ -46,16 +46,29 @@ public:
               const char* aVersion,
               const char* const* aMimeTypes,
               const char* const* aMimeDescriptions,
               const char* const* aExtensions,
               int32_t aVariants,
               int64_t aLastModifiedTime,
               bool fromExtension,
               bool aArgsAreUTF8 = false);
+  nsPluginTag(uint32_t aId,
+              const char* aName,
+              const char* aDescription,
+              const char* aFileName,
+              const char* aFullPath,
+              const char* aVersion,
+              nsTArray<nsCString> aMimeTypes,
+              nsTArray<nsCString> aMimeDescriptions,
+              nsTArray<nsCString> aExtensions,
+              bool aIsJavaPlugin,
+              bool aIsFlashPlugin,
+              int64_t aLastModifiedTime,
+              bool aFromExtension);
 
   void TryUnloadPlugin(bool inShutdown);
 
   // plugin is enabled and not blocklisted
   bool IsActive();
 
   bool IsEnabled();
   void SetEnabled(bool enabled);
@@ -69,16 +82,20 @@ public:
   void ImportFlagsToPrefs(uint32_t flag);
 
   bool HasSameNameAndMimes(const nsPluginTag *aPluginTag) const;
   nsCString GetNiceFileName();
 
   bool IsFromExtension() const;
 
   nsRefPtr<nsPluginTag> mNext;
+  uint32_t      mId;
+
+  // Number of PluginModuleParents living in all content processes.
+  size_t        mContentProcessRunningCount;
   nsCString     mName; // UTF-8
   nsCString     mDescription; // UTF-8
   nsTArray<nsCString> mMimeTypes; // UTF-8
   nsTArray<nsCString> mMimeDescriptions; // UTF-8
   nsTArray<nsCString> mExtensions; // UTF-8
   PRLibrary     *mLibrary;
   nsRefPtr<nsNPAPIPlugin> mPlugin;
   bool          mIsJavaPlugin;
@@ -101,11 +118,13 @@ private:
   bool          mIsFromExtension;
 
   void InitMime(const char* const* aMimeTypes,
                 const char* const* aMimeDescriptions,
                 const char* const* aExtensions,
                 uint32_t aVariantCount);
   nsresult EnsureMembersAreUTF8();
   void FixupVersion();
+
+  static uint32_t sNextId;
 };
 
 #endif // nsPluginTags_h_
--- a/dom/plugins/ipc/PPluginModule.ipdl
+++ b/dom/plugins/ipc/PPluginModule.ipdl
@@ -1,28 +1,31 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
 /* 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 PPluginInstance;
 include protocol PPluginScriptableObject;
 include protocol PCrashReporter;
+include protocol PContent;
 
 using NPError from "npapi.h";
 using NPNVariable from "npapi.h";
 using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
 using class mac_plugin_interposing::NSCursorInfo from "mozilla/plugins/PluginMessageUtils.h";
 using struct nsID from "nsID.h";
 
 namespace mozilla {
 namespace plugins {
 
 intr protocol PPluginModule
 {
+  bridges PContent, PPluginModule;
+
   manages PPluginInstance;
   manages PCrashReporter;
 
 both:
   // Window-specific message which instructs the interrupt mechanism to enter
   // a nested event loop for the current interrupt call.
   async ProcessNativeEventsInInterruptCall();
 
@@ -102,16 +105,20 @@ parent:
 
   // OS X Specific calls to allow the plugin to manage the cursor.
   async SetCursor(NSCursorInfo cursorInfo);
   async ShowCursor(bool show);
   async PushCursor(NSCursorInfo cursorInfo);
   async PopCursor();
   sync GetNativeCursorsSupported() returns (bool supported);
 
-  sync NPN_SetException(nullable PPluginScriptableObject actor,
-                        nsCString message);
+  sync NPN_SetException(nsCString message);
 
   async NPN_ReloadPlugins(bool aReloadPages);
+
+  // Notifies the chrome process that a PluginModuleChild linked to a content
+  // process was destroyed. The chrome process may choose to asynchronously shut
+  // down the plugin process in response.
+  async NotifyContentModuleDestroyed();
 };
 
 } // namespace plugins
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/plugins/ipc/PluginBridge.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et :
+ * 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/. */
+
+#ifndef mozilla_plugins_PluginBridge_h
+#define mozilla_plugins_PluginBridge_h
+
+namespace mozilla {
+
+namespace dom {
+class ContentParent;
+}
+
+namespace plugins {
+
+bool
+SetupBridge(uint32_t aPluginId, dom::ContentParent* aContentParent);
+
+bool
+FindPluginsForContent(uint32_t aPluginEpoch,
+                      nsTArray<PluginTag>* aPlugins,
+                      uint32_t* aNewPluginEpoch);
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // mozilla_plugins_PluginBridge_h
--- a/dom/plugins/ipc/PluginHangUIParent.cpp
+++ b/dom/plugins/ipc/PluginHangUIParent.cpp
@@ -64,17 +64,17 @@ private:
   uint32_t mResponseTimeMs;
   uint32_t mTimeoutMs;
 };
 } // anonymous namespace
 
 namespace mozilla {
 namespace plugins {
 
-PluginHangUIParent::PluginHangUIParent(PluginModuleParent* aModule,
+PluginHangUIParent::PluginHangUIParent(PluginModuleChromeParent* aModule,
                                        const int32_t aHangUITimeoutPref,
                                        const int32_t aChildTimeoutPref)
   : mMutex("mozilla::plugins::PluginHangUIParent::mMutex"),
     mModule(aModule),
     mTimeoutPrefMs(static_cast<uint32_t>(aHangUITimeoutPref) * 1000U),
     mIPCTimeoutMs(static_cast<uint32_t>(aChildTimeoutPref) * 1000U),
     mMainThreadMessageLoop(MessageLoop::current()),
     mIsShowing(false),
--- a/dom/plugins/ipc/PluginHangUIParent.h
+++ b/dom/plugins/ipc/PluginHangUIParent.h
@@ -15,32 +15,32 @@
 #include "mozilla/Mutex.h"
 #include "mozilla/plugins/PluginMessageUtils.h"
 
 #include "MiniShmParent.h"
 
 namespace mozilla {
 namespace plugins {
 
-class PluginModuleParent;
+class PluginModuleChromeParent;
 
 /**
  * This class is responsible for launching and communicating with the 
  * plugin-hang-ui process.
  *
  * NOTE: PluginHangUIParent is *not* an IPDL actor! In this case, "Parent" 
  *       is describing the fact that firefox is the parent process to the 
  *       plugin-hang-ui process, which is the PluginHangUIChild.
  *       PluginHangUIParent and PluginHangUIChild are a matched pair.
  * @see PluginHangUIChild
  */
 class PluginHangUIParent : public MiniShmObserver
 {
 public:
-  PluginHangUIParent(PluginModuleParent* aModule,
+  PluginHangUIParent(PluginModuleChromeParent* aModule,
                      const int32_t aHangUITimeoutPref,
                      const int32_t aChildTimeoutPref);
   virtual ~PluginHangUIParent();
 
   /**
    * Spawn the plugin-hang-ui.exe child process and terminate the given 
    * plugin container process if the user elects to stop the hung plugin.
    *
@@ -130,17 +130,17 @@ private:
   bool
   UnwatchHangUIChildProcess(bool aWait);
 
   static
   VOID CALLBACK SOnHangUIProcessExit(PVOID aContext, BOOLEAN aIsTimer);
 
 private:
   Mutex mMutex;
-  PluginModuleParent* mModule;
+  PluginModuleChromeParent* mModule;
   const uint32_t mTimeoutPrefMs;
   const uint32_t mIPCTimeoutMs;
   MessageLoop* mMainThreadMessageLoop;
   bool mIsShowing;
   unsigned int mLastUserResponse;
   base::ProcessHandle mHangUIProcessHandle;
   NativeWindowHandle mMainWindowHandle;
   HANDLE mRegWait;
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -160,16 +160,17 @@ PluginInstanceChild::PluginInstanceChild
     , mIsTransparent(false)
     , mSurfaceType(gfxSurfaceType::Max)
     , mCurrentInvalidateTask(nullptr)
     , mCurrentAsyncSetWindowTask(nullptr)
     , mPendingPluginCall(false)
     , mDoAlphaExtraction(false)
     , mHasPainted(false)
     , mSurfaceDifferenceRect(0,0,0,0)
+    , mDestroyed(false)
 {
     memset(&mWindow, 0, sizeof(mWindow));
     mWindow.type = NPWindowTypeWindow;
     mData.ndata = (void*) this;
     mData.pdata = nullptr;
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
     mWindow.ws_info = &mWsInfo;
     memset(&mWsInfo, 0, sizeof(mWsInfo));
@@ -207,17 +208,17 @@ PluginInstanceChild::~PluginInstanceChil
         UnscheduleTimer(mCARefreshTimer);
     }
 #endif
 }
 
 int
 PluginInstanceChild::GetQuirks()
 {
-    return PluginModuleChild::current()->GetQuirks();
+    return PluginModuleChild::GetChrome()->GetQuirks();
 }
 
 NPError
 PluginInstanceChild::InternalGetNPObjectForValue(NPNVariable aValue,
                                                  NPObject** aObject)
 {
     PluginScriptableObjectChild* actor = nullptr;
     NPError result = NPERR_NO_ERROR;
@@ -3271,17 +3272,17 @@ PluginInstanceChild::ShowPluginFrame()
         XSync(mWsInfo.display, False);
     } else
 #endif
 #ifdef XP_WIN
     if (SharedDIBSurface::IsSharedDIBSurface(mCurrentSurface)) {
         SharedDIBSurface* s = static_cast<SharedDIBSurface*>(mCurrentSurface.get());
         if (!mCurrentSurfaceActor) {
             base::SharedMemoryHandle handle = nullptr;
-            s->ShareToProcess(PluginModuleChild::current()->OtherProcess(), &handle);
+            s->ShareToProcess(OtherProcess(), &handle);
 
             mCurrentSurfaceActor =
                 SendPPluginSurfaceConstructor(handle,
                                               mCurrentSurface->GetSize(),
                                               haveTransparentPixels);
         }
         currSurf = mCurrentSurfaceActor;
         s->Flush();
@@ -3673,22 +3674,23 @@ PluginInstanceChild::ClearAllSurfaces()
         mCGLayer = nullptr;
     }
 
     mDoubleBufferCARenderer.ClearFrontSurface();
     mDoubleBufferCARenderer.ClearBackSurface();
 #endif
 }
 
-bool
-PluginInstanceChild::AnswerNPP_Destroy(NPError* aResult)
+void
+PluginInstanceChild::Destroy()
 {
-    PLUGIN_LOG_DEBUG_METHOD;
-    AssertPluginThread();
-    *aResult = NPERR_NO_ERROR;
+    if (mDestroyed) {
+        return;
+    }
+    mDestroyed = true;
 
 #if defined(OS_WIN)
     SetProp(mPluginWindowHWND, kPluginIgnoreSubclassProperty, (HANDLE)1);
 #endif
 
     InfallibleTArray<PBrowserStreamChild*> streams;
     ManagedPBrowserStreamChild(streams);
 
@@ -3702,17 +3704,17 @@ PluginInstanceChild::AnswerNPP_Destroy(N
     for (uint32_t i = 0; i < streams.Length(); ++i)
         static_cast<BrowserStreamChild*>(streams[i])->FinishDelivery();
 
     mTimers.Clear();
 
     // NPP_Destroy() should be a synchronization point for plugin threads
     // calling NPN_AsyncCall: after this function returns, they are no longer
     // allowed to make async calls on this instance.
-    PluginModuleChild::current()->NPP_Destroy(this);
+    static_cast<PluginModuleChild *>(Manager())->NPP_Destroy(this);
     mData.ndata = 0;
 
     if (mCurrentInvalidateTask) {
         mCurrentInvalidateTask->Cancel();
         mCurrentInvalidateTask = nullptr;
     }
     if (mCurrentAsyncSetWindowTask) {
         mCurrentAsyncSetWindowTask->Cancel();
@@ -3756,11 +3758,27 @@ PluginInstanceChild::AnswerNPP_Destroy(N
 #ifdef MOZ_WIDGET_GTK
     if (mWindow.type == NPWindowTypeWindow && !mXEmbed) {
       xt_client_xloop_destroy();
     }
 #endif
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
     DeleteWindow();
 #endif
+}
+
+bool
+PluginInstanceChild::AnswerNPP_Destroy(NPError* aResult)
+{
+    PLUGIN_LOG_DEBUG_METHOD;
+    AssertPluginThread();
+    *aResult = NPERR_NO_ERROR;
+
+    Destroy();
 
     return true;
 }
+
+void
+PluginInstanceChild::ActorDestroy(ActorDestroyReason why)
+{
+    Destroy();
+}
--- a/dom/plugins/ipc/PluginInstanceChild.h
+++ b/dom/plugins/ipc/PluginInstanceChild.h
@@ -520,16 +520,20 @@ private:
     void ClearCurrentSurface();
 
     // Swap mCurrentSurface/mBackSurface and their associated actors
     void SwapSurfaces();
 
     // Clear all surfaces in response to NPP_Destroy
     void ClearAllSurfaces();
 
+    void Destroy();
+
+    void ActorDestroy(ActorDestroyReason why);
+
     // Set as true when SetupLayer called
     // and go with different path in InvalidateRect function
     bool mLayersRendering;
 
     // Current surface available for rendering
     nsRefPtr<gfxASurface> mCurrentSurface;
 
     // Back surface, just keeping reference to
@@ -593,14 +597,17 @@ private:
     // that we ask a plugin to paint at least once even if it's invisible;
     // some plugin (instances) rely on this in order to work properly.
     bool mHasPainted;
 
     // Cached rectangle rendered to previous surface(mBackSurface)
     // Used for reading back to current surface and syncing data,
     // in plugin coordinates.
     nsIntRect mSurfaceDifferenceRect;
+
+    // Has this instance been destroyed, either by ActorDestroy or NPP_Destroy?
+    bool mDestroyed;
 };
 
 } // namespace plugins
 } // namespace mozilla
 
 #endif // ifndef dom_plugins_PluginInstanceChild_h
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -1773,17 +1773,17 @@ PluginInstanceParent::SharedSurfaceSetWi
     if (NS_FAILED(mSharedSurfaceDib.Create(reinterpret_cast<HDC>(aWindow->window),
                                            newPort.width, newPort.height, false)))
       return false;
 
     // save the new shared surface size we just allocated
     mSharedSize = newPort;
 
     base::SharedMemoryHandle handle;
-    if (NS_FAILED(mSharedSurfaceDib.ShareToProcess(mParent->ChildProcessHandle(), &handle)))
+    if (NS_FAILED(mSharedSurfaceDib.ShareToProcess(OtherProcess(), &handle)))
       return false;
 
     aRemoteWindow.surfaceHandle = handle;
 
     return true;
 }
 
 void
--- a/dom/plugins/ipc/PluginInterposeOSX.mm
+++ b/dom/plugins/ipc/PluginInterposeOSX.mm
@@ -540,17 +540,17 @@ void NSCursorInfo::SetCustomImageData(ui
 }
 
 // This should never be called from the browser process -- only from the
 // plugin process.
 bool NSCursorInfo::GetNativeCursorsSupported()
 {
   if (mNativeCursorsSupported == -1) {
     AssertPluginThread();
-    PluginModuleChild *pmc = PluginModuleChild::current();
+    PluginModuleChild *pmc = PluginModuleChild::GetChrome();
     if (pmc) {
       bool result = pmc->GetNativeCursorsSupported();
       if (result) {
         mNativeCursorsSupported = 1;
       } else {
         mNativeCursorsSupported = 0;
       }
     }
@@ -686,60 +686,60 @@ void FocusPluginProcess() {
     SetFrontProcess(&this_process);
   }
 }
 
 void NotifyBrowserOfPluginShowWindow(uint32_t window_id, CGRect bounds,
                                      bool modal) {
   AssertPluginThread();
 
-  PluginModuleChild *pmc = PluginModuleChild::current();
+  PluginModuleChild *pmc = PluginModuleChild::GetChrome();
   if (pmc)
     pmc->PluginShowWindow(window_id, modal, bounds);
 }
 
 void NotifyBrowserOfPluginHideWindow(uint32_t window_id, CGRect bounds) {
   AssertPluginThread();
 
-  PluginModuleChild *pmc = PluginModuleChild::current();
+  PluginModuleChild *pmc = PluginModuleChild::GetChrome();
   if (pmc)
     pmc->PluginHideWindow(window_id);
 }
 
 void NotifyBrowserOfSetCursor(NSCursorInfo& aCursorInfo)
 {
   AssertPluginThread();
-  PluginModuleChild *pmc = PluginModuleChild::current();
+  PluginModuleChild *pmc = PluginModuleChild::GetChrome();
   if (pmc) {
     pmc->SetCursor(aCursorInfo);
   }
 }
 
 void NotifyBrowserOfShowCursor(bool show)
 {
   AssertPluginThread();
-  PluginModuleChild *pmc = PluginModuleChild::current();
+  PluginModuleChild *pmc = PluginModuleChild::GetChrome();
   if (pmc) {
     pmc->ShowCursor(show);
   }
 }
 
 void NotifyBrowserOfPushCursor(NSCursorInfo& aCursorInfo)
 {
   AssertPluginThread();
-  PluginModuleChild *pmc = PluginModuleChild::current();
+  PluginModuleChild *pmc = PluginModuleChild::GetChrome();
   if (pmc) {
     pmc->PushCursor(aCursorInfo);
   }
 }
 
 void NotifyBrowserOfPopCursor()
 {
   AssertPluginThread();
-  PluginModuleChild *pmc = PluginModuleChild::current();
+  PluginModuleChild *pmc = PluginModuleChild::GetChrome();
   if (pmc) {
     pmc->PopCursor();
   }
 }
 
 struct WindowInfo {
   uint32_t window_id;
   CGRect bounds;
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -60,17 +60,18 @@ using mozilla::dom::CrashReporterChild;
 using mozilla::dom::PCrashReporterChild;
 
 #if defined(XP_WIN)
 const wchar_t * kFlashFullscreenClass = L"ShockwaveFlashFullScreen";
 const wchar_t * kMozillaWindowClass = L"MozillaWindowClass";
 #endif
 
 namespace {
-PluginModuleChild* gInstance = nullptr;
+PluginModuleChild* gChromeInstance = nullptr;
+nsTArray<PluginModuleChild*>* gAllInstances;
 }
 
 #ifdef MOZ_WIDGET_QT
 typedef void (*_gtk_init_fn)(int argc, char **argv);
 static _gtk_init_fn s_gtk_init = nullptr;
 static PRLibrary *sGtkLib = nullptr;
 #endif
 
@@ -79,80 +80,156 @@ static PRLibrary *sGtkLib = nullptr;
 static bool gDelayFlashFocusReplyUntilEval = false;
 // Used to fix GetWindowInfo problems with internal flash settings dialogs
 static WindowsDllInterceptor sUser32Intercept;
 typedef BOOL (WINAPI *GetWindowInfoPtr)(HWND hwnd, PWINDOWINFO pwi);
 static GetWindowInfoPtr sGetWindowInfoPtrStub = nullptr;
 static HWND sBrowserHwnd = nullptr;
 #endif
 
-PluginModuleChild::PluginModuleChild()
+/* static */
+PluginModuleChild*
+PluginModuleChild::CreateForContentProcess(mozilla::ipc::Transport* aTransport,
+                                           base::ProcessId aOtherProcess)
+{
+    PluginModuleChild* child = new PluginModuleChild(false);
+    ProcessHandle handle;
+    if (!base::OpenProcessHandle(aOtherProcess, &handle)) {
+        // XXX need to kill |aOtherProcess|, it's boned
+        return nullptr;
+    }
+
+    if (!child->InitForContent(handle, XRE_GetIOMessageLoop(), aTransport)) {
+        return nullptr;
+    }
+
+    return child;
+}
+
+PluginModuleChild::PluginModuleChild(bool aIsChrome)
   : mLibrary(0)
   , mPluginFilename("")
   , mQuirks(QUIRKS_NOT_INITIALIZED)
+  , mIsChrome(aIsChrome)
+  , mTransport(nullptr)
   , mShutdownFunc(0)
   , mInitializeFunc(0)
 #if defined(OS_WIN) || defined(OS_MACOSX)
   , mGetEntryPointsFunc(0)
 #elif defined(MOZ_WIDGET_GTK)
   , mNestedLoopTimerId(0)
 #elif defined(MOZ_WIDGET_QT)
   , mNestedLoopTimerObject(0)
 #endif
 #ifdef OS_WIN
   , mNestedEventHook(nullptr)
   , mGlobalCallWndProcHook(nullptr)
 #endif
 {
-    NS_ASSERTION(!gInstance, "Something terribly wrong here!");
+    if (!gAllInstances) {
+        gAllInstances = new nsTArray<PluginModuleChild*>(1);
+    }
+    gAllInstances->AppendElement(this);
+
     memset(&mFunctions, 0, sizeof(mFunctions));
-    memset(&mSavedData, 0, sizeof(mSavedData));
-    gInstance = this;
+    if (mIsChrome) {
+        MOZ_ASSERT(!gChromeInstance);
+        gChromeInstance = this;
+    }
     mUserAgent.SetIsVoid(true);
 #ifdef XP_MACOSX
     mac_plugin_interposing::child::SetUpCocoaInterposing();
 #endif
 }
 
 PluginModuleChild::~PluginModuleChild()
 {
-    NS_ASSERTION(gInstance == this, "Something terribly wrong here!");
-
-    // We don't unload the plugin library in case it uses atexit handlers or
-    // other similar hooks.
+    if (mTransport) {
+        // For some reason IPDL doesn't autmatically delete the channel for a
+        // bridged protocol (bug 1090570). So we have to do it ourselves. This
+        // code is only invoked for PluginModuleChild instances created via
+        // bridging; otherwise mTransport is null.
+        XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new DeleteTask<Transport>(mTransport));
+    }
 
-    DeinitGraphics();
-    PluginScriptableObjectChild::ClearIdentifiers();
+    gAllInstances->RemoveElement(this);
+    MOZ_ASSERT_IF(mIsChrome, gAllInstances->Length() == 0);
+    if (gAllInstances->IsEmpty()) {
+        delete gAllInstances;
+        gAllInstances = nullptr;
+    }
 
-    gInstance = nullptr;
+    if (mIsChrome) {
+        MOZ_ASSERT(gChromeInstance == this);
+
+        // We don't unload the plugin library in case it uses atexit handlers or
+        // other similar hooks.
+
+        DeinitGraphics();
+        PluginScriptableObjectChild::ClearIdentifiers();
+
+        gChromeInstance = nullptr;
+    }
 }
 
 // static
 PluginModuleChild*
-PluginModuleChild::current()
+PluginModuleChild::GetChrome()
 {
-    NS_ASSERTION(gInstance, "Null instance!");
-    return gInstance;
+    MOZ_ASSERT(gChromeInstance);
+    return gChromeInstance;
 }
 
 bool
-PluginModuleChild::Init(const std::string& aPluginFilename,
-                        base::ProcessHandle aParentProcessHandle,
-                        MessageLoop* aIOLoop,
-                        IPC::Channel* aChannel)
+PluginModuleChild::CommonInit(base::ProcessHandle aParentProcessHandle,
+                              MessageLoop* aIOLoop,
+                              IPC::Channel* aChannel)
 {
     PLUGIN_LOG_DEBUG_METHOD;
 
-    GetIPCChannel()->SetAbortOnError(true);
-
     // Request Windows message deferral behavior on our channel. This
     // applies to the top level and all sub plugin protocols since they
     // all share the same channel.
+    // Bug 1090573 - Don't do this for connections to content processes.
     GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
 
+    if (!Open(aChannel, aParentProcessHandle, aIOLoop))
+        return false;
+
+    memset((void*) &mFunctions, 0, sizeof(mFunctions));
+    mFunctions.size = sizeof(mFunctions);
+    mFunctions.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
+
+    return true;
+}
+
+bool
+PluginModuleChild::InitForContent(base::ProcessHandle aParentProcessHandle,
+                                  MessageLoop* aIOLoop,
+                                  IPC::Channel* aChannel)
+{
+    if (!CommonInit(aParentProcessHandle, aIOLoop, aChannel)) {
+        return false;
+    }
+
+    mTransport = aChannel;
+
+    mLibrary = GetChrome()->mLibrary;
+    mQuirks = GetChrome()->mQuirks;
+    mFunctions = GetChrome()->mFunctions;
+
+    return true;
+}
+
+bool
+PluginModuleChild::InitForChrome(const std::string& aPluginFilename,
+                                 base::ProcessHandle aParentProcessHandle,
+                                 MessageLoop* aIOLoop,
+                                 IPC::Channel* aChannel)
+{
 #ifdef XP_WIN
     COMMessageFilter::Initialize(this);
 #endif
 
     NS_ASSERTION(aChannel, "need a channel");
 
     if (!InitGraphics())
         return false;
@@ -196,22 +273,21 @@ PluginModuleChild::Init(const std::strin
 #endif
     {
         nsresult rv = pluginFile.LoadPlugin(&mLibrary);
         if (NS_FAILED(rv))
             return false;
     }
     NS_ASSERTION(mLibrary, "couldn't open shared object");
 
-    if (!Open(aChannel, aParentProcessHandle, aIOLoop))
+    if (!CommonInit(aParentProcessHandle, aIOLoop, aChannel)) {
         return false;
+    }
 
-    memset((void*) &mFunctions, 0, sizeof(mFunctions));
-    mFunctions.size = sizeof(mFunctions);
-    mFunctions.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
+    GetIPCChannel()->SetAbortOnError(true);
 
     // TODO: use PluginPRLibrary here
 
 #if defined(OS_LINUX) || defined(OS_BSD)
     mShutdownFunc =
         (NP_PLUGINSHUTDOWN) PR_FindFunctionSymbol(mLibrary, "NP_Shutdown");
 
     // create the new plugin handler
@@ -586,16 +662,17 @@ PluginModuleChild::DeinitGraphics()
     XCloseDisplay(DefaultXDisplay());
 #endif
 }
 
 bool
 PluginModuleChild::AnswerNP_Shutdown(NPError *rv)
 {
     AssertPluginThread();
+    MOZ_ASSERT(mIsChrome);
 
 #if defined XP_WIN
     mozilla::widget::StopAudioSession();
 #endif
 
     // the PluginModuleParent shuts down this process after this interrupt
     // call pops off its stack
 
@@ -673,16 +750,23 @@ PluginModuleChild::RecvSetAudioSessionDa
 
 void
 PluginModuleChild::QuickExit()
 {
     NS_WARNING("plugin process _exit()ing");
     _exit(0);
 }
 
+PPluginModuleChild*
+PluginModuleChild::AllocPPluginModuleChild(mozilla::ipc::Transport* aTransport,
+                                           base::ProcessId aOtherProcess)
+{
+    return PluginModuleChild::CreateForContentProcess(aTransport, aOtherProcess);
+}
+
 PCrashReporterChild*
 PluginModuleChild::AllocPCrashReporterChild(mozilla::dom::NativeThreadId* id,
                                             uint32_t* processType)
 {
     return new CrashReporterChild();
 }
 
 bool
@@ -703,16 +787,27 @@ PluginModuleChild::AnswerPCrashReporterC
     *processType = XRE_GetProcessType();
 #endif
     return true;
 }
 
 void
 PluginModuleChild::ActorDestroy(ActorDestroyReason why)
 {
+    if (!mIsChrome) {
+        PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
+        if (chromeInstance) {
+            chromeInstance->SendNotifyContentModuleDestroyed();
+        }
+
+        // Destroy ourselves once we finish other teardown activities.
+        MessageLoop::current()->PostTask(FROM_HERE, new DeleteTask<PluginModuleChild>(this));
+        return;
+    }
+
     if (AbnormalShutdown == why) {
         NS_WARNING("shutting down early because of crash!");
         QuickExit();
     }
 
     // doesn't matter why we're being destroyed; it's up to us to
     // initiate (clean) shutdown
     XRE_ShutdownChildProcess();
@@ -1027,17 +1122,17 @@ NPError
 
         case NPNVjavascriptEnabledBool: // Intentional fall-through
         case NPNVasdEnabledBool: // Intentional fall-through
         case NPNVisOfflineBool: // Intentional fall-through
         case NPNVSupportsXEmbedBool: // Intentional fall-through
         case NPNVSupportsWindowless: { // Intentional fall-through
             NPError result;
             bool value;
-            PluginModuleChild::current()->
+            PluginModuleChild::GetChrome()->
                 CallNPN_GetValue_WithBoolReturn(aVariable, &result, &value);
             *(NPBool*)aValue = value ? true : false;
             return result;
         }
 #if defined(MOZ_WIDGET_GTK)
         case NPNVxDisplay: {
             if (aNPP) {
                 return InstCast(aNPP)->NPN_GetValue(aVariable, aValue);
@@ -1218,17 +1313,20 @@ uint32_t
 }
 
 void
 _reloadplugins(NPBool aReloadPages)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     ENSURE_PLUGIN_THREAD_VOID();
 
-    PluginModuleChild::current()->SendNPN_ReloadPlugins(!!aReloadPages);
+    // Send the reload message to all modules. Chrome will need to reload from
+    // disk and content will need to request a new list of plugin tags from
+    // chrome.
+    PluginModuleChild::GetChrome()->SendNPN_ReloadPlugins(!!aReloadPages);
 }
 
 void
 _invalidaterect(NPP aNPP,
                 NPRect* aInvalidRect)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     ENSURE_PLUGIN_THREAD_VOID();
@@ -1257,17 +1355,17 @@ void
     // never be necessary.
 }
 
 const char*
 _useragent(NPP aNPP)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     ENSURE_PLUGIN_THREAD(nullptr);
-    return PluginModuleChild::current()->GetUserAgent();
+    return PluginModuleChild::GetChrome()->GetUserAgent();
 }
 
 void*
 _memalloc(uint32_t aSize)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     // Only assert plugin thread here for consistency with in-process plugins.
     AssertPluginThread();
@@ -1488,28 +1586,17 @@ void
 
 void
 _setexception(NPObject* aNPObj,
               const NPUTF8* aMessage)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     ENSURE_PLUGIN_THREAD_VOID();
 
-    PluginModuleChild* self = PluginModuleChild::current();
-    PluginScriptableObjectChild* actor = nullptr;
-    if (aNPObj) {
-        actor = PluginScriptableObjectChild::GetActorForNPObject(aNPObj);
-        if (!actor) {
-            NS_ERROR("Failed to get actor!");
-            return;
-        }
-    }
-
-    self->SendNPN_SetException(static_cast<PPluginScriptableObjectChild*>(actor),
-                               NullableString(aMessage));
+    // Do nothing. We no longer support this API.
 }
 
 void
 _pushpopupsenabledstate(NPP aNPP,
                         NPBool aEnabled)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     ENSURE_PLUGIN_THREAD_VOID();
@@ -1689,17 +1776,17 @@ NPError
 
     NPBool success = _convertpoint(instance, 
                                   pluginX,  pluginY, NPCoordinateSpacePlugin, 
                                  &screenX, &screenY, NPCoordinateSpaceScreen);
 
     if (success) {
         return mozilla::plugins::PluginUtilsOSX::ShowCocoaContextMenu(menu,
                                     screenX, screenY,
-                                    PluginModuleChild::current(),
+                                    PluginModuleChild::GetChrome(),
                                     ProcessBrowserEvents);
     } else {
         NS_WARNING("Convertpoint failed, could not created contextmenu.");
         return NPERR_GENERIC_ERROR;
     }
 
 #else
     NS_WARNING("Not supported on this platform!");
@@ -1747,32 +1834,34 @@ void
 
 //-----------------------------------------------------------------------------
 
 bool
 PluginModuleChild::AnswerNP_GetEntryPoints(NPError* _retval)
 {
     PLUGIN_LOG_DEBUG_METHOD;
     AssertPluginThread();
+    MOZ_ASSERT(mIsChrome);
 
 #if defined(OS_LINUX) || defined(OS_BSD)
     return true;
 #elif defined(OS_WIN) || defined(OS_MACOSX)
     *_retval = mGetEntryPointsFunc(&mFunctions);
     return true;
 #else
 #  error Please implement me for your platform
 #endif
 }
 
 bool
 PluginModuleChild::AnswerNP_Initialize(NPError* _retval)
 {
     PLUGIN_LOG_DEBUG_METHOD;
     AssertPluginThread();
+    MOZ_ASSERT(mIsChrome);
 
 #ifdef OS_WIN
     SetEventHooks();
 #endif
 
 #ifdef MOZ_X11
     // Send the parent our X socket to act as a proxy reference for our X
     // resources.
@@ -2193,17 +2282,17 @@ PluginModuleChild::CallWindowProcHook(in
     }
 
     return CallNextHookEx(nullptr, nCode, wParam, lParam);
 }
 
 LRESULT CALLBACK
 PluginModuleChild::NestedInputEventHook(int nCode, WPARAM wParam, LPARAM lParam)
 {
-    PluginModuleChild* self = current();
+    PluginModuleChild* self = GetChrome();
     uint32_t len = self->mIncallPumpingStack.Length();
     if (nCode >= 0 && len && !self->mIncallPumpingStack[len - 1]._spinning) {
         MessageLoop* loop = MessageLoop::current();
         self->SendProcessNativeEventsInInterruptCall();
         IncallFrame& f = self->mIncallPumpingStack[len - 1];
         f._spinning = true;
         f._savedNestableTasksAllowed = loop->NestableTasksAllowed();
         loop->SetNestableTasksAllowed(true);
--- a/dom/plugins/ipc/PluginModuleChild.h
+++ b/dom/plugins/ipc/PluginModuleChild.h
@@ -71,16 +71,20 @@ protected:
     }
 
     virtual bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE;
 
     // Implement the PPluginModuleChild interface
     virtual bool AnswerNP_GetEntryPoints(NPError* rv) MOZ_OVERRIDE;
     virtual bool AnswerNP_Initialize(NPError* rv) MOZ_OVERRIDE;
 
+    virtual PPluginModuleChild*
+    AllocPPluginModuleChild(mozilla::ipc::Transport* aTransport,
+                            base::ProcessId aOtherProcess) MOZ_OVERRIDE;
+
     virtual PPluginInstanceChild*
     AllocPPluginInstanceChild(const nsCString& aMimeType,
                               const uint16_t& aMode,
                               const InfallibleTArray<nsCString>& aNames,
                               const InfallibleTArray<nsCString>& aValues,
                               NPError* rv) MOZ_OVERRIDE;
 
     virtual bool
@@ -135,32 +139,44 @@ protected:
 
     virtual bool
     RecvProcessNativeEventsInInterruptCall() MOZ_OVERRIDE;
 
     virtual bool
     AnswerGeckoGetProfile(nsCString* aProfile) MOZ_OVERRIDE;
 
 public:
-    PluginModuleChild();
+    PluginModuleChild(bool aIsChrome);
     virtual ~PluginModuleChild();
 
+    bool CommonInit(base::ProcessHandle aParentProcessHandle,
+                    MessageLoop* aIOLoop,
+                    IPC::Channel* aChannel);
+
     // aPluginFilename is UTF8, not native-charset!
-    bool Init(const std::string& aPluginFilename,
-              base::ProcessHandle aParentProcessHandle,
-              MessageLoop* aIOLoop,
-              IPC::Channel* aChannel);
+    bool InitForChrome(const std::string& aPluginFilename,
+                       base::ProcessHandle aParentProcessHandle,
+                       MessageLoop* aIOLoop,
+                       IPC::Channel* aChannel);
+
+    bool InitForContent(base::ProcessHandle aParentProcessHandle,
+                        MessageLoop* aIOLoop,
+                        IPC::Channel* aChannel);
+
+    static PluginModuleChild*
+    CreateForContentProcess(mozilla::ipc::Transport* aTransport,
+                            base::ProcessId aOtherProcess);
 
     void CleanUp();
 
     const char* GetUserAgent();
 
     static const NPNetscapeFuncs sBrowserFuncs;
 
-    static PluginModuleChild* current();
+    static PluginModuleChild* GetChrome();
 
     /**
      * The child implementation of NPN_CreateObject.
      */
     static NPObject* NPN_CreateObject(NPP aNPP, NPClass* aClass);
     /**
      * The child implementation of NPN_RetainObject.
      */
@@ -283,27 +299,29 @@ private:
     virtual void ExitedCxxStack() MOZ_OVERRIDE;
 #endif
 
     PRLibrary* mLibrary;
     nsCString mPluginFilename; // UTF8
     nsCString mUserAgent;
     int mQuirks;
 
+    bool mIsChrome;
+    Transport* mTransport;
+
     // we get this from the plugin
     NP_PLUGINSHUTDOWN mShutdownFunc;
 #if defined(OS_LINUX) || defined(OS_BSD)
     NP_PLUGINUNIXINIT mInitializeFunc;
 #elif defined(OS_WIN) || defined(OS_MACOSX)
     NP_PLUGININIT mInitializeFunc;
     NP_GETENTRYPOINTS mGetEntryPointsFunc;
 #endif
 
     NPPluginFuncs mFunctions;
-    NPSavedData mSavedData;
 
 #if defined(MOZ_WIDGET_GTK)
     // If a plugin spins a nested glib event loop in response to a
     // synchronous IPC message from the browser, the loop might break
     // only after the browser responds to a request sent by the
     // plugin.  This can happen if a plugin uses gtk's synchronous
     // copy/paste, for example.  But because the browser is blocked on
     // a condvar, it can't respond to the request.  This situation
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -10,19 +10,22 @@
 #include <QtCore/QEventLoop>
 #include "NestedLoopTimer.h"
 #endif
 
 #include "mozilla/plugins/PluginModuleParent.h"
 
 #include "base/process_util.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/PCrashReporterParent.h"
 #include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/plugins/BrowserStreamParent.h"
+#include "mozilla/plugins/PluginBridge.h"
 #include "mozilla/plugins/PluginInstanceParent.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
 #include "nsAutoPtr.h"
 #include "nsCRT.h"
 #include "nsIFile.h"
 #include "nsIObserverService.h"
@@ -78,26 +81,92 @@ static const char kHangUIMinDisplayPref[
 template<>
 struct RunnableMethodTraits<mozilla::plugins::PluginModuleParent>
 {
     typedef mozilla::plugins::PluginModuleParent Class;
     static void RetainCallee(Class* obj) { }
     static void ReleaseCallee(Class* obj) { }
 };
 
+bool
+mozilla::plugins::SetupBridge(uint32_t aPluginId, dom::ContentParent* aContentParent)
+{
+    nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+    nsRefPtr<nsNPAPIPlugin> plugin;
+    nsresult rv = host->GetPluginForContentProcess(aPluginId, getter_AddRefs(plugin));
+    if (NS_FAILED(rv)) {
+        return false;
+    }
+    PluginModuleParent* chromeParent = static_cast<PluginModuleParent*>(plugin->GetLibrary());
+    return PPluginModule::Bridge(aContentParent, chromeParent);
+}
+
+PluginModuleContentParent* PluginModuleContentParent::sSavedModuleParent;
+
+/* static */ PluginLibrary*
+PluginModuleContentParent::LoadModule(uint32_t aPluginId)
+{
+    MOZ_ASSERT(!sSavedModuleParent);
+    MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
+
+    /*
+     * We send a LoadPlugin message to the chrome process using an intr
+     * message. Before it sends its response, it sends a message to create
+     * PluginModuleParent instance. That message is handled by
+     * PluginModuleContentParent::Create, which saves the instance in
+     * sSavedModuleParent. We fetch it from there after LoadPlugin finishes.
+     */
+    dom::ContentChild* cp = dom::ContentChild::GetSingleton();
+    if (!cp->CallLoadPlugin(aPluginId)) {
+        return nullptr;
+    }
+
+    PluginModuleContentParent* parent = sSavedModuleParent;
+    MOZ_ASSERT(parent);
+    sSavedModuleParent = nullptr;
+
+    return parent;
+}
+
+/* static */ PluginModuleContentParent*
+PluginModuleContentParent::Create(mozilla::ipc::Transport* aTransport,
+                                  base::ProcessId aOtherProcess)
+{
+    nsAutoPtr<PluginModuleContentParent> parent(new PluginModuleContentParent());
+    ProcessHandle handle;
+    if (!base::OpenProcessHandle(aOtherProcess, &handle)) {
+        // Bug 1090578 - need to kill |aOtherProcess|, it's boned.
+        return nullptr;
+    }
+
+    MOZ_ASSERT(!sSavedModuleParent);
+    sSavedModuleParent = parent;
+
+    DebugOnly<bool> ok = parent->Open(aTransport, handle, XRE_GetIOMessageLoop(),
+                                      mozilla::ipc::ParentSide);
+    MOZ_ASSERT(ok);
+
+    // Request Windows message deferral behavior on our channel. This
+    // applies to the top level and all sub plugin protocols since they
+    // all share the same channel.
+    parent->GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
+
+    return parent.forget();
+}
+
 // static
 PluginLibrary*
-PluginModuleParent::LoadModule(const char* aFilePath)
+PluginModuleChromeParent::LoadModule(const char* aFilePath, uint32_t aPluginId)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
 
     int32_t prefSecs = Preferences::GetInt(kLaunchTimeoutPref, 0);
 
     // Block on the child process being launched and initialized.
-    nsAutoPtr<PluginModuleParent> parent(new PluginModuleParent(aFilePath));
+    nsAutoPtr<PluginModuleChromeParent> parent(new PluginModuleChromeParent(aFilePath, aPluginId));
     bool launched = parent->mSubprocess->Launch(prefSecs * 1000);
     if (!launched) {
         // We never reached open
         parent->mShutdown = true;
         return nullptr;
     }
     parent->Open(parent->mSubprocess->GetChannel(),
                  parent->mSubprocess->GetChildProcessHandle());
@@ -119,25 +188,50 @@ PluginModuleParent::LoadModule(const cha
     mozilla::MutexAutoLock lock(parent->mCrashReporterMutex);
     parent->mCrashReporter = parent->CrashReporter();
 #endif
 #endif
 
     return parent.forget();
 }
 
-
-PluginModuleParent::PluginModuleParent(const char* aFilePath)
-    : mSubprocess(new PluginProcessParent(aFilePath))
+PluginModuleParent::PluginModuleParent(bool aIsChrome)
+    : mIsChrome(aIsChrome)
     , mShutdown(false)
     , mClearSiteDataSupported(false)
     , mGetSitesWithDataSupported(false)
     , mNPNIface(nullptr)
     , mPlugin(nullptr)
     , mTaskFactory(MOZ_THIS_IN_INITIALIZER_LIST())
+{
+}
+
+PluginModuleParent::~PluginModuleParent()
+{
+    if (!OkToCleanup()) {
+        NS_RUNTIMEABORT("unsafe destruction");
+    }
+
+    if (!mShutdown) {
+        NS_WARNING("Plugin host deleted the module without shutting down.");
+        NPError err;
+        NP_Shutdown(&err);
+    }
+}
+
+PluginModuleContentParent::PluginModuleContentParent()
+    : PluginModuleParent(false)
+{
+}
+
+PluginModuleChromeParent::PluginModuleChromeParent(const char* aFilePath, uint32_t aPluginId)
+    : PluginModuleParent(true)
+    , mSubprocess(new PluginProcessParent(aFilePath))
+    , mPluginId(aPluginId)
+    , mChromeTaskFactory(MOZ_THIS_IN_INITIALIZER_LIST())
     , mHangAnnotationFlags(0)
 #ifdef XP_WIN
     , mPluginCpuUsageOnHang()
     , mHangUIParent(nullptr)
     , mHangUIEnabled(true)
     , mIsTimerReset(true)
 #ifdef MOZ_CRASHREPORTER
     , mCrashReporterMutex("PluginModuleParent::mCrashReporterMutex")
@@ -160,17 +254,17 @@ PluginModuleParent::PluginModuleParent(c
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
     InitPluginProfiling();
 #endif
 
     mozilla::HangMonitor::RegisterAnnotator(*this);
 }
 
-PluginModuleParent::~PluginModuleParent()
+PluginModuleChromeParent::~PluginModuleChromeParent()
 {
     if (!OkToCleanup()) {
         NS_RUNTIMEABORT("unsafe destruction");
     }
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
     ShutdownPluginProfiling();
 #endif
@@ -207,17 +301,17 @@ PluginModuleParent::~PluginModuleParent(
     }
 #endif
 
     mozilla::HangMonitor::UnregisterAnnotator(*this);
 }
 
 #ifdef MOZ_CRASHREPORTER
 void
-PluginModuleParent::WriteExtraDataForMinidump(AnnotationTable& notes)
+PluginModuleChromeParent::WriteExtraDataForMinidump(AnnotationTable& notes)
 {
 #ifdef XP_WIN
     // mCrashReporterMutex is already held by the caller
     mCrashReporterMutex.AssertCurrentThreadOwns();
 #endif
     typedef nsDependentCString CS;
 
     // Get the plugin filename, try to get just the file leafname
@@ -252,58 +346,58 @@ PluginModuleParent::WriteExtraDataForMin
 #endif
         }
 #endif
     }
 }
 #endif  // MOZ_CRASHREPORTER
 
 void
-PluginModuleParent::SetChildTimeout(const int32_t aChildTimeout)
+PluginModuleChromeParent::SetChildTimeout(const int32_t aChildTimeout)
 {
     int32_t timeoutMs = (aChildTimeout > 0) ? (1000 * aChildTimeout) :
                       MessageChannel::kNoTimeout;
     SetReplyTimeoutMs(timeoutMs);
 }
 
 void
-PluginModuleParent::TimeoutChanged(const char* aPref, void* aModule)
+PluginModuleChromeParent::TimeoutChanged(const char* aPref, void* aModule)
 {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 #ifndef XP_WIN
     if (!strcmp(aPref, kChildTimeoutPref)) {
       // The timeout value used by the parent for children
       int32_t timeoutSecs = Preferences::GetInt(kChildTimeoutPref, 0);
-      static_cast<PluginModuleParent*>(aModule)->SetChildTimeout(timeoutSecs);
+      static_cast<PluginModuleChromeParent*>(aModule)->SetChildTimeout(timeoutSecs);
 #else
     if (!strcmp(aPref, kChildTimeoutPref) ||
         !strcmp(aPref, kHangUIMinDisplayPref) ||
         !strcmp(aPref, kHangUITimeoutPref)) {
-      static_cast<PluginModuleParent*>(aModule)->EvaluateHangUIState(true);
+      static_cast<PluginModuleChromeParent*>(aModule)->EvaluateHangUIState(true);
 #endif // XP_WIN
     } else if (!strcmp(aPref, kParentTimeoutPref)) {
       // The timeout value used by the child for its parent
       int32_t timeoutSecs = Preferences::GetInt(kParentTimeoutPref, 0);
-      unused << static_cast<PluginModuleParent*>(aModule)->SendSetParentHangTimeout(timeoutSecs);
+      unused << static_cast<PluginModuleChromeParent*>(aModule)->SendSetParentHangTimeout(timeoutSecs);
     }
 }
 
 void
-PluginModuleParent::CleanupFromTimeout(const bool aFromHangUI)
+PluginModuleChromeParent::CleanupFromTimeout(const bool aFromHangUI)
 {
     if (mShutdown) {
       return;
     }
 
     if (!OkToCleanup()) {
         // there's still plugin code on the C++ stack, try again
         MessageLoop::current()->PostDelayedTask(
             FROM_HERE,
-            mTaskFactory.NewRunnableMethod(
-                &PluginModuleParent::CleanupFromTimeout, aFromHangUI), 10);
+            mChromeTaskFactory.NewRunnableMethod(
+                &PluginModuleChromeParent::CleanupFromTimeout, aFromHangUI), 10);
         return;
     }
 
     /* If the plugin container was terminated by the Plugin Hang UI, 
        then either the I/O thread detects a channel error, or the 
        main thread must set the error (whomever gets there first).
        OTOH, if we terminate and return false from 
        ShouldContinueFromReplyTimeout, then the channel state has 
@@ -380,35 +474,35 @@ GetProcessCpuUsage(const InfallibleTArra
   return true;
 }
 
 } // anonymous namespace
 
 #endif // #ifdef XP_WIN
 
 void
-PluginModuleParent::EnteredCxxStack()
+PluginModuleChromeParent::EnteredCxxStack()
 {
     mHangAnnotationFlags |= kInPluginCall;
 }
 
 void
-PluginModuleParent::ExitedCxxStack()
+PluginModuleChromeParent::ExitedCxxStack()
 {
     mHangAnnotationFlags = 0;
 #ifdef XP_WIN
     FinishHangUI();
 #endif
 }
 
 /**
  * This function is always called by the HangMonitor thread.
  */
 void
-PluginModuleParent::AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations)
+PluginModuleChromeParent::AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations)
 {
     uint32_t flags = mHangAnnotationFlags;
     if (flags) {
         /* We don't actually annotate anything specifically for kInPluginCall;
            we use it to determine whether to annotate other things. It will
            be pretty obvious from the ChromeHang stack that we're in a plugin
            call when the hang occurred. */
         if (flags & kHangUIShown) {
@@ -446,32 +540,32 @@ CreateFlashMinidump(DWORD processId, Thr
   bool res = CreateAdditionalChildMinidump(handle, 0, parentMinidump, name);
   base::CloseProcessHandle(handle);
 
   return res;
 }
 #endif
 
 bool
-PluginModuleParent::ShouldContinueFromReplyTimeout()
+PluginModuleChromeParent::ShouldContinueFromReplyTimeout()
 {
 #ifdef XP_WIN
     if (LaunchHangUI()) {
         return true;
     }
     // If LaunchHangUI returned false then we should proceed with the 
     // original plugin hang behaviour and kill the plugin container.
     FinishHangUI();
 #endif // XP_WIN
     TerminateChildProcess(MessageLoop::current());
     return false;
 }
 
 void
-PluginModuleParent::TerminateChildProcess(MessageLoop* aMsgLoop)
+PluginModuleChromeParent::TerminateChildProcess(MessageLoop* aMsgLoop)
 {
 #ifdef MOZ_CRASHREPORTER
 #ifdef XP_WIN
     mozilla::MutexAutoLock lock(mCrashReporterMutex);
     CrashReporterParent* crashReporter = mCrashReporter;
     if (!crashReporter) {
         // If mCrashReporter is null then the hang has ended, the plugin module
         // is shutting down. There's nothing to do here.
@@ -551,18 +645,18 @@ PluginModuleParent::TerminateChildProces
     }
 #endif
 
     // this must run before the error notification from the channel,
     // or not at all
     bool isFromHangUI = aMsgLoop != MessageLoop::current();
     aMsgLoop->PostTask(
         FROM_HERE,
-        mTaskFactory.NewRunnableMethod(
-            &PluginModuleParent::CleanupFromTimeout, isFromHangUI));
+        mChromeTaskFactory.NewRunnableMethod(
+            &PluginModuleChromeParent::CleanupFromTimeout, isFromHangUI));
 
     if (!KillProcess(OtherProcess(), 1, false))
         NS_WARNING("failed to kill subprocess!");
 }
 
 bool
 PluginModuleParent::GetPluginDetails(nsACString& aPluginName,
                                      nsACString& aPluginVersion)
@@ -577,17 +671,17 @@ PluginModuleParent::GetPluginDetails(nsA
     }
     aPluginName = pluginTag->mName;
     aPluginVersion = pluginTag->mVersion;
     return true;
 }
 
 #ifdef XP_WIN
 void
-PluginModuleParent::EvaluateHangUIState(const bool aReset)
+PluginModuleChromeParent::EvaluateHangUIState(const bool aReset)
 {
     int32_t minDispSecs = Preferences::GetInt(kHangUIMinDisplayPref, 10);
     int32_t autoStopSecs = Preferences::GetInt(kChildTimeoutPref, 0);
     int32_t timeoutSecs = 0;
     if (autoStopSecs > 0 && autoStopSecs < minDispSecs) {
         /* If we're going to automatically terminate the plugin within a 
            time frame shorter than minDispSecs, there's no point in 
            showing the hang UI; it would just flash briefly on the screen. */
@@ -611,17 +705,17 @@ PluginModuleParent::EvaluateHangUIState(
             autoStopSecs *= 2;
         }
     }
     mIsTimerReset = false;
     SetChildTimeout(autoStopSecs);
 }
 
 bool
-PluginModuleParent::LaunchHangUI()
+PluginModuleChromeParent::LaunchHangUI()
 {
     if (!mHangUIEnabled) {
         return false;
     }
     if (mHangUIParent) {
         if (mHangUIParent->IsShowing()) {
             // We've already shown the UI but the timeout has expired again.
             return false;
@@ -648,17 +742,17 @@ PluginModuleParent::LaunchHangUI()
            after kChildTimeoutPref seconds if the user doesn't respond to 
            the hang UI. */
         EvaluateHangUIState(false);
     }
     return retval;
 }
 
 void
-PluginModuleParent::FinishHangUI()
+PluginModuleChromeParent::FinishHangUI()
 {
     if (mHangUIEnabled && mHangUIParent) {
         bool needsCancel = mHangUIParent->IsShowing();
         // If we're still showing, send a Cancel notification
         if (needsCancel) {
             mHangUIParent->Cancel();
         }
         /* If we cancelled the UI or if the user issued a response,
@@ -669,25 +763,25 @@ PluginModuleParent::FinishHangUI()
                UI was displayed. Now that we're finishing the UI, we need to 
                switch it back to kHangUITimeoutPref. */
             EvaluateHangUIState(true);
         }
     }
 }
 
 void
-PluginModuleParent::OnHangUIContinue()
+PluginModuleChromeParent::OnHangUIContinue()
 {
     mHangAnnotationFlags |= kHangUIContinued;
 }
 #endif // XP_WIN
 
 #ifdef MOZ_CRASHREPORTER
 CrashReporterParent*
-PluginModuleParent::CrashReporter()
+PluginModuleChromeParent::CrashReporter()
 {
     return static_cast<CrashReporterParent*>(ManagedPCrashReporterParent()[0]);
 }
 
 #ifdef MOZ_CRASHREPORTER_INJECTOR
 static void
 RemoveMinidump(nsIFile* minidump)
 {
@@ -699,17 +793,17 @@ RemoveMinidump(nsIFile* minidump)
     if (GetExtraFileForMinidump(minidump,
                                 getter_AddRefs(extraFile))) {
         extraFile->Remove(true);
     }
 }
 #endif // MOZ_CRASHREPORTER_INJECTOR
 
 void
-PluginModuleParent::ProcessFirstMinidump()
+PluginModuleChromeParent::ProcessFirstMinidump()
 {
 #ifdef XP_WIN
     mozilla::MutexAutoLock lock(mCrashReporterMutex);
 #endif
     CrashReporterParent* crashReporter = CrashReporter();
     if (!crashReporter)
         return;
 
@@ -776,20 +870,16 @@ PluginModuleParent::ProcessFirstMinidump
 }
 #endif
 
 void
 PluginModuleParent::ActorDestroy(ActorDestroyReason why)
 {
     switch (why) {
     case AbnormalShutdown: {
-#ifdef MOZ_CRASHREPORTER
-        ProcessFirstMinidump();
-#endif
-
         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)
             MessageLoop::current()->PostTask(
                 FROM_HERE,
                 mTaskFactory.NewRunnableMethod(
                     &PluginModuleParent::NotifyPluginCrashed));
@@ -800,16 +890,28 @@ PluginModuleParent::ActorDestroy(ActorDe
         break;
 
     default:
         NS_RUNTIMEABORT("Unexpected shutdown reason for toplevel actor.");
     }
 }
 
 void
+PluginModuleChromeParent::ActorDestroy(ActorDestroyReason why)
+{
+    if (why == AbnormalShutdown) {
+#ifdef MOZ_CRASHREPORTER
+        ProcessFirstMinidump();
+#endif
+    }
+
+    PluginModuleParent::ActorDestroy(why);
+}
+
+void
 PluginModuleParent::NotifyPluginCrashed()
 {
     if (!OkToCleanup()) {
         // there's still plugin code on the C++ stack.  try again
         MessageLoop::current()->PostDelayedTask(
             FROM_HERE,
             mTaskFactory.NewRunnableMethod(
                 &PluginModuleParent::NotifyPluginCrashed), 10);
@@ -1168,23 +1270,26 @@ PluginModuleParent::NP_Initialize(NPNets
 
     mNPNIface = bFuncs;
 
     if (mShutdown) {
         *error = NPERR_GENERIC_ERROR;
         return NS_ERROR_FAILURE;
     }
 
-    if (!CallNP_Initialize(error)) {
-        Close();
-        return NS_ERROR_FAILURE;
-    }
-    else if (*error != NPERR_NO_ERROR) {
-        Close();
-        return NS_OK;
+    *error = NPERR_NO_ERROR;
+    if (IsChrome()) {
+        if (!CallNP_Initialize(error)) {
+            Close();
+            return NS_ERROR_FAILURE;
+        }
+        else if (*error != NPERR_NO_ERROR) {
+            Close();
+            return NS_OK;
+        }
     }
 
     SetPluginFuncs(pFuncs);
 
     return NS_OK;
 }
 #else
 nsresult
@@ -1194,16 +1299,27 @@ PluginModuleParent::NP_Initialize(NPNets
 
     mNPNIface = bFuncs;
 
     if (mShutdown) {
         *error = NPERR_GENERIC_ERROR;
         return NS_ERROR_FAILURE;
     }
 
+    *error = NPERR_NO_ERROR;
+    return NS_OK;
+}
+
+nsresult
+PluginModuleChromeParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error)
+{
+    nsresult rv = PluginModuleParent::NP_Initialize(bFuncs, error);
+    if (NS_FAILED(rv))
+        return rv;
+
     if (!CallNP_Initialize(error)) {
         Close();
         return NS_ERROR_FAILURE;
     }
     if (*error != NPERR_NO_ERROR) {
         Close();
         return NS_OK;
     }
@@ -1233,17 +1349,20 @@ PluginModuleParent::NP_Shutdown(NPError*
 {
     PLUGIN_LOG_DEBUG_METHOD;
 
     if (mShutdown) {
         *error = NPERR_GENERIC_ERROR;
         return NS_ERROR_FAILURE;
     }
 
-    bool ok = CallNP_Shutdown(error);
+    bool ok = true;
+    if (IsChrome()) {
+        ok = CallNP_Shutdown(error);
+    }
 
     // if NP_Shutdown() is nested within another interrupt call, this will
     // break things.  but lord help us if we're doing that anyway; the
     // plugin dso will have been unloaded on the other side by the
     // CallNP_Shutdown() message
     Close();
 
     return ok ? NS_OK : NS_ERROR_FAILURE;
@@ -1271,26 +1390,31 @@ PluginModuleParent::NP_GetValue(void *fu
 }
 
 #if defined(XP_WIN) || defined(XP_MACOSX)
 nsresult
 PluginModuleParent::NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error)
 {
     NS_ASSERTION(pFuncs, "Null pointer!");
 
-    // We need to have the child process update its function table
-    // here by actually calling NP_GetEntryPoints since the parent's
-    // function table can reflect nullptr entries in the child's table.
-    if (!CallNP_GetEntryPoints(error)) {
-        return NS_ERROR_FAILURE;
-    }
-    else if (*error != NPERR_NO_ERROR) {
-        return NS_OK;
+    // We need to have the plugin process update its function table here by
+    // actually calling NP_GetEntryPoints. The parent's function table will
+    // reflect nullptr entries in the child's table once SetPluginFuncs is
+    // called.
+
+    if (IsChrome()) {
+        if (!CallNP_GetEntryPoints(error)) {
+            return NS_ERROR_FAILURE;
+        }
+        else if (*error != NPERR_NO_ERROR) {
+            return NS_OK;
+        }
     }
 
+    *error = NPERR_NO_ERROR;
     SetPluginFuncs(pFuncs);
 
     return NS_OK;
 }
 #endif
 
 nsresult
 PluginModuleParent::NPP_New(NPMIMEType pluginType, NPP instance,
@@ -1342,19 +1466,25 @@ PluginModuleParent::NPP_New(NPMIMEType p
         return NS_ERROR_FAILURE;
     }
 
     if (*error != NPERR_NO_ERROR) {
         NPP_Destroy(instance, 0);
         return NS_ERROR_FAILURE;
     }
 
+    UpdatePluginTimeout();
+
+    return NS_OK;
+}
+
+void
+PluginModuleChromeParent::UpdatePluginTimeout()
+{
     TimeoutChanged(kParentTimeoutPref, this);
-    
-    return NS_OK;
 }
 
 nsresult
 PluginModuleParent::NPP_ClearSiteData(const char* site, uint64_t flags,
                                       uint64_t maxAge)
 {
     if (!mClearSiteDataSupported)
         return NS_ERROR_NOT_AVAILABLE;
@@ -1523,25 +1653,38 @@ PluginModuleParent::RecvPluginHideWindow
     return false;
 #endif
 }
 
 PCrashReporterParent*
 PluginModuleParent::AllocPCrashReporterParent(mozilla::dom::NativeThreadId* id,
                                               uint32_t* processType)
 {
+    MOZ_CRASH("unreachable");
+}
+
+bool
+PluginModuleParent::DeallocPCrashReporterParent(PCrashReporterParent* actor)
+{
+    MOZ_CRASH("unreachable");
+}
+
+PCrashReporterParent*
+PluginModuleChromeParent::AllocPCrashReporterParent(mozilla::dom::NativeThreadId* id,
+                                                    uint32_t* processType)
+{
 #ifdef MOZ_CRASHREPORTER
     return new CrashReporterParent();
 #else
     return nullptr;
 #endif
 }
 
 bool
-PluginModuleParent::DeallocPCrashReporterParent(PCrashReporterParent* actor)
+PluginModuleChromeParent::DeallocPCrashReporterParent(PCrashReporterParent* actor)
 {
 #ifdef MOZ_CRASHREPORTER
 #ifdef XP_WIN
     mozilla::MutexAutoLock lock(mCrashReporterMutex);
     if (actor == static_cast<PCrashReporterParent*>(mCrashReporter)) {
         mCrashReporter = nullptr;
     }
 #endif
@@ -1617,42 +1760,44 @@ PluginModuleParent::RecvGetNativeCursors
 #else
     NS_NOTREACHED(
         "PluginInstanceParent::RecvGetNativeCursorSupportLevel not implemented!");
     return false;
 #endif
 }
 
 bool
-PluginModuleParent::RecvNPN_SetException(PPluginScriptableObjectParent* aActor,
-                                         const nsCString& aMessage)
+PluginModuleParent::RecvNPN_SetException(const nsCString& aMessage)
 {
     PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
 
-    NPObject* aNPObj = nullptr;
-    if (aActor) {
-        aNPObj = static_cast<PluginScriptableObjectParent*>(aActor)->GetObject(true);
-        if (!aNPObj) {
-            NS_ERROR("Failed to get object!");
-            return false;
-        }
-    }
-    mozilla::plugins::parent::_setexception(aNPObj, NullableStringGet(aMessage));
+    // This function ignores its first argument.
+    mozilla::plugins::parent::_setexception(nullptr, NullableStringGet(aMessage));
     return true;
 }
 
 bool
 PluginModuleParent::RecvNPN_ReloadPlugins(const bool& aReloadPages)
 {
     PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
 
     mozilla::plugins::parent::_reloadplugins(aReloadPages);
     return true;
 }
 
+bool
+PluginModuleChromeParent::RecvNotifyContentModuleDestroyed()
+{
+    nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+    if (host) {
+        host->NotifyContentModuleDestroyed(mPluginId);
+    }
+    return true;
+}
+
 #ifdef MOZ_CRASHREPORTER_INJECTOR
 
 // We only add the crash reporter to subprocess which have the filename
 // FlashPlayerPlugin*
 #define FLASH_PROCESS_PREFIX "FLASHPLAYERPLUGIN"
 
 static DWORD
 GetFlashChildOfPID(DWORD pid, HANDLE snapshot)
@@ -1673,17 +1818,17 @@ GetFlashChildOfPID(DWORD pid, HANDLE sna
     }
     return 0;
 }
 
 // We only look for child processes of the Flash plugin, NPSWF*
 #define FLASH_PLUGIN_PREFIX "NPSWF"
 
 void
-PluginModuleParent::InitializeInjector()
+PluginModuleChromeParent::InitializeInjector()
 {
     if (!Preferences::GetBool("dom.ipc.plugins.flash.subprocess.crashreporter.enabled", false))
         return;
 
     nsCString path(Process()->GetPluginFilePath().c_str());
     ToUpperCase(path);
     int32_t lastSlash = path.RFindCharInSet("\\/");
     if (kNotFound == lastSlash)
@@ -1705,17 +1850,17 @@ PluginModuleParent::InitializeInjector()
         mFlashProcess2 = GetFlashChildOfPID(mFlashProcess1, snapshot);
         if (mFlashProcess2) {
             InjectCrashReporterIntoProcess(mFlashProcess2, this);
         }
     }
 }
 
 void
-PluginModuleParent::OnCrash(DWORD processID)
+PluginModuleChromeParent::OnCrash(DWORD processID)
 {
     if (!mShutdown) {
         GetIPCChannel()->CloseWithError();
         KillProcess(OtherProcess(), 1, false);
     }
 }
 
 #endif // MOZ_CRASHREPORTER_INJECTOR
@@ -1751,26 +1896,26 @@ PluginProfilerObserver::Observe(nsISuppo
       if (success && !result.IsEmpty()) {
           pse->AddSubProfile(result.get());
       }
     }
     return NS_OK;
 }
 
 void
-PluginModuleParent::InitPluginProfiling()
+PluginModuleChromeParent::InitPluginProfiling()
 {
     nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
     if (observerService) {
         mProfilerObserver = new PluginProfilerObserver(this);
         observerService->AddObserver(mProfilerObserver, "profiler-subprocess", false);
     }
 }
 
 void
-PluginModuleParent::ShutdownPluginProfiling()
+PluginModuleChromeParent::ShutdownPluginProfiling()
 {
     nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
     if (observerService) {
         observerService->RemoveObserver(mProfilerObserver, "profiler-subprocess");
     }
 }
 #endif
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -48,104 +48,75 @@ class PluginHangUIParent;
  *
  * This class implements the NPP API from the perspective of the rest
  * of Gecko, forwarding NPP calls along to the child process that is
  * actually running the plugin.
  *
  * This class /also/ implements a version of the NPN API, because the
  * child process needs to make these calls back into Gecko proper.
  * This class is responsible for "actually" making those function calls.
+ *
+ * If a plugin is running, there will always be one PluginModuleParent for it in
+ * the chrome process. In addition, any content process using the plugin will
+ * have its own PluginModuleParent. The subclasses PluginModuleChromeParent and
+ * PluginModuleContentParent implement functionality that is specific to one
+ * case or the other.
  */
 class PluginModuleParent
     : public PPluginModuleParent
     , public PluginLibrary
 #ifdef MOZ_CRASHREPORTER_INJECTOR
     , public CrashReporter::InjectorCrashCallback
 #endif
-    , public mozilla::HangMonitor::Annotator
 {
-private:
+protected:
     typedef mozilla::PluginLibrary PluginLibrary;
     typedef mozilla::dom::PCrashReporterParent PCrashReporterParent;
     typedef mozilla::dom::CrashReporterParent CrashReporterParent;
 
-protected:
-
     PPluginInstanceParent*
     AllocPPluginInstanceParent(const nsCString& aMimeType,
                                const uint16_t& aMode,
                                const InfallibleTArray<nsCString>& aNames,
                                const InfallibleTArray<nsCString>& aValues,
                                NPError* rv) MOZ_OVERRIDE;
 
     virtual bool
     DeallocPPluginInstanceParent(PPluginInstanceParent* aActor) MOZ_OVERRIDE;
 
 public:
-    // aFilePath is UTF8, not native!
-    explicit PluginModuleParent(const char* aFilePath);
+    explicit PluginModuleParent(bool aIsChrome);
     virtual ~PluginModuleParent();
 
+    bool IsChrome() const { return mIsChrome; }
+
     virtual void SetPlugin(nsNPAPIPlugin* plugin) MOZ_OVERRIDE
     {
         mPlugin = plugin;
     }
 
     virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 
-    /**
-     * LoadModule
-     *
-     * This may or may not launch a plugin child process,
-     * and may or may not be very expensive.
-     */
-    static PluginLibrary* LoadModule(const char* aFilePath);
-
     const NPNetscapeFuncs* GetNetscapeFuncs() {
         return mNPNIface;
     }
 
-    PluginProcessParent* Process() const { return mSubprocess; }
-    base::ProcessHandle ChildProcessHandle() { return mSubprocess->GetChildProcessHandle(); }
-
     bool OkToCleanup() const {
         return !IsOnCxxStack();
     }
 
     void ProcessRemoteNativeEventsInInterruptCall();
 
-    void TerminateChildProcess(MessageLoop* aMsgLoop);
-
-    virtual void
-    EnteredCxxStack() MOZ_OVERRIDE;
-
-    virtual void
-    ExitedCxxStack() MOZ_OVERRIDE;
-
-    virtual void
-    AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations) MOZ_OVERRIDE;
-
-#ifdef XP_WIN
-    /**
-     * Called by Plugin Hang UI to notify that the user has clicked continue.
-     * Used for chrome hang annotations.
-     */
-    void
-    OnHangUIContinue();
-#endif // XP_WIN
-
 protected:
     virtual mozilla::ipc::RacyInterruptPolicy
     MediateInterruptRace(const Message& parent, const Message& child) MOZ_OVERRIDE
     {
         return MediateRace(parent, child);
     }
 
-    virtual bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE;
-
     virtual bool
     RecvBackUpXResources(const FileDescriptor& aXSocketFd) MOZ_OVERRIDE;
 
     virtual bool
     AnswerNPN_UserAgent(nsCString* userAgent) MOZ_OVERRIDE;
 
     virtual bool
     AnswerNPN_GetValue_WithBoolReturn(const NPNVariable& aVariable,
@@ -182,37 +153,30 @@ protected:
 
     virtual bool
     RecvPopCursor() MOZ_OVERRIDE;
 
     virtual bool
     RecvGetNativeCursorsSupported(bool* supported) MOZ_OVERRIDE;
 
     virtual bool
-    RecvNPN_SetException(PPluginScriptableObjectParent* aActor,
-                         const nsCString& aMessage) MOZ_OVERRIDE;
+    RecvNPN_SetException(const nsCString& aMessage) MOZ_OVERRIDE;
 
     virtual bool
     RecvNPN_ReloadPlugins(const bool& aReloadPages) MOZ_OVERRIDE;
 
     static PluginInstanceParent* InstCast(NPP instance);
     static BrowserStreamParent* StreamCast(NPP instance, NPStream* s);
 
-private:
-    void SetPluginFuncs(NPPluginFuncs* aFuncs);
+protected:
+    virtual void UpdatePluginTimeout() {}
 
-    // Implement the module-level functions from NPAPI; these are
-    // normally resolved directly from the DSO.
-#ifdef OS_LINUX
-    NPError NP_Initialize(const NPNetscapeFuncs* npnIface,
-                          NPPluginFuncs* nppIface);
-#else
-    NPError NP_Initialize(const NPNetscapeFuncs* npnIface);
-    NPError NP_GetEntryPoints(NPPluginFuncs* nppIface);
-#endif
+    virtual bool RecvNotifyContentModuleDestroyed() MOZ_OVERRIDE { return true; }
+
+    void SetPluginFuncs(NPPluginFuncs* aFuncs);
 
     // NPP-like API that Gecko calls are trampolined into.  These 
     // messages then get forwarded along to the plugin instance,
     // and then eventually the child process.
 
     static NPError NPP_Destroy(NPP instance, NPSavedData** save);
 
     static NPError NPP_SetWindow(NPP instance, NPWindow* window);
@@ -250,16 +214,17 @@ private:
                                          const nsIntRect& aRect) MOZ_OVERRIDE;
 
 #if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
     virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error);
 #else
     virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error);
 #endif
     virtual nsresult NP_Shutdown(NPError* error);
+
     virtual nsresult NP_GetMIMEDescription(const char** mimeDesc);
     virtual nsresult NP_GetValue(void *future, NPPVariable aVariable,
                                  void *aValue, NPError* error);
 #if defined(XP_WIN) || defined(XP_MACOSX)
     virtual nsresult NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error);
 #endif
     virtual nsresult NPP_New(NPMIMEType pluginType, NPP instance,
                              uint16_t mode, int16_t argc, char* argn[],
@@ -269,54 +234,152 @@ private:
                                        uint64_t maxAge);
     virtual nsresult NPP_GetSitesWithData(InfallibleTArray<nsCString>& result);
 
 #if defined(XP_MACOSX)
     virtual nsresult IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing);
     virtual nsresult ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor);
 #endif
 
-private:
-    CrashReporterParent* CrashReporter();
-
-#ifdef MOZ_CRASHREPORTER
-    void ProcessFirstMinidump();
-    void WriteExtraDataForMinidump(CrashReporter::AnnotationTable& notes);
-#endif
-    void CleanupFromTimeout(const bool aByHangUI);
-    void SetChildTimeout(const int32_t aChildTimeout);
-    static void TimeoutChanged(const char* aPref, void* aModule);
+protected:
     void NotifyPluginCrashed();
 
-#ifdef MOZ_ENABLE_PROFILER_SPS
-    void InitPluginProfiling();
-    void ShutdownPluginProfiling();
-#endif
-
-    PluginProcessParent* mSubprocess;
+    bool mIsChrome;
     bool mShutdown;
     bool mClearSiteDataSupported;
     bool mGetSitesWithDataSupported;
     const NPNetscapeFuncs* mNPNIface;
     nsNPAPIPlugin* mPlugin;
     ScopedMethodFactory<PluginModuleParent> mTaskFactory;
     nsString mPluginDumpID;
     nsString mBrowserDumpID;
     nsString mHangID;
     nsRefPtr<nsIObserver> mProfilerObserver;
+    nsCString mPluginName;
+    nsCString mPluginVersion;
+
+#ifdef MOZ_X11
+    // Dup of plugin's X socket, used to scope its resources to this
+    // object instead of the plugin process's lifetime
+    ScopedClose mPluginXSocketFdDup;
+#endif
+
+    bool
+    GetPluginDetails(nsACString& aPluginName, nsACString& aPluginVersion);
+
+    friend class mozilla::dom::CrashReporterParent;
+};
+
+class PluginModuleContentParent : public PluginModuleParent
+{
+  public:
+    static PluginLibrary* LoadModule(uint32_t aPluginId);
+
+    static PluginModuleContentParent* Create(mozilla::ipc::Transport* aTransport,
+                                             base::ProcessId aOtherProcess);
+
+  private:
+    explicit PluginModuleContentParent();
+
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+    void OnCrash(DWORD processID) MOZ_OVERRIDE {}
+#endif
+
+    static PluginModuleContentParent* sSavedModuleParent;
+};
+
+class PluginModuleChromeParent
+    : public PluginModuleParent
+    , public mozilla::HangMonitor::Annotator
+{
+  public:
+    /**
+     * LoadModule
+     *
+     * This may or may not launch a plugin child process,
+     * and may or may not be very expensive.
+     */
+    static PluginLibrary* LoadModule(const char* aFilePath, uint32_t aPluginId);
+
+    virtual ~PluginModuleChromeParent();
+
+    void TerminateChildProcess(MessageLoop* aMsgLoop);
+
+#ifdef XP_WIN
+    /**
+     * Called by Plugin Hang UI to notify that the user has clicked continue.
+     * Used for chrome hang annotations.
+     */
+    void
+    OnHangUIContinue();
+#endif // XP_WIN
+
+private:
+    virtual void
+    EnteredCxxStack() MOZ_OVERRIDE;
+
+    void
+    ExitedCxxStack() MOZ_OVERRIDE;
+
+    virtual void
+    AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations) MOZ_OVERRIDE;
+
+    virtual bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE;
+
+#ifdef MOZ_CRASHREPORTER
+    void ProcessFirstMinidump();
+    void WriteExtraDataForMinidump(CrashReporter::AnnotationTable& notes);
+#endif
+
+    virtual PCrashReporterParent*
+    AllocPCrashReporterParent(mozilla::dom::NativeThreadId* id,
+                              uint32_t* processType) MOZ_OVERRIDE;
+    virtual bool
+    DeallocPCrashReporterParent(PCrashReporterParent* actor) MOZ_OVERRIDE;
+
+    PluginProcessParent* Process() const { return mSubprocess; }
+    base::ProcessHandle ChildProcessHandle() { return mSubprocess->GetChildProcessHandle(); }
+
+#if !defined(XP_UNIX) || defined(XP_MACOSX) || defined(MOZ_WIDGET_GONK)
+    virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error);
+#endif
+
+    virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
+
+    // aFilePath is UTF8, not native!
+    explicit PluginModuleChromeParent(const char* aFilePath, uint32_t aPluginId);
+
+    CrashReporterParent* CrashReporter();
+
+    void CleanupFromTimeout(const bool aByHangUI);
+    void SetChildTimeout(const int32_t aChildTimeout);
+    static void TimeoutChanged(const char* aPref, void* aModule);
+
+    virtual void UpdatePluginTimeout() MOZ_OVERRIDE;
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+    void InitPluginProfiling();
+    void ShutdownPluginProfiling();
+#endif
+
+    virtual bool RecvNotifyContentModuleDestroyed() MOZ_OVERRIDE;
+
+    PluginProcessParent* mSubprocess;
+    uint32_t mPluginId;
+
+    ScopedMethodFactory<PluginModuleChromeParent> mChromeTaskFactory;
+
     enum HangAnnotationFlags
     {
         kInPluginCall = (1u << 0),
         kHangUIShown = (1u << 1),
         kHangUIContinued = (1u << 2),
         kHangUIDontShow = (1u << 3)
     };
     Atomic<uint32_t> mHangAnnotationFlags;
-    nsCString mPluginName;
-    nsCString mPluginVersion;
 #ifdef XP_WIN
     InfallibleTArray<float> mPluginCpuUsageOnHang;
     PluginHangUIParent *mHangUIParent;
     bool mHangUIEnabled;
     bool mIsTimerReset;
 #ifdef MOZ_CRASHREPORTER
     /**
      * This mutex protects the crash reporter when the Plugin Hang UI event
@@ -344,25 +407,16 @@ private:
 
     /**
      * Finishes the Plugin Hang UI and cancels if it is being shown to the user.
      */
     void
     FinishHangUI();
 #endif
 
-    bool
-    GetPluginDetails(nsACString& aPluginName, nsACString& aPluginVersion);
-
-#ifdef MOZ_X11
-    // Dup of plugin's X socket, used to scope its resources to this
-    // object instead of the plugin process's lifetime
-    ScopedClose mPluginXSocketFdDup;
-#endif
-
     friend class mozilla::dom::CrashReporterParent;
 
 #ifdef MOZ_CRASHREPORTER_INJECTOR
     void InitializeInjector();
     
     void OnCrash(DWORD processID) MOZ_OVERRIDE;
 
     DWORD mFlashProcess1;
--- a/dom/plugins/ipc/PluginProcessChild.cpp
+++ b/dom/plugins/ipc/PluginProcessChild.cpp
@@ -114,19 +114,19 @@ PluginProcessChild::Init()
 #  error Sorry
 #endif
 
     if (NS_FAILED(nsRegion::InitStatic())) {
       NS_ERROR("Could not initialize nsRegion");
       return false;
     }
 
-    return mPlugin.Init(pluginFilename, ParentHandle(),
-                        IOThreadChild::message_loop(),
-                        IOThreadChild::channel());
+    return mPlugin.InitForChrome(pluginFilename, ParentHandle(),
+                                 IOThreadChild::message_loop(),
+                                 IOThreadChild::channel());
 }
 
 void
 PluginProcessChild::CleanUp()
 {
 #ifdef XP_WIN
     ::OleUninitialize();
 #endif
--- a/dom/plugins/ipc/PluginProcessChild.h
+++ b/dom/plugins/ipc/PluginProcessChild.h
@@ -14,17 +14,18 @@ namespace mozilla {
 namespace plugins {
 //-----------------------------------------------------------------------------
 
 class PluginProcessChild : public mozilla::ipc::ProcessChild {
 protected:
     typedef mozilla::ipc::ProcessChild ProcessChild;
 
 public:
-    explicit PluginProcessChild(ProcessHandle aParentHandle) : ProcessChild(aParentHandle)
+    explicit PluginProcessChild(ProcessHandle aParentHandle)
+      : ProcessChild(aParentHandle), mPlugin(true)
     { }
 
     virtual ~PluginProcessChild()
     { }
 
     virtual bool Init() MOZ_OVERRIDE;
     virtual void CleanUp() MOZ_OVERRIDE;
 
--- a/dom/plugins/ipc/PluginScriptableObjectUtils-inl.h
+++ b/dom/plugins/ipc/PluginScriptableObjectUtils-inl.h
@@ -85,17 +85,17 @@ mozilla::plugins::ConvertToVariant(const
 
       npn->retainobject(object);
       OBJECT_TO_NPVARIANT(object, aVariant);
       break;
     }
 
     case Variant::TPPluginScriptableObjectChild: {
       NS_ASSERTION(!aInstance, "No instance should be given!");
-      NS_ASSERTION(PluginModuleChild::current(),
+      NS_ASSERTION(XRE_GetProcessType() == GeckoProcessType_Plugin,
                    "Should be running on child only!");
 
       NPObject* object = NPObjectFromVariant(aRemoteVariant);
       NS_ASSERTION(object, "Null object?!");
 
       PluginModuleChild::sBrowserFuncs.retainobject(object);
       OBJECT_TO_NPVARIANT(object, aVariant);
       break;
--- a/dom/plugins/ipc/PluginScriptableObjectUtils.h
+++ b/dom/plugins/ipc/PluginScriptableObjectUtils.h
@@ -107,17 +107,17 @@ ReleaseRemoteVariant(Variant& aVariant)
         const_cast<PluginScriptableObjectParent*>(
           reinterpret_cast<const PluginScriptableObjectParent*>(
             aVariant.get_PPluginScriptableObjectParent()));
       actor->Unprotect();
       break;
     }
 
     case Variant::TPPluginScriptableObjectChild: {
-      NS_ASSERTION(PluginModuleChild::current(),
+      NS_ASSERTION(XRE_GetProcessType() == GeckoProcessType_Plugin,
                    "Should only be running in the child!");
       PluginScriptableObjectChild* actor =
         const_cast<PluginScriptableObjectChild*>(
           reinterpret_cast<const PluginScriptableObjectChild*>(
             aVariant.get_PPluginScriptableObjectChild()));
       actor->Unprotect();
       break;
     }
--- a/dom/plugins/ipc/PluginTypes.ipdlh
+++ b/dom/plugins/ipc/PluginTypes.ipdlh
@@ -1,16 +1,32 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
 /* 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/. */
 
 namespace mozilla {
 namespace plugins {
 
+struct PluginTag
+{
+  uint32_t id;
+  nsCString name;
+  nsCString description;
+  nsCString[] mimeTypes;
+  nsCString[] mimeDescriptions;
+  nsCString[] extensions;
+  bool isJavaPlugin;
+  bool isFlashPlugin;
+  nsCString filename;
+  nsCString version;
+  int64_t lastModifiedTime;
+  bool isFromExtension;
+};
+
 union PluginIdentifier
 {
   nsCString;
   int32_t;
 };
 
 } // namespace plugins
 } // namespace mozilla
--- a/dom/plugins/ipc/moz.build
+++ b/dom/plugins/ipc/moz.build
@@ -16,16 +16,17 @@ EXPORTS.mozilla.plugins += [
     'BrowserStreamChild.h',
     'BrowserStreamParent.h',
     'ChildAsyncCall.h',
     'ChildTimer.h',
     'NPEventAndroid.h',
     'NPEventOSX.h',
     'NPEventUnix.h',
     'NPEventWindows.h',
+    'PluginBridge.h',
     'PluginInstanceChild.h',
     'PluginInstanceParent.h',
     'PluginMessageUtils.h',
     'PluginModuleChild.h',
     'PluginModuleParent.h',
     'PluginProcessChild.h',
     'PluginProcessParent.h',
     'PluginScriptableObjectChild.h',
--- a/dom/plugins/test/mochitest/mochitest.ini
+++ b/dom/plugins/test/mochitest/mochitest.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') || e10s #b2g-desktop(tests that use plugins)
+skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') || (e10s && debug) #b2g-desktop(tests that use plugins)
 support-files =
   307-xo-redirect.sjs
   crashing_subpage.html
   file_bug738396.html
   file_bug771202.html
   file_bug863792.html
   large-pic.jpg
   loremipsum.txt
@@ -58,35 +58,35 @@ skip-if = (!crashreporter) || true # Bug
 [test_CrashService_crash.html]
 skip-if = !crashreporter || e10s
 [test_CrashService_hang.html]
 skip-if = !crashreporter || e10s
 [test_defaultValue.html]
 [test_enumerate.html]
 [test_fullpage.html]
 [test_getauthenticationinfo.html]
+skip-if = e10s
 [test_hanging.html]
-skip-if = !crashreporter
+skip-if = !crashreporter || e10s
 [test_instance_re-parent.html]
 [test_instance_unparent1.html]
 [test_instance_unparent2.html]
 [test_instance_unparent3.html]
 [test_instantiation.html]
 [test_mixed_case_mime.html]
 [test_multipleinstanceobjects.html]
 [test_newstreamondestroy.html]
 [test_npn_asynccall.html]
 [test_npn_timers.html]
 [test_npobject_getters.html]
 [test_npruntime_construct.html]
 [test_npruntime_identifiers.html]
 [test_npruntime_npnevaluate.html]
 [test_npruntime_npninvoke.html]
 [test_npruntime_npninvokedefault.html]
-[test_npruntime_npnsetexception.html]
 [test_painting.html]
 [test_plugin_scroll_painting.html]
 skip-if = true # Bug 596491
 [test_pluginstream_asfile.html]
 [test_pluginstream_asfileonly.html]
 [test_pluginstream_err.html]
 [test_pluginstream_geturl.html]
 [test_pluginstream_geturlnotify.html]
@@ -103,15 +103,16 @@ skip-if = true # Bug 596491
 skip-if = true # disabled due to oddness, perhaps scrolling of the mochitest window?
 [test_propertyAndMethod.html]
 [test_queryContentsScaleFactor.html]
 skip-if = toolkit != "cocoa"
 [test_redirect_handling.html]
 [test_secondPlugin.html]
 [test_src_url_change.html]
 [test_streamNotify.html]
+skip-if = e10s
 [test_streamatclose.html]
 [test_twostreams.html]
 [test_windowed_invalidate.html]
 skip-if = os != "win"
 [test_visibility.html]
 skip-if = toolkit == "cocoa"
 [test_zero_opacity.html]
--- a/dom/plugins/test/mochitest/test_instance_re-parent.html
+++ b/dom/plugins/test/mochitest/test_instance_re-parent.html
@@ -5,17 +5,17 @@
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="utils.js"></script>
 </head>
 <body onload="begin()">
   <script type="application/javascript;version=1.8">
   SimpleTest.waitForExplicitFinish();
-  getTestPlugin().enabledState = SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED;
+  setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
 
   var exceptionThrown = false;
   var p = null;
   var d1 = null;
   var d2 = null;
 
   var destroyed = false;
 
deleted file mode 100644
--- a/dom/plugins/test/mochitest/test_npruntime_npnsetexception.html
+++ /dev/null
@@ -1,59 +0,0 @@
-<html>
-<head>
-  <title>NPN_SetException Tests</title>
-  <script type="text/javascript" 
-          src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="utils.js"></script>
-  <link rel="stylesheet" type="text/css" 
-        href="/tests/SimpleTest/test.css" />
-</head>
-<body onload="runTests()">
-  <script class="testbody" type="application/javascript">
-  SimpleTest.waitForExplicitFinish();
-  setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
-
-  function runTests() {
-    // test a single exception thrown in scriptable invoke
-    var plugin = document.getElementById("plugin1");
-    plugin.throwExceptionNextInvoke();
-    try {
-      plugin.npnInvokeTest("badFunction");
-      ok(false, "exception not thrown");
-    }
-    catch (e) {
-      is(e, "badFunction", "wrong exception thrown");
-    }
-    
-    // test multiple exceptions thrown in scriptable invokedefault
-    plugin.throwExceptionNextInvoke();
-    try {
-      plugin("first exception", "second exception");
-      ok(false, "exception not thrown");
-    }
-    catch (e) {
-      is(e, "second exception", "wrong exception thrown");
-    }    
-
-    // test calling exception with NULL message
-    plugin.throwExceptionNextInvoke();
-    try {
-      plugin();
-      ok(false, "exception not thrown");
-    }
-    catch (e) {
-      is(e.message, "Error calling method on NPObject!", "wrong exception thrown");
-    }    
-
-    SimpleTest.finish();
-  }
-  </script>
-
-  <p id="display"></p>
-
-  <embed id="plugin1" type="application/x-test" width="400" height="100">
-  </embed>
-
-  <div id="verbose">
-  </div>
- </body>
- </html>
--- a/dom/plugins/test/mochitest/test_secondPlugin.html
+++ b/dom/plugins/test/mochitest/test_secondPlugin.html
@@ -38,35 +38,36 @@
       function run() {
         // Add "Test Plug-in" (but not "Second Test Plug-in") to the list of
         // unhidden plugins. This test must modify the "plugins.enumerable_names"
         // pref BEFORE accessing the navigator.plugins or navigator.mimeTypes
         // arrays because they only read the pref when they first initialize
         // their internal arrays!
         var prefs = SpecialPowers.Cc["@mozilla.org/preferences-service;1"].getService(SpecialPowers.Ci.nsIPrefBranch);
         var defaultEnumerableNamesPref = prefs.getCharPref("plugins.enumerable_names");
-        prefs.setCharPref("plugins.enumerable_names", defaultEnumerableNamesPref + ",Test Plug-in");
-
+        SpecialPowers.pushPrefEnv(
+          {'set': [["plugins.enumerable_names", defaultEnumerableNamesPref + ",Test Plug-in"]]},
+          finishRun
+        );
+      }
+      function finishRun() {
         var pluginElement = document.getElementById("plugin");
         is(pluginElement.identifierToStringTest("foo"), "foo", "Should be able to call a function provided by the plugin");
 
         ok(navigator.plugins["Test Plug-in"], "Should have queried a non-hidden plugin named 'Test Plug-in'");
         ok(navigator.plugins["Second Test Plug-in"], "Should have queried a hidden plugin named 'Test Plug-in'");
 
         ok(findPlugin("Test Plug-in"), "Should have found a non-hidden plugin named 'Test Plug-in'");
         ok(!findPlugin("Second Test Plug-in"), "Should NOT found a hidden plugin named 'Test Plug-in'");
 
         ok(navigator.mimeTypes["application/x-test"], "Should have queried a non-hidden MIME type named 'application/x-test'");
         ok(navigator.mimeTypes["application/x-second-test"], "Should have queried a MIME type named 'application/x-second-test'");
 
         ok(findMimeType("application/x-test"), "Should have found a non-hidden MIME type named 'application/x-test'");
         ok(!findMimeType("application/x-second-test"), "Should NOT have found a MIME type named 'application/x-second-test'");
 
-        // Restore original pref to hide "Test Plug-in" and "Second Test Plug-in".
-        prefs.setCharPref("plugins.enumerable_names", defaultEnumerableNamesPref);
-
         SimpleTest.finish();
       }
     </script>
 
     <object id="plugin" type="application/x-second-test" width=200 height=200></object>
   </body>
 </html>