Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/gmp/GMPService.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 "GMPService.h"
7
#include "GMPServiceParent.h"
8
#include "GMPServiceChild.h"
9
#include "GMPContentParent.h"
10
#include "prio.h"
11
#include "mozilla/Logging.h"
12
#include "GMPParent.h"
13
#include "GMPVideoDecoderParent.h"
14
#include "nsIObserverService.h"
15
#include "GeckoChildProcessHost.h"
16
#include "mozilla/ClearOnShutdown.h"
17
#include "mozilla/SyncRunnable.h"
18
#include "nsXPCOMPrivate.h"
19
#include "mozilla/Services.h"
20
#include "nsNativeCharsetUtils.h"
21
#include "nsIXULAppInfo.h"
22
#include "nsIConsoleService.h"
23
#include "mozilla/Unused.h"
24
#include "nsComponentManagerUtils.h"
25
#include "runnable_utils.h"
26
#include "VideoUtils.h"
27
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
28
#include "mozilla/SandboxInfo.h"
29
#endif
30
#include "nsAppDirectoryServiceDefs.h"
31
#include "nsDirectoryServiceUtils.h"
32
#include "nsDirectoryServiceDefs.h"
33
#include "nsHashKeys.h"
34
#include "nsIFile.h"
35
#include "nsISimpleEnumerator.h"
36
#include "nsThreadUtils.h"
37
#include "GMPCrashHelper.h"
38
39
#include "MediaResult.h"
40
#include "mozilla/dom/PluginCrashedEvent.h"
41
#include "mozilla/EventDispatcher.h"
42
#include "mozilla/Attributes.h"
43
#include "mozilla/SystemGroup.h"
44
45
namespace mozilla {
46
47
#ifdef LOG
48
#undef LOG
49
#endif
50
51
LogModule*
52
GetGMPLog()
53
0
{
54
0
  static LazyLogModule sLog("GMP");
55
0
  return sLog;
56
0
}
57
58
0
#define LOGD(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Debug, msg)
59
#define LOG(level, msg) MOZ_LOG(GetGMPLog(), (level), msg)
60
61
#ifdef __CLASS__
62
#undef __CLASS__
63
#endif
64
0
#define __CLASS__ "GMPService"
65
66
namespace gmp {
67
68
static StaticRefPtr<GeckoMediaPluginService> sSingletonService;
69
70
class GMPServiceCreateHelper final : public mozilla::Runnable
71
{
72
  RefPtr<GeckoMediaPluginService> mService;
73
74
public:
75
  static already_AddRefed<GeckoMediaPluginService>
76
  GetOrCreate()
77
0
  {
78
0
    RefPtr<GeckoMediaPluginService> service;
79
0
80
0
    if (NS_IsMainThread()) {
81
0
      service = GetOrCreateOnMainThread();
82
0
    } else {
83
0
      RefPtr<GMPServiceCreateHelper> createHelper = new GMPServiceCreateHelper();
84
0
85
0
      mozilla::SyncRunnable::DispatchToThread(
86
0
        SystemGroup::EventTargetFor(mozilla::TaskCategory::Other),
87
0
        createHelper, true);
88
0
89
0
      service = createHelper->mService.forget();
90
0
    }
91
0
92
0
    return service.forget();
93
0
  }
94
95
private:
96
  GMPServiceCreateHelper()
97
    : Runnable("GMPServiceCreateHelper")
98
0
  {
99
0
  }
100
101
  ~GMPServiceCreateHelper()
102
0
  {
103
0
    MOZ_ASSERT(!mService);
104
0
  }
105
106
  static already_AddRefed<GeckoMediaPluginService>
107
  GetOrCreateOnMainThread()
108
0
  {
109
0
    MOZ_ASSERT(NS_IsMainThread());
110
0
111
0
    if (!sSingletonService) {
112
0
      if (XRE_IsParentProcess()) {
113
0
        RefPtr<GeckoMediaPluginServiceParent> service =
114
0
          new GeckoMediaPluginServiceParent();
115
0
        service->Init();
116
0
        sSingletonService = service;
117
0
      } else {
118
0
        RefPtr<GeckoMediaPluginServiceChild> service =
119
0
          new GeckoMediaPluginServiceChild();
120
0
        service->Init();
121
0
        sSingletonService = service;
122
0
      }
123
0
124
0
      ClearOnShutdown(&sSingletonService);
125
0
    }
126
0
127
0
    RefPtr<GeckoMediaPluginService> service = sSingletonService.get();
128
0
    return service.forget();
129
0
  }
130
131
  NS_IMETHOD
132
  Run() override
133
0
  {
134
0
    MOZ_ASSERT(NS_IsMainThread());
135
0
136
0
    mService = GetOrCreateOnMainThread();
137
0
    return NS_OK;
138
0
  }
139
};
140
141
already_AddRefed<GeckoMediaPluginService>
142
GeckoMediaPluginService::GetGeckoMediaPluginService()
143
0
{
144
0
  return GMPServiceCreateHelper::GetOrCreate();
145
0
}
146
147
NS_IMPL_ISUPPORTS(GeckoMediaPluginService, mozIGeckoMediaPluginService, nsIObserver)
148
149
GeckoMediaPluginService::GeckoMediaPluginService()
150
  : mMutex("GeckoMediaPluginService::mMutex")
151
  , mGMPThreadShutdown(false)
152
  , mShuttingDownOnGMPThread(false)
153
  , mXPCOMWillShutdown(false)
154
0
{
155
0
  MOZ_ASSERT(NS_IsMainThread());
156
0
157
0
  nsCOMPtr<nsIXULAppInfo> appInfo = do_GetService("@mozilla.org/xre/app-info;1");
158
0
  if (appInfo) {
159
0
    nsAutoCString version;
160
0
    nsAutoCString buildID;
161
0
    if (NS_SUCCEEDED(appInfo->GetVersion(version)) &&
162
0
        NS_SUCCEEDED(appInfo->GetAppBuildID(buildID))) {
163
0
      LOGD(("GeckoMediaPluginService created; Gecko version=%s buildID=%s",
164
0
            version.get(), buildID.get()));
165
0
    }
166
0
  }
167
0
}
168
169
GeckoMediaPluginService::~GeckoMediaPluginService()
170
0
{
171
0
}
172
173
NS_IMETHODIMP
174
GeckoMediaPluginService::RunPluginCrashCallbacks(uint32_t aPluginId,
175
                                                 const nsACString& aPluginName)
176
0
{
177
0
  MOZ_ASSERT(NS_IsMainThread());
178
0
  LOGD(("%s::%s(%i)", __CLASS__, __FUNCTION__, aPluginId));
179
0
180
0
  nsAutoPtr<nsTArray<RefPtr<GMPCrashHelper>>> helpers;
181
0
  {
182
0
    MutexAutoLock lock(mMutex);
183
0
    mPluginCrashHelpers.Remove(aPluginId, &helpers);
184
0
  }
185
0
  if (!helpers) {
186
0
    LOGD(("%s::%s(%i) No crash helpers, not handling crash.", __CLASS__, __FUNCTION__, aPluginId));
187
0
    return NS_OK;
188
0
  }
189
0
190
0
  for (const auto& helper : *helpers) {
191
0
    nsCOMPtr<nsPIDOMWindowInner> window = helper->GetPluginCrashedEventTarget();
192
0
    if (NS_WARN_IF(!window)) {
193
0
      continue;
194
0
    }
195
0
    nsCOMPtr<nsIDocument> document(window->GetExtantDoc());
196
0
    if (NS_WARN_IF(!document)) {
197
0
      continue;
198
0
    }
199
0
200
0
    dom::PluginCrashedEventInit init;
201
0
    init.mPluginID = aPluginId;
202
0
    init.mBubbles = true;
203
0
    init.mCancelable = true;
204
0
    init.mGmpPlugin = true;
205
0
    CopyUTF8toUTF16(aPluginName, init.mPluginName);
206
0
    init.mSubmittedCrashReport = false;
207
0
    RefPtr<dom::PluginCrashedEvent> event =
208
0
      dom::PluginCrashedEvent::Constructor(document,
209
0
                                           NS_LITERAL_STRING("PluginCrashed"),
210
0
                                           init);
211
0
    event->SetTrusted(true);
212
0
    event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
213
0
214
0
    EventDispatcher::DispatchDOMEvent(window, nullptr, event, nullptr, nullptr);
215
0
  }
216
0
217
0
  return NS_OK;
218
0
}
219
220
nsresult
221
GeckoMediaPluginService::Init()
222
0
{
223
0
  MOZ_ASSERT(NS_IsMainThread());
224
0
225
0
  nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
226
0
  MOZ_ASSERT(obsService);
227
0
  MOZ_ALWAYS_SUCCEEDS(obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false));
228
0
  MOZ_ALWAYS_SUCCEEDS(
229
0
    obsService->AddObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false));
230
0
231
0
  // Kick off scanning for plugins
232
0
  nsCOMPtr<nsIThread> thread;
233
0
  return GetThread(getter_AddRefs(thread));
234
0
}
235
236
RefPtr<GetCDMParentPromise>
237
GeckoMediaPluginService::GetCDM(const NodeId& aNodeId,
238
                                nsTArray<nsCString> aTags,
239
                                GMPCrashHelper* aHelper)
