Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 02 Apr 2015 16:20:54 -0400
changeset 237311 513265a4cbc2dc26d388e9408bde6c2d1b0bb70a
parent 237268 72c550e2358cf2dbe8aced017ae98633b2902e28 (current diff)
parent 237310 ada788536540872fb50ab05712c4c6c06a98fd7f (diff)
child 237392 aa5259817e5a41b60cb4496f71d4ec659cf4a3d6
child 237444 05c0b5cfe341f4ca5cf13b7e0a3612ae62234038
child 237452 58a70cf9dbfdb9e25e1fb809cfc6186b0e15c97e
push id28531
push userryanvm@gmail.com
push dateThu, 02 Apr 2015 20:21:16 +0000
treeherdermozilla-central@513265a4cbc2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone40.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
Merge inbound to m-c. a=merge
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1172,16 +1172,18 @@ pref("browser.flash-protected-mode-flip.
 #ifdef XP_MACOSX
 // On mac, the default pref is per-architecture
 pref("dom.ipc.plugins.enabled.i386", true);
 pref("dom.ipc.plugins.enabled.x86_64", true);
 #else
 pref("dom.ipc.plugins.enabled", true);
 #endif
 
+pref("dom.ipc.shims.enabledWarnings", false);
+
 // Start the browser in e10s mode
 pref("browser.tabs.remote.autostart", false);
 pref("browser.tabs.remote.desktopbehavior", true);
 
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
 // When this pref is true the Windows process sandbox will set up dummy
 // interceptions and log to the browser console when calls fail in the sandboxed
 // process and also if they are subsequently allowed by the broker process.
--- a/docshell/shistory/src/nsSHistory.cpp
+++ b/docshell/shistory/src/nsSHistory.cpp
@@ -1796,23 +1796,20 @@ nsSHistory::SetRootDocShell(nsIDocShell 
 {
   mRootDocShell = aDocShell;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSHistory::GetSHistoryEnumerator(nsISimpleEnumerator** aEnumerator)
 {
-  nsresult status = NS_OK;
-
   NS_ENSURE_ARG_POINTER(aEnumerator);
-  nsSHEnumerator * iterator = new nsSHEnumerator(this);
-  if (iterator && NS_FAILED(status = CallQueryInterface(iterator, aEnumerator)))
-    delete iterator;
-  return status;
+  nsRefPtr<nsSHEnumerator> iterator = new nsSHEnumerator(this);
+  iterator.forget(aEnumerator);
+  return NS_OK;
 }
 
 
 //*****************************************************************************
 //***    nsSHEnumerator: Object Management
 //*****************************************************************************
 
 nsSHEnumerator::nsSHEnumerator(nsSHistory * aSHistory):mIndex(-1)
--- a/dom/base/MultipartFileImpl.cpp
+++ b/dom/base/MultipartFileImpl.cpp
@@ -41,17 +41,18 @@ MultipartFileImpl::GetInternalStream(nsI
 
     rv = blobImpl->GetInternalStream(getter_AddRefs(scratchStream));
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = stream->AppendStream(scratchStream);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  return CallQueryInterface(stream, aStream);
+  stream.forget(aStream);
+  return NS_OK;
 }
 
 already_AddRefed<FileImpl>
 MultipartFileImpl::CreateSlice(uint64_t aStart, uint64_t aLength,
                                const nsAString& aContentType,
                                ErrorResult& aRv)
 {
   // If we clamped to nothing we create an empty blob
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -2494,17 +2494,19 @@ nsFrameLoader::CheckAppHasPermission(con
                                 NS_ConvertUTF16toUTF8(aPermission).get());
 }
 
 NS_IMETHODIMP
 nsFrameLoader::GetMessageManager(nsIMessageSender** aManager)
 {
   EnsureMessageManager();
   if (mMessageManager) {
-    CallQueryInterface(mMessageManager, aManager);
+    nsRefPtr<nsFrameMessageManager> mm(mMessageManager);
+    mm.forget(aManager);
+    return NS_OK;
   }
   return NS_OK;
 }
 
 nsresult
 nsFrameLoader::EnsureMessageManager()
 {
   NS_ENSURE_STATE(mOwnerContent);
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -1485,21 +1485,22 @@ MessageManagerReporter::CollectReports(n
 } // namespace dom
 } // namespace mozilla
 
 nsresult
 NS_NewGlobalMessageManager(nsIMessageBroadcaster** aResult)
 {
   NS_ENSURE_TRUE(XRE_GetProcessType() == GeckoProcessType_Default,
                  NS_ERROR_NOT_AVAILABLE);
-  nsFrameMessageManager* mm = new nsFrameMessageManager(nullptr,
-                                                        nullptr,
-                                                        MM_CHROME | MM_GLOBAL | MM_BROADCASTER);
+  nsRefPtr<nsFrameMessageManager> mm = new nsFrameMessageManager(nullptr,
+                                                                 nullptr,
+                                                                 MM_CHROME | MM_GLOBAL | MM_BROADCASTER);
   RegisterStrongMemoryReporter(new MessageManagerReporter());
-  return CallQueryInterface(mm, aResult);
+  mm.forget(aResult);
+  return NS_OK;
 }
 
 nsDataHashtable<nsStringHashKey, nsMessageManagerScriptHolder*>*
   nsMessageManagerScriptExecutor::sCachedScripts = nullptr;
 nsScriptCacheCleaner* nsMessageManagerScriptExecutor::sScriptCacheCleaner = nullptr;
 
 void
 nsMessageManagerScriptExecutor::DidCreateGlobal()
@@ -2003,17 +2004,18 @@ NS_NewParentProcessMessageManager(nsIMes
 {
   NS_ASSERTION(!nsFrameMessageManager::sParentProcessManager,
                "Re-creating sParentProcessManager");
   nsRefPtr<nsFrameMessageManager> mm = new nsFrameMessageManager(nullptr,
                                                                  nullptr,
                                                                  MM_CHROME | MM_PROCESSMANAGER | MM_BROADCASTER);
   nsFrameMessageManager::sParentProcessManager = mm;
   nsFrameMessageManager::NewProcessMessageManager(false); // Create same process message manager.
-  return CallQueryInterface(mm, aResult);
+  mm.forget(aResult);
+  return NS_OK;
 }
 
 
 nsFrameMessageManager*
 nsFrameMessageManager::NewProcessMessageManager(bool aIsRemote)
 {
   if (!nsFrameMessageManager::sParentProcessManager) {
      nsCOMPtr<nsIMessageBroadcaster> dummy =
@@ -2050,19 +2052,20 @@ NS_NewChildProcessMessageManager(nsISync
   } else {
     cb = new ChildProcessMessageManagerCallback();
     RegisterStrongMemoryReporter(new MessageManagerReporter());
   }
   nsFrameMessageManager* mm = new nsFrameMessageManager(cb,
                                                         nullptr,
                                                         MM_PROCESSMANAGER | MM_OWNSCALLBACK);
   nsFrameMessageManager::SetChildProcessManager(mm);
-  ProcessGlobal* global = new ProcessGlobal(mm);
+  nsRefPtr<ProcessGlobal> global = new ProcessGlobal(mm);
   NS_ENSURE_TRUE(global->Init(), NS_ERROR_UNEXPECTED);
-  return CallQueryInterface(global, aResult);
+  global.forget(aResult);
+  return NS_OK;
 
 }
 
 static PLDHashOperator
 CycleCollectorMarkListeners(const nsAString& aKey,
                             nsAutoTObserverArray<nsMessageListenerInfo, 1>* aListeners,
                             void* aData)
 {
--- a/dom/base/nsHTMLContentSerializer.cpp
+++ b/dom/base/nsHTMLContentSerializer.cpp
@@ -35,24 +35,22 @@
 #include "nsIDocShell.h"
 #include "nsIEditor.h"
 #include "nsIHTMLEditor.h"
 #include "mozilla/dom/Element.h"
 #include "nsParserConstants.h"
 
 using namespace mozilla::dom;
 
-nsresult NS_NewHTMLContentSerializer(nsIContentSerializer** aSerializer)
+nsresult
+NS_NewHTMLContentSerializer(nsIContentSerializer** aSerializer)
 {
-  nsHTMLContentSerializer* it = new nsHTMLContentSerializer();
-  if (!it) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  return CallQueryInterface(it, aSerializer);
+  nsRefPtr<nsHTMLContentSerializer> it = new nsHTMLContentSerializer();
+  it.forget(aSerializer);
+  return NS_OK;
 }
 
 nsHTMLContentSerializer::nsHTMLContentSerializer()
 {
     mIsHTMLSerializer = true;
 }
 
 nsHTMLContentSerializer::~nsHTMLContentSerializer()
--- a/dom/base/nsPlainTextSerializer.cpp
+++ b/dom/base/nsPlainTextSerializer.cpp
@@ -49,24 +49,22 @@ static const  char16_t kSPACE = ' ';
 static int32_t HeaderLevel(nsIAtom* aTag);
 static int32_t GetUnicharWidth(char16_t ucs);
 static int32_t GetUnicharStringWidth(const char16_t* pwcs, int32_t n);
 
 // Someday may want to make this non-const:
 static const uint32_t TagStackSize = 500;
 static const uint32_t OLStackSize = 100;
 
-nsresult NS_NewPlainTextSerializer(nsIContentSerializer** aSerializer)
+nsresult
+NS_NewPlainTextSerializer(nsIContentSerializer** aSerializer)
 {
-  nsPlainTextSerializer* it = new nsPlainTextSerializer();
-  if (!it) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  return CallQueryInterface(it, aSerializer);
+  nsRefPtr<nsPlainTextSerializer> it = new nsPlainTextSerializer();
+  it.forget(aSerializer);
+  return NS_OK;
 }
 
 nsPlainTextSerializer::nsPlainTextSerializer()
   : kSpace(NS_LITERAL_STRING(" ")) // Init of "constant"
 {
 
   mOutputString = nullptr;
   mHeadLevel = 0;
--- a/dom/base/nsXHTMLContentSerializer.cpp
+++ b/dom/base/nsXHTMLContentSerializer.cpp
@@ -35,24 +35,22 @@
 #include "nsParserConstants.h"
 #include "nsComputedDOMStyle.h"
 #include "mozilla/dom/Element.h"
 
 static const int32_t kLongLineLen = 128;
 
 #define kXMLNS "xmlns"
 
-nsresult NS_NewXHTMLContentSerializer(nsIContentSerializer** aSerializer)
+nsresult
+NS_NewXHTMLContentSerializer(nsIContentSerializer** aSerializer)
 {
-  nsXHTMLContentSerializer* it = new nsXHTMLContentSerializer();
-  if (!it) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  return CallQueryInterface(it, aSerializer);
+  nsRefPtr<nsXHTMLContentSerializer> it = new nsXHTMLContentSerializer();
+  it.forget(aSerializer);
+  return NS_OK;
 }
 
 nsXHTMLContentSerializer::nsXHTMLContentSerializer()
   : mIsHTMLSerializer(false)
 {
 }
 
 nsXHTMLContentSerializer::~nsXHTMLContentSerializer()
--- a/dom/base/nsXMLContentSerializer.cpp
+++ b/dom/base/nsXMLContentSerializer.cpp
@@ -38,24 +38,22 @@ using namespace mozilla::dom;
 // at least this number of characters (arbitrary value here).
 // This is a limit for the indentation.
 #define MIN_INDENTED_LINE_LENGTH 15
 
 // the string used to indent.
 #define INDENT_STRING "  "
 #define INDENT_STRING_LENGTH 2
 
-nsresult NS_NewXMLContentSerializer(nsIContentSerializer** aSerializer)
+nsresult
+NS_NewXMLContentSerializer(nsIContentSerializer** aSerializer)
 {
-  nsXMLContentSerializer* it = new nsXMLContentSerializer();
-  if (!it) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  return CallQueryInterface(it, aSerializer);
+  nsRefPtr<nsXMLContentSerializer> it = new nsXMLContentSerializer();
+  it.forget(aSerializer);
+  return NS_OK;
 }
 
 nsXMLContentSerializer::nsXMLContentSerializer()
   : mPrefixIndex(0),
     mColPos(0),
     mIndentOverflow(0),
     mIsIndentationAddedOnCurrentLine(false),
     mInAttribute(false),
--- a/dom/bindings/test/mochitest.ini
+++ b/dom/bindings/test/mochitest.ini
@@ -18,16 +18,17 @@ support-files =
 [test_bug788369.html]
 [test_bug852846.html]
 [test_bug862092.html]
 [test_bug1036214.html]
 skip-if = debug == false
 [test_bug963382.html]
 skip-if = debug == false
 [test_bug1041646.html]
+[test_bug1123875.html]
 [test_barewordGetsWindow.html]
 [test_callback_default_thisval.html]
 [test_cloneAndImportNode.html]
 [test_defineProperty.html]
 [test_enums.html]
 [test_exceptionThrowing.html]
 [test_exception_messages.html]
 [test_forOf.html]
new file mode 100644
--- /dev/null
+++ b/dom/bindings/test/test_bug1123875.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test for Bug 1123875</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+  test(() => {
+    assert_throws(new TypeError, () => {
+      "use strict";
+      document.childNodes.length = 0;
+    });
+  }, "setting a readonly attribute on a proxy in strict mode should throw a TypeError");
+</script>
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -1905,17 +1905,17 @@ HTMLInputElement::GetList(nsIDOMHTMLElem
 {
   *aValue = nullptr;
 
   nsRefPtr<nsGenericHTMLElement> element = GetList();
   if (!element) {
     return NS_OK;
   }
 
-  CallQueryInterface(element, aValue);
+  element.forget(aValue);
   return NS_OK;
 }
 
 void
 HTMLInputElement::SetValue(Decimal aValue)
 {
   MOZ_ASSERT(!aValue.isInfinity(), "aValue must not be Infinity!");
 
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -173,29 +173,31 @@
 #include "AudioChannelService.h"
 #include "DomainPolicy.h"
 #include "mozilla/dom/DataStoreService.h"
 #include "mozilla/dom/telephony/PTelephonyChild.h"
 #include "mozilla/dom/time/DateCacheCleaner.h"
 #include "mozilla/dom/voicemail/VoicemailIPCService.h"
 #include "mozilla/net/NeckoMessageUtils.h"
 #include "mozilla/RemoteSpellCheckEngineChild.h"
+#include "GMPServiceChild.h"
 
 using namespace mozilla;
 using namespace mozilla::docshell;
 using namespace mozilla::dom::bluetooth;
 using namespace mozilla::dom::cellbroadcast;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::icc;
 using namespace mozilla::dom::ipc;
 using namespace mozilla::dom::mobileconnection;
 using namespace mozilla::dom::mobilemessage;
 using namespace mozilla::dom::telephony;
 using namespace mozilla::dom::voicemail;
 using namespace mozilla::embedding;
+using namespace mozilla::gmp;
 using namespace mozilla::hal_sandbox;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::net;
 using namespace mozilla::jsipc;
 #if defined(MOZ_WIDGET_GONK)
 using namespace mozilla::system;
 #endif
@@ -1031,16 +1033,23 @@ ContentChild::AllocPContentBridgeParent(
                                         base::ProcessId aOtherProcess)
 {
     MOZ_ASSERT(!mLastBridge);
     mLastBridge = static_cast<ContentBridgeParent*>(
         ContentBridgeParent::Create(aTransport, aOtherProcess));
     return mLastBridge;
 }
 
+PGMPServiceChild*
+ContentChild::AllocPGMPServiceChild(mozilla::ipc::Transport* aTransport,
+                                    base::ProcessId aOtherProcess)
+{
+    return GMPServiceChild::Create(aTransport, aOtherProcess);
+}
+
 PCompositorChild*
 ContentChild::AllocPCompositorChild(mozilla::ipc::Transport* aTransport,
                                     base::ProcessId aOtherProcess)
 {
     return CompositorChild::Create(aTransport, aOtherProcess);
 }
 
 PSharedBufferManagerChild*
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -110,16 +110,20 @@ public:
 
     PContentBridgeParent*
     AllocPContentBridgeParent(mozilla::ipc::Transport* transport,
                               base::ProcessId otherProcess) override;
     PContentBridgeChild*
     AllocPContentBridgeChild(mozilla::ipc::Transport* transport,
                              base::ProcessId otherProcess) override;
 
+    PGMPServiceChild*
+    AllocPGMPServiceChild(mozilla::ipc::Transport* transport,
+                          base::ProcessId otherProcess) override;
+
     PCompositorChild*
     AllocPCompositorChild(mozilla::ipc::Transport* aTransport,
                           base::ProcessId aOtherProcess) override;
 
     PSharedBufferManagerChild*
     AllocPSharedBufferManagerChild(mozilla::ipc::Transport* aTransport,
                                     base::ProcessId aOtherProcess) override;
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -23,16 +23,17 @@
 #include "chrome/common/process_watcher.h"
 
 #include <set>
 
 #include "AppProcessChecker.h"
 #include "AudioChannelService.h"
 #include "BlobParent.h"
 #include "CrashReporterParent.h"
+#include "GMPServiceParent.h"
 #include "IHistory.h"
 #include "mozIApplication.h"
 #ifdef ACCESSIBILITY
 #include "mozilla/a11y/DocAccessibleParent.h"
 #include "nsAccessibilityService.h"
 #endif
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/docshell/OfflineCacheUpdateParent.h"
@@ -228,16 +229,17 @@ using namespace mozilla::dom::devicestor
 using namespace mozilla::dom::icc;
 using namespace mozilla::dom::indexedDB;
 using namespace mozilla::dom::power;
 using namespace mozilla::dom::mobileconnection;
 using namespace mozilla::dom::mobilemessage;
 using namespace mozilla::dom::telephony;
 using namespace mozilla::dom::voicemail;
 using namespace mozilla::embedding;
+using namespace mozilla::gmp;
 using namespace mozilla::hal;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::net;
 using namespace mozilla::jsipc;
 using namespace mozilla::widget;
 
 #ifdef ENABLE_TESTS
@@ -977,16 +979,33 @@ static nsIDocShell* GetOpenerDocShellHel
         NS_WARNING("Remote frame has no docshell");
         return nullptr;
     }
 
     return docShell;
 }
 
 bool
+ContentParent::RecvCreateGMPService()
+{
+    return PGMPService::Open(this);
+}
+
+bool
+ContentParent::RecvGetGMPPluginVersionForAPI(const nsCString& aAPI,
+                                             nsTArray<nsCString>&& aTags,
+                                             bool* aHasVersion,
+                                             nsCString* aVersion)
+{
+    return GMPServiceParent::RecvGetGMPPluginVersionForAPI(aAPI, Move(aTags),
+                                                           aHasVersion,
+                                                           aVersion);
+}
+
+bool
 ContentParent::RecvLoadPlugin(const uint32_t& aPluginId, nsresult* aRv)
 {
     *aRv = NS_OK;
     return mozilla::plugins::SetupBridge(aPluginId, this, false, aRv);
 }
 
 bool
 ContentParent::RecvConnectPluginBridge(const uint32_t& aPluginId, nsresult* aRv)
@@ -3057,16 +3076,23 @@ ContentParent::RecvPDocAccessibleConstru
   } else {
     MOZ_ASSERT(!aParentID);
     a11y::DocManager::RemoteDocAdded(doc);
   }
 #endif
   return true;
 }
 
+PGMPServiceParent*
+ContentParent::AllocPGMPServiceParent(mozilla::ipc::Transport* aTransport,
+                                      base::ProcessId aOtherProcess)
+{
+    return GMPServiceParent::Create(aTransport, aOtherProcess);
+}
+
 PCompositorParent*
 ContentParent::AllocPCompositorParent(mozilla::ipc::Transport* aTransport,
                                       base::ProcessId aOtherProcess)
 {
     return CompositorParent::Create(aTransport, aOtherProcess);
 }
 
 PImageBridgeParent*
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -148,16 +148,22 @@ public:
                                         const hal::ProcessPriority& aPriority,
                                         const TabId& aOpenerTabId,
                                         ContentParentId* aCpId,
                                         bool* aIsForApp,
                                         bool* aIsForBrowser,
                                         TabId* aTabId) override;
     virtual bool RecvBridgeToChildProcess(const ContentParentId& aCpId) override;
 
+    virtual bool RecvCreateGMPService() override;
+    virtual bool RecvGetGMPPluginVersionForAPI(const nsCString& aAPI,
+                                               nsTArray<nsCString>&& aTags,
+                                               bool* aHasPlugin,
+                                               nsCString* aVersion) override;
+
     virtual bool RecvLoadPlugin(const uint32_t& aPluginId, nsresult* aRv) override;
     virtual bool RecvConnectPluginBridge(const uint32_t& aPluginId, nsresult* aRv) override;
     virtual bool RecvFindPlugins(const uint32_t& aPluginEpoch,
                                  nsTArray<PluginTag>* aPlugins,
                                  uint32_t* aNewPluginEpoch) override;
 
     NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ContentParent, nsIObserver)
 
@@ -483,16 +489,19 @@ private:
     // manager and null out mMessageManager.
     void ShutDownMessageManager();
 
     // Start the force-kill timer on shutdown.
     void StartForceKillTimer();
 
     static void ForceKillTimerCallback(nsITimer* aTimer, void* aClosure);
 
+    PGMPServiceParent*
+    AllocPGMPServiceParent(mozilla::ipc::Transport* aTransport,
+                           base::ProcessId aOtherProcess) override;
     PCompositorParent*
     AllocPCompositorParent(mozilla::ipc::Transport* aTransport,
                            base::ProcessId aOtherProcess) override;
     PImageBridgeParent*
     AllocPImageBridgeParent(mozilla::ipc::Transport* aTransport,
                             base::ProcessId aOtherProcess) override;
 
     PSharedBufferManagerParent*
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -22,17 +22,25 @@ include protocol PFMRadio;
 include protocol PFileSystemRequest;
 include protocol PHal;
 include protocol PIcc;
 include protocol PProcessHangMonitor;
 include protocol PImageBridge;
 include protocol PMemoryReportRequest;
 include protocol PMobileConnection;
 include protocol PNecko;
+// FIXME This is pretty ridiculous, but we have to keep the order of the
+//       following 4 includes, or the parser is confused about PGMPContent
+//       bridging PContent and PGMP. As soon as it registers the bridge between
+//       PContent and PPluginModule it seems to think that PContent's parent and
+//       child live in the same process!
+include protocol PGMPContent;
+include protocol PGMPService;
 include protocol PPluginModule;
+include protocol PGMP;
 include protocol PPrinting;
 include protocol POfflineCacheUpdate;
 include protocol PScreenManager;
 include protocol PSharedBufferManager;
 include protocol PSms;
 include protocol PSpeechSynthesis;
 include protocol PStorage;
 include protocol PTelephony;
@@ -359,16 +367,17 @@ struct DomainPolicyClone
 prio(normal upto urgent) sync protocol PContent
 {
     parent spawns PPluginModule;
 
     parent opens PCompositor;
     parent opens PProcessHangMonitor;
     parent opens PSharedBufferManager;
     parent opens PImageBridge;
+    parent opens PGMPService;
     child opens PBackground;
 
     manages PAsmJSCacheEntry;
     manages PBlob;
     manages PBluetooth;
     manages PBrowser;
     manages PCellBroadcast;
     manages PCrashReporter;
@@ -603,16 +612,20 @@ parent:
                  DomainPolicyClone domainPolicy);
 
     sync CreateChildProcess(IPCTabContext context,
                             ProcessPriority priority,
                             TabId openerTabId)
         returns (ContentParentId cpId, bool isForApp, bool isForBrowser, TabId tabId);
     sync BridgeToChildProcess(ContentParentId cpId);
 
+    async CreateGMPService();
+    sync GetGMPPluginVersionForAPI(nsCString api, nsCString[] tags)
+        returns (bool hasPlugin, nsCString version);
+
     /**
      * 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.
      */
     sync LoadPlugin(uint32_t pluginId) returns (nsresult rv);
 
--- a/dom/media/eme/CDMProxy.cpp
+++ b/dom/media/eme/CDMProxy.cpp
@@ -20,16 +20,17 @@
 
 namespace mozilla {
 
 CDMProxy::CDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem)
   : mKeys(aKeys)
   , mKeySystem(aKeySystem)
   , mCDM(nullptr)
   , mDecryptionJobCount(0)
+  , mShutdownCalled(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_COUNT_CTOR(CDMProxy);
 }
 
 CDMProxy::~CDMProxy()
 {
   MOZ_COUNT_DTOR(CDMProxy);
@@ -65,69 +66,151 @@ CDMProxy::Init(PromiseId aPromiseId,
   nsAutoPtr<InitData> data(new InitData());
   data->mPromiseId = aPromiseId;
   data->mOrigin = aOrigin;
   data->mTopLevelOrigin = aTopLevelOrigin;
   data->mInPrivateBrowsing = aInPrivateBrowsing;
   nsCOMPtr<nsIRunnable> task(
     NS_NewRunnableMethodWithArg<nsAutoPtr<InitData>>(this,
                                                      &CDMProxy::gmp_Init,
-                                                     data));
+                                                     Move(data)));
   mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
 #ifdef DEBUG
 bool
 CDMProxy::IsOnGMPThread()
 {
   return NS_GetCurrentThread() == mGMPThread;
 }
 #endif
 
 void
-CDMProxy::gmp_Init(nsAutoPtr<InitData> aData)
+CDMProxy::gmp_InitDone(GMPDecryptorProxy* aCDM, nsAutoPtr<InitData>&& aData)
+{
+  EME_LOG("CDMProxy::gmp_InitDone");
+  if (!aCDM || mShutdownCalled) {
+    if (aCDM) {
+      aCDM->Close();
+    }
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  mCDM = aCDM;
+  mCallback = new CDMCallbackProxy(this);
+  mCDM->Init(mCallback);
+  nsCOMPtr<nsIRunnable> task(
+    NS_NewRunnableMethodWithArg<uint32_t>(this,
+                                          &CDMProxy::OnCDMCreated,
+                                          aData->mPromiseId));
+  NS_DispatchToMainThread(task);
+}
+
+class gmp_InitDoneCallback : public GetGMPDecryptorCallback
+{
+public:
+  gmp_InitDoneCallback(CDMProxy* aCDMProxy,
+                       nsAutoPtr<CDMProxy::InitData>&& aData)
+    : mCDMProxy(aCDMProxy),
+      mData(Move(aData))
+  {
+  }
+
+  void Done(GMPDecryptorProxy* aCDM)
+  {
+    mCDMProxy->gmp_InitDone(aCDM, Move(mData));
+  }
+
+private:
+  nsRefPtr<CDMProxy> mCDMProxy;
+  nsAutoPtr<CDMProxy::InitData> mData;
+};
+
+class gmp_InitGetGMPDecryptorCallback : public GetNodeIdCallback
+{
+public:
+  gmp_InitGetGMPDecryptorCallback(CDMProxy* aCDMProxy,
+                                  nsAutoPtr<CDMProxy::InitData>&& aData)
+    : mCDMProxy(aCDMProxy),
+      mData(aData)
+  {
+  }
+
+  void Done(nsresult aResult, const nsACString& aNodeId)
+  {
+    mCDMProxy->gmp_InitGetGMPDecryptor(aResult, aNodeId, Move(mData));
+  }
+
+private:
+  nsRefPtr<CDMProxy> mCDMProxy;
+  nsAutoPtr<CDMProxy::InitData> mData;
+};
+
+void
+CDMProxy::gmp_Init(nsAutoPtr<InitData>&& aData)
 {
   MOZ_ASSERT(IsOnGMPThread());
 
   nsCOMPtr<mozIGeckoMediaPluginService> mps =
     do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   if (!mps) {
     RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
-  nsresult rv = mps->GetNodeId(aData->mOrigin,
-                               aData->mTopLevelOrigin,
-                               aData->mInPrivateBrowsing,
-                               mNodeId);
+  // Make a copy before we transfer ownership of aData to the
+  // gmp_InitGetGMPDecryptorCallback.
+  InitData data(*aData);
+  UniquePtr<GetNodeIdCallback> callback(
+    new gmp_InitGetGMPDecryptorCallback(this, Move(aData)));
+  nsresult rv = mps->GetNodeId(data.mOrigin,
+                               data.mTopLevelOrigin,
+                               data.mInPrivateBrowsing,
+                               Move(callback));
+  if (NS_FAILED(rv)) {
+    RejectPromise(data.mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+  }
+}
+
+void
+CDMProxy::gmp_InitGetGMPDecryptor(nsresult aResult,
+                                  const nsACString& aNodeId,
+                                  nsAutoPtr<InitData>&& aData)
+{
+  uint32_t promiseID = aData->mPromiseId;
+  if (NS_FAILED(aResult)) {
+    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  mNodeId = aNodeId;
   MOZ_ASSERT(!GetNodeId().IsEmpty());
-  if (NS_FAILED(rv)) {
-    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+
+  nsCOMPtr<mozIGeckoMediaPluginService> mps =
+    do_GetService("@mozilla.org/gecko-media-plugin-service;1");
+  if (!mps) {
+    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   EME_LOG("CDMProxy::gmp_Init (%s, %s) %s NodeId=%s",
           NS_ConvertUTF16toUTF8(aData->mOrigin).get(),
           NS_ConvertUTF16toUTF8(aData->mTopLevelOrigin).get(),
           (aData->mInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"),
           GetNodeId().get());
 
   nsTArray<nsCString> tags;
   tags.AppendElement(NS_ConvertUTF16toUTF8(mKeySystem));
-  rv = mps->GetGMPDecryptor(&tags, GetNodeId(), &mCDM);
-  if (NS_FAILED(rv) || !mCDM) {
-    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
-  } else {
-    mCallback = new CDMCallbackProxy(this);
-    mCDM->Init(mCallback);
-    nsCOMPtr<nsIRunnable> task(
-      NS_NewRunnableMethodWithArg<uint32_t>(this,
-                                            &CDMProxy::OnCDMCreated,
-                                            aData->mPromiseId));
-    NS_DispatchToMainThread(task);
+
+  UniquePtr<GetGMPDecryptorCallback> callback(new gmp_InitDoneCallback(this,
+                                                                       Move(aData)));
+  nsresult rv = mps->GetGMPDecryptor(&tags, GetNodeId(), Move(callback));
+  if (NS_FAILED(rv)) {
+    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR);
   }
 }
 
 void
 CDMProxy::OnCDMCreated(uint32_t aPromiseId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
@@ -336,16 +419,18 @@ CDMProxy::Shutdown()
   }
 }
 
 void
 CDMProxy::gmp_Shutdown()
 {
   MOZ_ASSERT(IsOnGMPThread());
 
+  mShutdownCalled = true;
+
   // Abort any pending decrypt jobs, to awaken any clients waiting on a job.
   for (size_t i = 0; i < mDecryptionJobs.Length(); i++) {
     DecryptJob* job = mDecryptionJobs[i];
     job->mClient->Decrypted(GMPAbortedErr, nullptr);
   }
   mDecryptionJobs.Clear();
 
   if (mCDM) {
--- a/dom/media/eme/CDMProxy.h
+++ b/dom/media/eme/CDMProxy.h
@@ -169,26 +169,32 @@ public:
   void GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
                              nsTArray<nsCString>& aSessionIds);
 
 #ifdef DEBUG
   bool IsOnGMPThread();
 #endif
 
 private:
+  friend class gmp_InitDoneCallback;
+  friend class gmp_InitGetGMPDecryptorCallback;
 
   struct InitData {
     uint32_t mPromiseId;
     nsAutoString mOrigin;
     nsAutoString mTopLevelOrigin;
     bool mInPrivateBrowsing;
   };
 
   // GMP thread only.
-  void gmp_Init(nsAutoPtr<InitData> aData);
+  void gmp_Init(nsAutoPtr<InitData>&& aData);
+  void gmp_InitDone(GMPDecryptorProxy* aCDM, nsAutoPtr<InitData>&& aData);
+  void gmp_InitGetGMPDecryptor(nsresult aResult,
+                               const nsACString& aNodeId,
+                               nsAutoPtr<InitData>&& aData);
 
   // GMP thread only.
   void gmp_Shutdown();
 
   // Main thread only.
   void OnCDMCreated(uint32_t aPromiseId);
 
   struct CreateSessionData {
@@ -315,14 +321,18 @@ private:
   nsTArray<nsAutoPtr<DecryptJob>> mDecryptionJobs;
 
   // Number of buffers we've decrypted. Used to uniquely identify
   // decryption jobs sent to CDM. Note we can't just use the length of
   // mDecryptionJobs as that shrinks as jobs are completed and removed
   // from it.
   // GMP thread only.
   uint32_t mDecryptionJobCount;
+
+  // True if CDMProxy::gmp_Shutdown was called.
+  // GMP thread only.
+  bool mShutdownCalled;
 };
 
 
 } // namespace mozilla
 
 #endif // CDMProxy_h_
--- a/dom/media/eme/MediaKeySystemAccess.cpp
+++ b/dom/media/eme/MediaKeySystemAccess.cpp
@@ -88,35 +88,44 @@ HaveGMPFor(mozIGeckoMediaPluginService* 
     return false;
   }
   return hasPlugin;
 }
 
 static MediaKeySystemStatus
 EnsureMinCDMVersion(mozIGeckoMediaPluginService* aGMPService,
                     const nsAString& aKeySystem,
-                    int32_t aMinCdmVersion)
+                    int32_t aMinCdmVersion,
+                    bool aCheckForV6=false)
 {
-  if (aMinCdmVersion == NO_CDM_VERSION) {
-    return MediaKeySystemStatus::Available;
-  }
-
   nsTArray<nsCString> tags;
   tags.AppendElement(NS_ConvertUTF16toUTF8(aKeySystem));
+  bool hasPlugin;
   nsAutoCString versionStr;
   if (NS_FAILED(aGMPService->GetPluginVersionForAPI(NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
                                                     &tags,
-                                                    versionStr)) &&
+                                                    &hasPlugin,
+                                                    versionStr)) ||
       // XXX to be removed later in bug 1147692
-      NS_FAILED(aGMPService->GetPluginVersionForAPI(NS_LITERAL_CSTRING(GMP_API_DECRYPTOR_COMPAT),
-                                                    &tags,
-                                                    versionStr))) {
+      (aCheckForV6 && !hasPlugin &&
+       NS_FAILED(aGMPService->GetPluginVersionForAPI(NS_LITERAL_CSTRING(GMP_API_DECRYPTOR_COMPAT),
+                                                     &tags,
+                                                     &hasPlugin,
+                                                     versionStr)))) {
     return MediaKeySystemStatus::Error;
   }
 
+  if (!hasPlugin) {
+    return MediaKeySystemStatus::Cdm_not_installed;
+  }
+
+  if (aMinCdmVersion == NO_CDM_VERSION) {
+    return MediaKeySystemStatus::Available;
+  }
+
   nsresult rv;
   int32_t version = versionStr.ToInteger(&rv);
   if (NS_FAILED(rv) || version < 0 || aMinCdmVersion > version) {
     return MediaKeySystemStatus::Cdm_insufficient_version;
   }
 
   return MediaKeySystemStatus::Available;
 }
@@ -132,44 +141,30 @@ MediaKeySystemAccess::GetKeySystemStatus
   if (NS_WARN_IF(!mps)) {
     return MediaKeySystemStatus::Error;
   }
 
   if (aKeySystem.EqualsLiteral("org.w3.clearkey")) {
     if (!Preferences::GetBool("media.eme.clearkey.enabled", true)) {
       return MediaKeySystemStatus::Cdm_disabled;
     }
-    if (!HaveGMPFor(mps,
-                    NS_LITERAL_CSTRING("org.w3.clearkey"),
-                    NS_LITERAL_CSTRING(GMP_API_DECRYPTOR))) {
-      return MediaKeySystemStatus::Cdm_not_installed;
-    }
     return EnsureMinCDMVersion(mps, aKeySystem, aMinCdmVersion);
   }
 
 #ifdef XP_WIN
   if ((aKeySystem.EqualsLiteral("com.adobe.access") ||
        aKeySystem.EqualsLiteral("com.adobe.primetime"))) {
     // Win Vista and later only.
     if (!IsVistaOrLater()) {
       return MediaKeySystemStatus::Cdm_not_supported;
     }
     if (!Preferences::GetBool("media.gmp-eme-adobe.enabled", false)) {
       return MediaKeySystemStatus::Cdm_disabled;
     }
-    if (!HaveGMPFor(mps,
-                    NS_ConvertUTF16toUTF8(aKeySystem),
-                    NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)) &&
-        // XXX to be removed later in bug 1147692
-        !HaveGMPFor(mps,
-                    NS_ConvertUTF16toUTF8(aKeySystem),
-                    NS_LITERAL_CSTRING(GMP_API_DECRYPTOR_COMPAT))) {
-      return MediaKeySystemStatus::Cdm_not_installed;
-    }
-    return EnsureMinCDMVersion(mps, aKeySystem, aMinCdmVersion);
+    return EnsureMinCDMVersion(mps, aKeySystem, aMinCdmVersion, true);
   }
 #endif
 
   return MediaKeySystemStatus::Cdm_not_supported;
 }
 
 static bool
 IsPlayableWithGMP(mozIGeckoMediaPluginService* aGMPS,
--- a/dom/media/fmp4/gmp/GMPAudioDecoder.cpp
+++ b/dom/media/fmp4/gmp/GMPAudioDecoder.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "GMPAudioDecoder.h"
+#include "nsServiceManagerUtils.h"
 
 namespace mozilla {
 
 #if defined(DEBUG)
 bool IsOnGMPThread()
 {
   nsCOMPtr<mozIGeckoMediaPluginService> mps = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   MOZ_ASSERT(mps);
@@ -127,43 +128,71 @@ GMPAudioDecoder::InitTags(nsTArray<nsCSt
 }
 
 nsCString
 GMPAudioDecoder::GetNodeId()
 {
   return NS_LITERAL_CSTRING("");
 }
 
+void
+GMPAudioDecoder::GetGMPAPI(GMPInitDoneRunnable* aInitDone)
+{
+  MOZ_ASSERT(IsOnGMPThread());
+
+  nsTArray<nsCString> tags;
+  InitTags(tags);
+  UniquePtr<GetGMPAudioDecoderCallback> callback(
+    new GMPInitDoneCallback(this, aInitDone));
+  if (NS_FAILED(mMPS->GetGMPAudioDecoder(&tags, GetNodeId(), Move(callback)))) {
+    aInitDone->Dispatch();
+  }
+}
+
+void
+GMPAudioDecoder::GMPInitDone(GMPAudioDecoderProxy* aGMP)
+{
+  MOZ_ASSERT(aGMP);
+  nsTArray<uint8_t> codecSpecific;
+  codecSpecific.AppendElements(mConfig.audio_specific_config->Elements(),
+                               mConfig.audio_specific_config->Length());
+
+  nsresult rv = aGMP->InitDecode(kGMPAudioCodecAAC,
+                                 mConfig.channel_count,
+                                 mConfig.bits_per_sample,
+                                 mConfig.samples_per_second,
+                                 codecSpecific,
+                                 mAdapter);
+  if (NS_SUCCEEDED(rv)) {
+    mGMP = aGMP;
+  }
+}
+
 nsresult
 GMPAudioDecoder::Init()
 {
   MOZ_ASSERT(IsOnGMPThread());
 
   mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   MOZ_ASSERT(mMPS);
 
-  nsTArray<nsCString> tags;
-  InitTags(tags);
-  nsresult rv = mMPS->GetGMPAudioDecoder(&tags, GetNodeId(), &mGMP);
-  NS_ENSURE_SUCCESS(rv, rv);
-  MOZ_ASSERT(mGMP);
-
-  nsTArray<uint8_t> codecSpecific;
-  codecSpecific.AppendElements(mConfig.audio_specific_config->Elements(),
-                               mConfig.audio_specific_config->Length());
+  nsCOMPtr<nsIThread> gmpThread = NS_GetCurrentThread();
 
-  rv = mGMP->InitDecode(kGMPAudioCodecAAC,
-                        mConfig.channel_count,
-                        mConfig.bits_per_sample,
-                        mConfig.samples_per_second,
-                        codecSpecific,
-                        mAdapter);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsRefPtr<GMPInitDoneRunnable> initDone(new GMPInitDoneRunnable());
+  gmpThread->Dispatch(
+    NS_NewRunnableMethodWithArg<GMPInitDoneRunnable*>(this,
+                                                      &GMPAudioDecoder::GetGMPAPI,
+                                                      initDone),
+    NS_DISPATCH_NORMAL);
 
-  return NS_OK;
+  while (!initDone->IsDone()) {
+    NS_ProcessNextEvent(gmpThread, true);
+  }
+
+  return mGMP ? NS_OK : NS_ERROR_FAILURE;
 }
 
 nsresult
 GMPAudioDecoder::Input(mp4_demuxer::MP4Sample* aSample)
 {
   MOZ_ASSERT(IsOnGMPThread());
 
   nsAutoPtr<mp4_demuxer::MP4Sample> sample(aSample);
--- a/dom/media/fmp4/gmp/GMPAudioDecoder.h
+++ b/dom/media/fmp4/gmp/GMPAudioDecoder.h
@@ -75,16 +75,73 @@ public:
   virtual nsresult Drain() override;
   virtual nsresult Shutdown() override;
 
 protected:
   virtual void InitTags(nsTArray<nsCString>& aTags);
   virtual nsCString GetNodeId();
 
 private:
+  class GMPInitDoneRunnable : public nsRunnable
+  {
+  public:
+    GMPInitDoneRunnable()
+      : mInitDone(false),
+        mThread(do_GetCurrentThread())
+    {
+    }
+
+    NS_IMETHOD Run()
+    {
+      mInitDone = true;
+      return NS_OK;
+    }
+
+    void Dispatch()
+    {
+      mThread->Dispatch(this, NS_DISPATCH_NORMAL);
+    }
+
+    bool IsDone()
+    {
+      MOZ_ASSERT(nsCOMPtr<nsIThread>(do_GetCurrentThread()) == mThread);
+      return mInitDone;
+    }
+
+  private:
+    bool mInitDone;
+    nsCOMPtr<nsIThread> mThread;
+  };
+
+  void GetGMPAPI(GMPInitDoneRunnable* aInitDone);
+
+  class GMPInitDoneCallback : public GetGMPAudioDecoderCallback
+  {
+  public:
+    GMPInitDoneCallback(GMPAudioDecoder* aDecoder,
+                        GMPInitDoneRunnable* aGMPInitDone)
+      : mDecoder(aDecoder)
+      , mGMPInitDone(aGMPInitDone)
+    {
+    }
+
+    virtual void Done(GMPAudioDecoderProxy* aGMP)
+    {
+      if (aGMP) {
+        mDecoder->GMPInitDone(aGMP);
+      }
+      mGMPInitDone->Dispatch();
+    }
+
+  private:
+    nsRefPtr<GMPAudioDecoder> mDecoder;
+    nsRefPtr<GMPInitDoneRunnable> mGMPInitDone;
+  };
+  void GMPInitDone(GMPAudioDecoderProxy* aGMP);
+
   const mp4_demuxer::AudioDecoderConfig& mConfig;
   MediaDataDecoderCallbackProxy* mCallback;
   nsCOMPtr<mozIGeckoMediaPluginService> mMPS;
   GMPAudioDecoderProxy* mGMP;
   nsAutoPtr<AudioCallbackAdapter> mAdapter;
 };
 
 } // namespace mozilla
--- a/dom/media/fmp4/gmp/GMPVideoDecoder.cpp
+++ b/dom/media/fmp4/gmp/GMPVideoDecoder.cpp
@@ -152,61 +152,91 @@ GMPVideoDecoder::CreateFrame(mp4_demuxer
   frame->SetTimeStamp(aSample->composition_timestamp);
   frame->SetCompleteFrame(true);
   frame->SetDuration(aSample->duration);
   frame->SetFrameType(aSample->is_sync_point ? kGMPKeyFrame : kGMPDeltaFrame);
 
   return frame;
 }
 
-nsresult
-GMPVideoDecoder::Init()
+void
+GMPVideoDecoder::GetGMPAPI(GMPInitDoneRunnable* aInitDone)
 {
   MOZ_ASSERT(IsOnGMPThread());
 
-  mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
-  MOZ_ASSERT(mMPS);
-
   nsTArray<nsCString> tags;
   InitTags(tags);
-  nsresult rv = mMPS->GetGMPVideoDecoder(&tags, GetNodeId(), &mHost, &mGMP);
-  NS_ENSURE_SUCCESS(rv, rv);
-  MOZ_ASSERT(mHost && mGMP);
+  UniquePtr<GetGMPVideoDecoderCallback> callback(
+    new GMPInitDoneCallback(this, aInitDone));
+  if (NS_FAILED(mMPS->GetGMPVideoDecoder(&tags, GetNodeId(), Move(callback)))) {
+    aInitDone->Dispatch();
+  }
+}
 
-  // GMP implementations have interpreted the meaning of GMP_BufferLength32
-  // differently.  The OpenH264 GMP expects GMP_BufferLength32 to behave as
-  // specified in the GMP API, where each buffer is prefixed by a 32-bit
-  // host-endian buffer length that includes the size of the buffer length
-  // field.  Other existing GMPs currently expect GMP_BufferLength32 (when
-  // combined with kGMPVideoCodecH264) to mean "like AVCC but restricted to
-  // 4-byte NAL lengths" (i.e. buffer lengths are specified in big-endian
-  // and do not include the length of the buffer length field.
-  mConvertNALUnitLengths = mGMP->GetDisplayName().EqualsLiteral("gmpopenh264");
+void
+GMPVideoDecoder::GMPInitDone(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost)
+{
+  MOZ_ASSERT(aHost && aGMP);
 
   GMPVideoCodec codec;
   memset(&codec, 0, sizeof(codec));
 
   codec.mGMPApiVersion = kGMPVersion33;
 
   codec.mCodecType = kGMPVideoCodecH264;
   codec.mWidth = mConfig.display_width;
   codec.mHeight = mConfig.display_height;
 
   nsTArray<uint8_t> codecSpecific;
   codecSpecific.AppendElement(0); // mPacketizationMode.
   codecSpecific.AppendElements(mConfig.extra_data->Elements(),
                                mConfig.extra_data->Length());
 
-  rv = mGMP->InitDecode(codec,
-                        codecSpecific,
-                        mAdapter,
-                        PR_GetNumberOfProcessors());
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsresult rv = aGMP->InitDecode(codec,
+                                 codecSpecific,
+                                 mAdapter,
+                                 PR_GetNumberOfProcessors());
+  if (NS_SUCCEEDED(rv)) {
+    mGMP = aGMP;
+    mHost = aHost;
+
+    // GMP implementations have interpreted the meaning of GMP_BufferLength32
+    // differently.  The OpenH264 GMP expects GMP_BufferLength32 to behave as
+    // specified in the GMP API, where each buffer is prefixed by a 32-bit
+    // host-endian buffer length that includes the size of the buffer length
+    // field.  Other existing GMPs currently expect GMP_BufferLength32 (when
+    // combined with kGMPVideoCodecH264) to mean "like AVCC but restricted to
+    // 4-byte NAL lengths" (i.e. buffer lengths are specified in big-endian
+    // and do not include the length of the buffer length field.
+    mConvertNALUnitLengths = mGMP->GetDisplayName().EqualsLiteral("gmpopenh264");
+  }
+}
 
-  return NS_OK;
+nsresult
+GMPVideoDecoder::Init()
+{
+  MOZ_ASSERT(IsOnGMPThread());
+
+  mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
+  MOZ_ASSERT(mMPS);
+
+  nsCOMPtr<nsIThread> gmpThread = NS_GetCurrentThread();
+
+  nsRefPtr<GMPInitDoneRunnable> initDone(new GMPInitDoneRunnable());
+  gmpThread->Dispatch(
+    NS_NewRunnableMethodWithArg<GMPInitDoneRunnable*>(this,
+                                                      &GMPVideoDecoder::GetGMPAPI,
+                                                      initDone),
+    NS_DISPATCH_NORMAL);
+
+  while (!initDone->IsDone()) {
+    NS_ProcessNextEvent(gmpThread, true);
+  }
+
+  return mGMP ? NS_OK : NS_ERROR_FAILURE;
 }
 
 nsresult
 GMPVideoDecoder::Input(mp4_demuxer::MP4Sample* aSample)
 {
   MOZ_ASSERT(IsOnGMPThread());
 
   nsAutoPtr<mp4_demuxer::MP4Sample> sample(aSample);
--- a/dom/media/fmp4/gmp/GMPVideoDecoder.h
+++ b/dom/media/fmp4/gmp/GMPVideoDecoder.h
@@ -91,16 +91,73 @@ public:
   virtual nsresult Shutdown() override;
 
 protected:
   virtual void InitTags(nsTArray<nsCString>& aTags);
   virtual nsCString GetNodeId();
   virtual GMPUnique<GMPVideoEncodedFrame>::Ptr CreateFrame(mp4_demuxer::MP4Sample* aSample);
 
 private:
+  class GMPInitDoneRunnable : public nsRunnable
+  {
+  public:
+    GMPInitDoneRunnable()
+      : mInitDone(false),
+        mThread(do_GetCurrentThread())
+    {
+    }
+
+    NS_IMETHOD Run()
+    {
+      mInitDone = true;
+      return NS_OK;
+    }
+
+    void Dispatch()
+    {
+      mThread->Dispatch(this, NS_DISPATCH_NORMAL);
+    }
+
+    bool IsDone()
+    {
+      MOZ_ASSERT(nsCOMPtr<nsIThread>(do_GetCurrentThread()) == mThread);
+      return mInitDone;
+    }
+
+  private:
+    bool mInitDone;
+    nsCOMPtr<nsIThread> mThread;
+  };
+
+  void GetGMPAPI(GMPInitDoneRunnable* aInitDone);
+
+  class GMPInitDoneCallback : public GetGMPVideoDecoderCallback
+  {
+  public:
+    GMPInitDoneCallback(GMPVideoDecoder* aDecoder,
+                        GMPInitDoneRunnable* aGMPInitDone)
+      : mDecoder(aDecoder)
+      , mGMPInitDone(aGMPInitDone)
+    {
+    }
+
+    virtual void Done(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost)
+    {
+      if (aGMP) {
+        mDecoder->GMPInitDone(aGMP, aHost);
+      }
+      mGMPInitDone->Dispatch();
+    }
+
+  private:
+    nsRefPtr<GMPVideoDecoder> mDecoder;
+    nsRefPtr<GMPInitDoneRunnable> mGMPInitDone;
+  };
+  void GMPInitDone(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost);
+
   const mp4_demuxer::VideoDecoderConfig& mConfig;
   MediaDataDecoderCallbackProxy* mCallback;
   nsCOMPtr<mozIGeckoMediaPluginService> mMPS;
   GMPVideoDecoderProxy* mGMP;
   GMPVideoHost* mHost;
   nsAutoPtr<VideoCallbackAdapter> mAdapter;
   bool mConvertNALUnitLengths;
 };
--- a/dom/media/fmp4/wmf/WMFDecoderModule.cpp
+++ b/dom/media/fmp4/wmf/WMFDecoderModule.cpp
@@ -9,16 +9,17 @@
 #include "WMFDecoder.h"
 #include "WMFVideoMFTManager.h"
 #include "WMFAudioMFTManager.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/DebugOnly.h"
 #include "WMFMediaDataDecoder.h"
 #include "nsIWindowsRegKey.h"
 #include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
 #include "nsIGfxInfo.h"
 #include "GfxDriverInfo.h"
 #include "gfxWindowsPlatform.h"
 
 namespace mozilla {
 
 bool WMFDecoderModule::sIsWMFEnabled = false;
 bool WMFDecoderModule::sDXVAEnabled = false;
--- a/dom/media/gmp/GMPAudioDecoderChild.cpp
+++ b/dom/media/gmp/GMPAudioDecoderChild.cpp
@@ -1,23 +1,23 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "GMPAudioDecoderChild.h"
-#include "GMPChild.h"
+#include "GMPContentChild.h"
 #include "GMPAudioHost.h"
 #include "mozilla/unused.h"
 #include <stdio.h>
 
 namespace mozilla {
 namespace gmp {
 
-GMPAudioDecoderChild::GMPAudioDecoderChild(GMPChild* aPlugin)
+GMPAudioDecoderChild::GMPAudioDecoderChild(GMPContentChild* aPlugin)
   : mPlugin(aPlugin)
   , mAudioDecoder(nullptr)
 {
   MOZ_ASSERT(mPlugin);
 }
 
 GMPAudioDecoderChild::~GMPAudioDecoderChild()
 {
--- a/dom/media/gmp/GMPAudioDecoderChild.h
+++ b/dom/media/gmp/GMPAudioDecoderChild.h
@@ -8,23 +8,23 @@
 
 #include "mozilla/gmp/PGMPAudioDecoderChild.h"
 #include "gmp-audio-decode.h"
 #include "GMPAudioHost.h"
 
 namespace mozilla {
 namespace gmp {
 
-class GMPChild;
+class GMPContentChild;
 
 class GMPAudioDecoderChild : public PGMPAudioDecoderChild,
                              public GMPAudioDecoderCallback
 {
 public:
-  explicit GMPAudioDecoderChild(GMPChild* aPlugin);
+  explicit GMPAudioDecoderChild(GMPContentChild* aPlugin);
   virtual ~GMPAudioDecoderChild();
 
   void Init(GMPAudioDecoder* aDecoder);
   GMPAudioHostImpl& Host();
 
   // GMPAudioDecoderCallback
   virtual void Decoded(GMPAudioSamples* aEncodedSamples) override;
   virtual void InputDataExhausted() override;
@@ -35,17 +35,17 @@ public:
 private:
   // PGMPAudioDecoderChild
   virtual bool RecvInitDecode(const GMPAudioCodecData& codecSettings) override;
   virtual bool RecvDecode(const GMPAudioEncodedSampleData& input) override;
   virtual bool RecvReset() override;
   virtual bool RecvDrain() override;
   virtual bool RecvDecodingComplete() override;
 
-  GMPChild* mPlugin;
+  GMPContentChild* mPlugin;
   GMPAudioDecoder* mAudioDecoder;
   GMPAudioHostImpl mAudioHost;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMPAudioDecoderChild_h_
--- a/dom/media/gmp/GMPAudioDecoderParent.cpp
+++ b/dom/media/gmp/GMPAudioDecoderParent.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "GMPAudioDecoderParent.h"
-#include "GMPParent.h"
+#include "GMPContentParent.h"
 #include <stdio.h>
 #include "mozilla/unused.h"
 #include "GMPMessageUtils.h"
 #include "nsThreadUtils.h"
 #include "prlog.h"
 
 namespace mozilla {
 
@@ -24,17 +24,17 @@ extern PRLogModuleInfo* GetGMPLog();
 #define LOG(level, msg) PR_LOG(GetGMPLog(), (level), msg)
 #else
 #define LOGD(msg)
 #define LOG(level, msg)
 #endif
 
 namespace gmp {
 
-GMPAudioDecoderParent::GMPAudioDecoderParent(GMPParent* aPlugin)
+GMPAudioDecoderParent::GMPAudioDecoderParent(GMPContentParent* aPlugin)
   : mIsOpen(false)
   , mShuttingDown(false)
   , mPlugin(aPlugin)
   , mCallback(nullptr)
 {
   MOZ_ASSERT(mPlugin);
 }
 
@@ -259,16 +259,23 @@ GMPAudioDecoderParent::RecvError(const G
 
   // Ignore any return code. It is OK for this to fail without killing the process.
   mCallback->Error(aError);
 
   return true;
 }
 
 bool
+GMPAudioDecoderParent::RecvShutdown()
+{
+  Shutdown();
+  return true;
+}
+
+bool
 GMPAudioDecoderParent::Recv__delete__()
 {
   if (mPlugin) {
     // Ignore any return code. It is OK for this to fail without killing the process.
     mPlugin->AudioDecoderDestroyed(this);
     mPlugin = nullptr;
   }
 
--- a/dom/media/gmp/GMPAudioDecoderParent.h
+++ b/dom/media/gmp/GMPAudioDecoderParent.h
@@ -11,25 +11,25 @@
 #include "gmp-audio-codec.h"
 #include "mozilla/gmp/PGMPAudioDecoderParent.h"
 #include "GMPMessageUtils.h"
 #include "GMPAudioDecoderProxy.h"
 
 namespace mozilla {
 namespace gmp {
 
-class GMPParent;
+class GMPContentParent;
 
 class GMPAudioDecoderParent final : public GMPAudioDecoderProxy
                                   , public PGMPAudioDecoderParent
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(GMPAudioDecoderParent)
 
-  explicit GMPAudioDecoderParent(GMPParent *aPlugin);
+  explicit GMPAudioDecoderParent(GMPContentParent *aPlugin);
 
   nsresult Shutdown();
 
   // GMPAudioDecoderProxy
   virtual nsresult InitDecode(GMPAudioCodecType aCodecType,
                               uint32_t aChannelCount,
                               uint32_t aBitsPerChannel,
                               uint32_t aSamplesPerSecond,
@@ -45,20 +45,21 @@ private:
 
   // PGMPAudioDecoderParent
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
   virtual bool RecvDecoded(const GMPAudioDecodedSampleData& aDecoded) override;
   virtual bool RecvInputDataExhausted() override;
   virtual bool RecvDrainComplete() override;
   virtual bool RecvResetComplete() override;
   virtual bool RecvError(const GMPErr& aError) override;
+  virtual bool RecvShutdown() override;
   virtual bool Recv__delete__() override;
 
   bool mIsOpen;
   bool mShuttingDown;
-  nsRefPtr<GMPParent> mPlugin;
+  nsRefPtr<GMPContentParent> mPlugin;
   GMPAudioDecoderCallbackProxy* mCallback;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMPAudioDecoderParent_h_
--- a/dom/media/gmp/GMPChild.cpp
+++ b/dom/media/gmp/GMPChild.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "GMPChild.h"
+#include "GMPContentChild.h"
 #include "GMPProcessChild.h"
 #include "GMPLoader.h"
 #include "GMPVideoDecoderChild.h"
 #include "GMPVideoEncoderChild.h"
 #include "GMPAudioDecoderChild.h"
 #include "GMPDecryptorChild.h"
 #include "GMPVideoHost.h"
 #include "nsDebugImpl.h"
@@ -251,22 +252,16 @@ GMPChild::StartMacSandbox()
   nsAutoCString err;
   if (!mozilla::StartMacSandbox(info, err)) {
     NS_WARNING(err.get());
     MOZ_CRASH("sandbox_init() failed");
   }
 }
 #endif // XP_MACOSX && MOZ_GMP_SANDBOX
 
-void
-GMPChild::CheckThread()
-{
-  MOZ_ASSERT(mGMPMessageLoop == MessageLoop::current());
-}
-
 bool
 GMPChild::Init(const std::string& aPluginPath,
                const std::string& aVoucherPath,
                base::ProcessId aParentPid,
                MessageLoop* aIOLoop,
                IPC::Channel* aChannel)
 {
   LOGD("%s pluginPath=%s", __FUNCTION__, aPluginPath.c_str());
@@ -462,16 +457,21 @@ GMPChild::GMPMessageLoop()
   return mGMPMessageLoop;
 }
 
 void
 GMPChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   LOGD("%s reason=%d", __FUNCTION__, aWhy);
 
+  for (uint32_t i = mGMPContentChildren.Length(); i > 0; i--) {
+    MOZ_ASSERT_IF(aWhy == NormalShutdown, !mGMPContentChildren[i - 1]->IsUsed());
+    mGMPContentChildren[i - 1]->Close();
+  }
+
   if (mGMPLoader) {
     mGMPLoader->Shutdown();
   }
   if (AbnormalShutdown == aWhy) {
     NS_WARNING("Abnormal shutdown of GMP process!");
     _exit(0);
   }
 
@@ -496,156 +496,29 @@ GMPChild::ProcessingError(Result aCode, 
       MOZ_CRASH("aborting because of MsgRouteError");
     case MsgValueError:
       MOZ_CRASH("aborting because of MsgValueError");
     default:
       MOZ_CRASH("not reached");
   }
 }
 
-PGMPAudioDecoderChild*
-GMPChild::AllocPGMPAudioDecoderChild()
-{
-  return new GMPAudioDecoderChild(this);
-}
-
-bool
-GMPChild::DeallocPGMPAudioDecoderChild(PGMPAudioDecoderChild* aActor)
-{
-  delete aActor;
-  return true;
-}
-
 mozilla::dom::PCrashReporterChild*
 GMPChild::AllocPCrashReporterChild(const NativeThreadId& aThread)
 {
   return new CrashReporterChild();
 }
 
 bool
 GMPChild::DeallocPCrashReporterChild(PCrashReporterChild* aCrashReporter)
 {
   delete aCrashReporter;
   return true;
 }
 
-PGMPVideoDecoderChild*
-GMPChild::AllocPGMPVideoDecoderChild()
-{
-  return new GMPVideoDecoderChild(this);
-}
-
-bool
-GMPChild::DeallocPGMPVideoDecoderChild(PGMPVideoDecoderChild* aActor)
-{
-  delete aActor;
-  return true;
-}
-
-PGMPDecryptorChild*
-GMPChild::AllocPGMPDecryptorChild()
-{
-  GMPDecryptorChild* actor = new GMPDecryptorChild(this, mPluginVoucher, mSandboxVoucher);
-  actor->AddRef();
-  return actor;
-}
-
-bool
-GMPChild::DeallocPGMPDecryptorChild(PGMPDecryptorChild* aActor)
-{
-  static_cast<GMPDecryptorChild*>(aActor)->Release();
-  return true;
-}
-
-bool
-GMPChild::RecvPGMPAudioDecoderConstructor(PGMPAudioDecoderChild* aActor)
-{
-  auto vdc = static_cast<GMPAudioDecoderChild*>(aActor);
-
-  void* vd = nullptr;
-  GMPErr err = GetAPI(GMP_API_AUDIO_DECODER, &vdc->Host(), &vd);
-  if (err != GMPNoErr || !vd) {
-    return false;
-  }
-
-  vdc->Init(static_cast<GMPAudioDecoder*>(vd));
-
-  return true;
-}
-
-PGMPVideoEncoderChild*
-GMPChild::AllocPGMPVideoEncoderChild()
-{
-  return new GMPVideoEncoderChild(this);
-}
-
-bool
-GMPChild::DeallocPGMPVideoEncoderChild(PGMPVideoEncoderChild* aActor)
-{
-  delete aActor;
-  return true;
-}
-
-bool
-GMPChild::RecvPGMPVideoDecoderConstructor(PGMPVideoDecoderChild* aActor)
-{
-  auto vdc = static_cast<GMPVideoDecoderChild*>(aActor);
-
-  void* vd = nullptr;
-  GMPErr err = GetAPI(GMP_API_VIDEO_DECODER, &vdc->Host(), &vd);
-  if (err != GMPNoErr || !vd) {
-    NS_WARNING("GMPGetAPI call failed trying to construct decoder.");
-    return false;
-  }
-
-  vdc->Init(static_cast<GMPVideoDecoder*>(vd));
-
-  return true;
-}
-
-bool
-GMPChild::RecvPGMPVideoEncoderConstructor(PGMPVideoEncoderChild* aActor)
-{
-  auto vec = static_cast<GMPVideoEncoderChild*>(aActor);
-
-  void* ve = nullptr;
-  GMPErr err = GetAPI(GMP_API_VIDEO_ENCODER, &vec->Host(), &ve);
-  if (err != GMPNoErr || !ve) {
-    NS_WARNING("GMPGetAPI call failed trying to construct encoder.");
-    return false;
-  }
-
-  vec->Init(static_cast<GMPVideoEncoder*>(ve));
-
-  return true;
-}
-
-bool
-GMPChild::RecvPGMPDecryptorConstructor(PGMPDecryptorChild* aActor)
-{
-  GMPDecryptorChild* child = static_cast<GMPDecryptorChild*>(aActor);
-  GMPDecryptorHost* host = static_cast<GMPDecryptorHost*>(child);
-
-  void* session = nullptr;
-  GMPErr err = GetAPI(GMP_API_DECRYPTOR, host, &session);
-
-  if (err != GMPNoErr && !session) {
-    // XXX to remove in bug 1147692
-    err = GetAPI(GMP_API_DECRYPTOR_COMPAT, host, &session);
-  }
-
-  if (err != GMPNoErr || !session) {
-    return false;
-  }
-
-  child->Init(static_cast<GMPDecryptor*>(session));
-
-  return true;
-}
-
 PGMPTimerChild*
 GMPChild::AllocPGMPTimerChild()
 {
   return new GMPTimerChild(this);
 }
 
 bool
 GMPChild::DeallocPGMPTimerChild(PGMPTimerChild* aActor)
@@ -710,16 +583,25 @@ GMPChild::RecvBeginAsyncShutdown()
   if (mAsyncShutdown) {
     mAsyncShutdown->BeginShutdown();
   } else {
     ShutdownComplete();
   }
   return true;
 }
 
+bool
+GMPChild::RecvCloseActive()
+{
+  for (uint32_t i = mGMPContentChildren.Length(); i > 0; i--) {
+    mGMPContentChildren[i - 1]->CloseActive();
+  }
+  return true;
+}
+
 void
 GMPChild::ShutdownComplete()
 {
   LOGD("%s", __FUNCTION__);
   MOZ_ASSERT(mGMPMessageLoop == MessageLoop::current());
   SendAsyncShutdownComplete();
 }
 
@@ -797,13 +679,39 @@ GMPChild::PreLoadSandboxVoucher()
   mSandboxVoucher.SetLength(length);
   stream.read((char*)mSandboxVoucher.Elements(), length);
   if (!stream) {
     NS_WARNING("PreLoadSandboxVoucher failed to read plugin voucher file!");
     return;
   }
 }
 
+PGMPContentChild*
+GMPChild::AllocPGMPContentChild(Transport* aTransport,
+                                ProcessId aOtherPid)
+{
+  GMPContentChild* child =
+    mGMPContentChildren.AppendElement(new GMPContentChild(this))->get();
+  child->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop(), ipc::ChildSide);
+
+  return child;
+}
+
+void
+GMPChild::GMPContentChildActorDestroy(GMPContentChild* aGMPContentChild)
+{
+  for (uint32_t i = mGMPContentChildren.Length(); i > 0; i--) {
+    UniquePtr<GMPContentChild>& toDestroy = mGMPContentChildren[i - 1];
+    if (toDestroy.get() == aGMPContentChild) {
+      SendPGMPContentChildDestroyed();
+      MessageLoop::current()->PostTask(FROM_HERE,
+                                       new DeleteTask<GMPContentChild>(toDestroy.release()));
+      mGMPContentChildren.RemoveElementAt(i - 1);
+      break;
+    }
+  }
+}
+
 } // namespace gmp
 } // namespace mozilla
 
 #undef LOG
 #undef LOGD
--- a/dom/media/gmp/GMPChild.h
+++ b/dom/media/gmp/GMPChild.h
@@ -2,29 +2,29 @@
 /* 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 GMPChild_h_
 #define GMPChild_h_
 
 #include "mozilla/gmp/PGMPChild.h"
-#include "GMPSharedMemManager.h"
 #include "GMPTimerChild.h"
 #include "GMPStorageChild.h"
 #include "GMPLoader.h"
 #include "gmp-async-shutdown.h"
 #include "gmp-entrypoints.h"
 #include "prlink.h"
 
 namespace mozilla {
 namespace gmp {
 
+class GMPContentChild;
+
 class GMPChild : public PGMPChild
-               , public GMPSharedMem
                , public GMPAsyncShutdownHost
 {
 public:
   GMPChild();
   virtual ~GMPChild();
 
   bool Init(const std::string& aPluginPath,
             const std::string& aVoucherPath,
@@ -35,69 +35,58 @@ public:
   bool PreLoadLibraries(const std::string& aPluginPath);
 #endif
   MessageLoop* GMPMessageLoop();
 
   // Main thread only.
   GMPTimerChild* GetGMPTimers();
   GMPStorageChild* GetGMPStorage();
 
-  // GMPSharedMem
-  virtual void CheckThread() override;
-
   // GMPAsyncShutdownHost
   void ShutdownComplete() override;
 
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   void StartMacSandbox();
 #endif
 
 private:
+  friend class GMPContentChild;
 
   bool PreLoadPluginVoucher(const std::string& aPluginPath);
   void PreLoadSandboxVoucher();
 
   bool GetLibPath(nsACString& aOutLibPath);
 
   virtual bool RecvSetNodeId(const nsCString& aNodeId) override;
   virtual bool RecvStartPlugin() override;
 
   virtual PCrashReporterChild* AllocPCrashReporterChild(const NativeThreadId& aThread) override;
   virtual bool DeallocPCrashReporterChild(PCrashReporterChild*) override;
 
-  virtual PGMPVideoDecoderChild* AllocPGMPVideoDecoderChild() override;
-  virtual bool DeallocPGMPVideoDecoderChild(PGMPVideoDecoderChild* aActor) override;
-  virtual bool RecvPGMPVideoDecoderConstructor(PGMPVideoDecoderChild* aActor) override;
-
-  virtual PGMPVideoEncoderChild* AllocPGMPVideoEncoderChild() override;
-  virtual bool DeallocPGMPVideoEncoderChild(PGMPVideoEncoderChild* aActor) override;
-  virtual bool RecvPGMPVideoEncoderConstructor(PGMPVideoEncoderChild* aActor) override;
-
-  virtual PGMPDecryptorChild* AllocPGMPDecryptorChild() override;
-  virtual bool DeallocPGMPDecryptorChild(PGMPDecryptorChild* aActor) override;
-  virtual bool RecvPGMPDecryptorConstructor(PGMPDecryptorChild* aActor) override;
-
-  virtual PGMPAudioDecoderChild* AllocPGMPAudioDecoderChild() override;
-  virtual bool DeallocPGMPAudioDecoderChild(PGMPAudioDecoderChild* aActor) override;
-  virtual bool RecvPGMPAudioDecoderConstructor(PGMPAudioDecoderChild* aActor) override;
-
   virtual PGMPTimerChild* AllocPGMPTimerChild() override;
   virtual bool DeallocPGMPTimerChild(PGMPTimerChild* aActor) override;
 
   virtual PGMPStorageChild* AllocPGMPStorageChild() override;
   virtual bool DeallocPGMPStorageChild(PGMPStorageChild* aActor) override;
 
+  virtual PGMPContentChild* AllocPGMPContentChild(Transport* aTransport,
+                                                  ProcessId aOtherPid) override;
+  void GMPContentChildActorDestroy(GMPContentChild* aGMPContentChild);
+
   virtual bool RecvCrashPluginNow() override;
   virtual bool RecvBeginAsyncShutdown() override;
+  virtual bool RecvCloseActive() override;
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
   virtual void ProcessingError(Result aCode, const char* aReason) override;
 
   GMPErr GetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI);
 
+  nsTArray<UniquePtr<GMPContentChild>> mGMPContentChildren;
+
   GMPAsyncShutdown* mAsyncShutdown;
   nsRefPtr<GMPTimerChild> mTimerChild;
   nsRefPtr<GMPStorageChild> mStorage;
 
   MessageLoop* mGMPMessageLoop;
   std::string mPluginPath;
   std::string mVoucherPath;
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/GMPContentChild.cpp
@@ -0,0 +1,215 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "GMPContentChild.h"
+
+namespace mozilla {
+namespace gmp {
+
+GMPContentChild::GMPContentChild(GMPChild* aChild)
+  : mGMPChild(aChild)
+{
+  MOZ_COUNT_CTOR(GMPContentChild);
+}
+
+GMPContentChild::~GMPContentChild()
+{
+  MOZ_COUNT_DTOR(GMPContentChild);
+  XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                   new DeleteTask<Transport>(GetTransport()));
+}
+
+MessageLoop*
+GMPContentChild::GMPMessageLoop()
+{
+  return mGMPChild->GMPMessageLoop();
+}
+
+void
+GMPContentChild::CheckThread()
+{
+  MOZ_ASSERT(mGMPChild->mGMPMessageLoop == MessageLoop::current());
+}
+
+void
+GMPContentChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  mGMPChild->GMPContentChildActorDestroy(this);
+}
+
+void
+GMPContentChild::ProcessingError(Result aCode, const char* aReason)
+{
+  mGMPChild->ProcessingError(aCode, aReason);
+}
+
+PGMPAudioDecoderChild*
+GMPContentChild::AllocPGMPAudioDecoderChild()
+{
+  return new GMPAudioDecoderChild(this);
+}
+
+bool
+GMPContentChild::DeallocPGMPAudioDecoderChild(PGMPAudioDecoderChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+PGMPDecryptorChild*
+GMPContentChild::AllocPGMPDecryptorChild()
+{
+  GMPDecryptorChild* actor = new GMPDecryptorChild(this,
+                                                   mGMPChild->mPluginVoucher,
+                                                   mGMPChild->mSandboxVoucher);
+  actor->AddRef();
+  return actor;
+}
+
+bool
+GMPContentChild::DeallocPGMPDecryptorChild(PGMPDecryptorChild* aActor)
+{
+  static_cast<GMPDecryptorChild*>(aActor)->Release();
+  return true;
+}
+
+PGMPVideoDecoderChild*
+GMPContentChild::AllocPGMPVideoDecoderChild()
+{
+  return new GMPVideoDecoderChild(this);
+}
+
+bool
+GMPContentChild::DeallocPGMPVideoDecoderChild(PGMPVideoDecoderChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+PGMPVideoEncoderChild*
+GMPContentChild::AllocPGMPVideoEncoderChild()
+{
+  return new GMPVideoEncoderChild(this);
+}
+
+bool
+GMPContentChild::DeallocPGMPVideoEncoderChild(PGMPVideoEncoderChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+bool
+GMPContentChild::RecvPGMPDecryptorConstructor(PGMPDecryptorChild* aActor)
+{
+  GMPDecryptorChild* child = static_cast<GMPDecryptorChild*>(aActor);
+  GMPDecryptorHost* host = static_cast<GMPDecryptorHost*>(child);
+
+  void* session = nullptr;
+  GMPErr err = mGMPChild->GetAPI(GMP_API_DECRYPTOR, host, &session);
+  if (err != GMPNoErr && !session) {
+    // XXX to remove in bug 1147692
+    err = mGMPChild->GetAPI(GMP_API_DECRYPTOR_COMPAT, host, &session);
+  }
+
+  if (err != GMPNoErr || !session) {
+    return false;
+  }
+
+  child->Init(static_cast<GMPDecryptor*>(session));
+
+  return true;
+}
+
+bool
+GMPContentChild::RecvPGMPAudioDecoderConstructor(PGMPAudioDecoderChild* aActor)
+{
+  auto vdc = static_cast<GMPAudioDecoderChild*>(aActor);
+
+  void* vd = nullptr;
+  GMPErr err = mGMPChild->GetAPI(GMP_API_AUDIO_DECODER, &vdc->Host(), &vd);
+  if (err != GMPNoErr || !vd) {
+    return false;
+  }
+
+  vdc->Init(static_cast<GMPAudioDecoder*>(vd));
+
+  return true;
+}
+
+bool
+GMPContentChild::RecvPGMPVideoDecoderConstructor(PGMPVideoDecoderChild* aActor)
+{
+  auto vdc = static_cast<GMPVideoDecoderChild*>(aActor);
+
+  void* vd = nullptr;
+  GMPErr err = mGMPChild->GetAPI(GMP_API_VIDEO_DECODER, &vdc->Host(), &vd);
+  if (err != GMPNoErr || !vd) {
+    NS_WARNING("GMPGetAPI call failed trying to construct decoder.");
+    return false;
+  }
+
+  vdc->Init(static_cast<GMPVideoDecoder*>(vd));
+
+  return true;
+}
+
+bool
+GMPContentChild::RecvPGMPVideoEncoderConstructor(PGMPVideoEncoderChild* aActor)
+{
+  auto vec = static_cast<GMPVideoEncoderChild*>(aActor);
+
+  void* ve = nullptr;
+  GMPErr err = mGMPChild->GetAPI(GMP_API_VIDEO_ENCODER, &vec->Host(), &ve);
+  if (err != GMPNoErr || !ve) {
+    NS_WARNING("GMPGetAPI call failed trying to construct encoder.");
+    return false;
+  }
+
+  vec->Init(static_cast<GMPVideoEncoder*>(ve));
+
+  return true;
+}
+
+void
+GMPContentChild::CloseActive()
+{
+  // Invalidate and remove any remaining API objects.
+  const nsTArray<PGMPAudioDecoderChild*>& audioDecoders =
+    ManagedPGMPAudioDecoderChild();
+  for (uint32_t i = audioDecoders.Length(); i > 0; i--) {
+    audioDecoders[i - 1]->SendShutdown();
+  }
+
+  const nsTArray<PGMPDecryptorChild*>& decryptors =
+    ManagedPGMPDecryptorChild();
+  for (uint32_t i = decryptors.Length(); i > 0; i--) {
+    decryptors[i - 1]->SendShutdown();
+  }
+
+  const nsTArray<PGMPVideoDecoderChild*>& videoDecoders =
+    ManagedPGMPVideoDecoderChild();
+  for (uint32_t i = videoDecoders.Length(); i > 0; i--) {
+    videoDecoders[i - 1]->SendShutdown();
+  }
+
+  const nsTArray<PGMPVideoEncoderChild*>& videoEncoders =
+    ManagedPGMPVideoEncoderChild();
+  for (uint32_t i = videoEncoders.Length(); i > 0; i--) {
+    videoEncoders[i - 1]->SendShutdown();
+  }
+}
+
+bool
+GMPContentChild::IsUsed()
+{
+  return !ManagedPGMPAudioDecoderChild().IsEmpty() ||
+         !ManagedPGMPDecryptorChild().IsEmpty() ||
+         !ManagedPGMPVideoDecoderChild().IsEmpty() ||
+         !ManagedPGMPVideoEncoderChild().IsEmpty();
+}
+
+} // namespace gmp
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/GMPContentChild.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 GMPContentChild_h_
+#define GMPContentChild_h_
+
+#include "mozilla/gmp/PGMPContentChild.h"
+#include "GMPSharedMemManager.h"
+
+namespace mozilla {
+namespace gmp {
+
+class GMPChild;
+
+class GMPContentChild : public PGMPContentChild
+                      , public GMPSharedMem
+{
+public:
+  explicit GMPContentChild(GMPChild* aChild);
+  virtual ~GMPContentChild();
+
+  MessageLoop* GMPMessageLoop();
+
+  virtual bool RecvPGMPAudioDecoderConstructor(PGMPAudioDecoderChild* aActor) override;
+  virtual bool RecvPGMPDecryptorConstructor(PGMPDecryptorChild* aActor) override;
+  virtual bool RecvPGMPVideoDecoderConstructor(PGMPVideoDecoderChild* aActor) override;
+  virtual bool RecvPGMPVideoEncoderConstructor(PGMPVideoEncoderChild* aActor) override;
+
+  virtual PGMPAudioDecoderChild* AllocPGMPAudioDecoderChild() override;
+  virtual bool DeallocPGMPAudioDecoderChild(PGMPAudioDecoderChild* aActor) override;
+
+  virtual PGMPDecryptorChild* AllocPGMPDecryptorChild() override;
+  virtual bool DeallocPGMPDecryptorChild(PGMPDecryptorChild* aActor) override;
+
+  virtual PGMPVideoDecoderChild* AllocPGMPVideoDecoderChild() override;
+  virtual bool DeallocPGMPVideoDecoderChild(PGMPVideoDecoderChild* aActor) override;
+
+  virtual PGMPVideoEncoderChild* AllocPGMPVideoEncoderChild() override;
+  virtual bool DeallocPGMPVideoEncoderChild(PGMPVideoEncoderChild* aActor) override;
+
+  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+  virtual void ProcessingError(Result aCode, const char* aReason) override;
+
+  // GMPSharedMem
+  virtual void CheckThread() override;
+
+  void CloseActive();
+  bool IsUsed();
+
+  GMPChild* mGMPChild;
+};
+
+} // namespace gmp
+} // namespace mozilla
+
+#endif // GMPContentChild_h_
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/GMPContentParent.cpp
@@ -0,0 +1,302 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "GMPContentParent.h"
+#include "GMPAudioDecoderParent.h"
+#include "GMPDecryptorParent.h"
+#include "GMPParent.h"
+#include "GMPServiceChild.h"
+#include "GMPVideoDecoderParent.h"
+#include "GMPVideoEncoderParent.h"
+#include "mozIGeckoMediaPluginService.h"
+#include "prlog.h"
+
+namespace mozilla {
+
+#ifdef LOG
+#undef LOG
+#endif
+
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* GetGMPLog();
+
+#define LOGD(msg) PR_LOG(GetGMPLog(), PR_LOG_DEBUG, msg)
+#define LOG(level, msg) PR_LOG(GetGMPLog(), (level), msg)
+#else
+#define LOGD(msg)
+#define LOG(level, msg)
+#endif
+
+#ifdef __CLASS__
+#undef __CLASS__
+#endif
+#define __CLASS__ "GMPContentParent"
+
+namespace gmp {
+
+GMPContentParent::GMPContentParent(GMPParent* aParent)
+  : mParent(aParent)
+{
+  if (mParent) {
+    SetDisplayName(mParent->GetDisplayName());
+    SetPluginId(mParent->GetPluginId());
+  }
+}
+
+GMPContentParent::~GMPContentParent()
+{
+  XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                   new DeleteTask<Transport>(GetTransport()));
+}
+
+class ReleaseGMPContentParent : public nsRunnable
+{
+public:
+  explicit ReleaseGMPContentParent(GMPContentParent* aToRelease)
+    : mToRelease(aToRelease)
+  {
+  }
+
+  NS_IMETHOD Run()
+  {
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<GMPContentParent> mToRelease;
+};
+
+void
+GMPContentParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  MOZ_ASSERT(mAudioDecoders.IsEmpty() &&
+             mDecryptors.IsEmpty() &&
+             mVideoDecoders.IsEmpty() &&
+             mVideoEncoders.IsEmpty());
+  NS_DispatchToCurrentThread(new ReleaseGMPContentParent(this));
+}
+
+void
+GMPContentParent::CheckThread()
+{
+  MOZ_ASSERT(mGMPThread == NS_GetCurrentThread());
+}
+
+void
+GMPContentParent::AudioDecoderDestroyed(GMPAudioDecoderParent* aDecoder)
+{
+  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
+
+  MOZ_ALWAYS_TRUE(mAudioDecoders.RemoveElement(aDecoder));
+  CloseIfUnused();
+}
+
+void
+GMPContentParent::VideoDecoderDestroyed(GMPVideoDecoderParent* aDecoder)
+{
+  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
+
+  // If the constructor fails, we'll get called before it's added
+  unused << NS_WARN_IF(!mVideoDecoders.RemoveElement(aDecoder));
+  CloseIfUnused();
+}
+
+void
+GMPContentParent::VideoEncoderDestroyed(GMPVideoEncoderParent* aEncoder)
+{
+  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
+
+  // If the constructor fails, we'll get called before it's added
+  unused << NS_WARN_IF(!mVideoEncoders.RemoveElement(aEncoder));
+  CloseIfUnused();
+}
+
+void
+GMPContentParent::DecryptorDestroyed(GMPDecryptorParent* aSession)
+{
+  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
+
+  MOZ_ALWAYS_TRUE(mDecryptors.RemoveElement(aSession));
+  CloseIfUnused();
+}
+
+void
+GMPContentParent::CloseIfUnused()
+{
+  if (mAudioDecoders.IsEmpty() &&
+      mDecryptors.IsEmpty() &&
+      mVideoDecoders.IsEmpty() &&
+      mVideoEncoders.IsEmpty()) {
+    nsRefPtr<GMPContentParent> toClose;
+    if (mParent) {
+      toClose = mParent->ForgetGMPContentParent();
+    } else {
+      toClose = this;
+      nsRefPtr<GeckoMediaPluginServiceChild> gmp(
+        GeckoMediaPluginServiceChild::GetSingleton());
+      gmp->RemoveGMPContentParent(toClose);
+    }
+    NS_DispatchToCurrentThread(NS_NewRunnableMethod(toClose,
+                                                    &GMPContentParent::Close));
+  }
+}
+
+nsresult
+GMPContentParent::GetGMPDecryptor(GMPDecryptorParent** aGMPDP)
+{
+  PGMPDecryptorParent* pdp = SendPGMPDecryptorConstructor();
+  if (!pdp) {
+    return NS_ERROR_FAILURE;
+  }
+  GMPDecryptorParent* dp = static_cast<GMPDecryptorParent*>(pdp);
+  // This addref corresponds to the Proxy pointer the consumer is returned.
+  // It's dropped by calling Close() on the interface.
+  NS_ADDREF(dp);
+  mDecryptors.AppendElement(dp);
+  *aGMPDP = dp;
+
+  return NS_OK;
+}
+
+nsIThread*
+GMPContentParent::GMPThread()
+{
+  if (!mGMPThread) {
+    nsCOMPtr<mozIGeckoMediaPluginService> mps = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
+    MOZ_ASSERT(mps);
+    if (!mps) {
+      return nullptr;
+    }
+    // Not really safe if we just grab to the mGMPThread, as we don't know
+    // what thread we're running on and other threads may be trying to
+    // access this without locks!  However, debug only, and primary failure
+    // mode outside of compiler-helped TSAN is a leak.  But better would be
+    // to use swap() under a lock.
+    mps->GetThread(getter_AddRefs(mGMPThread));
+    MOZ_ASSERT(mGMPThread);
+  }
+
+  return mGMPThread;
+}
+
+nsresult
+GMPContentParent::GetGMPAudioDecoder(GMPAudioDecoderParent** aGMPAD)
+{
+  PGMPAudioDecoderParent* pvap = SendPGMPAudioDecoderConstructor();
+  if (!pvap) {
+    return NS_ERROR_FAILURE;
+  }
+  GMPAudioDecoderParent* vap = static_cast<GMPAudioDecoderParent*>(pvap);
+  // This addref corresponds to the Proxy pointer the consumer is returned.
+  // It's dropped by calling Close() on the interface.
+  NS_ADDREF(vap);
+  *aGMPAD = vap;
+  mAudioDecoders.AppendElement(vap);
+
+  return NS_OK;
+}
+
+nsresult
+GMPContentParent::GetGMPVideoDecoder(GMPVideoDecoderParent** aGMPVD)
+{
+  // returned with one anonymous AddRef that locks it until Destroy
+  PGMPVideoDecoderParent* pvdp = SendPGMPVideoDecoderConstructor();
+  if (!pvdp) {
+    return NS_ERROR_FAILURE;
+  }
+  GMPVideoDecoderParent *vdp = static_cast<GMPVideoDecoderParent*>(pvdp);
+  // This addref corresponds to the Proxy pointer the consumer is returned.
+  // It's dropped by calling Close() on the interface.
+  NS_ADDREF(vdp);
+  *aGMPVD = vdp;
+  mVideoDecoders.AppendElement(vdp);
+
+  return NS_OK;
+}
+
+nsresult
+GMPContentParent::GetGMPVideoEncoder(GMPVideoEncoderParent** aGMPVE)
+{
+  // returned with one anonymous AddRef that locks it until Destroy
+  PGMPVideoEncoderParent* pvep = SendPGMPVideoEncoderConstructor();
+  if (!pvep) {
+    return NS_ERROR_FAILURE;
+  }
+  GMPVideoEncoderParent *vep = static_cast<GMPVideoEncoderParent*>(pvep);
+  // This addref corresponds to the Proxy pointer the consumer is returned.
+  // It's dropped by calling Close() on the interface.
+  NS_ADDREF(vep);
+  *aGMPVE = vep;
+  mVideoEncoders.AppendElement(vep);
+
+  return NS_OK;
+}
+
+PGMPVideoDecoderParent*
+GMPContentParent::AllocPGMPVideoDecoderParent()
+{
+  GMPVideoDecoderParent* vdp = new GMPVideoDecoderParent(this);
+  NS_ADDREF(vdp);
+  return vdp;
+}
+
+bool
+GMPContentParent::DeallocPGMPVideoDecoderParent(PGMPVideoDecoderParent* aActor)
+{
+  GMPVideoDecoderParent* vdp = static_cast<GMPVideoDecoderParent*>(aActor);
+  NS_RELEASE(vdp);
+  return true;
+}
+
+PGMPVideoEncoderParent*
+GMPContentParent::AllocPGMPVideoEncoderParent()
+{
+  GMPVideoEncoderParent* vep = new GMPVideoEncoderParent(this);
+  NS_ADDREF(vep);
+  return vep;
+}
+
+bool
+GMPContentParent::DeallocPGMPVideoEncoderParent(PGMPVideoEncoderParent* aActor)
+{
+  GMPVideoEncoderParent* vep = static_cast<GMPVideoEncoderParent*>(aActor);
+  NS_RELEASE(vep);
+  return true;
+}
+
+PGMPDecryptorParent*
+GMPContentParent::AllocPGMPDecryptorParent()
+{
+  GMPDecryptorParent* ksp = new GMPDecryptorParent(this);
+  NS_ADDREF(ksp);
+  return ksp;
+}
+
+bool
+GMPContentParent::DeallocPGMPDecryptorParent(PGMPDecryptorParent* aActor)
+{
+  GMPDecryptorParent* ksp = static_cast<GMPDecryptorParent*>(aActor);
+  NS_RELEASE(ksp);
+  return true;
+}
+
+PGMPAudioDecoderParent*
+GMPContentParent::AllocPGMPAudioDecoderParent()
+{
+  GMPAudioDecoderParent* vdp = new GMPAudioDecoderParent(this);
+  NS_ADDREF(vdp);
+  return vdp;
+}
+
+bool
+GMPContentParent::DeallocPGMPAudioDecoderParent(PGMPAudioDecoderParent* aActor)
+{
+  GMPAudioDecoderParent* vdp = static_cast<GMPAudioDecoderParent*>(aActor);
+  NS_RELEASE(vdp);
+  return true;
+}
+
+} // namespace gmp
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/GMPContentParent.h
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 GMPContentParent_h_
+#define GMPContentParent_h_
+
+#include "mozilla/gmp/PGMPContentParent.h"
+#include "nsISupportsImpl.h"
+
+class nsITimer;
+
+namespace mozilla {
+namespace gmp {
+
+class GeckoMediaPluginService;
+class GMPAudioDecoderParent;
+class GMPCapability;
+class GMPDecryptorParent;
+class GMPParent;
+class GMPVideoDecoderParent;
+class GMPVideoEncoderParent;
+
+class GMPContentParent final : public PGMPContentParent,
+                               public GMPSharedMem
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPContentParent)
+
+  explicit GMPContentParent(GMPParent* aParent = nullptr);
+
+  nsresult GetGMPVideoDecoder(GMPVideoDecoderParent** aGMPVD);
+  void VideoDecoderDestroyed(GMPVideoDecoderParent* aDecoder);
+
+  nsresult GetGMPVideoEncoder(GMPVideoEncoderParent** aGMPVE);
+  void VideoEncoderDestroyed(GMPVideoEncoderParent* aEncoder);
+
+  nsresult GetGMPDecryptor(GMPDecryptorParent** aGMPKS);
+  void DecryptorDestroyed(GMPDecryptorParent* aSession);
+
+  nsresult GetGMPAudioDecoder(GMPAudioDecoderParent** aGMPAD);
+  void AudioDecoderDestroyed(GMPAudioDecoderParent* aDecoder);
+
+  nsIThread* GMPThread();
+
+  // GMPSharedMem
+  virtual void CheckThread() override;
+
+  void SetDisplayName(const nsCString& aDisplayName)
+  {
+    mDisplayName = aDisplayName;
+  }
+  const nsCString& GetDisplayName()
+  {
+    return mDisplayName;
+  }
+  void SetPluginId(const nsCString& aPluginId)
+  {
+    mPluginId = aPluginId;
+  }
+  const nsCString& GetPluginId()
+  {
+    return mPluginId;
+  }
+
+private:
+  ~GMPContentParent();
+
+  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+  virtual PGMPVideoDecoderParent* AllocPGMPVideoDecoderParent() override;
+  virtual bool DeallocPGMPVideoDecoderParent(PGMPVideoDecoderParent* aActor) override;
+
+  virtual PGMPVideoEncoderParent* AllocPGMPVideoEncoderParent() override;
+  virtual bool DeallocPGMPVideoEncoderParent(PGMPVideoEncoderParent* aActor) override;
+
+  virtual PGMPDecryptorParent* AllocPGMPDecryptorParent() override;
+  virtual bool DeallocPGMPDecryptorParent(PGMPDecryptorParent* aActor) override;
+
+  virtual PGMPAudioDecoderParent* AllocPGMPAudioDecoderParent() override;
+  virtual bool DeallocPGMPAudioDecoderParent(PGMPAudioDecoderParent* aActor) override;
+
+  void CloseIfUnused();
+  // Needed because NS_NewRunnableMethod tried to use the class that the method
+  // lives on to store the receiver, but PGMPContentParent isn't refcounted.
+  void Close()
+  {
+    PGMPContentParent::Close();
+  }
+
+  nsTArray<nsRefPtr<GMPVideoDecoderParent>> mVideoDecoders;
+  nsTArray<nsRefPtr<GMPVideoEncoderParent>> mVideoEncoders;
+  nsTArray<nsRefPtr<GMPDecryptorParent>> mDecryptors;
+  nsTArray<nsRefPtr<GMPAudioDecoderParent>> mAudioDecoders;
+  nsCOMPtr<nsIThread> mGMPThread;
+  nsRefPtr<GMPParent> mParent;
+  nsCString mDisplayName;
+  nsCString mPluginId;
+};
+
+} // namespace gmp
+} // namespace mozilla
+
+#endif // GMPParent_h_
--- a/dom/media/gmp/GMPDecryptorChild.cpp
+++ b/dom/media/gmp/GMPDecryptorChild.cpp
@@ -13,17 +13,17 @@
 #define ON_GMP_THREAD() (mPlugin->GMPMessageLoop() == MessageLoop::current())
 
 #define CALL_ON_GMP_THREAD(_func, ...) \
   CallOnGMPThread(&GMPDecryptorChild::_func, __VA_ARGS__)
 
 namespace mozilla {
 namespace gmp {
 
-GMPDecryptorChild::GMPDecryptorChild(GMPChild* aPlugin,
+GMPDecryptorChild::GMPDecryptorChild(GMPContentChild* aPlugin,
                                      const nsTArray<uint8_t>& aPluginVoucher,
                                      const nsTArray<uint8_t>& aSandboxVoucher)
   : mSession(nullptr)
   , mPlugin(aPlugin)
   , mPluginVoucher(aPluginVoucher)
   , mSandboxVoucher(aSandboxVoucher)
 {
   MOZ_ASSERT(mPlugin);
--- a/dom/media/gmp/GMPDecryptorChild.h
+++ b/dom/media/gmp/GMPDecryptorChild.h
@@ -10,26 +10,26 @@
 #include "gmp-decryption.h"
 #include "mozilla/gmp/GMPTypes.h"
 #include "GMPEncryptedBufferDataImpl.h"
 #include <string>
 
 namespace mozilla {
 namespace gmp {
 
-class GMPChild;
+class GMPContentChild;
 
 class GMPDecryptorChild : public GMPDecryptorCallback
                         , public GMPDecryptorHost
                         , public PGMPDecryptorChild
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPDecryptorChild);
 
-  explicit GMPDecryptorChild(GMPChild* aPlugin,
+  explicit GMPDecryptorChild(GMPContentChild* aPlugin,
                              const nsTArray<uint8_t>& aPluginVoucher,
                              const nsTArray<uint8_t>& aSandboxVoucher);
 
   void Init(GMPDecryptor* aSession);
 
   // GMPDecryptorCallback
   virtual void SetSessionId(uint32_t aCreateSessionToken,
                             const char* aSessionId,
@@ -118,17 +118,17 @@ private:
   void CallMethod(MethodType, ParamType&&...);
 
   template<typename MethodType, typename... ParamType>
   void CallOnGMPThread(MethodType, ParamType&&...);
 
   // GMP's GMPDecryptor implementation.
   // Only call into this on the (GMP process) main thread.
   GMPDecryptor* mSession;
-  GMPChild* mPlugin;
+  GMPContentChild* mPlugin;
 
   // Reference to the vouchers owned by the GMPChild.
   const nsTArray<uint8_t>& mPluginVoucher;
   const nsTArray<uint8_t>& mSandboxVoucher;
 };
 
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/gmp/GMPDecryptorParent.cpp
+++ b/dom/media/gmp/GMPDecryptorParent.cpp
@@ -1,22 +1,22 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "GMPDecryptorParent.h"
-#include "GMPParent.h"
+#include "GMPContentParent.h"
 #include "mp4_demuxer/DecoderData.h"
 #include "mozilla/unused.h"
 
 namespace mozilla {
 namespace gmp {
 
-GMPDecryptorParent::GMPDecryptorParent(GMPParent* aPlugin)
+GMPDecryptorParent::GMPDecryptorParent(GMPContentParent* aPlugin)
   : mIsOpen(false)
   , mShuttingDown(false)
   , mPlugin(aPlugin)
   , mCallback(nullptr)
 #ifdef DEBUG
   , mGMPThread(aPlugin->GMPThread())
 #endif
 {
@@ -302,16 +302,23 @@ GMPDecryptorParent::RecvDecrypted(const 
   if (!mIsOpen) {
     NS_WARNING("Trying to use a dead GMP decrypter!");
     return false;
   }
   mCallback->Decrypted(aId, aErr, aBuffer);
   return true;
 }
 
+bool
+GMPDecryptorParent::RecvShutdown()
+{
+  Shutdown();
+  return true;
+}
+
 // Note: may be called via Terminated()
 void
 GMPDecryptorParent::Close()
 {
   MOZ_ASSERT(mGMPThread == NS_GetCurrentThread());
   // Consumer is done with us; we can shut down.  No more callbacks should
   // be made to mCallback. Note: do this before Shutdown()!
   mCallback = nullptr;
--- a/dom/media/gmp/GMPDecryptorParent.h
+++ b/dom/media/gmp/GMPDecryptorParent.h
@@ -12,25 +12,25 @@
 #include "GMPDecryptorProxy.h"
 namespace mp4_demuxer {
 class CryptoSample;
 }
 
 namespace mozilla {
 namespace gmp {
 
-class GMPParent;
+class GMPContentParent;
 
 class GMPDecryptorParent final : public GMPDecryptorProxy
                                , public PGMPDecryptorParent
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(GMPDecryptorParent)
 
-  explicit GMPDecryptorParent(GMPParent *aPlugin);
+  explicit GMPDecryptorParent(GMPContentParent *aPlugin);
 
   // GMPDecryptorProxy
 
   virtual const nsACString& GetPluginId() const override;
 
   virtual nsresult Init(GMPDecryptorProxyCallback* aCallback) override;
 
   virtual void CreateSession(uint32_t aCreateSessionToken,
@@ -99,22 +99,24 @@ private:
                                     const GMPMediaKeyStatus& aStatus) override;
 
   virtual bool RecvDecrypted(const uint32_t& aId,
                              const GMPErr& aErr,
                              InfallibleTArray<uint8_t>&& aBuffer) override;
 
   virtual bool RecvSetCaps(const uint64_t& aCaps) override;
 
+  virtual bool RecvShutdown() override;
+
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
   virtual bool Recv__delete__() override;
 
   bool mIsOpen;
   bool mShuttingDown;
-  nsRefPtr<GMPParent> mPlugin;
+  nsRefPtr<GMPContentParent> mPlugin;
   nsCString mPluginId;
   GMPDecryptorProxyCallback* mCallback;
 #ifdef DEBUG
   nsIThread* const mGMPThread;
 #endif
 };
 
 } // namespace gmp
--- a/dom/media/gmp/GMPParent.cpp
+++ b/dom/media/gmp/GMPParent.cpp
@@ -9,16 +9,17 @@
 #include "nsComponentManagerUtils.h"
 #include "nsIInputStream.h"
 #include "nsILineInputStream.h"
 #include "nsNetUtil.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsThreadUtils.h"
 #include "nsIRunnable.h"
 #include "mozIGeckoMediaPluginService.h"
+#include "mozilla/SyncRunnable.h"
 #include "mozilla/unused.h"
 #include "nsIObserverService.h"
 #include "GMPTimerParent.h"
 #include "runnable_utils.h"
 #if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
 #include "mozilla/SandboxInfo.h"
 #endif
 
@@ -48,16 +49,17 @@ extern PRLogModuleInfo* GetGMPLog();
 
 namespace gmp {
 
 GMPParent::GMPParent()
   : mState(GMPStateNotLoaded)
   , mProcess(nullptr)
   , mDeleteProcessOnlyOnUnload(false)
   , mAbnormalShutdownInProgress(false)
+  , mGMPContentChildCount(0)
   , mAsyncShutdownRequired(false)
   , mAsyncShutdownInProgress(false)
 #ifdef PR_LOGGING
   , mChildPid(0)
 #endif
 {
   LOGD("GMPParent ctor");
   // Use the parent address to identify it.
@@ -67,32 +69,26 @@ GMPParent::GMPParent()
 
 GMPParent::~GMPParent()
 {
   // Can't Close or Destroy the process here, since destruction is MainThread only
   MOZ_ASSERT(NS_IsMainThread());
   LOGD("GMPParent dtor");
 }
 
-void
-GMPParent::CheckThread()
-{
-  MOZ_ASSERT(mGMPThread == NS_GetCurrentThread());
-}
-
 nsresult
 GMPParent::CloneFrom(const GMPParent* aOther)
 {
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
   MOZ_ASSERT(aOther->mDirectory && aOther->mService, "null plugin directory");
   return Init(aOther->mService, aOther->mDirectory);
 }
 
 nsresult
-GMPParent::Init(GeckoMediaPluginService *aService, nsIFile* aPluginDir)
+GMPParent::Init(GeckoMediaPluginServiceParent* aService, nsIFile* aPluginDir)
 {
   MOZ_ASSERT(aPluginDir);
   MOZ_ASSERT(aService);
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
 
   mService = aService;
   mDirectory = aPluginDir;
 
@@ -183,18 +179,18 @@ GMPParent::LoadProcess()
   return NS_OK;
 }
 
 void
 AbortWaitingForGMPAsyncShutdown(nsITimer* aTimer, void* aClosure)
 {
   NS_WARNING("Timed out waiting for GMP async shutdown!");
   GMPParent* parent = reinterpret_cast<GMPParent*>(aClosure);
-  nsRefPtr<GeckoMediaPluginService> service =
-    GeckoMediaPluginService::GetGeckoMediaPluginService();
+  nsRefPtr<GeckoMediaPluginServiceParent> service =
+    GeckoMediaPluginServiceParent::GetSingleton();
   if (service) {
     service->AsyncShutdownComplete(parent);
   }
 }
 
 nsresult
 GMPParent::EnsureAsyncShutdownTimeoutSet()
 {
@@ -211,40 +207,46 @@ GMPParent::EnsureAsyncShutdownTimeoutSet
   // Set timer to abort waiting for plugin to shutdown if it takes
   // too long.
   rv = mAsyncShutdownTimeout->SetTarget(mGMPThread);
   if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
   }
 
   int32_t timeout = GMP_DEFAULT_ASYNC_SHUTDONW_TIMEOUT;
-  nsRefPtr<GeckoMediaPluginService> service =
-    GeckoMediaPluginService::GetGeckoMediaPluginService();
+  nsRefPtr<GeckoMediaPluginServiceParent> service =
+    GeckoMediaPluginServiceParent::GetSingleton();
   if (service) {
     timeout = service->AsyncShutdownTimeoutMs();
   }
   return mAsyncShutdownTimeout->InitWithFuncCallback(
     &AbortWaitingForGMPAsyncShutdown, this, timeout,
     nsITimer::TYPE_ONE_SHOT);
 }
 
+bool
+GMPParent::RecvPGMPContentChildDestroyed()
+{
+  --mGMPContentChildCount;
+  if (!IsUsed()) {
+    CloseIfUnused();
+  }
+  return true;
+}
+
 void
 GMPParent::CloseIfUnused()
 {
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
   LOGD("%s: mAsyncShutdownRequired=%d", __FUNCTION__, mAsyncShutdownRequired);
 
   if ((mDeleteProcessOnlyOnUnload ||
        mState == GMPStateLoaded ||
        mState == GMPStateUnloading) &&
-      mVideoDecoders.IsEmpty() &&
-      mVideoEncoders.IsEmpty() &&
-      mDecryptors.IsEmpty() &&
-      mAudioDecoders.IsEmpty()) {
-
+      !IsUsed()) {
     // Ensure all timers are killed.
     for (uint32_t i = mTimers.Length(); i > 0; i--) {
       mTimers[i - 1]->Shutdown();
     }
 
     if (mAsyncShutdownRequired) {
       if (!mAsyncShutdownInProgress) {
         LOGD("%s: sending async shutdown notification", __FUNCTION__);
@@ -282,80 +284,45 @@ GMPParent::AbortAsyncShutdown()
   nsRefPtr<GMPParent> kungFuDeathGrip(this);
   mService->AsyncShutdownComplete(this);
   mAsyncShutdownRequired = false;
   mAsyncShutdownInProgress = false;
   CloseIfUnused();
 }
 
 void
-GMPParent::AudioDecoderDestroyed(GMPAudioDecoderParent* aDecoder)
-{
-  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
-
-  MOZ_ALWAYS_TRUE(mAudioDecoders.RemoveElement(aDecoder));
-
-  // Recv__delete__ is on the stack, don't potentially destroy the top-level actor
-  // until after this has completed.
-  nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &GMPParent::CloseIfUnused);
-  NS_DispatchToCurrentThread(event);
-}
-
-void
 GMPParent::CloseActive(bool aDieWhenUnloaded)
 {
   LOGD("%s: state %d", __FUNCTION__, mState);
+  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
+
   if (aDieWhenUnloaded) {
     mDeleteProcessOnlyOnUnload = true; // don't allow this to go back...
   }
   if (mState == GMPStateLoaded) {
     mState = GMPStateUnloading;
   }
-
-  // Invalidate and remove any remaining API objects.
-  for (uint32_t i = mVideoDecoders.Length(); i > 0; i--) {
-    mVideoDecoders[i - 1]->Shutdown();
-  }
-
-  for (uint32_t i = mVideoEncoders.Length(); i > 0; i--) {
-    mVideoEncoders[i - 1]->Shutdown();
-  }
-
-  for (uint32_t i = mDecryptors.Length(); i > 0; i--) {
-    mDecryptors[i - 1]->Shutdown();
+  if (mState != GMPStateNotLoaded && IsUsed()) {
+    unused << SendCloseActive();
   }
-
-  for (uint32_t i = mAudioDecoders.Length(); i > 0; i--) {
-    mAudioDecoders[i - 1]->Shutdown();
-  }
-
-  // Note: we don't shutdown timers here, we do that in CloseIfUnused(),
-  // as there are multiple entry points to CloseIfUnused().
-
-  // Note: We don't shutdown storage API objects here, as they need to
-  // work during async shutdown of GMPs.
-
-  // Note: the shutdown of the codecs is async!  don't kill
-  // the plugin-container until they're all safely shut down via
-  // CloseIfUnused();
-  CloseIfUnused();
 }
 
 void
 GMPParent::Shutdown()
 {
   LOGD("%s", __FUNCTION__);
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
 
   MOZ_ASSERT(!mAsyncShutdownTimeout, "Should have canceled shutdown timeout");
 
   if (mAbnormalShutdownInProgress) {
     return;
   }
-  MOZ_ASSERT(mVideoDecoders.IsEmpty() && mVideoEncoders.IsEmpty());
+
+  MOZ_ASSERT(!IsUsed());
   if (mState == GMPStateNotLoaded || mState == GMPStateClosing) {
     return;
   }
 
   DeleteProcess();
   // XXX Get rid of mDeleteProcessOnlyOnUnload and this code when
   // Bug 1043671 is fixed
   if (!mDeleteProcessOnlyOnUnload) {
@@ -401,88 +368,16 @@ GMPParent::DeleteProcess()
   mState = GMPStateNotLoaded;
 
   NS_DispatchToMainThread(
     new NotifyGMPShutdownTask(NS_ConvertUTF8toUTF16(mNodeId)),
     NS_DISPATCH_NORMAL);
 
 }
 
-void
-GMPParent::VideoDecoderDestroyed(GMPVideoDecoderParent* aDecoder)
-{
-  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
-
-  // If the constructor fails, we'll get called before it's added
-  unused << NS_WARN_IF(!mVideoDecoders.RemoveElement(aDecoder));
-
-  if (mVideoDecoders.IsEmpty() &&
-      mVideoEncoders.IsEmpty()) {
-    // Recv__delete__ is on the stack, don't potentially destroy the top-level actor
-    // until after this has completed.
-    nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &GMPParent::CloseIfUnused);
-    NS_DispatchToCurrentThread(event);
-  }
-}
-
-void
-GMPParent::VideoEncoderDestroyed(GMPVideoEncoderParent* aEncoder)
-{
-  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
-
-  // If the constructor fails, we'll get called before it's added
-  unused << NS_WARN_IF(!mVideoEncoders.RemoveElement(aEncoder));
-
-  if (mVideoDecoders.IsEmpty() &&
-      mVideoEncoders.IsEmpty()) {
-    // Recv__delete__ is on the stack, don't potentially destroy the top-level actor
-    // until after this has completed.
-    nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &GMPParent::CloseIfUnused);
-    NS_DispatchToCurrentThread(event);
-  }
-}
-
-void
-GMPParent::DecryptorDestroyed(GMPDecryptorParent* aSession)
-{
-  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
-
-  MOZ_ALWAYS_TRUE(mDecryptors.RemoveElement(aSession));
-
-  // Recv__delete__ is on the stack, don't potentially destroy the top-level actor
-  // until after this has completed.
-  if (mDecryptors.IsEmpty()) {
-    nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &GMPParent::CloseIfUnused);
-    NS_DispatchToCurrentThread(event);
-  }
-}
-
-nsresult
-GMPParent::GetGMPDecryptor(GMPDecryptorParent** aGMPDP)
-{
-  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
-
-  if (!EnsureProcessLoaded()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  PGMPDecryptorParent* pdp = SendPGMPDecryptorConstructor();
-  if (!pdp) {
-    return NS_ERROR_FAILURE;
-  }
-  GMPDecryptorParent* dp = static_cast<GMPDecryptorParent*>(pdp);
-  // This addref corresponds to the Proxy pointer the consumer is returned.
-  // It's dropped by calling Close() on the interface.
-  NS_ADDREF(dp);
-  mDecryptors.AppendElement(dp);
-  *aGMPDP = dp;
-
-  return NS_OK;
-}
-
 GMPState
 GMPParent::State() const
 {
   return mState;
 }
 
 // Not changing to use mService since we'll be removing it
 nsIThread*
@@ -534,87 +429,16 @@ GMPParent::EnsureProcessLoaded()
     return false;
   }
 
   nsresult rv = LoadProcess();
 
   return NS_SUCCEEDED(rv);
 }
 
-nsresult
-GMPParent::GetGMPAudioDecoder(GMPAudioDecoderParent** aGMPAD)
-{
-  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
-
-  if (!EnsureProcessLoaded()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  PGMPAudioDecoderParent* pvap = SendPGMPAudioDecoderConstructor();
-  if (!pvap) {
-    return NS_ERROR_FAILURE;
-  }
-  GMPAudioDecoderParent* vap = static_cast<GMPAudioDecoderParent*>(pvap);
-  // This addref corresponds to the Proxy pointer the consumer is returned.
-  // It's dropped by calling Close() on the interface.
-  NS_ADDREF(vap);
-  *aGMPAD = vap;
-  mAudioDecoders.AppendElement(vap);
-
-  return NS_OK;
-}
-
-nsresult
-GMPParent::GetGMPVideoDecoder(GMPVideoDecoderParent** aGMPVD)
-{
-  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
-
-  if (!EnsureProcessLoaded()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  // returned with one anonymous AddRef that locks it until Destroy
-  PGMPVideoDecoderParent* pvdp = SendPGMPVideoDecoderConstructor();
-  if (!pvdp) {
-    return NS_ERROR_FAILURE;
-  }
-  GMPVideoDecoderParent *vdp = static_cast<GMPVideoDecoderParent*>(pvdp);
-  // This addref corresponds to the Proxy pointer the consumer is returned.
-  // It's dropped by calling Close() on the interface.
-  NS_ADDREF(vdp);
-  *aGMPVD = vdp;
-  mVideoDecoders.AppendElement(vdp);
-
-  return NS_OK;
-}
-
-nsresult
-GMPParent::GetGMPVideoEncoder(GMPVideoEncoderParent** aGMPVE)
-{
-  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
-
-  if (!EnsureProcessLoaded()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  // returned with one anonymous AddRef that locks it until Destroy
-  PGMPVideoEncoderParent* pvep = SendPGMPVideoEncoderConstructor();
-  if (!pvep) {
-    return NS_ERROR_FAILURE;
-  }
-  GMPVideoEncoderParent *vep = static_cast<GMPVideoEncoderParent*>(pvep);
-  // This addref corresponds to the Proxy pointer the consumer is returned.
-  // It's dropped by calling Close() on the interface.
-  NS_ADDREF(vep);
-  *aGMPVE = vep;
-  mVideoEncoders.AppendElement(vep);
-
-  return NS_OK;
-}
-
 #ifdef MOZ_CRASHREPORTER
 void
 GMPParent::WriteExtraDataForMinidump(CrashReporter::AnnotationTable& notes)
 {
   notes.Put(NS_LITERAL_CSTRING("GMPPlugin"), NS_LITERAL_CSTRING("1"));
   notes.Put(NS_LITERAL_CSTRING("PluginFilename"),
                                NS_ConvertUTF16toUTF8(mName));
   notes.Put(NS_LITERAL_CSTRING("PluginName"), mDisplayName);
@@ -719,80 +543,16 @@ GMPParent::AllocPCrashReporterParent(con
 
 bool
 GMPParent::DeallocPCrashReporterParent(PCrashReporterParent* aCrashReporter)
 {
   delete aCrashReporter;
   return true;
 }
 
-PGMPVideoDecoderParent*
-GMPParent::AllocPGMPVideoDecoderParent()
-{
-  GMPVideoDecoderParent* vdp = new GMPVideoDecoderParent(this);
-  NS_ADDREF(vdp);
-  return vdp;
-}
-
-bool
-GMPParent::DeallocPGMPVideoDecoderParent(PGMPVideoDecoderParent* aActor)
-{
-  GMPVideoDecoderParent* vdp = static_cast<GMPVideoDecoderParent*>(aActor);
-  NS_RELEASE(vdp);
-  return true;
-}
-
-PGMPVideoEncoderParent*
-GMPParent::AllocPGMPVideoEncoderParent()
-{
-  GMPVideoEncoderParent* vep = new GMPVideoEncoderParent(this);
-  NS_ADDREF(vep);
-  return vep;
-}
-
-bool
-GMPParent::DeallocPGMPVideoEncoderParent(PGMPVideoEncoderParent* aActor)
-{
-  GMPVideoEncoderParent* vep = static_cast<GMPVideoEncoderParent*>(aActor);
-  NS_RELEASE(vep);
-  return true;
-}
-
-PGMPDecryptorParent*
-GMPParent::AllocPGMPDecryptorParent()
-{
-  GMPDecryptorParent* ksp = new GMPDecryptorParent(this);
-  NS_ADDREF(ksp);
-  return ksp;
-}
-
-bool
-GMPParent::DeallocPGMPDecryptorParent(PGMPDecryptorParent* aActor)
-{
-  GMPDecryptorParent* ksp = static_cast<GMPDecryptorParent*>(aActor);
-  NS_RELEASE(ksp);
-  return true;
-}
-
-PGMPAudioDecoderParent*
-GMPParent::AllocPGMPAudioDecoderParent()
-{
-  GMPAudioDecoderParent* vdp = new GMPAudioDecoderParent(this);
-  NS_ADDREF(vdp);
-  return vdp;
-}
-
-bool
-GMPParent::DeallocPGMPAudioDecoderParent(PGMPAudioDecoderParent* aActor)
-{
-  GMPAudioDecoderParent* vdp = static_cast<GMPAudioDecoderParent*>(aActor);
-  NS_RELEASE(vdp);
-  return true;
-}
-
 PGMPStorageParent*
 GMPParent::AllocPGMPStorageParent()
 {
   GMPStorageParent* p = new GMPStorageParent(mNodeId, this);
   mStorage.AppendElement(p); // Addrefs, released in DeallocPGMPStorageParent.
   return p;
 }
 
@@ -1019,17 +779,17 @@ GMPParent::GetDisplayName() const
 }
 
 const nsCString&
 GMPParent::GetVersion() const
 {
   return mVersion;
 }
 
-const nsACString&
+const nsCString&
 GMPParent::GetPluginId() const
 {
   return mPluginId;
 }
 
 bool
 GMPParent::RecvAsyncShutdownRequired()
 {
@@ -1048,13 +808,114 @@ GMPParent::RecvAsyncShutdownComplete()
 {
   LOGD("%s", __FUNCTION__);
 
   MOZ_ASSERT(mAsyncShutdownRequired);
   AbortAsyncShutdown();
   return true;
 }
 
+class RunCreateContentParentCallbacks : public nsRunnable
+{
+public:
+  explicit RunCreateContentParentCallbacks(GMPContentParent* aGMPContentParent)
+    : mGMPContentParent(aGMPContentParent)
+  {
+  }
+
+  void TakeCallbacks(nsTArray<UniquePtr<GetGMPContentParentCallback>>& aCallbacks)
+  {
+    mCallbacks.SwapElements(aCallbacks);
+  }
+
+  NS_IMETHOD
+  Run()
+  {
+    for (uint32_t i = 0, length = mCallbacks.Length(); i < length; ++i) {
+      mCallbacks[i]->Done(mGMPContentParent);
+    }
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<GMPContentParent> mGMPContentParent;
+  nsTArray<UniquePtr<GetGMPContentParentCallback>> mCallbacks;
+};
+
+PGMPContentParent*
+GMPParent::AllocPGMPContentParent(Transport* aTransport, ProcessId aOtherPid)
+{
+  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
+  MOZ_ASSERT(!mGMPContentParent);
+
+  mGMPContentParent = new GMPContentParent(this);
+  mGMPContentParent->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop(),
+                          ipc::ParentSide);
+
+  nsRefPtr<RunCreateContentParentCallbacks> runCallbacks =
+    new RunCreateContentParentCallbacks(mGMPContentParent);
+  runCallbacks->TakeCallbacks(mCallbacks);
+  NS_DispatchToCurrentThread(runCallbacks);
+  MOZ_ASSERT(mCallbacks.IsEmpty());
+
+  return mGMPContentParent;
+}
+
+bool
+GMPParent::GetGMPContentParent(UniquePtr<GetGMPContentParentCallback>&& aCallback)
+{
+  LOGD("%s %p", __FUNCTION__, this);
+  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
+
+  if (mGMPContentParent) {
+    aCallback->Done(mGMPContentParent);
+  } else {
+    mCallbacks.AppendElement(Move(aCallback));
+    // If we don't have a GMPContentParent and we try to get one for the first
+    // time (mCallbacks.Length() == 1) then call PGMPContent::Open. If more
+    // calls to GetGMPContentParent happen before mGMPContentParent has been
+    // set then we should just store them, so that they get called when we set
+    // mGMPContentParent as a result of the PGMPContent::Open call.
+    if (mCallbacks.Length() == 1) {
+      if (!EnsureProcessLoaded() || !PGMPContent::Open(this)) {
+        return false;
+      }
+      // We want to increment this as soon as possible, to avoid that we'd try
+      // to shut down the GMP process while we're still trying to get a
+      // PGMPContentParent actor.
+      ++mGMPContentChildCount;
+    }
+  }
+  return true;
+}
+
+already_AddRefed<GMPContentParent>
+GMPParent::ForgetGMPContentParent()
+{
+  MOZ_ASSERT(mCallbacks.IsEmpty());
+  return Move(mGMPContentParent.forget());
+}
+
+bool
+GMPParent::EnsureProcessLoaded(base::ProcessId* aID)
+{
+  if (!EnsureProcessLoaded()) {
+    return false;
+  }
+  *aID = OtherPid();
+  return true;
+}
+
+bool
+GMPParent::Bridge(GMPServiceParent* aGMPServiceParent)
+{
+  if (!PGMPContent::Bridge(aGMPServiceParent, this)) {
+    return false;
+  }
+  ++mGMPContentChildCount;
+  return true;
+}
+
 } // namespace gmp
 } // namespace mozilla
 
 #undef LOG
 #undef LOGD
--- a/dom/media/gmp/GMPParent.h
+++ b/dom/media/gmp/GMPParent.h
@@ -2,17 +2,17 @@
 /* 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 GMPParent_h_
 #define GMPParent_h_
 
 #include "GMPProcessParent.h"
-#include "GMPService.h"
+#include "GMPServiceParent.h"
 #include "GMPAudioDecoderParent.h"
 #include "GMPDecryptorParent.h"
 #include "GMPVideoDecoderParent.h"
 #include "GMPVideoEncoderParent.h"
 #include "GMPTimerParent.h"
 #include "GMPStorageParent.h"
 #include "mozilla/gmp/PGMPParent.h"
 #include "nsCOMPtr.h"
@@ -49,25 +49,40 @@ public:
 
 enum GMPState {
   GMPStateNotLoaded,
   GMPStateLoaded,
   GMPStateUnloading,
   GMPStateClosing
 };
 
-class GMPParent final : public PGMPParent,
-                        public GMPSharedMem
+class GMPContentParent;
+
+class GetGMPContentParentCallback
+{
+public:
+  GetGMPContentParentCallback()
+  {
+    MOZ_COUNT_CTOR(GetGMPContentParentCallback);
+  };
+  virtual ~GetGMPContentParentCallback()
+  {
+    MOZ_COUNT_DTOR(GetGMPContentParentCallback);
+  };
+  virtual void Done(GMPContentParent* aGMPContentParent) = 0;
+};
+
+class GMPParent final : public PGMPParent
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(GMPParent)
 
   GMPParent();
 
-  nsresult Init(GeckoMediaPluginService *aService, nsIFile* aPluginDir);
+  nsresult Init(GeckoMediaPluginServiceParent* aService, nsIFile* aPluginDir);
   nsresult CloneFrom(const GMPParent* aOther);
 
   void Crash();
 
   nsresult LoadProcess();
 
   // Called internally to close this if we don't need it
   void CloseIfUnused();
@@ -79,28 +94,16 @@ public:
   // Called by the GMPService to forcibly close active de/encoders at shutdown
   void Shutdown();
 
   // This must not be called while we're in the middle of abnormal ActorDestroy
   void DeleteProcess();
 
   bool SupportsAPI(const nsCString& aAPI, const nsCString& aTag);
 
-  nsresult GetGMPVideoDecoder(GMPVideoDecoderParent** aGMPVD);
-  void VideoDecoderDestroyed(GMPVideoDecoderParent* aDecoder);
-
-  nsresult GetGMPVideoEncoder(GMPVideoEncoderParent** aGMPVE);
-  void VideoEncoderDestroyed(GMPVideoEncoderParent* aEncoder);
-
-  nsresult GetGMPDecryptor(GMPDecryptorParent** aGMPKS);
-  void DecryptorDestroyed(GMPDecryptorParent* aSession);
-
-  nsresult GetGMPAudioDecoder(GMPAudioDecoderParent** aGMPAD);
-  void AudioDecoderDestroyed(GMPAudioDecoderParent* aDecoder);
-
   GMPState State() const;
   nsIThread* GMPThread();
 
   // A GMP can either be a single instance shared across all NodeIds (like
   // in the OpenH264 case), or we can require a new plugin instance for every
   // NodeIds running the plugin (as in the EME plugin case).
   //
   // A NodeId is a hash of the ($urlBarOrigin, $ownerDocOrigin) pair.
@@ -112,97 +115,100 @@ public:
   // be shared across NodeIds.
 
   // Specifies that a GMP can only work with the specified NodeIds.
   void SetNodeId(const nsACString& aNodeId);
   const nsACString& GetNodeId() const { return mNodeId; }
 
   const nsCString& GetDisplayName() const;
   const nsCString& GetVersion() const;
-  const nsACString& GetPluginId() const;
+  const nsCString& GetPluginId() const;
 
   // Returns true if a plugin can be or is being used across multiple NodeIds.
   bool CanBeSharedCrossNodeIds() const;
 
   // A GMP can be used from a NodeId if it's already been set to work with
   // that NodeId, or if it's not been set to work with any NodeId and has
   // not yet been loaded (i.e. it's not shared across NodeIds).
   bool CanBeUsedFrom(const nsACString& aNodeId) const;
 
   already_AddRefed<nsIFile> GetDirectory() {
     return nsCOMPtr<nsIFile>(mDirectory).forget();
   }
 
-  // GMPSharedMem
-  virtual void CheckThread() override;
+  void AbortAsyncShutdown();
 
-  void AbortAsyncShutdown();
+  bool GetGMPContentParent(UniquePtr<GetGMPContentParentCallback>&& aCallback);
+  already_AddRefed<GMPContentParent> ForgetGMPContentParent();
+
+  bool EnsureProcessLoaded(base::ProcessId* aID);
+
+  bool Bridge(GMPServiceParent* aGMPServiceParent);
 
 private:
   ~GMPParent();
-  nsRefPtr<GeckoMediaPluginService> mService;
+  nsRefPtr<GeckoMediaPluginServiceParent> mService;
   bool EnsureProcessLoaded();
   nsresult ReadGMPMetaData();
 #ifdef MOZ_CRASHREPORTER
   void WriteExtraDataForMinidump(CrashReporter::AnnotationTable& notes);
   void GetCrashID(nsString& aResult);
 #endif
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   virtual PCrashReporterParent* AllocPCrashReporterParent(const NativeThreadId& aThread) override;
   virtual bool DeallocPCrashReporterParent(PCrashReporterParent* aCrashReporter) override;
 
-  virtual PGMPVideoDecoderParent* AllocPGMPVideoDecoderParent() override;
-  virtual bool DeallocPGMPVideoDecoderParent(PGMPVideoDecoderParent* aActor) override;
-
-  virtual PGMPVideoEncoderParent* AllocPGMPVideoEncoderParent() override;
-  virtual bool DeallocPGMPVideoEncoderParent(PGMPVideoEncoderParent* aActor) override;
-
-  virtual PGMPDecryptorParent* AllocPGMPDecryptorParent() override;
-  virtual bool DeallocPGMPDecryptorParent(PGMPDecryptorParent* aActor) override;
-
-  virtual PGMPAudioDecoderParent* AllocPGMPAudioDecoderParent() override;
-  virtual bool DeallocPGMPAudioDecoderParent(PGMPAudioDecoderParent* aActor) override;
-
   virtual bool RecvPGMPStorageConstructor(PGMPStorageParent* actor) override;
   virtual PGMPStorageParent* AllocPGMPStorageParent() override;
   virtual bool DeallocPGMPStorageParent(PGMPStorageParent* aActor) override;
 
+  virtual PGMPContentParent* AllocPGMPContentParent(Transport* aTransport,
+                                                    ProcessId aOtherPid) override;
+
   virtual bool RecvPGMPTimerConstructor(PGMPTimerParent* actor) override;
   virtual PGMPTimerParent* AllocPGMPTimerParent() override;
   virtual bool DeallocPGMPTimerParent(PGMPTimerParent* aActor) override;
 
   virtual bool RecvAsyncShutdownComplete() override;
   virtual bool RecvAsyncShutdownRequired() override;
 
+  virtual bool RecvPGMPContentChildDestroyed() override;
+  bool IsUsed()
+  {
+    return mGMPContentChildCount > 0;
+  }
+
+
   nsresult EnsureAsyncShutdownTimeoutSet();
 
   GMPState mState;
   nsCOMPtr<nsIFile> mDirectory; // plugin directory on disk
   nsString mName; // base name of plugin on disk, UTF-16 because used for paths
   nsCString mDisplayName; // name of plugin displayed to users
   nsCString mDescription; // description of plugin for display to users
   nsCString mVersion;
   nsCString mPluginId;
   nsTArray<nsAutoPtr<GMPCapability>> mCapabilities;
   GMPProcessParent* mProcess;
   bool mDeleteProcessOnlyOnUnload;
   bool mAbnormalShutdownInProgress;
 
-  nsTArray<nsRefPtr<GMPVideoDecoderParent>> mVideoDecoders;
-  nsTArray<nsRefPtr<GMPVideoEncoderParent>> mVideoEncoders;
-  nsTArray<nsRefPtr<GMPDecryptorParent>> mDecryptors;
-  nsTArray<nsRefPtr<GMPAudioDecoderParent>> mAudioDecoders;
   nsTArray<nsRefPtr<GMPTimerParent>> mTimers;
   nsTArray<nsRefPtr<GMPStorageParent>> mStorage;
   nsCOMPtr<nsIThread> mGMPThread;
   nsCOMPtr<nsITimer> mAsyncShutdownTimeout; // GMP Thread only.
   // NodeId the plugin is assigned to, or empty if the the plugin is not
   // assigned to a NodeId.
   nsAutoCString mNodeId;
+  // This is used for GMP content in the parent, there may be more of these in
+  // the content processes.
+  nsRefPtr<GMPContentParent> mGMPContentParent;
+  nsTArray<UniquePtr<GetGMPContentParentCallback>> mCallbacks;
+  uint32_t mGMPContentChildCount;
 
   bool mAsyncShutdownRequired;
   bool mAsyncShutdownInProgress;
 
 #ifdef PR_LOGGING
   int mChildPid;
 #endif
 };
--- a/dom/media/gmp/GMPService.cpp
+++ b/dom/media/gmp/GMPService.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "GMPService.h"
+#include "GMPServiceParent.h"
+#include "GMPServiceChild.h"
 #include "prio.h"
 #include "prlog.h"
 #include "GMPParent.h"
 #include "GMPVideoDecoderParent.h"
 #include "nsIObserverService.h"
 #include "GeckoChildProcessHost.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ClearOnShutdown.h"
@@ -59,17 +60,16 @@ GetGMPLog()
 
 #ifdef __CLASS__
 #undef __CLASS__
 #endif
 #define __CLASS__ "GMPService"
 
 namespace gmp {
 
-static const uint32_t NodeIdSaltLength = 32;
 static StaticRefPtr<GeckoMediaPluginService> sSingletonService;
 
 class GMPServiceCreateHelper final : public nsRunnable
 {
   nsRefPtr<GeckoMediaPluginService> mService;
 
 public:
   static already_AddRefed<GeckoMediaPluginService>
@@ -103,25 +103,33 @@ private:
     MOZ_ASSERT(!mService);
   }
 
   static already_AddRefed<GeckoMediaPluginService>
   GetOrCreateOnMainThread()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
-    nsRefPtr<GeckoMediaPluginService> service = sSingletonService.get();
-    if (!service) {
-      service = new GeckoMediaPluginService();
-      service->Init();
+    if (!sSingletonService) {
+      if (XRE_GetProcessType() == GeckoProcessType_Default) {
+        nsRefPtr<GeckoMediaPluginServiceParent> service =
+          new GeckoMediaPluginServiceParent();
+        service->Init();
+        sSingletonService = service;
+      } else {
+        nsRefPtr<GeckoMediaPluginServiceChild> service =
+          new GeckoMediaPluginServiceChild();
+        service->Init();
+        sSingletonService = service;
+      }
 
-      sSingletonService = service;
       ClearOnShutdown(&sSingletonService);
     }
 
+    nsRefPtr<GeckoMediaPluginService> service = sSingletonService.get();
     return service.forget();
   }
 
   NS_IMETHOD
   Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
@@ -133,46 +141,26 @@ private:
 already_AddRefed<GeckoMediaPluginService>
 GeckoMediaPluginService::GetGeckoMediaPluginService()
 {
   return GMPServiceCreateHelper::GetOrCreate();
 }
 
 NS_IMPL_ISUPPORTS(GeckoMediaPluginService, mozIGeckoMediaPluginService, nsIObserver)
 
-static int32_t sMaxAsyncShutdownWaitMs = 0;
-static bool sHaveSetTimeoutPrefCache = false;
-
 GeckoMediaPluginService::GeckoMediaPluginService()
   : mMutex("GeckoMediaPluginService::mMutex")
-  , mShuttingDown(false)
+  , mGMPThreadShutdown(false)
   , mShuttingDownOnGMPThread(false)
-  , mScannedPluginOnDisk(false)
-  , mWaitingForPluginsAsyncShutdown(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  if (!sHaveSetTimeoutPrefCache) {
-    sHaveSetTimeoutPrefCache = true;
-    Preferences::AddIntVarCache(&sMaxAsyncShutdownWaitMs,
-                                "media.gmp.async-shutdown-timeout",
-                                GMP_DEFAULT_ASYNC_SHUTDONW_TIMEOUT);
-  }
 }
 
 GeckoMediaPluginService::~GeckoMediaPluginService()
 {
-  MOZ_ASSERT(mPlugins.IsEmpty());
-  MOZ_ASSERT(mAsyncShutdownPlugins.IsEmpty());
-}
-
-int32_t
-GeckoMediaPluginService::AsyncShutdownTimeoutMs()
-{
-  MOZ_ASSERT(sHaveSetTimeoutPrefCache);
-  return sMaxAsyncShutdownWaitMs;
 }
 
 void
 GeckoMediaPluginService::RemoveObsoletePluginCrashCallbacks()
 {
   MOZ_ASSERT(NS_IsMainThread());
   for (size_t i = mPluginCrashCallbacks.Length(); i != 0; --i) {
     nsRefPtr<PluginCrashCallback>& callback = mPluginCrashCallbacks[i - 1];
@@ -231,206 +219,37 @@ GeckoMediaPluginService::RunPluginCrashC
 
 nsresult
 GeckoMediaPluginService::Init()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
   MOZ_ASSERT(obsService);
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "profile-change-teardown", false)));
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false)));
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "last-pb-context-exited", false)));
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "browser:purge-session-history", false)));
-
-#ifdef DEBUG
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "mediakeys-request", false)));
-#endif
-
-  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
-  if (prefs) {
-    prefs->AddObserver("media.gmp.plugin.crash", this, false);
-  }
-
-  nsresult rv = InitStorage();
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
 
   // Kick off scanning for plugins
   nsCOMPtr<nsIThread> thread;
   return GetThread(getter_AddRefs(thread));
 }
 
-
-nsresult
-GeckoMediaPluginService::InitStorage()
+void
+GeckoMediaPluginService::ShutdownGMPThread()
 {
-  MOZ_ASSERT(NS_IsMainThread());
-
-  // GMP storage should be used in the chrome process only.
-  if (XRE_GetProcessType() != GeckoProcessType_Default) {
-    return NS_OK;
-  }
-
-  // Directory service is main thread only, so cache the profile dir here
-  // so that we can use it off main thread.
-#ifdef MOZ_WIDGET_GONK
-  nsresult rv = NS_NewLocalFile(NS_LITERAL_STRING("/data/b2g/mozilla"), false, getter_AddRefs(mStorageBaseDir));
-#else
-  nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mStorageBaseDir));
-#endif
-
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = mStorageBaseDir->AppendNative(NS_LITERAL_CSTRING("gmp"));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = mStorageBaseDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
-  if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)) {
-    return rv;
+  LOGD(("%s::%s", __CLASS__, __FUNCTION__));
+  nsCOMPtr<nsIThread> gmpThread;
+  {
+    MutexAutoLock lock(mMutex);
+    mGMPThreadShutdown = true;
+    mGMPThread.swap(gmpThread);
   }
 
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-GeckoMediaPluginService::Observe(nsISupports* aSubject,
-                                 const char* aTopic,
-                                 const char16_t* aSomeData)
-{
-  LOGD(("%s::%s topic='%s' data='%s'", __CLASS__, __FUNCTION__,
-       aTopic, NS_ConvertUTF16toUTF8(aSomeData).get()));
-  if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
-    nsCOMPtr<nsIPrefBranch> branch( do_QueryInterface(aSubject) );
-    if (branch) {
-      bool crashNow = false;
-      if (NS_LITERAL_STRING("media.gmp.plugin.crash").Equals(aSomeData)) {
-        branch->GetBoolPref("media.gmp.plugin.crash",  &crashNow);
-      }
-      if (crashNow) {
-        nsCOMPtr<nsIThread> gmpThread;
-        {
-          MutexAutoLock lock(mMutex);
-          gmpThread = mGMPThread;
-        }
-        if (gmpThread) {
-          gmpThread->Dispatch(WrapRunnable(this, &GeckoMediaPluginService::CrashPlugins),
-                              NS_DISPATCH_NORMAL);
-        }
-      }
-    }
-  } else if (!strcmp("profile-change-teardown", aTopic)) {
-
-    // How shutdown works:
-    //
-    // Some GMPs require time to do bookkeeping upon shutdown. These GMPs
-    // need to be given time to access storage during shutdown. To signal
-    // that time to shutdown is required, those GMPs implement the
-    // GMPAsyncShutdown interface.
-    //
-    // When we startup the child process, we query the GMP for the
-    // GMPAsyncShutdown interface, and if it's present, we send a message
-    // back to the GMPParent, which then registers the GMPParent by calling
-    // GMPService::AsyncShutdownNeeded().
-    //
-    // On shutdown, we set mWaitingForPluginsAsyncShutdown to true, and then
-    // call UnloadPlugins on the GMPThread, and process events on the main
-    // thread until an event sets mWaitingForPluginsAsyncShutdown=false on
-    // the main thread.
-    //
-    // UnloadPlugins() sends close messages for all plugins' API objects to
-    // the GMP interfaces in the child process, and then sends the async
-    // shutdown notifications to child GMPs. When a GMP has completed its
-    // shutdown, it calls GMPAsyncShutdownHost::ShutdownComplete(), which
-    // sends a message back to the parent, which calls
-    // GMPService::AsyncShutdownComplete(). If all plugins requiring async
-    // shutdown have called AsyncShutdownComplete() we stick an event on the
-    // main thread to set mWaitingForPluginsAsyncShutdown=false. We must use
-    // an event to do this, as we must ensure the main thread processes an
-    // event to run its loop. This will unblock the main thread, and shutdown
-    // of other components will proceed.
-    //
-    // We set a timer in UnloadPlugins(), and abort waiting for async
-    // shutdown if the GMPs are taking too long to shutdown.
-    //
-    // We shutdown in "profile-change-teardown", as the profile dir is
-    // still writable then, and it's required for GMPStorage. We block the
-    // shutdown process by spinning the main thread event loop until all GMPs
-    // have shutdown, or timeout has occurred.
-    //
-    // GMPStorage needs to work up until the shutdown-complete notification
-    // arrives from the GMP process.
-
-    mWaitingForPluginsAsyncShutdown = true;
-
-    nsCOMPtr<nsIThread> gmpThread;
-    {
-      MutexAutoLock lock(mMutex);
-      MOZ_ASSERT(!mShuttingDown);
-      mShuttingDown = true;
-      gmpThread = mGMPThread;
-    }
-
-    if (gmpThread) {
-      gmpThread->Dispatch(
-        NS_NewRunnableMethod(this, &GeckoMediaPluginService::UnloadPlugins),
-        NS_DISPATCH_NORMAL);
-    } else {
-      MOZ_ASSERT(mPlugins.IsEmpty());
-    }
-
-    // Wait for plugins to do async shutdown...
-    while (mWaitingForPluginsAsyncShutdown) {
-      NS_ProcessNextEvent(NS_GetCurrentThread(), true);
-    }
-
-  } else if (!strcmp(NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, aTopic)) {
-    nsCOMPtr<nsIThread> gmpThread;
-    {
-      MutexAutoLock lock(mMutex);
-      // XXX The content process never gets profile-change-teardown, so mShuttingDown
-      // will always be false here. GMPService needs to be proxied to the parent.
-      // See bug 1057908.
-      MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default || mShuttingDown);
-      mGMPThread.swap(gmpThread);
-    }
-
-    if (gmpThread) {
-      gmpThread->Shutdown();
-    }
-  } else if (!strcmp("last-pb-context-exited", aTopic)) {
-    // When Private Browsing mode exits, all we need to do is clear
-    // mTempNodeIds. This drops all the node ids we've cached in memory
-    // for PB origin-pairs. If we try to open an origin-pair for non-PB
-    // mode, we'll get the NodeId salt stored on-disk, and if we try to
-    // open a PB mode origin-pair, we'll re-generate new salt.
-    mTempNodeIds.Clear();
-  } else if (!strcmp("browser:purge-session-history", aTopic)) {
-    // Clear everything!
-    if (!aSomeData || nsDependentString(aSomeData).IsEmpty()) {
-      return GMPDispatch(NS_NewRunnableMethod(
-          this, &GeckoMediaPluginService::ClearStorage));
-    }
-
-    // Clear nodeIds/records modified after |t|.
-    nsresult rv;
-    PRTime t = nsDependentString(aSomeData).ToInteger64(&rv, 10);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-    return GMPDispatch(NS_NewRunnableMethodWithArg<PRTime>(
-        this, &GeckoMediaPluginService::ClearRecentHistoryOnGMPThread, t));
+  if (gmpThread) {
+    gmpThread->Shutdown();
   }
-
-  return NS_OK;
 }
 
 nsresult
 GeckoMediaPluginService::GMPDispatch(nsIRunnable* event, uint32_t flags)
 {
   nsCOMPtr<nsIRunnable> r(event);
   nsCOMPtr<nsIThread> thread;
   nsresult rv = GetThread(getter_AddRefs(thread));
@@ -446,1189 +265,224 @@ GeckoMediaPluginService::GetThread(nsITh
 {
   MOZ_ASSERT(aThread);
 
   // This can be called from any thread.
   MutexAutoLock lock(mMutex);
 
   if (!mGMPThread) {
     // Don't allow the thread to be created after shutdown has started.
-    if (mShuttingDown) {
+    if (mGMPThreadShutdown) {
       return NS_ERROR_FAILURE;
     }
 
     nsresult rv = NS_NewNamedThread("GMPThread", getter_AddRefs(mGMPThread));
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     // Tell the thread to initialize plugins
-    mGMPThread->Dispatch(NS_NewRunnableMethod(this, &GeckoMediaPluginService::LoadFromEnvironment), NS_DISPATCH_NORMAL);
+    InitializePlugins();
   }
 
   NS_ADDREF(mGMPThread);
   *aThread = mGMPThread;
 
   return NS_OK;
 }
 
+class GetGMPContentParentForAudioDecoderDone : public GetGMPContentParentCallback
+{
+public:
+  explicit GetGMPContentParentForAudioDecoderDone(UniquePtr<GetGMPAudioDecoderCallback>&& aCallback)
+   : mCallback(Move(aCallback))
+  {
+  }
+
+  virtual void Done(GMPContentParent* aGMPParent) override
+  {
+    GMPAudioDecoderParent* gmpADP = nullptr;
+    if (aGMPParent) {
+      aGMPParent->GetGMPAudioDecoder(&gmpADP);
+    }
+    mCallback->Done(gmpADP);
+  }
+
+private:
+  UniquePtr<GetGMPAudioDecoderCallback> mCallback;
+};
+
 NS_IMETHODIMP
 GeckoMediaPluginService::GetGMPAudioDecoder(nsTArray<nsCString>* aTags,
                                             const nsACString& aNodeId,
-                                            GMPAudioDecoderProxy** aGMPAD)
+                                            UniquePtr<GetGMPAudioDecoderCallback>&& aCallback)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   NS_ENSURE_ARG(aTags && aTags->Length() > 0);
-  NS_ENSURE_ARG(aGMPAD);
+  NS_ENSURE_ARG(aCallback);
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
-  nsRefPtr<GMPParent> gmp = SelectPluginForAPI(aNodeId,
-                                               NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
-                                               *aTags);
-  if (!gmp) {
+  UniquePtr<GetGMPContentParentCallback> callback(
+    new GetGMPContentParentForAudioDecoderDone(Move(aCallback)));
+  if (!GetContentParentFrom(aNodeId, NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
+                            *aTags, Move(callback))) {
     return NS_ERROR_FAILURE;
   }
 
-  GMPAudioDecoderParent* gmpADP;
-  nsresult rv = gmp->GetGMPAudioDecoder(&gmpADP);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  *aGMPAD = gmpADP;
-
   return NS_OK;
 }
 
+class GetGMPContentParentForVideoDecoderDone : public GetGMPContentParentCallback
+{
+public:
+  explicit GetGMPContentParentForVideoDecoderDone(UniquePtr<GetGMPVideoDecoderCallback>&& aCallback)
+   : mCallback(Move(aCallback))
+  {
+  }
+
+  virtual void Done(GMPContentParent* aGMPParent) override
+  {
+    GMPVideoDecoderParent* gmpVDP = nullptr;
+    GMPVideoHostImpl* videoHost = nullptr;
+    if (aGMPParent && NS_SUCCEEDED(aGMPParent->GetGMPVideoDecoder(&gmpVDP))) {
+      videoHost = &gmpVDP->Host();
+    }
+    mCallback->Done(gmpVDP, videoHost);
+  }
+
+private:
+  UniquePtr<GetGMPVideoDecoderCallback> mCallback;
+};
+
 NS_IMETHODIMP
 GeckoMediaPluginService::GetGMPVideoDecoder(nsTArray<nsCString>* aTags,
                                             const nsACString& aNodeId,
-                                            GMPVideoHost** aOutVideoHost,
-                                            GMPVideoDecoderProxy** aGMPVD)
+                                            UniquePtr<GetGMPVideoDecoderCallback>&& aCallback)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   NS_ENSURE_ARG(aTags && aTags->Length() > 0);
-  NS_ENSURE_ARG(aOutVideoHost);
-  NS_ENSURE_ARG(aGMPVD);
+  NS_ENSURE_ARG(aCallback);
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
-  nsRefPtr<GMPParent> gmp = SelectPluginForAPI(aNodeId,
-                                               NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
-                                               *aTags);
-#ifdef PR_LOGGING
-  nsCString api = (*aTags)[0];
-  LOGD(("%s: %p returning %p for api %s", __FUNCTION__, (void *)this, (void *)gmp, api.get()));
-#endif
-  if (!gmp) {
+  UniquePtr<GetGMPContentParentCallback> callback(
+    new GetGMPContentParentForVideoDecoderDone(Move(aCallback)));
+  if (!GetContentParentFrom(aNodeId, NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
+                            *aTags, Move(callback))) {
     return NS_ERROR_FAILURE;
   }
 
-
-  GMPVideoDecoderParent* gmpVDP;
-  nsresult rv = gmp->GetGMPVideoDecoder(&gmpVDP);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  *aGMPVD = gmpVDP;
-  *aOutVideoHost = &gmpVDP->Host();
-
   return NS_OK;
 }
 
+class GetGMPContentParentForVideoEncoderDone : public GetGMPContentParentCallback
+{
+public:
+  explicit GetGMPContentParentForVideoEncoderDone(UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
+   : mCallback(Move(aCallback))
+  {
+  }
+
+  virtual void Done(GMPContentParent* aGMPParent) override
+  {
+    GMPVideoEncoderParent* gmpVEP = nullptr;
+    GMPVideoHostImpl* videoHost = nullptr;
+    if (aGMPParent && NS_SUCCEEDED(aGMPParent->GetGMPVideoEncoder(&gmpVEP))) {
+      videoHost = &gmpVEP->Host();
+    }
+    mCallback->Done(gmpVEP, videoHost);
+  }
+
+private:
+  UniquePtr<GetGMPVideoEncoderCallback> mCallback;
+};
+
 NS_IMETHODIMP
 GeckoMediaPluginService::GetGMPVideoEncoder(nsTArray<nsCString>* aTags,
                                             const nsACString& aNodeId,
-                                            GMPVideoHost** aOutVideoHost,
-                                            GMPVideoEncoderProxy** aGMPVE)
+                                            UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   NS_ENSURE_ARG(aTags && aTags->Length() > 0);
-  NS_ENSURE_ARG(aOutVideoHost);
-  NS_ENSURE_ARG(aGMPVE);
+  NS_ENSURE_ARG(aCallback);
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
-  nsRefPtr<GMPParent> gmp = SelectPluginForAPI(aNodeId,
-                                               NS_LITERAL_CSTRING(GMP_API_VIDEO_ENCODER),
-                                               *aTags);
-#ifdef PR_LOGGING
-  nsCString api = (*aTags)[0];
-  LOGD(("%s: %p returning %p for api %s", __FUNCTION__, (void *)this, (void *)gmp, api.get()));
-#endif
-  if (!gmp) {
+  UniquePtr<GetGMPContentParentCallback> callback(
+    new GetGMPContentParentForVideoEncoderDone(Move(aCallback)));
+  if (!GetContentParentFrom(aNodeId, NS_LITERAL_CSTRING(GMP_API_VIDEO_ENCODER),
+                            *aTags, Move(callback))) {
     return NS_ERROR_FAILURE;
   }
 
-  GMPVideoEncoderParent* gmpVEP;
-  nsresult rv = gmp->GetGMPVideoEncoder(&gmpVEP);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  *aGMPVE = gmpVEP;
-  *aOutVideoHost = &gmpVEP->Host();
-
   return NS_OK;
 }
 
+class GetGMPContentParentForDecryptorDone : public GetGMPContentParentCallback
+{
+public:
+  explicit GetGMPContentParentForDecryptorDone(UniquePtr<GetGMPDecryptorCallback>&& aCallback)
+   : mCallback(Move(aCallback))
+  {
+  }
+
+  virtual void Done(GMPContentParent* aGMPParent) override
+  {
+    GMPDecryptorParent* ksp = nullptr;
+    if (aGMPParent) {
+      aGMPParent->GetGMPDecryptor(&ksp);
+    }
+    mCallback->Done(ksp);
+  }
+
+private:
+  UniquePtr<GetGMPDecryptorCallback> mCallback;
+};
+
 NS_IMETHODIMP
 GeckoMediaPluginService::GetGMPDecryptor(nsTArray<nsCString>* aTags,
                                          const nsACString& aNodeId,
-                                         GMPDecryptorProxy** aDecryptor)
+                                         UniquePtr<GetGMPDecryptorCallback>&& aCallback)
 {
 #if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
   if (!SandboxInfo::Get().CanSandboxMedia()) {
     NS_WARNING("GeckoMediaPluginService::GetGMPDecryptor: "
                "EME decryption not available without sandboxing support.");
     return NS_ERROR_NOT_AVAILABLE;
   }
 #endif
 
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   NS_ENSURE_ARG(aTags && aTags->Length() > 0);
-  NS_ENSURE_ARG(aDecryptor);
+  NS_ENSURE_ARG(aCallback);
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
-  nsRefPtr<GMPParent> gmp = SelectPluginForAPI(aNodeId,
-                                               NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
-                                               *aTags);
-
-  if (!gmp) {
-    // XXX to remove in bug 1147692
-    gmp = SelectPluginForAPI(aNodeId,
-                             NS_LITERAL_CSTRING(GMP_API_DECRYPTOR_COMPAT),
-                             *aTags);
-  }
-
-  if (!gmp) {
+  UniquePtr<GetGMPContentParentCallback> callback(
+    new GetGMPContentParentForDecryptorDone(Move(aCallback)));
+  if (!GetContentParentFrom(aNodeId, NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
+                            *aTags, Move(callback))) {
     return NS_ERROR_FAILURE;
   }
 
-  GMPDecryptorParent* ksp;
-  nsresult rv = gmp->GetGMPDecryptor(&ksp);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  *aDecryptor = static_cast<GMPDecryptorProxy*>(ksp);
-
-  return NS_OK;
-}
-
-void
-GeckoMediaPluginService::AsyncShutdownNeeded(GMPParent* aParent)
-{
-  LOGD(("%s::%s %p", __CLASS__, __FUNCTION__, aParent));
-  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
-
-  MOZ_ASSERT(!mAsyncShutdownPlugins.Contains(aParent));
-  mAsyncShutdownPlugins.AppendElement(aParent);
-}
-
-void
-GeckoMediaPluginService::AsyncShutdownComplete(GMPParent* aParent)
-{
-  LOGD(("%s::%s %p", __CLASS__, __FUNCTION__, aParent));
-  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
-
-  mAsyncShutdownPlugins.RemoveElement(aParent);
-  if (mAsyncShutdownPlugins.IsEmpty() && mShuttingDownOnGMPThread) {
-    // The main thread may be waiting for async shutdown of plugins,
-    // which has completed. Break the main thread out of its waiting loop.
-    nsCOMPtr<nsIRunnable> task(NS_NewRunnableMethod(
-      this, &GeckoMediaPluginService::SetAsyncShutdownComplete));
-    NS_DispatchToMainThread(task);
-  }
-}
-
-void
-GeckoMediaPluginService::SetAsyncShutdownComplete()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  mWaitingForPluginsAsyncShutdown = false;
-}
-
-void
-GeckoMediaPluginService::UnloadPlugins()
-{
-  LOGD(("%s::%s async_shutdown=%d", __CLASS__, __FUNCTION__,
-        mAsyncShutdownPlugins.Length()));
-  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
-
-  MOZ_ASSERT(!mShuttingDownOnGMPThread);
-  mShuttingDownOnGMPThread = true;
-
-  {
-    MutexAutoLock lock(mMutex);
-    // Note: CloseActive is async; it will actually finish
-    // shutting down when all the plugins have unloaded.
-    for (size_t i = 0; i < mPlugins.Length(); i++) {
-      mPlugins[i]->CloseActive(true);
-    }
-    mPlugins.Clear();
-  }
-
-  if (mAsyncShutdownPlugins.IsEmpty()) {
-    nsCOMPtr<nsIRunnable> task(NS_NewRunnableMethod(
-      this, &GeckoMediaPluginService::SetAsyncShutdownComplete));
-    NS_DispatchToMainThread(task);
-  }
-}
-
-void
-GeckoMediaPluginService::CrashPlugins()
-{
-  LOGD(("%s::%s", __CLASS__, __FUNCTION__));
-  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
-
-  MutexAutoLock lock(mMutex);
-  for (size_t i = 0; i < mPlugins.Length(); i++) {
-    mPlugins[i]->Crash();
-  }
-}
-
-void
-GeckoMediaPluginService::LoadFromEnvironment()
-{
-  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
-
-  const char* env = PR_GetEnv("MOZ_GMP_PATH");
-  if (!env || !*env) {
-    return;
-  }
-
-  nsString allpaths;
-  if (NS_WARN_IF(NS_FAILED(NS_CopyNativeToUnicode(nsDependentCString(env), allpaths)))) {
-    return;
-  }
-
-  uint32_t pos = 0;
-  while (pos < allpaths.Length()) {
-    // Loop over multiple path entries separated by colons (*nix) or
-    // semicolons (Windows)
-    int32_t next = allpaths.FindChar(XPCOM_ENV_PATH_SEPARATOR[0], pos);
-    if (next == -1) {
-      AddOnGMPThread(nsDependentSubstring(allpaths, pos));
-      break;
-    } else {
-      AddOnGMPThread(nsDependentSubstring(allpaths, pos, next - pos));
-      pos = next + 1;
-    }
-  }
-
-  mScannedPluginOnDisk = true;
-}
-
-NS_IMETHODIMP
-GeckoMediaPluginService::PathRunnable::Run()
-{
-  if (mOperation == ADD) {
-    mService->AddOnGMPThread(mPath);
-  } else {
-    mService->RemoveOnGMPThread(mPath,
-                                mOperation == REMOVE_AND_DELETE_FROM_DISK);
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-GeckoMediaPluginService::AddPluginDirectory(const nsAString& aDirectory)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  return GMPDispatch(new PathRunnable(this, aDirectory,
-                                      PathRunnable::EOperation::ADD));
-}
-
-NS_IMETHODIMP
-GeckoMediaPluginService::RemovePluginDirectory(const nsAString& aDirectory)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  return GMPDispatch(new PathRunnable(this, aDirectory,
-                                      PathRunnable::EOperation::REMOVE));
-}
-
-NS_IMETHODIMP
-GeckoMediaPluginService::RemoveAndDeletePluginDirectory(
-  const nsAString& aDirectory)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  return GMPDispatch(
-    new PathRunnable(this, aDirectory,
-                     PathRunnable::EOperation::REMOVE_AND_DELETE_FROM_DISK));
-}
-
-class DummyRunnable : public nsRunnable {
-public:
-  NS_IMETHOD Run() { return NS_OK; }
-};
-
-NS_IMETHODIMP
-GeckoMediaPluginService::GetPluginVersionForAPI(const nsACString& aAPI,
-                                                nsTArray<nsCString>* aTags,
-                                                nsACString& aOutVersion)
-{
-  NS_ENSURE_ARG(aTags && aTags->Length() > 0);
-
-  nsresult rv = EnsurePluginsOnDiskScanned();
-  if (NS_FAILED(rv)) {
-    NS_WARNING("Failed to load GMPs from disk.");
-    return rv;
-  }
-
-  {
-    MutexAutoLock lock(mMutex);
-    nsCString api(aAPI);
-    GMPParent* gmp = FindPluginForAPIFrom(0, api, *aTags, nullptr);
-    if (!gmp) {
-      return NS_ERROR_FAILURE;
-    }
-    aOutVersion = gmp->GetVersion();
-  }
-
-  return NS_OK;
-}
-
-nsresult
-GeckoMediaPluginService::EnsurePluginsOnDiskScanned()
-{
-  const char* env = nullptr;
-  if (!mScannedPluginOnDisk && (env = PR_GetEnv("MOZ_GMP_PATH")) && *env) {
-    // We have a MOZ_GMP_PATH environment variable which may specify the
-    // location of plugins to load, and we haven't yet scanned the disk to
-    // see if there are plugins there. Get the GMP thread, which will
-    // cause an event to be dispatched to which scans for plugins. We
-    // dispatch a sync event to the GMP thread here in order to wait until
-    // after the GMP thread has scanned any paths in MOZ_GMP_PATH.
-    nsresult rv = GMPDispatch(new DummyRunnable(), NS_DISPATCH_SYNC);
-    NS_ENSURE_SUCCESS(rv, rv);
-    MOZ_ASSERT(mScannedPluginOnDisk, "Should have scanned MOZ_GMP_PATH by now");
-  }
-
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GeckoMediaPluginService::HasPluginForAPI(const nsACString& aAPI,
                                          nsTArray<nsCString>* aTags,
                                          bool* aOutHavePlugin)
 {
-  NS_ENSURE_ARG(aTags && aTags->Length() > 0);
-  NS_ENSURE_ARG(aOutHavePlugin);
-
-  nsresult rv = EnsurePluginsOnDiskScanned();
-  if (NS_FAILED(rv)) {
-    NS_WARNING("Failed to load GMPs from disk.");
-    return rv;
-  }
-
-  {
-    MutexAutoLock lock(mMutex);
-    nsCString api(aAPI);
-    GMPParent* gmp = FindPluginForAPIFrom(0, api, *aTags, nullptr);
-    *aOutHavePlugin = (gmp != nullptr);
-  }
-
-  return NS_OK;
-}
-
-GMPParent*
-GeckoMediaPluginService::FindPluginForAPIFrom(size_t aSearchStartIndex,
-                                              const nsCString& aAPI,
-                                              const nsTArray<nsCString>& aTags,
-                                              size_t* aOutPluginIndex)
-{
-  mMutex.AssertCurrentThreadOwns();
-  for (size_t i = aSearchStartIndex; i < mPlugins.Length(); i++) {
-    GMPParent* gmp = mPlugins[i];
-    bool supportsAllTags = true;
-    for (size_t t = 0; t < aTags.Length(); t++) {
-      const nsCString& tag = aTags.ElementAt(t);
-      if (!gmp->SupportsAPI(aAPI, tag)) {
-        supportsAllTags = false;
-        break;
-      }
-    }
-    if (!supportsAllTags) {
-      continue;
-    }
-    if (aOutPluginIndex) {
-      *aOutPluginIndex = i;
-    }
-    return gmp;
-  }
-  return nullptr;
-}
-
-GMPParent*
-GeckoMediaPluginService::SelectPluginForAPI(const nsACString& aNodeId,
-                                            const nsCString& aAPI,
-                                            const nsTArray<nsCString>& aTags)
-{
-  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread,
-             "Can't clone GMP plugins on non-GMP threads.");
-
-  GMPParent* gmpToClone = nullptr;
-  {
-    MutexAutoLock lock(mMutex);
-    size_t index = 0;
-    GMPParent* gmp = nullptr;
-    while ((gmp = FindPluginForAPIFrom(index, aAPI, aTags, &index))) {
-      if (aNodeId.IsEmpty()) {
-        if (gmp->CanBeSharedCrossNodeIds()) {
-          return gmp;
-        }
-      } else if (gmp->CanBeUsedFrom(aNodeId)) {
-        MOZ_ASSERT(!aNodeId.IsEmpty());
-        gmp->SetNodeId(aNodeId);
-        return gmp;
-      }
-
-      // This GMP has the correct type but has the wrong nodeId; hold on to it
-      // in case we need to clone it.
-      gmpToClone = gmp;
-      // Loop around and try the next plugin; it may be usable from aNodeId.
-      index++;
-    }
-  }
-
-  // Plugin exists, but we can't use it due to cross-origin separation. Create a
-  // new one.
-  if (gmpToClone) {
-    GMPParent* clone = ClonePlugin(gmpToClone);
-    if (!aNodeId.IsEmpty()) {
-      clone->SetNodeId(aNodeId);
-    }
-    return clone;
-  }
-
-  return nullptr;
-}
-
-class CreateGMPParentTask : public nsRunnable {
-public:
-  NS_IMETHOD Run() {
-    MOZ_ASSERT(NS_IsMainThread());
-#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
-    if (!SandboxInfo::Get().CanSandboxMedia()) {
-      if (!Preferences::GetBool("media.gmp.insecure.allow")) {
-        NS_WARNING("Denying media plugin load due to lack of sandboxing.");
-        return NS_ERROR_NOT_AVAILABLE;
-      }
-      NS_WARNING("Loading media plugin despite lack of sandboxing.");
-    }
-#endif
-    mParent = new GMPParent();
-    return NS_OK;
-  }
-  already_AddRefed<GMPParent> GetParent() {
-    return mParent.forget();
-  }
-private:
-  nsRefPtr<GMPParent> mParent;
-};
-
-GMPParent*
-GeckoMediaPluginService::ClonePlugin(const GMPParent* aOriginal)
-{
-  MOZ_ASSERT(aOriginal);
-
-  // The GMPParent inherits from IToplevelProtocol, which must be created
-  // on the main thread to be threadsafe. See Bug 1035653.
-  nsRefPtr<CreateGMPParentTask> task(new CreateGMPParentTask());
-  if (!NS_IsMainThread()) {
-    nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
-    MOZ_ASSERT(mainThread);
-    mozilla::SyncRunnable::DispatchToThread(mainThread, task);
-  }
-
-  nsRefPtr<GMPParent> gmp = task->GetParent();
-  nsresult rv = gmp->CloneFrom(aOriginal);
-
-  if (NS_FAILED(rv)) {
-    NS_WARNING("Can't Create GMPParent");
-    return nullptr;
-  }
-
-  MutexAutoLock lock(mMutex);
-  mPlugins.AppendElement(gmp);
-
-  return gmp.get();
-}
-
-class NotifyObserversTask final : public nsRunnable {
-public:
-  explicit NotifyObserversTask(const char* aTopic)
-    : mTopic(aTopic)
-  {}
-  NS_IMETHOD Run() override {
-    MOZ_ASSERT(NS_IsMainThread());
-    nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
-    MOZ_ASSERT(obsService);
-    if (obsService) {
-      obsService->NotifyObservers(nullptr, mTopic, nullptr);
-    }
-    return NS_OK;
-  }
-private:
-  ~NotifyObserversTask() {}
-  const char* mTopic;
-};
-
-void
-GeckoMediaPluginService::AddOnGMPThread(const nsAString& aDirectory)
-{
-  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
-  LOGD(("%s::%s: %s", __CLASS__, __FUNCTION__, NS_LossyConvertUTF16toASCII(aDirectory).get()));
-
-  nsCOMPtr<nsIFile> directory;
-  nsresult rv = NS_NewLocalFile(aDirectory, false, getter_AddRefs(directory));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
-  }
-
-  // The GMPParent inherits from IToplevelProtocol, which must be created
-  // on the main thread to be threadsafe. See Bug 1035653.
-  nsRefPtr<CreateGMPParentTask> task(new CreateGMPParentTask());
-  nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
-  MOZ_ASSERT(mainThread);
-  mozilla::SyncRunnable::DispatchToThread(mainThread, task);
-  nsRefPtr<GMPParent> gmp = task->GetParent();
-  rv = gmp ? gmp->Init(this, directory) : NS_ERROR_NOT_AVAILABLE;
-  if (NS_FAILED(rv)) {
-    NS_WARNING("Can't Create GMPParent");
-    return;
-  }
-
-  {
-    MutexAutoLock lock(mMutex);
-    mPlugins.AppendElement(gmp);
-  }
-
-  NS_DispatchToMainThread(new NotifyObserversTask("gmp-path-added"), NS_DISPATCH_NORMAL);
-}
-
-void
-GeckoMediaPluginService::RemoveOnGMPThread(const nsAString& aDirectory,
-                                           const bool aDeleteFromDisk)
-{
-  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
-  LOGD(("%s::%s: %s", __CLASS__, __FUNCTION__, NS_LossyConvertUTF16toASCII(aDirectory).get()));
-
-  nsCOMPtr<nsIFile> directory;
-  nsresult rv = NS_NewLocalFile(aDirectory, false, getter_AddRefs(directory));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
-  }
-
-  MutexAutoLock lock(mMutex);
-  for (size_t i = 0; i < mPlugins.Length(); ++i) {
-    nsCOMPtr<nsIFile> pluginpath = mPlugins[i]->GetDirectory();
-    bool equals;
-    if (NS_SUCCEEDED(directory->Equals(pluginpath, &equals)) && equals) {
-      mPlugins[i]->AbortAsyncShutdown();
-      mPlugins[i]->CloseActive(true);
-      if (aDeleteFromDisk) {
-        pluginpath->Remove(true);
-      }
-      mPlugins.RemoveElementAt(i);
-      return;
-    }
-  }
-  NS_WARNING("Removing GMP which was never added.");
-  nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
-  cs->LogStringMessage(MOZ_UTF16("Removing GMP which was never added."));
-}
-
-// May remove when Bug 1043671 is fixed
-static void Dummy(nsRefPtr<GMPParent>& aOnDeathsDoor)
-{
-  // exists solely to do nothing and let the Runnable kill the GMPParent
-  // when done.
-}
-
-void
-GeckoMediaPluginService::ReAddOnGMPThread(nsRefPtr<GMPParent>& aOld)
-{
-  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
-  LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, (void*) aOld));
-
-  nsRefPtr<GMPParent> gmp;
-  if (!mShuttingDownOnGMPThread) {
-    // Don't re-add plugin if we're shutting down. Let the old plugin die.
-    gmp = ClonePlugin(aOld);
-  }
-  // Note: both are now in the list
-  // Until we give up the GMPThread, we're safe even if we unlock temporarily
-  // since off-main-thread users just test for existance; they don't modify the list.
-  MutexAutoLock lock(mMutex);
-  mPlugins.RemoveElement(aOld);
-
-  // Schedule aOld to be destroyed.  We can't destroy it from here since we
-  // may be inside ActorDestroyed() for it.
-  NS_DispatchToCurrentThread(WrapRunnableNM(&Dummy, aOld));
-}
-
-NS_IMETHODIMP
-GeckoMediaPluginService::GetStorageDir(nsIFile** aOutFile)
-{
-  if (NS_WARN_IF(!mStorageBaseDir)) {
-    return NS_ERROR_FAILURE;
-  }
-  return mStorageBaseDir->Clone(aOutFile);
-}
-
-static nsresult
-WriteToFile(nsIFile* aPath,
-            const nsCString& aFileName,
-            const nsCString& aData)
-{
-  nsCOMPtr<nsIFile> path;
-  nsresult rv = aPath->Clone(getter_AddRefs(path));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = path->AppendNative(aFileName);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  PRFileDesc* f = nullptr;
-  rv = path->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, PR_IRWXU, &f);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  int32_t len = PR_Write(f, aData.get(), aData.Length());
-  PR_Close(f);
-  if (NS_WARN_IF(len < 0 || (size_t)len != aData.Length())) {
-    return NS_ERROR_FAILURE;
-  }
-
-  return NS_OK;
-}
-
-static nsresult
-ReadFromFile(nsIFile* aPath,
-             const nsACString& aFileName,
-             nsACString& aOutData,
-             int32_t aMaxLength)
-{
-  nsCOMPtr<nsIFile> path;
-  nsresult rv = aPath->Clone(getter_AddRefs(path));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = path->AppendNative(aFileName);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  PRFileDesc* f = nullptr;
-  rv = path->OpenNSPRFileDesc(PR_RDONLY | PR_CREATE_FILE, PR_IRWXU, &f);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  auto size = PR_Seek(f, 0, PR_SEEK_END);
-  PR_Seek(f, 0, PR_SEEK_SET);
-
-  if (size > aMaxLength) {
-    return NS_ERROR_FAILURE;
-  }
-  aOutData.SetLength(size);
-
-  auto len = PR_Read(f, aOutData.BeginWriting(), size);
-  PR_Close(f);
-  if (NS_WARN_IF(len != size)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  return NS_OK;
-}
-
-nsresult
-ReadSalt(nsIFile* aPath, nsACString& aOutData)
-{
-  return ReadFromFile(aPath, NS_LITERAL_CSTRING("salt"),
-                      aOutData, NodeIdSaltLength);
-
-}
-
-NS_IMETHODIMP
-GeckoMediaPluginService::IsPersistentStorageAllowed(const nsACString& aNodeId,
-                                                    bool* aOutAllowed)
-{
-  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
-  NS_ENSURE_ARG(aOutAllowed);
-  *aOutAllowed = mPersistentStorageAllowed.Get(aNodeId);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-GeckoMediaPluginService::GetNodeId(const nsAString& aOrigin,
-                                   const nsAString& aTopLevelOrigin,
-                                   bool aInPrivateBrowsing,
-                                   nsACString& aOutId)
-{
-  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
-  LOGD(("%s::%s: (%s, %s), %s", __CLASS__, __FUNCTION__,
-       NS_ConvertUTF16toUTF8(aOrigin).get(),
-       NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
-       (aInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing")));
-
-  nsresult rv;
-
-  if (aOrigin.EqualsLiteral("null") ||
-      aOrigin.IsEmpty() ||
-      aTopLevelOrigin.EqualsLiteral("null") ||
-      aTopLevelOrigin.IsEmpty()) {
-    // At least one of the (origin, topLevelOrigin) is null or empty;
-    // probably a local file. Generate a random node id, and don't store
-    // it so that the GMP's storage is temporary and not shared.
-    nsAutoCString salt;
-    rv = GenerateRandomPathName(salt, NodeIdSaltLength);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-    aOutId = salt;
-    mPersistentStorageAllowed.Put(salt, false);
-    return NS_OK;
-  }
-
-  const uint32_t hash = AddToHash(HashString(aOrigin),
-                                  HashString(aTopLevelOrigin));
-
-  if (aInPrivateBrowsing) {
-    // For PB mode, we store the node id, indexed by the origin pair,
-    // so that if the same origin pair is opened in this session, it gets
-    // the same node id.
-    nsCString* salt = nullptr;
-    if (!(salt = mTempNodeIds.Get(hash))) {
-      // No salt stored, generate and temporarily store some for this id.
-      nsAutoCString newSalt;
-      rv = GenerateRandomPathName(newSalt, NodeIdSaltLength);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-      salt = new nsCString(newSalt);
-      mTempNodeIds.Put(hash, salt);
-      mPersistentStorageAllowed.Put(*salt, false);
-    }
-    aOutId = *salt;
-    return NS_OK;
-  }
-
-  // Otherwise, try to see if we've previously generated and stored salt
-  // for this origin pair.
-  nsCOMPtr<nsIFile> path; // $profileDir/gmp/
-  rv = GetStorageDir(getter_AddRefs(path));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = path->AppendNative(NS_LITERAL_CSTRING("id"));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  // $profileDir/gmp/id/
-  rv = path->Create(nsIFile::DIRECTORY_TYPE, 0700);
-  if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  nsAutoCString hashStr;
-  hashStr.AppendInt((int64_t)hash);
-
-  // $profileDir/gmp/id/$hash
-  rv = path->AppendNative(hashStr);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = path->Create(nsIFile::DIRECTORY_TYPE, 0700);
-  if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  nsCOMPtr<nsIFile> saltFile;
-  rv = path->Clone(getter_AddRefs(saltFile));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = saltFile->AppendNative(NS_LITERAL_CSTRING("salt"));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  nsAutoCString salt;
-  bool exists = false;
-  rv = saltFile->Exists(&exists);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  if (!exists) {
-    // No stored salt for this origin. Generate salt, and store it and
-    // the origin on disk.
-    nsresult rv = GenerateRandomPathName(salt, NodeIdSaltLength);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-    MOZ_ASSERT(salt.Length() == NodeIdSaltLength);
-
-    // $profileDir/gmp/id/$hash/salt
-    rv = WriteToFile(path, NS_LITERAL_CSTRING("salt"), salt);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    // $profileDir/gmp/id/$hash/origin
-    rv = WriteToFile(path,
-                     NS_LITERAL_CSTRING("origin"),
-                     NS_ConvertUTF16toUTF8(aOrigin));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    // $profileDir/gmp/id/$hash/topLevelOrigin
-    rv = WriteToFile(path,
-                     NS_LITERAL_CSTRING("topLevelOrigin"),
-                     NS_ConvertUTF16toUTF8(aTopLevelOrigin));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-  } else {
-    rv = ReadSalt(path, salt);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-  }
-
-  aOutId = salt;
-  mPersistentStorageAllowed.Put(salt, true);
-
-  return NS_OK;
-}
-
-bool
-MatchOrigin(nsIFile* aPath, const nsACString& aOrigin)
-{
-  // http://en.wikipedia.org/wiki/Domain_Name_System#Domain_name_syntax
-  static const uint32_t MaxDomainLength = 253;
-
-  nsresult rv;
-  nsCString str;
-  rv = ReadFromFile(aPath, NS_LITERAL_CSTRING("origin"), str, MaxDomainLength);
-  if (NS_SUCCEEDED(rv) && aOrigin.Equals(str)) {
-    return true;
-  }
-  rv = ReadFromFile(aPath, NS_LITERAL_CSTRING("topLevelOrigin"), str, MaxDomainLength);
-  if (NS_SUCCEEDED(rv) && aOrigin.Equals(str)) {
-    return true;
-  }
-  return false;
-}
-
-template<typename T> static void
-KillPlugins(const nsTArray<nsRefPtr<GMPParent>>& aPlugins,
-            Mutex& aMutex, T&& aFilter)
-{
-  // Shutdown the plugins when |aFilter| evaluates to true.
-  // After we clear storage data, node IDs will become invalid and shouldn't be
-  // used anymore. We need to kill plugins with such nodeIDs.
-  // Note: we can't shut them down while holding the lock,
-  // as the lock is not re-entrant and shutdown requires taking the lock.
-  // The plugin list is only edited on the GMP thread, so this should be OK.
-  nsTArray<nsRefPtr<GMPParent>> pluginsToKill;
-  {
-    MutexAutoLock lock(aMutex);
-    for (size_t i = 0; i < aPlugins.Length(); i++) {
-      nsRefPtr<GMPParent> parent(aPlugins[i]);
-      if (aFilter(parent)) {
-        pluginsToKill.AppendElement(parent);
-      }
-    }
-  }
-
-  for (size_t i = 0; i < pluginsToKill.Length(); i++) {
-    pluginsToKill[i]->CloseActive(false);
-    // Abort async shutdown because we're going to wipe the plugin's storage,
-    // so we don't want it writing more data in its async shutdown path.
-    pluginsToKill[i]->AbortAsyncShutdown();
-  }
-}
-
-static nsresult
-DeleteDir(nsIFile* aPath)
-{
-  bool exists = false;
-  nsresult rv = aPath->Exists(&exists);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-  if (exists) {
-    return aPath->Remove(true);
-  }
-  return NS_OK;
-}
-
-struct NodeFilter {
-  explicit NodeFilter(const nsTArray<nsCString>& nodeIDs) : mNodeIDs(nodeIDs) {}
-  bool operator()(GMPParent* aParent) {
-    return mNodeIDs.Contains(aParent->GetNodeId());
-  }
-private:
-  const nsTArray<nsCString>& mNodeIDs;
-};
-
-void
-GeckoMediaPluginService::ClearNodeIdAndPlugin(DirectoryFilter& aFilter)
-{
-  nsresult rv;
-  nsCOMPtr<nsIFile> path;
-
-  // $profileDir/gmp/
-  rv = GetStorageDir(getter_AddRefs(path));
-  if (NS_FAILED(rv)) {
-    return;
-  }
-
-  // $profileDir/gmp/id/
-  rv = path->AppendNative(NS_LITERAL_CSTRING("id"));
-  if (NS_FAILED(rv)) {
-    return;
-  }
-
-  // Iterate all sub-folders of $profileDir/gmp/id/
-  nsCOMPtr<nsISimpleEnumerator> iter;
-  rv = path->GetDirectoryEntries(getter_AddRefs(iter));
-  if (NS_FAILED(rv)) {
-    return;
-  }
-
-  bool hasMore = false;
-  nsTArray<nsCString> nodeIDsToClear;
-  while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
-    nsCOMPtr<nsISupports> supports;
-    rv = iter->GetNext(getter_AddRefs(supports));
-    if (NS_FAILED(rv)) {
-      continue;
-    }
-
-    // $profileDir/gmp/id/$hash
-    nsCOMPtr<nsIFile> dirEntry(do_QueryInterface(supports, &rv));
-    if (NS_FAILED(rv)) {
-      continue;
-    }
-
-    // Skip non-directory files.
-    bool isDirectory = false;
-    rv = dirEntry->IsDirectory(&isDirectory);
-    if (NS_FAILED(rv) || !isDirectory) {
-      continue;
-    }
-
-    if (!aFilter(dirEntry)) {
-      continue;
-    }
-
-    nsAutoCString salt;
-    if (NS_SUCCEEDED(ReadSalt(dirEntry, salt))) {
-      // Keep node IDs to clear data/plugins associated with them later.
-      nodeIDsToClear.AppendElement(salt);
-      // Also remove node IDs from the table.
-      mPersistentStorageAllowed.Remove(salt);
-    }
-    // Now we can remove the directory for the origin pair.
-    if (NS_FAILED(dirEntry->Remove(true))) {
-      NS_WARNING("Failed to delete the directory for the origin pair");
-    }
-  }
-
-  // Kill plugins that have node IDs to be cleared.
-  KillPlugins(mPlugins, mMutex, NodeFilter(nodeIDsToClear));
-
-  // Clear all matching $profileDir/gmp/storage/$nodeId/
-  rv = GetStorageDir(getter_AddRefs(path));
-  if (NS_FAILED(rv)) {
-    return;
-  }
-
-  rv = path->AppendNative(NS_LITERAL_CSTRING("storage"));
-  if (NS_FAILED(rv)) {
-    return;
-  }
-
-  for (size_t i = 0; i < nodeIDsToClear.Length(); i++) {
-    nsCOMPtr<nsIFile> dirEntry;
-    rv = path->Clone(getter_AddRefs(dirEntry));
-    if (NS_FAILED(rv)) {
-      continue;
-    }
-
-    rv = dirEntry->AppendNative(nodeIDsToClear[i]);
-    if (NS_FAILED(rv)) {
-      continue;
-    }
-
-    if (NS_FAILED(DeleteDir(dirEntry))) {
-      NS_WARNING("Failed to delete GMP storage directory for the node");
-    }
-  }
-}
-
-void
-GeckoMediaPluginService::ForgetThisSiteOnGMPThread(const nsACString& aOrigin)
-{
-  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
-  LOGD(("%s::%s: origin=%s", __CLASS__, __FUNCTION__, aOrigin.Data()));
-
-  struct OriginFilter : public DirectoryFilter {
-    explicit OriginFilter(const nsACString& aOrigin) : mOrigin(aOrigin) {}
-    virtual bool operator()(nsIFile* aPath) {
-      return MatchOrigin(aPath, mOrigin);
-    }
-  private:
-    const nsACString& mOrigin;
-  } filter(aOrigin);
-
-  ClearNodeIdAndPlugin(filter);
-}
-
-void
-GeckoMediaPluginService::ClearRecentHistoryOnGMPThread(PRTime aSince)
-{
-  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
-  LOGD(("%s::%s: since=%lld", __CLASS__, __FUNCTION__, (int64_t)aSince));
-
-  nsCOMPtr<nsIFile> storagePath;
-  nsCOMPtr<nsIFile> temp;
-  if (NS_SUCCEEDED(GetStorageDir(getter_AddRefs(temp))) &&
-      NS_SUCCEEDED(temp->AppendNative(NS_LITERAL_CSTRING("storage")))) {
-    storagePath = temp.forget();
-  }
-
-  struct MTimeFilter : public DirectoryFilter {
-    explicit MTimeFilter(PRTime aSince, already_AddRefed<nsIFile> aPath)
-      : mSince(aSince), mStoragePath(aPath) {}
-
-    // Return true if any files under aPath is modified after |mSince|.
-    bool IsModifiedAfter(nsIFile* aPath) {
-      PRTime lastModified;
-      nsresult rv = aPath->GetLastModifiedTime(&lastModified);
-      if (NS_SUCCEEDED(rv) && lastModified >= mSince) {
-        return true;
-      }
-      // Check sub-directories recursively
-      nsCOMPtr<nsISimpleEnumerator> iter;
-      rv = aPath->GetDirectoryEntries(getter_AddRefs(iter));
-      if (NS_FAILED(rv)) {
-        return false;
-      }
-
-      bool hasMore = false;
-      while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
-        nsCOMPtr<nsISupports> supports;
-        rv = iter->GetNext(getter_AddRefs(supports));
-        if (NS_FAILED(rv)) {
-          continue;
-        }
-
-        nsCOMPtr<nsIFile> path(do_QueryInterface(supports, &rv));
-        if (NS_FAILED(rv)) {
-          continue;
-        }
-
-        if (IsModifiedAfter(path)) {
-          return true;
-        }
-      }
-      return false;
-    }
-
-    // |aPath| is $profileDir/gmp/id/$hash
-    virtual bool operator()(nsIFile* aPath) {
-      if (IsModifiedAfter(aPath)) {
-        return true;
-      }
-
-      nsAutoCString salt;
-      nsresult rv = ReadSalt(aPath, salt);
-      if (NS_FAILED(rv)) {
-        return false;
-      }
-
-      // $profileDir/gmp/storage/
-      if (!mStoragePath) {
-        return false;
-      }
-      // $profileDir/gmp/storage/$nodeId/
-      nsCOMPtr<nsIFile> path;
-      rv = mStoragePath->Clone(getter_AddRefs(path));
-      if (NS_FAILED(rv)) {
-        return false;
-      }
-
-      rv = path->AppendNative(salt);
-      return NS_SUCCEEDED(rv) && IsModifiedAfter(path);
-    }
-  private:
-    const PRTime mSince;
-    const nsCOMPtr<nsIFile> mStoragePath;
-  } filter(aSince, storagePath.forget());
-
-  ClearNodeIdAndPlugin(filter);
-
-  NS_DispatchToMainThread(new NotifyObserversTask("gmp-clear-storage-complete"), NS_DISPATCH_NORMAL);
-}
-
-NS_IMETHODIMP
-GeckoMediaPluginService::ForgetThisSite(const nsAString& aOrigin)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  return GMPDispatch(NS_NewRunnableMethodWithArg<nsCString>(
-      this, &GeckoMediaPluginService::ForgetThisSiteOnGMPThread,
-      NS_ConvertUTF16toUTF8(aOrigin)));
-}
-
-static bool IsNodeIdValid(GMPParent* aParent) {
-  return !aParent->GetNodeId().IsEmpty();
-}
-
-void
-GeckoMediaPluginService::ClearStorage()
-{
-  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
-  LOGD(("%s::%s", __CLASS__, __FUNCTION__));
-
-  // Kill plugins with valid nodeIDs.
-  KillPlugins(mPlugins, mMutex, &IsNodeIdValid);
-
-  nsCOMPtr<nsIFile> path; // $profileDir/gmp/
-  nsresult rv = GetStorageDir(getter_AddRefs(path));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
-  }
-
-  if (NS_FAILED(DeleteDir(path))) {
-    NS_WARNING("Failed to delete GMP storage directory");
-  }
-  NS_DispatchToMainThread(new NotifyObserversTask("gmp-clear-storage-complete"), NS_DISPATCH_NORMAL);
+  nsCString unused;
+  return GetPluginVersionForAPI(aAPI, aTags, aOutHavePlugin, unused);
 }
 
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/gmp/GMPService.h
+++ b/dom/media/gmp/GMPService.h
@@ -10,47 +10,59 @@
 #include "mozIGeckoMediaPluginService.h"
 #include "nsIObserver.h"
 #include "nsTArray.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Monitor.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsIThread.h"
-#include "nsThreadUtils.h"
-#include "nsITimer.h"
-#include "nsClassHashtable.h"
-#include "nsDataHashtable.h"
-#include "mozilla/Atomics.h"
 
 template <class> struct already_AddRefed;
 
 namespace mozilla {
+
+extern PRLogModuleInfo* GetGMPLog();
+
 namespace gmp {
 
-class GMPParent;
+class GetGMPContentParentCallback;
 
 #define GMP_DEFAULT_ASYNC_SHUTDONW_TIMEOUT 3000
 
-class GeckoMediaPluginService final : public mozIGeckoMediaPluginService
-                                    , public nsIObserver
+class GeckoMediaPluginService : public mozIGeckoMediaPluginService
+                              , public nsIObserver
 {
 public:
   static already_AddRefed<GeckoMediaPluginService> GetGeckoMediaPluginService();
 
-  GeckoMediaPluginService();
-  nsresult Init();
+  virtual nsresult Init();
 
   NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_MOZIGECKOMEDIAPLUGINSERVICE
-  NS_DECL_NSIOBSERVER
 
-  void AsyncShutdownNeeded(GMPParent* aParent);
-  void AsyncShutdownComplete(GMPParent* aParent);
-  void AbortAsyncShutdown();
+  // mozIGeckoMediaPluginService
+  NS_IMETHOD GetThread(nsIThread** aThread) override;
+  NS_IMETHOD HasPluginForAPI(const nsACString& aAPI, nsTArray<nsCString>* aTags,
+                             bool *aRetVal) override;
+  NS_IMETHOD GetGMPVideoDecoder(nsTArray<nsCString>* aTags,
+                                const nsACString& aNodeId,
+                                UniquePtr<GetGMPVideoDecoderCallback>&& aCallback)
+    override;
+  NS_IMETHOD GetGMPVideoEncoder(nsTArray<nsCString>* aTags,
+                                const nsACString& aNodeId,
+                                UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
+    override;
+  NS_IMETHOD GetGMPAudioDecoder(nsTArray<nsCString>* aTags,
+                                const nsACString& aNodeId,
+                                UniquePtr<GetGMPAudioDecoderCallback>&& aCallback)
+    override;
+  NS_IMETHOD GetGMPDecryptor(nsTArray<nsCString>* aTags,
+                             const nsACString& aNodeId,
+                             UniquePtr<GetGMPDecryptorCallback>&& aCallback)
+    override;
 
   int32_t AsyncShutdownTimeoutMs();
 
   class PluginCrashCallback
   {
   public:
     NS_INLINE_DECL_REFCOUNTING(PluginCrashCallback)
 
@@ -72,126 +84,35 @@ public:
   };
   void RemoveObsoletePluginCrashCallbacks(); // Called from add/remove/run.
   void AddPluginCrashCallback(nsRefPtr<PluginCrashCallback> aPluginCrashCallback);
   void RemovePluginCrashCallbacks(const nsACString& aPluginId);
   void RunPluginCrashCallbacks(const nsACString& aPluginId,
                                const nsACString& aPluginName,
                                const nsAString& aPluginDumpId);
 
-private:
-  ~GeckoMediaPluginService();
+protected:
+  GeckoMediaPluginService();
+  virtual ~GeckoMediaPluginService();
+
+  virtual void InitializePlugins() = 0;
+  virtual bool GetContentParentFrom(const nsACString& aNodeId,
+                                    const nsCString& aAPI,
+                                    const nsTArray<nsCString>& aTags,
+                                    UniquePtr<GetGMPContentParentCallback>&& aCallback) = 0;
 
   nsresult GMPDispatch(nsIRunnable* event, uint32_t flags = NS_DISPATCH_NORMAL);
-
-  void ClearStorage();
-
-  GMPParent* SelectPluginForAPI(const nsACString& aNodeId,
-                                const nsCString& aAPI,
-                                const nsTArray<nsCString>& aTags);
-  GMPParent* FindPluginForAPIFrom(size_t aSearchStartIndex,
-                                  const nsCString& aAPI,
-                                  const nsTArray<nsCString>& aTags,
-                                  size_t* aOutPluginIndex);
-
-  void UnloadPlugins();
-  void CrashPlugins();
-  void SetAsyncShutdownComplete();
-
-  void LoadFromEnvironment();
-  void ProcessPossiblePlugin(nsIFile* aDir);
-
-  void AddOnGMPThread(const nsAString& aDirectory);
-  void RemoveOnGMPThread(const nsAString& aDirectory,
-                         const bool aDeleteFromDisk);
-
-  nsresult SetAsyncShutdownTimeout();
-
-  struct DirectoryFilter {
-    virtual bool operator()(nsIFile* aPath) = 0;
-    ~DirectoryFilter() {}
-  };
-  void ClearNodeIdAndPlugin(DirectoryFilter& aFilter);
-
-  void ForgetThisSiteOnGMPThread(const nsACString& aOrigin);
-  void ClearRecentHistoryOnGMPThread(PRTime aSince);
+  void ShutdownGMPThread();
 
 protected:
-  friend class GMPParent;
-  void ReAddOnGMPThread(nsRefPtr<GMPParent>& aOld);
-private:
-  GMPParent* ClonePlugin(const GMPParent* aOriginal);
-  nsresult EnsurePluginsOnDiskScanned();
-  nsresult InitStorage();
-
-  class PathRunnable : public nsRunnable
-  {
-  public:
-    enum EOperation {
-      ADD,
-      REMOVE,
-      REMOVE_AND_DELETE_FROM_DISK,
-    };
-
-    PathRunnable(GeckoMediaPluginService* aService, const nsAString& aPath,
-                 EOperation aOperation)
-      : mService(aService)
-      , mPath(aPath)
-      , mOperation(aOperation)
-    { }
-
-    NS_DECL_NSIRUNNABLE
-
-  private:
-    nsRefPtr<GeckoMediaPluginService> mService;
-    nsString mPath;
-    EOperation mOperation;
-  };
-
-  Mutex mMutex; // Protects mGMPThread and mShuttingDown and mPlugins
-  nsTArray<nsRefPtr<GMPParent>> mPlugins;
+  Mutex mMutex; // Protects mGMPThread and mGMPThreadShutdown and some members
+                // in derived classes.
   nsCOMPtr<nsIThread> mGMPThread;
-  bool mShuttingDown;
+  bool mGMPThreadShutdown;
   bool mShuttingDownOnGMPThread;
 
   nsTArray<nsRefPtr<PluginCrashCallback>> mPluginCrashCallbacks;
-
-  // True if we've inspected MOZ_GMP_PATH on the GMP thread and loaded any
-  // plugins found there into mPlugins.
-  Atomic<bool> mScannedPluginOnDisk;
-
-  template<typename T>
-  class MainThreadOnly {
-  public:
-    MOZ_IMPLICIT MainThreadOnly(T aValue)
-      : mValue(aValue)
-    {}
-    operator T&() {
-      MOZ_ASSERT(NS_IsMainThread());
-      return mValue;
-    }
-
-  private:
-    T mValue;
-  };
-
-  MainThreadOnly<bool> mWaitingForPluginsAsyncShutdown;
-
-  nsTArray<nsRefPtr<GMPParent>> mAsyncShutdownPlugins; // GMP Thread only.
-
-  nsCOMPtr<nsIFile> mStorageBaseDir;
-
-  // Hashes of (origin,topLevelOrigin) to the node id for
-  // non-persistent sessions.
-  nsClassHashtable<nsUint32HashKey, nsCString> mTempNodeIds;
-
-  // Hashes node id to whether that node id is allowed to store data
-  // persistently on disk.
-  nsDataHashtable<nsCStringHashKey, bool> mPersistentStorageAllowed;
 };
 
-nsresult ReadSalt(nsIFile* aPath, nsACString& aOutData);
-bool MatchOrigin(nsIFile* aPath, const nsACString& aOrigin);
-
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMPService_h_
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/GMPServiceChild.cpp
@@ -0,0 +1,382 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "GMPService.h"
+#include "mozilla/dom/ContentChild.h"
+
+namespace mozilla {
+
+#ifdef LOG
+#undef LOG
+#endif
+
+#ifdef PR_LOGGING
+#define LOGD(msg) PR_LOG(GetGMPLog(), PR_LOG_DEBUG, msg)
+#define LOG(level, msg) PR_LOG(GetGMPLog(), (level), msg)
+#else
+#define LOGD(msg)
+#define LOG(leve1, msg)
+#endif
+
+#ifdef __CLASS__
+#undef __CLASS__
+#endif
+#define __CLASS__ "GMPService"
+
+namespace gmp {
+
+already_AddRefed<GeckoMediaPluginServiceChild>
+GeckoMediaPluginServiceChild::GetSingleton()
+{
+  MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default);
+  nsRefPtr<GeckoMediaPluginService> service(
+    GeckoMediaPluginService::GetGeckoMediaPluginService());
+#ifdef DEBUG
+  if (service) {
+    nsCOMPtr<mozIGeckoMediaPluginChromeService> chromeService;
+    CallQueryInterface(service.get(), getter_AddRefs(chromeService));
+    MOZ_ASSERT(!chromeService);
+  }
+#endif
+  return service.forget().downcast<GeckoMediaPluginServiceChild>();
+}
+
+class GetServiceChildCallback
+{
+public:
+  GetServiceChildCallback()
+  {
+    MOZ_COUNT_CTOR(GetServiceChildCallback);
+  }
+  virtual ~GetServiceChildCallback()
+  {
+    MOZ_COUNT_DTOR(GetServiceChildCallback);
+  }
+  virtual void Done(GMPServiceChild* aGMPServiceChild) = 0;
+};
+
+class GetContentParentFromDone : public GetServiceChildCallback
+{
+public:
+  GetContentParentFromDone(const nsACString& aNodeId, const nsCString& aAPI,
+                           const nsTArray<nsCString>& aTags,
+                           UniquePtr<GetGMPContentParentCallback>&& aCallback)
+    : mNodeId(aNodeId),
+      mAPI(aAPI),
+      mTags(aTags),
+      mCallback(Move(aCallback))
+  {
+  }
+
+  virtual void Done(GMPServiceChild* aGMPServiceChild)
+  {
+    if (!aGMPServiceChild) {
+      mCallback->Done(nullptr);
+      return;
+    }
+
+    nsTArray<base::ProcessId> alreadyBridgedTo;
+    aGMPServiceChild->GetAlreadyBridgedTo(alreadyBridgedTo);
+
+    base::ProcessId otherProcess;
+    nsCString displayName;
+    nsCString pluginId;
+    bool ok = aGMPServiceChild->SendLoadGMP(mNodeId, mAPI, mTags,
+                                            alreadyBridgedTo, &otherProcess,
+                                            &displayName, &pluginId);
+    if (!ok) {
+      mCallback->Done(nullptr);
+      return;
+    }
+
+    nsRefPtr<GMPContentParent> parent;
+    aGMPServiceChild->GetBridgedGMPContentParent(otherProcess,
+                                                 getter_AddRefs(parent));
+    if (!alreadyBridgedTo.Contains(otherProcess)) {
+      parent->SetDisplayName(displayName);
+      parent->SetPluginId(pluginId);
+    }
+
+    mCallback->Done(parent);
+  }
+
+private:
+  nsCString mNodeId;
+  nsCString mAPI;
+  const nsTArray<nsCString> mTags;
+  UniquePtr<GetGMPContentParentCallback> mCallback;
+};
+
+bool
+GeckoMediaPluginServiceChild::GetContentParentFrom(const nsACString& aNodeId,
+                                                   const nsCString& aAPI,
+                                                   const nsTArray<nsCString>& aTags,
+                                                   UniquePtr<GetGMPContentParentCallback>&& aCallback)
+{
+  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
+
+  UniquePtr<GetServiceChildCallback> callback(
+    new GetContentParentFromDone(aNodeId, aAPI, aTags, Move(aCallback)));
+  GetServiceChild(Move(callback));
+
+  return true;
+}
+
+NS_IMETHODIMP
+GeckoMediaPluginServiceChild::GetPluginVersionForAPI(const nsACString& aAPI,
+                                                     nsTArray<nsCString>* aTags,
+                                                     bool* aHasPlugin,
+                                                     nsACString& aOutVersion)
+{
+  dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
+  if (!contentChild) {
+    return NS_ERROR_FAILURE;
+  }
+
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsCString version;
+  bool ok = contentChild->SendGetGMPPluginVersionForAPI(nsCString(aAPI), *aTags,
+                                                        aHasPlugin, &version);
+  aOutVersion = version;
+  return ok ? NS_OK : NS_ERROR_FAILURE;
+}
+
+class GetNodeIdDone : public GetServiceChildCallback
+{
+public:
+  GetNodeIdDone(const nsAString& aOrigin, const nsAString& aTopLevelOrigin,
+                bool aInPrivateBrowsing,
+                UniquePtr<GetNodeIdCallback>&& aCallback)
+    : mOrigin(aOrigin),
+      mTopLevelOrigin(aTopLevelOrigin),
+      mInPrivateBrowsing(aInPrivateBrowsing),
+      mCallback(Move(aCallback))
+  {
+  }
+
+  virtual void Done(GMPServiceChild* aGMPServiceChild)
+  {
+    if (!aGMPServiceChild) {
+      mCallback->Done(NS_ERROR_FAILURE, EmptyCString());
+      return;
+    }
+
+    nsCString outId;
+    if (!aGMPServiceChild->SendGetGMPNodeId(mOrigin, mTopLevelOrigin,
+                                            mInPrivateBrowsing, &outId)) {
+      mCallback->Done(NS_ERROR_FAILURE, EmptyCString());
+      return;
+    }
+
+    mCallback->Done(NS_OK, outId);
+  }
+
+private:
+  nsString mOrigin;
+  nsString mTopLevelOrigin;
+  bool mInPrivateBrowsing;
+  UniquePtr<GetNodeIdCallback> mCallback;
+};
+
+NS_IMETHODIMP
+GeckoMediaPluginServiceChild::GetNodeId(const nsAString& aOrigin,
+                                        const nsAString& aTopLevelOrigin,
+                                        bool aInPrivateBrowsing,
+                                        UniquePtr<GetNodeIdCallback>&& aCallback)
+{
+  UniquePtr<GetServiceChildCallback> callback(
+    new GetNodeIdDone(aOrigin, aTopLevelOrigin, aInPrivateBrowsing,
+                      Move(aCallback)));
+  GetServiceChild(Move(callback));
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+GeckoMediaPluginServiceChild::Observe(nsISupports* aSubject,
+                                      const char* aTopic,
+                                      const char16_t* aSomeData)
+{
+  LOGD(("%s::%s: %s", __CLASS__, __FUNCTION__, aTopic));
+  if (!strcmp(NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, aTopic)) {
+    if (mServiceChild) {
+      mozilla::SyncRunnable::DispatchToThread(mGMPThread,
+                                              WrapRunnable(mServiceChild.get(),
+                                                           &PGMPServiceChild::Close));
+      mServiceChild = nullptr;
+    }
+    ShutdownGMPThread();
+  }
+
+  return NS_OK;
+}
+
+void
+GeckoMediaPluginServiceChild::GetServiceChild(UniquePtr<GetServiceChildCallback>&& aCallback)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  if (!mServiceChild) {
+    dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
+    if (!contentChild) {
+      return;
+    }
+    mGetServiceChildCallbacks.AppendElement(Move(aCallback));
+    if (mGetServiceChildCallbacks.Length() == 1) {
+        NS_DispatchToMainThread(WrapRunnable(contentChild,
+                                             &dom::ContentChild::SendCreateGMPService));
+    }
+    return;
+  }
+
+  aCallback->Done(mServiceChild.get());
+}
+
+void
+GeckoMediaPluginServiceChild::SetServiceChild(UniquePtr<GMPServiceChild>&& aServiceChild)
+{
+  mServiceChild = Move(aServiceChild);
+  nsTArray<UniquePtr<GetServiceChildCallback>> getServiceChildCallbacks;
+  getServiceChildCallbacks.SwapElements(mGetServiceChildCallbacks);
+  for (uint32_t i = 0, length = getServiceChildCallbacks.Length(); i < length; ++i) {
+    getServiceChildCallbacks[i]->Done(mServiceChild.get());
+  }
+}
+
+void
+GeckoMediaPluginServiceChild::RemoveGMPContentParent(GMPContentParent* aGMPContentParent)
+{
+  if (mServiceChild) {
+    mServiceChild->RemoveGMPContentParent(aGMPContentParent);
+  }
+}
+
+GMPServiceChild::GMPServiceChild()
+{
+}
+
+GMPServiceChild::~GMPServiceChild()
+{
+  XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                   new DeleteTask<Transport>(GetTransport()));
+}
+
+PGMPContentParent*
+GMPServiceChild::AllocPGMPContentParent(Transport* aTransport,
+                                        ProcessId aOtherPid)
+{
+  MOZ_ASSERT(!mContentParents.GetWeak(aOtherPid));
+
+  nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+  MOZ_ASSERT(mainThread);
+
+  nsRefPtr<GMPContentParent> parent = new GMPContentParent();
+
+  DebugOnly<bool> ok = parent->Open(aTransport, aOtherPid,
+                                    XRE_GetIOMessageLoop(),
+                                    mozilla::ipc::ParentSide);
+  MOZ_ASSERT(ok);
+
+  mContentParents.Put(aOtherPid, parent);
+  return parent;
+}
+
+void
+GMPServiceChild::GetBridgedGMPContentParent(ProcessId aOtherPid,
+                                            GMPContentParent** aGMPContentParent)
+{
+  mContentParents.Get(aOtherPid, aGMPContentParent);
+}
+
+static PLDHashOperator
+FindAndRemoveGMPContentParent(const uint64_t& aKey,
+                              nsRefPtr<GMPContentParent>& aData,
+                              void* aUserArg)
+{
+  return aData == aUserArg ?
+         (PLDHashOperator)(PL_DHASH_STOP | PL_DHASH_REMOVE) :
+         PL_DHASH_NEXT;
+}
+
+void
+GMPServiceChild::RemoveGMPContentParent(GMPContentParent* aGMPContentParent)
+{
+  mContentParents.Enumerate(FindAndRemoveGMPContentParent, aGMPContentParent);
+}
+
+static PLDHashOperator
+FillProcessIDArray(const uint64_t& aKey, GMPContentParent*, void* aUserArg)
+{
+  static_cast<nsTArray<base::ProcessId>*>(aUserArg)->AppendElement(aKey);
+  return PL_DHASH_NEXT;
+}
+
+void
+GMPServiceChild::GetAlreadyBridgedTo(nsTArray<base::ProcessId>& aAlreadyBridgedTo)
+{
+  aAlreadyBridgedTo.SetCapacity(mContentParents.Count());
+  mContentParents.EnumerateRead(FillProcessIDArray, &aAlreadyBridgedTo);
+}
+
+class OpenPGMPServiceChild : public nsRunnable
+{
+public:
+  OpenPGMPServiceChild(UniquePtr<GMPServiceChild>&& aGMPServiceChild,
+                       mozilla::ipc::Transport* aTransport,
+                       base::ProcessId aOtherPid)
+    : mGMPServiceChild(Move(aGMPServiceChild)),
+      mTransport(aTransport),
+      mOtherPid(aOtherPid)
+  {
+  }
+
+  NS_IMETHOD Run()
+  {
+    nsRefPtr<GeckoMediaPluginServiceChild> gmp =
+      GeckoMediaPluginServiceChild::GetSingleton();
+    MOZ_ASSERT(!gmp->mServiceChild);
+    if (mGMPServiceChild->Open(mTransport, mOtherPid, XRE_GetIOMessageLoop(),
+                               ipc::ChildSide)) {
+      gmp->SetServiceChild(Move(mGMPServiceChild));
+    } else {
+      gmp->SetServiceChild(nullptr);
+    }
+    return NS_OK;
+  }
+
+private:
+  UniquePtr<GMPServiceChild> mGMPServiceChild;
+  mozilla::ipc::Transport* mTransport;
+  base::ProcessId mOtherPid;
+};
+
+/* static */
+PGMPServiceChild*
+GMPServiceChild::Create(Transport* aTransport, ProcessId aOtherPid)
+{
+  nsRefPtr<GeckoMediaPluginServiceChild> gmp =
+    GeckoMediaPluginServiceChild::GetSingleton();
+  MOZ_ASSERT(!gmp->mServiceChild);
+
+  UniquePtr<GMPServiceChild> serviceChild(new GMPServiceChild());
+
+  nsCOMPtr<nsIThread> gmpThread;
+  nsresult rv = gmp->GetThread(getter_AddRefs(gmpThread));
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  GMPServiceChild* result = serviceChild.get();
+  rv = gmpThread->Dispatch(new OpenPGMPServiceChild(Move(serviceChild),
+                                                    aTransport,
+                                                    aOtherPid),
+                           NS_DISPATCH_NORMAL);
+  if (NS_FAILED(rv)) {
+    return nullptr;
+  }
+
+  return result;
+}
+
+} // namespace gmp
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/GMPServiceChild.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 GMPServiceChild_h_
+#define GMPServiceChild_h_
+
+#include "GMPService.h"
+#include "base/process.h"
+#include "mozilla/ipc/Transport.h"
+#include "mozilla/gmp/PGMPServiceChild.h"
+
+namespace mozilla {
+namespace gmp {
+
+#define GMP_DEFAULT_ASYNC_SHUTDONW_TIMEOUT 3000
+
+class GMPContentParent;
+class GMPServiceChild;
+class GetServiceChildCallback;
+
+class GeckoMediaPluginServiceChild : public GeckoMediaPluginService
+{
+  friend class GMPServiceChild;
+
+public:
+  static already_AddRefed<GeckoMediaPluginServiceChild> GetSingleton();
+
+  NS_IMETHOD GetPluginVersionForAPI(const nsACString& aAPI,
+                                    nsTArray<nsCString>* aTags,
+                                    bool* aHasPlugin,
+                                    nsACString& aOutVersion) override;
+  NS_IMETHOD GetNodeId(const nsAString& aOrigin,
+                       const nsAString& aTopLevelOrigin,
+                       bool aInPrivateBrowsingMode,
+                       UniquePtr<GetNodeIdCallback>&& aCallback) override;
+
+  NS_DECL_NSIOBSERVER
+
+  void SetServiceChild(UniquePtr<GMPServiceChild>&& aServiceChild);
+
+  void RemoveGMPContentParent(GMPContentParent* aGMPContentParent);
+
+protected:
+  virtual void InitializePlugins() override
+  {
+    // Nothing to do here.
+  }
+  virtual bool GetContentParentFrom(const nsACString& aNodeId,
+                                    const nsCString& aAPI,
+                                    const nsTArray<nsCString>& aTags,
+                                    UniquePtr<GetGMPContentParentCallback>&& aCallback)
+    override;
+
+private:
+  friend class OpenPGMPServiceChild;
+
+  void GetServiceChild(UniquePtr<GetServiceChildCallback>&& aCallback);
+
+  UniquePtr<GMPServiceChild> mServiceChild;
+  nsTArray<UniquePtr<GetServiceChildCallback>> mGetServiceChildCallbacks;
+};
+
+class GMPServiceChild : public PGMPServiceChild
+{
+public:
+  explicit GMPServiceChild();
+  virtual ~GMPServiceChild();
+
+  virtual PGMPContentParent* AllocPGMPContentParent(Transport* aTransport,
+                                                    ProcessId aOtherPid)
+    override;
+
+  void GetBridgedGMPContentParent(ProcessId aOtherPid,
+                                  GMPContentParent** aGMPContentParent);
+  void RemoveGMPContentParent(GMPContentParent* aGMPContentParent);
+
+  void GetAlreadyBridgedTo(nsTArray<ProcessId>& aAlreadyBridgedTo);
+
+  static PGMPServiceChild* Create(Transport* aTransport, ProcessId aOtherPid);
+
+private:
+  nsRefPtrHashtable<nsUint64HashKey, GMPContentParent> mContentParents;
+};
+
+} // namespace gmp
+} // namespace mozilla
+
+#endif // GMPServiceChild_h_
copy from dom/media/gmp/GMPService.cpp
copy to dom/media/gmp/GMPServiceParent.cpp
--- a/dom/media/gmp/GMPService.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -36,213 +36,90 @@
 
 namespace mozilla {
 
 #ifdef LOG
 #undef LOG
 #endif
 
 #ifdef PR_LOGGING
-PRLogModuleInfo*
-GetGMPLog()
-{
-  static PRLogModuleInfo *sLog;
-  if (!sLog)
-    sLog = PR_NewLogModule("GMP");
-  return sLog;
-}
-
 #define LOGD(msg) PR_LOG(GetGMPLog(), PR_LOG_DEBUG, msg)
 #define LOG(level, msg) PR_LOG(GetGMPLog(), (level), msg)
 #else
 #define LOGD(msg)
 #define LOG(leve1, msg)
 #endif
 
 #ifdef __CLASS__
 #undef __CLASS__
 #endif
 #define __CLASS__ "GMPService"
 
 namespace gmp {
 
 static const uint32_t NodeIdSaltLength = 32;
-static StaticRefPtr<GeckoMediaPluginService> sSingletonService;
 
-class GMPServiceCreateHelper final : public nsRunnable
+already_AddRefed<GeckoMediaPluginServiceParent>
+GeckoMediaPluginServiceParent::GetSingleton()
 {
-  nsRefPtr<GeckoMediaPluginService> mService;
-
-public:
-  static already_AddRefed<GeckoMediaPluginService>
-  GetOrCreate()
-  {
-    nsRefPtr<GeckoMediaPluginService> service;
-
-    if (NS_IsMainThread()) {
-      service = GetOrCreateOnMainThread();
-    } else {
-      nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
-      MOZ_ASSERT(mainThread);
-
-      nsRefPtr<GMPServiceCreateHelper> createHelper = new GMPServiceCreateHelper();
-
-      mozilla::SyncRunnable::DispatchToThread(mainThread, createHelper, true);
-
-      service = createHelper->mService.forget();
-    }
-
-    return service.forget();
-  }
-
-private:
-  GMPServiceCreateHelper()
-  {
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+  nsRefPtr<GeckoMediaPluginService> service(
+    GeckoMediaPluginServiceParent::GetGeckoMediaPluginService());
+#ifdef DEBUG
+  if (service) {
+    nsCOMPtr<mozIGeckoMediaPluginChromeService> chromeService;
+    CallQueryInterface(service.get(), getter_AddRefs(chromeService));
+    MOZ_ASSERT(chromeService);
   }
-
-  ~GMPServiceCreateHelper()
-  {
-    MOZ_ASSERT(!mService);
-  }
-
-  static already_AddRefed<GeckoMediaPluginService>
-  GetOrCreateOnMainThread()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    nsRefPtr<GeckoMediaPluginService> service = sSingletonService.get();
-    if (!service) {
-      service = new GeckoMediaPluginService();
-      service->Init();
-
-      sSingletonService = service;
-      ClearOnShutdown(&sSingletonService);
-    }
-
-    return service.forget();
-  }
-
-  NS_IMETHOD
-  Run()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    mService = GetOrCreateOnMainThread();
-    return NS_OK;
-  }
-};
-
-already_AddRefed<GeckoMediaPluginService>
-GeckoMediaPluginService::GetGeckoMediaPluginService()
-{
-  return GMPServiceCreateHelper::GetOrCreate();
+#endif
+  return service.forget().downcast<GeckoMediaPluginServiceParent>();
 }
 
-NS_IMPL_ISUPPORTS(GeckoMediaPluginService, mozIGeckoMediaPluginService, nsIObserver)
+NS_IMPL_ISUPPORTS_INHERITED(GeckoMediaPluginServiceParent,
+                            GeckoMediaPluginService,
+                            mozIGeckoMediaPluginChromeService)
 
 static int32_t sMaxAsyncShutdownWaitMs = 0;
 static bool sHaveSetTimeoutPrefCache = false;
 
-GeckoMediaPluginService::GeckoMediaPluginService()
-  : mMutex("GeckoMediaPluginService::mMutex")
-  , mShuttingDown(false)
-  , mShuttingDownOnGMPThread(false)
+GeckoMediaPluginServiceParent::GeckoMediaPluginServiceParent()
+  : mShuttingDown(false)
   , mScannedPluginOnDisk(false)
   , mWaitingForPluginsAsyncShutdown(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!sHaveSetTimeoutPrefCache) {
     sHaveSetTimeoutPrefCache = true;
     Preferences::AddIntVarCache(&sMaxAsyncShutdownWaitMs,
                                 "media.gmp.async-shutdown-timeout",
                                 GMP_DEFAULT_ASYNC_SHUTDONW_TIMEOUT);
   }
 }
 
-GeckoMediaPluginService::~GeckoMediaPluginService()
+GeckoMediaPluginServiceParent::~GeckoMediaPluginServiceParent()
 {
   MOZ_ASSERT(mPlugins.IsEmpty());
   MOZ_ASSERT(mAsyncShutdownPlugins.IsEmpty());
 }
 
 int32_t
-GeckoMediaPluginService::AsyncShutdownTimeoutMs()
+GeckoMediaPluginServiceParent::AsyncShutdownTimeoutMs()
 {
   MOZ_ASSERT(sHaveSetTimeoutPrefCache);
   return sMaxAsyncShutdownWaitMs;
 }
 
-void
-GeckoMediaPluginService::RemoveObsoletePluginCrashCallbacks()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  for (size_t i = mPluginCrashCallbacks.Length(); i != 0; --i) {
-    nsRefPtr<PluginCrashCallback>& callback = mPluginCrashCallbacks[i - 1];
-    if (!callback->IsStillValid()) {
-      LOGD(("%s::%s - Removing obsolete callback for pluginId %s",
-            __CLASS__, __FUNCTION__,
-            PromiseFlatCString(callback->PluginId()).get()));
-      mPluginCrashCallbacks.RemoveElementAt(i - 1);
-    }
-  }
-}
-
-void
-GeckoMediaPluginService::AddPluginCrashCallback(
-  nsRefPtr<PluginCrashCallback> aPluginCrashCallback)
-{
-  RemoveObsoletePluginCrashCallbacks();
-  mPluginCrashCallbacks.AppendElement(aPluginCrashCallback);
-}
-
-void
-GeckoMediaPluginService::RemovePluginCrashCallbacks(const nsACString& aPluginId)
-{
-  RemoveObsoletePluginCrashCallbacks();
-  for (size_t i = mPluginCrashCallbacks.Length(); i != 0; --i) {
-    nsRefPtr<PluginCrashCallback>& callback = mPluginCrashCallbacks[i - 1];
-    if (callback->PluginId() == aPluginId) {
-      mPluginCrashCallbacks.RemoveElementAt(i - 1);
-    }
-  }
-}
-
-void
-GeckoMediaPluginService::RunPluginCrashCallbacks(const nsACString& aPluginId,
-                                                 const nsACString& aPluginName,
-                                                 const nsAString& aPluginDumpId)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  LOGD(("%s::%s(%s)", __CLASS__, __FUNCTION__, aPluginId.Data()));
-  for (size_t i = mPluginCrashCallbacks.Length(); i != 0; --i) {
-    nsRefPtr<PluginCrashCallback>& callback = mPluginCrashCallbacks[i - 1];
-    const nsACString& callbackPluginId = callback->PluginId();
-    if (!callback->IsStillValid()) {
-      LOGD(("%s::%s(%s) - Removing obsolete callback for pluginId %s",
-            __CLASS__, __FUNCTION__, aPluginId.Data(),
-            PromiseFlatCString(callback->PluginId()).get()));
-      mPluginCrashCallbacks.RemoveElementAt(i - 1);
-    } else if (callbackPluginId == aPluginId) {
-      LOGD(("%s::%s(%s) - Running #%u",
-          __CLASS__, __FUNCTION__, aPluginId.Data(), i - 1));
-      callback->Run(aPluginName, aPluginDumpId);
-      mPluginCrashCallbacks.RemoveElementAt(i - 1);
-    }
-  }
-}
-
 nsresult
-GeckoMediaPluginService::Init()
+GeckoMediaPluginServiceParent::Init()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
   MOZ_ASSERT(obsService);
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "profile-change-teardown", false)));
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false)));
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "last-pb-context-exited", false)));
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "browser:purge-session-history", false)));
 
 #ifdef DEBUG
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "mediakeys-request", false)));
 #endif
 
   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
@@ -257,17 +134,17 @@ GeckoMediaPluginService::Init()
 
   // Kick off scanning for plugins
   nsCOMPtr<nsIThread> thread;
   return GetThread(getter_AddRefs(thread));
 }
 
 
 nsresult
-GeckoMediaPluginService::InitStorage()
+GeckoMediaPluginServiceParent::InitStorage()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // GMP storage should be used in the chrome process only.
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     return NS_OK;
   }
 
@@ -288,23 +165,23 @@ GeckoMediaPluginService::InitStorage()
     return rv;
   }
 
   rv = mStorageBaseDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
   if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)) {
     return rv;
   }
 
-  return NS_OK;
+  return GeckoMediaPluginService::Init();
 }
 
 NS_IMETHODIMP
-GeckoMediaPluginService::Observe(nsISupports* aSubject,
-                                 const char* aTopic,
-                                 const char16_t* aSomeData)
+GeckoMediaPluginServiceParent::Observe(nsISupports* aSubject,
+                                       const char* aTopic,
+                                       const char16_t* aSomeData)
 {
   LOGD(("%s::%s topic='%s' data='%s'", __CLASS__, __FUNCTION__,
        aTopic, NS_ConvertUTF16toUTF8(aSomeData).get()));
   if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
     nsCOMPtr<nsIPrefBranch> branch( do_QueryInterface(aSubject) );
     if (branch) {
       bool crashNow = false;
       if (NS_LITERAL_STRING("media.gmp.plugin.crash").Equals(aSomeData)) {
@@ -312,17 +189,18 @@ GeckoMediaPluginService::Observe(nsISupp
       }
       if (crashNow) {
         nsCOMPtr<nsIThread> gmpThread;
         {
           MutexAutoLock lock(mMutex);
           gmpThread = mGMPThread;
         }
         if (gmpThread) {
-          gmpThread->Dispatch(WrapRunnable(this, &GeckoMediaPluginService::CrashPlugins),
+          gmpThread->Dispatch(WrapRunnable(this,
+                                           &GeckoMediaPluginServiceParent::CrashPlugins),
                               NS_DISPATCH_NORMAL);
         }
       }
     }
   } else if (!strcmp("profile-change-teardown", aTopic)) {
 
     // How shutdown works:
     //
@@ -371,300 +249,122 @@ GeckoMediaPluginService::Observe(nsISupp
       MutexAutoLock lock(mMutex);
       MOZ_ASSERT(!mShuttingDown);
       mShuttingDown = true;
       gmpThread = mGMPThread;
     }
 
     if (gmpThread) {
       gmpThread->Dispatch(
-        NS_NewRunnableMethod(this, &GeckoMediaPluginService::UnloadPlugins),
+        NS_NewRunnableMethod(this,
+                             &GeckoMediaPluginServiceParent::UnloadPlugins),
         NS_DISPATCH_NORMAL);
     } else {
       MOZ_ASSERT(mPlugins.IsEmpty());
     }
 
     // Wait for plugins to do async shutdown...
     while (mWaitingForPluginsAsyncShutdown) {
       NS_ProcessNextEvent(NS_GetCurrentThread(), true);
     }
 
   } else if (!strcmp(NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, aTopic)) {
-    nsCOMPtr<nsIThread> gmpThread;
-    {
-      MutexAutoLock lock(mMutex);
-      // XXX The content process never gets profile-change-teardown, so mShuttingDown
-      // will always be false here. GMPService needs to be proxied to the parent.
-      // See bug 1057908.
-      MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default || mShuttingDown);
-      mGMPThread.swap(gmpThread);
-    }
-
-    if (gmpThread) {
-      gmpThread->Shutdown();
-    }
+    MOZ_ASSERT(mShuttingDown);
+    ShutdownGMPThread();
   } else if (!strcmp("last-pb-context-exited", aTopic)) {
     // When Private Browsing mode exits, all we need to do is clear
     // mTempNodeIds. This drops all the node ids we've cached in memory
     // for PB origin-pairs. If we try to open an origin-pair for non-PB
     // mode, we'll get the NodeId salt stored on-disk, and if we try to
     // open a PB mode origin-pair, we'll re-generate new salt.
     mTempNodeIds.Clear();
   } else if (!strcmp("browser:purge-session-history", aTopic)) {
     // Clear everything!
     if (!aSomeData || nsDependentString(aSomeData).IsEmpty()) {
       return GMPDispatch(NS_NewRunnableMethod(
-          this, &GeckoMediaPluginService::ClearStorage));
+          this, &GeckoMediaPluginServiceParent::ClearStorage));
     }
 
     // Clear nodeIds/records modified after |t|.
     nsresult rv;
     PRTime t = nsDependentString(aSomeData).ToInteger64(&rv, 10);
     if (NS_FAILED(rv)) {
       return rv;
     }
     return GMPDispatch(NS_NewRunnableMethodWithArg<PRTime>(
-        this, &GeckoMediaPluginService::ClearRecentHistoryOnGMPThread, t));
+        this, &GeckoMediaPluginServiceParent::ClearRecentHistoryOnGMPThread,
+        t));
   }
 
   return NS_OK;
 }
 
-nsresult
-GeckoMediaPluginService::GMPDispatch(nsIRunnable* event, uint32_t flags)
-{
-  nsCOMPtr<nsIRunnable> r(event);
-  nsCOMPtr<nsIThread> thread;
-  nsresult rv = GetThread(getter_AddRefs(thread));
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-  return thread->Dispatch(r, flags);
-}
-
-// always call with getter_AddRefs, because it does
-NS_IMETHODIMP
-GeckoMediaPluginService::GetThread(nsIThread** aThread)
-{
-  MOZ_ASSERT(aThread);
-
-  // This can be called from any thread.
-  MutexAutoLock lock(mMutex);
-
-  if (!mGMPThread) {
-    // Don't allow the thread to be created after shutdown has started.
-    if (mShuttingDown) {
-      return NS_ERROR_FAILURE;
-    }
-
-    nsresult rv = NS_NewNamedThread("GMPThread", getter_AddRefs(mGMPThread));
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-
-    // Tell the thread to initialize plugins
-    mGMPThread->Dispatch(NS_NewRunnableMethod(this, &GeckoMediaPluginService::LoadFromEnvironment), NS_DISPATCH_NORMAL);
-  }
-
-  NS_ADDREF(mGMPThread);
-  *aThread = mGMPThread;
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-GeckoMediaPluginService::GetGMPAudioDecoder(nsTArray<nsCString>* aTags,
-                                            const nsACString& aNodeId,
-                                            GMPAudioDecoderProxy** aGMPAD)
+bool
+GeckoMediaPluginServiceParent::GetContentParentFrom(const nsACString& aNodeId,
+                                                    const nsCString& aAPI,
+                                                    const nsTArray<nsCString>& aTags,
+                                                    UniquePtr<GetGMPContentParentCallback>&& aCallback)
 {
-  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
-  NS_ENSURE_ARG(aTags && aTags->Length() > 0);
-  NS_ENSURE_ARG(aGMPAD);
-
-  if (mShuttingDownOnGMPThread) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsRefPtr<GMPParent> gmp = SelectPluginForAPI(aNodeId,
-                                               NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
-                                               *aTags);
-  if (!gmp) {
-    return NS_ERROR_FAILURE;
-  }
-
-  GMPAudioDecoderParent* gmpADP;
-  nsresult rv = gmp->GetGMPAudioDecoder(&gmpADP);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  *aGMPAD = gmpADP;
+  nsRefPtr<GMPParent> gmp = SelectPluginForAPI(aNodeId, aAPI, aTags);
 
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-GeckoMediaPluginService::GetGMPVideoDecoder(nsTArray<nsCString>* aTags,
-                                            const nsACString& aNodeId,
-                                            GMPVideoHost** aOutVideoHost,
-                                            GMPVideoDecoderProxy** aGMPVD)
-{
-  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
-  NS_ENSURE_ARG(aTags && aTags->Length() > 0);
-  NS_ENSURE_ARG(aOutVideoHost);
-  NS_ENSURE_ARG(aGMPVD);
-
-  if (mShuttingDownOnGMPThread) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsRefPtr<GMPParent> gmp = SelectPluginForAPI(aNodeId,
-                                               NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
-                                               *aTags);
 #ifdef PR_LOGGING
-  nsCString api = (*aTags)[0];
+  nsCString api = aTags[0];
   LOGD(("%s: %p returning %p for api %s", __FUNCTION__, (void *)this, (void *)gmp, api.get()));
 #endif
+
   if (!gmp) {
-    return NS_ERROR_FAILURE;
-  }
-
-
-  GMPVideoDecoderParent* gmpVDP;
-  nsresult rv = gmp->GetGMPVideoDecoder(&gmpVDP);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  *aGMPVD = gmpVDP;
-  *aOutVideoHost = &gmpVDP->Host();
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-GeckoMediaPluginService::GetGMPVideoEncoder(nsTArray<nsCString>* aTags,
-                                            const nsACString& aNodeId,
-                                            GMPVideoHost** aOutVideoHost,
-                                            GMPVideoEncoderProxy** aGMPVE)
-{
-  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
-  NS_ENSURE_ARG(aTags && aTags->Length() > 0);
-  NS_ENSURE_ARG(aOutVideoHost);
-  NS_ENSURE_ARG(aGMPVE);
-
-  if (mShuttingDownOnGMPThread) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsRefPtr<GMPParent> gmp = SelectPluginForAPI(aNodeId,
-                                               NS_LITERAL_CSTRING(GMP_API_VIDEO_ENCODER),
-                                               *aTags);
-#ifdef PR_LOGGING
-  nsCString api = (*aTags)[0];
-  LOGD(("%s: %p returning %p for api %s", __FUNCTION__, (void *)this, (void *)gmp, api.get()));
-#endif
-  if (!gmp) {
-    return NS_ERROR_FAILURE;
-  }
-
-  GMPVideoEncoderParent* gmpVEP;
-  nsresult rv = gmp->GetGMPVideoEncoder(&gmpVEP);
-  if (NS_FAILED(rv)) {
-    return rv;
+    return false;
   }
 
-  *aGMPVE = gmpVEP;
-  *aOutVideoHost = &gmpVEP->Host();
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-GeckoMediaPluginService::GetGMPDecryptor(nsTArray<nsCString>* aTags,
-                                         const nsACString& aNodeId,
-                                         GMPDecryptorProxy** aDecryptor)
-{
-#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
-  if (!SandboxInfo::Get().CanSandboxMedia()) {
-    NS_WARNING("GeckoMediaPluginService::GetGMPDecryptor: "
-               "EME decryption not available without sandboxing support.");
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-#endif
-
-  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
-  NS_ENSURE_ARG(aTags && aTags->Length() > 0);
-  NS_ENSURE_ARG(aDecryptor);
-
-  if (mShuttingDownOnGMPThread) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsRefPtr<GMPParent> gmp = SelectPluginForAPI(aNodeId,
-                                               NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
-                                               *aTags);
-
-  if (!gmp) {
-    // XXX to remove in bug 1147692
-    gmp = SelectPluginForAPI(aNodeId,
-                             NS_LITERAL_CSTRING(GMP_API_DECRYPTOR_COMPAT),
-                             *aTags);
-  }
-
-  if (!gmp) {
-    return NS_ERROR_FAILURE;
-  }
-
-  GMPDecryptorParent* ksp;
-  nsresult rv = gmp->GetGMPDecryptor(&ksp);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  *aDecryptor = static_cast<GMPDecryptorProxy*>(ksp);
-
-  return NS_OK;
+  return gmp->GetGMPContentParent(Move(aCallback));
 }
 
 void
-GeckoMediaPluginService::AsyncShutdownNeeded(GMPParent* aParent)
+GeckoMediaPluginServiceParent::InitializePlugins()
+{
+  mGMPThread->Dispatch(
+    NS_NewRunnableMethod(this, &GeckoMediaPluginServiceParent::LoadFromEnvironment),
+    NS_DISPATCH_NORMAL);
+}
+
+void
+GeckoMediaPluginServiceParent::AsyncShutdownNeeded(GMPParent* aParent)
 {
   LOGD(("%s::%s %p", __CLASS__, __FUNCTION__, aParent));
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
 
   MOZ_ASSERT(!mAsyncShutdownPlugins.Contains(aParent));
   mAsyncShutdownPlugins.AppendElement(aParent);
 }
 
 void
-GeckoMediaPluginService::AsyncShutdownComplete(GMPParent* aParent)
+GeckoMediaPluginServiceParent::AsyncShutdownComplete(GMPParent* aParent)
 {
   LOGD(("%s::%s %p", __CLASS__, __FUNCTION__, aParent));
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
 
   mAsyncShutdownPlugins.RemoveElement(aParent);
   if (mAsyncShutdownPlugins.IsEmpty() && mShuttingDownOnGMPThread) {
     // The main thread may be waiting for async shutdown of plugins,
     // which has completed. Break the main thread out of its waiting loop.
     nsCOMPtr<nsIRunnable> task(NS_NewRunnableMethod(
-      this, &GeckoMediaPluginService::SetAsyncShutdownComplete));
+      this, &GeckoMediaPluginServiceParent::SetAsyncShutdownComplete));
     NS_DispatchToMainThread(task);
   }
 }
 
 void
-GeckoMediaPluginService::SetAsyncShutdownComplete()
+GeckoMediaPluginServiceParent::SetAsyncShutdownComplete()
 {
   MOZ_ASSERT(NS_IsMainThread());
   mWaitingForPluginsAsyncShutdown = false;
 }
 
 void
-GeckoMediaPluginService::UnloadPlugins()
+GeckoMediaPluginServiceParent::UnloadPlugins()
 {
   LOGD(("%s::%s async_shutdown=%d", __CLASS__, __FUNCTION__,
         mAsyncShutdownPlugins.Length()));
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
 
   MOZ_ASSERT(!mShuttingDownOnGMPThread);
   mShuttingDownOnGMPThread = true;
 
@@ -675,35 +375,35 @@ GeckoMediaPluginService::UnloadPlugins()
     for (size_t i = 0; i < mPlugins.Length(); i++) {
       mPlugins[i]->CloseActive(true);
     }
     mPlugins.Clear();
   }
 
   if (mAsyncShutdownPlugins.IsEmpty()) {
     nsCOMPtr<nsIRunnable> task(NS_NewRunnableMethod(
-      this, &GeckoMediaPluginService::SetAsyncShutdownComplete));
+      this, &GeckoMediaPluginServiceParent::SetAsyncShutdownComplete));
     NS_DispatchToMainThread(task);
   }
 }
 
 void
-GeckoMediaPluginService::CrashPlugins()
+GeckoMediaPluginServiceParent::CrashPlugins()
 {
   LOGD(("%s::%s", __CLASS__, __FUNCTION__));
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
 
   MutexAutoLock lock(mMutex);
   for (size_t i = 0; i < mPlugins.Length(); i++) {
     mPlugins[i]->Crash();
   }
 }
 
 void
-GeckoMediaPluginService::LoadFromEnvironment()
+GeckoMediaPluginServiceParent::LoadFromEnvironment()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
 
   const char* env = PR_GetEnv("MOZ_GMP_PATH");
   if (!env || !*env) {
     return;
   }
 
@@ -725,86 +425,90 @@ GeckoMediaPluginService::LoadFromEnviron
       pos = next + 1;
     }
   }
 
   mScannedPluginOnDisk = true;
 }
 
 NS_IMETHODIMP
-GeckoMediaPluginService::PathRunnable::Run()
+GeckoMediaPluginServiceParent::PathRunnable::Run()
 {
   if (mOperation == ADD) {
     mService->AddOnGMPThread(mPath);
   } else {
     mService->RemoveOnGMPThread(mPath,
                                 mOperation == REMOVE_AND_DELETE_FROM_DISK);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-GeckoMediaPluginService::AddPluginDirectory(const nsAString& aDirectory)
+GeckoMediaPluginServiceParent::AddPluginDirectory(const nsAString& aDirectory)
 {
   MOZ_ASSERT(NS_IsMainThread());
   return GMPDispatch(new PathRunnable(this, aDirectory,
                                       PathRunnable::EOperation::ADD));
 }
 
 NS_IMETHODIMP
-GeckoMediaPluginService::RemovePluginDirectory(const nsAString& aDirectory)
+GeckoMediaPluginServiceParent::RemovePluginDirectory(const nsAString& aDirectory)
 {
   MOZ_ASSERT(NS_IsMainThread());
   return GMPDispatch(new PathRunnable(this, aDirectory,
                                       PathRunnable::EOperation::REMOVE));
 }
 
 NS_IMETHODIMP
-GeckoMediaPluginService::RemoveAndDeletePluginDirectory(
+GeckoMediaPluginServiceParent::RemoveAndDeletePluginDirectory(
   const nsAString& aDirectory)
 {
   MOZ_ASSERT(NS_IsMainThread());
   return GMPDispatch(
     new PathRunnable(this, aDirectory,
                      PathRunnable::EOperation::REMOVE_AND_DELETE_FROM_DISK));
 }
 
 class DummyRunnable : public nsRunnable {
 public:
   NS_IMETHOD Run() { return NS_OK; }
 };
 
 NS_IMETHODIMP
-GeckoMediaPluginService::GetPluginVersionForAPI(const nsACString& aAPI,
-                                                nsTArray<nsCString>* aTags,
-                                                nsACString& aOutVersion)
+GeckoMediaPluginServiceParent::GetPluginVersionForAPI(const nsACString& aAPI,
+                                                      nsTArray<nsCString>* aTags,
+                                                      bool* aHasPlugin,
+                                                      nsACString& aOutVersion)
 {
   NS_ENSURE_ARG(aTags && aTags->Length() > 0);
+  NS_ENSURE_ARG(aHasPlugin);
 
   nsresult rv = EnsurePluginsOnDiskScanned();
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to load GMPs from disk.");
     return rv;
   }
 
   {
     MutexAutoLock lock(mMutex);
     nsCString api(aAPI);
     GMPParent* gmp = FindPluginForAPIFrom(0, api, *aTags, nullptr);
-    if (!gmp) {
-      return NS_ERROR_FAILURE;
+    if (gmp) {
+      *aHasPlugin = true;
+      aOutVersion = gmp->GetVersion();
+    } else {
+      *aHasPlugin = false;
     }
-    aOutVersion = gmp->GetVersion();
   }
 
   return NS_OK;
 }
 
 nsresult
-GeckoMediaPluginService::EnsurePluginsOnDiskScanned()
+GeckoMediaPluginServiceParent::EnsurePluginsOnDiskScanned()
 {
   const char* env = nullptr;
   if (!mScannedPluginOnDisk && (env = PR_GetEnv("MOZ_GMP_PATH")) && *env) {
     // We have a MOZ_GMP_PATH environment variable which may specify the
     // location of plugins to load, and we haven't yet scanned the disk to
     // see if there are plugins there. Get the GMP thread, which will
     // cause an event to be dispatched to which scans for plugins. We
     // dispatch a sync event to the GMP thread here in order to wait until
@@ -812,45 +516,21 @@ GeckoMediaPluginService::EnsurePluginsOn
     nsresult rv = GMPDispatch(new DummyRunnable(), NS_DISPATCH_SYNC);
     NS_ENSURE_SUCCESS(rv, rv);
     MOZ_ASSERT(mScannedPluginOnDisk, "Should have scanned MOZ_GMP_PATH by now");
   }
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-GeckoMediaPluginService::HasPluginForAPI(const nsACString& aAPI,
-                                         nsTArray<nsCString>* aTags,
-                                         bool* aOutHavePlugin)
-{
-  NS_ENSURE_ARG(aTags && aTags->Length() > 0);
-  NS_ENSURE_ARG(aOutHavePlugin);
-
-  nsresult rv = EnsurePluginsOnDiskScanned();
-  if (NS_FAILED(rv)) {
-    NS_WARNING("Failed to load GMPs from disk.");
-    return rv;
-  }
-
-  {
-    MutexAutoLock lock(mMutex);
-    nsCString api(aAPI);
-    GMPParent* gmp = FindPluginForAPIFrom(0, api, *aTags, nullptr);
-    *aOutHavePlugin = (gmp != nullptr);
-  }
-
-  return NS_OK;
-}
-
 GMPParent*
-GeckoMediaPluginService::FindPluginForAPIFrom(size_t aSearchStartIndex,
-                                              const nsCString& aAPI,
-                                              const nsTArray<nsCString>& aTags,
-                                              size_t* aOutPluginIndex)
+GeckoMediaPluginServiceParent::FindPluginForAPIFrom(size_t aSearchStartIndex,
+                                                    const nsCString& aAPI,
+                                                    const nsTArray<nsCString>& aTags,
+                                                    size_t* aOutPluginIndex)
 {
   mMutex.AssertCurrentThreadOwns();
   for (size_t i = aSearchStartIndex; i < mPlugins.Length(); i++) {
     GMPParent* gmp = mPlugins[i];
     bool supportsAllTags = true;
     for (size_t t = 0; t < aTags.Length(); t++) {
       const nsCString& tag = aTags.ElementAt(t);
       if (!gmp->SupportsAPI(aAPI, tag)) {
@@ -865,19 +545,19 @@ GeckoMediaPluginService::FindPluginForAP
       *aOutPluginIndex = i;
     }
     return gmp;
   }
   return nullptr;
 }
 
 GMPParent*
-GeckoMediaPluginService::SelectPluginForAPI(const nsACString& aNodeId,
-                                            const nsCString& aAPI,
-                                            const nsTArray<nsCString>& aTags)
+GeckoMediaPluginServiceParent::SelectPluginForAPI(const nsACString& aNodeId,
+                                                  const nsCString& aAPI,
+                                                  const nsTArray<nsCString>& aTags)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread,
              "Can't clone GMP plugins on non-GMP threads.");
 
   GMPParent* gmpToClone = nullptr;
   {
     MutexAutoLock lock(mMutex);
     size_t index = 0;
@@ -906,16 +586,23 @@ GeckoMediaPluginService::SelectPluginFor
   if (gmpToClone) {
     GMPParent* clone = ClonePlugin(gmpToClone);
     if (!aNodeId.IsEmpty()) {
       clone->SetNodeId(aNodeId);
     }
     return clone;
   }
 
+  if (aAPI.EqualsLiteral(GMP_API_DECRYPTOR)) {
+    // XXX to remove in bug 1147692
+    return SelectPluginForAPI(aNodeId,
+                              NS_LITERAL_CSTRING(GMP_API_DECRYPTOR_COMPAT),
+                              aTags);
+  }
+
   return nullptr;
 }
 
 class CreateGMPParentTask : public nsRunnable {
 public:
   NS_IMETHOD Run() {
     MOZ_ASSERT(NS_IsMainThread());
 #if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
@@ -933,17 +620,17 @@ public:
   already_AddRefed<GMPParent> GetParent() {
     return mParent.forget();
   }
 private:
   nsRefPtr<GMPParent> mParent;
 };
 
 GMPParent*
-GeckoMediaPluginService::ClonePlugin(const GMPParent* aOriginal)
+GeckoMediaPluginServiceParent::ClonePlugin(const GMPParent* aOriginal)
 {
   MOZ_ASSERT(aOriginal);
 
   // The GMPParent inherits from IToplevelProtocol, which must be created
   // on the main thread to be threadsafe. See Bug 1035653.
   nsRefPtr<CreateGMPParentTask> task(new CreateGMPParentTask());
   if (!NS_IsMainThread()) {
     nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
@@ -980,17 +667,17 @@ public:
     return NS_OK;
   }
 private:
   ~NotifyObserversTask() {}
   const char* mTopic;
 };
 
 void
-GeckoMediaPluginService::AddOnGMPThread(const nsAString& aDirectory)
+GeckoMediaPluginServiceParent::AddOnGMPThread(const nsAString& aDirectory)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   LOGD(("%s::%s: %s", __CLASS__, __FUNCTION__, NS_LossyConvertUTF16toASCII(aDirectory).get()));
 
   nsCOMPtr<nsIFile> directory;
   nsresult rv = NS_NewLocalFile(aDirectory, false, getter_AddRefs(directory));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
@@ -1013,18 +700,18 @@ GeckoMediaPluginService::AddOnGMPThread(
     MutexAutoLock lock(mMutex);
     mPlugins.AppendElement(gmp);
   }
 
   NS_DispatchToMainThread(new NotifyObserversTask("gmp-path-added"), NS_DISPATCH_NORMAL);
 }
 
 void
-GeckoMediaPluginService::RemoveOnGMPThread(const nsAString& aDirectory,
-                                           const bool aDeleteFromDisk)
+GeckoMediaPluginServiceParent::RemoveOnGMPThread(const nsAString& aDirectory,
+                                                 const bool aDeleteFromDisk)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   LOGD(("%s::%s: %s", __CLASS__, __FUNCTION__, NS_LossyConvertUTF16toASCII(aDirectory).get()));
 
   nsCOMPtr<nsIFile> directory;
   nsresult rv = NS_NewLocalFile(aDirectory, false, getter_AddRefs(directory));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
@@ -1052,17 +739,17 @@ GeckoMediaPluginService::RemoveOnGMPThre
 // May remove when Bug 1043671 is fixed
 static void Dummy(nsRefPtr<GMPParent>& aOnDeathsDoor)
 {
   // exists solely to do nothing and let the Runnable kill the GMPParent
   // when done.
 }
 
 void
-GeckoMediaPluginService::ReAddOnGMPThread(nsRefPtr<GMPParent>& aOld)
+GeckoMediaPluginServiceParent::ReAddOnGMPThread(nsRefPtr<GMPParent>& aOld)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, (void*) aOld));
 
   nsRefPtr<GMPParent> gmp;
   if (!mShuttingDownOnGMPThread) {
     // Don't re-add plugin if we're shutting down. Let the old plugin die.
     gmp = ClonePlugin(aOld);
@@ -1074,17 +761,17 @@ GeckoMediaPluginService::ReAddOnGMPThrea
   mPlugins.RemoveElement(aOld);
 
   // Schedule aOld to be destroyed.  We can't destroy it from here since we
   // may be inside ActorDestroyed() for it.
   NS_DispatchToCurrentThread(WrapRunnableNM(&Dummy, aOld));
 }
 
 NS_IMETHODIMP
-GeckoMediaPluginService::GetStorageDir(nsIFile** aOutFile)
+GeckoMediaPluginServiceParent::GetStorageDir(nsIFile** aOutFile)
 {
   if (NS_WARN_IF(!mStorageBaseDir)) {
     return NS_ERROR_FAILURE;
   }
   return mStorageBaseDir->Clone(aOutFile);
 }
 
 static nsresult
@@ -1162,30 +849,30 @@ nsresult
 ReadSalt(nsIFile* aPath, nsACString& aOutData)
 {
   return ReadFromFile(aPath, NS_LITERAL_CSTRING("salt"),
                       aOutData, NodeIdSaltLength);
 
 }
 
 NS_IMETHODIMP
-GeckoMediaPluginService::IsPersistentStorageAllowed(const nsACString& aNodeId,
-                                                    bool* aOutAllowed)
+GeckoMediaPluginServiceParent::IsPersistentStorageAllowed(const nsACString& aNodeId,
+                                                          bool* aOutAllowed)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   NS_ENSURE_ARG(aOutAllowed);
   *aOutAllowed = mPersistentStorageAllowed.Get(aNodeId);
   return NS_OK;
 }
 
-NS_IMETHODIMP
-GeckoMediaPluginService::GetNodeId(const nsAString& aOrigin,
-                                   const nsAString& aTopLevelOrigin,
-                                   bool aInPrivateBrowsing,
-                                   nsACString& aOutId)
+nsresult
+GeckoMediaPluginServiceParent::GetNodeId(const nsAString& aOrigin,
+                                         const nsAString& aTopLevelOrigin,
+                                         bool aInPrivateBrowsing,
+                                         nsACString& aOutId)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   LOGD(("%s::%s: (%s, %s), %s", __CLASS__, __FUNCTION__,
        NS_ConvertUTF16toUTF8(aOrigin).get(),
        NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
        (aInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing")));
 
   nsresult rv;
@@ -1319,16 +1006,28 @@ GeckoMediaPluginService::GetNodeId(const
   }
 
   aOutId = salt;
   mPersistentStorageAllowed.Put(salt, true);
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+GeckoMediaPluginServiceParent::GetNodeId(const nsAString& aOrigin,
+                                         const nsAString& aTopLevelOrigin,
+                                         bool aInPrivateBrowsing,
+                                         UniquePtr<GetNodeIdCallback>&& aCallback)
+{
+  nsCString nodeId;
+  nsresult rv = GetNodeId(aOrigin, aTopLevelOrigin, aInPrivateBrowsing, nodeId);
+  aCallback->Done(rv, nodeId);
+  return rv;
+}
+
 bool
 MatchOrigin(nsIFile* aPath, const nsACString& aOrigin)
 {
   // http://en.wikipedia.org/wiki/Domain_Name_System#Domain_name_syntax
   static const uint32_t MaxDomainLength = 253;
 
   nsresult rv;
   nsCString str;
@@ -1391,17 +1090,17 @@ struct NodeFilter {
   bool operator()(GMPParent* aParent) {
     return mNodeIDs.Contains(aParent->GetNodeId());
   }
 private:
   const nsTArray<nsCString>& mNodeIDs;
 };
 
 void
-GeckoMediaPluginService::ClearNodeIdAndPlugin(DirectoryFilter& aFilter)
+GeckoMediaPluginServiceParent::ClearNodeIdAndPlugin(DirectoryFilter& aFilter)
 {
   nsresult rv;
   nsCOMPtr<nsIFile> path;
 
   // $profileDir/gmp/
   rv = GetStorageDir(getter_AddRefs(path));
   if (NS_FAILED(rv)) {
     return;
@@ -1487,17 +1186,17 @@ GeckoMediaPluginService::ClearNodeIdAndP
 
     if (NS_FAILED(DeleteDir(dirEntry))) {
       NS_WARNING("Failed to delete GMP storage directory for the node");
     }
   }
 }
 
 void
-GeckoMediaPluginService::ForgetThisSiteOnGMPThread(const nsACString& aOrigin)
+GeckoMediaPluginServiceParent::ForgetThisSiteOnGMPThread(const nsACString& aOrigin)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   LOGD(("%s::%s: origin=%s", __CLASS__, __FUNCTION__, aOrigin.Data()));
 
   struct OriginFilter : public DirectoryFilter {
     explicit OriginFilter(const nsACString& aOrigin) : mOrigin(aOrigin) {}
     virtual bool operator()(nsIFile* aPath) {
       return MatchOrigin(aPath, mOrigin);
@@ -1505,17 +1204,17 @@ GeckoMediaPluginService::ForgetThisSiteO
   private:
     const nsACString& mOrigin;
   } filter(aOrigin);
 
   ClearNodeIdAndPlugin(filter);
 }
 
 void
-GeckoMediaPluginService::ClearRecentHistoryOnGMPThread(PRTime aSince)
+GeckoMediaPluginServiceParent::ClearRecentHistoryOnGMPThread(PRTime aSince)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   LOGD(("%s::%s: since=%lld", __CLASS__, __FUNCTION__, (int64_t)aSince));
 
   nsCOMPtr<nsIFile> storagePath;
   nsCOMPtr<nsIFile> temp;
   if (NS_SUCCEEDED(GetStorageDir(getter_AddRefs(temp))) &&
       NS_SUCCEEDED(temp->AppendNative(NS_LITERAL_CSTRING("storage")))) {
@@ -1592,30 +1291,30 @@ GeckoMediaPluginService::ClearRecentHist
   } filter(aSince, storagePath.forget());
 
   ClearNodeIdAndPlugin(filter);
 
   NS_DispatchToMainThread(new NotifyObserversTask("gmp-clear-storage-complete"), NS_DISPATCH_NORMAL);
 }
 
 NS_IMETHODIMP
-GeckoMediaPluginService::ForgetThisSite(const nsAString& aOrigin)
+GeckoMediaPluginServiceParent::ForgetThisSite(const nsAString& aOrigin)
 {
   MOZ_ASSERT(NS_IsMainThread());
   return GMPDispatch(NS_NewRunnableMethodWithArg<nsCString>(
-      this, &GeckoMediaPluginService::ForgetThisSiteOnGMPThread,
+      this, &GeckoMediaPluginServiceParent::ForgetThisSiteOnGMPThread,
       NS_ConvertUTF16toUTF8(aOrigin)));
 }
 
 static bool IsNodeIdValid(GMPParent* aParent) {
   return !aParent->GetNodeId().IsEmpty();
 }
 
 void
-GeckoMediaPluginService::ClearStorage()
+GeckoMediaPluginServiceParent::ClearStorage()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   LOGD(("%s::%s", __CLASS__, __FUNCTION__));
 
   // Kill plugins with valid nodeIDs.
   KillPlugins(mPlugins, mMutex, &IsNodeIdValid);
 
   nsCOMPtr<nsIFile> path; // $profileDir/gmp/
@@ -1625,10 +1324,143 @@ GeckoMediaPluginService::ClearStorage()
   }
 
   if (NS_FAILED(DeleteDir(path))) {
     NS_WARNING("Failed to delete GMP storage directory");
   }
   NS_DispatchToMainThread(new NotifyObserversTask("gmp-clear-storage-complete"), NS_DISPATCH_NORMAL);
 }
 
+GMPServiceParent::~GMPServiceParent()
+{
+  XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                   new DeleteTask<Transport>(GetTransport()));
+}
+
+bool
+GMPServiceParent::RecvLoadGMP(const nsCString& aNodeId,
+                              const nsCString& aAPI,
+                              nsTArray<nsCString>&& aTags,
+                              nsTArray<ProcessId>&& aAlreadyBridgedTo,
+                              ProcessId* aId,
+                              nsCString* aDisplayName,
+                              nsCString* aPluginId)
+{
+  nsRefPtr<GMPParent> gmp = mService->SelectPluginForAPI(aNodeId, aAPI, aTags);
+
+#ifdef PR_LOGGING
+  nsCString api = aTags[0];
+  LOGD(("%s: %p returning %p for api %s", __FUNCTION__, (void *)this, (void *)gmp, api.get()));
+#endif
+
+  if (!gmp || !gmp->EnsureProcessLoaded(aId)) {
+    return false;
+  }
+
+  *aDisplayName = gmp->GetDisplayName();
+  *aPluginId = gmp->GetPluginId();
+
+  return aAlreadyBridgedTo.Contains(*aId) || gmp->Bridge(this);
+}
+
+bool
+GMPServiceParent::RecvGetGMPNodeId(const nsString& aOrigin,
+                                   const nsString& aTopLevelOrigin,
+                                   const bool& aInPrivateBrowsing,
+                                   nsCString* aID)
+{
+  nsresult rv = mService->GetNodeId(aOrigin, aTopLevelOrigin,
+                                    aInPrivateBrowsing, *aID);
+  return NS_SUCCEEDED(rv);
+}
+
+/* static */
+bool
+GMPServiceParent::RecvGetGMPPluginVersionForAPI(const nsCString& aAPI,
+                                                nsTArray<nsCString>&& aTags,
+                                                bool* aHasPlugin,
+                                                nsCString* aVersion)
+{
+  nsRefPtr<GeckoMediaPluginServiceParent> service =
+    GeckoMediaPluginServiceParent::GetSingleton();
+  return service &&
+         NS_SUCCEEDED(service->GetPluginVersionForAPI(aAPI, &aTags, aHasPlugin,
+                                                      *aVersion));
+}
+
+class DeleteGMPServiceParent : public nsRunnable
+{
+public:
+  explicit DeleteGMPServiceParent(GMPServiceParent* aToDelete)
+    : mToDelete(aToDelete)
+  {
+  }
+
+  NS_IMETHODIMP Run()
+  {
+    return NS_OK;
+  }
+
+private:
+  nsAutoPtr<GMPServiceParent> mToDelete;
+};
+
+void
+GMPServiceParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  NS_DispatchToCurrentThread(new DeleteGMPServiceParent(this));
+}
+
+class OpenPGMPServiceParent : public nsRunnable
+{
+public:
+  OpenPGMPServiceParent(GMPServiceParent* aGMPServiceParent,
+                        mozilla::ipc::Transport* aTransport,
+                        base::ProcessId aOtherPid,
+                        bool* aResult)
+    : mGMPServiceParent(aGMPServiceParent),
+      mTransport(aTransport),
+      mOtherPid(aOtherPid),
+      mResult(aResult)
+  {
+  }
+
+  NS_IMETHOD Run()
+  {
+    *mResult = mGMPServiceParent->Open(mTransport, mOtherPid,
+                                       XRE_GetIOMessageLoop(), ipc::ParentSide);
+    return NS_OK;
+  }
+
+private:
+  GMPServiceParent* mGMPServiceParent;
+  mozilla::ipc::Transport* mTransport;
+  base::ProcessId mOtherPid;
+  bool* mResult;
+};
+
+/* static */
+PGMPServiceParent*
+GMPServiceParent::Create(Transport* aTransport, ProcessId aOtherPid)
+{
+  nsRefPtr<GeckoMediaPluginServiceParent> gmp =
+    GeckoMediaPluginServiceParent::GetSingleton();
+
+  nsAutoPtr<GMPServiceParent> serviceParent(new GMPServiceParent(gmp));
+
+  nsCOMPtr<nsIThread> gmpThread;
+  nsresult rv = gmp->GetThread(getter_AddRefs(gmpThread));
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  bool ok;
+  rv = gmpThread->Dispatch(new OpenPGMPServiceParent(serviceParent,
+                                                     aTransport,
+                                                     aOtherPid, &ok),
+                           NS_DISPATCH_SYNC);
+  if (NS_FAILED(rv) || !ok) {
+    return nullptr;
+  }
+
+  return serviceParent.forget();
+}
+
 } // namespace gmp
 } // namespace mozilla
copy from dom/media/gmp/GMPService.h
copy to dom/media/gmp/GMPServiceParent.h
--- a/dom/media/gmp/GMPService.h
+++ b/dom/media/gmp/GMPServiceParent.h
@@ -1,102 +1,81 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 GMPService_h_
-#define GMPService_h_
+#ifndef GMPServiceParent_h_
+#define GMPServiceParent_h_
 
-#include "nsString.h"
-#include "mozIGeckoMediaPluginService.h"
-#include "nsIObserver.h"
-#include "nsTArray.h"
-#include "mozilla/Attributes.h"
-#include "mozilla/Monitor.h"
-#include "nsString.h"
-#include "nsCOMPtr.h"
-#include "nsIThread.h"
-#include "nsThreadUtils.h"
-#include "nsITimer.h"
+#include "GMPService.h"
+#include "mozilla/gmp/PGMPServiceParent.h"
+#include "mozIGeckoMediaPluginChromeService.h"
 #include "nsClassHashtable.h"
 #include "nsDataHashtable.h"
 #include "mozilla/Atomics.h"
+#include "nsThreadUtils.h"
 
 template <class> struct already_AddRefed;
 
 namespace mozilla {
 namespace gmp {
 
 class GMPParent;
 
 #define GMP_DEFAULT_ASYNC_SHUTDONW_TIMEOUT 3000
 
-class GeckoMediaPluginService final : public mozIGeckoMediaPluginService
-                                    , public nsIObserver
+class GeckoMediaPluginServiceParent final : public GeckoMediaPluginService
+                                          , public mozIGeckoMediaPluginChromeService
 {
 public:
-  static already_AddRefed<GeckoMediaPluginService> GetGeckoMediaPluginService();
+  static already_AddRefed<GeckoMediaPluginServiceParent> GetSingleton();
+
+  GeckoMediaPluginServiceParent();
+  virtual nsresult Init() override;
+
+  NS_DECL_ISUPPORTS_INHERITED
 
-  GeckoMediaPluginService();
-  nsresult Init();
+  // mozIGeckoMediaPluginService
+  NS_IMETHOD GetPluginVersionForAPI(const nsACString& aAPI,
+                                    nsTArray<nsCString>* aTags,
+                                    bool* aHasPlugin,
+                                    nsACString& aOutVersion) override;
+  NS_IMETHOD GetNodeId(const nsAString& aOrigin,
+                       const nsAString& aTopLevelOrigin,
+                       bool aInPrivateBrowsingMode,
+                       UniquePtr<GetNodeIdCallback>&& aCallback) override;
 
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_MOZIGECKOMEDIAPLUGINSERVICE
+  NS_DECL_MOZIGECKOMEDIAPLUGINCHROMESERVICE
   NS_DECL_NSIOBSERVER
 
   void AsyncShutdownNeeded(GMPParent* aParent);
   void AsyncShutdownComplete(GMPParent* aParent);
   void AbortAsyncShutdown();
 
   int32_t AsyncShutdownTimeoutMs();
 
-  class PluginCrashCallback
-  {
-  public:
-    NS_INLINE_DECL_REFCOUNTING(PluginCrashCallback)
+private:
+  friend class GMPServiceParent;
 
-    PluginCrashCallback(const nsACString& aPluginId)
-      : mPluginId(aPluginId)
-    {
-      MOZ_ASSERT(NS_IsMainThread());
-    }
-    const nsACString& PluginId() const { return mPluginId; }
-    virtual void Run(const nsACString& aPluginName, const nsAString& aPluginDumpId) = 0;
-    virtual bool IsStillValid() = 0; // False if callback has become useless.
-  protected:
-    virtual ~PluginCrashCallback()
-    {
-      MOZ_ASSERT(NS_IsMainThread());
-    }
-  private:
-    const nsCString mPluginId;
-  };
-  void RemoveObsoletePluginCrashCallbacks(); // Called from add/remove/run.
-  void AddPluginCrashCallback(nsRefPtr<PluginCrashCallback> aPluginCrashCallback);
-  void RemovePluginCrashCallbacks(const nsACString& aPluginId);
-  void RunPluginCrashCallbacks(const nsACString& aPluginId,
-                               const nsACString& aPluginName,
-                               const nsAString& aPluginDumpId);
-
-private:
-  ~GeckoMediaPluginService();
-
-  nsresult GMPDispatch(nsIRunnable* event, uint32_t flags = NS_DISPATCH_NORMAL);
+  virtual ~GeckoMediaPluginServiceParent();
 
   void ClearStorage();
 
   GMPParent* SelectPluginForAPI(const nsACString& aNodeId,
                                 const nsCString& aAPI,
                                 const nsTArray<nsCString>& aTags);
   GMPParent* FindPluginForAPIFrom(size_t aSearchStartIndex,
                                   const nsCString& aAPI,
                                   const nsTArray<nsCString>& aTags,
                                   size_t* aOutPluginIndex);
 
+  nsresult GetNodeId(const nsAString& aOrigin, const nsAString& aTopLevelOrigin,
+                     bool aInPrivateBrowsing, nsACString& aOutId);
+
   void UnloadPlugins();
   void CrashPlugins();
   void SetAsyncShutdownComplete();
 
   void LoadFromEnvironment();
   void ProcessPossiblePlugin(nsIFile* aDir);
 
   void AddOnGMPThread(const nsAString& aDirectory);
@@ -112,52 +91,54 @@ private:
   void ClearNodeIdAndPlugin(DirectoryFilter& aFilter);
 
   void ForgetThisSiteOnGMPThread(const nsACString& aOrigin);
   void ClearRecentHistoryOnGMPThread(PRTime aSince);
 
 protected:
   friend class GMPParent;
   void ReAddOnGMPThread(nsRefPtr<GMPParent>& aOld);
+  virtual void InitializePlugins() override;
+  virtual bool GetContentParentFrom(const nsACString& aNodeId,
+                                    const nsCString& aAPI,
+                                    const nsTArray<nsCString>& aTags,
+                                    UniquePtr<GetGMPContentParentCallback>&& aCallback)
+    override;
 private:
   GMPParent* ClonePlugin(const GMPParent* aOriginal);
   nsresult EnsurePluginsOnDiskScanned();
   nsresult InitStorage();
 
   class PathRunnable : public nsRunnable
   {
   public:
     enum EOperation {
       ADD,
       REMOVE,
       REMOVE_AND_DELETE_FROM_DISK,
     };
 
-    PathRunnable(GeckoMediaPluginService* aService, const nsAString& aPath,
+    PathRunnable(GeckoMediaPluginServiceParent* aService, const nsAString& aPath,
                  EOperation aOperation)
       : mService(aService)
       , mPath(aPath)
       , mOperation(aOperation)
     { }
 
     NS_DECL_NSIRUNNABLE
 
   private:
-    nsRefPtr<GeckoMediaPluginService> mService;
+    nsRefPtr<GeckoMediaPluginServiceParent> mService;
     nsString mPath;
     EOperation mOperation;
   };
 
-  Mutex mMutex; // Protects mGMPThread and mShuttingDown and mPlugins
+  // Protected by mMutex from the base class.
   nsTArray<nsRefPtr<GMPParent>> mPlugins;
-  nsCOMPtr<nsIThread> mGMPThread;
   bool mShuttingDown;
-  bool mShuttingDownOnGMPThread;
-
-  nsTArray<nsRefPtr<PluginCrashCallback>> mPluginCrashCallbacks;
 
   // True if we've inspected MOZ_GMP_PATH on the GMP thread and loaded any
   // plugins found there into mPlugins.
   Atomic<bool> mScannedPluginOnDisk;
 
   template<typename T>
   class MainThreadOnly {
   public:
@@ -186,12 +167,45 @@ private:
   // Hashes node id to whether that node id is allowed to store data
   // persistently on disk.
   nsDataHashtable<nsCStringHashKey, bool> mPersistentStorageAllowed;
 };
 
 nsresult ReadSalt(nsIFile* aPath, nsACString& aOutData);
 bool MatchOrigin(nsIFile* aPath, const nsACString& aOrigin);
 
+class GMPServiceParent final : public PGMPServiceParent
+{
+public:
+  explicit GMPServiceParent(GeckoMediaPluginServiceParent* aService)
+    : mService(aService)
+  {
+  }
+  virtual ~GMPServiceParent();
+
+  virtual bool RecvLoadGMP(const nsCString& aNodeId,
+                           const nsCString& aApi,
+                           nsTArray<nsCString>&& aTags,
+                           nsTArray<ProcessId>&& aAlreadyBridgedTo,
+                           base::ProcessId* aID,
+                           nsCString* aDisplayName,
+                           nsCString* aPluginId) override;
+  virtual bool RecvGetGMPNodeId(const nsString& aOrigin,
+                                const nsString& aTopLevelOrigin,
+                                const bool& aInPrivateBrowsing,
+                                nsCString* aID) override;
+  static bool RecvGetGMPPluginVersionForAPI(const nsCString& aAPI,
+                                            nsTArray<nsCString>&& aTags,
+                                            bool* aHasPlugin,
+                                            nsCString* aVersion);
+
+  virtual void ActorDestroy(ActorDestroyReason aWhy);
+
+  static PGMPServiceParent* Create(Transport* aTransport, ProcessId aOtherPid);
+
+private:
+  nsRefPtr<GeckoMediaPluginServiceParent> mService;
+};
+
 } // namespace gmp
 } // namespace mozilla
 
-#endif // GMPService_h_
+#endif // GMPServiceParent_h_
--- a/dom/media/gmp/GMPStorageParent.cpp
+++ b/dom/media/gmp/GMPStorageParent.cpp
@@ -47,17 +47,17 @@ namespace gmp {
 // $profileDir/gmp/storage/$nodeId/
 static nsresult
 GetGMPStorageDir(nsIFile** aTempDir, const nsCString& aNodeId)
 {
   if (NS_WARN_IF(!aTempDir)) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  nsCOMPtr<mozIGeckoMediaPluginService> mps =
+  nsCOMPtr<mozIGeckoMediaPluginChromeService> mps =
     do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   if (NS_WARN_IF(!mps)) {
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIFile> tmpFile;
   nsresult rv = mps->GetStorageDir(getter_AddRefs(tmpFile));
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -463,17 +463,17 @@ GMPStorageParent::GMPStorageParent(const
 }
 
 nsresult
 GMPStorageParent::Init()
 {
   if (NS_WARN_IF(mNodeId.IsEmpty())) {
     return NS_ERROR_FAILURE;
   }
-  nsCOMPtr<mozIGeckoMediaPluginService> mps =
+  nsCOMPtr<mozIGeckoMediaPluginChromeService> mps =
     do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   if (NS_WARN_IF(!mps)) {
     return NS_ERROR_FAILURE;
   }
 
   bool persistent = false;
   if (NS_WARN_IF(NS_FAILED(mps->IsPersistentStorageAllowed(mNodeId, &persistent)))) {
     return NS_ERROR_FAILURE;
--- a/dom/media/gmp/GMPVideoDecoderChild.cpp
+++ b/dom/media/gmp/GMPVideoDecoderChild.cpp
@@ -1,24 +1,24 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "GMPVideoDecoderChild.h"
 #include "GMPVideoi420FrameImpl.h"
-#include "GMPChild.h"
+#include "GMPContentChild.h"
 #include <stdio.h>
 #include "mozilla/unused.h"
 #include "GMPVideoEncodedFrameImpl.h"
 
 namespace mozilla {
 namespace gmp {
 
-GMPVideoDecoderChild::GMPVideoDecoderChild(GMPChild* aPlugin)
+GMPVideoDecoderChild::GMPVideoDecoderChild(GMPContentChild* aPlugin)
 : GMPSharedMemManager(aPlugin),
   mPlugin(aPlugin),
   mVideoDecoder(nullptr),
   mVideoHost(this)
 {
   MOZ_ASSERT(mPlugin);
 }
 
--- a/dom/media/gmp/GMPVideoDecoderChild.h
+++ b/dom/media/gmp/GMPVideoDecoderChild.h
@@ -11,24 +11,24 @@
 #include "gmp-video-decode.h"
 #include "GMPSharedMemManager.h"
 #include "GMPVideoHost.h"
 #include "mozilla/gmp/GMPTypes.h"
 
 namespace mozilla {
 namespace gmp {
 
-class GMPChild;
+class GMPContentChild;
 
 class GMPVideoDecoderChild : public PGMPVideoDecoderChild,
                              public GMPVideoDecoderCallback,
                              public GMPSharedMemManager
 {
 public:
-  explicit GMPVideoDecoderChild(GMPChild* aPlugin);
+  explicit GMPVideoDecoderChild(GMPContentChild* aPlugin);
   virtual ~GMPVideoDecoderChild();
 
   void Init(GMPVideoDecoder* aDecoder);
   GMPVideoHostImpl& Host();
 
   // GMPVideoDecoderCallback
   virtual void Decoded(GMPVideoi420Frame* decodedFrame) override;
   virtual void ReceivedDecodedReferenceFrame(const uint64_t pictureId) override;
@@ -69,17 +69,17 @@ private:
                           const bool& aMissingFrames,
                           InfallibleTArray<uint8_t>&& aCodecSpecificInfo,
                           const int64_t& aRenderTimeMs) override;
   virtual bool RecvChildShmemForPool(Shmem&& aFrameBuffer) override;
   virtual bool RecvReset() override;
   virtual bool RecvDrain() override;
   virtual bool RecvDecodingComplete() override;
 
-  GMPChild* mPlugin;
+  GMPContentChild* mPlugin;
   GMPVideoDecoder* mVideoDecoder;
   GMPVideoHostImpl mVideoHost;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMPVideoDecoderChild_h_
--- a/dom/media/gmp/GMPVideoDecoderParent.cpp
+++ b/dom/media/gmp/GMPVideoDecoderParent.cpp
@@ -6,17 +6,17 @@
 #include "GMPVideoDecoderParent.h"
 #include "prlog.h"
 #include "mozilla/unused.h"
 #include "nsAutoRef.h"
 #include "nsThreadUtils.h"
 #include "GMPUtils.h"
 #include "GMPVideoEncodedFrameImpl.h"
 #include "GMPVideoi420FrameImpl.h"
-#include "GMPParent.h"
+#include "GMPContentParent.h"
 #include "GMPMessageUtils.h"
 #include "mozilla/gmp/GMPTypes.h"
 
 namespace mozilla {
 
 #ifdef LOG
 #undef LOG
 #endif
@@ -38,17 +38,17 @@ namespace gmp {
 //    on InitDecode success -> Open
 //    on Shutdown -> Dead
 // Open: mIsOpen == true
 //    on Close -> Dead
 //    on ActorDestroy -> Dead
 //    on Shutdown -> Dead
 // Dead: mIsOpen == false
 
-GMPVideoDecoderParent::GMPVideoDecoderParent(GMPParent* aPlugin)
+GMPVideoDecoderParent::GMPVideoDecoderParent(GMPContentParent* aPlugin)
   : GMPSharedMemManager(aPlugin)
   , mIsOpen(false)
   , mShuttingDown(false)
   , mPlugin(aPlugin)
   , mCallback(nullptr)
   , mVideoHost(this)
 {
   MOZ_ASSERT(mPlugin);
@@ -331,16 +331,23 @@ GMPVideoDecoderParent::RecvError(const G
 
   // Ignore any return code. It is OK for this to fail without killing the process.
   mCallback->Error(aError);
 
   return true;
 }
 
 bool
+GMPVideoDecoderParent::RecvShutdown()
+{
+  Shutdown();
+  return true;
+}
+
+bool
 GMPVideoDecoderParent::RecvParentShmemForPool(Shmem&& aEncodedBuffer)
 {
   if (aEncodedBuffer.IsWritable()) {
     mVideoHost.SharedMemMgr()->MgrDeallocShmem(GMPSharedMem::kGMPEncodedData,
                                                aEncodedBuffer);
   }
   return true;
 }
--- a/dom/media/gmp/GMPVideoDecoderParent.h
+++ b/dom/media/gmp/GMPVideoDecoderParent.h
@@ -13,26 +13,26 @@
 #include "GMPSharedMemManager.h"
 #include "GMPUtils.h"
 #include "GMPVideoHost.h"
 #include "GMPVideoDecoderProxy.h"
 
 namespace mozilla {
 namespace gmp {
 
-class GMPParent;
+class GMPContentParent;
 
 class GMPVideoDecoderParent final : public PGMPVideoDecoderParent
                                   , public GMPVideoDecoderProxy
                                   , public GMPSharedMemManager
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(GMPVideoDecoderParent)
 
-  explicit GMPVideoDecoderParent(GMPParent *aPlugin);
+  explicit GMPVideoDecoderParent(GMPContentParent *aPlugin);
 
   GMPVideoHostImpl& Host();
   nsresult Shutdown();
 
   // GMPVideoDecoder
   virtual void Close() override;
   virtual nsresult InitDecode(const GMPVideoCodec& aCodecSettings,
                               const nsTArray<uint8_t>& aCodecSpecific,
@@ -68,24 +68,25 @@ private:
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
   virtual bool RecvDecoded(const GMPVideoi420FrameData& aDecodedFrame) override;
   virtual bool RecvReceivedDecodedReferenceFrame(const uint64_t& aPictureId) override;
   virtual bool RecvReceivedDecodedFrame(const uint64_t& aPictureId) override;
   virtual bool RecvInputDataExhausted() override;
   virtual bool RecvDrainComplete() override;
   virtual bool RecvResetComplete() override;
   virtual bool RecvError(const GMPErr& aError) override;
+  virtual bool RecvShutdown() override;
   virtual bool RecvParentShmemForPool(Shmem&& aEncodedBuffer) override;
   virtual bool AnswerNeedShmem(const uint32_t& aFrameBufferSize,
                                Shmem* aMem) override;
   virtual bool Recv__delete__() override;
 
   bool mIsOpen;
   bool mShuttingDown;
-  nsRefPtr<GMPParent> mPlugin;
+  nsRefPtr<GMPContentParent> mPlugin;
   GMPVideoDecoderCallbackProxy* mCallback;
   GMPVideoHostImpl mVideoHost;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMPVideoDecoderParent_h_
--- a/dom/media/gmp/GMPVideoEncoderChild.cpp
+++ b/dom/media/gmp/GMPVideoEncoderChild.cpp
@@ -1,24 +1,24 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "GMPVideoEncoderChild.h"
-#include "GMPChild.h"
+#include "GMPContentChild.h"
 #include <stdio.h>
 #include "mozilla/unused.h"
 #include "GMPVideoEncodedFrameImpl.h"
 #include "GMPVideoi420FrameImpl.h"
 
 namespace mozilla {
 namespace gmp {
 
-GMPVideoEncoderChild::GMPVideoEncoderChild(GMPChild* aPlugin)
+GMPVideoEncoderChild::GMPVideoEncoderChild(GMPContentChild* aPlugin)
 : GMPSharedMemManager(aPlugin),
   mPlugin(aPlugin),
   mVideoEncoder(nullptr),
   mVideoHost(this)
 {
   MOZ_ASSERT(mPlugin);
 }
 
--- a/dom/media/gmp/GMPVideoEncoderChild.h
+++ b/dom/media/gmp/GMPVideoEncoderChild.h
@@ -10,24 +10,24 @@
 #include "mozilla/gmp/PGMPVideoEncoderChild.h"
 #include "gmp-video-encode.h"
 #include "GMPSharedMemManager.h"
 #include "GMPVideoHost.h"
 
 namespace mozilla {
 namespace gmp {
 
-class GMPChild;
+class GMPContentChild;
 
 class GMPVideoEncoderChild : public PGMPVideoEncoderChild,
                              public GMPVideoEncoderCallback,
                              public GMPSharedMemManager
 {
 public:
-  explicit GMPVideoEncoderChild(GMPChild* aPlugin);
+  explicit GMPVideoEncoderChild(GMPContentChild* aPlugin);
   virtual ~GMPVideoEncoderChild();
 
   void Init(GMPVideoEncoder* aEncoder);
   GMPVideoHostImpl& Host();
 
   // GMPVideoEncoderCallback
   virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
                        const uint8_t* aCodecSpecificInfo,
@@ -68,17 +68,17 @@ private:
   virtual bool RecvChildShmemForPool(Shmem&& aEncodedBuffer) override;
   virtual bool RecvSetChannelParameters(const uint32_t& aPacketLoss,
                                         const uint32_t& aRTT) override;
   virtual bool RecvSetRates(const uint32_t& aNewBitRate,
                             const uint32_t& aFrameRate) override;
   virtual bool RecvSetPeriodicKeyFrames(const bool& aEnable) override;
   virtual bool RecvEncodingComplete() override;
 
-  GMPChild* mPlugin;
+  GMPContentChild* mPlugin;
   GMPVideoEncoder* mVideoEncoder;
   GMPVideoHostImpl mVideoHost;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMPVideoEncoderChild_h_
--- a/dom/media/gmp/GMPVideoEncoderParent.cpp
+++ b/dom/media/gmp/GMPVideoEncoderParent.cpp
@@ -5,17 +5,17 @@
 
 #include "GMPVideoEncoderParent.h"
 #include "prlog.h"
 #include "GMPVideoi420FrameImpl.h"
 #include "GMPVideoEncodedFrameImpl.h"
 #include "mozilla/unused.h"
 #include "GMPMessageUtils.h"
 #include "nsAutoRef.h"
-#include "GMPParent.h"
+#include "GMPContentParent.h"
 #include "mozilla/gmp/GMPTypes.h"
 #include "nsThread.h"
 #include "nsThreadUtils.h"
 #include "runnable_utils.h"
 #include "GMPUtils.h"
 
 namespace mozilla {
 
@@ -45,17 +45,17 @@ namespace gmp {
 //    on InitDecode success -> Open
 //    on Shutdown -> Dead
 // Open: mIsOpen == true
 //    on Close -> Dead
 //    on ActorDestroy -> Dead
 //    on Shutdown -> Dead
 // Dead: mIsOpen == false
 
-GMPVideoEncoderParent::GMPVideoEncoderParent(GMPParent *aPlugin)
+GMPVideoEncoderParent::GMPVideoEncoderParent(GMPContentParent *aPlugin)
 : GMPSharedMemManager(aPlugin),
   mIsOpen(false),
   mShuttingDown(false),
   mPlugin(aPlugin),
   mCallback(nullptr),
   mVideoHost(this)
 {
   MOZ_ASSERT(mPlugin);
@@ -316,16 +316,23 @@ GMPVideoEncoderParent::RecvError(const G
 
   // Ignore any return code. It is OK for this to fail without killing the process.
   mCallback->Error(aError);
 
   return true;
 }
 
 bool
+GMPVideoEncoderParent::RecvShutdown()
+{
+  Shutdown();
+  return true;
+}
+
+bool
 GMPVideoEncoderParent::RecvParentShmemForPool(Shmem&& aFrameBuffer)
 {
   if (aFrameBuffer.IsWritable()) {
     mVideoHost.SharedMemMgr()->MgrDeallocShmem(GMPSharedMem::kGMPFrameData,
                                                aFrameBuffer);
   }
   return true;
 }
--- a/dom/media/gmp/GMPVideoEncoderParent.h
+++ b/dom/media/gmp/GMPVideoEncoderParent.h
@@ -13,26 +13,26 @@
 #include "GMPSharedMemManager.h"
 #include "GMPUtils.h"
 #include "GMPVideoHost.h"
 #include "GMPVideoEncoderProxy.h"
 
 namespace mozilla {
 namespace gmp {
 
-class GMPParent;
+class GMPContentParent;
 
 class GMPVideoEncoderParent : public GMPVideoEncoderProxy,
                               public PGMPVideoEncoderParent,
                               public GMPSharedMemManager
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(GMPVideoEncoderParent)
 
-  explicit GMPVideoEncoderParent(GMPParent *aPlugin);
+  explicit GMPVideoEncoderParent(GMPContentParent *aPlugin);
 
   GMPVideoHostImpl& Host();
   void Shutdown();
 
   // GMPVideoEncoderProxy
   virtual void Close() override;
   virtual GMPErr InitEncode(const GMPVideoCodec& aCodecSettings,
                             const nsTArray<uint8_t>& aCodecSpecific,
@@ -64,24 +64,25 @@ public:
 private:
   virtual ~GMPVideoEncoderParent();
 
   // PGMPVideoEncoderParent
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
   virtual bool RecvEncoded(const GMPVideoEncodedFrameData& aEncodedFrame,
                            InfallibleTArray<uint8_t>&& aCodecSpecificInfo) override;
   virtual bool RecvError(const GMPErr& aError) override;
+  virtual bool RecvShutdown() override;
   virtual bool RecvParentShmemForPool(Shmem&& aFrameBuffer) override;
   virtual bool AnswerNeedShmem(const uint32_t& aEncodedBufferSize,
                                Shmem* aMem) override;
   virtual bool Recv__delete__() override;
 
   bool mIsOpen;
   bool mShuttingDown;
-  nsRefPtr<GMPParent> mPlugin;
+  nsRefPtr<GMPContentParent> mPlugin;
   GMPVideoEncoderCallbackProxy* mCallback;
   GMPVideoHostImpl mVideoHost;
   nsCOMPtr<nsIThread> mEncodedThread;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
--- a/dom/media/gmp/PGMP.ipdl
+++ b/dom/media/gmp/PGMP.ipdl
@@ -1,50 +1,43 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 PGMPVideoDecoder;
-include protocol PGMPVideoEncoder;
 include protocol PCrashReporter;
-include protocol PGMPDecryptor;
-include protocol PGMPAudioDecoder;
+include protocol PGMPContent;
 include protocol PGMPTimer;
 include protocol PGMPStorage;
 
 using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
 
 namespace mozilla {
 namespace gmp {
 
 intr protocol PGMP
 {
-  manages PGMPAudioDecoder;
-  manages PGMPDecryptor;
-  manages PGMPVideoDecoder;
-  manages PGMPVideoEncoder;
+  parent opens PGMPContent;
+
   manages PCrashReporter;
   manages PGMPTimer;
   manages PGMPStorage;
 
 parent:
   async PCrashReporter(NativeThreadId tid);
   async PGMPTimer();
   async PGMPStorage();
 
+  async PGMPContentChildDestroyed();
+
   async AsyncShutdownComplete();
   async AsyncShutdownRequired();
 
 child:
-  async PGMPAudioDecoder();
-  async PGMPDecryptor();
-  async PGMPVideoDecoder();
-  async PGMPVideoEncoder();
-
-  async SetNodeId(nsCString nodeId);
-  async StartPlugin();
   async BeginAsyncShutdown();
   async CrashPluginNow();
+  async StartPlugin();
+  async SetNodeId(nsCString nodeId);
+  async CloseActive();
 };
 
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/gmp/PGMPAudioDecoder.ipdl
+++ b/dom/media/gmp/PGMPAudioDecoder.ipdl
@@ -1,36 +1,37 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 PGMP;
+include protocol PGMPContent;
 include GMPTypes;
 
 using GMPCodecSpecificInfo from "gmp-audio-codec.h";
 using GMPErr from "gmp-errors.h";
 
 include "GMPMessageUtils.h";
 
 namespace mozilla {
 namespace gmp {
 
 async protocol PGMPAudioDecoder
 {
-  manager PGMP;
+  manager PGMPContent;
 child:
   InitDecode(GMPAudioCodecData aCodecSettings);
   Decode(GMPAudioEncodedSampleData aInput);
   Reset();
   Drain();
   DecodingComplete();
 parent:
   __delete__();
   Decoded(GMPAudioDecodedSampleData aDecoded);
   InputDataExhausted();
   DrainComplete();
   ResetComplete();
   Error(GMPErr aErr);
+  async Shutdown();
 };
 
 } // namespace gmp
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/PGMPContent.ipdl
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 PGMP;
+include protocol PGMPService;
+include protocol PGMPVideoDecoder;
+include protocol PGMPVideoEncoder;
+include protocol PGMPDecryptor;
+include protocol PGMPAudioDecoder;
+
+namespace mozilla {
+namespace gmp {
+
+intr protocol PGMPContent
+{
+  bridges PGMPService, PGMP;
+
+  manages PGMPAudioDecoder;
+  manages PGMPDecryptor;
+  manages PGMPVideoDecoder;
+  manages PGMPVideoEncoder;
+
+child:
+  async PGMPAudioDecoder();
+  async PGMPDecryptor();
+  async PGMPVideoDecoder();
+  async PGMPVideoEncoder();
+};
+
+} // namespace gmp
+} // namespace mozilla
--- a/dom/media/gmp/PGMPDecryptor.ipdl
+++ b/dom/media/gmp/PGMPDecryptor.ipdl
@@ -1,28 +1,28 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 PGMP;
+include protocol PGMPContent;
 include GMPTypes;
 
 using GMPSessionMessageType from  "gmp-decryption.h";
 using GMPMediaKeyStatus from  "gmp-decryption.h";
 using GMPSessionType from  "gmp-decryption.h";
 using GMPDOMException from "gmp-decryption.h";
 using GMPErr from "gmp-errors.h";
 
 namespace mozilla {
 namespace gmp {
 
 async protocol PGMPDecryptor
 {
-  manager PGMP;
+  manager PGMPContent;
 child:
 
   Init();
 
   CreateSession(uint32_t aCreateSessionToken,
                 uint32_t aPromiseId,
                 nsCString aInitDataType,
                 uint8_t[] aInitData,
@@ -79,12 +79,14 @@ parent:
                nsCString aMessage);
 
   KeyStatusChanged(nsCString aSessionId, uint8_t[] aKey,
                    GMPMediaKeyStatus aStatus);
 
   SetCaps(uint64_t aCaps);
 
   Decrypted(uint32_t aId, GMPErr aResult, uint8_t[] aBuffer);
+
+  async Shutdown();
 };
 
 } // namespace gmp
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/PGMPService.ipdl
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 PGMP;
+
+using base::ProcessId from "base/process.h";
+
+namespace mozilla {
+namespace gmp {
+
+sync protocol PGMPService
+{
+  parent spawns PGMP as child;
+
+parent:
+  sync LoadGMP(nsCString nodeId, nsCString api, nsCString[] tags,
+               ProcessId[] alreadyBridgedTo)
+    returns (ProcessId id, nsCString displayName, nsCString pluginId);
+  sync GetGMPNodeId(nsString origin, nsString topLevelOrigin,
+                    bool inPrivateBrowsing)
+    returns (nsCString id);
+};
+
+} // namespace gmp
+} // namespace mozilla
--- a/dom/media/gmp/PGMPVideoDecoder.ipdl
+++ b/dom/media/gmp/PGMPVideoDecoder.ipdl
@@ -1,27 +1,27 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 PGMP;
+include protocol PGMPContent;
 include GMPTypes;
 
 using GMPVideoCodec from "gmp-video-codec.h";
 using GMPErr from "gmp-errors.h";
 
 include "GMPMessageUtils.h";
 
 namespace mozilla {
 namespace gmp {
 
 intr protocol PGMPVideoDecoder
 {
-  manager PGMP;
+  manager PGMPContent;
 child:
   async InitDecode(GMPVideoCodec aCodecSettings,
                    uint8_t[] aCodecSpecific,
                    int32_t aCoreCount);
   async Decode(GMPVideoEncodedFrameData aInputFrame,
                bool aMissingFrames,
                uint8_t[] aCodecSpecificInfo,
                int64_t aRenderTimeMs);
@@ -34,16 +34,17 @@ parent:
   async __delete__();
   async Decoded(GMPVideoi420FrameData aDecodedFrame);
   async ReceivedDecodedReferenceFrame(uint64_t aPictureId);
   async ReceivedDecodedFrame(uint64_t aPictureId);
   async InputDataExhausted();
   async DrainComplete();
   async ResetComplete();
   async Error(GMPErr aErr);
+  async Shutdown();
   async ParentShmemForPool(Shmem aEncodedBuffer);
   // MUST be intr - if sync and we create a new Shmem, when the returned
   // Shmem is received in the Child it will fail to Deserialize
   intr NeedShmem(uint32_t aFrameBufferSize) returns (Shmem aMem);
 };
 
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/gmp/PGMPVideoEncoder.ipdl
+++ b/dom/media/gmp/PGMPVideoEncoder.ipdl
@@ -1,28 +1,28 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 PGMP;
+include protocol PGMPContent;
 include GMPTypes;
 
 using GMPVideoCodec from "gmp-video-codec.h";
 using GMPVideoFrameType from "gmp-video-frame-encoded.h";
 using GMPErr from "gmp-errors.h";
 
 include "GMPMessageUtils.h";
 
 namespace mozilla {
 namespace gmp {
 
 intr protocol PGMPVideoEncoder
 {
-  manager PGMP;
+  manager PGMPContent;
 child:
   async InitEncode(GMPVideoCodec aCodecSettings,
                    uint8_t[] aCodecSpecific,
                    int32_t aNumberOfCores,
                    uint32_t aMaxPayloadSize);
   async Encode(GMPVideoi420FrameData aInputFrame,
                uint8_t[] aCodecSpecificInfo,
                GMPVideoFrameType[] aFrameTypes);
@@ -32,16 +32,17 @@ child:
   async EncodingComplete();
   async ChildShmemForPool(Shmem aEncodedBuffer);
 
 parent:
   async __delete__();
   async Encoded(GMPVideoEncodedFrameData aEncodedFrame,
                 uint8_t[] aCodecSpecificInfo);
   async Error(GMPErr aErr);
+  async Shutdown();
   async ParentShmemForPool(Shmem aFrameBuffer);
   // MUST be intr - if sync and we create a new Shmem, when the returned
   // Shmem is received in the Child it will fail to Deserialize
   intr NeedShmem(uint32_t aEncodedBufferSize) returns (Shmem aMem);
 };
 
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/gmp/moz.build
+++ b/dom/media/gmp/moz.build
@@ -2,16 +2,17 @@
 # vim: set filetype=python:
 # 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/.
 
 XPIDL_MODULE = 'content_geckomediaplugins'
 
 XPIDL_SOURCES += [
+    'mozIGeckoMediaPluginChromeService.idl',
     'mozIGeckoMediaPluginService.idl',
 ]
 
 EXPORTS += [
     'gmp-api/gmp-async-shutdown.h',
     'gmp-api/gmp-audio-codec.h',
     'gmp-api/gmp-audio-decode.h',
     'gmp-api/gmp-audio-host.h',
@@ -30,27 +31,31 @@ EXPORTS += [
     'gmp-api/gmp-video-host.h',
     'gmp-api/gmp-video-plane.h',
     'GMPAudioDecoderChild.h',
     'GMPAudioDecoderParent.h',
     'GMPAudioDecoderProxy.h',
     'GMPAudioHost.h',
     'GMPCallbackBase.h',
     'GMPChild.h',
+    'GMPContentChild.h',
+    'GMPContentParent.h',
     'GMPDecryptorChild.h',
     'GMPDecryptorParent.h',
     'GMPDecryptorProxy.h',
     'GMPEncryptedBufferDataImpl.h',
     'GMPLoader.h',
     'GMPMessageUtils.h',
     'GMPParent.h',
     'GMPPlatform.h',
     'GMPProcessChild.h',
     'GMPProcessParent.h',
     'GMPService.h',
+    'GMPServiceChild.h',
+    'GMPServiceParent.h',
     'GMPSharedMemManager.h',
     'GMPStorageChild.h',
     'GMPStorageParent.h',
     'GMPTimerChild.h',
     'GMPTimerParent.h',
     'GMPUtils.h',
     'GMPVideoDecoderChild.h',
     'GMPVideoDecoderParent.h',
@@ -71,24 +76,28 @@ if CONFIG['OS_TARGET'] == 'Android':
       'GMPLoader.cpp',
     ]
 
 UNIFIED_SOURCES += [
     'GMPAudioDecoderChild.cpp',
     'GMPAudioDecoderParent.cpp',
     'GMPAudioHost.cpp',
     'GMPChild.cpp',
+    'GMPContentChild.cpp',
+    'GMPContentParent.cpp',
     'GMPDecryptorChild.cpp',
     'GMPDecryptorParent.cpp',
     'GMPEncryptedBufferDataImpl.cpp',
     'GMPParent.cpp',
     'GMPPlatform.cpp',
     'GMPProcessChild.cpp',
     'GMPProcessParent.cpp',
     'GMPService.cpp',
+    'GMPServiceChild.cpp',
+    'GMPServiceParent.cpp',
     'GMPSharedMemManager.cpp',
     'GMPStorageChild.cpp',
     'GMPStorageParent.cpp',
     'GMPTimerChild.cpp',
     'GMPTimerParent.cpp',
     'GMPVideoDecoderChild.cpp',
     'GMPVideoDecoderParent.cpp',
     'GMPVideoEncodedFrameImpl.cpp',
@@ -103,17 +112,19 @@ if CONFIG['OS_TARGET'] == 'WINNT':
   DIRS += [
       'rlz',
   ]
 
 IPDL_SOURCES += [
   'GMPTypes.ipdlh',
   'PGMP.ipdl',
   'PGMPAudioDecoder.ipdl',
+  'PGMPContent.ipdl',
   'PGMPDecryptor.ipdl',
+  'PGMPService.ipdl',
   'PGMPStorage.ipdl',
   'PGMPTimer.ipdl',
   'PGMPVideoDecoder.ipdl',
   'PGMPVideoEncoder.ipdl',
 ]
 
 if CONFIG['GKMEDIAS_SHARED_LIBRARY']:
     NO_VISIBILITY_FLAGS = True
copy from dom/media/gmp/mozIGeckoMediaPluginService.idl
copy to dom/media/gmp/mozIGeckoMediaPluginChromeService.idl
--- a/dom/media/gmp/mozIGeckoMediaPluginService.idl
+++ b/dom/media/gmp/mozIGeckoMediaPluginChromeService.idl
@@ -1,95 +1,19 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "nsISupports.idl"
-#include "nsIThread.idl"
-#include "nsIPrincipal.idl"
 #include "nsIFile.idl"
 
-%{C++
-#include "nsTArray.h"
-#include "nsStringGlue.h"
-class GMPAudioDecoderProxy;
-class GMPDecryptorProxy;
-class GMPVideoDecoderProxy;
-class GMPVideoEncoderProxy;
-class GMPVideoHost;
-%}
-
-[ptr] native GMPVideoDecoderProxy(GMPVideoDecoderProxy);
-[ptr] native GMPVideoEncoderProxy(GMPVideoEncoderProxy);
-[ptr] native GMPVideoHost(GMPVideoHost);
-[ptr] native MessageLoop(MessageLoop);
-[ptr] native TagArray(nsTArray<nsCString>);
-[ptr] native GMPDecryptorProxy(GMPDecryptorProxy);
-[ptr] native GMPAudioDecoderProxy(GMPAudioDecoderProxy);
-
-[scriptable, uuid(f71e6e57-5175-4cf3-8cc2-629273a75b67)]
-interface mozIGeckoMediaPluginService : nsISupports
+[scriptable, uuid(5878366c-73f6-486e-ad2f-9aca602864e4)]
+interface mozIGeckoMediaPluginChromeService : nsISupports
 {
-
-  /**
-   * The GMP thread. Callable from any thread.
-   */
-  readonly attribute nsIThread thread;
-
-  /**
-   * Get a plugin that supports the specified tags.
-   * Callable on any thread
-   */
-  [noscript]
-  boolean hasPluginForAPI(in ACString api, in TagArray tags);
-
-  /**
-   * Get the version of the plugin that supports the specified tags.
-   * Callable on any thread
-   */
-  [noscript]
-  ACString getPluginVersionForAPI(in ACString api,
-                                  in TagArray tags);
-
-  /**
-   * Get a video decoder that supports the specified tags.
-   * The array of tags should at least contain a codec tag, and optionally
-   * other tags such as for EME keysystem.
-   * Callable only on GMP thread.
-   */
-  [noscript]
-  GMPVideoDecoderProxy getGMPVideoDecoder(in TagArray tags,
-                                          [optional] in ACString nodeId,
-                                          out GMPVideoHost outVideoHost);
-
-  /**
-   * Get a video encoder that supports the specified tags.
-   * The array of tags should at least contain a codec tag, and optionally
-   * other tags.
-   * Callable only on GMP thread.
-   */
-  [noscript]
-  GMPVideoEncoderProxy getGMPVideoEncoder(in TagArray tags,
-		                                      [optional] in ACString nodeId,
-		                                      out GMPVideoHost outVideoHost);
-
-  // Returns an audio decoder that supports the specified tags.
-  // The array of tags should at least contain a codec tag, and optionally
-  // other tags such as for EME keysystem.
-  // Callable only on GMP thread.
-  GMPAudioDecoderProxy getGMPAudioDecoder(in TagArray tags,
-                                          [optional] in ACString nodeId);
-
-  // Returns a decryption session manager that supports the specified tags.
-  // The array of tags should at least contain a key system tag, and optionally
-  // other tags.
-  // Callable only on GMP thread.
-  GMPDecryptorProxy getGMPDecryptor(in TagArray tags, in ACString nodeId);
-
   /**
    * Add a directory to scan for gecko media plugins.
    * @note Main-thread API.
    */
   void addPluginDirectory(in AString directory);
 
   /**
    * Remove a directory for gecko media plugins.
@@ -99,23 +23,16 @@ interface mozIGeckoMediaPluginService : 
 
   /**
    * Remove a directory for gecko media plugins and delete it from disk.
    * @note Main-thread API.
    */
   void removeAndDeletePluginDirectory(in AString directory);
 
   /**
-   * Gets the NodeId for a (origin, urlbarOrigin, isInprivateBrowsing) tuple.
-   */
-  ACString getNodeId(in AString origin,
-                     in AString topLevelOrigin,
-                     in bool inPrivateBrowsingMode);
-
-  /**
    * Clears storage data associated with the origin.
    */
   void forgetThisSite(in AString origin);
 
   /**
    * Returns true if the given node id is allowed to store things
    * persistently on disk. Private Browsing and local content are not
    * allowed to store persistent data.
--- a/dom/media/gmp/mozIGeckoMediaPluginService.idl
+++ b/dom/media/gmp/mozIGeckoMediaPluginService.idl
@@ -1,37 +1,63 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "nsISupports.idl"
 #include "nsIThread.idl"
-#include "nsIPrincipal.idl"
-#include "nsIFile.idl"
 
 %{C++
+#include "mozilla/UniquePtr.h"
 #include "nsTArray.h"
 #include "nsStringGlue.h"
 class GMPAudioDecoderProxy;
 class GMPDecryptorProxy;
 class GMPVideoDecoderProxy;
 class GMPVideoEncoderProxy;
 class GMPVideoHost;
+
+template<class T>
+class GMPGetterCallback
+{
+public:
+  GMPGetterCallback() { MOZ_COUNT_CTOR(GMPGetterCallback<T>); }
+  virtual ~GMPGetterCallback() { MOZ_COUNT_DTOR(GMPGetterCallback<T>); }
+  virtual void Done(T*) = 0;
+};
+template<class T>
+class GMPVideoGetterCallback
+{
+public:
+  GMPVideoGetterCallback() { MOZ_COUNT_CTOR(GMPVideoGetterCallback<T>); }
+  virtual ~GMPVideoGetterCallback() { MOZ_COUNT_DTOR(GMPVideoGetterCallback<T>); }
+  virtual void Done(T*, GMPVideoHost*) = 0;
+};
+typedef GMPGetterCallback<GMPDecryptorProxy> GetGMPDecryptorCallback;
+typedef GMPGetterCallback<GMPAudioDecoderProxy> GetGMPAudioDecoderCallback;
+typedef GMPVideoGetterCallback<GMPVideoDecoderProxy> GetGMPVideoDecoderCallback;
+typedef GMPVideoGetterCallback<GMPVideoEncoderProxy> GetGMPVideoEncoderCallback;
+class GetNodeIdCallback
+{
+public:
+  GetNodeIdCallback() { MOZ_COUNT_CTOR(GetNodeIdCallback); }
+  virtual ~GetNodeIdCallback() { MOZ_COUNT_DTOR(GetNodeIdCallback); }
+  virtual void Done(nsresult aResult, const nsACString& aNodeId) = 0;
+};
 %}
 
-[ptr] native GMPVideoDecoderProxy(GMPVideoDecoderProxy);
-[ptr] native GMPVideoEncoderProxy(GMPVideoEncoderProxy);
-[ptr] native GMPVideoHost(GMPVideoHost);
-[ptr] native MessageLoop(MessageLoop);
 [ptr] native TagArray(nsTArray<nsCString>);
-[ptr] native GMPDecryptorProxy(GMPDecryptorProxy);
-[ptr] native GMPAudioDecoderProxy(GMPAudioDecoderProxy);
+native GetGMPDecryptorCallback(mozilla::UniquePtr<GetGMPDecryptorCallback>&&);
+native GetGMPAudioDecoderCallback(mozilla::UniquePtr<GetGMPAudioDecoderCallback>&&);
+native GetGMPVideoDecoderCallback(mozilla::UniquePtr<GetGMPVideoDecoderCallback>&&);
+native GetGMPVideoEncoderCallback(mozilla::UniquePtr<GetGMPVideoEncoderCallback>&&);
+native GetNodeIdCallback(mozilla::UniquePtr<GetNodeIdCallback>&&);
 
-[scriptable, uuid(f71e6e57-5175-4cf3-8cc2-629273a75b67)]
+[scriptable, uuid(2b0c90e1-df89-4583-a123-8bdb2f5f7e39)]
 interface mozIGeckoMediaPluginService : nsISupports
 {
 
   /**
    * The GMP thread. Callable from any thread.
    */
   readonly attribute nsIThread thread;
 
@@ -42,89 +68,83 @@ interface mozIGeckoMediaPluginService : 
   [noscript]
   boolean hasPluginForAPI(in ACString api, in TagArray tags);
 
   /**
    * Get the version of the plugin that supports the specified tags.
    * Callable on any thread
    */
   [noscript]
-  ACString getPluginVersionForAPI(in ACString api,
-                                  in TagArray tags);
+  void getPluginVersionForAPI(in ACString api, in TagArray tags,
+                              out boolean hasPlugin, out ACString version);
 
   /**
    * Get a video decoder that supports the specified tags.
    * The array of tags should at least contain a codec tag, and optionally
    * other tags such as for EME keysystem.
    * Callable only on GMP thread.
+   * This is an asynchronous operation, the Done method of the callback object
+   * will be called on the GMP thread with the result (which might be null in
+   * the case of failure). This method always takes ownership of the callback
+   * object, but if this method returns an error then the Done method of the
+   * callback object will not be called at all.
    */
   [noscript]
-  GMPVideoDecoderProxy getGMPVideoDecoder(in TagArray tags,
-                                          [optional] in ACString nodeId,
-                                          out GMPVideoHost outVideoHost);
+  void getGMPVideoDecoder(in TagArray tags,
+                          [optional] in ACString nodeId,
+                          in GetGMPVideoDecoderCallback callback);
 
   /**
    * Get a video encoder that supports the specified tags.
    * The array of tags should at least contain a codec tag, and optionally
    * other tags.
    * Callable only on GMP thread.
+   * This is an asynchronous operation, the Done method of the callback object
+   * will be called on the GMP thread with the result (which might be null in
+   * the case of failure). This method always takes ownership of the callback
+   * object, but if this method returns an error then the Done method of the
+   * callback object will not be called at all.
    */
   [noscript]
-  GMPVideoEncoderProxy getGMPVideoEncoder(in TagArray tags,
-		                                      [optional] in ACString nodeId,
-		                                      out GMPVideoHost outVideoHost);
-
-  // Returns an audio decoder that supports the specified tags.
-  // The array of tags should at least contain a codec tag, and optionally
-  // other tags such as for EME keysystem.
-  // Callable only on GMP thread.
-  GMPAudioDecoderProxy getGMPAudioDecoder(in TagArray tags,
-                                          [optional] in ACString nodeId);
-
-  // Returns a decryption session manager that supports the specified tags.
-  // The array of tags should at least contain a key system tag, and optionally
-  // other tags.
-  // Callable only on GMP thread.
-  GMPDecryptorProxy getGMPDecryptor(in TagArray tags, in ACString nodeId);
+  void getGMPVideoEncoder(in TagArray tags,
+		                      [optional] in ACString nodeId,
+		                      in GetGMPVideoEncoderCallback callback);
 
   /**
-   * Add a directory to scan for gecko media plugins.
-   * @note Main-thread API.
+   * Returns an audio decoder that supports the specified tags.
+   * The array of tags should at least contain a codec tag, and optionally
+   * other tags such as for EME keysystem.
+   * Callable only on GMP thread.
+   * This is an asynchronous operation, the Done method of the callback object
+   * will be called on the GMP thread with the result (which might be null in
+   * the case of failure). This method always takes ownership of the callback
+   * object, but if this method returns an error then the Done method of the
+   * callback object will not be called at all.
    */
-  void addPluginDirectory(in AString directory);
+  [noscript]
+  void getGMPAudioDecoder(in TagArray tags,
+                          [optional] in ACString nodeId,
+                          in GetGMPAudioDecoderCallback callback);
 
   /**
-   * Remove a directory for gecko media plugins.
-   * @note Main-thread API.
+   * Returns a decryption session manager that supports the specified tags.
+   * The array of tags should at least contain a key system tag, and optionally
+   * other tags.
+   * Callable only on GMP thread.
+   * This is an asynchronous operation, the Done method of the callback object
+   * will be called on the GMP thread with the result (which might be null in
+   * the case of failure). This method always takes ownership of the callback
+   * object, but if this method returns an error then the Done method of the
+   * callback object will not be called at all.
    */
-  void removePluginDirectory(in AString directory);
-
-  /**
-   * Remove a directory for gecko media plugins and delete it from disk.
-   * @note Main-thread API.
-   */
-  void removeAndDeletePluginDirectory(in AString directory);
+  [noscript]
+  void getGMPDecryptor(in TagArray tags, in ACString nodeId,
+                       in GetGMPDecryptorCallback callback);
 
   /**
    * Gets the NodeId for a (origin, urlbarOrigin, isInprivateBrowsing) tuple.
    */
-  ACString getNodeId(in AString origin,
-                     in AString topLevelOrigin,
-                     in bool inPrivateBrowsingMode);
-
-  /**
-   * Clears storage data associated with the origin.
-   */
-  void forgetThisSite(in AString origin);
-
-  /**
-   * Returns true if the given node id is allowed to store things
-   * persistently on disk. Private Browsing and local content are not
-   * allowed to store persistent data.
-   */
-  bool isPersistentStorageAllowed(in ACString nodeId);
-
-  /**
-   * Returns the directory to use as the base for storing data about GMPs.
-   */
-  nsIFile getStorageDir();
-
+  [noscript]
+  void getNodeId(in AString origin,
+                 in AString topLevelOrigin,
+                 in bool inPrivateBrowsingMode,
+                 in GetNodeIdCallback callback);
 };
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp
+++ b/dom/media/gtest/TestGMPCrossOrigin.cpp
@@ -6,113 +6,261 @@
 
 #include "gtest/gtest.h"
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "GMPVideoDecoderProxy.h"
 #include "GMPVideoEncoderProxy.h"
 #include "GMPDecryptorProxy.h"
-#include "GMPService.h"
+#include "GMPServiceParent.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsIFile.h"
 #include "nsISimpleEnumerator.h"
 #include "mozilla/Atomics.h"
 #include "nsNSSComponent.h"
+#include "mozilla/DebugOnly.h"
 
 #if defined(XP_WIN)
 #include "mozilla/WindowsVersion.h"
 #endif
 
 using namespace std;
 
 using namespace mozilla;
 using namespace mozilla::gmp;
 
+class GMPTestMonitor
+{
+public:
+  GMPTestMonitor()
+    : mFinished(false)
+  {
+  }
+
+  void AwaitFinished()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    while (!mFinished) {
+      NS_ProcessNextEvent(nullptr, true);
+    }
+    mFinished = false;
+  }
+
+private:
+  void MarkFinished()
+  {
+    mFinished = true;
+  }
+
+public:
+  void SetFinished()
+  {
+    NS_DispatchToMainThread(NS_NewNonOwningRunnableMethod(this,
+                                                          &GMPTestMonitor::MarkFinished));
+  }
+
+private:
+  bool mFinished;
+};
+
 struct GMPTestRunner
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPTestRunner)
 
-  void DoTest(void (GMPTestRunner::*aTestMethod)());
-  void RunTestGMPTestCodec();
-  void RunTestGMPCrossOrigin();
+  void DoTest(void (GMPTestRunner::*aTestMethod)(GMPTestMonitor&));
+  void RunTestGMPTestCodec1(GMPTestMonitor& aMonitor);
+  void RunTestGMPTestCodec2(GMPTestMonitor& aMonitor);
+  void RunTestGMPTestCodec3(GMPTestMonitor& aMonitor);
+  void RunTestGMPCrossOrigin1(GMPTestMonitor& aMonitor);
+  void RunTestGMPCrossOrigin2(GMPTestMonitor& aMonitor);
+  void RunTestGMPCrossOrigin3(GMPTestMonitor& aMonitor);
+  void RunTestGMPCrossOrigin4(GMPTestMonitor& aMonitor);
 
 private:
   ~GMPTestRunner() { }
 };
 
-void
-GMPTestRunner::RunTestGMPTestCodec()
+template<class T, class Base,
+         nsresult (NS_STDCALL GeckoMediaPluginService::*Getter)(nsTArray<nsCString>*,
+                                                                const nsACString&,
+                                                                UniquePtr<Base>&&)>
+class RunTestGMPVideoCodec : public Base
 {
-  nsRefPtr<GeckoMediaPluginService> service =
-    GeckoMediaPluginService::GetGeckoMediaPluginService();
+public:
+  virtual void Done(T* aGMP, GMPVideoHost* aHost)
+  {
+    EXPECT_TRUE(aGMP);
+    EXPECT_TRUE(aHost);
+    if (aGMP) {
+      aGMP->Close();
+    }
+    mMonitor.SetFinished();
+  }
 
-  GMPVideoHost* host = nullptr;
-  GMPVideoDecoderProxy* decoder = nullptr;
-  GMPVideoDecoderProxy* decoder2 = nullptr;
-  GMPVideoEncoderProxy* encoder = nullptr;
+  static void Run(GMPTestMonitor& aMonitor, const nsCString& aOrigin)
+  {
+    UniquePtr<GMPCallbackType> callback(new RunTestGMPVideoCodec(aMonitor));
+    Get(aOrigin, Move(callback));
+  }
 
-  nsTArray<nsCString> tags;
-  tags.AppendElement(NS_LITERAL_CSTRING("h264"));
+protected:
+  typedef T GMPCodecType;
+  typedef Base GMPCallbackType;
+
+  explicit RunTestGMPVideoCodec(GMPTestMonitor& aMonitor)
+    : mMonitor(aMonitor)
+  {
+  }
 
-  service->GetGMPVideoDecoder(&tags, NS_LITERAL_CSTRING("o"), &host, &decoder2);
-  service->GetGMPVideoDecoder(&tags, NS_LITERAL_CSTRING(""), &host, &decoder);
+  static nsresult Get(const nsACString& aNodeId, UniquePtr<Base>&& aCallback)
+  {
+    nsTArray<nsCString> tags;
+    tags.AppendElement(NS_LITERAL_CSTRING("h264"));
 
-  service->GetGMPVideoEncoder(&tags, NS_LITERAL_CSTRING(""), &host, &encoder);
+    nsRefPtr<GeckoMediaPluginService> service =
+      GeckoMediaPluginService::GetGeckoMediaPluginService();
+    return ((*service).*Getter)(&tags, aNodeId, Move(aCallback));
+  }
+
+protected:
+  GMPTestMonitor& mMonitor;
+};
 
-  EXPECT_TRUE(host);
-  EXPECT_TRUE(decoder);
-  EXPECT_TRUE(decoder2);
-  EXPECT_TRUE(encoder);
+typedef RunTestGMPVideoCodec<GMPVideoDecoderProxy,
+                             GetGMPVideoDecoderCallback,
+                             &GeckoMediaPluginService::GetGMPVideoDecoder>
+  RunTestGMPVideoDecoder;
+typedef RunTestGMPVideoCodec<GMPVideoEncoderProxy,
+                             GetGMPVideoEncoderCallback,
+                             &GeckoMediaPluginService::GetGMPVideoEncoder>
+  RunTestGMPVideoEncoder;
 
-  if (decoder) decoder->Close();
-  if (decoder2) decoder2->Close();
-  if (encoder) encoder->Close();
+void
+GMPTestRunner::RunTestGMPTestCodec1(GMPTestMonitor& aMonitor)
+{
+  RunTestGMPVideoDecoder::Run(aMonitor, NS_LITERAL_CSTRING("o"));
+}
+
+void
+GMPTestRunner::RunTestGMPTestCodec2(GMPTestMonitor& aMonitor)
+{
+  RunTestGMPVideoDecoder::Run(aMonitor, NS_LITERAL_CSTRING(""));
 }
 
 void
-GMPTestRunner::RunTestGMPCrossOrigin()
+GMPTestRunner::RunTestGMPTestCodec3(GMPTestMonitor& aMonitor)
+{
+  RunTestGMPVideoEncoder::Run(aMonitor, NS_LITERAL_CSTRING(""));
+}
+
+template<class Base>
+class RunTestGMPCrossOrigin : public Base
 {
-  nsRefPtr<GeckoMediaPluginService> service =
-    GeckoMediaPluginService::GetGeckoMediaPluginService();
+public:
+  virtual void Done(typename Base::GMPCodecType* aGMP, GMPVideoHost* aHost)
+  {
+    EXPECT_TRUE(aGMP);
 
-  GMPVideoHost* host = nullptr;
-  nsTArray<nsCString> tags;
-  tags.AppendElement(NS_LITERAL_CSTRING("h264"));
+    UniquePtr<typename Base::GMPCallbackType> callback(
+      new Step2(Base::mMonitor, aGMP, mShouldBeEqual));
+    nsresult rv = Base::Get(mOrigin2, Move(callback));
+    EXPECT_TRUE(NS_SUCCEEDED(rv));
+    if (NS_FAILED(rv)) {
+      Base::mMonitor.SetFinished();
+    }
+  }
 
-  GMPVideoDecoderProxy* decoder1 = nullptr;
-  GMPVideoDecoderProxy* decoder2 = nullptr;
-  GMPVideoEncoderProxy* encoder1 = nullptr;
-  GMPVideoEncoderProxy* encoder2 = nullptr;
+  static void Run(GMPTestMonitor& aMonitor, const nsCString& aOrigin1,
+                  const nsCString& aOrigin2)
+  {
+    UniquePtr<typename Base::GMPCallbackType> callback(
+      new RunTestGMPCrossOrigin<Base>(aMonitor, aOrigin1, aOrigin2));
+    nsresult rv = Base::Get(aOrigin1, Move(callback));
+    EXPECT_TRUE(NS_SUCCEEDED(rv));
+    if (NS_FAILED(rv)) {
+      aMonitor.SetFinished();
+    }
+  }
 
-  service->GetGMPVideoDecoder(&tags, NS_LITERAL_CSTRING("origin1"), &host, &decoder1);
-  service->GetGMPVideoDecoder(&tags, NS_LITERAL_CSTRING("origin2"), &host, &decoder2);
-  EXPECT_TRUE(!!decoder1 && !!decoder2 &&
-              decoder1->ParentID() != decoder2->ParentID());
+private:
+  RunTestGMPCrossOrigin(GMPTestMonitor& aMonitor, const nsCString& aOrigin1,
+                        const nsCString& aOrigin2)
+    : Base(aMonitor),
+      mGMP(nullptr),
+      mOrigin2(aOrigin2),
+      mShouldBeEqual(aOrigin1.Equals(aOrigin2))
+  {
+  }
 
-  service->GetGMPVideoEncoder(&tags, NS_LITERAL_CSTRING("origin1"), &host, &encoder1);
-  service->GetGMPVideoEncoder(&tags, NS_LITERAL_CSTRING("origin2"), &host, &encoder2);
-  EXPECT_TRUE(!!encoder1 && !!encoder2 &&
-              encoder1->ParentID() != encoder2->ParentID());
+  class Step2 : public Base
+  {
+  public:
+    Step2(GMPTestMonitor& aMonitor,
+          typename Base::GMPCodecType* aGMP,
+          bool aShouldBeEqual)
+      : Base(aMonitor),
+        mGMP(aGMP),
+        mShouldBeEqual(aShouldBeEqual)
+    {
+    }
+    virtual void Done(typename Base::GMPCodecType* aGMP, GMPVideoHost* aHost)
+    {
+      EXPECT_TRUE(aGMP);
+      if (aGMP) {
+        EXPECT_TRUE(mGMP &&
+                    (mGMP->ParentID() == aGMP->ParentID()) == mShouldBeEqual);
+      }
+      if (mGMP) {
+        mGMP->Close();
+      }
+      Base::Done(aGMP, aHost);
+    }
 
-  if (decoder2) decoder2->Close();
-  if (encoder2) encoder2->Close();
+  private:
+    typename Base::GMPCodecType* mGMP;
+    bool mShouldBeEqual;
+  };
 
-  service->GetGMPVideoDecoder(&tags, NS_LITERAL_CSTRING("origin1"), &host, &decoder2);
-  EXPECT_TRUE(!!decoder1 && !!decoder2 &&
-              decoder1->ParentID() == decoder2->ParentID());
+  typename Base::GMPCodecType* mGMP;
+  nsCString mOrigin2;
+  bool mShouldBeEqual;
+};
+
+typedef RunTestGMPCrossOrigin<RunTestGMPVideoDecoder>
+  RunTestGMPVideoDecoderCrossOrigin;
+typedef RunTestGMPCrossOrigin<RunTestGMPVideoEncoder>
+  RunTestGMPVideoEncoderCrossOrigin;
+
+void
+GMPTestRunner::RunTestGMPCrossOrigin1(GMPTestMonitor& aMonitor)
+{
+  RunTestGMPVideoDecoderCrossOrigin::Run(
+    aMonitor, NS_LITERAL_CSTRING("origin1"), NS_LITERAL_CSTRING("origin2"));
+}
 
-  service->GetGMPVideoEncoder(&tags, NS_LITERAL_CSTRING("origin1"), &host, &encoder2);
-  EXPECT_TRUE(!!encoder1 && !!encoder2 &&
-              encoder1->ParentID() == encoder2->ParentID());
+void
+GMPTestRunner::RunTestGMPCrossOrigin2(GMPTestMonitor& aMonitor)
+{
+  RunTestGMPVideoEncoderCrossOrigin::Run(
+    aMonitor, NS_LITERAL_CSTRING("origin1"), NS_LITERAL_CSTRING("origin2"));
+}
 
-  if (decoder1) decoder1->Close();
-  if (decoder2) decoder2->Close();
-  if (encoder1) encoder1->Close();
-  if (encoder2) encoder2->Close();
+void
+GMPTestRunner::RunTestGMPCrossOrigin3(GMPTestMonitor& aMonitor)
+{
+  RunTestGMPVideoDecoderCrossOrigin::Run(
+    aMonitor, NS_LITERAL_CSTRING("origin1"), NS_LITERAL_CSTRING("origin1"));
+}
+
+void
+GMPTestRunner::RunTestGMPCrossOrigin4(GMPTestMonitor& aMonitor)
+{
+  RunTestGMPVideoEncoderCrossOrigin::Run(
+    aMonitor, NS_LITERAL_CSTRING("origin1"), NS_LITERAL_CSTRING("origin1"));
 }
 
 static already_AddRefed<nsIThread>
 GetGMPThread()
 {
   nsRefPtr<GeckoMediaPluginService> service =
     GeckoMediaPluginService::GetGeckoMediaPluginService();
   nsCOMPtr<nsIThread> thread;
@@ -153,18 +301,18 @@ EnumerateDir(nsIFile* aPath, T&& aDirIte
 
 /**
  * Enumerate files under $profileDir/gmp/$aDir/ (non-recursive).
  */
 template<typename T>
 static nsresult
 EnumerateGMPStorageDir(const nsACString& aDir, T&& aDirIter)
 {
-  nsRefPtr<GeckoMediaPluginService> service =
-    GeckoMediaPluginService::GetGeckoMediaPluginService();
+  nsRefPtr<GeckoMediaPluginServiceParent> service =
+    GeckoMediaPluginServiceParent::GetSingleton();
   MOZ_ASSERT(service);
 
   // $profileDir/gmp/
   nsCOMPtr<nsIFile> path;
   nsresult rv = service->GetStorageDir(getter_AddRefs(path));
   if (NS_FAILED(rv)) {
     return rv;
   }
@@ -307,38 +455,63 @@ ClearGMPStorage(nsIRunnable* aContinuati
 }
 
 static void
 SimulatePBModeExit()
 {
   NS_DispatchToMainThread(new NotifyObserversTask("last-pb-context-exited"), NS_DISPATCH_SYNC);
 }
 
+class TestGetNodeIdCallback : public GetNodeIdCallback
+{
+public:
+  TestGetNodeIdCallback(nsCString& aNodeId, nsresult& aResult)
+    : mNodeId(aNodeId),
+      mResult(aResult)
+  {
+  }
+
+  void Done(nsresult aResult, const nsACString& aNodeId)
+  {
+    mResult = aResult;
+    mNodeId = aNodeId;
+  }
+
+private:
+  nsCString& mNodeId;
+  nsresult& mResult;
+};
+
 static nsCString
 GetNodeId(const nsAString& aOrigin,
           const nsAString& aTopLevelOrigin,
           bool aInPBMode)
 {
-  nsRefPtr<GeckoMediaPluginService> service =
-    GeckoMediaPluginService::GetGeckoMediaPluginService();
+  nsRefPtr<GeckoMediaPluginServiceParent> service =
+    GeckoMediaPluginServiceParent::GetSingleton();
   EXPECT_TRUE(service);
   nsCString nodeId;
+  nsresult result;
+  UniquePtr<GetNodeIdCallback> callback(new TestGetNodeIdCallback(nodeId,
+                                                                  result));
+  // We rely on the fact that the GetNodeId implementation for
+  // GeckoMediaPluginServiceParent is synchronous.
   nsresult rv = service->GetNodeId(aOrigin,
                                    aTopLevelOrigin,
                                    aInPBMode,
-                                   nodeId);
-  EXPECT_TRUE(NS_SUCCEEDED(rv));
+                                   Move(callback));
+  EXPECT_TRUE(NS_SUCCEEDED(rv) && NS_SUCCEEDED(result));
   return nodeId;
 }
 
 static bool
 IsGMPStorageIsEmpty()
 {
-  nsRefPtr<GeckoMediaPluginService> service =
-    GeckoMediaPluginService::GetGeckoMediaPluginService();
+  nsRefPtr<GeckoMediaPluginServiceParent> service =
+    GeckoMediaPluginServiceParent::GetSingleton();
   MOZ_ASSERT(service);
   nsCOMPtr<nsIFile> storage;
   nsresult rv = service->GetStorageDir(getter_AddRefs(storage));
   EXPECT_TRUE(NS_SUCCEEDED(rv));
   bool exists = false;
   if (storage) {
     storage->Exists(&exists);
   }
@@ -432,90 +605,152 @@ class GMPStorageTest : public GMPDecrypt
     const nsString origin1 = NS_LITERAL_STRING("example1.com");
     const nsString origin2 = NS_LITERAL_STRING("example2.org");
     nsCString nodeId3 = GetNodeId(origin1, origin2, false);
     EXPECT_TRUE(!aNodeId1.Equals(nodeId3));
 
     SetFinished();
   }
 
+  class CreateDecryptorDone : public GetGMPDecryptorCallback
+  {
+  public:
+    CreateDecryptorDone(GMPStorageTest* aRunner, nsIRunnable* aContinuation)
+      : mRunner(aRunner),
+        mContinuation(aContinuation)
+    {
+    }
+
+    virtual void Done(GMPDecryptorProxy* aDecryptor) override
+    {
+      mRunner->mDecryptor = aDecryptor;
+      EXPECT_TRUE(!!mRunner->mDecryptor);
+
+      if (mRunner->mDecryptor) {
+        mRunner->mDecryptor->Init(mRunner);
+      }
+      nsCOMPtr<nsIThread> thread(GetGMPThread());
+      thread->Dispatch(mContinuation, NS_DISPATCH_NORMAL);
+    }
+
+  private:
+    nsRefPtr<GMPStorageTest> mRunner;
+    nsCOMPtr<nsIRunnable> mContinuation;
+  };
+
   void CreateDecryptor(const nsAString& aOrigin,
                        const nsAString& aTopLevelOrigin,
-                       bool aInPBMode) {
+                       bool aInPBMode,
+                       const nsCString& aUpdate)
+  {
+    nsTArray<nsCString> updates;
+    updates.AppendElement(aUpdate);
+    CreateDecryptor(aOrigin, aTopLevelOrigin, aInPBMode, Move(updates));
+  }
+  class Updates : public nsRunnable
+  {
+  public:
+    Updates(GMPStorageTest* aRunner, nsTArray<nsCString>&& aUpdates)
+      : mRunner(aRunner),
+        mUpdates(aUpdates)
+    {
+    }
+
+    NS_IMETHOD Run()
+    {
+      for (auto& update : mUpdates) {
+        mRunner->Update(update);
+      }
+      return NS_OK;
+    }
+
+  private:
+    nsRefPtr<GMPStorageTest> mRunner;
+    nsTArray<nsCString> mUpdates;
+  };
+  void CreateDecryptor(const nsAString& aOrigin,
+                       const nsAString& aTopLevelOrigin,
+                       bool aInPBMode,
+                       nsTArray<nsCString>&& aUpdates) {
+    nsCOMPtr<nsIRunnable> updates(new Updates(this, Move(aUpdates)));
+    CreateDecryptor(aOrigin, aTopLevelOrigin, aInPBMode, updates);
+  }
+  void CreateDecryptor(const nsAString& aOrigin,
+                       const nsAString& aTopLevelOrigin,
+                       bool aInPBMode,
+                       nsIRunnable* aContinuation) {
     nsRefPtr<GeckoMediaPluginService> service =
       GeckoMediaPluginService::GetGeckoMediaPluginService();
     EXPECT_TRUE(service);
 
     mNodeId = GetNodeId(aOrigin, aTopLevelOrigin, aInPBMode);
     EXPECT_TRUE(!mNodeId.IsEmpty());
 
     nsTArray<nsCString> tags;
     tags.AppendElement(NS_LITERAL_CSTRING("fake"));
 
-    nsresult rv = service->GetGMPDecryptor(&tags, mNodeId, &mDecryptor);
+    UniquePtr<GetGMPDecryptorCallback> callback(
+      new CreateDecryptorDone(this, aContinuation));
+    nsresult rv =
+      service->GetGMPDecryptor(&tags, mNodeId, Move(callback));
     EXPECT_TRUE(NS_SUCCEEDED(rv));
-    EXPECT_TRUE(!!mDecryptor);
-
-    if (mDecryptor) {
-      mDecryptor->Init(this);
-    }
   }
 
   void TestBasicStorage() {
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     nsRefPtr<GeckoMediaPluginService> service =
       GeckoMediaPluginService::GetGeckoMediaPluginService();
 
-    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
-                    NS_LITERAL_STRING("example2.com"),
-                    false);
-
     // Send a message to the fake GMP for it to run its own tests internally.
     // It sends us a "test-storage complete" message when its passed, or
     // some other message if its tests fail.
     Expect(NS_LITERAL_CSTRING("test-storage complete"),
            NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
-    Update(NS_LITERAL_CSTRING("test-storage"));
+
+    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
+                    NS_LITERAL_STRING("example2.com"),
+                    false,
+                    NS_LITERAL_CSTRING("test-storage"));
   }
 
   /**
    * 1. Generate storage data for some sites.
    * 2. Forget about one of the sites.
    * 3. Check if the storage data for the forgotten site are erased correctly.
    * 4. Check if the storage data for other sites remain unchanged.
    */
   void TestForgetThisSite() {
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Generate storage data for some site.
-    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
-                    NS_LITERAL_STRING("example2.com"),
-                    false);
-
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestForgetThisSite_AnotherSite);
     Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
-    Update(NS_LITERAL_CSTRING("test-storage"));
+
+    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
+                    NS_LITERAL_STRING("example2.com"),
+                    false,
+                    NS_LITERAL_CSTRING("test-storage"));
   }
 
   void TestForgetThisSite_AnotherSite() {
     Shutdown();
 
     // Generate storage data for another site.
-    CreateDecryptor(NS_LITERAL_STRING("example3.com"),
-                    NS_LITERAL_STRING("example4.com"),
-                    false);
-
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestForgetThisSite_CollectSiteInfo);
     Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
-    Update(NS_LITERAL_CSTRING("test-storage"));
+
+    CreateDecryptor(NS_LITERAL_STRING("example3.com"),
+                    NS_LITERAL_STRING("example4.com"),
+                    false,
+                    NS_LITERAL_CSTRING("test-storage"));
   }
 
   struct NodeInfo {
     explicit NodeInfo(const nsACString& aSite) : siteToForget(aSite) {}
     nsCString siteToForget;
     nsTArray<nsCString> expectedRemainingNodeIds;
   };
 
@@ -540,18 +775,18 @@ class GMPStorageTest : public GMPDecrypt
     // Collect nodeIds that are expected to remain for later comparison.
     EnumerateGMPStorageDir(NS_LITERAL_CSTRING("id"), NodeIdCollector(siteInfo));
     // Invoke "Forget this site" on the main thread.
     NS_DispatchToMainThread(NS_NewRunnableMethodWithArg<nsAutoPtr<NodeInfo>>(
         this, &GMPStorageTest::TestForgetThisSite_Forget, siteInfo));
   }
 
   void TestForgetThisSite_Forget(nsAutoPtr<NodeInfo> aSiteInfo) {
-    nsRefPtr<GeckoMediaPluginService> service =
-        GeckoMediaPluginService::GetGeckoMediaPluginService();
+    nsRefPtr<GeckoMediaPluginServiceParent> service =
+        GeckoMediaPluginServiceParent::GetSingleton();
     service->ForgetThisSite(NS_ConvertUTF8toUTF16(aSiteInfo->siteToForget));
 
     nsCOMPtr<nsIThread> thread;
     service->GetThread(getter_AddRefs(thread));
 
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethodWithArg<nsAutoPtr<NodeInfo>>(
         this, &GMPStorageTest::TestForgetThisSite_Verify, aSiteInfo);
     thread->Dispatch(r, NS_DISPATCH_NORMAL);
@@ -617,71 +852,68 @@ class GMPStorageTest : public GMPDecrypt
    * 4. Check if all directories in $profileDir/gmp/id/ and
    *    $profileDir/gmp/storage are removed.
    */
   void TestClearRecentHistory1() {
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Generate storage data for some site.
-    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
-                    NS_LITERAL_STRING("example2.com"),
-                    false);
-
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestClearRecentHistory1_Clear);
     Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
-    Update(NS_LITERAL_CSTRING("test-storage"));
 
-  }
+    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
+                    NS_LITERAL_STRING("example2.com"),
+                    false,
+                    NS_LITERAL_CSTRING("test-storage"));
+}
 
   /**
    * 1. Generate some storage data.
    * 2. Find the max mtime |t| in $profileDir/gmp/storage/.
    * 3. Pass |t| to clear recent history.
    * 4. Check if all directories in $profileDir/gmp/id/ and
    *    $profileDir/gmp/storage are removed.
    */
   void TestClearRecentHistory2() {
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Generate storage data for some site.
-    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
-                    NS_LITERAL_STRING("example2.com"),
-                    false);
-
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestClearRecentHistory2_Clear);
     Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
-    Update(NS_LITERAL_CSTRING("test-storage"));
 
+    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
+                    NS_LITERAL_STRING("example2.com"),
+                    false,
+                    NS_LITERAL_CSTRING("test-storage"));
   }
 
   /**
    * 1. Generate some storage data.
    * 2. Find the max mtime |t| in $profileDir/gmp/storage/.
    * 3. Pass |t+1| to clear recent history.
    * 4. Check if all directories in $profileDir/gmp/id/ and
    *    $profileDir/gmp/storage remain unchanged.
    */
   void TestClearRecentHistory3() {
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Generate storage data for some site.
-    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
-                    NS_LITERAL_STRING("example2.com"),
-                    false);
-
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestClearRecentHistory3_Clear);
     Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
-    Update(NS_LITERAL_CSTRING("test-storage"));
 
+    CreateDecryptor(NS_LITERAL_STRING("example1.com"),
+                    NS_LITERAL_STRING("example2.com"),
+                    false,
+                    NS_LITERAL_CSTRING("test-storage"));
   }
 
   class MaxMTimeFinder {
   public:
     MaxMTimeFinder() : mMaxTime(0) {}
     void operator()(nsIFile* aFile) {
       PRTime lastModified;
       nsresult rv = aFile->GetLastModifiedTime(&lastModified);
@@ -769,228 +1001,251 @@ class GMPStorageTest : public GMPDecrypt
     EXPECT_EQ(c2.GetCount(), 1);
 
     SetFinished();
   }
 
   void TestCrossOriginStorage() {
     EXPECT_TRUE(!mDecryptor);
 
-    // Open decryptor on one, origin, write a record, and test that that
-    // record can't be read on another origin.
-    CreateDecryptor(NS_LITERAL_STRING("example3.com"),
-                    NS_LITERAL_STRING("example4.com"),
-                    false);
-
     // Send the decryptor the message "store recordid $time"
     // Wait for the decrytor to send us "stored recordid $time"
     auto t = time(0);
     nsCString response("stored crossOriginTestRecordId ");
     response.AppendInt((int64_t)t);
     Expect(response, NS_NewRunnableMethod(this,
       &GMPStorageTest::TestCrossOriginStorage_RecordStoredContinuation));
 
     nsCString update("store crossOriginTestRecordId ");
     update.AppendInt((int64_t)t);
-    Update(update);
+
+    // Open decryptor on one, origin, write a record, and test that that
+    // record can't be read on another origin.
+    CreateDecryptor(NS_LITERAL_STRING("example3.com"),
+                    NS_LITERAL_STRING("example4.com"),
+                    false,
+                    update);
   }
 
   void TestCrossOriginStorage_RecordStoredContinuation() {
     // Close the old decryptor, and create a new one on a different origin,
     // and try to read the record.
     Shutdown();
 
+    Expect(NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId succeeded (length 0 bytes)"),
+           NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
+
     CreateDecryptor(NS_LITERAL_STRING("example5.com"),
                     NS_LITERAL_STRING("example6.com"),
-                    false);
-
-    Expect(NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId succeeded (length 0 bytes)"),
-           NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
-    Update(NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId"));
+                    false,
+                    NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId"));
   }
 
   void TestPBStorage() {
-    // Open decryptor on one, origin, write a record, close decryptor,
-    // open another, and test that record can be read, close decryptor,
-    // then send pb-last-context-closed notification, then open decryptor
-    // and check that it can't read that data; it should have been purged.
-    CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
-                    NS_LITERAL_STRING("pb2.com"),
-                    true);
-
     // Send the decryptor the message "store recordid $time"
     // Wait for the decrytor to send us "stored recordid $time"
     nsCString response("stored pbdata test-pb-data");
     Expect(response, NS_NewRunnableMethod(this,
       &GMPStorageTest::TestPBStorage_RecordStoredContinuation));
 
-    nsCString update("store pbdata test-pb-data");
-    Update(update);
+    // Open decryptor on one, origin, write a record, close decryptor,
+    // open another, and test that record can be read, close decryptor,
+    // then send pb-last-context-closed notification, then open decryptor
+    // and check that it can't read that data; it should have been purged.
+    CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
+                    NS_LITERAL_STRING("pb2.com"),
+                    true,
+                    NS_LITERAL_CSTRING("store pbdata test-pb-data"));
   }
 
   void TestPBStorage_RecordStoredContinuation() {
     Shutdown();
 
-    CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
-                    NS_LITERAL_STRING("pb2.com"),
-                    true);
-
     Expect(NS_LITERAL_CSTRING("retrieve pbdata succeeded (length 12 bytes)"),
            NS_NewRunnableMethod(this,
               &GMPStorageTest::TestPBStorage_RecordRetrievedContinuation));
-    Update(NS_LITERAL_CSTRING("retrieve pbdata"));
+
+    CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
+                    NS_LITERAL_STRING("pb2.com"),
+                    true,
+                    NS_LITERAL_CSTRING("retrieve pbdata"));
   }
 
   void TestPBStorage_RecordRetrievedContinuation() {
     Shutdown();
     SimulatePBModeExit();
 
-    CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
-                    NS_LITERAL_STRING("pb2.com"),
-                    true);
-
     Expect(NS_LITERAL_CSTRING("retrieve pbdata succeeded (length 0 bytes)"),
            NS_NewRunnableMethod(this,
               &GMPStorageTest::SetFinished));
-    Update(NS_LITERAL_CSTRING("retrieve pbdata"));
+
+    CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
+                    NS_LITERAL_STRING("pb2.com"),
+                    true,
+                    NS_LITERAL_CSTRING("retrieve pbdata"));
+  }
+
+  void NextAsyncShutdownTimeoutTest(nsIRunnable* aContinuation)
+  {
+    if (mDecryptor) {
+      Update(NS_LITERAL_CSTRING("shutdown-mode timeout"));
+      Shutdown();
+    }
+    nsCOMPtr<nsIThread> thread(GetGMPThread());
+    thread->Dispatch(aContinuation, NS_DISPATCH_NORMAL);
   }
 
   void CreateAsyncShutdownTimeoutGMP(const nsAString& aOrigin1,
-                                     const nsAString& aOrigin2) {
-    CreateDecryptor(aOrigin1, aOrigin2, false);
-    Update(NS_LITERAL_CSTRING("shutdown-mode timeout"));
-    Shutdown();
+                                     const nsAString& aOrigin2,
+                                     void (GMPStorageTest::*aCallback)()) {
+    nsCOMPtr<nsIRunnable> continuation(
+      NS_NewRunnableMethodWithArg<nsCOMPtr<nsIRunnable>>(
+        this,
+        &GMPStorageTest::NextAsyncShutdownTimeoutTest,
+        NS_NewRunnableMethod(this, aCallback)));
+
+    CreateDecryptor(aOrigin1, aOrigin2, false, continuation);
   }
 
   void TestAsyncShutdownTimeout() {
     // Create decryptors that timeout in their async shutdown.
     // If the gtest hangs on shutdown, test fails!
     CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example7.com"),
-                                  NS_LITERAL_STRING("example8.com"));
+                                  NS_LITERAL_STRING("example8.com"),
+                                  &GMPStorageTest::TestAsyncShutdownTimeout2);
+  };
+
+  void TestAsyncShutdownTimeout2() {
     CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example9.com"),
-                                  NS_LITERAL_STRING("example10.com"));
+                                  NS_LITERAL_STRING("example10.com"),
+                                  &GMPStorageTest::TestAsyncShutdownTimeout3);
+  };
+
+  void TestAsyncShutdownTimeout3() {
     CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example11.com"),
-                                  NS_LITERAL_STRING("example12.com"));
-    SetFinished();
+                                  NS_LITERAL_STRING("example12.com"),
+                                  &GMPStorageTest::SetFinished);
   };
 
   void TestAsyncShutdownStorage() {
-    // Test that a GMP can write to storage during shutdown, and retrieve
-    // that written data in a subsequent session.
-    CreateDecryptor(NS_LITERAL_STRING("example13.com"),
-                    NS_LITERAL_STRING("example14.com"),
-                    false);
-
     // Instruct the GMP to write a token (the current timestamp, so it's
     // unique) during async shutdown, then shutdown the plugin, re-create
     // it, and check that the token was successfully stored.
     auto t = time(0);
     nsCString update("shutdown-mode token ");
     nsCString token;
     token.AppendInt((int64_t)t);
     update.Append(token);
 
     // Wait for a response from the GMP, so we know it's had time to receive
     // the token.
     nsCString response("shutdown-token received ");
     response.Append(token);
     Expect(response, NS_NewRunnableMethodWithArg<nsCString>(this,
       &GMPStorageTest::TestAsyncShutdownStorage_ReceivedShutdownToken, token));
 
-    Update(update);
+    // Test that a GMP can write to storage during shutdown, and retrieve
+    // that written data in a subsequent session.
+    CreateDecryptor(NS_LITERAL_STRING("example13.com"),
+                    NS_LITERAL_STRING("example14.com"),
+                    false,
+                    update);
   }
 
   void TestAsyncShutdownStorage_ReceivedShutdownToken(const nsCString& aToken) {
     ShutdownThen(NS_NewRunnableMethodWithArg<nsCString>(this,
       &GMPStorageTest::TestAsyncShutdownStorage_AsyncShutdownComplete, aToken));
   }
 
   void TestAsyncShutdownStorage_AsyncShutdownComplete(const nsCString& aToken) {
     // Create a new instance of the plugin, retrieve the token written
     // during shutdown and verify it is correct.
-    CreateDecryptor(NS_LITERAL_STRING("example13.com"),
-                    NS_LITERAL_STRING("example14.com"),
-                    false);
     nsCString response("retrieved shutdown-token ");
     response.Append(aToken);
     Expect(response,
            NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
-    Update(NS_LITERAL_CSTRING("retrieve-shutdown-token"));
+
+    CreateDecryptor(NS_LITERAL_STRING("example13.com"),
+                    NS_LITERAL_STRING("example14.com"),
+                    false,
+                    NS_LITERAL_CSTRING("retrieve-shutdown-token"));
   }
 
 #if defined(XP_WIN)
   void TestOutputProtection() {
     Shutdown();
 
+    Expect(NS_LITERAL_CSTRING("OP tests completed"),
+           NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
+
     CreateDecryptor(NS_LITERAL_STRING("example15.com"),
                     NS_LITERAL_STRING("example16.com"),
-                    false);
-
-    Expect(NS_LITERAL_CSTRING("OP tests completed"),
-           NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
-    Update(NS_LITERAL_CSTRING("test-op-apis"));
+                    false,
+                    NS_LITERAL_CSTRING("test-op-apis"));
   }
 #endif
 
   void TestPluginVoucher() {
+    Expect(NS_LITERAL_CSTRING("retrieved plugin-voucher: gmp-fake placeholder voucher"),
+           NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
+
     CreateDecryptor(NS_LITERAL_STRING("example17.com"),
                     NS_LITERAL_STRING("example18.com"),
-                    false);
-    Expect(NS_LITERAL_CSTRING("retrieved plugin-voucher: gmp-fake placeholder voucher"),
-           NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
-    Update(NS_LITERAL_CSTRING("retrieve-plugin-voucher"));
+                    false,
+                    NS_LITERAL_CSTRING("retrieve-plugin-voucher"));
   }
 
   void TestGetRecordNamesInMemoryStorage() {
     TestGetRecordNames(true);
   }
 
   nsCString mRecordNames;
 
   void AppendIntPadded(nsACString& aString, uint32_t aInt) {
     if (aInt > 0 && aInt < 10) {
       aString.AppendLiteral("0");
     }
     aString.AppendInt(aInt);
   }
 
   void TestGetRecordNames(bool aPrivateBrowsing) {
-    CreateDecryptor(NS_LITERAL_STRING("foo.com"),
-                    NS_LITERAL_STRING("bar.com"),
-                    aPrivateBrowsing);
-
     // Create a number of records of different names.
     const uint32_t num = 100;
+    nsTArray<nsCString> updates(num);
     for (uint32_t i = 0; i < num; i++) {
       nsAutoCString response;
       response.AppendLiteral("stored data");
       AppendIntPadded(response, i);
       response.AppendLiteral(" test-data");
       AppendIntPadded(response, i);
 
       if (i != 0) {
         mRecordNames.AppendLiteral(",");
       }
       mRecordNames.AppendLiteral("data");
       AppendIntPadded(mRecordNames, i);
 
-      nsAutoCString update;
+      nsCString& update = *updates.AppendElement();
       update.AppendLiteral("store data");
       AppendIntPadded(update, i);
       update.AppendLiteral(" test-data");
       AppendIntPadded(update, i);
 
       nsIRunnable* continuation = nullptr;
       if (i + 1 == num) {
         continuation =
           NS_NewRunnableMethod(this, &GMPStorageTest::TestGetRecordNames_QueryNames);
       }
       Expect(response, continuation);
-      Update(update);
     }
+
+    CreateDecryptor(NS_LITERAL_STRING("foo.com"),
+                    NS_LITERAL_STRING("bar.com"),
+                    aPrivateBrowsing,
+                    Move(updates));
   }
 
   void TestGetRecordNames_QueryNames() {
     nsCString response("record-names ");
     response.Append(mRecordNames);
     Expect(response,
            NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
     Update(NS_LITERAL_CSTRING("retrieve-record-names"));
@@ -1020,31 +1275,30 @@ class GMPStorageTest : public GMPDecrypt
       "very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_"
       "long_record_name");
 
     NS_NAMED_LITERAL_CSTRING(data, "Just_some_arbitrary_data.");
 
     MOZ_ASSERT(longRecordName.Length() < GMP_MAX_RECORD_NAME_SIZE);
     MOZ_ASSERT(longRecordName.Length() > 260); // Windows MAX_PATH
 
-    CreateDecryptor(NS_LITERAL_STRING("fuz.com"),
-                    NS_LITERAL_STRING("baz.com"),
-                    false);
-
     nsCString response("stored ");
     response.Append(longRecordName);
     response.AppendLiteral(" ");
     response.Append(data);
     Expect(response, NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
 
     nsCString update("store ");
     update.Append(longRecordName);
     update.AppendLiteral(" ");
     update.Append(data);
-    Update(update);
+    CreateDecryptor(NS_LITERAL_STRING("fuz.com"),
+                    NS_LITERAL_STRING("baz.com"),
+                    false,
+                    update);
   }
 
   void Expect(const nsCString& aMessage, nsIRunnable* aContinuation) {
     mExpected.AppendElement(ExpectedMessage(aMessage, aContinuation));
   }
 
   void AwaitFinished() {
     while (!mFinished) {
@@ -1141,34 +1395,41 @@ private:
 
   GMPDecryptorProxy* mDecryptor;
   Monitor mMonitor;
   Atomic<bool> mFinished;
   nsCString mNodeId;
 };
 
 void
-GMPTestRunner::DoTest(void (GMPTestRunner::*aTestMethod)())
+GMPTestRunner::DoTest(void (GMPTestRunner::*aTestMethod)(GMPTestMonitor&))
 {
-  nsRefPtr<GeckoMediaPluginService> service =
-    GeckoMediaPluginService::GetGeckoMediaPluginService();
-  nsCOMPtr<nsIThread> thread;
+  nsCOMPtr<nsIThread> thread(GetGMPThread());
 
-  service->GetThread(getter_AddRefs(thread));
-  thread->Dispatch(NS_NewRunnableMethod(this, aTestMethod), NS_DISPATCH_SYNC);
+  GMPTestMonitor monitor;
+  thread->Dispatch(NS_NewRunnableMethodWithArg<GMPTestMonitor&>(this,
+                                                                aTestMethod,
+                                                                monitor),
+                   NS_DISPATCH_NORMAL);
+  monitor.AwaitFinished();
 }
 
 TEST(GeckoMediaPlugins, GMPTestCodec) {
   nsRefPtr<GMPTestRunner> runner = new GMPTestRunner();
-  runner->DoTest(&GMPTestRunner::RunTestGMPTestCodec);
+  runner->DoTest(&GMPTestRunner::RunTestGMPTestCodec1);
+  runner->DoTest(&GMPTestRunner::RunTestGMPTestCodec2);
+  runner->DoTest(&GMPTestRunner::RunTestGMPTestCodec3);
 }
 
 TEST(GeckoMediaPlugins, GMPCrossOrigin) {
   nsRefPtr<GMPTestRunner> runner = new GMPTestRunner();
-  runner->DoTest(&GMPTestRunner::RunTestGMPCrossOrigin);
+  runner->DoTest(&GMPTestRunner::RunTestGMPCrossOrigin1);
+  runner->DoTest(&GMPTestRunner::RunTestGMPCrossOrigin2);
+  runner->DoTest(&GMPTestRunner::RunTestGMPCrossOrigin3);
+  runner->DoTest(&GMPTestRunner::RunTestGMPCrossOrigin4);
 }
 
 TEST(GeckoMediaPlugins, GMPStorageGetNodeId) {
   nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
   runner->DoTest(&GMPStorageTest::TestGetNodeId);
 }
 
 TEST(GeckoMediaPlugins, GMPStorageBasic) {
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -366,32 +366,32 @@ skip-if = (toolkit == 'android' && proce
 [test_decode_error.html]
 [test_decoder_disable.html]
 [test_defaultMuted.html]
 [test_delay_load.html]
 skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
 [test_dormant_playback.html]
 skip-if = (os == 'win' && os_version == '5.1') || (os != 'win' && toolkit != 'gonk')
 [test_eme_canvas_blocked.html]
-skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s || (os == 'win' && !debug) # bug 1043403, bug 1057908, bug 1140675
+skip-if = buildapp == 'b2g' || toolkit == 'android' || (os == 'win' && !debug) # bug 1043403, bug 1140675
 [test_eme_non_mse_fails.html]
-skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
+skip-if = buildapp == 'b2g' || toolkit == 'android' # bug 1043403
 #[test_eme_obs_notification.html]
-#skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
+#skip-if = buildapp == 'b2g' || toolkit == 'android' # bug 1043403
 # Disabled (bug 1140778) since this test fails and we don't want to remove the
 # functionality being tested by this test. We should still test other observers
 # in future however, so I'm not removing the test, just disabling it.
 [test_eme_persistent_sessions.html]
-skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
+skip-if = buildapp == 'b2g' || toolkit == 'android' # bug 1043403
 [test_eme_playback.html]
-skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
+skip-if = buildapp == 'b2g' || toolkit == 'android' # bug 1043403
 [test_eme_requestKeySystemAccess.html]
-skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
+skip-if = buildapp == 'b2g' || toolkit == 'android' # bug 1043403
 [test_eme_stream_capture_blocked.html]
-skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s || (os == 'win' && !debug) # bug 1043403, bug 1057908, bug 1140675
+skip-if = buildapp == 'b2g' || toolkit == 'android' || (os == 'win' && !debug) # bug 1043403, bug 1140675
 [test_empty_resource.html]
 [test_error_in_video_document.html]
 skip-if = toolkit == 'android' || (os == 'win' && !debug) || (os == 'mac' && !debug) # bug 608634
 [test_error_on_404.html]
 [test_fastSeek.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_fastSeek-forwards.html]
 [test_imagecapture.html]
--- a/dom/plugins/test/mochitest/cocoa_focus.html
+++ b/dom/plugins/test/mochitest/cocoa_focus.html
@@ -67,17 +67,17 @@
 
       // Blur the window.
       SpecialPowers.focus(opener);
 
       is(plugin1.getFocusState(), true, "(2) Plugin should still have focus.");
       is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
 
       // Focus the window.
-      window.focus();
+      SpecialPowers.focus(window);
 
       is(plugin1.getFocusState(), true, "(3) Plugin should still have focus.");
       is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
 
       // Take focus from the plugin.
       utils.sendNativeMouseEvent(plugin2X * scale, plugin2Y * scale, NSLeftMouseDown, 0, plugin2);
       utils.sendNativeMouseEvent(plugin2X * scale, plugin2Y * scale, NSLeftMouseUp, 0, plugin2);
       expectedEventCount++;
--- a/dom/xbl/nsXBLContentSink.cpp
+++ b/dom/xbl/nsXBLContentSink.cpp
@@ -33,23 +33,22 @@ using namespace mozilla::dom;
 nsresult
 NS_NewXBLContentSink(nsIXMLContentSink** aResult,
                      nsIDocument* aDoc,
                      nsIURI* aURI,
                      nsISupports* aContainer)
 {
   NS_ENSURE_ARG_POINTER(aResult);
 
-  nsXBLContentSink* it = new nsXBLContentSink();
-
-  nsCOMPtr<nsIXMLContentSink> kungFuDeathGrip = it;
+  nsRefPtr<nsXBLContentSink> it = new nsXBLContentSink();
   nsresult rv = it->Init(aDoc, aURI, aContainer);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return CallQueryInterface(it, aResult);
+  it.forget(aResult);
+  return NS_OK;
 }
 
 nsXBLContentSink::nsXBLContentSink()
   : mState(eXBL_InDocument),
     mSecondaryState(eXBL_None),
     mDocInfo(nullptr),
     mIsChromeOrResource(false),
     mFoundFirstBinding(false),
--- a/dom/xml/nsXMLContentSink.cpp
+++ b/dom/xml/nsXMLContentSink.cpp
@@ -81,23 +81,23 @@ NS_NewXMLContentSink(nsIXMLContentSink**
                      nsIURI* aURI,
                      nsISupports* aContainer,
                      nsIChannel* aChannel)
 {
   NS_PRECONDITION(nullptr != aResult, "null ptr");
   if (nullptr == aResult) {
     return NS_ERROR_NULL_POINTER;
   }
-  nsXMLContentSink* it = new nsXMLContentSink();
+  nsRefPtr<nsXMLContentSink> it = new nsXMLContentSink();
   
-  nsCOMPtr<nsIXMLContentSink> kungFuDeathGrip = it;
   nsresult rv = it->Init(aDoc, aURI, aContainer, aChannel);
   NS_ENSURE_SUCCESS(rv, rv);
-  
-  return CallQueryInterface(it, aResult);
+
+  it.forget(aResult);
+  return NS_OK;
 }
 
 nsXMLContentSink::nsXMLContentSink()
   : mConstrainSize(true),
     mPrettyPrintXML(true)
 {
 }
 
--- a/dom/xul/nsXULCommandDispatcher.cpp
+++ b/dom/xul/nsXULCommandDispatcher.cpp
@@ -167,17 +167,17 @@ nsXULCommandDispatcher::GetFocusedWindow
   nsresult rv = window->GetDocument(getter_AddRefs(domdoc));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Note: If there is no document, then this window has been cleared and
   // there's nothing left to protect, so let the window pass through.
   if (domdoc && !nsContentUtils::CanCallerAccess(domdoc))
     return NS_ERROR_DOM_SECURITY_ERR;
 
-  CallQueryInterface(window, aWindow);
+  window.forget(aWindow);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXULCommandDispatcher::SetFocusedElement(nsIDOMElement* aElement)
 {
   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
   NS_ENSURE_TRUE(fm, NS_ERROR_FAILURE);
--- a/dom/xul/templates/nsXULTemplateQueryProcessorRDF.cpp
+++ b/dom/xul/templates/nsXULTemplateQueryProcessorRDF.cpp
@@ -1309,24 +1309,24 @@ nsXULTemplateQueryProcessorRDF::ParseLit
         nsCOMPtr<nsIRDFInt> intLiteral;
         nsresult errorCode;
         int32_t intValue = aValue.ToInteger(&errorCode);
         if (NS_FAILED(errorCode))
             return NS_ERROR_FAILURE;
         rv = gRDFService->GetIntLiteral(intValue, getter_AddRefs(intLiteral));
         if (NS_FAILED(rv)) 
             return rv;
-        rv = CallQueryInterface(intLiteral, aResult);
+        intLiteral.forget(aResult);
     }
     else {
         nsCOMPtr<nsIRDFLiteral> literal;
         rv = gRDFService->GetLiteral(aValue.get(), getter_AddRefs(literal));
         if (NS_FAILED(rv)) 
             return rv;
-        rv = CallQueryInterface(literal, aResult);
+        literal.forget(aResult);
     }
     return rv;
 }
 
 nsresult
 nsXULTemplateQueryProcessorRDF::CompileTripleCondition(nsRDFQuery* aQuery,
                                                        nsIContent* aCondition,
                                                        TestNode* aParentNode,
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -623,16 +623,19 @@ SendSetTargetAPZCNotificationHelper(nsIP
 void
 APZCCallbackHelper::SendSetTargetAPZCNotification(nsIWidget* aWidget,
                                                   nsIDocument* aDocument,
                                                   const WidgetGUIEvent& aEvent,
                                                   const ScrollableLayerGuid& aGuid,
                                                   uint64_t aInputBlockId,
                                                   const nsRefPtr<SetTargetAPZCCallback>& aCallback)
 {
+  if (!aWidget || !aDocument) {
+    return;
+  }
   if (nsIPresShell* shell = aDocument->GetShell()) {
     if (nsIFrame* rootFrame = shell->GetRootFrame()) {
       bool waitForRefresh = false;
       nsTArray<ScrollableLayerGuid> targets;
 
       if (const WidgetTouchEvent* touchEvent = aEvent.AsTouchEvent()) {
         for (size_t i = 0; i < touchEvent->touches.Length(); i++) {
           waitForRefresh |= PrepareForSetTargetAPZCNotification(aWidget, aGuid,
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -610,17 +610,17 @@ bool ImageBridgeChild::StartUpOnThread(T
   MOZ_ASSERT(aThread, "ImageBridge needs a thread.");
   if (sImageBridgeChildSingleton == nullptr) {
     sImageBridgeChildThread = aThread;
     if (!aThread->IsRunning()) {
       aThread->Start();
     }
     sImageBridgeChildSingleton = new ImageBridgeChild();
     sImageBridgeParentSingleton = new ImageBridgeParent(
-      CompositorParent::CompositorLoop(), nullptr, ipc::kCurrentProcessId);
+      CompositorParent::CompositorLoop(), nullptr, base::GetCurrentProcId());
     sImageBridgeChildSingleton->ConnectAsync(sImageBridgeParentSingleton);
     return true;
   } else {
     return false;
   }
 }
 
 bool InImageBridgeChildThread()
@@ -938,17 +938,17 @@ void ImageBridgeChild::RemoveTexture(Tex
   // the other thread
   while (!done) {
     barrier.Wait();
   }
 }
 
 bool ImageBridgeChild::IsSameProcess() const
 {
-  return OtherPid() == ipc::kCurrentProcessId;
+  return OtherPid() == base::GetCurrentProcId();
 }
 
 void ImageBridgeChild::SendPendingAsyncMessges()
 {
   if (!IsCreated() ||
       mTransactionsToRespond.empty()) {
     return;
   }
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -335,17 +335,17 @@ ImageBridgeParent::CloneToplevel(const I
       return bridge;
     }
   }
   return nullptr;
 }
 
 bool ImageBridgeParent::IsSameProcess() const
 {
-  return OtherPid() == ipc::kCurrentProcessId;
+  return OtherPid() == base::GetCurrentProcId();
 }
 
 void
 ImageBridgeParent::ReplyRemoveTexture(const OpReplyRemoveTexture& aReply)
 {
   mPendingAsyncMessage.push_back(aReply);
 }
 
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -971,17 +971,17 @@ LayerTransactionParent::RecvChildAsyncMe
 void
 LayerTransactionParent::ActorDestroy(ActorDestroyReason why)
 {
   DestroyAsyncTransactionTrackersHolder();
 }
 
 bool LayerTransactionParent::IsSameProcess() const
 {
-  return OtherPid() == ipc::kCurrentProcessId;
+  return OtherPid() == base::GetCurrentProcId();
 }
 
 void
 LayerTransactionParent::SendFenceHandleIfPresent(PTextureParent* aTexture,
                                                  CompositableHost* aCompositableHost)
 {
   RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture);
   if (!texture) {
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -733,17 +733,17 @@ ShadowLayerForwarder::IPCOpen() const
 }
 
 bool
 ShadowLayerForwarder::IsSameProcess() const
 {
   if (!HasShadowManager() || !mShadowManager->IPCOpen()) {
     return false;
   }
-  return mShadowManager->OtherPid() == kCurrentProcessId;
+  return mShadowManager->OtherPid() == base::GetCurrentProcId();
 }
 
 /**
   * We bail out when we have no shadow manager. That can happen when the
   * layer manager is created by the preallocated process.
   * See bug 914843 for details.
   */
 PLayerChild*
--- a/image/src/imgTools.cpp
+++ b/image/src/imgTools.cpp
@@ -136,17 +136,18 @@ static nsresult EncodeImageData(DataSour
                                       size.width,
                                       size.height,
                                       map.mStride,
                                       imgIEncoder::INPUT_FORMAT_HOSTARGB,
                                       aOutputOptions);
   aDataSurface->Unmap();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return CallQueryInterface(encoder, aStream);
+  encoder.forget(aStream);
+  return NS_OK;
 }
 
 NS_IMETHODIMP imgTools::EncodeImage(imgIContainer *aContainer,
                                     const nsACString& aMimeType,
                                     const nsAString& aOutputOptions,
                                     nsIInputStream **aStream)
 {
   // Use frame 0 from the image container.
--- a/ipc/glue/BackgroundImpl.cpp
+++ b/ipc/glue/BackgroundImpl.cpp
@@ -1952,17 +1952,17 @@ ChildImpl::OpenMainProcessActorRunnable:
       callback->ActorFailed();
       callback = ChildImpl::GetNextCallback();
     }
 
     return NS_OK;
   }
 
   // Make sure the parent knows it is same process.
-  parentActor->SetOtherProcessId(kCurrentProcessId);
+  parentActor->SetOtherProcessId(base::GetCurrentProcId());
 
   // Now that Open() has succeeded transfer the ownership of the actors to IPDL.
   unused << parentActor.forget();
 
   auto threadLocalInfo =
     static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
 
   MOZ_ASSERT(threadLocalInfo);
--- a/ipc/glue/ProtocolUtils.cpp
+++ b/ipc/glue/ProtocolUtils.cpp
@@ -260,17 +260,17 @@ UnpackChannelOpened(const PrivateIPDLInt
 
 #if defined(XP_WIN)
 bool DuplicateHandle(HANDLE aSourceHandle,
                      DWORD aTargetProcessId,
                      HANDLE* aTargetHandle,
                      DWORD aDesiredAccess,
                      DWORD aOptions) {
   // If our process is the target just duplicate the handle.
-  if (aTargetProcessId == kCurrentProcessId) {
+  if (aTargetProcessId == base::GetCurrentProcId()) {
     return !!::DuplicateHandle(::GetCurrentProcess(), aSourceHandle,
                                ::GetCurrentProcess(), aTargetHandle,
                                aDesiredAccess, false, aOptions);
 
   }
 
 #if defined(MOZ_SANDBOX)
   // Try the broker next (will fail if not sandboxed).
@@ -313,17 +313,17 @@ FatalError(const char* aProtocolName, co
   nsAutoCString formattedMessage("IPDL error [");
   formattedMessage.AppendASCII(aProtocolName);
   formattedMessage.AppendLiteral("]: \"");
   formattedMessage.AppendASCII(aMsg);
   if (aIsParent) {
     formattedMessage.AppendLiteral("\". Killing child side as a result.");
     NS_ERROR(formattedMessage.get());
 
-    if (aOtherPid != kInvalidProcessId && aOtherPid != kCurrentProcessId) {
+    if (aOtherPid != kInvalidProcessId && aOtherPid != base::GetCurrentProcId()) {
       ScopedProcessHandle otherProcessHandle;
       if (base::OpenProcessHandle(aOtherPid, &otherProcessHandle.rwget())) {
         if (!base::KillProcess(otherProcessHandle,
                                base::PROCESS_END_KILLED_BY_USER, false)) {
           NS_ERROR("May have failed to kill child!");
         }
       } else {
         NS_ERROR("Failed to open child process when attempting kill.");
--- a/ipc/glue/ProtocolUtils.h
+++ b/ipc/glue/ProtocolUtils.h
@@ -67,17 +67,16 @@ const base::ProcessHandle kInvalidProces
 // As this could change for process IDs we shouldn't generally rely on this
 // property, however even if that were to change, it seems safe to rely on this
 // particular value never being used.
 const base::ProcessId kInvalidProcessId = kuint32max;
 #else
 const base::ProcessHandle kInvalidProcessHandle = -1;
 const base::ProcessId kInvalidProcessId = -1;
 #endif
-const base::ProcessId kCurrentProcessId = base::GetCurrentProcId();
 
 // Scoped base::ProcessHandle to ensure base::CloseProcessHandle is called.
 struct ScopedProcessHandleTraits
 {
   typedef base::ProcessHandle type;
 
   static type empty()
   {
--- a/ipc/ipdl/ipdl/cxx/cgen.py
+++ b/ipc/ipdl/ipdl/cxx/cgen.py
@@ -180,17 +180,17 @@ class CxxCodeGen(CodePrinter, Visitor):
             self.write('template<')
             self.write('typename ')
             md.T.accept(self)
             self.println('>')
             self.printdent()
 
         if md.inline:
             self.write('inline ')
-        if md.inline:
+        if md.never_inline:
             self.write('MOZ_NEVER_INLINE ')
         if md.static:
             self.write('static ')
         if md.virtual:
             self.write('virtual ')
         if md.ret:
             if md.only_for_definition:
                 self.write('auto ')
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -3000,17 +3000,17 @@ class _GenerateProtocolActorCode(ipdl.as
                              Param(Type('MessageLoop', ptr=True),
                                    aMessageLoop.name),
                              Param(Type('mozilla::ipc::Side'),
                                    sidevar.name,
                                    default=ExprVar('mozilla::ipc::UnknownSide')) ],
                     ret=Type.BOOL))
 
             openmeth.addstmts([
-                StmtExpr(ExprAssn(p.otherPidVar(), ExprVar('ipc::kCurrentProcessId'))),
+                StmtExpr(ExprAssn(p.otherPidVar(), ExprCall(ExprVar('base::GetCurrentProcId')))),
                 StmtReturn(ExprCall(ExprSelect(p.channelVar(), '.', 'Open'),
                                     [ aChannel, aMessageLoop, sidevar ]))
             ])
             self.cls.addstmts([
                 openmeth,
                 Whitespace.NL ])
 
             # Close()
--- a/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.cpp
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.cpp
@@ -14,17 +14,17 @@ using mozilla::ipc::IOThreadChild;
 
 namespace mozilla {
 namespace _ipdltest {
 
 bool
 IPDLUnitTestProcessChild::Init()
 {
     IPDLUnitTestChildInit(IOThreadChild::channel(),
-                          ParentId(),
+                          ParentPid(),
                           IOThreadChild::message_loop());
 
     if (NS_FAILED(nsRegion::InitStatic()))
       return false;
 
     return true;
 }
 
--- a/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp
+++ b/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp
@@ -176,17 +176,17 @@ IPDLUnitTestMain(void* aData)
     gSubprocess = new IPDLUnitTestSubprocess();
     if (!gSubprocess->SyncLaunch(testCaseArgs))
         fail("problem launching subprocess");
 
     IPC::Channel* transport = gSubprocess->GetChannel();
     if (!transport)
         fail("no transport");
 
-    base::ProcessHandle child = gSubprocess->GetChildProcessHandle();
+    base::ProcessId child = base::GetProcId(gSubprocess->GetChildProcessHandle());
 
     switch (test) {
 //-----------------------------------------------------------------------------
 //===== TEMPLATED =====
 ${PARENT_MAIN_CASES_PROC}
 //-----------------------------------------------------------------------------
 
     default:
--- a/ipc/ipdl/test/cxx/TestBridgeMain.cpp
+++ b/ipc/ipdl/test/cxx/TestBridgeMain.cpp
@@ -22,25 +22,20 @@ void
 TestBridgeMainParent::Main()
 {
     if (!SendStart())
         fail("sending Start");
 }
 
 PTestBridgeMainSubParent*
 TestBridgeMainParent::AllocPTestBridgeMainSubParent(Transport* transport,
-                                                    ProcessId otherProcess)
+                                                    ProcessId otherPid)
 {
-    ProcessHandle h;
-    if (!base::OpenProcessHandle(otherProcess, &h)) {
-        return nullptr;
-    }
-
     nsAutoPtr<TestBridgeMainSubParent> a(new TestBridgeMainSubParent(transport));
-    if (!a->Open(transport, h, XRE_GetIOMessageLoop(), ipc::ParentSide)) {
+    if (!a->Open(transport, otherPid, XRE_GetIOMessageLoop(), ipc::ParentSide)) {
         return nullptr;
     }
     return a.forget();
 }
 
 void
 TestBridgeMainParent::ActorDestroy(ActorDestroyReason why)
 {
@@ -105,17 +100,17 @@ TestBridgeMainChild::RecvStart()
     if (!mSubprocess->SyncLaunch(subsubArgs))
         fail("problem launching subprocess");
 
     IPC::Channel* transport = mSubprocess->GetChannel();
     if (!transport)
         fail("no transport");
 
     TestBridgeSubParent* bsp = new TestBridgeSubParent();
-    bsp->Open(transport, mSubprocess->GetChildProcessHandle());
+    bsp->Open(transport, base::GetProcId(mSubprocess->GetChildProcessHandle()));
 
     bsp->Main();
     return true;
 }
 
 void
 TestBridgeMainChild::ActorDestroy(ActorDestroyReason why)
 {
@@ -173,25 +168,20 @@ TestBridgeSubChild::RecvPing()
 {
     if (!SendBridgeEm())
         fail("sending BridgeEm");
     return true;
 }
 
 PTestBridgeMainSubChild*
 TestBridgeSubChild::AllocPTestBridgeMainSubChild(Transport* transport,
-                                                 ProcessId otherProcess)
+                                                 ProcessId otherPid)
 {
-    ProcessHandle h;
-    if (!base::OpenProcessHandle(otherProcess, &h)) {
-        return nullptr;
-    }
-
     nsAutoPtr<TestBridgeMainSubChild> a(new TestBridgeMainSubChild(transport));
-    if (!a->Open(transport, h, XRE_GetIOMessageLoop(), ipc::ChildSide)) {
+    if (!a->Open(transport, otherPid, XRE_GetIOMessageLoop(), ipc::ChildSide)) {
         return nullptr;
     }
 
     if (!a->SendHello())
         fail("sending Hello");
 
     return a.forget();
 }
--- a/ipc/ipdl/test/cxx/TestDataStructures.cpp
+++ b/ipc/ipdl/test/cxx/TestDataStructures.cpp
@@ -489,17 +489,17 @@ TestDataStructuresChild::RecvStart()
     Test10();
     Test11();
     Test12();
     Test13();
     Test14();
     Test15();
     Test16();
     Test17();
-    if (OtherProcess() != 0) {
+    if (OtherPid() != base::GetCurrentProcId()) {
         //FIXME/bug 703317 allocation of nsIntRegion uses a global
         //region pool which breaks threads
         Test18();
     }
 
     for (uint32_t i = 0; i < nactors; ++i)
         if (!PTestDataStructuresSubChild::Send__delete__(mKids[i]))
             fail("can't send dtor");
--- a/ipc/ipdl/test/cxx/TestFailedCtor.cpp
+++ b/ipc/ipdl/test/cxx/TestFailedCtor.cpp
@@ -105,17 +105,17 @@ TestFailedCtorChild::DeallocPTestFailedC
 {
     delete actor;
     return true;
 }
 
 void
 TestFailedCtorChild::ProcessingError(Result aCode, const char* aReason)
 {
-    if (OtherProcess() != 0) // thread-mode
+    if (OtherPid() != base::GetCurrentProcId()) // thread-mode
         _exit(0);
 }
 
 PTestFailedCtorSubsubChild*
 TestFailedCtorSubChild::AllocPTestFailedCtorSubsubChild()
 {
     return new TestFailedCtorSubsub();
 }
--- a/ipc/ipdl/test/cxx/TestHangs.cpp
+++ b/ipc/ipdl/test/cxx/TestHangs.cpp
@@ -106,18 +106,24 @@ TestHangsParent::AnswerStackFrame()
     }
 
     return true;
 }
 
 void
 TestHangsParent::CleanUp()
 {
-    if (!KillProcess(OtherProcess(), 0, false))
-        fail("terminating child process");
+    ipc::ScopedProcessHandle otherProcessHandle;
+    if (!base::OpenProcessHandle(OtherPid(), &otherProcessHandle.rwget())) {
+        fail("couldn't open child process");
+    } else {
+        if (!KillProcess(otherProcessHandle, 0, false)) {
+            fail("terminating child process");
+        }
+    }
     Close();
 }
 
 
 //-----------------------------------------------------------------------------
 // child
 
 TestHangsChild::TestHangsChild()
--- a/ipc/ipdl/test/cxx/TestOpens.cpp
+++ b/ipc/ipdl/test/cxx/TestOpens.cpp
@@ -49,47 +49,42 @@ void
 TestOpensParent::Main()
 {
     if (!SendStart())
         fail("sending Start");
 }
 
 static void
 OpenParent(TestOpensOpenedParent* aParent,
-           Transport* aTransport, ProcessHandle aOtherProcess)
+           Transport* aTransport, base::ProcessId aOtherPid)
 {
     AssertNotMainThread();
 
     // Open the actor on the off-main thread to park it there.
     // Messages will be delivered to this thread's message loop
     // instead of the main thread's.
-    if (!aParent->Open(aTransport, aOtherProcess,
+    if (!aParent->Open(aTransport, aOtherPid,
                        XRE_GetIOMessageLoop(), ipc::ParentSide))
         fail("opening Parent");
 }
 
 PTestOpensOpenedParent*
 TestOpensParent::AllocPTestOpensOpenedParent(Transport* transport,
-                                             ProcessId otherProcess)
+                                             ProcessId otherPid)
 {
     gMainThread = MessageLoop::current();
 
-    ProcessHandle h;
-    if (!base::OpenProcessHandle(otherProcess, &h)) {
-        return nullptr;
-    }
-
     gParentThread = new Thread("ParentThread");
     if (!gParentThread->Start())
         fail("starting parent thread");
 
     TestOpensOpenedParent* a = new TestOpensOpenedParent(transport);
     gParentThread->message_loop()->PostTask(
         FROM_HERE,
-        NewRunnableFunction(OpenParent, a, transport, h));
+        NewRunnableFunction(OpenParent, a, transport, otherPid));
 
     return a;
 }
 
 void
 TestOpensParent::ActorDestroy(ActorDestroyReason why)
 {
     // Stops the thread and joins it
@@ -169,51 +164,46 @@ TestOpensChild::RecvStart()
 {
     if (!PTestOpensOpened::Open(this))
         fail("opening PTestOpensOpened");
     return true;
 }
 
 static void
 OpenChild(TestOpensOpenedChild* aChild,
-           Transport* aTransport, ProcessHandle aOtherProcess)
+           Transport* aTransport, base::ProcessId aOtherPid)
 {
     AssertNotMainThread();
 
     // Open the actor on the off-main thread to park it there.
     // Messages will be delivered to this thread's message loop
     // instead of the main thread's.
-    if (!aChild->Open(aTransport, aOtherProcess,
+    if (!aChild->Open(aTransport, aOtherPid,
                       XRE_GetIOMessageLoop(), ipc::ChildSide))
         fail("opening Child");
 
     // Kick off the unit tests
     if (!aChild->SendHello())
         fail("sending Hello");
 }
 
 PTestOpensOpenedChild*
 TestOpensChild::AllocPTestOpensOpenedChild(Transport* transport,
-                                           ProcessId otherProcess)
+                                           ProcessId otherPid)
 {
     gMainThread = MessageLoop::current();
 
-    ProcessHandle h;
-    if (!base::OpenProcessHandle(otherProcess, &h)) {
-        return nullptr;
-    }
-
     gChildThread = new Thread("ChildThread");
     if (!gChildThread->Start())
         fail("starting child thread");
 
     TestOpensOpenedChild* a = new TestOpensOpenedChild(transport);
     gChildThread->message_loop()->PostTask(
         FROM_HERE,
-        NewRunnableFunction(OpenChild, a, transport, h));
+        NewRunnableFunction(OpenChild, a, transport, otherPid));
 
     return a;
 }
 
 void
 TestOpensChild::ActorDestroy(ActorDestroyReason why)
 {
     // Stops the thread and joins it
--- a/ipc/ipdl/test/cxx/genIPDLUnitTests.py
+++ b/ipc/ipdl/test/cxx/genIPDLUnitTests.py
@@ -111,17 +111,17 @@ def main(argv):
 '''% (t, t) for t in unittests+extras ])
 
 
     child_init_cases = '\n'.join([
 '''    case %s: {
         %sChild** child =
             reinterpret_cast<%sChild**>(&gChildActor);
         *child = new %sChild();
-        (*child)->Open(transport, parent, worker);
+        (*child)->Open(transport, parentPid, worker);
         return;
     }
 '''% (t, t, t, t) for t in unittests+extras ])
 
     templatefile = open(template, 'r')
     sys.stdout.write(
         string.Template(templatefile.read()).substitute(
             INCLUDES=includes,
--- a/js/ipc/JavaScriptLogging.h
+++ b/js/ipc/JavaScriptLogging.h
@@ -93,17 +93,18 @@ class Logging
         print(nsPrintfCString(fmt, tmp1.get(), tmp2.get(), tmp3.get()));
     }
 
     void format(const nsString& str, nsCString& out) {
         out = NS_ConvertUTF16toUTF8(str);
     }
 
     void formatObject(bool incoming, bool local, ObjectId id, nsCString& out) {
-        const char* side, *objDesc;
+        const char* side;
+        const char* objDesc;
         void* ptr;
 
         if (local == incoming) {
             JS::RootedObject obj(cx);
             obj = shared->objects_.find(id);
             if (obj) {
                 JSAutoCompartment ac(cx, obj);
                 objDesc = js::ObjectClassName(cx, obj);
--- a/js/public/HashTable.h
+++ b/js/public/HashTable.h
@@ -865,17 +865,18 @@ class HashTable : private AllocPolicy
           , generation(tableArg.generation())
           , validEntry(true)
 #endif
         {
             while (cur < end && !cur->isLive())
                 ++cur;
         }
 
-        Entry* cur, *end;
+        Entry* cur;
+        Entry* end;
 #ifdef JS_DEBUG
         const HashTable* table_;
         uint64_t mutationCount;
         uint32_t generation;
         bool validEntry;
 #endif
 
       public:
@@ -1085,17 +1086,18 @@ class HashTable : private AllocPolicy
     {
         static_assert(sFreeKey == 0,
                       "newly-calloc'd tables have to be considered empty");
         return alloc.template pod_calloc<Entry>(capacity);
     }
 
     static void destroyTable(AllocPolicy& alloc, Entry* oldTable, uint32_t capacity)
     {
-        for (Entry* e = oldTable, *end = e + capacity; e < end; ++e)
+        Entry* end = oldTable + capacity;
+        for (Entry* e = oldTable; e < end; ++e)
             e->destroyIfLive();
         alloc.free_(oldTable);
     }
 
   public:
     explicit HashTable(AllocPolicy ap)
       : AllocPolicy(ap)
       , table(nullptr)
@@ -1341,17 +1343,18 @@ class HashTable : private AllocPolicy
 
         // We can't fail from here on, so update table parameters.
         setTableSizeLog2(newLog2);
         removedCount = 0;
         gen++;
         table = newTable;
 
         // Copy only live entries, leaving removed ones behind.
-        for (Entry* src = oldTable, *end = src + oldCap; src < end; ++src) {
+        Entry* end = oldTable + oldCap;
+        for (Entry* src = oldTable; src < end; ++src) {
             if (src->isLive()) {
                 HashNumber hn = src->getKeyHash();
                 findFreeEntry(hn).setLive(
                     hn, mozilla::Move(const_cast<typename Entry::NonConstT&>(src->get())));
                 src->destroy();
             }
         }
 
@@ -1475,17 +1478,18 @@ class HashTable : private AllocPolicy
 
   public:
     void clear()
     {
         if (mozilla::IsPod<Entry>::value) {
             memset(table, 0, sizeof(*table) * capacity());
         } else {
             uint32_t tableCapacity = capacity();
-            for (Entry* e = table, *end = table + tableCapacity; e < end; ++e)
+            Entry* end = table + tableCapacity;
+            for (Entry* e = table; e < end; ++e)
                 e->clear();
         }
         removedCount = 0;
         entryCount = 0;
 #ifdef JS_DEBUG
         mutationCount++;
 #endif
     }
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -790,17 +790,18 @@ class MOZ_STACK_CLASS Rooted : public js
     DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(ptr);
 
   private:
     /*
      * These need to be templated on void* to avoid aliasing issues between, for
      * example, Rooted<JSObject> and Rooted<JSFunction>, which use the same
      * stack head pointer for different classes.
      */
-    Rooted<void*>** stack, *prev;
+    Rooted<void*>** stack;
+    Rooted<void*>* prev;
 
     /*
      * |ptr| must be the last field in Rooted because the analysis treats all
      * Rooted as Rooted<void*> during the analysis. See bug 829372.
      */
     T ptr;
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -6358,17 +6358,18 @@ CheckConditional(FunctionCompiler& f, Pa
     MDefinition* condDef;
     Type condType;
     if (!CheckExpr(f, cond, &condDef, &condType))
         return false;
 
     if (!condType.isInt())
         return f.failf(cond, "%s is not a subtype of int", condType.toChars());
 
-    MBasicBlock* thenBlock = nullptr, *elseBlock = nullptr;
+    MBasicBlock* thenBlock = nullptr;
+    MBasicBlock* elseBlock = nullptr;
     if (!f.branchAndStartThen(condDef, &thenBlock, &elseBlock, thenExpr, elseExpr))
         return false;
 
     MDefinition* thenDef;
     Type thenType;
     if (!CheckExpr(f, thenExpr, &thenDef, &thenType))
         return false;
 
@@ -6477,17 +6478,18 @@ CheckAddOrSub(FunctionCompiler& f, Parse
               unsigned* numAddOrSubOut = nullptr)
 {
     JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
 
     MOZ_ASSERT(expr->isKind(PNK_ADD) || expr->isKind(PNK_SUB));
     ParseNode* lhs = AddSubLeft(expr);
     ParseNode* rhs = AddSubRight(expr);
 
-    MDefinition* lhsDef, *rhsDef;
+    MDefinition* lhsDef;
+    MDefinition* rhsDef;
     Type lhsType, rhsType;
     unsigned lhsNumAddOrSub, rhsNumAddOrSub;
 
     if (lhs->isKind(PNK_ADD) || lhs->isKind(PNK_SUB)) {
         if (!CheckAddOrSub(f, lhs, &lhsDef, &lhsType, &lhsNumAddOrSub))
             return false;
         if (lhsType == Type::Intish)
             lhsType = Type::Int;
@@ -6540,17 +6542,18 @@ CheckAddOrSub(FunctionCompiler& f, Parse
 static bool
 CheckDivOrMod(FunctionCompiler& f, ParseNode* expr, MDefinition** def, Type* type)
 {
     MOZ_ASSERT(expr->isKind(PNK_DIV) || expr->isKind(PNK_MOD));
 
     ParseNode* lhs = DivOrModLeft(expr);
     ParseNode* rhs = DivOrModRight(expr);
 
-    MDefinition* lhsDef, *rhsDef;
+    MDefinition* lhsDef;
+    MDefinition* rhsDef;
     Type lhsType, rhsType;
     if (!CheckExpr(f, lhs, &lhsDef, &lhsType))
         return false;
     if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
         return false;
 
     if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
         *def = expr->isKind(PNK_DIV)
@@ -6595,17 +6598,18 @@ static bool
 CheckComparison(FunctionCompiler& f, ParseNode* comp, MDefinition** def, Type* type)
 {
     MOZ_ASSERT(comp->isKind(PNK_LT) || comp->isKind(PNK_LE) || comp->isKind(PNK_GT) ||
                comp->isKind(PNK_GE) || comp->isKind(PNK_EQ) || comp->isKind(PNK_NE));
 
     ParseNode* lhs = ComparisonLeft(comp);
     ParseNode* rhs = ComparisonRight(comp);
 
-    MDefinition* lhsDef, *rhsDef;
+    MDefinition* lhsDef;
+    MDefinition* rhsDef;
     Type lhsType, rhsType;
     if (!CheckExpr(f, lhs, &lhsDef, &lhsType))
         return false;
     if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
         return false;
 
     if ((lhsType.isSigned() && rhsType.isSigned()) || (lhsType.isUnsigned() && rhsType.isUnsigned())) {
         MCompare::CompareType compareType = (lhsType.isUnsigned() && rhsType.isUnsigned())
@@ -6942,19 +6946,22 @@ CheckIfConditional(FunctionCompiler& f, 
 
     // a ? b : c <=> (a && b) || (!a && c)
     // b is always referred to the AND condition, as we need A and B to reach this test,
     // c is always referred as the OR condition, as we reach it if we don't have A.
     ParseNode* cond = TernaryKid1(conditional);
     ParseNode* lhs = TernaryKid2(conditional);
     ParseNode* rhs = TernaryKid3(conditional);
 
-    MBasicBlock* maybeAndTest = nullptr, *maybeOrTest = nullptr;
-    MBasicBlock** ifTrueBlock = &maybeAndTest, **ifFalseBlock = &maybeOrTest;
-    ParseNode* ifTrueBlockNode = lhs, *ifFalseBlockNode = rhs;
+    MBasicBlock* maybeAndTest = nullptr;
+    MBasicBlock* maybeOrTest = nullptr;
+    MBasicBlock** ifTrueBlock = &maybeAndTest;
+    MBasicBlock** ifFalseBlock = &maybeOrTest;
+    ParseNode* ifTrueBlockNode = lhs;
+    ParseNode* ifFalseBlockNode = rhs;
 
     // Try to spot opportunities for short-circuiting in the AND subpart
     uint32_t andTestLiteral = 0;
     bool skipAndTest = false;
 
     if (IsLiteralInt(f.m(), lhs, &andTestLiteral)) {
         skipAndTest = true;
         if (andTestLiteral == 0) {
@@ -7066,17 +7073,18 @@ CheckIf(FunctionCompiler& f, ParseNode* 
 
     ParseNode* nextStmt = NextNode(ifStmt);
   recurse:
     MOZ_ASSERT(ifStmt->isKind(PNK_IF));
     ParseNode* cond = TernaryKid1(ifStmt);
     ParseNode* thenStmt = TernaryKid2(ifStmt);
     ParseNode* elseStmt = TernaryKid3(ifStmt);
 
-    MBasicBlock* thenBlock = nullptr, *elseBlock = nullptr;
+    MBasicBlock* thenBlock = nullptr;
+    MBasicBlock* elseBlock = nullptr;
     ParseNode* elseOrJoinStmt = elseStmt ? elseStmt : nextStmt;
 
     if (!CheckIfCondition(f, cond, thenStmt, elseOrJoinStmt, &thenBlock, &elseBlock))
         return false;
 
     if (!CheckStatement(f, thenStmt))
         return false;
 
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -132,19 +132,20 @@ class OrderedHashTable
         dataCapacity = capacity;
         liveCount = 0;
         hashShift = HashNumberSizeBits - initialBucketsLog2();
         MOZ_ASSERT(hashBuckets() == buckets);
         return true;
     }
 
     ~OrderedHashTable() {
-        for (Range* r = ranges, *next; r; r = next) {
-            next = r->next;
+        for (Range* r = ranges; r; ) {
+            Range* next = r->next;
             r->onTableDestroyed();
+            r = next;
         }
         alloc.free_(hashTable);
         freeData(data, dataLength);
     }
 
     /* Return the number of elements in the table. */
     uint32_t count() const { return liveCount; }
 
@@ -588,17 +589,18 @@ class OrderedHashTable
         for (Range* r = ranges; r; r = r->next)
             r->onCompact();
     }
 
     /* Compact the entries in |data| and rehash them. */
     void rehashInPlace() {
         for (uint32_t i = 0, N = hashBuckets(); i < N; i++)
             hashTable[i] = nullptr;
-        Data* wp = data, *end = data + dataLength;
+        Data* wp = data;
+        Data* end = data + dataLength;
         for (Data* rp = data; rp != end; rp++) {
             if (!Ops::isEmpty(Ops::getKey(rp->element))) {
                 HashNumber h = prepareHash(Ops::getKey(rp->element)) >> hashShift;
                 if (rp != wp)
                     wp->element = Move(rp->element);
                 wp->chain = hashTable[h];
                 hashTable[h] = wp;
                 wp++;
@@ -637,17 +639,18 @@ class OrderedHashTable
         uint32_t newCapacity = uint32_t(newHashBuckets * fillFactor());
         Data* newData = alloc.template pod_malloc<Data>(newCapacity);
         if (!newData) {
             alloc.free_(newHashTable);
             return false;
         }
 
         Data* wp = newData;
-        for (Data* p = data, *end = data + dataLength; p != end; p++) {
+        Data* end = data + dataLength;
+        for (Data* p = data; p != end; p++) {
             if (!Ops::isEmpty(Ops::getKey(p->element))) {
                 HashNumber h = prepareHash(Ops::getKey(p->element)) >> newHashShift;
                 new (wp) Data(Move(p->element), newHashTable[h]);
                 newHashTable[h] = wp;
                 wp++;
             }
         }
         MOZ_ASSERT(wp == newData + liveCount);
--- a/js/src/ds/InlineMap.h
+++ b/js/src/ds/InlineMap.h
@@ -55,17 +55,18 @@ class InlineMap
         if (map.initialized()) {
             map.clear();
         } else {
             if (!map.init(count()))
                 return false;
             MOZ_ASSERT(map.initialized());
         }
 
-        for (InlineElem* it = inl, *end = inl + inlNext; it != end; ++it) {
+        InlineElem* end = inl + inlNext;
+        for (InlineElem* it = inl; it != end; ++it) {
             if (it->key && !map.putNew(it->key, it->value))
                 return false;
         }
 
         inlNext = InlineElems + 1;
         MOZ_ASSERT(map.count() == inlCount);
         MOZ_ASSERT(usingMap());
         return true;
@@ -206,30 +207,32 @@ class InlineMap
         return inl + inlNext;
     }
 
     MOZ_ALWAYS_INLINE
     Ptr lookup(const K& key) {
         if (usingMap())</