Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/gmp/GMPServiceParent.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 "GMPServiceParent.h"
7
#include "GMPService.h"
8
#include "prio.h"
9
#include "base/task.h"
10
#include "mozilla/AbstractThread.h"
11
#include "mozilla/Logging.h"
12
#include "mozilla/StaticPrefs.h"
13
#include "mozilla/dom/ContentParent.h"
14
#include "GMPParent.h"
15
#include "GMPVideoDecoderParent.h"
16
#include "nsAutoPtr.h"
17
#include "nsIObserverService.h"
18
#include "GeckoChildProcessHost.h"
19
#include "mozilla/Preferences.h"
20
#include "mozilla/ClearOnShutdown.h"
21
#include "mozilla/SyncRunnable.h"
22
#include "nsXPCOMPrivate.h"
23
#include "mozilla/Services.h"
24
#include "nsNativeCharsetUtils.h"
25
#include "nsIConsoleService.h"
26
#include "mozilla/Unused.h"
27
#include "nsComponentManagerUtils.h"
28
#include "runnable_utils.h"
29
#include "VideoUtils.h"
30
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
31
#include "mozilla/SandboxInfo.h"
32
#endif
33
#include "nsAppDirectoryServiceDefs.h"
34
#include "nsDirectoryServiceUtils.h"
35
#include "nsDirectoryServiceDefs.h"
36
#include "nsHashKeys.h"
37
#include "nsIFile.h"
38
#include "nsISimpleEnumerator.h"
39
#include "nsIXULRuntime.h"
40
#include "GMPDecoderModule.h"
41
#include <limits>
42
#include "mozilla/SystemGroup.h"
43
44
using mozilla::ipc::Transport;
45
46
namespace mozilla {
47
48
#ifdef LOG
49
#undef LOG
50
#endif
51
52
0
#define LOGD(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Debug, msg)
53
#define LOG(level, msg) MOZ_LOG(GetGMPLog(), (level), msg)
54
55
#ifdef __CLASS__
56
#undef __CLASS__
57
#endif
58
#define __CLASS__ "GMPService"
59
60
#define NS_DispatchToMainThread(...) CompileError_UseAbstractMainThreadInstead
61
62
namespace gmp {
63
64
static const uint32_t NodeIdSaltLength = 32;
65
66
already_AddRefed<GeckoMediaPluginServiceParent>
67
GeckoMediaPluginServiceParent::GetSingleton()
68
0
{
69
0
  MOZ_ASSERT(XRE_IsParentProcess());
70
0
  RefPtr<GeckoMediaPluginService> service(
71
0
    GeckoMediaPluginServiceParent::GetGeckoMediaPluginService());
72
#ifdef DEBUG
73
  if (service) {
74
    nsCOMPtr<mozIGeckoMediaPluginChromeService> chromeService;
75
    CallQueryInterface(service.get(), getter_AddRefs(chromeService));
76
    MOZ_ASSERT(chromeService);
77
  }
78
#endif
79
  return service.forget().downcast<GeckoMediaPluginServiceParent>();
80
0
}
81
82
NS_IMPL_ISUPPORTS_INHERITED(GeckoMediaPluginServiceParent,
83
                            GeckoMediaPluginService,
84
                            mozIGeckoMediaPluginChromeService,
85
                            nsIAsyncShutdownBlocker)
86
87
GeckoMediaPluginServiceParent::GeckoMediaPluginServiceParent()
88
  : mShuttingDown(false)
89
  , mScannedPluginOnDisk(false)
90
  , mWaitingForPluginsSyncShutdown(false)
91
  , mInitPromiseMonitor("GeckoMediaPluginServiceParent::mInitPromiseMonitor")
92
  , mLoadPluginsFromDiskComplete(false)
93
  , mMainThread(SystemGroup::AbstractMainThreadFor(TaskCategory::Other))
94
0
{
95
0
  MOZ_ASSERT(NS_IsMainThread());
96
0
  mInitPromise.SetMonitor(&mInitPromiseMonitor);
97
0
}
98
99
GeckoMediaPluginServiceParent::~GeckoMediaPluginServiceParent()
100
0
{
101
0
  MOZ_ASSERT(mPlugins.IsEmpty());
102
0
}
103
104
nsresult
105
GeckoMediaPluginServiceParent::Init()
106
0
{
107
0
  MOZ_ASSERT(NS_IsMainThread());
108
0
109
0
  nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
110
0
  MOZ_ASSERT(obsService);
111
0
  MOZ_ALWAYS_SUCCEEDS(obsService->AddObserver(this, "profile-change-teardown", false));
112
0
  MOZ_ALWAYS_SUCCEEDS(obsService->AddObserver(this, "last-pb-context-exited", false));
113
0
  MOZ_ALWAYS_SUCCEEDS(obsService->AddObserver(this, "browser:purge-session-history", false));
114
0
115
#ifdef DEBUG
116
  MOZ_ALWAYS_SUCCEEDS(obsService->AddObserver(this, "mediakeys-request", false));
117
#endif
118
119
0
  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
120
0
  if (prefs) {
121
0
    prefs->AddObserver("media.gmp.plugin.crash", this, false);
122
0
  }
123
0
124
0
  nsresult rv = InitStorage();
125
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
126
0
    return rv;
127
0
  }
128
0
129
0
  // Kick off scanning for plugins
130
0
  nsCOMPtr<nsIThread> thread;
131
0
  rv = GetThread(getter_AddRefs(thread));
132
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
133
0
    return rv;
134
0
  }
135
0
136
0
  // Detect if GMP storage has an incompatible version, and if so nuke it.
137
0
  int32_t version = Preferences::GetInt("media.gmp.storage.version.observed", 0);
138
0
  int32_t expected = Preferences::GetInt("media.gmp.storage.version.expected", 0);
139
0
  if (version != expected) {
140
0
    Preferences::SetInt("media.gmp.storage.version.observed", expected);
141
0
    return GMPDispatch(
142
0
      NewRunnableMethod("gmp::GeckoMediaPluginServiceParent::ClearStorage",
143
0
                        this,
144
0
                        &GeckoMediaPluginServiceParent::ClearStorage));
145
0
  }
146
0
  return NS_OK;
147
0
}
148
149
already_AddRefed<nsIFile>
150
CloneAndAppend(nsIFile* aFile, const nsAString& aDir)
151
0
{
152
0
  nsCOMPtr<nsIFile> f;
153
0
  nsresult rv = aFile->Clone(getter_AddRefs(f));
154
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
155
0
    return nullptr;
156
0
  }
157
0
158
0
  rv = f->Append(aDir);
159
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
160
0
    return nullptr;
161
0
  }
162
0
  return f.forget();
163
0
}
164
165
static nsresult
166
GMPPlatformString(nsAString& aOutPlatform)
167
0
{
168
0
  // Append the OS and arch so that we don't reuse the storage if the profile is
169
0
  // copied or used under a different bit-ness, or copied to another platform.
170
0
  nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
171
0
  if (!runtime) {
172
0
    return NS_ERROR_FAILURE;
173
0
  }
174
0
175
0
  nsAutoCString OS;
176
0
  nsresult rv = runtime->GetOS(OS);
177
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
178
0
    return rv;
179
0
  }
180
0
181
0
  nsAutoCString arch;
182
0
  rv = runtime->GetXPCOMABI(arch);
183
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
184
0
    return rv;
185
0
  }
186
0
187
0
  nsCString platform;
188
0
  platform.Append(OS);
189
0
  platform.AppendLiteral("_");
190
0
  platform.Append(arch);
191
0
192
0
  aOutPlatform = NS_ConvertUTF8toUTF16(platform);
193
0
194
0
  return NS_OK;
195
0
}
196
197
nsresult
198
GeckoMediaPluginServiceParent::InitStorage()
199
0
{
200
0
  MOZ_ASSERT(NS_IsMainThread());
201
0
202
0
  // GMP storage should be used in the chrome process only.
203
0
  if (!XRE_IsParentProcess()) {
204
0
    return NS_OK;
205
0
  }
206
0
207
0
  // Directory service is main thread only, so cache the profile dir here
208
0
  // so that we can use it off main thread.
209
0
  nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mStorageBaseDir));
210
0
211
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
212
0
    return rv;
213
0
  }
214
0
215
0
  rv = mStorageBaseDir->AppendNative(NS_LITERAL_CSTRING("gmp"));
216
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
217
0
    return rv;
218
0
  }
219
0
220
0
  rv = mStorageBaseDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
221
0
  if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)) {
222
0
    return rv;
223
0
  }
224
0
225
0
  nsCOMPtr<nsIFile> gmpDirWithoutPlatform;
226
0
  rv = mStorageBaseDir->Clone(getter_AddRefs(gmpDirWithoutPlatform));
227
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
228
0
    return rv;
229
0
  }
230
0
231
0
  nsAutoString platform;
232
0
  rv = GMPPlatformString(platform);
233
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
234
0
    return rv;
235
0
  }
236
0
237
0
  rv = mStorageBaseDir->Append(platform);
238
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
239
0
    return rv;
240
0
  }
241
0
242
0
  rv = mStorageBaseDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
243
0
  if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)) {
244
0
    return rv;
245
0
  }
246
0
247
0
  return GeckoMediaPluginService::Init();
248
0
}
249
250
NS_IMETHODIMP
251
GeckoMediaPluginServiceParent::Observe(nsISupports* aSubject,
252
                                       const char* aTopic,
253
                                       const char16_t* aSomeData)