240
0
{
241
0
  MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread());
242
0
243
0
  if (mShuttingDownOnGMPThread || aTags.IsEmpty()) {
244
0
    nsPrintfCString reason("%s::%s failed, aTags.IsEmpty() = %d, mShuttingDownOnGMPThread = %d.",
245
0
      __CLASS__, __FUNCTION__, aTags.IsEmpty(), mShuttingDownOnGMPThread);
246
0
    return GetCDMParentPromise::CreateAndReject(MediaResult(NS_ERROR_FAILURE, reason.get()), __func__);
247
0
  }
248
0
249
0
  typedef MozPromiseHolder<GetCDMParentPromise> PromiseHolder;
250
0
  PromiseHolder* rawHolder(new PromiseHolder());
251
0
  RefPtr<GetCDMParentPromise> promise = rawHolder->Ensure(__func__);
252
0
  RefPtr<AbstractThread> thread(GetAbstractGMPThread());
253
0
  RefPtr<GMPCrashHelper> helper(aHelper);
254
0
  GetContentParent(
255
0
    aHelper, aNodeId, NS_LITERAL_CSTRING(CHROMIUM_CDM_API), aTags)
256
0
    ->Then(thread,
257
0
           __func__,
258
0
           [rawHolder, helper](RefPtr<GMPContentParent::CloseBlocker> wrapper) {
259
0
             RefPtr<GMPContentParent> parent = wrapper->mParent;
260
0
             UniquePtr<PromiseHolder> holder(rawHolder);
261
0
             RefPtr<ChromiumCDMParent> cdm = parent->GetChromiumCDM();
262
0
             if (!parent) {
263
0
               nsPrintfCString reason(
264
0
                 "%s::%s failed since GetChromiumCDM returns nullptr.",
265
0
                 __CLASS__, __FUNCTION__);
266
0
               holder->Reject(MediaResult(NS_ERROR_FAILURE, reason.get()), __func__);
267
0
               return;
268
0
             }
269
0
             if (helper) {
270
0
               cdm->SetCrashHelper(helper);
271
0
             }
272
0
             holder->Resolve(cdm, __func__);
273
0
           },
274
0
           [rawHolder](MediaResult result) {
275
0
             nsPrintfCString reason(
276
0
               "%s::%s failed since GetContentParent rejects the promise with reason %s.",
277
0
               __CLASS__, __FUNCTION__, result.Description().get());
278
0
             UniquePtr<PromiseHolder> holder(rawHolder);
279
0
             holder->Reject(MediaResult(NS_ERROR_FAILURE, reason.get()), __func__);
280
0
           });
