Bug 1088488 - Add GMPLoader interface to encapsulate loading GMPs, pass that to XRE_InitChildProcess. r=jesup,r=bsmedberg,r=glandium
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,