254
0
{
255
0
  LOGD(("%s::%s topic='%s' data='%s'", __CLASS__, __FUNCTION__,
256
0
       aTopic, NS_ConvertUTF16toUTF8(aSomeData).get()));
257
0
  if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
258
0
    nsCOMPtr<nsIPrefBranch> branch( do_QueryInterface(aSubject) );
259
0
    if (branch) {
260
0
      bool crashNow = false;
261
0
      if (NS_LITERAL_STRING("media.gmp.plugin.crash").Equals(aSomeData)) {
262
0
        branch->GetBoolPref("media.gmp.plugin.crash",  &crashNow);
263
0
      }
264
0
      if (crashNow) {
265
0
        nsCOMPtr<nsIThread> gmpThread;
266
0
        {
267
0
          MutexAutoLock lock(mMutex);
268
0
          gmpThread = mGMPThread;
269
0
        }
270
0
        if (gmpThread) {
271
0
          // Note: the GeckoMediaPluginServiceParent singleton is kept alive by a
272
0
          // static refptr that is only cleared in the final stage of
273
0
          // shutdown after everything else is shutdown, so this RefPtr<> is not
274
0
          // strictly necessary so long as that is true, but it's safer.
275
0
          gmpThread->Dispatch(WrapRunnable(RefPtr<GeckoMediaPluginServiceParent>(this),
276
0
                                           &GeckoMediaPluginServiceParent::CrashPlugins),
277
0
                              NS_DISPATCH_NORMAL);
278
0
        }
279
0
      }
280
0
    }
281
0
  } else if (!strcmp("profile-change-teardown", aTopic)) {
282
0
    mWaitingForPluginsSyncShutdown = true;
283
0
284
0
    nsCOMPtr<nsIThread> gmpThread;
285
0
    {
286
0
      MutexAutoLock lock(mMutex);
287
0
      MOZ_ASSERT(!mShuttingDown);
288
0
      mShuttingDown = true;
289
0
      gmpThread = mGMPThread;
290
0
    }
291
0
292
0
    if (gmpThread) {
293
0
      LOGD(("%s::%s Starting to unload plugins, waiting for sync shutdown..."
294
0
            , __CLASS__, __FUNCTION__));
295
0
      gmpThread->Dispatch(
296
0
        NewRunnableMethod("gmp::GeckoMediaPluginServiceParent::UnloadPlugins",
297
0
                          this,
298
0
                          &GeckoMediaPluginServiceParent::UnloadPlugins),
299
0
        NS_DISPATCH_NORMAL);
300
0
301
0
      // Wait for UnloadPlugins() to do sync shutdown...
302
0
      SpinEventLoopUntil([&]() { return !mWaitingForPluginsSyncShutdown; });
303
0
    } else {
304
0
      // GMP thread has already shutdown.
305
0
      MOZ_ASSERT(mPlugins.IsEmpty());
306
0
      mWaitingForPluginsSyncShutdown = false;
307
0
    }
308
0
309
0
  } else if (!strcmp(NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, aTopic)) {
310
0
    MOZ_ASSERT(mShuttingDown);
311
0
    ShutdownGMPThread();
312
0
  } else if (!strcmp(NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, aTopic)) {
313
0
    mXPCOMWillShutdown = true;
314
0
  } else if (!strcmp("last-pb-context-exited", aTopic)) {
315
0
    // When Private Browsing mode exits, all we need to do is clear
316
0
    // mTempNodeIds. This drops all the node ids we've cached in memory
317
0
    // for PB origin-pairs. If we try to open an origin-pair for non-PB
318
0
    // mode, we'll get the NodeId salt stored on-disk, and if we try to
319
0
    // open a PB mode origin-pair, we'll re-generate new salt.
320
0
    mTempNodeIds.Clear();
321
0
  } else if (!strcmp("browser:purge-session-history", aTopic)) {
322
0
    // Clear everything!
323
0
    if (!aSomeData || nsDependentString(aSomeData).IsEmpty()) {
324
0
      return GMPDispatch(
325
0
        NewRunnableMethod("gmp::GeckoMediaPluginServiceParent::ClearStorage",
326
0
                          this,
327
0
                          &GeckoMediaPluginServiceParent::ClearStorage));
328
0
    }
329
0
330
0
    // Clear nodeIds/records modified after |t|.
331
0
    nsresult rv;
332
0
    PRTime t = nsDependentString(aSomeData).ToInteger64(&rv, 10);
333
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
334
0
      return rv;
335
0
    }
336
0
    return GMPDispatch(NewRunnableMethod<PRTime>(
337
0
      "gmp::GeckoMediaPluginServiceParent::ClearRecentHistoryOnGMPThread",
338
0
      this,
339
0
      &GeckoMediaPluginServiceParent::ClearRecentHistoryOnGMPThread,
340
0
      t));
341
0
  }
342
0
343
0
  return NS_OK;
344
0
}
345
346
RefPtr<GenericPromise>
347
0
GeckoMediaPluginServiceParent::EnsureInitialized() {
348
0
  MonitorAutoLock lock(mInitPromiseMonitor);
349
0
  if (mLoadPluginsFromDiskComplete) {
350
0
    return GenericPromise::CreateAndResolve(true, __func__);
351
0
  }
352
0
  // We should have an init promise in flight.
353
0
  MOZ_ASSERT(!mInitPromise.IsEmpty());
354
0
  return mInitPromise.Ensure(__func__);
355
0
}
356
357
RefPtr<GetGMPContentParentPromise>
358
GeckoMediaPluginServiceParent::GetContentParent(
359
  GMPCrashHelper* aHelper,
360
  const nsACString& aNodeIdString,
361
  const nsCString& aAPI,
362
  const nsTArray<nsCString>& aTags)
363
0
{
364
0
  RefPtr<AbstractThread> thread(GetAbstractGMPThread());
365
0
  if (!thread) {
366
0
    return GetGMPContentParentPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
367
0
  }
368
0
369
0
  auto holder = MakeUnique<MozPromiseHolder<GetGMPContentParentPromise>>();
370
0
  RefPtr<GetGMPContentParentPromise> promise = holder->Ensure(__func__);
371
0
  EnsureInitialized()->Then(thread, __func__, [
372
0
    self = RefPtr<GeckoMediaPluginServiceParent>(this),
373
0
    nodeIdString = nsCString(aNodeIdString),
374
0
    api = nsCString(aAPI),
375
0
    tags = nsTArray<nsCString>(aTags),
376
0
    helper = RefPtr<GMPCrashHelper>(aHelper),
377
0
    holder = std::move(holder)
378
0
  ](const GenericPromise::ResolveOrRejectValue& aValue) mutable -> void {
379
0
    if (aValue.IsReject()) {
380
0
      NS_WARNING("GMPService::EnsureInitialized failed.");
381
0
      holder->Reject(NS_ERROR_FAILURE, __func__);
382
0
      return;
383
0
    }
384
0
    RefPtr<GMPParent> gmp = self->SelectPluginForAPI(nodeIdString, api, tags);
385
0
    LOGD(("%s: %p returning %p for api %s",
386
0
          __FUNCTION__,
387
0
          self.get(),
388
0
          gmp.get(),
389
0
          api.get()));
390
0
    if (!gmp) {
391
0
      NS_WARNING("GeckoMediaPluginServiceParent::GetContentParentFrom failed");
392
0
      holder->Reject(NS_ERROR_FAILURE, __func__);
393
0
      return;
394
0
    }
395
0
    self->ConnectCrashHelper(gmp->GetPluginId(), helper);
396
0
    gmp->GetGMPContentParent(std::move(holder));
397
0
  });
398
0
399
0
  return promise;
400
0
}
401
402
RefPtr<GetGMPContentParentPromise>
403
GeckoMediaPluginServiceParent::GetContentParent(
404
  GMPCrashHelper* aHelper,
405
  const NodeId& aNodeId,
406
  const nsCString& aAPI,
407
  const nsTArray<nsCString>& aTags)
408
0
{
409
0
  MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread());
410
0
411
0
  nsCString nodeIdString;
412
0
  nsresult rv = GetNodeId(
413
0
    aNodeId.mOrigin, aNodeId.mTopLevelOrigin, aNodeId.mGMPName, nodeIdString);
414
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
415
0
    return GetGMPContentParentPromise::CreateAndReject(NS_ERROR_FAILURE,
416
0
                                                       __func__);
417
0
  }
418
0
  return GetContentParent(aHelper, nodeIdString, aAPI, aTags);
419
0
}
420
421
void
422
GeckoMediaPluginServiceParent::InitializePlugins(
423
  AbstractThread* aAbstractGMPThread)
424
0
{
425
0
  MOZ_ASSERT(aAbstractGMPThread);
426
0
  MonitorAutoLock lock(mInitPromiseMonitor);
427
0
  if (mLoadPluginsFromDiskComplete) {
428
0
    return;
429
0
  }
430
0
431
0
  RefPtr<GeckoMediaPluginServiceParent> self(this);
432
0
  RefPtr<GenericPromise> p = mInitPromise.Ensure(__func__);
433
0
  InvokeAsync(aAbstractGMPThread, this, __func__,
434
0
              &GeckoMediaPluginServiceParent::LoadFromEnvironment)
435
0
    ->Then(aAbstractGMPThread, __func__,
436
0
      [self]() -> void {
437
0
        MonitorAutoLock lock(self->mInitPromiseMonitor);
438
0
        self->mLoadPluginsFromDiskComplete = true;
439
0
        self->mInitPromise.Resolve(true, __func__);
440
0
      },
441
0
      [self]() -> void {
442
0
        MonitorAutoLock lock(self->mInitPromiseMonitor);
443
0
        self->mLoadPluginsFromDiskComplete = true;
444
0
        self->mInitPromise.Reject(NS_ERROR_FAILURE, __func__);
445
0
      });
446
0
}
447
448
void
449
GeckoMediaPluginServiceParent::NotifySyncShutdownComplete()
450
0
{
451
0
  MOZ_ASSERT(NS_IsMainThread());
452
0
  mWaitingForPluginsSyncShutdown = false;
453
0
}
454
455
bool
456
GeckoMediaPluginServiceParent::IsShuttingDown()
457
0
{
458
0
  MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread());
459
0
  return mShuttingDownOnGMPThread;
460
0
}
461
462
void
463
GeckoMediaPluginServiceParent::UnloadPlugins()
464
0
{
465
0
  MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread());
466
0
  MOZ_ASSERT(!mShuttingDownOnGMPThread);
467
0
  mShuttingDownOnGMPThread = true;
468
0
469
0
  nsTArray<RefPtr<GMPParent>> plugins;
470
0
  {
471
0
    MutexAutoLock lock(mMutex);
472
0
    // Move all plugins references to a local array. This way mMutex won't be
473
0
    // locked when calling CloseActive (to avoid inter-locking).
474
0
    Swap(plugins, mPlugins);
475
0
476
0
    for (GMPServiceParent* parent : mServiceParents) {
477
0
      Unused << parent->SendBeginShutdown();
478
0
    }
479
0
  }
480
0
481
0
  LOGD(("%s::%s plugins:%zu", __CLASS__, __FUNCTION__,
482
0
        plugins.Length()));
483
#ifdef DEBUG
484
  for (const auto& plugin : plugins) {
485
    LOGD(("%s::%s plugin: '%s'", __CLASS__, __FUNCTION__,
486
          plugin->GetDisplayName().get()));
487
  }
488
#endif
489
  // Note: CloseActive may be async; it could actually finish
490
0
  // shutting down when all the plugins have unloaded.
491
0
  for (const auto& plugin : plugins) {
492
0
    plugin->CloseActive(true);
493
0
  }
494
0
495
0
  nsCOMPtr<nsIRunnable> task = NewRunnableMethod(
496
0
    "GeckoMediaPluginServiceParent::NotifySyncShutdownComplete",
497
0
    this, &GeckoMediaPluginServiceParent::NotifySyncShutdownComplete);
498
0
  mMainThread->Dispatch(task.forget());
499
0
}
500
501
void
502
GeckoMediaPluginServiceParent::CrashPlugins()
503
0
{
504
0
  LOGD(("%s::%s", __CLASS__, __FUNCTION__));
505
0
  MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread());
506
0
507
0
  MutexAutoLock lock(mMutex);
508
0
  for (size_t i = 0; i < mPlugins.Length(); i++) {
509
0
    mPlugins[i]->Crash();
510
0
  }
511
0
}
512
513
RefPtr<GenericPromise::AllPromiseType>
514
GeckoMediaPluginServiceParent::LoadFromEnvironment()
515
0
{
516
0
  MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread());
517
0
  RefPtr<AbstractThread> thread(GetAbstractGMPThread());
