Bug 1088488 - Change GMPChild to use GMPLoader to load GMPs. r=jesup
authorChris Pearce <cpearce@mozilla.com>
Fri, 14 Nov 2014 21:26:24 +1300
changeset 240111 8078ba66f37cd1c9b6b6a63e358ad5bdc0db32bd
parent 240110 591742c4c713288981400ff91e18b373cf17b3a6
child 240112 dd073758b0bc177428ef4d6e4560b459a6ef5499
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup
bugs1088488
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1088488 - Change GMPChild to use GMPLoader to load GMPs. r=jesup
dom/media/gmp/GMPChild.cpp
dom/media/gmp/GMPChild.h
--- a/dom/media/gmp/GMPChild.cpp
+++ b/dom/media/gmp/GMPChild.cpp
@@ -1,14 +1,16 @@
 /* -*- 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 "GMPProcessChild.h"
+#include "GMPLoader.h"
 #include "GMPVideoDecoderChild.h"
 #include "GMPVideoEncoderChild.h"
 #include "GMPAudioDecoderChild.h"
 #include "GMPDecryptorChild.h"
 #include "GMPVideoHost.h"
 #include "nsDebugImpl.h"
 #include "nsIFile.h"
 #include "nsXULAppAPI.h"
@@ -24,48 +26,32 @@
 using mozilla::dom::CrashReporterChild;
 
 #ifdef XP_WIN
 #include <stdlib.h> // for _exit()
 #else
 #include <unistd.h> // for _exit()
 #endif
 
-#if defined(XP_WIN)
-// In order to provide EME plugins with a "device binding" capability,
-// in the parent we generate and store some random bytes as salt for every
-// (origin, urlBarOrigin) pair that uses EME. We store these bytes so
-// that every time we revisit the same origin we get the same salt.
-// We send this salt to the child on startup. The child collects some
-// device specific data and munges that with the salt to create the
-// "node id" that we expose to EME plugins. It then overwrites the device
-// specific data, and activates the sandbox.
-#define HASH_NODE_ID_WITH_DEVICE_ID 1
-#include "rlz/lib/machine_id.h"
-#include "rlz/lib/string_utils.h"
-#include "mozilla/SHA1.h"
-#endif
-
 #if defined(MOZ_SANDBOX) && defined(XP_WIN)
 #define TARGET_SANDBOX_EXPORTS
 #include "mozilla/sandboxTarget.h"
 #elif defined (MOZ_GMP_SANDBOX)
 #if defined(XP_LINUX) || defined(XP_MACOSX)
 #include "mozilla/Sandbox.h"
 #endif
 #endif
 
 namespace mozilla {
 namespace gmp {
 
 GMPChild::GMPChild()
   : mAsyncShutdown(nullptr)
-  , mLib(nullptr)
-  , mGetAPIFunc(nullptr)
   , mGMPMessageLoop(MessageLoop::current())
+  , mGMPLoader(nullptr)
 {
   nsDebugImpl::SetMultiprocessMode("GMP");
 }
 
 GMPChild::~GMPChild()
 {
 }
 
@@ -278,63 +264,30 @@ GMPChild::Init(const std::string& aPlugi
 
   mPluginPath = aPluginPath;
   return true;
 }
 
 bool
 GMPChild::RecvSetNodeId(const nsCString& aNodeId)
 {
-#ifdef HASH_NODE_ID_WITH_DEVICE_ID
-  if (!aNodeId.IsEmpty() && !aNodeId.EqualsLiteral("null")) {
-    string16 deviceId;
-    int volumeId;
-    if (!rlz_lib::GetRawMachineId(&deviceId, &volumeId)) {
-      return false;
-    }
-
-    // TODO: Switch to SHA256.
-    mozilla::SHA1Sum hash;
-    hash.update(deviceId.c_str(), deviceId.size() * sizeof(string16::value_type));
-    hash.update(aNodeId.get(), aNodeId.Length());
-    hash.update(&volumeId, sizeof(int));
-    uint8_t digest[mozilla::SHA1Sum::kHashSize];
-    hash.finish(digest);
-    if (!rlz_lib::BytesToString(digest, mozilla::SHA1Sum::kHashSize, &mNodeId)) {
-      return false;
-    }
-
-    // Overwrite device id as it could potentially identify the user, so
-    // there's no chance a GMP can read it and use it for identity tracking.
-    volumeId = 0;
-    memset(&deviceId.front(), '*', sizeof(string16::value_type) * deviceId.size());
-    deviceId = L"";
-  } else {
-    mNodeId = "null";
-  }
-#else
+  // Store the per origin salt for the node id. Note: we do this in a
+  // separate message than RecvStartPlugin() so that the string is not
+  // sitting in a string on the IPC code's call stack.
   mNodeId = std::string(aNodeId.BeginReading(), aNodeId.EndReading());
-#endif
   return true;
 }
 
-bool
-GMPChild::RecvStartPlugin()
+GMPErr
+GMPChild::GetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI)
 {
-#ifdef XP_WIN
-  PreLoadLibraries(mPluginPath);
-#endif
-
-#if defined(MOZ_SANDBOX) && defined(XP_WIN)
-  mozilla::SandboxTarget::Instance()->StartSandbox();
-#elif defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
-  StartMacSandbox();
-#endif
-
-  return LoadPluginLibrary(mPluginPath);
+  if (!mGMPLoader) {
+    return GMPGenericErr;
+  }
+  return mGMPLoader->GetAPI(aAPIName, aHostAPI, aPluginAPI);
 }
 
 #ifdef XP_WIN
 // Pre-load DLLs that need to be used by the EME plugin but that can't be
 // loaded after the sandbox has started
 bool
 GMPChild::PreLoadLibraries(const std::string& aPluginPath)
 {
@@ -387,74 +340,116 @@ GMPChild::PreLoadLibraries(const std::st
       break;
     }
   } while (!stream.eof());
 
   return true;
 }
 #endif
 
+#if defined(MOZ_GMP_SANDBOX)
+
+#if defined(XP_LINUX)
+class LinuxSandboxStarter : public SandboxStarter {
+public:
+  LinuxSandboxStarter(const std::string& aLibPath)
+    : mLibPath(aLibPath)
+  {}
+  virtual void Start() MOZ_OVERRIDE {
+    if (mozilla::MediaPluginSandboxStatus() != mozilla::kSandboxingWouldFail) {
+      mozilla::SetMediaPluginSandbox(mLibPath.c_str());
+    } else {
+      printf_stderr("GMPChild::LoadPluginLibrary: Loading media plugin %s unsandboxed.\n",
+                    mLibPath.c_str());
+    }
+  }
+private:
+  std::string mLibPath;
+};
+#endif
+
+#if defined(XP_MACOSX)
+class MacOSXSandboxStarter : public SandboxStarter {
+public:
+  MacOSXSandboxStarter(GMPChild* aGMPChild)
+    : mGMPChild(aGMPChild)
+  {}
+  virtual void Start() MOZ_OVERRIDE {
+    mGMPChild->StartMacSandbox();
+  }
+private:
+  GMPChild* mGMPChild;
+};
+#endif
+
+#endif // MOZ_GMP_SANDBOX
+
 bool
-GMPChild::LoadPluginLibrary(const std::string& aPluginPath)
+GMPChild::GetLibPath(nsACString& aOutLibPath)
 {
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
-  nsAutoCString nativePath;
-  nativePath.Assign(mPluginBinaryPath);
-
-  mLib = PR_LoadLibrary(nativePath.get());
+  nsAutoCString pluginDirectoryPath, pluginFilePath;
+  if (!GetPluginPaths(mPluginPath, pluginDirectoryPath, pluginFilePath)) {
+    MOZ_CRASH("Error scanning plugin path");
+  }
+  aOutLibPath.Assign(pluginFilePath);
+  return true;
 #else
   nsCOMPtr<nsIFile> libFile;
-  if (!GetPluginFile(aPluginPath, libFile)) {
+  if (!GetPluginFile(mPluginPath, libFile)) {
     return false;
   }
-#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
-  nsAutoCString nativePath;
-  libFile->GetNativePath(nativePath);
+  return NS_SUCCEEDED(libFile->GetNativePath(aOutLibPath));
+#endif
+}
 
-  // Enable sandboxing here -- we know the plugin file's path, but
-  // this process's execution hasn't been affected by its content yet.
-  if (mozilla::MediaPluginSandboxStatus() != mozilla::kSandboxingWouldFail) {
-    mozilla::SetMediaPluginSandbox(nativePath.get());
-  } else {
-    printf_stderr("GMPChild::LoadPluginLibrary: Loading media plugin %s unsandboxed.\n",
-                  nativePath.get());
-  }
-#endif // XP_LINUX && MOZ_GMP_SANDBOX
+bool
+GMPChild::RecvStartPlugin()
+{
+#if defined(XP_WIN)
+  PreLoadLibraries(mPluginPath);
+#endif
 
-  libFile->Load(&mLib);
-#endif // XP_MACOSX && MOZ_GMP_SANDBOX
-
-  if (!mLib) {
-    NS_WARNING("Failed to link Gecko Media Plugin library.");
-    return false;
-  }
-
-  GMPInitFunc initFunc = reinterpret_cast<GMPInitFunc>(PR_FindFunctionSymbol(mLib, "GMPInit"));
-  if (!initFunc) {
-    NS_WARNING("Failed to link Gecko Media Plugin Init function.");
+  nsCString libPath;
+  if (!GetLibPath(libPath)) {
     return false;
   }
 
   auto platformAPI = new GMPPlatformAPI();
   InitPlatformAPI(*platformAPI, this);
 
-  if (initFunc(platformAPI) != GMPNoErr) {
-    NS_WARNING("Gecko Media Plugin failed to initialize.");
+  mGMPLoader = GMPProcessChild::GetGMPLoader();
+  if (!mGMPLoader) {
+    NS_WARNING("Failed to get GMPLoader");
     return false;
   }
 
-  mGetAPIFunc = reinterpret_cast<GMPGetAPIFunc>(PR_FindFunctionSymbol(mLib, "GMPGetAPI"));
-  if (!mGetAPIFunc) {
-    NS_WARNING("Failed to link Gecko Media Plugin GetAPI function.");
+#if defined(MOZ_GMP_SANDBOX)
+#if defined(XP_MACOSX)
+  nsAutoPtr<SandboxStarter> starter(new MacOSXSandboxStarter(this));
+  mGMPLoader->SetStartSandboxStarter(starter);
+#elif defined(XP_LINUX)
+  nsAutoPtr<SandboxStarter> starter(new
+    LinuxSandboxStarter(std::string(libPath.get(), libPath.get()+libPath.Length())));
+  mGMPLoader->SetStartSandboxStarter(starter);
+#endif
+#endif // MOZ_GMP_SANDBOX
+
+  if (!mGMPLoader->Load(libPath.get(),
+                        libPath.Length(),
+                        &mNodeId[0],
+                        mNodeId.size(),
+                        platformAPI)) {
+    NS_WARNING("Failed to load GMP");
     return false;
   }
 
   void* sh = nullptr;
   GMPAsyncShutdownHost* host = static_cast<GMPAsyncShutdownHost*>(this);
-  GMPErr err = mGetAPIFunc("async-shutdown", host, &sh);
+  GMPErr err = GetAPI("async-shutdown", host, &sh);
   if (err == GMPNoErr && sh) {
     mAsyncShutdown = reinterpret_cast<GMPAsyncShutdown*>(sh);
     SendAsyncShutdownRequired();
   }
 
   return true;
 }
 
@@ -462,23 +457,19 @@ MessageLoop*
 GMPChild::GMPMessageLoop()
 {
   return mGMPMessageLoop;
 }
 
 void
 GMPChild::ActorDestroy(ActorDestroyReason aWhy)
 {
-  if (mLib) {
-    GMPShutdownFunc shutdownFunc = reinterpret_cast<GMPShutdownFunc>(PR_FindFunctionSymbol(mLib, "GMPShutdown"));
-    if (shutdownFunc) {
-      shutdownFunc();
-    }
+  if (mGMPLoader) {
+    mGMPLoader->Shutdown();
   }
-
   if (AbnormalShutdown == aWhy) {
     NS_WARNING("Abnormal shutdown of GMP process!");
     _exit(0);
   }
 
   XRE_ShutdownChildProcess();
 }
 
@@ -560,17 +551,17 @@ GMPChild::DeallocPGMPDecryptorChild(PGMP
 }
 
 bool
 GMPChild::RecvPGMPAudioDecoderConstructor(PGMPAudioDecoderChild* aActor)
 {
   auto vdc = static_cast<GMPAudioDecoderChild*>(aActor);
 
   void* vd = nullptr;
-  GMPErr err = mGetAPIFunc("decode-audio", &vdc->Host(), &vd);
+  GMPErr err = GetAPI("decode-audio", &vdc->Host(), &vd);
   if (err != GMPNoErr || !vd) {
     return false;
   }
 
   vdc->Init(static_cast<GMPAudioDecoder*>(vd));
 
   return true;
 }
@@ -589,34 +580,34 @@ GMPChild::DeallocPGMPVideoEncoderChild(P
 }
 
 bool
 GMPChild::RecvPGMPVideoDecoderConstructor(PGMPVideoDecoderChild* aActor)
 {
   auto vdc = static_cast<GMPVideoDecoderChild*>(aActor);
 
   void* vd = nullptr;
-  GMPErr err = mGetAPIFunc("decode-video", &vdc->Host(), &vd);
+  GMPErr err = GetAPI("decode-video", &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 = mGetAPIFunc("encode-video", &vec->Host(), &ve);
+  GMPErr err = GetAPI("encode-video", &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;
@@ -624,17 +615,17 @@ GMPChild::RecvPGMPVideoEncoderConstructo
 
 bool
 GMPChild::RecvPGMPDecryptorConstructor(PGMPDecryptorChild* aActor)
 {
   GMPDecryptorChild* child = static_cast<GMPDecryptorChild*>(aActor);
   GMPDecryptorHost* host = static_cast<GMPDecryptorHost*>(child);
 
   void* session = nullptr;
-  GMPErr err = mGetAPIFunc("eme-decrypt", host, &session);
+  GMPErr err = GetAPI("eme-decrypt", host, &session);
   if (err != GMPNoErr || !session) {
     return false;
   }
 
   child->Init(static_cast<GMPDecryptor*>(session));
 
   return true;
 }
--- a/dom/media/gmp/GMPChild.h
+++ b/dom/media/gmp/GMPChild.h
@@ -5,16 +5,17 @@
 
 #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 GMPChild : public PGMPChild
@@ -24,38 +25,39 @@ class GMPChild : public PGMPChild
 public:
   GMPChild();
   virtual ~GMPChild();
 
   bool Init(const std::string& aPluginPath,
             base::ProcessHandle aParentProcessHandle,
             MessageLoop* aIOLoop,
             IPC::Channel* aChannel);
-  bool LoadPluginLibrary(const std::string& aPluginPath);
 #ifdef XP_WIN
   bool PreLoadLibraries(const std::string& aPluginPath);
 #endif
   MessageLoop* GMPMessageLoop();
 
   // Main thread only.
   GMPTimerChild* GetGMPTimers();
   GMPStorageChild* GetGMPStorage();
 
   // GMPSharedMem
   virtual void CheckThread() MOZ_OVERRIDE;
 
   // GMPAsyncShutdownHost
   void ShutdownComplete() MOZ_OVERRIDE;
 
-private:
-
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   void StartMacSandbox();
 #endif
 
+private:
+
+  bool GetLibPath(nsACString& aOutLibPath);
+
   virtual bool RecvSetNodeId(const nsCString& aNodeId) MOZ_OVERRIDE;
   virtual bool RecvStartPlugin() MOZ_OVERRIDE;
 
   virtual PCrashReporterChild* AllocPCrashReporterChild(const NativeThreadId& aThread) MOZ_OVERRIDE;
   virtual bool DeallocPCrashReporterChild(PCrashReporterChild*) MOZ_OVERRIDE;
 
   virtual PGMPVideoDecoderChild* AllocPGMPVideoDecoderChild() MOZ_OVERRIDE;
   virtual bool DeallocPGMPVideoDecoderChild(PGMPVideoDecoderChild* aActor) MOZ_OVERRIDE;
@@ -80,26 +82,27 @@ private:
   virtual bool DeallocPGMPStorageChild(PGMPStorageChild* aActor) MOZ_OVERRIDE;
 
   virtual bool RecvCrashPluginNow() MOZ_OVERRIDE;
   virtual bool RecvBeginAsyncShutdown() MOZ_OVERRIDE;
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
   virtual void ProcessingError(Result aWhat) MOZ_OVERRIDE;
 
+  GMPErr GetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI);
+
   GMPAsyncShutdown* mAsyncShutdown;
   nsRefPtr<GMPTimerChild> mTimerChild;
   nsRefPtr<GMPStorageChild> mStorage;
 
-  PRLibrary* mLib;
-  GMPGetAPIFunc mGetAPIFunc;
   MessageLoop* mGMPMessageLoop;
   std::string mPluginPath;
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   nsCString mPluginBinaryPath;
 #endif
   std::string mNodeId;
+  GMPLoader* mGMPLoader;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMPChild_h_