summaryrefslogtreecommitdiffstats
path: root/dom/media/gmp/GMPChild.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /dom/media/gmp/GMPChild.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/media/gmp/GMPChild.cpp')
-rw-r--r--dom/media/gmp/GMPChild.cpp646
1 files changed, 646 insertions, 0 deletions
diff --git a/dom/media/gmp/GMPChild.cpp b/dom/media/gmp/GMPChild.cpp
new file mode 100644
index 000000000..953dae3c6
--- /dev/null
+++ b/dom/media/gmp/GMPChild.cpp
@@ -0,0 +1,646 @@
+/* -*- 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"
+#include "nsIFile.h"
+#include "nsXULAppAPI.h"
+#include "gmp-video-decode.h"
+#include "gmp-video-encode.h"
+#include "GMPPlatform.h"
+#include "mozilla/dom/CrashReporterChild.h"
+#include "mozilla/ipc/ProcessChild.h"
+#include "GMPUtils.h"
+#include "prio.h"
+#include "base/task.h"
+#include "widevine-adapter/WidevineAdapter.h"
+
+using namespace mozilla::ipc;
+using mozilla::dom::CrashReporterChild;
+
+static const int MAX_VOUCHER_LENGTH = 500000;
+
+#ifdef XP_WIN
+#include <stdlib.h> // for _exit()
+#else
+#include <unistd.h> // for _exit()
+#endif
+
+#if defined(MOZ_GMP_SANDBOX)
+#if defined(XP_MACOSX)
+#include "mozilla/Sandbox.h"
+#endif
+#endif
+
+namespace mozilla {
+
+#undef LOG
+#undef LOGD
+
+extern LogModule* GetGMPLog();
+#define LOG(level, x, ...) MOZ_LOG(GetGMPLog(), (level), (x, ##__VA_ARGS__))
+#define LOGD(x, ...) LOG(mozilla::LogLevel::Debug, "GMPChild[pid=%d] " x, (int)base::GetCurrentProcId(), ##__VA_ARGS__)
+
+namespace gmp {
+
+GMPChild::GMPChild()
+ : mAsyncShutdown(nullptr)
+ , mGMPMessageLoop(MessageLoop::current())
+ , mGMPLoader(nullptr)
+{
+ LOGD("GMPChild ctor");
+ nsDebugImpl::SetMultiprocessMode("GMP");
+}
+
+GMPChild::~GMPChild()
+{
+ LOGD("GMPChild dtor");
+}
+
+static bool
+GetFileBase(const nsAString& aPluginPath,
+ nsCOMPtr<nsIFile>& aLibDirectory,
+ nsCOMPtr<nsIFile>& aFileBase,
+ nsAutoString& aBaseName)
+{
+ nsresult rv = NS_NewLocalFile(aPluginPath,
+ true, getter_AddRefs(aFileBase));
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ if (NS_FAILED(aFileBase->Clone(getter_AddRefs(aLibDirectory)))) {
+ return false;
+ }
+
+ nsCOMPtr<nsIFile> parent;
+ rv = aFileBase->GetParent(getter_AddRefs(parent));
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ nsAutoString parentLeafName;
+ rv = parent->GetLeafName(parentLeafName);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ aBaseName = Substring(parentLeafName,
+ 4,
+ parentLeafName.Length() - 1);
+ return true;
+}
+
+static bool
+GetFileBase(const nsAString& aPluginPath,
+ nsCOMPtr<nsIFile>& aFileBase,
+ nsAutoString& aBaseName)
+{
+ nsCOMPtr<nsIFile> unusedLibDir;
+ return GetFileBase(aPluginPath, unusedLibDir, aFileBase, aBaseName);
+}
+
+static bool
+GetPluginFile(const nsAString& aPluginPath,
+ nsCOMPtr<nsIFile>& aLibDirectory,
+ nsCOMPtr<nsIFile>& aLibFile)
+{
+ nsAutoString baseName;
+ GetFileBase(aPluginPath, aLibDirectory, aLibFile, baseName);
+
+#if defined(XP_MACOSX)
+ nsAutoString binaryName = NS_LITERAL_STRING("lib") + baseName + NS_LITERAL_STRING(".dylib");
+#elif defined(OS_POSIX)
+ nsAutoString binaryName = NS_LITERAL_STRING("lib") + baseName + NS_LITERAL_STRING(".so");
+#elif defined(XP_WIN)
+ nsAutoString binaryName = baseName + NS_LITERAL_STRING(".dll");
+#else
+#error not defined
+#endif
+ aLibFile->AppendRelativePath(binaryName);
+ return true;
+}
+
+#if !defined(XP_MACOSX) || !defined(MOZ_GMP_SANDBOX)
+static bool
+GetPluginFile(const nsAString& aPluginPath,
+ nsCOMPtr<nsIFile>& aLibFile)
+{
+ nsCOMPtr<nsIFile> unusedlibDir;
+ return GetPluginFile(aPluginPath, unusedlibDir, aLibFile);
+}
+#endif
+
+#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
+static nsCString
+GetNativeTarget(nsIFile* aFile)
+{
+ bool isLink;
+ nsCString path;
+ aFile->IsSymlink(&isLink);
+ if (isLink) {
+ aFile->GetNativeTarget(path);
+ } else {
+ aFile->GetNativePath(path);
+ }
+ return path;
+}
+
+static bool
+GetPluginPaths(const nsAString& aPluginPath,
+ nsCString &aPluginDirectoryPath,
+ nsCString &aPluginFilePath)
+{
+ nsCOMPtr<nsIFile> libDirectory, libFile;
+ if (!GetPluginFile(aPluginPath, libDirectory, libFile)) {
+ return false;
+ }
+
+ // Mac sandbox rules expect paths to actual files and directories -- not
+ // soft links.
+ libDirectory->Normalize();
+ aPluginDirectoryPath = GetNativeTarget(libDirectory);
+
+ libFile->Normalize();
+ aPluginFilePath = GetNativeTarget(libFile);
+
+ return true;
+}
+
+static bool
+GetAppPaths(nsCString &aAppPath, nsCString &aAppBinaryPath)
+{
+ nsAutoCString appPath;
+ nsAutoCString appBinaryPath(
+ (CommandLine::ForCurrentProcess()->argv()[0]).c_str());
+
+ nsAutoCString::const_iterator start, end;
+ appBinaryPath.BeginReading(start);
+ appBinaryPath.EndReading(end);
+ if (RFindInReadable(NS_LITERAL_CSTRING(".app/Contents/MacOS/"), start, end)) {
+ end = start;
+ ++end; ++end; ++end; ++end;
+ appBinaryPath.BeginReading(start);
+ appPath.Assign(Substring(start, end));
+ } else {
+ return false;
+ }
+
+ nsCOMPtr<nsIFile> app, appBinary;
+ nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(appPath),
+ true, getter_AddRefs(app));
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+ rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(appBinaryPath),
+ true, getter_AddRefs(appBinary));
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ // Mac sandbox rules expect paths to actual files and directories -- not
+ // soft links.
+ aAppPath = GetNativeTarget(app);
+ appBinaryPath = GetNativeTarget(appBinary);
+
+ return true;
+}
+
+bool
+GMPChild::SetMacSandboxInfo(MacSandboxPluginType aPluginType)
+{
+ if (!mGMPLoader) {
+ return false;
+ }
+ nsAutoCString pluginDirectoryPath, pluginFilePath;
+ if (!GetPluginPaths(mPluginPath, pluginDirectoryPath, pluginFilePath)) {
+ return false;
+ }
+ nsAutoCString appPath, appBinaryPath;
+ if (!GetAppPaths(appPath, appBinaryPath)) {
+ return false;
+ }
+
+ MacSandboxInfo info;
+ info.type = MacSandboxType_Plugin;
+ info.pluginInfo.type = aPluginType;
+ info.pluginInfo.pluginPath.assign(pluginDirectoryPath.get());
+ info.pluginInfo.pluginBinaryPath.assign(pluginFilePath.get());
+ info.appPath.assign(appPath.get());
+ info.appBinaryPath.assign(appBinaryPath.get());
+
+ mGMPLoader->SetSandboxInfo(&info);
+ return true;
+}
+#endif // XP_MACOSX && MOZ_GMP_SANDBOX
+
+bool
+GMPChild::Init(const nsAString& aPluginPath,
+ const nsAString& aVoucherPath,
+ base::ProcessId aParentPid,
+ MessageLoop* aIOLoop,
+ IPC::Channel* aChannel)
+{
+ LOGD("%s pluginPath=%s", __FUNCTION__, NS_ConvertUTF16toUTF8(aPluginPath).get());
+
+ if (NS_WARN_IF(!Open(aChannel, aParentPid, aIOLoop))) {
+ return false;
+ }
+
+#ifdef MOZ_CRASHREPORTER
+ SendPCrashReporterConstructor(CrashReporter::CurrentThreadId());
+#endif
+
+ mPluginPath = aPluginPath;
+ mSandboxVoucherPath = aVoucherPath;
+
+ return true;
+}
+
+bool
+GMPChild::RecvSetNodeId(const nsCString& aNodeId)
+{
+ LOGD("%s nodeId=%s", __FUNCTION__, aNodeId.Data());
+
+ // 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 = aNodeId;
+ return true;
+}
+
+GMPErr
+GMPChild::GetAPI(const char* aAPIName,
+ void* aHostAPI,
+ void** aPluginAPI,
+ uint32_t aDecryptorId)
+{
+ if (!mGMPLoader) {
+ return GMPGenericErr;
+ }
+ return mGMPLoader->GetAPI(aAPIName, aHostAPI, aPluginAPI, aDecryptorId);
+}
+
+bool
+GMPChild::RecvPreloadLibs(const nsCString& aLibs)
+{
+#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
+ // Items in this must be lowercase!
+ static const char* whitelist[] = {
+ "d3d9.dll", // Create an `IDirect3D9` to get adapter information
+ "dxva2.dll", // Get monitor information
+ "evr.dll", // MFGetStrideForBitmapInfoHeader
+ "mfh264dec.dll", // H.264 decoder (on Windows Vista)
+ "mfheaacdec.dll", // AAC decoder (on Windows Vista)
+ "mfplat.dll", // MFCreateSample, MFCreateAlignedMemoryBuffer, MFCreateMediaType
+ "msauddecmft.dll", // AAC decoder (on Windows 8)
+ "msmpeg2adec.dll", // AAC decoder (on Windows 7)
+ "msmpeg2vdec.dll", // H.264 decoder
+ };
+
+ nsTArray<nsCString> libs;
+ SplitAt(", ", aLibs, libs);
+ for (nsCString lib : libs) {
+ ToLowerCase(lib);
+ for (const char* whiteListedLib : whitelist) {
+ if (lib.EqualsASCII(whiteListedLib)) {
+ LoadLibraryA(lib.get());
+ break;
+ }
+ }
+ }
+#endif
+ return true;
+}
+
+bool
+GMPChild::GetUTF8LibPath(nsACString& aOutLibPath)
+{
+#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
+ 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(mPluginPath, libFile)) {
+ return false;
+ }
+
+ if (!FileExists(libFile)) {
+ NS_WARNING("Can't find GMP library file!");
+ return false;
+ }
+
+ nsAutoString path;
+ libFile->GetPath(path);
+ aOutLibPath = NS_ConvertUTF16toUTF8(path);
+
+ return true;
+#endif
+}
+
+bool
+GMPChild::AnswerStartPlugin(const nsString& aAdapter)
+{
+ LOGD("%s", __FUNCTION__);
+
+ if (!PreLoadPluginVoucher()) {
+ NS_WARNING("Plugin voucher failed to load!");
+ return false;
+ }
+ PreLoadSandboxVoucher();
+
+ nsCString libPath;
+ if (!GetUTF8LibPath(libPath)) {
+ return false;
+ }
+
+ auto platformAPI = new GMPPlatformAPI();
+ InitPlatformAPI(*platformAPI, this);
+
+ mGMPLoader = GMPProcessChild::GetGMPLoader();
+ if (!mGMPLoader) {
+ NS_WARNING("Failed to get GMPLoader");
+ delete platformAPI;
+ return false;
+ }
+
+ bool isWidevine = aAdapter.EqualsLiteral("widevine");
+#if defined(MOZ_GMP_SANDBOX) && defined(XP_MACOSX)
+ MacSandboxPluginType pluginType = MacSandboxPluginType_GMPlugin_Default;
+ if (isWidevine) {
+ pluginType = MacSandboxPluginType_GMPlugin_EME_Widevine;
+ }
+ if (!SetMacSandboxInfo(pluginType)) {
+ NS_WARNING("Failed to set Mac GMP sandbox info");
+ delete platformAPI;
+ return false;
+ }
+#endif
+
+ GMPAdapter* adapter = (isWidevine) ? new WidevineAdapter() : nullptr;
+ if (!mGMPLoader->Load(libPath.get(),
+ libPath.Length(),
+ mNodeId.BeginWriting(),
+ mNodeId.Length(),
+ platformAPI,
+ adapter)) {
+ NS_WARNING("Failed to load GMP");
+ delete platformAPI;
+ return false;
+ }
+
+ void* sh = nullptr;
+ GMPAsyncShutdownHost* host = static_cast<GMPAsyncShutdownHost*>(this);
+ GMPErr err = GetAPI(GMP_API_ASYNC_SHUTDOWN, host, &sh);
+ if (err == GMPNoErr && sh) {
+ mAsyncShutdown = reinterpret_cast<GMPAsyncShutdown*>(sh);
+ SendAsyncShutdownRequired();
+ }
+
+ return true;
+}
+
+MessageLoop*
+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!");
+ ProcessChild::QuickExit();
+ }
+
+ XRE_ShutdownChildProcess();
+}
+
+void
+GMPChild::ProcessingError(Result aCode, const char* aReason)
+{
+ switch (aCode) {
+ case MsgDropped:
+ _exit(0); // Don't trigger a crash report.
+ case MsgNotKnown:
+ MOZ_CRASH("aborting because of MsgNotKnown");
+ case MsgNotAllowed:
+ MOZ_CRASH("aborting because of MsgNotAllowed");
+ case MsgPayloadError:
+ MOZ_CRASH("aborting because of MsgPayloadError");
+ case MsgProcessingError:
+ MOZ_CRASH("aborting because of MsgProcessingError");
+ case MsgRouteError:
+ MOZ_CRASH("aborting because of MsgRouteError");
+ case MsgValueError:
+ MOZ_CRASH("aborting because of MsgValueError");
+ default:
+ MOZ_CRASH("not reached");
+ }
+}
+
+mozilla::dom::PCrashReporterChild*
+GMPChild::AllocPCrashReporterChild(const NativeThreadId& aThread)
+{
+ return new CrashReporterChild();
+}
+
+bool
+GMPChild::DeallocPCrashReporterChild(PCrashReporterChild* aCrashReporter)
+{
+ delete aCrashReporter;
+ return true;
+}
+
+PGMPTimerChild*
+GMPChild::AllocPGMPTimerChild()
+{
+ return new GMPTimerChild(this);
+}
+
+bool
+GMPChild::DeallocPGMPTimerChild(PGMPTimerChild* aActor)
+{
+ MOZ_ASSERT(mTimerChild == static_cast<GMPTimerChild*>(aActor));
+ mTimerChild = nullptr;
+ return true;
+}
+
+GMPTimerChild*
+GMPChild::GetGMPTimers()
+{
+ if (!mTimerChild) {
+ PGMPTimerChild* sc = SendPGMPTimerConstructor();
+ if (!sc) {
+ return nullptr;
+ }
+ mTimerChild = static_cast<GMPTimerChild*>(sc);
+ }
+ return mTimerChild;
+}
+
+PGMPStorageChild*
+GMPChild::AllocPGMPStorageChild()
+{
+ return new GMPStorageChild(this);
+}
+
+bool
+GMPChild::DeallocPGMPStorageChild(PGMPStorageChild* aActor)
+{
+ mStorage = nullptr;
+ return true;
+}
+
+GMPStorageChild*
+GMPChild::GetGMPStorage()
+{
+ if (!mStorage) {
+ PGMPStorageChild* sc = SendPGMPStorageConstructor();
+ if (!sc) {
+ return nullptr;
+ }
+ mStorage = static_cast<GMPStorageChild*>(sc);
+ }
+ return mStorage;
+}
+
+bool
+GMPChild::RecvCrashPluginNow()
+{
+ MOZ_CRASH();
+ return true;
+}
+
+bool
+GMPChild::RecvBeginAsyncShutdown()
+{
+ LOGD("%s AsyncShutdown=%d", __FUNCTION__, mAsyncShutdown!=nullptr);
+
+ MOZ_ASSERT(mGMPMessageLoop == MessageLoop::current());
+ 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());
+ mAsyncShutdown = nullptr;
+ SendAsyncShutdownComplete();
+}
+
+static void
+GetPluginVoucherFile(const nsAString& aPluginPath,
+ nsCOMPtr<nsIFile>& aOutVoucherFile)
+{
+ nsAutoString baseName;
+ GetFileBase(aPluginPath, aOutVoucherFile, baseName);
+ nsAutoString infoFileName = baseName + NS_LITERAL_STRING(".voucher");
+ aOutVoucherFile->AppendRelativePath(infoFileName);
+}
+
+bool
+GMPChild::PreLoadPluginVoucher()
+{
+ nsCOMPtr<nsIFile> voucherFile;
+ GetPluginVoucherFile(mPluginPath, voucherFile);
+ if (!FileExists(voucherFile)) {
+ // Assume missing file is not fatal; that would break OpenH264.
+ return true;
+ }
+ return ReadIntoArray(voucherFile, mPluginVoucher, MAX_VOUCHER_LENGTH);
+}
+
+void
+GMPChild::PreLoadSandboxVoucher()
+{
+ nsCOMPtr<nsIFile> f;
+ nsresult rv = NS_NewLocalFile(mSandboxVoucherPath, true, getter_AddRefs(f));
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Can't create nsIFile for sandbox voucher");
+ return;
+ }
+ if (!FileExists(f)) {
+ // Assume missing file is not fatal; that would break OpenH264.
+ return;
+ }
+
+ if (!ReadIntoArray(f, mSandboxVoucher, MAX_VOUCHER_LENGTH)) {
+ NS_WARNING("Failed to read sandbox voucher");
+ }
+}
+
+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();
+ RefPtr<DeleteTask<GMPContentChild>> task =
+ new DeleteTask<GMPContentChild>(toDestroy.release());
+ MessageLoop::current()->PostTask(task.forget());
+ mGMPContentChildren.RemoveElementAt(i - 1);
+ break;
+ }
+ }
+}
+
+} // namespace gmp
+} // namespace mozilla
+
+#undef LOG
+#undef LOGD