518
0
  if (!thread) {
519
0
    return GenericPromise::AllPromiseType::CreateAndReject(NS_ERROR_FAILURE, __func__);
520
0
  }
521
0
522
0
  const char* env = PR_GetEnv("MOZ_GMP_PATH");
523
0
  if (!env || !*env) {
524
0
    return GenericPromise::AllPromiseType::CreateAndResolve(true, __func__);
525
0
  }
526
0
527
0
  nsString allpaths;
528
0
  if (NS_WARN_IF(NS_FAILED(NS_CopyNativeToUnicode(nsDependentCString(env), allpaths)))) {
529
0
    return GenericPromise::AllPromiseType::CreateAndReject(NS_ERROR_FAILURE, __func__);
530
0
  }
531
0
532
0
  nsTArray<RefPtr<GenericPromise>> promises;
533
0
  uint32_t pos = 0;
534
0
  while (pos < allpaths.Length()) {
535
0
    // Loop over multiple path entries separated by colons (*nix) or
536
0
    // semicolons (Windows)
537
0
    int32_t next = allpaths.FindChar(XPCOM_ENV_PATH_SEPARATOR[0], pos);
538
0
    if (next == -1) {
539
0
      promises.AppendElement(AddOnGMPThread(nsString(Substring(allpaths, pos))));
540
0
      break;
541
0
    } else {
542
0
      promises.AppendElement(AddOnGMPThread(nsString(Substring(allpaths, pos, next - pos))));
543
0
      pos = next + 1;
544
0
    }
545
0
  }
546
0
547
0
  mScannedPluginOnDisk = true;
548
0
  return GenericPromise::All(thread, promises);
549
0
}
550
551
class NotifyObserversTask final : public mozilla::Runnable {
552
public:
553
  explicit NotifyObserversTask(const char* aTopic, nsString aData = EmptyString())
554
    : Runnable(aTopic)
555
    , mTopic(aTopic)
556
    , mData(aData)
557
0
  {}
558
0
  NS_IMETHOD Run() override {
559
0
    MOZ_ASSERT(NS_IsMainThread());
560
0
    nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
561
0
    MOZ_ASSERT(obsService);
562
0
    if (obsService) {
563
0
      obsService->NotifyObservers(nullptr, mTopic, mData.get());
564
0
    }
565
0
    return NS_OK;
566
0
  }
567
private:
568
0
  ~NotifyObserversTask() {}
569
  const char* mTopic;
570
  const nsString mData;
571
};
572
573
NS_IMETHODIMP
574
GeckoMediaPluginServiceParent::PathRunnable::Run()
575
0
{
576
0
  mService->RemoveOnGMPThread(mPath,
577
0
                              mOperation == REMOVE_AND_DELETE_FROM_DISK,
578
0
                              mDefer);
579
0
580
0
  mService->UpdateContentProcessGMPCapabilities();
581
0
  return NS_OK;
582
0
}
583
584
void
585
GeckoMediaPluginServiceParent::UpdateContentProcessGMPCapabilities()
586
0
{
587
0
  if (!NS_IsMainThread()) {
588
0
    nsCOMPtr<nsIRunnable> task = NewRunnableMethod(
589
0
      "GeckoMediaPluginServiceParent::UpdateContentProcessGMPCapabilities",
590
0
      this, &GeckoMediaPluginServiceParent::UpdateContentProcessGMPCapabilities);
591
0
    mMainThread->Dispatch(task.forget());
592
0
    return;
593
0
  }
594
0
595
0
  typedef mozilla::dom::GMPCapabilityData GMPCapabilityData;
596
0
  typedef mozilla::dom::GMPAPITags GMPAPITags;
597
0
  typedef mozilla::dom::ContentParent ContentParent;
598
0
599
0
  nsTArray<GMPCapabilityData> caps;
600
0
  {
601
0
    MutexAutoLock lock(mMutex);
602
0
    for (const RefPtr<GMPParent>& gmp : mPlugins) {
603
0
      // We have multiple instances of a GMPParent for a given GMP in the
604
0
      // list, one per origin. So filter the list so that we don't include
605
0
      // the same GMP's capabilities twice.
606
0
      NS_ConvertUTF16toUTF8 name(gmp->GetPluginBaseName());
607
0
      bool found = false;
608
0
      for (const GMPCapabilityData& cap : caps) {
609
0
        if (cap.name().Equals(name)) {
610
0
          found = true;
611
0
          break;
612
0
        }
613
0
      }
614
0
      if (found) {
615
0
        continue;
616
0
      }
617
0
      GMPCapabilityData x;
618
0
      x.name() = name;
619
0
      x.version() = gmp->GetVersion();
620
0
      for (const GMPCapability& tag : gmp->GetCapabilities()) {
621
0
        x.capabilities().AppendElement(GMPAPITags(tag.mAPIName, tag.mAPITags));
622
0
      }
623
0
      caps.AppendElement(std::move(x));
624
0
    }
625
0
  }
626
0
  for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
627
0
    Unused << cp->SendGMPsChanged(caps);
628
0
  }
629
0
630
0
  // For non-e10s, we must fire a notification so that any MediaKeySystemAccess
631
0
  // requests waiting on a CDM to download will retry.
632
0
  nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
633
0
  MOZ_ASSERT(obsService);
634
0
  if (obsService) {
635
0
    obsService->NotifyObservers(nullptr, "gmp-changed", nullptr);
636
0
  }
637
0
}
638
639
RefPtr<GenericPromise>
640
GeckoMediaPluginServiceParent::AsyncAddPluginDirectory(const nsAString& aDirectory)
641
0
{
642
0
  RefPtr<AbstractThread> thread(GetAbstractGMPThread());
643
0
  if (!thread) {
644
0
    return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
645
0
  }
646
0
647
0
  nsString dir(aDirectory);
648
0
  RefPtr<GeckoMediaPluginServiceParent> self = this;
649
0
  return InvokeAsync(
650
0
           thread, this, __func__,
651
0
           &GeckoMediaPluginServiceParent::AddOnGMPThread, dir)
652
0
    ->Then(
653
0
      mMainThread,
654
0
      __func__,
655
0
      [dir, self](bool aVal) {
656
0
        LOGD(("GeckoMediaPluginServiceParent::AsyncAddPluginDirectory %s succeeded",
657
0
              NS_ConvertUTF16toUTF8(dir).get()));
658
0
        MOZ_ASSERT(NS_IsMainThread());
659
0
        self->UpdateContentProcessGMPCapabilities();
660
0
        return GenericPromise::CreateAndResolve(aVal, __func__);
661
0
      },
662
0
      [dir](nsresult aResult) {
663
0
        LOGD(("GeckoMediaPluginServiceParent::AsyncAddPluginDirectory %s failed",
664
0
              NS_ConvertUTF16toUTF8(dir).get()));
665
0
        return GenericPromise::CreateAndReject(aResult, __func__);
666
0
      });
667
0
}
668
669
NS_IMETHODIMP
670
GeckoMediaPluginServiceParent::AddPluginDirectory(const nsAString& aDirectory)
671
0
{
672
0
  MOZ_ASSERT(NS_IsMainThread());
673
0
  RefPtr<GenericPromise> p = AsyncAddPluginDirectory(aDirectory);
674
0
  Unused << p;
675
0
  return NS_OK;
676
0
}
677
678
NS_IMETHODIMP
679
GeckoMediaPluginServiceParent::RemovePluginDirectory(const nsAString& aDirectory)
680
0
{
681
0
  MOZ_ASSERT(NS_IsMainThread());
682
0
  return GMPDispatch(new PathRunnable(this, aDirectory,
683
0
                                      PathRunnable::EOperation::REMOVE));
684
0
}
685
686
NS_IMETHODIMP
687
GeckoMediaPluginServiceParent::RemoveAndDeletePluginDirectory(
688
  const nsAString& aDirectory, const bool aDefer)
689
0
{
690
0
  MOZ_ASSERT(NS_IsMainThread());
691
0
  return GMPDispatch(
692
0
    new PathRunnable(this, aDirectory,
693
0
                     PathRunnable::EOperation::REMOVE_AND_DELETE_FROM_DISK,
694
0
                     aDefer));
695
0
}
696
697
NS_IMETHODIMP
698
GeckoMediaPluginServiceParent::HasPluginForAPI(const nsACString& aAPI,
699
                                               nsTArray<nsCString>* aTags,
700
                                               bool* aHasPlugin)
701
0
{
702
0
  NS_ENSURE_ARG(aTags && aTags->Length() > 0);
703
0
  NS_ENSURE_ARG(aHasPlugin);
704
0
705
0
  nsresult rv = EnsurePluginsOnDiskScanned();
706
0
  if (NS_FAILED(rv)) {
707
0
    NS_WARNING("Failed to load GMPs from disk.");
708
0
    return rv;
709
0
  }
710
0
711
0
  {
712
0
    MutexAutoLock lock(mMutex);
713
0
    nsCString api(aAPI);
714
0
    size_t index = 0;
715
0
    RefPtr<GMPParent> gmp = FindPluginForAPIFrom(index, api, *aTags, &index);
716
0
    *aHasPlugin = !!gmp;
717
0
  }
718
0
719
0
  return NS_OK;
720
0
}
721
722
nsresult
723
GeckoMediaPluginServiceParent::EnsurePluginsOnDiskScanned()
724
0
{
725
0
  const char* env = nullptr;
726
0
  if (!mScannedPluginOnDisk && (env = PR_GetEnv("MOZ_GMP_PATH")) && *env) {
727
0
    // We have a MOZ_GMP_PATH environment variable which may specify the
728
0
    // location of plugins to load, and we haven't yet scanned the disk to
729
0
    // see if there are plugins there. Get the GMP thread, which will
730
0
    // cause an event to be dispatched to which scans for plugins. We
731
0
    // dispatch a sync event to the GMP thread here in order to wait until
732
0
    // after the GMP thread has scanned any paths in MOZ_GMP_PATH.
733
0
    nsresult rv = GMPDispatch(new mozilla::Runnable("GMPDummyRunnable"), NS_DISPATCH_SYNC);
734
0
    NS_ENSURE_SUCCESS(rv, rv);
735
0
    MOZ_ASSERT(mScannedPluginOnDisk, "Should have scanned MOZ_GMP_PATH by now");
736
0
  }
737
0
738
0
  return NS_OK;
739
0
}
740
741
already_AddRefed<GMPParent>
742
GeckoMediaPluginServiceParent::FindPluginForAPIFrom(size_t aSearchStartIndex,
743
                                                    const nsCString& aAPI,
744
                                                    const nsTArray<nsCString>& aTags,
745
                                                    size_t* aOutPluginIndex)
746
0
{
747
0
  mMutex.AssertCurrentThreadOwns();
748
0
  for (size_t i = aSearchStartIndex; i < mPlugins.Length(); i++) {
749
0
    RefPtr<GMPParent> gmp = mPlugins[i];
750
0
    if (!GMPCapability::Supports(gmp->GetCapabilities(), aAPI, aTags)) {
751
0
      continue;
752
0
    }
753
0
    if (aOutPluginIndex) {
754
0
      *aOutPluginIndex = i;
755
0
    }
756
0
    return gmp.forget();
757
0
  }
758
0
  return nullptr;
759
0
}
760
761
already_AddRefed<GMPParent>
762
GeckoMediaPluginServiceParent::SelectPluginForAPI(const nsACString& aNodeId,
763
                                                  const nsCString& aAPI,
764
                                                  const nsTArray<nsCString>& aTags)
