Bug 1088488 - Add GMPLoader interface to encapsulate loading GMPs, pass that to XRE_InitChildProcess. r=jesup,r=bsmedberg,r=glandium
authorChris Pearce <cpearce@mozilla.com>
Fri, 14 Nov 2014 21:26:24 +1300
changeset 215796 591742c4c713288981400ff91e18b373cf17b3a6
parent 215722 ea818fdbd81c5a06b063136f8ce7929d397e5e7a
child 215797 8078ba66f37cd1c9b6b6a63e358ad5bdc0db32bd
push id27827
push userryanvm@gmail.com
push dateFri, 14 Nov 2014 22:48:07 +0000
treeherdermozilla-central@acbd7b68fa8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup, bsmedberg, glandium
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 - Add GMPLoader interface to encapsulate loading GMPs, pass that to XRE_InitChildProcess. r=jesup,r=bsmedberg,r=glandium
dom/media/gmp/GMPLoader.cpp
dom/media/gmp/GMPLoader.h
dom/media/gmp/GMPProcessChild.cpp
dom/media/gmp/GMPProcessChild.h
dom/media/gmp/moz.build
ipc/app/moz.build
ipc/contentproc/plugin-container.cpp
mozglue/android/APKOpen.cpp
toolkit/xre/nsEmbedFunctions.cpp
xpcom/build/nsXULAppAPI.h
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/GMPLoader.cpp
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GMPLoader.h"
+#include <stdio.h>
+#include "mozilla/Attributes.h"
+#include "mozilla/NullPtr.h"
+#include "gmp-entrypoints.h"
+#include "prlink.h"
+
+#include <string>
+
+namespace mozilla {
+namespace gmp {
+
+class GMPLoaderImpl : public GMPLoader {
+public:
+  explicit GMPLoaderImpl(SandboxStarter* aStarter)
+    : mSandboxStarter(aStarter)
+  {}
+  virtual ~GMPLoaderImpl() {}
+
+  virtual bool Load(const char* aLibPath,
+                    uint32_t aLibPathLen,
+                    char* aOriginSalt,
+                    uint32_t aOriginSaltLen,
+                    const GMPPlatformAPI* aPlatformAPI) MOZ_OVERRIDE;
+
+  virtual GMPErr GetAPI(const char* aAPIName,
+                        void* aHostAPI,
+                        void** aPluginAPI) MOZ_OVERRIDE;
+
+  virtual void Shutdown() MOZ_OVERRIDE;
+
+#ifdef SANDBOX_NOT_STATICALLY_LINKED_INTO_PLUGIN_CONTAINER
+  virtual void SetStartSandboxStarter(SandboxStarter* aStarter) MOZ_OVERRIDE {
+    mSandboxStarter = aStarter;
+  }
+#endif
+
+private:
+  PRLibrary* mLib;
+  GMPGetAPIFunc mGetAPIFunc;
+  SandboxStarter* mSandboxStarter;
+};
+
+GMPLoader* CreateGMPLoader(SandboxStarter* aStarter) {
+  return static_cast<GMPLoader*>(new GMPLoaderImpl(aStarter));
+}
+
+bool
+GMPLoaderImpl::Load(const char* aLibPath,
+                    uint32_t aLibPathLen,
+                    char* aOriginSalt,
+                    uint32_t aOriginSaltLen,
+                    const GMPPlatformAPI* aPlatformAPI)
+{
+  std::string nodeId(aOriginSalt, aOriginSalt + aOriginSaltLen);
+
+  // TODO (subsequent patch): Hash node id with device id, send to GMP.
+
+#if defined(MOZ_GMP_SANDBOX)
+  // Start the sandbox now that we've generated the device bound node id.
+  // This must happen after the node id is bound to the device id, as
+  // generating the device id requires privileges.
+  if (mSandboxStarter) {
+    mSandboxStarter->Start();
+  }
+#endif
+
+  // Load the GMP.
+  PRLibSpec libSpec;
+  libSpec.value.pathname = aLibPath;
+  libSpec.type = PR_LibSpec_Pathname;
+  mLib = PR_LoadLibraryWithFlags(libSpec, 0);
+  if (!mLib) {
+    return false;
+  }
+
+  GMPInitFunc initFunc = reinterpret_cast<GMPInitFunc>(PR_FindFunctionSymbol(mLib, "GMPInit"));
+  if (!initFunc) {
+    return false;
+  }
+
+  if (initFunc(aPlatformAPI) != GMPNoErr) {
+    return false;
+  }
+
+  mGetAPIFunc = reinterpret_cast<GMPGetAPIFunc>(PR_FindFunctionSymbol(mLib, "GMPGetAPI"));
+  if (!mGetAPIFunc) {
+    return false;
+  }
+
+  return true;
+}
+
+GMPErr
+GMPLoaderImpl::GetAPI(const char* aAPIName,
+                      void* aHostAPI,
+                      void** aPluginAPI)
+{
+  return mGetAPIFunc ? mGetAPIFunc(aAPIName, aHostAPI, aPluginAPI)
+                     : GMPGenericErr;
+}
+
+void
+GMPLoaderImpl::Shutdown()
+{
+  if (mLib) {
+    GMPShutdownFunc shutdownFunc = reinterpret_cast<GMPShutdownFunc>(PR_FindFunctionSymbol(mLib, "GMPShutdown"));
+    if (shutdownFunc) {
+      shutdownFunc();
+    }
+    PR_UnloadLibrary(mLib);
+    mLib = nullptr;
+  }
+}
+
+} // namespace gmp
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/GMPLoader.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GMP_LOADER_H__
+#define GMP_LOADER_H__
+
+#include <stdint.h>
+#include "gmp-entrypoints.h"
+
+namespace mozilla {
+namespace gmp {
+
+class SandboxStarter {
+public:
+  virtual ~SandboxStarter() {}
+  virtual void Start() = 0;
+};
+
+#if (defined(XP_LINUX) || defined(XP_MACOSX))
+#define SANDBOX_NOT_STATICALLY_LINKED_INTO_PLUGIN_CONTAINER 1
+#endif
+
+// 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).
+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.
+  virtual bool Load(const char* aLibPath,
+                    uint32_t aLibPathLen,
+                    char* aOriginSalt,
+                    uint32_t aOriginSaltLen,
+                    const GMPPlatformAPI* aPlatformAPI) = 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;
+
+#ifdef SANDBOX_NOT_STATICALLY_LINKED_INTO_PLUGIN_CONTAINER
+  // Encapsulates starting the sandbox on Linux and MacOSX.
+  // TODO: Remove this, and put sandbox in plugin-container on all platforms.
+  virtual void SetStartSandboxStarter(SandboxStarter* aStarter) = 0;
+#endif
+};
+
+// On Desktop, this function resides in plugin-container.
+// On Mobile, this function resides in XUL.
+GMPLoader* CreateGMPLoader(SandboxStarter* aStarter);
+
+} // namespace gmp
+} // namespace mozilla
+
+#endif // GMP_LOADER_H__
--- a/dom/media/gmp/GMPProcessChild.cpp
+++ b/dom/media/gmp/GMPProcessChild.cpp
@@ -54,10 +54,26 @@ GMPProcessChild::Init()
 }
 
 void
 GMPProcessChild::CleanUp()
 {
   BackgroundHangMonitor::Shutdown();
 }
 
