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

Generated by: LCOV version 1.13