765
0
{
766
0
  MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread(),
767
0
             "Can't clone GMP plugins on non-GMP threads.");
768
0
769
0
  GMPParent* gmpToClone = nullptr;
770
0
  {
771
0
    MutexAutoLock lock(mMutex);
772
0
    size_t index = 0;
773
0
    RefPtr<GMPParent> gmp;
774
0
    while ((gmp = FindPluginForAPIFrom(index, aAPI, aTags, &index))) {
775
0
      if (aNodeId.IsEmpty()) {
776
0
        if (gmp->CanBeSharedCrossNodeIds()) {
777
0
          return gmp.forget();
778
0
        }
779
0
      } else if (gmp->CanBeUsedFrom(aNodeId)) {
780
0
        return gmp.forget();
781
0
      }
782
0
783
0
      if (!gmpToClone ||
784
0
          (gmpToClone->IsMarkedForDeletion() && !gmp->IsMarkedForDeletion())) {
785
0
        // This GMP has the correct type but has the wrong nodeId; hold on to it
786
0
        // in case we need to clone it.
787
0
        // Prefer GMPs in-use for the case where an upgraded plugin version is
788
0
        // waiting for the old one to die. If the old plugin is in use, we
789
0
        // should continue using it so that any persistent state remains
790
0
        // consistent. Otherwise, just check that the plugin isn't scheduled
791
0
        // for deletion.
792
0
        gmpToClone = gmp;
793
0
      }
794
0
      // Loop around and try the next plugin; it may be usable from aNodeId.
795
0
      index++;
796
0
    }
797
0
  }
798
0
799
0
  // Plugin exists, but we can't use it due to cross-origin separation. Create a
800
0
  // new one.
801
0
  if (gmpToClone) {
802
0
    RefPtr<GMPParent> clone = ClonePlugin(gmpToClone);
803
0
    {
804
0
      MutexAutoLock lock(mMutex);
805
0
      mPlugins.AppendElement(clone);
806
0
    }
807
0
    if (!aNodeId.IsEmpty()) {
808
0
      clone->SetNodeId(aNodeId);
809
0
    }
810
0
    return clone.forget();
811
0
  }
812
0
813
0
  return nullptr;
814
0
}
815
816
RefPtr<GMPParent>
817
CreateGMPParent(AbstractThread* aMainThread)
818
0
{
819
0
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
820
0
  if (!SandboxInfo::Get().CanSandboxMedia()) {
821
0
    if (!StaticPrefs::MediaGmpInsecureAllow()) {
822
0
      NS_WARNING("Denying media plugin load due to lack of sandboxing.");
823
0
      return nullptr;
824
0
    }
825
0
    NS_WARNING("Loading media plugin despite lack of sandboxing.");
826
0
  }
827
0
#endif
828
0
  return new GMPParent(aMainThread);
829
0
}
830
831
already_AddRefed<GMPParent>
832
GeckoMediaPluginServiceParent::ClonePlugin(const GMPParent* aOriginal)
833
0
{
834
0
  MOZ_ASSERT(aOriginal);
835
0
836
0
  RefPtr<GMPParent> gmp = CreateGMPParent(mMainThread);
837
0
  nsresult rv = gmp ? gmp->CloneFrom(aOriginal) : NS_ERROR_NOT_AVAILABLE;
838
0
839
0
  if (NS_FAILED(rv)) {
840
0
    NS_WARNING("Can't Create GMPParent");
841
0
    return nullptr;
842
0
  }
843
0
844
0
  return gmp.forget();
845
0
}
846
847
RefPtr<GenericPromise>
848
GeckoMediaPluginServiceParent::AddOnGMPThread(nsString aDirectory)
849
0
{
850
#ifdef XP_WIN
851
  // On Windows our various test harnesses often pass paths with UNIX dir
852
  // separators, or a mix of dir separators. NS_NewLocalFile() can't handle
853
  // that, so fixup to match the platform's expected format. This makes us
854
  // more robust in the face of bad input and test harnesses changing...
855
  std::replace(aDirectory.BeginWriting(), aDirectory.EndWriting(), '/', '\\');
856
#endif
857
858
0
  MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread());
859
0
  nsCString dir = NS_ConvertUTF16toUTF8(aDirectory);
860
0
  RefPtr<AbstractThread> thread(GetAbstractGMPThread());
861
0
  if (!thread) {
862
0
    LOGD(("%s::%s: %s No GMP Thread", __CLASS__, __FUNCTION__, dir.get()));
863
0
    return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
864
0
  }
865
0
  LOGD(("%s::%s: %s", __CLASS__, __FUNCTION__, dir.get()));
866
0
867
0
  nsCOMPtr<nsIFile> directory;
868
0
  nsresult rv = NS_NewLocalFile(aDirectory, false, getter_AddRefs(directory));
869
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
870
0
    LOGD(("%s::%s: failed to create nsIFile for dir=%s rv=%" PRIx32,
871
0
          __CLASS__, __FUNCTION__, dir.get(), static_cast<uint32_t>(rv)));
872
0
    return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
873
0
  }
874
0
875
0
  RefPtr<GMPParent> gmp = CreateGMPParent(mMainThread);
876
0
  if (!gmp) {
877
0
    NS_WARNING("Can't Create GMPParent");
878
0
    return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
879
0
  }
880
0
881
0
  RefPtr<GeckoMediaPluginServiceParent> self(this);
882
0
  return gmp->Init(this, directory)->Then(thread, __func__,
883
0
    [gmp, self, dir](bool aVal) {
884
0
      LOGD(("%s::%s: %s Succeeded", __CLASS__, __FUNCTION__, dir.get()));
885
0
      {
886
0
        MutexAutoLock lock(self->mMutex);
887
0
        self->mPlugins.AppendElement(gmp);
888
0
      }
889
0
      return GenericPromise::CreateAndResolve(aVal, __func__);
890
0
    },
891
0
    [dir](nsresult aResult) {
892
0
      LOGD(("%s::%s: %s Failed", __CLASS__, __FUNCTION__, dir.get()));
893
0
      return GenericPromise::CreateAndReject(aResult, __func__);
894
0
    });
895
0
}
896
897
void
898
GeckoMediaPluginServiceParent::RemoveOnGMPThread(const nsAString& aDirectory,
899
                                                 const bool aDeleteFromDisk,
900
                                                 const bool aCanDefer)
901
0
{
902
0
  MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread());
903
0
  LOGD(("%s::%s: %s", __CLASS__, __FUNCTION__, NS_LossyConvertUTF16toASCII(aDirectory).get()));
904
0
905
0
  nsCOMPtr<nsIFile> directory;
906
0
  nsresult rv = NS_NewLocalFile(aDirectory, false, getter_AddRefs(directory));
907
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
908
0
    return;
909
0
  }
910
0
911
0
  // Plugin destruction can modify |mPlugins|. Put them aside for now and
912
0
  // destroy them once we're done with |mPlugins|.
913
0
  nsTArray<RefPtr<GMPParent>> deadPlugins;
914
0
915
0
  bool inUse = false;
916
0
  MutexAutoLock lock(mMutex);
917
0
  for (size_t i = mPlugins.Length(); i-- > 0; ) {
918
0
    nsCOMPtr<nsIFile> pluginpath = mPlugins[i]->GetDirectory();
919
0
    bool equals;
920
0
    if (NS_FAILED(directory->Equals(pluginpath, &equals)) || !equals) {
921
0
      continue;
922
0
    }
923
0
924
0
    RefPtr<GMPParent> gmp = mPlugins[i];
925
0
    if (aDeleteFromDisk && gmp->State() != GMPStateNotLoaded) {
926
0
      // We have to wait for the child process to release its lib handle
927
0
      // before we can delete the GMP.
928
0
      inUse = true;
929
0
      gmp->MarkForDeletion();
930
0
931
0
      if (!mPluginsWaitingForDeletion.Contains(aDirectory)) {
932
0
        mPluginsWaitingForDeletion.AppendElement(aDirectory);
933
0
      }
934
0
    }
935
0
936
0
    if (gmp->State() == GMPStateNotLoaded || !aCanDefer) {
937
0
      // GMP not in use or shutdown is being forced; can shut it down now.
938
0
      deadPlugins.AppendElement(gmp);
939
0
      mPlugins.RemoveElementAt(i);
940
0
    }
941
0
  }
942
0
943
0
  {
944
0
    MutexAutoUnlock unlock(mMutex);
945
0
    for (auto& gmp : deadPlugins) {
946
0
      gmp->CloseActive(true);
947
0
    }
948
0
  }
949
0
950
0
  if (aDeleteFromDisk && !inUse) {
951
0
    // Ensure the GMP dir and all files in it are writable, so we have
952
0
    // permission to delete them.
953
0
    directory->SetPermissions(0700);
954
0
    DirectoryEnumerator iter(directory, DirectoryEnumerator::FilesAndDirs);
955
0
    for (nsCOMPtr<nsIFile> dirEntry; (dirEntry = iter.Next()) != nullptr;) {
956
0
      dirEntry->SetPermissions(0700);
957
0
    }
958
0
    if (NS_SUCCEEDED(directory->Remove(true))) {
959
0
      mPluginsWaitingForDeletion.RemoveElement(aDirectory);
960
0
      nsCOMPtr<nsIRunnable> task = new NotifyObserversTask(
961
0
        "gmp-directory-deleted", nsString(aDirectory));
962
0
      mMainThread->Dispatch(task.forget());
963
0
    }
964
0
  }
965
0
}
966
967
// May remove when Bug 1043671 is fixed
968
static void Dummy(RefPtr<GMPParent>& aOnDeathsDoor)
969
0
{
970
0
  // exists solely to do nothing and let the Runnable kill the GMPParent
971
0
  // when done.
972
0
}
973
974
void
975
GeckoMediaPluginServiceParent::PluginTerminated(const RefPtr<GMPParent>& aPlugin)
976
0
{
977
0
  MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread());
978
0
979
0
  if (aPlugin->IsMarkedForDeletion()) {
980
0
    nsString path;
981
0
    RefPtr<nsIFile> dir = aPlugin->GetDirectory();
982
0
    nsresult rv = dir->GetPath(path);
983
0
    NS_ENSURE_SUCCESS_VOID(rv);
984
0
    if (mPluginsWaitingForDeletion.Contains(path)) {
985
0
      RemoveOnGMPThread(path, true /* delete */, true /* can defer */);
986
0
    }
987
0
  }
988
0
}
989
990
void
991
GeckoMediaPluginServiceParent::ReAddOnGMPThread(const RefPtr<GMPParent>& aOld)
992
0
{
993
0
  MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread());
994
0
  LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, (void*) aOld));
995
0
996
0
  RefPtr<GMPParent> gmp;
