Bug 1245789 - Add GMPAdapter which can be passed to GMPLoader to adapt non GMP library to GMP API. r=gerald
authorChris Pearce <cpearce@mozilla.com>
Tue, 12 Apr 2016 16:12:20 +1200
changeset 330603 9912ed83e3e3e14c4fbbdd6a767f01352731b1fe
parent 330602 9da40ac362b7294eff57bd8fb4caa38e8cc7c8a0
child 330604 f8602e536671b8724251c0e2e39bf7338b4b3562
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgerald
bugs1245789
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1245789 - Add GMPAdapter which can be passed to GMPLoader to adapt non GMP library to GMP API. r=gerald This means we can pass a non GMP library to GMPLoader::Load() and the adapter will make it usable. MozReview-Commit-ID: GihjX2JRw3l
dom/media/gmp/GMPLoader.cpp
dom/media/gmp/GMPLoader.h
--- a/dom/media/gmp/GMPLoader.cpp
+++ b/dom/media/gmp/GMPLoader.cpp
@@ -48,45 +48,109 @@
 
 namespace mozilla {
 namespace gmp {
 
 class GMPLoaderImpl : public GMPLoader {
 public:
   explicit GMPLoaderImpl(SandboxStarter* aStarter)
     : mSandboxStarter(aStarter)
+    , mAdapter(nullptr)
   {}
   virtual ~GMPLoaderImpl() {}
 
   bool Load(const char* aUTF8LibPath,
             uint32_t aUTF8LibPathLen,
             char* aOriginSalt,
             uint32_t aOriginSaltLen,
-            const GMPPlatformAPI* aPlatformAPI) override;
+            const GMPPlatformAPI* aPlatformAPI,
+            GMPAdapter* aAdapter) override;
 
   GMPErr GetAPI(const char* aAPIName,
                 void* aHostAPI,
                 void** aPluginAPI) override;
 
   void Shutdown() override;
 
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   void SetSandboxInfo(MacSandboxInfo* aSandboxInfo) override;
 #endif
 
 private:
-  PRLibrary* mLib;
-  GMPGetAPIFunc mGetAPIFunc;
   SandboxStarter* mSandboxStarter;
+  UniquePtr<GMPAdapter> mAdapter;
 };
 
 GMPLoader* CreateGMPLoader(SandboxStarter* aStarter) {
   return static_cast<GMPLoader*>(new GMPLoaderImpl(aStarter));
 }
 
+class PassThroughGMPAdapter : public GMPAdapter {
+public:
+  ~PassThroughGMPAdapter() {
+    // Ensure we're always shutdown, even if caller forgets to call GMPShutdown().
+    GMPShutdown();
+  }
+
+  void SetAdaptee(PRLibrary* aLib) override
+  {
+    mLib = aLib;
+  }
+
+  GMPErr GMPInit(const GMPPlatformAPI* aPlatformAPI) override
+  {
+    if (!mLib) {
+      return GMPGenericErr;
+    }
+    GMPInitFunc initFunc = reinterpret_cast<GMPInitFunc>(PR_FindFunctionSymbol(mLib, "GMPInit"));
+    if (!initFunc) {
+      return GMPNotImplementedErr;
+    }
+    return initFunc(aPlatformAPI);
+  }
+
+  GMPErr GMPGetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI) override
+  {
+    if (!mLib) {
+      return GMPGenericErr;
+    }
+    GMPGetAPIFunc getapiFunc = reinterpret_cast<GMPGetAPIFunc>(PR_FindFunctionSymbol(mLib, "GMPGetAPI"));
+    if (!getapiFunc) {
+      return GMPNotImplementedErr;
+    }
+    return getapiFunc(aAPIName, aHostAPI, aPluginAPI);
+  }
+
+  void GMPShutdown() override
+  {
+    if (mLib) {
+      GMPShutdownFunc shutdownFunc = reinterpret_cast<GMPShutdownFunc>(PR_FindFunctionSymbol(mLib, "GMPShutdown"));
+      if (shutdownFunc) {
+        shutdownFunc();
+      }
+      PR_UnloadLibrary(mLib);
+      mLib = nullptr;
+    }
+  }
+
+  void GMPSetNodeId(const char* aNodeId, uint32_t aLength) override
+  {
+    if (!mLib) {
+      return;
+    }
+    GMPSetNodeIdFunc setNodeIdFunc = reinterpret_cast<GMPSetNodeIdFunc>(PR_FindFunctionSymbol(mLib, "GMPSetNodeId"));
+    if (setNodeIdFunc) {
+      setNodeIdFunc(aNodeId, aLength);
+    }
+  }
+
+private:
+  PRLibrary* mLib = nullptr;
+};
+
 #if defined(XP_WIN) && defined(HASH_NODE_ID_WITH_DEVICE_ID)
 MOZ_NEVER_INLINE
 static bool
 GetStackAfterCurrentFrame(uint8_t** aOutTop, uint8_t** aOutBottom)
 {
   // "Top" of the free space on the stack is directly after the memory
   // holding our return address.
   uint8_t* top = (uint8_t*)_AddressOfReturnAddress();
@@ -170,17 +234,18 @@ static void SecureMemset(void* start, ui
 }
 #endif
 
 bool
 GMPLoaderImpl::Load(const char* aUTF8LibPath,
                     uint32_t aUTF8LibPathLen,
                     char* aOriginSalt,
                     uint32_t aOriginSaltLen,
-                    const GMPPlatformAPI* aPlatformAPI)
+                    const GMPPlatformAPI* aPlatformAPI,
+                    GMPAdapter* aAdapter)
 {
   std::string nodeId;
 #ifdef HASH_NODE_ID_WITH_DEVICE_ID
   if (aOriginSaltLen > 0) {
     std::vector<uint8_t> deviceId;
     int volumeId;
     if (!rlz_lib::GetRawMachineId(&deviceId, &volumeId)) {
       return false;
@@ -254,62 +319,54 @@ GMPLoaderImpl::Load(const char* aUTF8Lib
   }
 
   libSpec.value.pathname_u = widePath.get();
   libSpec.type = PR_LibSpec_PathnameU;
 #else
   libSpec.value.pathname = aUTF8LibPath;
   libSpec.type = PR_LibSpec_Pathname;
 #endif
-  mLib = PR_LoadLibraryWithFlags(libSpec, 0);
-  if (!mLib) {
-    return false;
-  }
-
-  GMPInitFunc initFunc = reinterpret_cast<GMPInitFunc>(PR_FindFunctionSymbol(mLib, "GMPInit"));
-  if (!initFunc) {
+  PRLibrary* lib = PR_LoadLibraryWithFlags(libSpec, 0);
+  if (!lib) {
     return false;
   }
 
-  if (initFunc(aPlatformAPI) != GMPNoErr) {
+  GMPInitFunc initFunc = reinterpret_cast<GMPInitFunc>(PR_FindFunctionSymbol(lib, "GMPInit"));
+  if ((initFunc && aAdapter) ||
+      (!initFunc && !aAdapter)) {
+    // Ensure that if we're dealing with a GMP we do *not* use an adapter
+    // provided from the outside world. This is important as it means we
+    // don't call code not covered by Adobe's plugin-container voucher
+    // before we pass the node Id to Adobe's GMP.
     return false;
   }
 
-  GMPSetNodeIdFunc setNodeIdFunc = reinterpret_cast<GMPSetNodeIdFunc>(PR_FindFunctionSymbol(mLib, "GMPSetNodeId"));
-  if (setNodeIdFunc) {
-    setNodeIdFunc(nodeId.c_str(), nodeId.size());
-  }
+  // Note: PassThroughGMPAdapter's code must remain in this file so that it's
+  // covered by Adobe's plugin-container voucher.
+  mAdapter.reset((!aAdapter) ? new PassThroughGMPAdapter() : aAdapter);
+  mAdapter->SetAdaptee(lib);
 
-  mGetAPIFunc = reinterpret_cast<GMPGetAPIFunc>(PR_FindFunctionSymbol(mLib, "GMPGetAPI"));
-  if (!mGetAPIFunc) {
-    return false;
-  }
+  mAdapter->GMPInit(aPlatformAPI);
 
   return true;
 }
 
 GMPErr
 GMPLoaderImpl::GetAPI(const char* aAPIName,
                       void* aHostAPI,
                       void** aPluginAPI)
 {
-  return mGetAPIFunc ? mGetAPIFunc(aAPIName, aHostAPI, aPluginAPI)
-                     : GMPGenericErr;
+  return mAdapter->GMPGetAPI(aAPIName, aHostAPI, aPluginAPI);
 }
 
 void
 GMPLoaderImpl::Shutdown()
 {
-  if (mLib) {
-    GMPShutdownFunc shutdownFunc = reinterpret_cast<GMPShutdownFunc>(PR_FindFunctionSymbol(mLib, "GMPShutdown"));
-    if (shutdownFunc) {
-      shutdownFunc();
-    }
-    PR_UnloadLibrary(mLib);
-    mLib = nullptr;
+  if (mAdapter) {
+    mAdapter->GMPShutdown();
   }
 }
 
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
 void
 GMPLoaderImpl::SetSandboxInfo(MacSandboxInfo* aSandboxInfo)
 {
   if (mSandboxStarter) {
--- a/dom/media/gmp/GMPLoader.h
+++ b/dom/media/gmp/GMPLoader.h
@@ -3,16 +3,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 GMP_LOADER_H__
 #define GMP_LOADER_H__
 
 #include <stdint.h>
+#include "prlink.h"
 #include "gmp-entrypoints.h"
 
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
 #include "mozilla/Sandbox.h"
 #endif
 
 namespace mozilla {
 namespace gmp {
@@ -24,44 +25,66 @@ public:
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   // On OS X we need to set Mac-specific sandbox info just before we start the
   // sandbox, which we don't yet know when the GMPLoader and SandboxStarter
   // objects are created.
   virtual void SetSandboxInfo(MacSandboxInfo* aSandboxInfo) = 0;
 #endif
 };
 
+// Interface that adapts a plugin to the GMP API.
+class GMPAdapter {
+public:
+  virtual ~GMPAdapter() {}
+  // Sets the adapted to plugin library module.
+  // Note: the GMPAdapter is responsible for calling PR_UnloadLibrary on aLib
+  // when it's finished with it.
+  virtual void SetAdaptee(PRLibrary* aLib) = 0;
+
+  // These are called in place of the corresponding GMP API functions.
+  virtual GMPErr GMPInit(const GMPPlatformAPI* aPlatformAPI) = 0;
+  virtual GMPErr GMPGetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI) = 0;
+  virtual void GMPShutdown() = 0;
+  virtual void GMPSetNodeId(const char* aNodeId, uint32_t aLength) = 0;
+};
+
 // Encapsulates generating the device-bound node id, activating the sandbox,
 // loading the GMP, and passing the node id to the GMP (in that order).
 //
 // In Desktop Gecko, the implementation of this lives in plugin-container,
 // and is passed into XUL code from on startup. The GMP IPC child protocol actor
 // uses this interface to load and retrieve interfaces from the GMPs.
 //
 // In Desktop Gecko the implementation lives in the plugin-container so that
 // it can be covered by DRM vendor's voucher.
 //
 // On Android the GMPLoader implementation lives in libxul (because for the time
 // being GMPLoader relies upon NSPR, which we can't use in plugin-container
 // on Android).
 //
 // There is exactly one GMPLoader per GMP child process, and only one GMP
 // per child process (so the GMPLoader only loads one GMP).
+//
+// Load() takes an optional GMPAdapter which can be used to adapt non-GMPs
+// to adhere to the GMP API.
 class GMPLoader {
 public:
   virtual ~GMPLoader() {}
 
   // Calculates the device-bound node id, then activates the sandbox,
   // then loads the GMP library and (if applicable) passes the bound node id
-  // to the GMP.
+  // to the GMP. If aAdapter is non-null, the lib path is assumed to be
+  // a non-GMP, and the adapter is initialized with the lib and the adapter
+  // is used to interact with the plugin.
   virtual bool Load(const char* aUTF8LibPath,
                     uint32_t aLibPathLen,
                     char* aOriginSalt,
                     uint32_t aOriginSaltLen,
-                    const GMPPlatformAPI* aPlatformAPI) = 0;
+                    const GMPPlatformAPI* aPlatformAPI,
+                    GMPAdapter* aAdapter = nullptr) = 0;
 
   // Retrieves an interface pointer from the GMP.
   virtual GMPErr GetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI) = 0;
 
   // Calls the GMPShutdown function exported by the GMP lib, and unloads the
   // plugin library.
   virtual void Shutdown() = 0;