281
0
282
0
  return promise;
283
0
}
284
285
void
286
GeckoMediaPluginService::ShutdownGMPThread()
287
0
{
288
0
  LOGD(("%s::%s", __CLASS__, __FUNCTION__));
289
0
  nsCOMPtr<nsIThread> gmpThread;
290
0
  {
291
0
    MutexAutoLock lock(mMutex);
292
0
    mGMPThreadShutdown = true;
293
0
    mGMPThread.swap(gmpThread);
294
0
    mAbstractGMPThread = nullptr;
295
0
  }
296
0
297
0
  if (gmpThread) {
298
0
    gmpThread->Shutdown();
299
0
  }
300
0
}
301
302
nsresult
303
GeckoMediaPluginService::GMPDispatch(nsIRunnable* event,
304
                                     uint32_t flags)
305
0
{
306
0
  nsCOMPtr<nsIRunnable> r(event);
307
0
  return GMPDispatch(r.forget());
308
0
}
309
310
nsresult
311
GeckoMediaPluginService::GMPDispatch(already_AddRefed<nsIRunnable> event,
312
                                     uint32_t flags)
313
0
{
314
0
  nsCOMPtr<nsIRunnable> r(event);
315
0
  nsCOMPtr<nsIThread> thread;
316
0
  nsresult rv = GetThread(getter_AddRefs(thread));
317
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
318
0
    return rv;
319
0
  }