997
0
  if (!mShuttingDownOnGMPThread) {
998
0
    // We're not shutting down, so replace the old plugin in the list with a
999
0
    // clone which is in a pristine state. Note: We place the plugin in
1000
0
    // the same slot in the array as a hack to ensure if we re-request with
1001
0
    // the same capabilities we get an instance of the same plugin.
1002
0
    gmp = ClonePlugin(aOld);
1003
0
    MutexAutoLock lock(mMutex);
1004
0
    MOZ_ASSERT(mPlugins.Contains(aOld));
1005
0
    if (mPlugins.Contains(aOld)) {
1006
0
      mPlugins[mPlugins.IndexOf(aOld)] = gmp;
1007
0
    }
1008
0
  } else {
1009
0
    // We're shutting down; don't re-add plugin, let the old plugin die.
1010
0
    MutexAutoLock lock(mMutex);
1011
0
    mPlugins.RemoveElement(aOld);
1012
0
  }
1013
0
  // Schedule aOld to be destroyed.  We can't destroy it from here since we
1014
0
  // may be inside ActorDestroyed() for it.
1015
0
  NS_DispatchToCurrentThread(WrapRunnableNM(&Dummy, aOld));
1016
0
}
1017
1018
NS_IMETHODIMP
1019
GeckoMediaPluginServiceParent::GetStorageDir(nsIFile** aOutFile)
1020
0
{
1021
0
  if (NS_WARN_IF(!mStorageBaseDir)) {
1022
0
    return NS_ERROR_FAILURE;
1023
0
  }
1024
0
  return mStorageBaseDir->Clone(aOutFile);
1025
0
}
1026
1027
static nsresult
1028
WriteToFile(nsIFile* aPath,
1029
            const nsCString& aFileName,
1030
            const nsCString& aData)
1031
0
{
1032
0
  nsCOMPtr<nsIFile> path;
1033
0
  nsresult rv = aPath->Clone(getter_AddRefs(path));
1034
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1035
0
    return rv;
1036
0
  }
1037
0
1038
0
  rv = path->AppendNative(aFileName);
1039
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1040
0
    return rv;
1041
0
  }
1042
0
1043
0
  PRFileDesc* f = nullptr;
1044
0
  rv = path->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, PR_IRWXU, &f);
1045
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1046
0
    return rv;
1047
0
  }
1048
0
1049
0
  int32_t len = PR_Write(f, aData.get(), aData.Length());
1050
0
  PR_Close(f);
1051
0
  if (NS_WARN_IF(len < 0 || (size_t)len != aData.Length())) {
1052
0
    return NS_ERROR_FAILURE;
1053
0
  }
1054
0
1055
0
  return NS_OK;
1056
0
}
1057
1058
static nsresult
1059
ReadFromFile(nsIFile* aPath,
1060
             const nsACString& aFileName,
1061
             nsACString& aOutData,
1062
             int32_t aMaxLength)
1063
0
{
1064
0
  nsCOMPtr<nsIFile> path;
1065
0
  nsresult rv = aPath->Clone(getter_AddRefs(path));
1066
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1067
0
    return rv;
1068
0
  }
1069
0
1070
0
  rv = path->AppendNative(aFileName);
1071
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1072
0
    return rv;
1073
0
  }
1074
0
1075
0
  PRFileDesc* f = nullptr;
1076
0
  rv = path->OpenNSPRFileDesc(PR_RDONLY | PR_CREATE_FILE, PR_IRWXU, &f);
1077
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1078
0
    return rv;
1079
0
  }
1080
0
1081
0
  auto size = PR_Seek(f, 0, PR_SEEK_END);
1082
0
  PR_Seek(f, 0, PR_SEEK_SET);
1083
0
1084
0
  if (size > aMaxLength) {
1085
0
    return NS_ERROR_FAILURE;
1086
0
  }
1087
0
  aOutData.SetLength(size);
1088
0
1089
0
  auto len = PR_Read(f, aOutData.BeginWriting(), size);
1090
0
  PR_Close(f);
1091
0
  if (NS_WARN_IF(len != size)) {
1092
0
    return NS_ERROR_FAILURE;
1093
0
  }
1094
0
1095
0
  return NS_OK;
1096
0
}
1097
1098
nsresult
1099
ReadSalt(nsIFile* aPath, nsACString& aOutData)
1100
0
{
1101
0
  return ReadFromFile(aPath, NS_LITERAL_CSTRING("salt"),
1102
0
                      aOutData, NodeIdSaltLength);
1103
0
1104
0
}
1105
1106
already_AddRefed<GMPStorage>
1107
GeckoMediaPluginServiceParent::GetMemoryStorageFor(const nsACString& aNodeId)
1108
0
{
1109
0
  RefPtr<GMPStorage> s;
1110
0
  if (!mTempGMPStorage.Get(aNodeId, getter_AddRefs(s))) {
1111
0
    s = CreateGMPMemoryStorage();
1112
0
    mTempGMPStorage.Put(aNodeId, s);
1113
0
  }
1114
0
  return s.forget();
1115
0
}
1116
1117
NS_IMETHODIMP
1118
GeckoMediaPluginServiceParent::IsPersistentStorageAllowed(const nsACString& aNodeId,
1119
                                                          bool* aOutAllowed)
1120
0
{
1121
0
  MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread());
1122
0
  NS_ENSURE_ARG(aOutAllowed);
1123
0
  // We disallow persistent storage for the NodeId used for shared GMP
1124
0
  // decoding, to prevent GMP decoding being used to track what a user
1125
0
  // watches somehow.
1126
0
  *aOutAllowed = !aNodeId.Equals(SHARED_GMP_DECODING_NODE_ID) &&
1127
0
                 mPersistentStorageAllowed.Get(aNodeId);
1128
0
  return NS_OK;
1129
0
}
1130
1131
nsresult
1132
GeckoMediaPluginServiceParent::GetNodeId(const nsAString& aOrigin,
1133
                                         const nsAString& aTopLevelOrigin,
1134
                                         const nsAString& aGMPName,
1135
                                         nsACString& aOutId)
1136
0
{
1137
0
  MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread());
1138
0
  LOGD(("%s::%s: (%s, %s)", __CLASS__, __FUNCTION__,
1139
0
       NS_ConvertUTF16toUTF8(aOrigin).get(),
1140
0
       NS_ConvertUTF16toUTF8(aTopLevelOrigin).get()));
1141
0
1142
0
  nsresult rv;
1143
0
1144
0
  if (aOrigin.EqualsLiteral("null") ||
1145
0
      aOrigin.IsEmpty() ||
1146
0
      aTopLevelOrigin.EqualsLiteral("null") ||
1147
0
      aTopLevelOrigin.IsEmpty()) {
1148
0
    // (origin, topLevelOrigin) is null or empty; this is for an anonymous
1149
0
    // origin, probably a local file, for which we don't provide persistent storage.
1150
0
    // Generate a random node id, and don't store it so that the GMP's storage
1151
0
    // is temporary and the process for this GMP is not shared with GMP
1152
0
    // instances that have the same nodeId.
1153
0
    nsAutoCString salt;
1154
0
    rv = GenerateRandomPathName(salt, NodeIdSaltLength);
1155
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1156
0
      return rv;
1157
0
    }
1158
0
    aOutId = salt;
1159
0
    mPersistentStorageAllowed.Put(salt, false);
1160
0
    return NS_OK;
1161
0
  }
1162
0
1163
0
  const uint32_t hash = AddToHash(HashString(aOrigin),
1164
0
                                  HashString(aTopLevelOrigin));
1165
0
1166
0
  if (OriginAttributes::IsPrivateBrowsing(NS_ConvertUTF16toUTF8(aOrigin))) {
1167
0
    // For PB mode, we store the node id, indexed by the origin pair and GMP name,
1168
0
    // so that if the same origin pair is opened for the same GMP in this session,
1169
0
    // it gets the same node id.
1170
0
    const uint32_t pbHash = AddToHash(HashString(aGMPName), hash);
1171
0
    nsCString* salt = nullptr;
1172
0
    if (!(salt = mTempNodeIds.Get(pbHash))) {
1173
0
      // No salt stored, generate and temporarily store some for this id.
1174
0
      nsAutoCString newSalt;
1175
0
      rv = GenerateRandomPathName(newSalt, NodeIdSaltLength);
1176
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
1177
0
        return rv;
1178
0
      }
1179
0
      salt = new nsCString(newSalt);
1180
0
      mTempNodeIds.Put(pbHash, salt);
1181
0
      mPersistentStorageAllowed.Put(*salt, false);
1182
0
    }
1183
0
    aOutId = *salt;
1184
0
    return NS_OK;
1185
0
  }
1186
0
1187
0
  // Otherwise, try to see if we've previously generated and stored salt
1188
0
  // for this origin pair.
1189
0
  nsCOMPtr<nsIFile> path; // $profileDir/gmp/$platform/
1190
0
  rv = GetStorageDir(getter_AddRefs(path));
1191
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1192
0
    return rv;
1193
0
  }
1194
0
1195
0
  rv = path->Append(aGMPName);
1196
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1197
0
    return rv;
1198
0
  }
1199
0
1200
0
  // $profileDir/gmp/$platform/$gmpName/
1201
0
  rv = path->Create(nsIFile::DIRECTORY_TYPE, 0700);
1202
0
  if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
1203
0
    return rv;
1204
0
  }
1205
0
1206
0
  rv = path->AppendNative(NS_LITERAL_CSTRING("id"));
1207
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1208
0
    return rv;
1209
0
  }
1210
0
1211
0
  // $profileDir/gmp/$platform/$gmpName/id/
1212
0
  rv = path->Create(nsIFile::DIRECTORY_TYPE, 0700);
1213
0
  if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
1214
0
    return rv;
1215
0
  }
1216
0
1217
0
  nsAutoCString hashStr;
1218
0
  hashStr.AppendInt((int64_t)hash);
1219
0
1220
0
  // $profileDir/gmp/$platform/$gmpName/id/$hash
1221
0
  rv = path->AppendNative(hashStr);
1222
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1223
0
    return rv;
1224
0
  }
1225
0
1226
0
  rv = path->Create(nsIFile::DIRECTORY_TYPE, 0700);
1227
0
  if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
1228
0
    return rv;
1229
0
  }
1230
0
1231
0
  nsCOMPtr<nsIFile> saltFile;
1232
0
  rv = path->Clone(getter_AddRefs(saltFile));
1233
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1234
0
    return rv;
1235
0
  }
1236
0
1237
0
  rv = saltFile->AppendNative(NS_LITERAL_CSTRING("salt"));
1238
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1239
0
    return rv;
1240
0
  }
1241
0
1242
0
  nsAutoCString salt;
1243
0
  bool exists = false;
1244
0
  rv = saltFile->Exists(&exists);
1245
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1246
0
    return rv;
1247
0
  }
1248
0
  if (!exists) {
1249
0
    // No stored salt for this origin. Generate salt, and store it and
1250
0
    // the origin on disk.
1251
0
    nsresult rv = GenerateRandomPathName(salt, NodeIdSaltLength);
1252
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1253
0
      return rv;
1254
0
    }
