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 "GMPParent.h"
7 
8 #include "CDMStorageIdProvider.h"
9 #include "ChromiumCDMAdapter.h"
10 #include "GMPContentParent.h"
11 #include "GMPLog.h"
12 #include "GMPTimerParent.h"
13 #include "MediaResult.h"
14 #include "mozIGeckoMediaPluginService.h"
15 #include "mozilla/dom/WidevineCDMManifestBinding.h"
16 #include "mozilla/ipc/CrashReporterHost.h"
17 #include "mozilla/ipc/Endpoint.h"
18 #include "mozilla/ipc/GeckoChildProcessHost.h"
19 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
20 #  include "mozilla/SandboxInfo.h"
21 #endif
22 #include "mozilla/Services.h"
23 #include "mozilla/SSE.h"
24 #include "mozilla/SyncRunnable.h"
25 #include "mozilla/Telemetry.h"
26 #include "mozilla/Unused.h"
27 #include "nsComponentManagerUtils.h"
28 #include "nsIRunnable.h"
29 #include "nsIObserverService.h"
30 #include "nsIWritablePropertyBag2.h"
31 #include "nsPrintfCString.h"
32 #include "nsThreadUtils.h"
33 #include "runnable_utils.h"
34 #include "VideoUtils.h"
35 #ifdef XP_WIN
36 #  include "WMFDecoderModule.h"
37 #endif
38 #if defined(MOZ_WIDGET_ANDROID)
39 #  include "mozilla/java/GeckoProcessManagerWrappers.h"
40 #  include "mozilla/java/GeckoProcessTypeWrappers.h"
41 #endif  // defined(MOZ_WIDGET_ANDROID)
42 #if defined(XP_MACOSX)
43 #  include "nsMacUtilsImpl.h"
44 #  include "base/process_util.h"
45 #endif  // defined(XP_MACOSX)
46 
47 using mozilla::ipc::GeckoChildProcessHost;
48 
49 using CrashReporter::AnnotationTable;
50 using CrashReporter::GetIDFromMinidump;
51 
52 namespace mozilla::gmp {
53 
54 #define GMP_PARENT_LOG_DEBUG(x, ...) \
55   GMP_LOG_DEBUG("GMPParent[%p|childPid=%d] " x, this, mChildPid, ##__VA_ARGS__)
56 
57 #ifdef __CLASS__
58 #  undef __CLASS__
59 #endif
60 #define __CLASS__ "GMPParent"
61 
GMPParent()62 GMPParent::GMPParent()
63     : mState(GMPStateNotLoaded),
64       mPluginId(GeckoChildProcessHost::GetUniqueID()),
65       mProcess(nullptr),
66       mDeleteProcessOnlyOnUnload(false),
67       mAbnormalShutdownInProgress(false),
68       mIsBlockingDeletion(false),
69       mCanDecrypt(false),
70       mGMPContentChildCount(0),
71       mChildPid(0),
72       mHoldingSelfRef(false),
73 #if defined(XP_MACOSX) && defined(__aarch64__)
74       mChildLaunchArch(base::PROCESS_ARCH_INVALID),
75 #endif
76       mMainThread(GetMainThreadSerialEventTarget()) {
77   MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread());
78   GMP_PARENT_LOG_DEBUG("GMPParent ctor id=%u", mPluginId);
79 }
80 
~GMPParent()81 GMPParent::~GMPParent() {
82   // This method is not restricted to a specific thread.
83   GMP_PARENT_LOG_DEBUG("GMPParent dtor id=%u", mPluginId);
84   MOZ_ASSERT(!mProcess);
85 }
86 
CloneFrom(const GMPParent * aOther)87 void GMPParent::CloneFrom(const GMPParent* aOther) {
88   MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread());
89   MOZ_ASSERT(aOther->mDirectory && aOther->mService, "null plugin directory");
90 
91   mService = aOther->mService;
92   mDirectory = aOther->mDirectory;
93   mName = aOther->mName;
94   mVersion = aOther->mVersion;
95   mDescription = aOther->mDescription;
96   mDisplayName = aOther->mDisplayName;
97 #if defined(XP_WIN) || defined(XP_LINUX)
98   mLibs = aOther->mLibs;
99 #endif
100   for (const GMPCapability& cap : aOther->mCapabilities) {
101     mCapabilities.AppendElement(cap);
102   }
103   mAdapter = aOther->mAdapter;
104 
105 #if defined(XP_MACOSX) && defined(__aarch64__)
106   mChildLaunchArch = aOther->mChildLaunchArch;
107 #endif
108 }
109 
110 #if defined(XP_MACOSX)
GetPluginFileArch(nsIFile * aPluginDir,nsAutoString & aLeafName,uint32_t & aArchSet)111 nsresult GMPParent::GetPluginFileArch(nsIFile* aPluginDir,
112                                       nsAutoString& aLeafName,
113                                       uint32_t& aArchSet) {
114   // Build up the plugin filename
115   nsAutoString baseName;
116   baseName = Substring(aLeafName, 4, aLeafName.Length() - 1);
117   nsAutoString pluginFileName = u"lib"_ns + baseName + u".dylib"_ns;
118   GMP_PARENT_LOG_DEBUG("%s: pluginFileName: %s", __FUNCTION__,
119                        NS_LossyConvertUTF16toASCII(pluginFileName).get());
120 
121   // Create an nsIFile representing the plugin
122   nsCOMPtr<nsIFile> pluginFile;
123   nsresult rv = aPluginDir->Clone(getter_AddRefs(pluginFile));
124   NS_ENSURE_SUCCESS(rv, rv);
125   pluginFile->AppendRelativePath(pluginFileName);
126 
127   // Get the full plugin path
128   nsCString pluginPath;
129   rv = pluginFile->GetNativePath(pluginPath);
130   NS_ENSURE_SUCCESS(rv, rv);
131   GMP_PARENT_LOG_DEBUG("%s: pluginPath: %s", __FUNCTION__, pluginPath.get());
132 
133   rv = nsMacUtilsImpl::GetArchitecturesForBinary(pluginPath.get(), &aArchSet);
134   NS_ENSURE_SUCCESS(rv, rv);
135 
136 #  if defined(__aarch64__)
137   mPluginFilePath = pluginPath;
138 #  endif
139 
140   return NS_OK;
141 }
142 #endif  // defined(XP_MACOSX)
143 
Init(GeckoMediaPluginServiceParent * aService,nsIFile * aPluginDir)144 RefPtr<GenericPromise> GMPParent::Init(GeckoMediaPluginServiceParent* aService,
145                                        nsIFile* aPluginDir) {
146   MOZ_ASSERT(aPluginDir);
147   MOZ_ASSERT(aService);
148   MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread());
149 
150   mService = aService;
151   mDirectory = aPluginDir;
152 
153   // aPluginDir is <profile-dir>/<gmp-plugin-id>/<version>
154   // where <gmp-plugin-id> should be gmp-gmpopenh264
155   nsCOMPtr<nsIFile> parent;
156   nsresult rv = aPluginDir->GetParent(getter_AddRefs(parent));
157   if (NS_WARN_IF(NS_FAILED(rv))) {
158     return GenericPromise::CreateAndReject(rv, __func__);
159   }
160   nsAutoString parentLeafName;
161   rv = parent->GetLeafName(parentLeafName);
162   if (NS_WARN_IF(NS_FAILED(rv))) {
163     return GenericPromise::CreateAndReject(rv, __func__);
164   }
165   GMP_PARENT_LOG_DEBUG("%s: for %s", __FUNCTION__,
166                        NS_LossyConvertUTF16toASCII(parentLeafName).get());
167 
168   MOZ_ASSERT(parentLeafName.Length() > 4);
169   mName = Substring(parentLeafName, 4);
170 
171 #if defined(XP_MACOSX)
172   uint32_t pluginArch = 0;
173   rv = GetPluginFileArch(aPluginDir, parentLeafName, pluginArch);
174   if (NS_FAILED(rv)) {
175     GMP_PARENT_LOG_DEBUG("%s: Plugin arch error: %d", __FUNCTION__, rv);
176   } else {
177     GMP_PARENT_LOG_DEBUG("%s: Plugin arch: 0x%x", __FUNCTION__, pluginArch);
178   }
179 
180   uint32_t x86 = base::PROCESS_ARCH_X86_64 | base::PROCESS_ARCH_I386;
181 #  if defined(__aarch64__)
182   uint32_t arm64 = base::PROCESS_ARCH_ARM_64;
183   // When executing in an ARM64 process, if the library is x86 or x64,
184   // set |mChildLaunchArch| to x64 and allow the library to be used as long
185   // as this process is a universal binary.
186   if (!(pluginArch & arm64) && (pluginArch & x86)) {
187     bool isWidevine = parentLeafName.Find("widevine") != kNotFound;
188     bool isWidevineAllowed =
189         StaticPrefs::media_gmp_widevinecdm_allow_x64_plugin_on_arm64();
190     bool isH264 = parentLeafName.Find("openh264") != kNotFound;
191     bool isH264Allowed =
192         StaticPrefs::media_gmp_gmpopenh264_allow_x64_plugin_on_arm64();
193 
194     // Only allow x64 child GMP processes for Widevine and OpenH264
195     if (!isWidevine && !isH264) {
196       return GenericPromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED,
197                                              __func__);
198     }
199     // And only if prefs permit it.
200     if ((isWidevine && !isWidevineAllowed) || (isH264 && !isH264Allowed)) {
201       return GenericPromise::CreateAndReject(NS_ERROR_PLUGIN_DISABLED,
202                                              __func__);
203     }
204 
205     // We have an x64 library. Get the bundle architecture to determine
206     // if we are a universal binary and hence if we can launch an x64
207     // child process to host this plugin.
208     uint32_t bundleArch = base::PROCESS_ARCH_INVALID;
209     rv = nsMacUtilsImpl::GetArchitecturesForBundle(&bundleArch);
210     if (NS_FAILED(rv)) {
211       // If we fail here, continue as if this is not a univeral binary.
212       GMP_PARENT_LOG_DEBUG("%s: Bundle arch error: %d", __FUNCTION__, rv);
213     } else {
214       GMP_PARENT_LOG_DEBUG("%s: Bundle arch: 0x%x", __FUNCTION__, bundleArch);
215     }
216 
217     bool isUniversalBinary = (bundleArch & base::PROCESS_ARCH_X86_64) &&
218                              (bundleArch & base::PROCESS_ARCH_ARM_64);
219     if (isUniversalBinary) {
220       mChildLaunchArch = base::PROCESS_ARCH_X86_64;
221       PreTranslateBins();
222     } else {
223       return GenericPromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED,
224                                              __func__);
225     }
226   }
227 #  else
228   // When executing in a non-ARM process, if the library is not x86 or x64,
229   // remove it and return an error. This prevents a child process crash due
230   // to loading an incompatible library and forces a new plugin version to be
231   // downloaded when the check is next performed. This could occur if a profile
232   // is moved from an ARM64 system to an x64 system.
233   if ((pluginArch & x86) == 0) {
234     GMP_PARENT_LOG_DEBUG("%s: Removing plugin directory", __FUNCTION__);
235     aPluginDir->Remove(true);
236     return GenericPromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED, __func__);
237   }
238 #  endif  // defined(__aarch64__)
239 #endif    // defined(XP_MACOSX)
240 
241   return ReadGMPMetaData();
242 }
243 
Crash()244 void GMPParent::Crash() {
245   if (mState != GMPStateNotLoaded) {
246     Unused << SendCrashPluginNow();
247   }
248 }
249 
LoadProcess()250 nsresult GMPParent::LoadProcess() {
251   MOZ_ASSERT(mDirectory, "Plugin directory cannot be NULL!");
252   MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread());
253   MOZ_ASSERT(mState == GMPStateNotLoaded);
254 
255   nsAutoString path;
256   if (NS_WARN_IF(NS_FAILED(mDirectory->GetPath(path)))) {
257     return NS_ERROR_FAILURE;
258   }
259   GMP_PARENT_LOG_DEBUG("%s: for %s", __FUNCTION__,
260                        NS_ConvertUTF16toUTF8(path).get());
261 
262   if (!mProcess) {
263     mProcess = new GMPProcessParent(NS_ConvertUTF16toUTF8(path).get());
264 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
265     mProcess->SetRequiresWindowServer(mAdapter.EqualsLiteral("chromium"));
266 #endif
267 
268 #if defined(XP_MACOSX) && defined(__aarch64__)
269     mProcess->SetLaunchArchitecture(mChildLaunchArch);
270 #endif
271 
272     if (!mProcess->Launch(30 * 1000)) {
273       GMP_PARENT_LOG_DEBUG("%s: Failed to launch new child process",
274                            __FUNCTION__);
275       mProcess->Delete();
276       mProcess = nullptr;
277       return NS_ERROR_FAILURE;
278     }
279 
280     mChildPid = base::GetProcId(mProcess->GetChildProcessHandle());
281     GMP_PARENT_LOG_DEBUG("%s: Launched new child process", __FUNCTION__);
282 
283     bool opened = Open(mProcess->TakeInitialPort(),
284                        base::GetProcId(mProcess->GetChildProcessHandle()));
285     if (!opened) {
286       GMP_PARENT_LOG_DEBUG("%s: Failed to open channel to new child process",
287                            __FUNCTION__);
288       mProcess->Delete();
289       mProcess = nullptr;
290       return NS_ERROR_FAILURE;
291     }
292     GMP_PARENT_LOG_DEBUG("%s: Opened channel to new child process",
293                          __FUNCTION__);
294 
295     // ComputeStorageId may return empty string, we leave the error handling to
296     // CDM. The CDM will reject the promise once we provide a empty string of
297     // storage id.
298     bool ok =
299         SendProvideStorageId(CDMStorageIdProvider::ComputeStorageId(mNodeId));
300     if (!ok) {
301       GMP_PARENT_LOG_DEBUG("%s: Failed to send storage id to child process",
302                            __FUNCTION__);
303       return NS_ERROR_FAILURE;
304     }
305     GMP_PARENT_LOG_DEBUG("%s: Sent storage id to child process", __FUNCTION__);
306 
307 #if defined(XP_WIN) || defined(XP_LINUX)
308     if (!mLibs.IsEmpty()) {
309       bool ok = SendPreloadLibs(mLibs);
310       if (!ok) {
311         GMP_PARENT_LOG_DEBUG("%s: Failed to send preload-libs to child process",
312                              __FUNCTION__);
313         return NS_ERROR_FAILURE;
314       }
315       GMP_PARENT_LOG_DEBUG("%s: Sent preload-libs ('%s') to child process",
316                            __FUNCTION__, mLibs.get());
317     }
318 #endif
319 
320     // Intr call to block initialization on plugin load.
321     if (!CallStartPlugin(mAdapter)) {
322       GMP_PARENT_LOG_DEBUG("%s: Failed to send start to child process",
323                            __FUNCTION__);
324       return NS_ERROR_FAILURE;
325     }
326     GMP_PARENT_LOG_DEBUG("%s: Sent StartPlugin to child process", __FUNCTION__);
327   }
328 
329   mState = GMPStateLoaded;
330 
331   // Hold a self ref while the child process is alive. This ensures that
332   // during shutdown the GMPParent stays alive long enough to
333   // terminate the child process.
334   MOZ_ASSERT(!mHoldingSelfRef);
335   mHoldingSelfRef = true;
336   AddRef();
337 
338   return NS_OK;
339 }
340 
RecvPGMPContentChildDestroyed()341 mozilla::ipc::IPCResult GMPParent::RecvPGMPContentChildDestroyed() {
342   --mGMPContentChildCount;
343   if (!IsUsed()) {
344     CloseIfUnused();
345   }
346   return IPC_OK();
347 }
348 
CloseIfUnused()349 void GMPParent::CloseIfUnused() {
350   MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread());
351   GMP_PARENT_LOG_DEBUG("%s", __FUNCTION__);
352 
353   if ((mDeleteProcessOnlyOnUnload || mState == GMPStateLoaded ||
354        mState == GMPStateUnloading) &&
355       !IsUsed()) {
356     // Ensure all timers are killed.
357     for (uint32_t i = mTimers.Length(); i > 0; i--) {
358       mTimers[i - 1]->Shutdown();
359     }
360 
361     // Shutdown GMPStorage. Given that all protocol actors must be shutdown
362     // (!Used() is true), all storage operations should be complete.
363     for (size_t i = mStorage.Length(); i > 0; i--) {
364       mStorage[i - 1]->Shutdown();
365     }
366     Shutdown();
367   }
368 }
369 
CloseActive(bool aDieWhenUnloaded)370 void GMPParent::CloseActive(bool aDieWhenUnloaded) {
371   MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread());
372   GMP_PARENT_LOG_DEBUG("%s: state %d", __FUNCTION__, mState);
373 
374   if (aDieWhenUnloaded) {
375     mDeleteProcessOnlyOnUnload = true;  // don't allow this to go back...
376   }
377   if (mState == GMPStateLoaded) {
378     mState = GMPStateUnloading;
379   }
380   if (mState != GMPStateNotLoaded && IsUsed()) {
381     Unused << SendCloseActive();
382     CloseIfUnused();
383   }
384 }
385 
MarkForDeletion()386 void GMPParent::MarkForDeletion() {
387   mDeleteProcessOnlyOnUnload = true;
388   mIsBlockingDeletion = true;
389 }
390 
IsMarkedForDeletion()391 bool GMPParent::IsMarkedForDeletion() { return mIsBlockingDeletion; }
392 
Shutdown()393 void GMPParent::Shutdown() {
394   MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread());
395   GMP_PARENT_LOG_DEBUG("%s", __FUNCTION__);
396 
397   if (mAbnormalShutdownInProgress) {
398     return;
399   }
400 
401   MOZ_ASSERT(!IsUsed());
402   if (mState == GMPStateNotLoaded || mState == GMPStateClosing) {
403     return;
404   }
405 
406   RefPtr<GMPParent> self(this);
407   DeleteProcess();
408 
409   // XXX Get rid of mDeleteProcessOnlyOnUnload and this code when
410   // Bug 1043671 is fixed
411   if (!mDeleteProcessOnlyOnUnload) {
412     // Destroy ourselves and rise from the fire to save memory
413     mService->ReAddOnGMPThread(self);
414   }  // else we've been asked to die and stay dead
415   MOZ_ASSERT(mState == GMPStateNotLoaded);
416 }
417 
418 class NotifyGMPShutdownTask : public Runnable {
419  public:
NotifyGMPShutdownTask(const nsAString & aNodeId)420   explicit NotifyGMPShutdownTask(const nsAString& aNodeId)
421       : Runnable("NotifyGMPShutdownTask"), mNodeId(aNodeId) {}
Run()422   NS_IMETHOD Run() override {
423     MOZ_ASSERT(NS_IsMainThread());
424     nsCOMPtr<nsIObserverService> obsService =
425         mozilla::services::GetObserverService();
426     MOZ_ASSERT(obsService);
427     if (obsService) {
428       obsService->NotifyObservers(nullptr, "gmp-shutdown", mNodeId.get());
429     }
430     return NS_OK;
431   }
432   nsString mNodeId;
433 };
434 
ChildTerminated()435 void GMPParent::ChildTerminated() {
436   RefPtr<GMPParent> self(this);
437   nsCOMPtr<nsISerialEventTarget> gmpEventTarget = GMPEventTarget();
438 
439   if (!gmpEventTarget) {
440     // Bug 1163239 - this can happen on shutdown.
441     // PluginTerminated removes the GMP from the GMPService.
442     // On shutdown we can have this case where it is already been
443     // removed so there is no harm in not trying to remove it again.
444     GMP_PARENT_LOG_DEBUG("%s::%s: GMPEventTarget() returned nullptr.",
445                          __CLASS__, __FUNCTION__);
446   } else {
447     gmpEventTarget->Dispatch(
448         NewRunnableMethod<RefPtr<GMPParent>>(
449             "gmp::GeckoMediaPluginServiceParent::PluginTerminated", mService,
450             &GeckoMediaPluginServiceParent::PluginTerminated, self),
451         NS_DISPATCH_NORMAL);
452   }
453 }
454 
DeleteProcess()455 void GMPParent::DeleteProcess() {
456   MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread());
457   GMP_PARENT_LOG_DEBUG("%s", __FUNCTION__);
458 
459   if (mState != GMPStateClosing) {
460     // Don't Close() twice!
461     // Probably remove when bug 1043671 is resolved
462     mState = GMPStateClosing;
463     Close();
464   }
465   mProcess->Delete(NewRunnableMethod("gmp::GMPParent::ChildTerminated", this,
466                                      &GMPParent::ChildTerminated));
467   GMP_PARENT_LOG_DEBUG("%s: Shut down process", __FUNCTION__);
468   mProcess = nullptr;
469 
470 #if defined(MOZ_WIDGET_ANDROID)
471   if (mState != GMPStateNotLoaded) {
472     nsCOMPtr<nsIEventTarget> launcherThread(GetIPCLauncher());
473     MOZ_ASSERT(launcherThread);
474 
475     auto procType = java::GeckoProcessType::GMPLUGIN();
476     auto selector =
477         java::GeckoProcessManager::Selector::New(procType, OtherPid());
478 
479     launcherThread->Dispatch(NS_NewRunnableFunction(
480         "GMPParent::DeleteProcess",
481         [selector =
482              java::GeckoProcessManager::Selector::GlobalRef(selector)]() {
483           java::GeckoProcessManager::ShutdownProcess(selector);
484         }));
485   }
486 #endif  // defined(MOZ_WIDGET_ANDROID)
487 
488   mState = GMPStateNotLoaded;
489 
490   nsCOMPtr<nsIRunnable> r =
491       new NotifyGMPShutdownTask(NS_ConvertUTF8toUTF16(mNodeId));
492   mMainThread->Dispatch(r.forget());
493 
494   if (mHoldingSelfRef) {
495     Release();
496     mHoldingSelfRef = false;
497   }
498 }
499 
State() const500 GMPState GMPParent::State() const { return mState; }
501 
GMPEventTarget()502 nsCOMPtr<nsISerialEventTarget> GMPParent::GMPEventTarget() {
503   nsCOMPtr<mozIGeckoMediaPluginService> mps =
504       do_GetService("@mozilla.org/gecko-media-plugin-service;1");
505   MOZ_ASSERT(mps);
506   if (!mps) {
507     return nullptr;
508   }
509   // Note: GeckoMediaPluginService::GetThread() is threadsafe, and returns
510   // nullptr if the GeckoMediaPluginService has started shutdown.
511   nsCOMPtr<nsIThread> gmpThread;
512   mps->GetThread(getter_AddRefs(gmpThread));
513   return gmpThread ? gmpThread->SerialEventTarget() : nullptr;
514 }
515 
516 /* static */
Supports(const nsTArray<GMPCapability> & aCapabilities,const nsCString & aAPI,const nsTArray<nsCString> & aTags)517 bool GMPCapability::Supports(const nsTArray<GMPCapability>& aCapabilities,
518                              const nsCString& aAPI,
519                              const nsTArray<nsCString>& aTags) {
520   for (const nsCString& tag : aTags) {
521     if (!GMPCapability::Supports(aCapabilities, aAPI, tag)) {
522       return false;
523     }
524   }
525   return true;
526 }
527 
528 /* static */
Supports(const nsTArray<GMPCapability> & aCapabilities,const nsCString & aAPI,const nsCString & aTag)529 bool GMPCapability::Supports(const nsTArray<GMPCapability>& aCapabilities,
530                              const nsCString& aAPI, const nsCString& aTag) {
531   for (const GMPCapability& capabilities : aCapabilities) {
532     if (!capabilities.mAPIName.Equals(aAPI)) {
533       continue;
534     }
535     for (const nsCString& tag : capabilities.mAPITags) {
536       if (tag.Equals(aTag)) {
537 #ifdef XP_WIN
538         // Clearkey on Windows advertises that it can decode in its GMP info
539         // file, but uses Windows Media Foundation to decode. That's not present
540         // on Windows XP, and on some Vista, Windows N, and KN variants without
541         // certain services packs.
542         if (tag.EqualsLiteral(EME_KEY_SYSTEM_CLEARKEY)) {
543           if (capabilities.mAPIName.EqualsLiteral(GMP_API_VIDEO_DECODER)) {
544             if (!WMFDecoderModule::HasH264()) {
545               continue;
546             }
547           }
548         }
549 #endif
550         return true;
551       }
552     }
553   }
554   return false;
555 }
556 
EnsureProcessLoaded()557 bool GMPParent::EnsureProcessLoaded() {
558   if (mState == GMPStateLoaded) {
559     return true;
560   }
561   if (mState == GMPStateClosing || mState == GMPStateUnloading) {
562     return false;
563   }
564 
565   nsresult rv = LoadProcess();
566 
567   return NS_SUCCEEDED(rv);
568 }
569 
AddCrashAnnotations()570 void GMPParent::AddCrashAnnotations() {
571   if (mCrashReporter) {
572     mCrashReporter->AddAnnotation(CrashReporter::Annotation::GMPPlugin, true);
573     mCrashReporter->AddAnnotation(CrashReporter::Annotation::PluginFilename,
574                                   NS_ConvertUTF16toUTF8(mName));
575     mCrashReporter->AddAnnotation(CrashReporter::Annotation::PluginName,
576                                   mDisplayName);
577     mCrashReporter->AddAnnotation(CrashReporter::Annotation::PluginVersion,
578                                   mVersion);
579   }
580 }
581 
GetCrashID(nsString & aResult)582 void GMPParent::GetCrashID(nsString& aResult) {
583   AddCrashAnnotations();
584   GenerateCrashReport(OtherPid(), &aResult);
585 }
586 
GMPNotifyObservers(const uint32_t aPluginID,const nsACString & aPluginName,const nsAString & aPluginDumpID)587 static void GMPNotifyObservers(const uint32_t aPluginID,
588                                const nsACString& aPluginName,
589                                const nsAString& aPluginDumpID) {
590   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
591   nsCOMPtr<nsIWritablePropertyBag2> propbag =
592       do_CreateInstance("@mozilla.org/hash-property-bag;1");
593   if (obs && propbag) {
594     propbag->SetPropertyAsUint32(u"pluginID"_ns, aPluginID);
595     propbag->SetPropertyAsACString(u"pluginName"_ns, aPluginName);
596     propbag->SetPropertyAsAString(u"pluginDumpID"_ns, aPluginDumpID);
597     obs->NotifyObservers(propbag, "gmp-plugin-crash", nullptr);
598   }
599 
600   RefPtr<gmp::GeckoMediaPluginService> service =
601       gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
602   if (service) {
603     service->RunPluginCrashCallbacks(aPluginID, aPluginName);
604   }
605 }
606 
ActorDestroy(ActorDestroyReason aWhy)607 void GMPParent::ActorDestroy(ActorDestroyReason aWhy) {
608   MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread());
609   GMP_PARENT_LOG_DEBUG("%s: (%d)", __FUNCTION__, (int)aWhy);
610 
611   if (AbnormalShutdown == aWhy) {
612     Telemetry::Accumulate(Telemetry::SUBPROCESS_ABNORMAL_ABORT, "gmplugin"_ns,
613                           1);
614     nsString dumpID;
615     GetCrashID(dumpID);
616     if (dumpID.IsEmpty()) {
617       NS_WARNING("GMP crash without crash report");
618       dumpID = mName;
619       dumpID += '-';
620       AppendUTF8toUTF16(mVersion, dumpID);
621     }
622 
623     // NotifyObservers is mainthread-only
624     nsCOMPtr<nsIRunnable> r =
625         WrapRunnableNM(&GMPNotifyObservers, mPluginId, mDisplayName, dumpID);
626     mMainThread->Dispatch(r.forget());
627   }
628 
629   // warn us off trying to close again
630   mState = GMPStateClosing;
631   mAbnormalShutdownInProgress = true;
632   CloseActive(false);
633 
634   // Normal Shutdown() will delete the process on unwind.
635   if (AbnormalShutdown == aWhy) {
636     RefPtr<GMPParent> self(this);
637     // Must not call Close() again in DeleteProcess(), as we'll recurse
638     // infinitely if we do.
639     MOZ_ASSERT(mState == GMPStateClosing);
640     DeleteProcess();
641     // Note: final destruction will be Dispatched to ourself
642     mService->ReAddOnGMPThread(self);
643   }
644 }
645 
AllocPGMPStorageParent()646 PGMPStorageParent* GMPParent::AllocPGMPStorageParent() {
647   GMPStorageParent* p = new GMPStorageParent(mNodeId, this);
648   mStorage.AppendElement(p);  // Addrefs, released in DeallocPGMPStorageParent.
649   return p;
650 }
651 
DeallocPGMPStorageParent(PGMPStorageParent * aActor)652 bool GMPParent::DeallocPGMPStorageParent(PGMPStorageParent* aActor) {
653   GMPStorageParent* p = static_cast<GMPStorageParent*>(aActor);
654   p->Shutdown();
655   mStorage.RemoveElement(p);
656   return true;
657 }
658 
RecvPGMPStorageConstructor(PGMPStorageParent * aActor)659 mozilla::ipc::IPCResult GMPParent::RecvPGMPStorageConstructor(
660     PGMPStorageParent* aActor) {
661   GMPStorageParent* p = (GMPStorageParent*)aActor;
662   if (NS_WARN_IF(NS_FAILED(p->Init()))) {
663     return IPC_FAIL_NO_REASON(this);
664   }
665   return IPC_OK();
666 }
667 
RecvPGMPTimerConstructor(PGMPTimerParent * actor)668 mozilla::ipc::IPCResult GMPParent::RecvPGMPTimerConstructor(
669     PGMPTimerParent* actor) {
670   return IPC_OK();
671 }
672 
AllocPGMPTimerParent()673 PGMPTimerParent* GMPParent::AllocPGMPTimerParent() {
674   nsCOMPtr<nsISerialEventTarget> target = GMPEventTarget();
675   GMPTimerParent* p = new GMPTimerParent(target);
676   mTimers.AppendElement(
677       p);  // Released in DeallocPGMPTimerParent, or on shutdown.
678   return p;
679 }
680 
DeallocPGMPTimerParent(PGMPTimerParent * aActor)681 bool GMPParent::DeallocPGMPTimerParent(PGMPTimerParent* aActor) {
682   GMPTimerParent* p = static_cast<GMPTimerParent*>(aActor);
683   p->Shutdown();
684   mTimers.RemoveElement(p);
685   return true;
686 }
687 
ReadInfoField(GMPInfoFileParser & aParser,const nsCString & aKey,nsACString & aOutValue)688 bool ReadInfoField(GMPInfoFileParser& aParser, const nsCString& aKey,
689                    nsACString& aOutValue) {
690   if (!aParser.Contains(aKey) || aParser.Get(aKey).IsEmpty()) {
691     return false;
692   }
693   aOutValue = aParser.Get(aKey);
694   return true;
695 }
696 
ReadGMPMetaData()697 RefPtr<GenericPromise> GMPParent::ReadGMPMetaData() {
698   MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread());
699   MOZ_ASSERT(mDirectory, "Plugin directory cannot be NULL!");
700   MOZ_ASSERT(!mName.IsEmpty(), "Plugin mName cannot be empty!");
701 
702   nsCOMPtr<nsIFile> infoFile;
703   nsresult rv = mDirectory->Clone(getter_AddRefs(infoFile));
704   if (NS_WARN_IF(NS_FAILED(rv))) {
705     return GenericPromise::CreateAndReject(rv, __func__);
706   }
707   infoFile->AppendRelativePath(mName + u".info"_ns);
708 
709   if (FileExists(infoFile)) {
710     return ReadGMPInfoFile(infoFile);
711   }
712 
713   // Maybe this is the Widevine adapted plugin?
714   nsCOMPtr<nsIFile> manifestFile;
715   rv = mDirectory->Clone(getter_AddRefs(manifestFile));
716   if (NS_WARN_IF(NS_FAILED(rv))) {
717     return GenericPromise::CreateAndReject(rv, __func__);
718   }
719   manifestFile->AppendRelativePath(u"manifest.json"_ns);
720   return ReadChromiumManifestFile(manifestFile);
721 }
722 
ReadGMPInfoFile(nsIFile * aFile)723 RefPtr<GenericPromise> GMPParent::ReadGMPInfoFile(nsIFile* aFile) {
724   MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread());
725   GMPInfoFileParser parser;
726   if (!parser.Init(aFile)) {
727     return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
728   }
729 
730   nsAutoCString apis;
731   if (!ReadInfoField(parser, "name"_ns, mDisplayName) ||
732       !ReadInfoField(parser, "description"_ns, mDescription) ||
733       !ReadInfoField(parser, "version"_ns, mVersion) ||
734       !ReadInfoField(parser, "apis"_ns, apis)) {
735     return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
736   }
737 
738 #if defined(XP_WIN) || defined(XP_LINUX)
739   // "Libraries" field is optional.
740   ReadInfoField(parser, "libraries"_ns, mLibs);
741 #endif
742 
743   nsTArray<nsCString> apiTokens;
744   SplitAt(", ", apis, apiTokens);
745   for (nsCString api : apiTokens) {
746     int32_t tagsStart = api.FindChar('[');
747     if (tagsStart == 0) {
748       // Not allowed to be the first character.
749       // API name must be at least one character.
750       continue;
751     }
752 
753     GMPCapability cap;
754     if (tagsStart == -1) {
755       // No tags.
756       cap.mAPIName.Assign(api);
757     } else {
758       auto tagsEnd = api.FindChar(']');
759       if (tagsEnd == -1 || tagsEnd < tagsStart) {
760         // Invalid syntax, skip whole capability.
761         continue;
762       }
763 
764       cap.mAPIName.Assign(Substring(api, 0, tagsStart));
765 
766       if ((tagsEnd - tagsStart) > 1) {
767         const nsDependentCSubstring ts(
768             Substring(api, tagsStart + 1, tagsEnd - tagsStart - 1));
769         nsTArray<nsCString> tagTokens;
770         SplitAt(":", ts, tagTokens);
771         for (nsCString tag : tagTokens) {
772           cap.mAPITags.AppendElement(tag);
773         }
774       }
775     }
776 
777     mCapabilities.AppendElement(std::move(cap));
778   }
779 
780   if (mCapabilities.IsEmpty()) {
781     return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
782   }
783 
784   return GenericPromise::CreateAndResolve(true, __func__);
785 }
786 
ReadChromiumManifestFile(nsIFile * aFile)787 RefPtr<GenericPromise> GMPParent::ReadChromiumManifestFile(nsIFile* aFile) {
788   MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread());
789   nsAutoCString json;
790   if (!ReadIntoString(aFile, json, 5 * 1024)) {
791     return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
792   }
793 
794   // DOM JSON parsing needs to run on the main thread.
795   return InvokeAsync(mMainThread, this, __func__,
796                      &GMPParent::ParseChromiumManifest,
797                      NS_ConvertUTF8toUTF16(json));
798 }
799 
IsCDMAPISupported(const mozilla::dom::WidevineCDMManifest & aManifest)800 static bool IsCDMAPISupported(
801     const mozilla::dom::WidevineCDMManifest& aManifest) {
802   nsresult ignored;  // Note: ToInteger returns 0 on failure.
803   int32_t moduleVersion = aManifest.mX_cdm_module_versions.ToInteger(&ignored);
804   int32_t interfaceVersion =
805       aManifest.mX_cdm_interface_versions.ToInteger(&ignored);
806   int32_t hostVersion = aManifest.mX_cdm_host_versions.ToInteger(&ignored);
807   return ChromiumCDMAdapter::Supports(moduleVersion, interfaceVersion,
808                                       hostVersion);
809 }
810 
ParseChromiumManifest(const nsAString & aJSON)811 RefPtr<GenericPromise> GMPParent::ParseChromiumManifest(
812     const nsAString& aJSON) {
813   GMP_PARENT_LOG_DEBUG("%s: for '%s'", __FUNCTION__,
814                        NS_LossyConvertUTF16toASCII(aJSON).get());
815 
816   MOZ_ASSERT(NS_IsMainThread());
817   mozilla::dom::WidevineCDMManifest m;
818   if (!m.Init(aJSON)) {
819     GMP_PARENT_LOG_DEBUG("%s: Failed to initialize json parser, failing.",
820                          __FUNCTION__);
821     return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
822   }
823 
824   if (!IsCDMAPISupported(m)) {
825     GMP_PARENT_LOG_DEBUG("%s: CDM API not supported, failing.", __FUNCTION__);
826     return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
827   }
828 
829   CopyUTF16toUTF8(m.mName, mDisplayName);
830   CopyUTF16toUTF8(m.mDescription, mDescription);
831   CopyUTF16toUTF8(m.mVersion, mVersion);
832 
833 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
834   if (!mozilla::SandboxInfo::Get().CanSandboxMedia()) {
835     nsPrintfCString msg(
836         "GMPParent::ParseChromiumManifest: Plugin \"%s\" is an EME CDM"
837         " but this system can't sandbox it; not loading.",
838         mDisplayName.get());
839     printf_stderr("%s\n", msg.get());
840     GMP_PARENT_LOG_DEBUG("%s", msg.get());
841     return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
842   }
843 #endif
844 
845   nsCString kEMEKeySystem;
846 
847   // We hard code a few of the settings because they can't be stored in the
848   // widevine manifest without making our API different to widevine's.
849   if (mDisplayName.EqualsASCII("clearkey")) {
850     kEMEKeySystem.AssignLiteral(EME_KEY_SYSTEM_CLEARKEY);
851 #if XP_WIN
852     mLibs = nsLiteralCString(
853         "dxva2.dll, evr.dll, freebl3.dll, mfh264dec.dll, mfplat.dll, "
854         "msmpeg2vdec.dll, nss3.dll, softokn3.dll");
855 #elif XP_LINUX
856     mLibs = "libfreeblpriv3.so, libsoftokn3.so"_ns;
857 #endif
858   } else if (mDisplayName.EqualsASCII("WidevineCdm")) {
859     kEMEKeySystem.AssignLiteral(EME_KEY_SYSTEM_WIDEVINE);
860 #if XP_WIN
861     // psapi.dll added for GetMappedFileNameW, which could possibly be avoided
862     // in future versions, see bug 1383611 for details.
863     mLibs = "dxva2.dll, psapi.dll"_ns;
864 #endif
865   } else if (mDisplayName.EqualsASCII("fake")) {
866     kEMEKeySystem.AssignLiteral("fake");
867 #if XP_WIN
868     mLibs = "dxva2.dll"_ns;
869 #endif
870   } else {
871     GMP_PARENT_LOG_DEBUG("%s: Unrecognized key system: %s, failing.",
872                          __FUNCTION__, mDisplayName.get());
873     return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
874   }
875 
876 #ifdef XP_LINUX
877   // These glibc libraries were merged into libc.so.6 as of glibc
878   // 2.34; they now exist only as stub libraries for compatibility and
879   // newly linked code won't depend on them, so we need to ensure
880   // they're loaded for plugins that may have been linked against a
881   // different version of glibc.  (See also bug 1725828.)
882   if (!mDisplayName.EqualsASCII("clearkey")) {
883     if (!mLibs.IsEmpty()) {
884       mLibs.AppendLiteral(", ");
885     }
886     mLibs.AppendLiteral("libdl.so.2, libpthread.so.0, librt.so.1");
887   }
888 #endif
889 
890   GMPCapability video;
891 
892   nsCString codecsString = NS_ConvertUTF16toUTF8(m.mX_cdm_codecs);
893   nsTArray<nsCString> codecs;
894   SplitAt(",", codecsString, codecs);
895 
896   // Parse the codec strings in the manifest and map them to strings used
897   // internally by Gecko for capability recognition.
898   //
899   // Google's code to parse manifests can be used as a reference for strings
900   // the manifest may contain
901   // https://source.chromium.org/chromium/chromium/src/+/master:components/cdm/common/cdm_manifest.cc;l=74;drc=775880ced8a989191281e93854c7f2201f25068f
902   //
903   // Gecko's internal strings can be found at
904   // https://searchfox.org/mozilla-central/rev/ea63a0888d406fae720cf24f4727d87569a8cab5/dom/media/eme/MediaKeySystemAccess.cpp#149-155
905   for (const nsCString& chromiumCodec : codecs) {
906     nsCString codec;
907     if (chromiumCodec.EqualsASCII("vp8")) {
908       codec = "vp8"_ns;
909     } else if (chromiumCodec.EqualsASCII("vp9.0") ||  // Legacy string.
910                chromiumCodec.EqualsASCII("vp09")) {
911       codec = "vp9"_ns;
912     } else if (chromiumCodec.EqualsASCII("avc1")) {
913       codec = "h264"_ns;
914     } else if (chromiumCodec.EqualsASCII("av01")) {
915       codec = "av1"_ns;
916     } else {
917       GMP_PARENT_LOG_DEBUG("%s: Unrecognized codec: %s.", __FUNCTION__,
918                            chromiumCodec.get());
919       MOZ_ASSERT_UNREACHABLE(
920           "Unhandled codec string! Need to add it to the parser.");
921       continue;
922     }
923 
924     video.mAPITags.AppendElement(codec);
925   }
926 
927   video.mAPITags.AppendElement(kEMEKeySystem);
928 
929   video.mAPIName = nsLiteralCString(CHROMIUM_CDM_API);
930   mAdapter = u"chromium"_ns;
931 
932   mCapabilities.AppendElement(std::move(video));
933 
934   GMP_PARENT_LOG_DEBUG("%s: Successfully parsed manifest.", __FUNCTION__);
935   return GenericPromise::CreateAndResolve(true, __func__);
936 }
937 
CanBeSharedCrossNodeIds() const938 bool GMPParent::CanBeSharedCrossNodeIds() const {
939   return mNodeId.IsEmpty() &&
940          // XXX bug 1159300 hack -- maybe remove after openh264 1.4
941          // We don't want to use CDM decoders for non-encrypted playback
942          // just yet; especially not for WebRTC. Don't allow CDMs to be used
943          // without a node ID.
944          !mCanDecrypt;
945 }
946 
CanBeUsedFrom(const nsACString & aNodeId) const947 bool GMPParent::CanBeUsedFrom(const nsACString& aNodeId) const {
948   return mNodeId == aNodeId;
949 }
950 
SetNodeId(const nsACString & aNodeId)951 void GMPParent::SetNodeId(const nsACString& aNodeId) {
952   MOZ_ASSERT(!aNodeId.IsEmpty());
953   mNodeId = aNodeId;
954 }
955 
GetDisplayName() const956 const nsCString& GMPParent::GetDisplayName() const { return mDisplayName; }
957 
GetVersion() const958 const nsCString& GMPParent::GetVersion() const { return mVersion; }
959 
GetPluginId() const960 uint32_t GMPParent::GetPluginId() const { return mPluginId; }
961 
ResolveGetContentParentPromises()962 void GMPParent::ResolveGetContentParentPromises() {
963   nsTArray<UniquePtr<MozPromiseHolder<GetGMPContentParentPromise>>> promises =
964       std::move(mGetContentParentPromises);
965   MOZ_ASSERT(mGetContentParentPromises.IsEmpty());
966   RefPtr<GMPContentParent::CloseBlocker> blocker(
967       new GMPContentParent::CloseBlocker(mGMPContentParent));
968   for (auto& holder : promises) {
969     holder->Resolve(blocker, __func__);
970   }
971 }
972 
OpenPGMPContent()973 bool GMPParent::OpenPGMPContent() {
974   MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread());
975   MOZ_ASSERT(!mGMPContentParent);
976 
977   Endpoint<PGMPContentParent> parent;
978   Endpoint<PGMPContentChild> child;
979   if (NS_WARN_IF(NS_FAILED(PGMPContent::CreateEndpoints(
980           base::GetCurrentProcId(), OtherPid(), &parent, &child)))) {
981     return false;
982   }
983 
984   mGMPContentParent = new GMPContentParent(this);
985 
986   if (!parent.Bind(mGMPContentParent)) {
987     return false;
988   }
989 
990   if (!SendInitGMPContentChild(std::move(child))) {
991     return false;
992   }
993 
994   ResolveGetContentParentPromises();
995 
996   return true;
997 }
998 
RejectGetContentParentPromises()999 void GMPParent::RejectGetContentParentPromises() {
1000   nsTArray<UniquePtr<MozPromiseHolder<GetGMPContentParentPromise>>> promises =
1001       std::move(mGetContentParentPromises);
1002   MOZ_ASSERT(mGetContentParentPromises.IsEmpty());
1003   for (auto& holder : promises) {
1004     holder->Reject(NS_ERROR_FAILURE, __func__);
1005   }
1006 }
1007 
GetGMPContentParent(UniquePtr<MozPromiseHolder<GetGMPContentParentPromise>> && aPromiseHolder)1008 void GMPParent::GetGMPContentParent(
1009     UniquePtr<MozPromiseHolder<GetGMPContentParentPromise>>&& aPromiseHolder) {
1010   MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread());
1011   GMP_PARENT_LOG_DEBUG("%s %p", __FUNCTION__, this);
1012 
1013   if (mGMPContentParent) {
1014     RefPtr<GMPContentParent::CloseBlocker> blocker(
1015         new GMPContentParent::CloseBlocker(mGMPContentParent));
1016     aPromiseHolder->Resolve(blocker, __func__);
1017   } else {
1018     mGetContentParentPromises.AppendElement(std::move(aPromiseHolder));
1019     // If we don't have a GMPContentParent and we try to get one for the first
1020     // time (mGetContentParentPromises.Length() == 1) then call
1021     // PGMPContent::Open. If more calls to GetGMPContentParent happen before
1022     // mGMPContentParent has been set then we should just store them, so that
1023     // they get called when we set mGMPContentParent as a result of the
1024     // PGMPContent::Open call.
1025     if (mGetContentParentPromises.Length() == 1) {
1026       if (!EnsureProcessLoaded() || !OpenPGMPContent()) {
1027         RejectGetContentParentPromises();
1028         return;
1029       }
1030       // We want to increment this as soon as possible, to avoid that we'd try
1031       // to shut down the GMP process while we're still trying to get a
1032       // PGMPContentParent actor.
1033       ++mGMPContentChildCount;
1034     }
1035   }
1036 }
1037 
ForgetGMPContentParent()1038 already_AddRefed<GMPContentParent> GMPParent::ForgetGMPContentParent() {
1039   MOZ_ASSERT(mGetContentParentPromises.IsEmpty());
1040   return mGMPContentParent.forget();
1041 }
1042 
EnsureProcessLoaded(base::ProcessId * aID)1043 bool GMPParent::EnsureProcessLoaded(base::ProcessId* aID) {
1044   if (!EnsureProcessLoaded()) {
1045     return false;
1046   }
1047   *aID = OtherPid();
1048   return true;
1049 }
1050 
IncrementGMPContentChildCount()1051 void GMPParent::IncrementGMPContentChildCount() { ++mGMPContentChildCount; }
1052 
GetPluginBaseName() const1053 nsString GMPParent::GetPluginBaseName() const { return u"gmp-"_ns + mName; }
1054 
1055 #if defined(XP_MACOSX) && defined(__aarch64__)
PreTranslateBins()1056 void GMPParent::PreTranslateBins() {
1057   nsCOMPtr<nsIRunnable> event = mozilla::NewRunnableMethod(
1058       "RosettaTranslation", this, &GMPParent::PreTranslateBinsWorker);
1059 
1060   DebugOnly<nsresult> rv =
1061       NS_DispatchBackgroundTask(event.forget(), NS_DISPATCH_EVENT_MAY_BLOCK);
1062 
1063   MOZ_ASSERT(NS_SUCCEEDED(rv));
1064 }
1065 
PreTranslateBinsWorker()1066 void GMPParent::PreTranslateBinsWorker() {
1067   int rv = nsMacUtilsImpl::PreTranslateXUL();
1068   GMP_PARENT_LOG_DEBUG("%s: XUL translation result: %d", __FUNCTION__, rv);
1069 
1070   rv = nsMacUtilsImpl::PreTranslateBinary(mPluginFilePath);
1071   GMP_PARENT_LOG_DEBUG("%s: %s translation result: %d", __FUNCTION__,
1072                        mPluginFilePath.get(), rv);
1073 }
1074 #endif
1075 
1076 }  // namespace mozilla::gmp
1077 
1078 #undef GMP_PARENT_LOG_DEBUG
1079 #undef __CLASS__
1080