320
0
  return thread->Dispatch(r, flags);
321
0
}
322
323
// always call with getter_AddRefs, because it does
324
NS_IMETHODIMP
325
GeckoMediaPluginService::GetThread(nsIThread** aThread)
326
0
{
327
0
  MOZ_ASSERT(aThread);
328
0
329
0
  // This can be called from any thread.
330
0
  MutexAutoLock lock(mMutex);
331
0
332
0
  if (!mGMPThread) {
333
0
    // Don't allow the thread to be created after shutdown has started.
334
0
    if (mGMPThreadShutdown) {
335
0
      return NS_ERROR_FAILURE;
336
0
    }
337
0
338
0
    nsresult rv = NS_NewNamedThread("GMPThread", getter_AddRefs(mGMPThread));
339
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
340
0
      return rv;
341
0
    }
342
0
343
0
    mAbstractGMPThread = AbstractThread::CreateXPCOMThreadWrapper(mGMPThread, false);
344
0
345
0
    // Tell the thread to initialize plugins
346
0
    InitializePlugins(mAbstractGMPThread.get());
347
0
  }
348
0
349
0
  nsCOMPtr<nsIThread> copy = mGMPThread;
350
0
  copy.forget(aThread);
351
0
352
0
  return NS_OK;
353
0
}
354
355
RefPtr<AbstractThread>
356
GeckoMediaPluginService::GetAbstractGMPThread()
357
0
{
358
0
  MutexAutoLock lock(mMutex);
359
0
  return mAbstractGMPThread;
360
0
}
361
362
NS_IMETHODIMP
363
GeckoMediaPluginService::GetDecryptingGMPVideoDecoder(GMPCrashHelper* aHelper,
364
                                                      nsTArray<nsCString>* aTags,
365
                                                      const nsACString& aNodeId,
366
                                                      UniquePtr<GetGMPVideoDecoderCallback>&& aCallback,
367
                                                      uint32_t aDecryptorId)
368
0
{
369
0
  MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread());
370
0
  NS_ENSURE_ARG(aTags && aTags->Length() > 0);
371
0
  NS_ENSURE_ARG(aCallback);
372
0
373
0
  if (mShuttingDownOnGMPThread) {
374
0
    return NS_ERROR_FAILURE;
375
0
  }
376
0
377
0
  GetGMPVideoDecoderCallback* rawCallback = aCallback.release();
378
0
  RefPtr<AbstractThread> thread(GetAbstractGMPThread());
379
0
  RefPtr<GMPCrashHelper> helper(aHelper);
380
0
  GetContentParent(aHelper, aNodeId, NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER), *aTags)
381
0
    ->Then(thread, __func__,
382
0
      [rawCallback, helper, aDecryptorId](RefPtr<GMPContentParent::CloseBlocker> wrapper) {
383
0
        RefPtr<GMPContentParent> parent = wrapper->mParent;
384
0
        UniquePtr<GetGMPVideoDecoderCallback> callback(rawCallback);
385
0
        GMPVideoDecoderParent* actor = nullptr;
386
0
        GMPVideoHostImpl* host = nullptr;
387
0
        if (parent && NS_SUCCEEDED(parent->GetGMPVideoDecoder(&actor, aDecryptorId))) {
388
0
          host = &(actor->Host());
389
0
          actor->SetCrashHelper(helper);
390
0
        }
391
0
        callback->Done(actor, host);
392
0
      },
393
0
      [rawCallback] {
394
0
        UniquePtr<GetGMPVideoDecoderCallback> callback(rawCallback);
395
0
        callback->Done(nullptr, nullptr);
396
0
      });