1255
0
    MOZ_ASSERT(salt.Length() == NodeIdSaltLength);
1256
0
1257
0
    // $profileDir/gmp/$platform/$gmpName/id/$hash/salt
1258
0
    rv = WriteToFile(path, NS_LITERAL_CSTRING("salt"), salt);
1259
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1260
0
      return rv;
1261
0
    }
1262
0
1263
0
    // $profileDir/gmp/$platform/$gmpName/id/$hash/origin
1264
0
    rv = WriteToFile(path,
1265
0
                     NS_LITERAL_CSTRING("origin"),
1266
0
                     NS_ConvertUTF16toUTF8(aOrigin));
1267
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1268
0
      return rv;
1269
0
    }
1270
0
1271
0
    // $profileDir/gmp/$platform/$gmpName/id/$hash/topLevelOrigin
1272
0
    rv = WriteToFile(path,
1273
0
                     NS_LITERAL_CSTRING("topLevelOrigin"),
1274
0
                     NS_ConvertUTF16toUTF8(aTopLevelOrigin));
1275
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1276
0
      return rv;
1277
0
    }
1278
0
1279
0
  } else {
1280
0
    rv = ReadSalt(path, salt);
1281
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1282
0
      return rv;
1283
0
    }
1284
0
  }
1285
0
1286
0
  aOutId = salt;
1287
0
  mPersistentStorageAllowed.Put(salt, true);
1288
0
1289
0
  return NS_OK;
1290
0
}
1291
1292
NS_IMETHODIMP
1293
GeckoMediaPluginServiceParent::GetNodeId(const nsAString& aOrigin,
1294
                                         const nsAString& aTopLevelOrigin,
1295
                                         const nsAString& aGMPName,
1296
                                         UniquePtr<GetNodeIdCallback>&& aCallback)
1297
0
{
1298
0
  nsCString nodeId;
1299
0
  nsresult rv = GetNodeId(aOrigin, aTopLevelOrigin, aGMPName, nodeId);
1300
0
  aCallback->Done(rv, nodeId);
1301
0
  return rv;
1302
0
}
1303
1304
static bool
1305
ExtractHostName(const nsACString& aOrigin, nsACString& aOutData)
1306
0
{
1307
0
  nsCString str;
1308
0
  str.Assign(aOrigin);
1309
0
  int begin = str.Find("://");
1310
0
  // The scheme is missing!
1311
0
  if (begin == -1) {
1312
0
    return false;
1313
0
  }
1314
0
1315
0
  int end = str.RFind(":");
1316
0
  // Remove the port number
1317
0
  if (end != begin) {
1318
0
    str.SetLength(end);
1319
0
  }
1320
0
1321
0
  nsDependentCSubstring host(str, begin + 3);
1322
0
  aOutData.Assign(host);
1323
0
  return true;
1324
0
}
1325
1326
bool
1327
MatchOrigin(nsIFile* aPath,
1328
            const nsACString& aSite,
1329
            const mozilla::OriginAttributesPattern& aPattern)
1330
0
{
1331
0
  // http://en.wikipedia.org/wiki/Domain_Name_System#Domain_name_syntax
1332
0
  static const uint32_t MaxDomainLength = 253;
1333
0
1334
0
  nsresult rv;
1335
0
  nsCString str;
1336
0
  nsCString originNoSuffix;
1337
0
  mozilla::OriginAttributes originAttributes;
1338
0
1339
0
  rv = ReadFromFile(aPath, NS_LITERAL_CSTRING("origin"), str, MaxDomainLength);
1340
0
  if (!originAttributes.PopulateFromOrigin(str, originNoSuffix)) {
1341
0
    // Fails on parsing the originAttributes, treat this as a non-match.
1342
0
    return false;
1343
0
  }
1344
0
1345
0
  if (NS_SUCCEEDED(rv) && ExtractHostName(originNoSuffix, str) && str.Equals(aSite) &&
1346
0
      aPattern.Matches(originAttributes)) {
1347
0
    return true;
1348
0
  }
1349
0
1350
0
  mozilla::OriginAttributes topLevelOriginAttributes;
1351
0
  rv = ReadFromFile(aPath, NS_LITERAL_CSTRING("topLevelOrigin"), str, MaxDomainLength);
1352
0
  if (!topLevelOriginAttributes.PopulateFromOrigin(str, originNoSuffix)) {
1353
0
    // Fails on paring the originAttributes, treat this as a non-match.
1354
0
    return false;
1355
0
  }
1356
0
1357
0
  if (NS_SUCCEEDED(rv) && ExtractHostName(originNoSuffix, str) && str.Equals(aSite) &&
1358
0
      aPattern.Matches(topLevelOriginAttributes)) {
1359
0
    return true;
1360
0
  }
1361
0
  return false;
1362
0
}
1363
1364
template<typename T> static void
1365
KillPlugins(const nsTArray<RefPtr<GMPParent>>& aPlugins,
1366
            Mutex& aMutex, T&& aFilter)
1367
0
{
1368
0
  // Shutdown the plugins when |aFilter| evaluates to true.
1369
0
  // After we clear storage data, node IDs will become invalid and shouldn't be
1370
0
  // used anymore. We need to kill plugins with such nodeIDs.
1371
0
  // Note: we can't shut them down while holding the lock,
1372
0
  // as the lock is not re-entrant and shutdown requires taking the lock.
1373
0
  // The plugin list is only edited on the GMP thread, so this should be OK.
1374
0
  nsTArray<RefPtr<GMPParent>> pluginsToKill;
1375
0
  {
1376
0
    MutexAutoLock lock(aMutex);
1377
0
    for (size_t i = 0; i < aPlugins.Length(); i++) {
1378
0
      RefPtr<GMPParent> parent(aPlugins[i]);
1379
0
      if (aFilter(parent)) {
1380
0
        pluginsToKill.AppendElement(parent);
1381
0
      }
1382
0
    }
1383
0
  }
1384
0
1385
0
  for (size_t i = 0; i < pluginsToKill.Length(); i++) {
1386
0
    pluginsToKill[i]->CloseActive(false);
1387
0
  }
1388
0
}
Unexecuted instantiation: Unified_cpp_dom_media_gmp1.cpp:void mozilla::gmp::KillPlugins<mozilla::gmp::NodeFilter>(nsTArray<RefPtr<mozilla::gmp::GMPParent> > const&, mozilla::Mutex&, mozilla::gmp::NodeFilter&&)
Unexecuted instantiation: Unified_cpp_dom_media_gmp1.cpp:void mozilla::gmp::KillPlugins<bool (*)(mozilla::gmp::GMPParent*)>(nsTArray<RefPtr<mozilla::gmp::GMPParent> > const&, mozilla::Mutex&, bool (*&&)(mozilla::gmp::GMPParent*))
1389
1390
static nsresult
1391
DeleteDir(nsIFile* aPath)
1392
0
{
1393
0
  bool exists = false;
1394
0
  nsresult rv = aPath->Exists(&exists);
1395
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1396
0
    return rv;
1397
0
  }
1398
0
  if (exists) {
1399
0
    return aPath->Remove(true);
1400
0
  }
1401
0
  return NS_OK;
1402
0
}
1403
1404
struct NodeFilter {
1405
0
  explicit NodeFilter(const nsTArray<nsCString>& nodeIDs) : mNodeIDs(nodeIDs) {}
1406
0
  bool operator()(GMPParent* aParent) {
1407
0
    return mNodeIDs.Contains(aParent->GetNodeId());
1408
0
  }
1409
private:
1410
  const nsTArray<nsCString>& mNodeIDs;
1411
};
1412
1413
void
1414
GeckoMediaPluginServiceParent::ClearNodeIdAndPlugin(DirectoryFilter& aFilter)
1415
0
{
1416
0
  // $profileDir/gmp/$platform/
1417
0
  nsCOMPtr<nsIFile> path;
1418
0
  nsresult rv = GetStorageDir(getter_AddRefs(path));
1419
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1420
0
    return;
1421
0
  }
1422
0
1423
0
  // Iterate all sub-folders of $profileDir/gmp/$platform/, i.e. the dirs in which
1424
0
  // specific GMPs store their data.
1425
0
  DirectoryEnumerator iter(path, DirectoryEnumerator::DirsOnly);
1426
0
  for (nsCOMPtr<nsIFile> pluginDir; (pluginDir = iter.Next()) != nullptr;) {
1427
0
    ClearNodeIdAndPlugin(pluginDir, aFilter);
1428
0
  }
1429
0
}
1430
1431
void
1432
GeckoMediaPluginServiceParent::ClearNodeIdAndPlugin(nsIFile* aPluginStorageDir,
1433
                                                    DirectoryFilter& aFilter)
1434
0
{
1435
0
  // $profileDir/gmp/$platform/$gmpName/id/
1436
0
  nsCOMPtr<nsIFile> path = CloneAndAppend(aPluginStorageDir, NS_LITERAL_STRING("id"));
1437
0
  if (!path) {
1438
0
    return;
1439
0
  }
1440
0
1441
0
  // Iterate all sub-folders of $profileDir/gmp/$platform/$gmpName/id/
1442
0
  nsTArray<nsCString> nodeIDsToClear;
1443
0
  DirectoryEnumerator iter(path, DirectoryEnumerator::DirsOnly);
1444
0
  for (nsCOMPtr<nsIFile> dirEntry; (dirEntry = iter.Next()) != nullptr;) {
1445
0
    // dirEntry is the hash of origins, i.e.:
1446
0
    // $profileDir/gmp/$platform/$gmpName/id/$originHash/
1447
0
    if (!aFilter(dirEntry)) {
1448
0
      continue;
1449
0
    }
1450
0
    nsAutoCString salt;
1451
0
    if (NS_SUCCEEDED(ReadSalt(dirEntry, salt))) {
1452
0
      // Keep node IDs to clear data/plugins associated with them later.
1453
0
      nodeIDsToClear.AppendElement(salt);
1454
0
      // Also remove node IDs from the table.
1455
0
      mPersistentStorageAllowed.Remove(salt);
1456
0
    }
1457
0
    // Now we can remove the directory for the origin pair.
1458
0
    if (NS_FAILED(dirEntry->Remove(true))) {
1459
0
      NS_WARNING("Failed to delete the directory for the origin pair");
1460
0
    }
1461
0
  }
1462
0
1463
0
  // Kill plugin instances that have node IDs being cleared.
1464
0
  KillPlugins(mPlugins, mMutex, NodeFilter(nodeIDsToClear));
1465
0
1466
0
  // Clear all storage in $profileDir/gmp/$platform/$gmpName/storage/$nodeId/
1467
0
  path = CloneAndAppend(aPluginStorageDir, NS_LITERAL_STRING("storage"));
1468
0
  if (!path) {
1469
0
    return;
1470
0
  }
1471
0
1472
0
  for (const nsCString& nodeId : nodeIDsToClear) {
1473
0
    nsCOMPtr<nsIFile> dirEntry;
1474
0
    nsresult rv = path->Clone(getter_AddRefs(dirEntry));
1475
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1476
0
      continue;
1477
0
    }
1478
0
1479
0
    rv = dirEntry->AppendNative(nodeId);
1480
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1481
0
      continue;
1482
0
    }
