Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "GMPChild.h"
7 : #include "GMPContentChild.h"
8 : #include "GMPProcessChild.h"
9 : #include "GMPLoader.h"
10 : #include "GMPVideoDecoderChild.h"
11 : #include "GMPVideoEncoderChild.h"
12 : #include "GMPDecryptorChild.h"
13 : #include "GMPVideoHost.h"
14 : #include "nsDebugImpl.h"
15 : #include "nsIFile.h"
16 : #include "nsXULAppAPI.h"
17 : #include "gmp-video-decode.h"
18 : #include "gmp-video-encode.h"
19 : #include "GMPPlatform.h"
20 : #include "mozilla/ipc/CrashReporterClient.h"
21 : #include "mozilla/ipc/ProcessChild.h"
22 : #include "GMPUtils.h"
23 : #include "prio.h"
24 : #include "base/task.h"
25 : #include "widevine-adapter/WidevineAdapter.h"
26 : #include "ChromiumCDMAdapter.h"
27 :
28 : using namespace mozilla::ipc;
29 :
30 : #ifdef XP_WIN
31 : #include <stdlib.h> // for _exit()
32 : #else
33 : #include <unistd.h> // for _exit()
34 : #endif
35 :
36 : #if defined(MOZ_GMP_SANDBOX)
37 : #if defined(XP_MACOSX)
38 : #include "mozilla/Sandbox.h"
39 : #endif
40 : #endif
41 :
42 : namespace mozilla {
43 :
44 : #undef LOG
45 : #undef LOGD
46 :
47 : extern LogModule* GetGMPLog();
48 : #define LOG(level, x, ...) MOZ_LOG(GetGMPLog(), (level), (x, ##__VA_ARGS__))
49 : #define LOGD(x, ...) LOG(mozilla::LogLevel::Debug, "GMPChild[pid=%d] " x, (int)base::GetCurrentProcId(), ##__VA_ARGS__)
50 :
51 : namespace gmp {
52 :
53 0 : GMPChild::GMPChild()
54 0 : : mGMPMessageLoop(MessageLoop::current())
55 0 : , mGMPLoader(nullptr)
56 : {
57 0 : LOGD("GMPChild ctor");
58 0 : nsDebugImpl::SetMultiprocessMode("GMP");
59 0 : }
60 :
61 0 : GMPChild::~GMPChild()
62 : {
63 0 : LOGD("GMPChild dtor");
64 0 : }
65 :
66 : static bool
67 0 : GetFileBase(const nsAString& aPluginPath,
68 : nsCOMPtr<nsIFile>& aLibDirectory,
69 : nsCOMPtr<nsIFile>& aFileBase,
70 : nsAutoString& aBaseName)
71 : {
72 0 : nsresult rv = NS_NewLocalFile(aPluginPath,
73 0 : true, getter_AddRefs(aFileBase));
74 0 : if (NS_FAILED(rv)) {
75 0 : return false;
76 : }
77 :
78 0 : if (NS_FAILED(aFileBase->Clone(getter_AddRefs(aLibDirectory)))) {
79 0 : return false;
80 : }
81 :
82 0 : nsCOMPtr<nsIFile> parent;
83 0 : rv = aFileBase->GetParent(getter_AddRefs(parent));
84 0 : if (NS_FAILED(rv)) {
85 0 : return false;
86 : }
87 :
88 0 : nsAutoString parentLeafName;
89 0 : rv = parent->GetLeafName(parentLeafName);
90 0 : if (NS_FAILED(rv)) {
91 0 : return false;
92 : }
93 :
94 0 : aBaseName = Substring(parentLeafName,
95 : 4,
96 0 : parentLeafName.Length() - 1);
97 0 : return true;
98 : }
99 :
100 : static bool
101 0 : GetPluginFile(const nsAString& aPluginPath,
102 : nsCOMPtr<nsIFile>& aLibDirectory,
103 : nsCOMPtr<nsIFile>& aLibFile)
104 : {
105 0 : nsAutoString baseName;
106 0 : GetFileBase(aPluginPath, aLibDirectory, aLibFile, baseName);
107 :
108 : #if defined(XP_MACOSX)
109 : nsAutoString binaryName = NS_LITERAL_STRING("lib") + baseName + NS_LITERAL_STRING(".dylib");
110 : #elif defined(OS_POSIX)
111 0 : nsAutoString binaryName = NS_LITERAL_STRING("lib") + baseName + NS_LITERAL_STRING(".so");
112 : #elif defined(XP_WIN)
113 : nsAutoString binaryName = baseName + NS_LITERAL_STRING(".dll");
114 : #else
115 : #error not defined
116 : #endif
117 0 : aLibFile->AppendRelativePath(binaryName);
118 0 : return true;
119 : }
120 :
121 : #if !defined(XP_MACOSX) || !defined(MOZ_GMP_SANDBOX)
122 : static bool
123 0 : GetPluginFile(const nsAString& aPluginPath,
124 : nsCOMPtr<nsIFile>& aLibFile)
125 : {
126 0 : nsCOMPtr<nsIFile> unusedlibDir;
127 0 : return GetPluginFile(aPluginPath, unusedlibDir, aLibFile);
128 : }
129 : #endif
130 :
131 : #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
132 : static nsCString
133 : GetNativeTarget(nsIFile* aFile)
134 : {
135 : bool isLink;
136 : nsCString path;
137 : aFile->IsSymlink(&isLink);
138 : if (isLink) {
139 : aFile->GetNativeTarget(path);
140 : } else {
141 : aFile->GetNativePath(path);
142 : }
143 : return path;
144 : }
145 :
146 : static bool
147 : GetPluginPaths(const nsAString& aPluginPath,
148 : nsCString &aPluginDirectoryPath,
149 : nsCString &aPluginFilePath)
150 : {
151 : nsCOMPtr<nsIFile> libDirectory, libFile;
152 : if (!GetPluginFile(aPluginPath, libDirectory, libFile)) {
153 : return false;
154 : }
155 :
156 : // Mac sandbox rules expect paths to actual files and directories -- not
157 : // soft links.
158 : libDirectory->Normalize();
159 : aPluginDirectoryPath = GetNativeTarget(libDirectory);
160 :
161 : libFile->Normalize();
162 : aPluginFilePath = GetNativeTarget(libFile);
163 :
164 : return true;
165 : }
166 :
167 : static bool
168 : GetAppPaths(nsCString &aAppPath, nsCString &aAppBinaryPath)
169 : {
170 : nsAutoCString appPath;
171 : nsAutoCString appBinaryPath(
172 : (CommandLine::ForCurrentProcess()->argv()[0]).c_str());
173 :
174 : nsAutoCString::const_iterator start, end;
175 : appBinaryPath.BeginReading(start);
176 : appBinaryPath.EndReading(end);
177 : if (RFindInReadable(NS_LITERAL_CSTRING(".app/Contents/MacOS/"), start, end)) {
178 : end = start;
179 : ++end; ++end; ++end; ++end;
180 : appBinaryPath.BeginReading(start);
181 : appPath.Assign(Substring(start, end));
182 : } else {
183 : return false;
184 : }
185 :
186 : nsCOMPtr<nsIFile> app, appBinary;
187 : nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(appPath),
188 : true, getter_AddRefs(app));
189 : if (NS_FAILED(rv)) {
190 : return false;
191 : }
192 : rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(appBinaryPath),
193 : true, getter_AddRefs(appBinary));
194 : if (NS_FAILED(rv)) {
195 : return false;
196 : }
197 :
198 : // Mac sandbox rules expect paths to actual files and directories -- not
199 : // soft links.
200 : aAppPath = GetNativeTarget(app);
201 : appBinaryPath = GetNativeTarget(appBinary);
202 :
203 : return true;
204 : }
205 :
206 : bool
207 : GMPChild::SetMacSandboxInfo(MacSandboxPluginType aPluginType)
208 : {
209 : if (!mGMPLoader) {
210 : return false;
211 : }
212 : nsAutoCString pluginDirectoryPath, pluginFilePath;
213 : if (!GetPluginPaths(mPluginPath, pluginDirectoryPath, pluginFilePath)) {
214 : return false;
215 : }
216 : nsAutoCString appPath, appBinaryPath;
217 : if (!GetAppPaths(appPath, appBinaryPath)) {
218 : return false;
219 : }
220 :
221 : MacSandboxInfo info;
222 : info.type = MacSandboxType_Plugin;
223 : info.shouldLog = Preferences::GetBool("security.sandbox.logging.enabled") ||
224 : PR_GetEnv("MOZ_SANDBOX_LOGGING");
225 : info.pluginInfo.type = aPluginType;
226 : info.pluginInfo.pluginPath.assign(pluginDirectoryPath.get());
227 : info.pluginInfo.pluginBinaryPath.assign(pluginFilePath.get());
228 : info.appPath.assign(appPath.get());
229 : info.appBinaryPath.assign(appBinaryPath.get());
230 :
231 : mGMPLoader->SetSandboxInfo(&info);
232 : return true;
233 : }
234 : #endif // XP_MACOSX && MOZ_GMP_SANDBOX
235 :
236 : bool
237 0 : GMPChild::Init(const nsAString& aPluginPath,
238 : base::ProcessId aParentPid,
239 : MessageLoop* aIOLoop,
240 : IPC::Channel* aChannel)
241 : {
242 0 : LOGD("%s pluginPath=%s", __FUNCTION__, NS_ConvertUTF16toUTF8(aPluginPath).get());
243 :
244 0 : if (NS_WARN_IF(!Open(aChannel, aParentPid, aIOLoop))) {
245 0 : return false;
246 : }
247 :
248 : #ifdef MOZ_CRASHREPORTER
249 0 : CrashReporterClient::InitSingleton(this);
250 : #endif
251 :
252 0 : mPluginPath = aPluginPath;
253 :
254 0 : return true;
255 : }
256 :
257 : GMPErr
258 0 : GMPChild::GetAPI(const char* aAPIName,
259 : void* aHostAPI,
260 : void** aPluginAPI,
261 : uint32_t aDecryptorId)
262 : {
263 0 : if (!mGMPLoader) {
264 0 : return GMPGenericErr;
265 : }
266 0 : return mGMPLoader->GetAPI(aAPIName, aHostAPI, aPluginAPI, aDecryptorId);
267 : }
268 :
269 : mozilla::ipc::IPCResult
270 0 : GMPChild::RecvPreloadLibs(const nsCString& aLibs)
271 : {
272 : #ifdef XP_WIN
273 : // Pre-load DLLs that need to be used by the EME plugin but that can't be
274 : // loaded after the sandbox has started
275 : // Items in this must be lowercase!
276 : static const char *const whitelist[] = {
277 : "dxva2.dll", // Get monitor information
278 : "evr.dll", // MFGetStrideForBitmapInfoHeader
279 : "mfplat.dll", // MFCreateSample, MFCreateAlignedMemoryBuffer, MFCreateMediaType
280 : "msmpeg2vdec.dll", // H.264 decoder
281 : };
282 :
283 : nsTArray<nsCString> libs;
284 : SplitAt(", ", aLibs, libs);
285 : for (nsCString lib : libs) {
286 : ToLowerCase(lib);
287 : for (const char* whiteListedLib : whitelist) {
288 : if (lib.EqualsASCII(whiteListedLib)) {
289 : LoadLibraryA(lib.get());
290 : break;
291 : }
292 : }
293 : }
294 : #endif
295 0 : return IPC_OK();
296 : }
297 :
298 : bool
299 0 : GMPChild::GetUTF8LibPath(nsACString& aOutLibPath)
300 : {
301 : #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
302 : nsAutoCString pluginDirectoryPath, pluginFilePath;
303 : if (!GetPluginPaths(mPluginPath, pluginDirectoryPath, pluginFilePath)) {
304 : MOZ_CRASH("Error scanning plugin path");
305 : }
306 : aOutLibPath.Assign(pluginFilePath);
307 : return true;
308 : #else
309 0 : nsCOMPtr<nsIFile> libFile;
310 0 : if (!GetPluginFile(mPluginPath, libFile)) {
311 0 : return false;
312 : }
313 :
314 0 : if (!FileExists(libFile)) {
315 0 : NS_WARNING("Can't find GMP library file!");
316 0 : return false;
317 : }
318 :
319 0 : nsAutoString path;
320 0 : libFile->GetPath(path);
321 0 : aOutLibPath = NS_ConvertUTF16toUTF8(path);
322 :
323 0 : return true;
324 : #endif
325 : }
326 :
327 : mozilla::ipc::IPCResult
328 0 : GMPChild::AnswerStartPlugin(const nsString& aAdapter)
329 : {
330 0 : LOGD("%s", __FUNCTION__);
331 :
332 0 : nsCString libPath;
333 0 : if (!GetUTF8LibPath(libPath)) {
334 0 : return IPC_FAIL_NO_REASON(this);
335 : }
336 :
337 0 : auto platformAPI = new GMPPlatformAPI();
338 0 : InitPlatformAPI(*platformAPI, this);
339 :
340 0 : mGMPLoader = MakeUnique<GMPLoader>();
341 : #if defined(MOZ_GMP_SANDBOX)
342 : if (!mGMPLoader->CanSandbox()) {
343 : LOGD("%s Can't sandbox GMP, failing", __FUNCTION__);
344 : delete platformAPI;
345 : return IPC_FAIL_NO_REASON(this);
346 : }
347 : #endif
348 :
349 0 : bool isWidevine = aAdapter.EqualsLiteral("widevine");
350 0 : bool isChromium = aAdapter.EqualsLiteral("chromium");
351 : #if defined(MOZ_GMP_SANDBOX) && defined(XP_MACOSX)
352 : MacSandboxPluginType pluginType = MacSandboxPluginType_GMPlugin_Default;
353 : if (isWidevine || isChromium) {
354 : pluginType = MacSandboxPluginType_GMPlugin_EME_Widevine;
355 : }
356 : if (!SetMacSandboxInfo(pluginType)) {
357 : NS_WARNING("Failed to set Mac GMP sandbox info");
358 : delete platformAPI;
359 : return IPC_FAIL_NO_REASON(this);
360 : }
361 : #endif
362 :
363 0 : GMPAdapter* adapter = nullptr;
364 0 : if (isWidevine) {
365 0 : adapter = new WidevineAdapter();
366 0 : } else if (isChromium) {
367 0 : adapter = new ChromiumCDMAdapter();
368 : }
369 :
370 0 : if (!mGMPLoader->Load(libPath.get(),
371 : libPath.Length(),
372 : platformAPI,
373 : adapter)) {
374 0 : NS_WARNING("Failed to load GMP");
375 : delete platformAPI;
376 0 : return IPC_FAIL_NO_REASON(this);
377 : }
378 :
379 0 : return IPC_OK();
380 : }
381 :
382 : MessageLoop*
383 0 : GMPChild::GMPMessageLoop()
384 : {
385 0 : return mGMPMessageLoop;
386 : }
387 :
388 : void
389 0 : GMPChild::ActorDestroy(ActorDestroyReason aWhy)
390 : {
391 0 : LOGD("%s reason=%d", __FUNCTION__, aWhy);
392 :
393 0 : for (uint32_t i = mGMPContentChildren.Length(); i > 0; i--) {
394 0 : MOZ_ASSERT_IF(aWhy == NormalShutdown, !mGMPContentChildren[i - 1]->IsUsed());
395 0 : mGMPContentChildren[i - 1]->Close();
396 : }
397 :
398 0 : if (mGMPLoader) {
399 0 : mGMPLoader->Shutdown();
400 : }
401 0 : if (AbnormalShutdown == aWhy) {
402 0 : NS_WARNING("Abnormal shutdown of GMP process!");
403 0 : ProcessChild::QuickExit();
404 : }
405 :
406 : #ifdef MOZ_CRASHREPORTER
407 0 : CrashReporterClient::DestroySingleton();
408 : #endif
409 0 : XRE_ShutdownChildProcess();
410 0 : }
411 :
412 : void
413 0 : GMPChild::ProcessingError(Result aCode, const char* aReason)
414 : {
415 0 : switch (aCode) {
416 : case MsgDropped:
417 0 : _exit(0); // Don't trigger a crash report.
418 : case MsgNotKnown:
419 0 : MOZ_CRASH("aborting because of MsgNotKnown");
420 : case MsgNotAllowed:
421 0 : MOZ_CRASH("aborting because of MsgNotAllowed");
422 : case MsgPayloadError:
423 0 : MOZ_CRASH("aborting because of MsgPayloadError");
424 : case MsgProcessingError:
425 0 : MOZ_CRASH("aborting because of MsgProcessingError");
426 : case MsgRouteError:
427 0 : MOZ_CRASH("aborting because of MsgRouteError");
428 : case MsgValueError:
429 0 : MOZ_CRASH("aborting because of MsgValueError");
430 : default:
431 0 : MOZ_CRASH("not reached");
432 : }
433 : }
434 :
435 : PGMPTimerChild*
436 0 : GMPChild::AllocPGMPTimerChild()
437 : {
438 0 : return new GMPTimerChild(this);
439 : }
440 :
441 : bool
442 0 : GMPChild::DeallocPGMPTimerChild(PGMPTimerChild* aActor)
443 : {
444 0 : MOZ_ASSERT(mTimerChild == static_cast<GMPTimerChild*>(aActor));
445 0 : mTimerChild = nullptr;
446 0 : return true;
447 : }
448 :
449 : GMPTimerChild*
450 0 : GMPChild::GetGMPTimers()
451 : {
452 0 : if (!mTimerChild) {
453 0 : PGMPTimerChild* sc = SendPGMPTimerConstructor();
454 0 : if (!sc) {
455 0 : return nullptr;
456 : }
457 0 : mTimerChild = static_cast<GMPTimerChild*>(sc);
458 : }
459 0 : return mTimerChild;
460 : }
461 :
462 : PGMPStorageChild*
463 0 : GMPChild::AllocPGMPStorageChild()
464 : {
465 0 : return new GMPStorageChild(this);
466 : }
467 :
468 : bool
469 0 : GMPChild::DeallocPGMPStorageChild(PGMPStorageChild* aActor)
470 : {
471 0 : mStorage = nullptr;
472 0 : return true;
473 : }
474 :
475 : GMPStorageChild*
476 0 : GMPChild::GetGMPStorage()
477 : {
478 0 : if (!mStorage) {
479 0 : PGMPStorageChild* sc = SendPGMPStorageConstructor();
480 0 : if (!sc) {
481 0 : return nullptr;
482 : }
483 0 : mStorage = static_cast<GMPStorageChild*>(sc);
484 : }
485 0 : return mStorage;
486 : }
487 :
488 : mozilla::ipc::IPCResult
489 0 : GMPChild::RecvCrashPluginNow()
490 : {
491 0 : MOZ_CRASH();
492 : return IPC_OK();
493 : }
494 :
495 : mozilla::ipc::IPCResult
496 0 : GMPChild::RecvCloseActive()
497 : {
498 0 : for (uint32_t i = mGMPContentChildren.Length(); i > 0; i--) {
499 0 : mGMPContentChildren[i - 1]->CloseActive();
500 : }
501 0 : return IPC_OK();
502 : }
503 :
504 : mozilla::ipc::IPCResult
505 0 : GMPChild::RecvInitGMPContentChild(Endpoint<PGMPContentChild>&& aEndpoint)
506 : {
507 : GMPContentChild* child =
508 0 : mGMPContentChildren.AppendElement(new GMPContentChild(this))->get();
509 0 : aEndpoint.Bind(child);
510 0 : return IPC_OK();
511 : }
512 :
513 : void
514 0 : GMPChild::GMPContentChildActorDestroy(GMPContentChild* aGMPContentChild)
515 : {
516 0 : for (uint32_t i = mGMPContentChildren.Length(); i > 0; i--) {
517 0 : UniquePtr<GMPContentChild>& toDestroy = mGMPContentChildren[i - 1];
518 0 : if (toDestroy.get() == aGMPContentChild) {
519 0 : SendPGMPContentChildDestroyed();
520 : RefPtr<DeleteTask<GMPContentChild>> task =
521 0 : new DeleteTask<GMPContentChild>(toDestroy.release());
522 0 : MessageLoop::current()->PostTask(task.forget());
523 0 : mGMPContentChildren.RemoveElementAt(i - 1);
524 0 : break;
525 : }
526 : }
527 0 : }
528 :
529 : } // namespace gmp
530 : } // namespace mozilla
531 :
532 : #undef LOG
533 : #undef LOGD
|