397
0
398
0
  return NS_OK;
399
0
}
400
401
NS_IMETHODIMP
402
GeckoMediaPluginService::GetGMPVideoEncoder(GMPCrashHelper* aHelper,
403
                                            nsTArray<nsCString>* aTags,
404
                                            const nsACString& aNodeId,
405
                                            UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
406
0
{
407
0
  MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread());
408
0
  NS_ENSURE_ARG(aTags && aTags->Length() > 0);
409
0
  NS_ENSURE_ARG(aCallback);
410
0
411
0
  if (mShuttingDownOnGMPThread) {
412
0
    return NS_ERROR_FAILURE;
413
0
  }
414
0
415
0
  GetGMPVideoEncoderCallback* rawCallback = aCallback.release();
416
0
  RefPtr<AbstractThread> thread(GetAbstractGMPThread());
417
0
  RefPtr<GMPCrashHelper> helper(aHelper);
418
0
  GetContentParent(aHelper, aNodeId, NS_LITERAL_CSTRING(GMP_API_VIDEO_ENCODER), *aTags)
419
0
    ->Then(thread, __func__,
420
0
      [rawCallback, helper](RefPtr<GMPContentParent::CloseBlocker> wrapper) {
421
0
        RefPtr<GMPContentParent> parent = wrapper->mParent;
422
0
        UniquePtr<GetGMPVideoEncoderCallback> callback(rawCallback);
423
0
        GMPVideoEncoderParent* actor = nullptr;
424
0
        GMPVideoHostImpl* host = nullptr;
425
0
        if (parent && NS_SUCCEEDED(parent->GetGMPVideoEncoder(&actor))) {
426
0
          host = &(actor->Host());
427
0
          actor->SetCrashHelper(helper);
428
0
        }
429
0
        callback->Done(actor, host);
430
0
      },
431
0
      [rawCallback] {
432
0
        UniquePtr<GetGMPVideoEncoderCallback> callback(rawCallback);
433
0
        callback->Done(nullptr, nullptr);
434
0
      });
435
0
436
0
  return NS_OK;
437
0
}
438
439
void
440
GeckoMediaPluginService::ConnectCrashHelper(uint32_t aPluginId, GMPCrashHelper* aHelper)
441
0
{
442
0
  if (!aHelper) {
443
0
    return;
444
0
  }
445
0
  MutexAutoLock lock(mMutex);
446
0
  nsTArray<RefPtr<GMPCrashHelper>>* helpers;
447
0
  if (!mPluginCrashHelpers.Get(aPluginId, &helpers)) {
448
0
    helpers = new nsTArray<RefPtr<GMPCrashHelper>>();
449
0
    mPluginCrashHelpers.Put(aPluginId, helpers);
450
0
  } else if (helpers->Contains(aHelper)) {
451
0
    return;
452
0
  }
453
0
  helpers->AppendElement(aHelper);
454
0
}
455
456
void GeckoMediaPluginService::DisconnectCrashHelper(GMPCrashHelper* aHelper)
457
0
{
458
0
  if (!aHelper) {
459
0
    return;
460
0
  }
461
0
  MutexAutoLock lock(mMutex);
462
0
  for (auto iter = mPluginCrashHelpers.Iter(); !iter.Done(); iter.Next()) {
463
0
    nsTArray<RefPtr<GMPCrashHelper>>* helpers = iter.Data();
464
0
    if (!helpers->Contains(aHelper)) {
465
0
      continue;
466
0
    }
467
0
    helpers->RemoveElement(aHelper);
468
0
    MOZ_ASSERT(!helpers->Contains(aHelper)); // Ensure there aren't duplicates.
469
0
    if (helpers->IsEmpty()) {
470
0
      iter.Remove();
471
0
    }
472
0
  }
473
0
}
474
475
} // namespace gmp
476
} // namespace mozilla