1483
0
1484
0
    if (NS_FAILED(DeleteDir(dirEntry))) {
1485
0
      NS_WARNING("Failed to delete GMP storage directory for the node");
1486
0
    }
1487
0
  }
1488
0
}
1489
1490
void
1491
GeckoMediaPluginServiceParent::ForgetThisSiteOnGMPThread(const nsACString& aSite,
1492
                                                         const mozilla::OriginAttributesPattern& aPattern)
1493
0
{
1494
0
  MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread());
1495
0
  LOGD(("%s::%s: origin=%s", __CLASS__, __FUNCTION__, aSite.Data()));
1496
0
1497
0
  struct OriginFilter : public DirectoryFilter {
1498
0
    explicit OriginFilter(const nsACString& aSite,
1499
0
                          const mozilla::OriginAttributesPattern& aPattern)
1500
0
    : mSite(aSite)
1501
0
    , mPattern(aPattern)
1502
0
    { }
1503
0
    bool operator()(nsIFile* aPath) override {
1504
0
      return MatchOrigin(aPath, mSite, mPattern);
1505
0
    }
1506
0
  private:
1507
0
    const nsACString& mSite;
1508
0
    const mozilla::OriginAttributesPattern& mPattern;
1509
0
  } filter(aSite, aPattern);
1510
0
1511
0
  ClearNodeIdAndPlugin(filter);
1512
0
}
1513
1514
void
1515
GeckoMediaPluginServiceParent::ClearRecentHistoryOnGMPThread(PRTime aSince)
1516
0
{
1517
0
  MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread());
1518
0
  LOGD(("%s::%s: since=%" PRId64, __CLASS__, __FUNCTION__, (int64_t)aSince));
1519
0
1520
0
  struct MTimeFilter : public DirectoryFilter {
1521
0
    explicit MTimeFilter(PRTime aSince)
1522
0
      : mSince(aSince) {}
1523
0
1524
0
    // Return true if any files under aPath is modified after |mSince|.
1525
0
    bool IsModifiedAfter(nsIFile* aPath) {
1526
0
      PRTime lastModified;
1527
0
      nsresult rv = aPath->GetLastModifiedTime(&lastModified);
1528
0
      if (NS_SUCCEEDED(rv) && lastModified >= mSince) {
1529
0
        return true;
1530
0
      }
1531
0
      DirectoryEnumerator iter(aPath, DirectoryEnumerator::FilesAndDirs);
1532
0
      for (nsCOMPtr<nsIFile> dirEntry; (dirEntry = iter.Next()) != nullptr;) {
1533
0
        if (IsModifiedAfter(dirEntry)) {
1534
0
          return true;
1535
0
        }
1536
0
      }
1537
0
      return false;
1538
0
    }
1539
0
1540
0
    // |aPath| is $profileDir/gmp/$platform/$gmpName/id/$originHash/
1541
0
    bool operator()(nsIFile* aPath) override {
1542
0
      if (IsModifiedAfter(aPath)) {
1543
0
        return true;
1544
0
      }
1545
0
1546
0
      nsAutoCString salt;
1547
0
      if (NS_WARN_IF(NS_FAILED(ReadSalt(aPath, salt)))) {
1548
0
        return false;
1549
0
      }
1550
0
1551
0
      // $profileDir/gmp/$platform/$gmpName/id/
1552
0
      nsCOMPtr<nsIFile> idDir;
1553
0
      if (NS_WARN_IF(NS_FAILED(aPath->GetParent(getter_AddRefs(idDir))))) {
1554
0
        return false;
1555
0
      }
1556
0
      // $profileDir/gmp/$platform/$gmpName/
1557
0
      nsCOMPtr<nsIFile> temp;
1558
0
      if (NS_WARN_IF(NS_FAILED(idDir->GetParent(getter_AddRefs(temp))))) {
1559
0
        return false;
1560
0
      }
1561
0
1562
0
      // $profileDir/gmp/$platform/$gmpName/storage/
1563
0
      if (NS_WARN_IF(NS_FAILED(temp->Append(NS_LITERAL_STRING("storage"))))) {
1564
0
        return false;
1565
0
      }
1566
0
      // $profileDir/gmp/$platform/$gmpName/storage/$originSalt
1567
0
      return NS_SUCCEEDED(temp->AppendNative(salt)) && IsModifiedAfter(temp);
1568
0
    }
1569
0
  private:
1570
0
    const PRTime mSince;
1571
0
  } filter(aSince);
1572
0
1573
0
  ClearNodeIdAndPlugin(filter);
1574
0
1575
0
  nsCOMPtr<nsIRunnable> task
1576
0
    = new NotifyObserversTask("gmp-clear-storage-complete");
1577
0
  mMainThread->Dispatch(task.forget());
1578
0
}
1579
1580
NS_IMETHODIMP
1581
GeckoMediaPluginServiceParent::ForgetThisSite(const nsAString& aSite,
1582
                                              const nsAString& aPattern)
1583
0
{
1584
0
  MOZ_ASSERT(NS_IsMainThread());
1585
0
1586
0
  mozilla::OriginAttributesPattern pattern;
1587
0
1588
0
  if (!pattern.Init(aPattern)) {
1589
0
    return NS_ERROR_INVALID_ARG;
1590
0
  }
1591
0
1592
0
  return ForgetThisSiteNative(aSite, pattern);
1593
0
}
1594
1595
nsresult
1596
GeckoMediaPluginServiceParent::ForgetThisSiteNative(const nsAString& aSite,
1597
                                                    const mozilla::OriginAttributesPattern& aPattern)
1598
0
{
1599
0
  MOZ_ASSERT(NS_IsMainThread());
1600
0
1601
0
  return GMPDispatch(
1602
0
    NewRunnableMethod<nsCString, mozilla::OriginAttributesPattern>(
1603
0
      "gmp::GeckoMediaPluginServiceParent::ForgetThisSiteOnGMPThread",
1604
0
      this,
1605
0
      &GeckoMediaPluginServiceParent::ForgetThisSiteOnGMPThread,
1606
0
      NS_ConvertUTF16toUTF8(aSite),
1607
0
      aPattern));
1608
0
}
1609
1610
0
static bool IsNodeIdValid(GMPParent* aParent) {
1611
0
  return !aParent->GetNodeId().IsEmpty();
1612
0
}
1613
1614
static nsCOMPtr<nsIAsyncShutdownClient>
1615
GetShutdownBarrier()
1616
0
{
1617
0
  nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown();
1618
0
  MOZ_RELEASE_ASSERT(svc);
1619
0
1620
0
  nsCOMPtr<nsIAsyncShutdownClient> barrier;
1621
0
  nsresult rv = svc->GetXpcomWillShutdown(getter_AddRefs(barrier));
1622
0
1623
0
  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
1624
0
  MOZ_RELEASE_ASSERT(barrier);
1625
0
  return barrier.forget();
1626
0
}
1627
1628
NS_IMETHODIMP
1629
GeckoMediaPluginServiceParent::GetName(nsAString& aName)
1630
0
{
1631
0
  aName = NS_LITERAL_STRING("GeckoMediaPluginServiceParent: shutdown");
1632
0
  return NS_OK;
1633
0
}
1634
1635
NS_IMETHODIMP
1636
GeckoMediaPluginServiceParent::GetState(nsIPropertyBag**)
1637
0
{
1638
0
  return NS_OK;
1639
0
}
1640
1641
NS_IMETHODIMP
1642
GeckoMediaPluginServiceParent::BlockShutdown(nsIAsyncShutdownClient*)
1643
0
{
1644
0
  return NS_OK;
1645
0
}
1646
1647
void
1648
GeckoMediaPluginServiceParent::ServiceUserCreated(
1649
  GMPServiceParent* aServiceParent)
1650
0
{
1651
0
  MOZ_ASSERT(NS_IsMainThread());
1652
0
  MutexAutoLock lock(mMutex);
1653
0
  MOZ_ASSERT(!mServiceParents.Contains(aServiceParent));
1654
0
  mServiceParents.AppendElement(aServiceParent);
1655
0
  if (mServiceParents.Length() == 1) {
1656
0
    nsresult rv = GetShutdownBarrier()->AddBlocker(
1657
0
      this, NS_LITERAL_STRING(__FILE__), __LINE__,
1658
0
      NS_LITERAL_STRING("GeckoMediaPluginServiceParent shutdown"));
1659
0
    MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
1660
0
  }
1661
0
}
1662
1663
void
1664
GeckoMediaPluginServiceParent::ServiceUserDestroyed(
1665
  GMPServiceParent* aServiceParent)
1666
0
{
1667
0
  MOZ_ASSERT(NS_IsMainThread());
1668
0
  MutexAutoLock lock(mMutex);
1669
0
  MOZ_ASSERT(mServiceParents.Length() > 0);
1670
0
  MOZ_ASSERT(mServiceParents.Contains(aServiceParent));
1671
0
  mServiceParents.RemoveElement(aServiceParent);
1672
0
  if (mServiceParents.IsEmpty()) {
1673
0
    nsresult rv = GetShutdownBarrier()->RemoveBlocker(this);
1674
0
    MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
1675
0
  }
1676
0
}
1677
1678
void
1679
GeckoMediaPluginServiceParent::ClearStorage()
1680
0
{
1681
0
  MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread());
1682
0
  LOGD(("%s::%s", __CLASS__, __FUNCTION__));
1683
0
1684
0
  // Kill plugins with valid nodeIDs.
1685
0
  KillPlugins(mPlugins, mMutex, &IsNodeIdValid);
1686
0
1687
0
  nsCOMPtr<nsIFile> path; // $profileDir/gmp/$platform/
1688
0
  nsresult rv = GetStorageDir(getter_AddRefs(path));
1689
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1690
0
    return;
1691
0
  }
1692
0
1693
0
  if (NS_FAILED(DeleteDir(path))) {
1694
0
    NS_WARNING("Failed to delete GMP storage directory");
1695
0
  }
1696
0
1697
0
  // Clear private-browsing storage.
1698
0
  mTempGMPStorage.Clear();
1699
0
1700
0
  nsCOMPtr<nsIRunnable> task
1701
0
    = new NotifyObserversTask("gmp-clear-storage-complete");
1702
0
  mMainThread->Dispatch(task.forget());
1703
0
}
1704
1705
already_AddRefed<GMPParent>
1706
GeckoMediaPluginServiceParent::GetById(uint32_t aPluginId)
1707
0
{
1708
0
  MutexAutoLock lock(mMutex);
1709
0
  for (const RefPtr<GMPParent>& gmp : mPlugins) {
1710
0
    if (gmp->GetPluginId() == aPluginId) {
1711
0
      return do_AddRef(gmp);
1712
0
    }
1713
0
  }
1714
0
  return nullptr;
1715
0
}
1716
1717
GMPServiceParent::GMPServiceParent(GeckoMediaPluginServiceParent* aService)
1718
  : mService(aService)
