Coverage Report

Created: 2018-09-25 14:53

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