LCOV - code coverage report
Current view: top level - dom/media/gmp - GMPServiceParent.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 152 946 16.1 %
Date: 2017-07-14 16:53:18 Functions: 30 117 25.6 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13