tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

GMPServiceParent.cpp (67959B)


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