+GMPLoader* GMPProcessChild::mLoader = nullptr;
+
+/* static */
+void
+GMPProcessChild::SetGMPLoader(GMPLoader* aLoader)
+{
+  mLoader = aLoader;
+}
+
+/* static */
+GMPLoader*
+GMPProcessChild::GetGMPLoader()
+{
+  return mLoader;
+}
+
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/gmp/GMPProcessChild.h
+++ b/dom/media/gmp/GMPProcessChild.h
@@ -7,29 +7,37 @@
 #define GMPProcessChild_h_
 
 #include "mozilla/ipc/ProcessChild.h"
 #include "GMPChild.h"
 
 namespace mozilla {
 namespace gmp {
 
+class GMPLoader;
+
 class GMPProcessChild MOZ_FINAL : public mozilla::ipc::ProcessChild {
 protected:
   typedef mozilla::ipc::ProcessChild ProcessChild;
 
 public:
   explicit GMPProcessChild(ProcessHandle parentHandle);
   ~GMPProcessChild();
 
   virtual bool Init() MOZ_OVERRIDE;
   virtual void CleanUp() MOZ_OVERRIDE;
 
+  // Set/get the GMPLoader singleton for this child process.
+  // Note: The GMPLoader is not deleted by this object, the caller of
+  // SetGMPLoader() must manage the GMPLoader's lifecycle.
+  static void SetGMPLoader(GMPLoader* aHost);
+  static GMPLoader* GetGMPLoader();
+
 private:
   GMPChild mPlugin;
-
+  static GMPLoader* mLoader;
   DISALLOW_COPY_AND_ASSIGN(GMPProcessChild);
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMPProcessChild_h_
--- a/dom/media/gmp/moz.build
+++ b/dom/media/gmp/moz.build
@@ -34,16 +34,17 @@ EXPORTS += [
     'GMPAudioDecoderProxy.h',
     'GMPAudioHost.h',
     'GMPCallbackBase.h',
     'GMPChild.h',
     'GMPDecryptorChild.h',
     'GMPDecryptorParent.h',
     'GMPDecryptorProxy.h',
     'GMPEncryptedBufferDataImpl.h',
+    'GMPLoader.h',
     'GMPMessageUtils.h',
     'GMPParent.h',
     'GMPPlatform.h',
     'GMPProcessChild.h',
     'GMPProcessParent.h',
     'GMPService.h',
     'GMPSharedMemManager.h',
     'GMPStorageChild.h',
@@ -57,16 +58,23 @@ EXPORTS += [
     'GMPVideoEncoderChild.h',
     'GMPVideoEncoderParent.h',
     'GMPVideoEncoderProxy.h',
     'GMPVideoHost.h',
     'GMPVideoi420FrameImpl.h',
     'GMPVideoPlaneImpl.h',
 ]
 
+# We link GMPLoader into xul on B2G/Fennec as its code does not need to be
+# covered by a DRM vendor's voucher.
+if CONFIG['OS_TARGET'] == 'Android':
+    SOURCES += [
+      'GMPLoader.cpp',
+    ]
+
 UNIFIED_SOURCES += [
     'GMPAudioDecoderChild.cpp',
     'GMPAudioDecoderParent.cpp',
     'GMPAudioHost.cpp',
     'GMPChild.cpp',
     'GMPDecryptorChild.cpp',
     'GMPDecryptorParent.cpp',
     'GMPEncryptedBufferDataImpl.cpp',
--- a/ipc/app/moz.build
+++ b/ipc/app/moz.build
@@ -28,16 +28,23 @@ else:
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 LOCAL_INCLUDES += [
     '/toolkit/xre',
     '/xpcom/base',
 ]
 
+# We link GMPLoader into plugin-container on desktop so that its code is
+# covered by the desktop DRM vendor's voucher.
+if CONFIG['OS_TARGET'] != 'Android':
+    SOURCES += [
+        '../../dom/media/gmp/GMPLoader.cpp',
+    ]
+
 if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_ARCH'] == 'WINNT':
     # For sandbox includes and the include dependencies those have
     LOCAL_INCLUDES += [
         '/security',
         '/security/sandbox',
         '/security/sandbox/chromium',
     ]
     USE_LIBS += [
--- a/ipc/contentproc/plugin-container.cpp
+++ b/ipc/contentproc/plugin-container.cpp
@@ -1,31 +1,34 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: sw=4 ts=4 et :
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsXPCOM.h"
 #include "nsXULAppAPI.h"
+#include "nsAutoPtr.h"
 
 // FIXME/cjones testing
 #if !defined(OS_WIN)
 #include <unistd.h>
 #endif
 
 #ifdef XP_WIN
 #include <windows.h>
 // we want a wmain entry point
 // but we don't want its DLL load protection, because we'll handle it here
 #define XRE_DONT_PROTECT_DLL_LOAD
 #include "nsWindowsWMain.cpp"
 #include "nsSetDllDirectory.h"
 #endif
 
+#include "GMPLoader.h"
+
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
 #include "sandbox/chromium/base/basictypes.h"
 #include "sandbox/win/src/sandbox.h"
 #include "sandbox/win/src/sandbox_factory.h"
 #include "mozilla/sandboxTarget.h"
 
 #if defined(MOZ_CONTENT_SANDBOX)
 #include "mozilla/warnonlysandbox/wosCallbacks.h"
@@ -77,18 +80,39 @@ static bool gIsSandboxEnabled = false;
 void StartSandboxCallback()
 {
     if (gIsSandboxEnabled) {
         sandbox::TargetServices* target_service =
             sandbox::SandboxFactory::GetTargetServices();
         target_service->LowerToken();
     }
 }
+
+class WinSandboxStarter : public mozilla::gmp::SandboxStarter {
+public:
+    virtual void Start() MOZ_OVERRIDE {
+        StartSandboxCallback();
+    }
+};
 #endif
 
+mozilla::gmp::SandboxStarter*
+MakeSandboxStarter()
+{
+    // Note: Linux and MacOSX create their SandboxStarters inside xul code,
+    // they need to change to statically link their sandbox code into
+    // plugin-container. Once they do that, we can create SandboxStarters for
+    // them here.
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+    return new WinSandboxStarter();
+#else
+    return nullptr;
+#endif
+}
+
 int
 content_process_main(int argc, char* argv[])
 {
     // Check for the absolute minimum number of args we need to move
     // forward here. We expect the last arg to be the child process type.
     if (argc < 1) {
       return 3;
     }
@@ -149,14 +173,22 @@ content_process_main(int argc, char* arg
         mozilla::SandboxTarget::Instance()->SetStartSandboxCallback(StartSandboxCallback);
 
 #if defined(MOZ_CONTENT_SANDBOX)
         mozilla::warnonlysandbox::PrepareForInit();
 #endif
     }
 #endif
 #endif
-
-    nsresult rv = XRE_InitChildProcess(argc, argv);
+    nsAutoPtr<mozilla::gmp::GMPLoader> loader;
+#if !defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_WIDGET_GONK)
+    // On desktop, the GMPLoader lives in plugin-container, so that its
+    // code can be covered by an EME/GMP vendor's voucher.
+    nsAutoPtr<mozilla::gmp::SandboxStarter> starter(MakeSandboxStarter());
+    if (XRE_GetProcessType() == GeckoProcessType_GMPlugin) {
+        loader = mozilla::gmp::CreateGMPLoader(starter);
+    }
+#endif
+    nsresult rv = XRE_InitChildProcess(argc, argv, loader);
     NS_ENSURE_SUCCESS(rv, 1);
 
     return 0;
 }
--- a/mozglue/android/APKOpen.cpp
+++ b/mozglue/android/APKOpen.cpp
@@ -440,16 +440,16 @@ ChildProcessInit(int argc, char* argv[])
   }
   if (loadGeckoLibs(argv[i]) != SUCCESS) {
     return FAILURE;
   }
 
   void (*fXRE_SetProcessType)(char*);
   xul_dlsym("XRE_SetProcessType", &fXRE_SetProcessType);
 
-  mozglueresult (*fXRE_InitChildProcess)(int, char**);
+  mozglueresult (*fXRE_InitChildProcess)(int, char**, void*);
   xul_dlsym("XRE_InitChildProcess", &fXRE_InitChildProcess);
 
   fXRE_SetProcessType(argv[--argc]);
 
-  return fXRE_InitChildProcess(argc, argv);
+  return fXRE_InitChildProcess(argc, argv, nullptr);
 }
 
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -66,16 +66,17 @@
 #include "mozilla/dom/ContentProcess.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentChild.h"
 
 #include "mozilla/ipc/TestShellParent.h"
 #include "mozilla/ipc/XPCShellEnvironment.h"
 
 #include "GMPProcessChild.h"
+#include "GMPLoader.h"
 
 #include "GeckoProfiler.h"
 
 #if defined(MOZ_CONTENT_SANDBOX) && defined(XP_WIN)
 #define TARGET_SANDBOX_EXPORTS
 #include "mozilla/warnonlysandbox/wosCallbacks.h"
 #endif
 
@@ -99,16 +100,20 @@ using mozilla::ipc::IOThreadChild;
 using mozilla::ipc::ProcessChild;
 using mozilla::ipc::ScopedXREEmbed;
 
 using mozilla::plugins::PluginProcessChild;
 using mozilla::dom::ContentProcess;
 using mozilla::dom::ContentParent;
 using mozilla::dom::ContentChild;
 
+using mozilla::gmp::GMPLoader;
+using mozilla::gmp::CreateGMPLoader;
+using mozilla::gmp::GMPProcessChild;
+
 using mozilla::ipc::TestShellParent;
 using mozilla::ipc::TestShellCommandParent;
 using mozilla::ipc::XPCShellEnvironment;
 
 using mozilla::startup::sChildProcessType;
 
 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
 
@@ -283,22 +288,35 @@ SetTaskbarGroupId(const nsString& aId)
 
     if (hDLL)
         ::FreeLibrary(hDLL);
 }
 #endif
 
 nsresult
 XRE_InitChildProcess(int aArgc,
-                     char* aArgv[])
+                     char* aArgv[],
+                     GMPLoader* aGMPLoader)
 {
   NS_ENSURE_ARG_MIN(aArgc, 2);
   NS_ENSURE_ARG_POINTER(aArgv);
   NS_ENSURE_ARG_POINTER(aArgv[0]);
 
+#if !defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_WIDGET_GONK)
+  // On non-Fennec Gecko, the GMPLoader code resides in plugin-container,
+  // and we must forward it through to the GMP code here.
+  GMPProcessChild::SetGMPLoader(aGMPLoader);
+#else
+  // On Fennec, the GMPLoader's code resides inside XUL (because for the time
+  // being GMPLoader relies upon NSPR, which we can't use in plugin-container
+  // on Android), so we create it here inside XUL and pass it to the GMP code.
+  nsAutoPtr<GMPLoader> loader(CreateGMPLoader(nullptr));
+  GMPProcessChild::SetGMPLoader(loader);
+#endif
+
 #if defined(XP_WIN)
   // From the --attach-console support in nsNativeAppSupportWin.cpp, but
   // here we are a content child process, so we always attempt to attach
   // to the parent's (ie, the browser's) console.
   // Try to attach console to the parent process.
   // It will succeed when the parent process is a command line,
   // so that stdio will be displayed in it.
   if (AttachConsole(ATTACH_PARENT_PROCESS)) {
--- a/xpcom/build/nsXULAppAPI.h
+++ b/xpcom/build/nsXULAppAPI.h
@@ -388,19 +388,26 @@ XRE_API(bool,
         XRE_TakeMinidumpForChild, (uint32_t aChildPid, nsIFile** aDump,
                                    uint32_t* aSequence))
 
 // Used in child processes.
 XRE_API(bool,
         XRE_SetRemoteExceptionHandler, (const char* aPipe))
 #endif
 
+namespace mozilla {
+namespace gmp {
+class GMPLoader;
+} // namespace gmp
+} // namepsace mozilla
+
 XRE_API(nsresult,
         XRE_InitChildProcess, (int aArgc,
-                               char* aArgv[]))
+                               char* aArgv[],
+                               mozilla::gmp::GMPLoader* aGMPLoader))
 
 XRE_API(GeckoProcessType,
         XRE_GetProcessType, ())
 
 typedef void (*MainFunction)(void* aData);
 
 XRE_API(nsresult,
         XRE_InitParentProcess, (int aArgc,