1719
0
{
1720
0
  MOZ_ASSERT(mService);
1721
0
  mService->ServiceUserCreated(this);
1722
0
}
1723
1724
GMPServiceParent::~GMPServiceParent()
1725
0
{
1726
0
  MOZ_ASSERT(mService);
1727
0
  mService->ServiceUserDestroyed(this);
1728
0
}
1729
1730
mozilla::ipc::IPCResult
1731
GMPServiceParent::RecvLaunchGMP(const nsCString& aNodeId,
1732
                                const nsCString& aAPI,
1733
                                nsTArray<nsCString>&& aTags,
1734
                                nsTArray<ProcessId>&& aAlreadyBridgedTo,
1735
                                uint32_t* aOutPluginId,
1736
                                ProcessId* aOutProcessId,
1737
                                nsCString* aOutDisplayName,
1738
                                Endpoint<PGMPContentParent>* aOutEndpoint,
1739
                                nsresult* aOutRv,
1740
                                nsCString* aOutErrorDescription)
1741
0
{
1742
0
  if (mService->IsShuttingDown()) {
1743
0
    *aOutRv = NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
1744
0
    *aOutErrorDescription = NS_LITERAL_CSTRING("Service is shutting down.");
1745
0
    return IPC_OK();
1746
0
  }
1747
0
1748
0
  RefPtr<GMPParent> gmp = mService->SelectPluginForAPI(aNodeId, aAPI, aTags);
1749
0
  if (gmp) {
1750
0
    *aOutPluginId = gmp->GetPluginId();
1751
0
  } else {
1752
0
    *aOutRv = NS_ERROR_FAILURE;
1753
0
    *aOutErrorDescription = NS_LITERAL_CSTRING("SelectPluginForAPI returns nullptr.");
1754
0
    *aOutPluginId = 0;
1755
0
    return IPC_OK();
1756
0
  }
1757
0
1758
0
  if (!gmp->EnsureProcessLoaded(aOutProcessId)) {
1759
0
    *aOutRv = NS_ERROR_FAILURE;
1760
0
    *aOutErrorDescription = NS_LITERAL_CSTRING("Process has not loaded.");
1761
0
    return IPC_OK();
1762
0
  }
1763
0
1764
0
  *aOutDisplayName = gmp->GetDisplayName();
1765
0
1766
0
  if (aAlreadyBridgedTo.Contains(*aOutProcessId)) {
1767
0
    *aOutRv = NS_OK;
1768
0
    return IPC_OK();
1769
0
  }
1770
0
1771
0
  Endpoint<PGMPContentParent> parent;
1772
0
  Endpoint<PGMPContentChild> child;
1773
0
  nsresult rv =
1774
0
    PGMPContent::CreateEndpoints(OtherPid(), *aOutProcessId, &parent, &child);
1775
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1776
0
    *aOutRv = rv;
1777
0
    *aOutErrorDescription =
1778
0
      NS_LITERAL_CSTRING("PGMPContent::CreateEndpoints failed.");
1779
0
    return IPC_OK();
1780
0
  }
1781
0
1782
0
  *aOutEndpoint = std::move(parent);
1783
0
1784
0
  if (!gmp->SendInitGMPContentChild(std::move(child))) {
1785
0
    *aOutRv = NS_ERROR_FAILURE;
1786
0
    *aOutErrorDescription =
1787
0
      NS_LITERAL_CSTRING("SendInitGMPContentChild failed.");
1788
0
    return IPC_OK();
1789
0
  }
1790
0
1791
0
  gmp->IncrementGMPContentChildCount();
1792
0
1793
0
  *aOutRv = NS_OK;
1794
0
  return IPC_OK();
1795
0
}
1796
1797
mozilla::ipc::IPCResult
1798
GMPServiceParent::RecvLaunchGMPForNodeId(
1799
  const NodeIdData& aNodeId,
1800
  const nsCString& aApi,
1801
  nsTArray<nsCString>&& aTags,
1802
  nsTArray<ProcessId>&& aAlreadyBridgedTo,
1803
  uint32_t* aOutPluginId,
1804
  ProcessId* aOutId,
1805
  nsCString* aOutDisplayName,
1806
  Endpoint<PGMPContentParent>* aOutEndpoint,
1807
  nsresult* aOutRv,
1808
  nsCString* aOutErrorDescription)
1809
0
{
1810
0
  nsCString nodeId;
1811
0
  nsresult rv = mService->GetNodeId(
1812
0
    aNodeId.mOrigin(), aNodeId.mTopLevelOrigin(), aNodeId.mGMPName(), nodeId);
1813
0
  if (!NS_SUCCEEDED(rv)) {
1814
0
    *aOutRv = rv;
1815
0
    *aOutErrorDescription = NS_LITERAL_CSTRING("GetNodeId failed.");
1816
0
    return IPC_OK();
1817
0
  }
1818
0
  return RecvLaunchGMP(nodeId,
1819
0
                       aApi,
1820
0
                       std::move(aTags),
1821
0
                       std::move(aAlreadyBridgedTo),
1822
0
                       aOutPluginId,
1823
0
                       aOutId,
1824
0
                       aOutDisplayName,
1825
0
                       aOutEndpoint,
1826
0
                       aOutRv,
1827
0
                       aOutErrorDescription);
1828
0
}
1829
1830
mozilla::ipc::IPCResult
1831
GMPServiceParent::RecvGetGMPNodeId(const nsString& aOrigin,
1832
                                   const nsString& aTopLevelOrigin,
1833
                                   const nsString& aGMPName,
1834
                                   nsCString* aID)
1835
0
{
1836
0
  nsresult rv = mService->GetNodeId(aOrigin, aTopLevelOrigin, aGMPName, *aID);
1837
0
  if (!NS_SUCCEEDED(rv)) {
1838
0
    return IPC_FAIL_NO_REASON(this);
1839
0
  }
1840
0
  return IPC_OK();
1841
0
}
1842
1843
class DeleteGMPServiceParent : public mozilla::Runnable
1844
{
1845
public:
1846
  explicit DeleteGMPServiceParent(GMPServiceParent* aToDelete)
1847
    : Runnable("gmp::DeleteGMPServiceParent")
1848
    , mToDelete(aToDelete)
1849
0
  {
1850
0
  }
1851
1852
  NS_IMETHOD Run() override
1853
0
  {
1854
0
    return NS_OK;
1855
0
  }
1856
1857
private:
1858
  nsAutoPtr<GMPServiceParent> mToDelete;
1859
};
1860
1861
void GMPServiceParent::CloseTransport(Monitor* aSyncMonitor, bool* aCompleted)
1862
0
{
1863
0
  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
1864
0
1865
0
  MonitorAutoLock lock(*aSyncMonitor);
1866
0
1867
0
  // This deletes the transport.
1868
0
  SetTransport(nullptr);
1869
0
1870
0
  *aCompleted = true;
1871
0
  lock.NotifyAll();
1872
0
}
1873
1874
void
1875
GMPServiceParent::ActorDestroy(ActorDestroyReason aWhy)
1876
0
{
1877
0
  Monitor monitor("DeleteGMPServiceParent");
1878
0
  bool completed = false;
1879
0
1880
0
  // Make sure the IPC channel is closed before destroying mToDelete.
1881
0
  MonitorAutoLock lock(monitor);
1882
0
  RefPtr<Runnable> task = NewNonOwningRunnableMethod<Monitor*, bool*>(
1883
0
    "gmp::GMPServiceParent::CloseTransport",
1884
0
    this,
1885
0
    &GMPServiceParent::CloseTransport,
1886
0
    &monitor,
1887
0
    &completed);
1888
0
  XRE_GetIOMessageLoop()->PostTask(task.forget());
1889
0
1890
0
  while (!completed) {
1891
0
    lock.Wait();
1892
0
  }
1893
0
1894
0
  // Dispatch a task to the current thread to ensure we don't delete the
1895
0
  // GMPServiceParent until the current calling context is finished with
1896
0
  // the object.
1897
0
  GMPServiceParent* self = this;
1898
0
  NS_DispatchToCurrentThread(
1899
0
    NS_NewRunnableFunction("gmp::GMPServiceParent::ActorDestroy", [self]() {
1900
0
      // The GMPServiceParent must be destroyed on the main thread.
1901
0
      self->mService->mMainThread->Dispatch(
1902
0
        NS_NewRunnableFunction(
1903
0
          "gmp::GMPServiceParent::ActorDestroy", [self]() { delete self; }),
1904
0
        NS_DISPATCH_NORMAL);
1905
0
    }));
1906
0
}
1907
1908
class OpenPGMPServiceParent : public mozilla::Runnable
1909
{
1910
public:
1911
  OpenPGMPServiceParent(GMPServiceParent* aGMPServiceParent,
1912
                        ipc::Endpoint<PGMPServiceParent>&& aEndpoint,
1913
                        bool* aResult)
1914
    : Runnable("gmp::OpenPGMPServiceParent")
1915
    , mGMPServiceParent(aGMPServiceParent)
1916
    , mEndpoint(std::move(aEndpoint))
1917
    , mResult(aResult)
1918
0
  {
1919
0
  }
1920
1921
  NS_IMETHOD Run() override
1922
0
  {
1923
0
    *mResult = mEndpoint.Bind(mGMPServiceParent);
1924
0
    return NS_OK;
1925
0
  }
1926
1927
private:
1928
  GMPServiceParent* mGMPServiceParent;
1929
  ipc::Endpoint<PGMPServiceParent> mEndpoint;
1930
  bool* mResult;
1931
};
1932
1933
/* static */
1934
bool
1935
GMPServiceParent::Create(Endpoint<PGMPServiceParent>&& aGMPService)
1936
0
{
1937
0
  RefPtr<GeckoMediaPluginServiceParent> gmp =
1938
0
    GeckoMediaPluginServiceParent::GetSingleton();
1939
0
1940
0
  if (gmp->mShuttingDown) {
1941
0
    // Shutdown is initiated. There is no point creating a new actor.
1942
0
    return false;
1943
0
  }
1944
0
1945
0
  nsCOMPtr<nsIThread> gmpThread;
1946
0
  nsresult rv = gmp->GetThread(getter_AddRefs(gmpThread));
1947
0
  NS_ENSURE_SUCCESS(rv, false);
1948
0
1949
0
  nsAutoPtr<GMPServiceParent> serviceParent(new GMPServiceParent(gmp));
1950
0
  bool ok;
1951
0
  rv = gmpThread->Dispatch(new OpenPGMPServiceParent(serviceParent,
1952
0
                                                     std::move(aGMPService),
1953
0
                                                     &ok),
1954
0
                           NS_DISPATCH_SYNC);
1955
0
1956
0
  if (NS_WARN_IF(NS_FAILED(rv) || !ok)) {
1957
0
    return false;
1958
0
  }
1959
0
1960
0
  // Now that the service parent is set up, it will be destroyed by
1961
0
  // ActorDestroy.
1962
0
  Unused << serviceParent.forget();
1963
0
1964
0
  return true;
1965
0
}
1966
1967
} // namespace gmp
1968
} // namespace mozilla
1969
1970
#undef NS_DispatchToMainThread