Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/dom/media/gmp/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 17 kB image not shown  

Quelle  GMPService.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "GMPService.h"

#include "ChromiumCDMParent.h"
#include "GMPLog.h"
#include "GMPParent.h"
#include "GMPProcessParent.h"
#include "GMPServiceChild.h"
#include "GMPServiceParent.h"
#include "GMPVideoDecoderParent.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/PluginCrashedEvent.h"
#include "nsThreadUtils.h"
#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
#  include "mozilla/SandboxInfo.h"
#endif
#include "VideoUtils.h"
#include "mozilla/Services.h"
#include "mozilla/SyncRunnable.h"
#include "mozilla/Unused.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsComponentManagerUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "nsGlobalWindowInner.h"
#include "nsHashKeys.h"
#include "nsIObserverService.h"
#include "nsIXULAppInfo.h"
#include "nsNativeCharsetUtils.h"
#include "nsXPCOMPrivate.h"
#include "prio.h"
#include "runnable_utils.h"

namespace mozilla {

LogModule* GetGMPLog() {
  static LazyLogModule sLog("GMP");
  return sLog;
}

LogModule* GetGMPLibraryLog() {
  static LazyLogModule sLog("GMPLibrary");
  return sLog;
}

GMPLogLevel GetGMPLibraryLogLevel() {
  switch (GetGMPLibraryLog()->Level()) {
    case LogLevel::Disabled:
      return kGMPLogQuiet;
    case LogLevel::Error:
      return kGMPLogError;
    case LogLevel::Warning:
      return kGMPLogWarning;
    case LogLevel::Info:
      return kGMPLogInfo;
    case LogLevel::Debug:
      return kGMPLogDebug;
    case LogLevel::Verbose:
      return kGMPLogDetail;
  }
  return kGMPLogInvalid;
}

#ifdef __CLASS__
#  undef __CLASS__
#endif
#define __CLASS__ "GMPService"

namespace gmp {

static StaticRefPtr<GeckoMediaPluginService> sSingletonService;

class GMPServiceCreateHelper final : public mozilla::Runnable {
  RefPtr<GeckoMediaPluginService> mService;

 public:
  static already_AddRefed<GeckoMediaPluginService> GetOrCreate() {
    RefPtr<GeckoMediaPluginService> service;

    if (NS_IsMainThread()) {
      service = GetOrCreateOnMainThread();
    } else {
      RefPtr<GMPServiceCreateHelper> createHelper =
          new GMPServiceCreateHelper();

      mozilla::SyncRunnable::DispatchToThread(GetMainThreadSerialEventTarget(),
                                              createHelper, true);

      service = std::move(createHelper->mService);
    }

    return service.forget();
  }

 private:
  GMPServiceCreateHelper() : Runnable("GMPServiceCreateHelper") {}

  ~GMPServiceCreateHelper() { MOZ_ASSERT(!mService); }

  static already_AddRefed<GeckoMediaPluginService> GetOrCreateOnMainThread() {
    MOZ_ASSERT(NS_IsMainThread());

    if (!sSingletonService) {
      if (XRE_IsParentProcess()) {
        RefPtr<GeckoMediaPluginServiceParent> service =
            new GeckoMediaPluginServiceParent();
        if (NS_WARN_IF(NS_FAILED(service->Init()))) {
          return nullptr;
        }
        sSingletonService = service;
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
        // GMPProcessParent should only be instantiated in the parent
        // so initialization only needs to be done in the parent.
        GMPProcessParent::InitStaticMainThread();
#endif
      } else {
        RefPtr<GeckoMediaPluginServiceChild> service =
            new GeckoMediaPluginServiceChild();
        if (NS_WARN_IF(NS_FAILED(service->Init()))) {
          return nullptr;
        }
        sSingletonService = service;
      }
      ClearOnShutdown(&sSingletonService);
    }

    RefPtr<GeckoMediaPluginService> service = sSingletonService.get();
    return service.forget();
  }

  NS_IMETHOD
  Run() override {
    MOZ_ASSERT(NS_IsMainThread());

    mService = GetOrCreateOnMainThread();
    return NS_OK;
  }
};

already_AddRefed<GeckoMediaPluginService>
GeckoMediaPluginService::GetGeckoMediaPluginService() {
  return GMPServiceCreateHelper::GetOrCreate();
}

NS_IMPL_ISUPPORTS(GeckoMediaPluginService, mozIGeckoMediaPluginService,
                  nsIObserver)

GeckoMediaPluginService::GeckoMediaPluginService()
    : mMutex("GeckoMediaPluginService::mMutex"),
      mMainThread(GetMainThreadSerialEventTarget()),
      mGMPThreadShutdown(false),
      mShuttingDownOnGMPThread(false),
      mXPCOMWillShutdown(false) {
  MOZ_ASSERT(NS_IsMainThread());

  nsCOMPtr<nsIXULAppInfo> appInfo =
      do_GetService("@mozilla.org/xre/app-info;1");
  if (appInfo) {
    nsAutoCString version;
    nsAutoCString buildID;
    if (NS_SUCCEEDED(appInfo->GetVersion(version)) &&
        NS_SUCCEEDED(appInfo->GetAppBuildID(buildID))) {
      GMP_LOG_DEBUG(
          "GeckoMediaPluginService created; Gecko version=%s buildID=%s",
          version.get(), buildID.get());
    }
  }
}

GeckoMediaPluginService::~GeckoMediaPluginService() = default;

NS_IMETHODIMP
GeckoMediaPluginService::RunPluginCrashCallbacks(
    uint32_t aPluginId, const nsACString& aPluginName) {
  MOZ_ASSERT(NS_IsMainThread());
  GMP_LOG_DEBUG("%s::%s(%i)", __CLASS__, __FUNCTION__, aPluginId);

  mozilla::UniquePtr<nsTArray<RefPtr<GMPCrashHelper>>> helpers;
  {
    MutexAutoLock lock(mMutex);
    mPluginCrashHelpers.Remove(aPluginId, &helpers);
  }
  if (!helpers) {
    GMP_LOG_DEBUG("%s::%s(%i) No crash helpers, not handling crash.", __CLASS__,
                  __FUNCTION__, aPluginId);
    return NS_OK;
  }

  for (const auto& helper : *helpers) {
    nsCOMPtr<nsPIDOMWindowInner> window = helper->GetPluginCrashedEventTarget();
    if (NS_WARN_IF(!window)) {
      continue;
    }
    RefPtr<dom::Document> document(window->GetExtantDoc());
    if (NS_WARN_IF(!document)) {
      continue;
    }

    dom::PluginCrashedEventInit init;
    init.mPluginID = aPluginId;
    init.mBubbles = true;
    init.mCancelable = true;
    init.mGmpPlugin = true;
    CopyUTF8toUTF16(aPluginName, init.mPluginName);
    init.mSubmittedCrashReport = false;
    RefPtr<dom::PluginCrashedEvent> event =
        dom::PluginCrashedEvent::Constructor(document, u"PluginCrashed"_ns,
                                             init);
    event->SetTrusted(true);
    event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;

    // MOZ_KnownLive due to bug 1506441
    EventDispatcher::DispatchDOMEvent(
        MOZ_KnownLive(nsGlobalWindowInner::Cast(window)), nullptr, event,
        nullptr, nullptr);
  }

  return NS_OK;
}

nsresult GeckoMediaPluginService::Init() {
  MOZ_ASSERT(NS_IsMainThread());

  nsCOMPtr<nsIObserverService> obsService =
      mozilla::services::GetObserverService();
  MOZ_ASSERT(obsService);
  MOZ_ALWAYS_SUCCEEDS(obsService->AddObserver(
      this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false));

  // Kick off scanning for plugins
  nsCOMPtr<nsIThread> thread;
  return GetThread(getter_AddRefs(thread));
}

RefPtr<GetCDMParentPromise> GeckoMediaPluginService::GetCDM(
    const NodeIdParts& aNodeIdParts, const nsACString& aKeySystem,
    GMPCrashHelper* aHelper) {
  AssertOnGMPThread();

  if (mShuttingDownOnGMPThread || aKeySystem.IsEmpty()) {
    nsPrintfCString reason(
        "%s::%s failed, aKeySystem.IsEmpty() = %d, mShuttingDownOnGMPThread = "
        "%d.",
        __CLASS__, __FUNCTION__, aKeySystem.IsEmpty(),
        mShuttingDownOnGMPThread);
    return GetCDMParentPromise::CreateAndReject(
        MediaResult(NS_ERROR_FAILURE, reason.get()), __func__);
  }

  typedef MozPromiseHolder<GetCDMParentPromise> PromiseHolder;
  PromiseHolder* rawHolder(new PromiseHolder());
  RefPtr<GetCDMParentPromise> promise = rawHolder->Ensure(__func__);
  nsCOMPtr<nsISerialEventTarget> thread(GetGMPThread());
  RefPtr<GMPCrashHelper> helper(aHelper);
  nsTArray<nsCString> tags{nsCString{aKeySystem}};
  GetContentParent(aHelper, NodeIdVariant{aNodeIdParts},
                   nsLiteralCString(CHROMIUM_CDM_API), tags)
      ->Then(
          thread, __func__,
          [rawHolder, helper, keySystem = nsCString{aKeySystem}](
              const RefPtr<GMPContentParentCloseBlocker>& wrapper) {
            RefPtr<GMPContentParent> parent = wrapper->mParent;
            MOZ_ASSERT(
                parent,
                "Wrapper should wrap a valid parent if we're in this path.");
            UniquePtr<PromiseHolder> holder(rawHolder);
            RefPtr<ChromiumCDMParent> cdm = parent->GetChromiumCDM(keySystem);
            if (!cdm) {
              nsPrintfCString reason(
                  "%s::%s failed since GetChromiumCDM returns nullptr.",
                  __CLASS__, __FUNCTION__);
              holder->Reject(MediaResult(NS_ERROR_FAILURE, reason.get()),
                             __func__);
              return;
            }
            if (helper) {
              cdm->SetCrashHelper(helper);
            }
            holder->Resolve(cdm, __func__);
          },
          [rawHolder](MediaResult result) {
            nsPrintfCString reason(
                "%s::%s failed since GetContentParent rejects the promise with "
                "reason %s.",
                __CLASS__, __FUNCTION__, result.Description().get());
            UniquePtr<PromiseHolder> holder(rawHolder);
            holder->Reject(MediaResult(NS_ERROR_FAILURE, reason.get()),
                           __func__);
          });

  return promise;
}

#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
RefPtr<GetGMPContentParentPromise>
GeckoMediaPluginService::GetContentParentForTest() {
  AssertOnGMPThread();

  nsTArray<nsCString> tags;
  tags.AppendElement("fake"_ns);

  const nsString origin1 = u"http://example1.com"_ns;
  const nsString origin2 = u"http://example2.org"_ns;
  const nsString gmpName = u"gmp-fake"_ns;

  NodeIdParts nodeIdParts = NodeIdParts{origin1, origin2, gmpName};

  if (mShuttingDownOnGMPThread) {
    nsPrintfCString reason("%s::%s failed, mShuttingDownOnGMPThread = %d.",
                           __CLASS__, __FUNCTION__, mShuttingDownOnGMPThread);
    return GetGMPContentParentPromise::CreateAndReject(
        MediaResult(NS_ERROR_FAILURE, reason.get()), __func__);
  }

  using PromiseHolder = MozPromiseHolder<GetGMPContentParentPromise>;
  PromiseHolder* rawHolder(new PromiseHolder());
  RefPtr<GetGMPContentParentPromise> promise = rawHolder->Ensure(__func__);
  nsCOMPtr<nsISerialEventTarget> thread(GetGMPThread());
  GetContentParent(nullptr, NodeIdVariant{nodeIdParts},
                   nsLiteralCString(CHROMIUM_CDM_API), tags)
      ->Then(
          thread, __func__,
          [rawHolder](const RefPtr<GMPContentParentCloseBlocker>& wrapper) {
            RefPtr<GMPContentParent> parent = wrapper->mParent;
            MOZ_ASSERT(
                parent,
                "Wrapper should wrap a valid parent if we're in this path.");
            UniquePtr<PromiseHolder> holder(rawHolder);
            if (!parent) {
              nsPrintfCString reason("%s::%s failed since no GMPContentParent.",
                                     __CLASS__, __FUNCTION__);
              holder->Reject(MediaResult(NS_ERROR_FAILURE, reason.get()),
                             __func__);
              return;
            }
            holder->Resolve(wrapper, __func__);
          },
          [rawHolder](const MediaResult& result) {
            nsPrintfCString reason(
                "%s::%s failed since GetContentParent rejects the promise with "
                "reason %s.",
                __CLASS__, __FUNCTION__, result.Description().get());
            UniquePtr<PromiseHolder> holder(rawHolder);
            holder->Reject(MediaResult(NS_ERROR_FAILURE, reason.get()),
                           __func__);
          });

  return promise;
}
#endif

void GeckoMediaPluginService::ShutdownGMPThread() {
  GMP_LOG_DEBUG("%s::%s", __CLASS__, __FUNCTION__);
  nsCOMPtr<nsIThread> gmpThread;
  {
    MutexAutoLock lock(mMutex);
    mGMPThreadShutdown = true;
    mGMPThread.swap(gmpThread);
  }

  if (gmpThread) {
    gmpThread->Shutdown();
  }
}

nsresult GeckoMediaPluginService::GMPDispatch(nsIRunnable* event,
                                              uint32_t flags) {
  nsCOMPtr<nsIRunnable> r(event);
  return GMPDispatch(r.forget(), flags);
}

nsresult GeckoMediaPluginService::GMPDispatch(
    already_AddRefed<nsIRunnable> event, uint32_t flags) {
  nsCOMPtr<nsIRunnable> r(event);
  nsCOMPtr<nsIThread> thread;
  nsresult rv = GetThread(getter_AddRefs(thread));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }
  return thread->Dispatch(r, flags);
}

// always call with getter_AddRefs, because it does
NS_IMETHODIMP
GeckoMediaPluginService::GetThread(nsIThread** aThread) {
  MOZ_ASSERT(aThread);

  // This can be called from any thread.
  MutexAutoLock lock(mMutex);

  return GetThreadLocked(aThread);
}

// always call with getter_AddRefs, because it does
nsresult GeckoMediaPluginService::GetThreadLocked(nsIThread** aThread) {
  MOZ_ASSERT(aThread);

  mMutex.AssertCurrentThreadOwns();

  if (!mGMPThread) {
    // Don't allow the thread to be created after shutdown has started.
    if (mGMPThreadShutdown) {
      return NS_ERROR_FAILURE;
    }

    nsresult rv = NS_NewNamedThread("GMPThread", getter_AddRefs(mGMPThread));
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }

    // Tell the thread to initialize plugins
    InitializePlugins(mGMPThread);
  }

  nsCOMPtr<nsIThread> copy = mGMPThread;
  copy.forget(aThread);

  return NS_OK;
}

already_AddRefed<nsISerialEventTarget> GeckoMediaPluginService::GetGMPThread() {
  nsCOMPtr<nsISerialEventTarget> thread;
  {
    MutexAutoLock lock(mMutex);
    thread = mGMPThread;
  }
  return thread.forget();
}

NS_IMETHODIMP
GeckoMediaPluginService::GetGMPVideoDecoder(
    GMPCrashHelper* aHelper, nsTArray<nsCString>* aTags,
    const nsACString& aNodeId,
    UniquePtr<GetGMPVideoDecoderCallback>&& aCallback) {
  AssertOnGMPThread();
  NS_ENSURE_ARG(aTags && aTags->Length() > 0);
  NS_ENSURE_ARG(aCallback);

  if (mShuttingDownOnGMPThread) {
    return NS_ERROR_FAILURE;
  }

  GetGMPVideoDecoderCallback* rawCallback = aCallback.release();
  nsCOMPtr<nsISerialEventTarget> thread(GetGMPThread());
  RefPtr<GMPCrashHelper> helper(aHelper);
  GetContentParent(aHelper, NodeIdVariant{nsCString(aNodeId)},
                   nsLiteralCString(GMP_API_VIDEO_DECODER), *aTags)
      ->Then(
          thread, __func__,
          [rawCallback,
           helper](const RefPtr<GMPContentParentCloseBlocker>& wrapper) {
            RefPtr<GMPContentParent> parent = wrapper->mParent;
            UniquePtr<GetGMPVideoDecoderCallback> callback(rawCallback);
            GMPVideoDecoderParent* actor = nullptr;
            GMPVideoHostImpl* host = nullptr;
            if (parent && NS_SUCCEEDED(parent->GetGMPVideoDecoder(&actor))) {
              host = &(actor->Host());
              actor->SetCrashHelper(helper);
            }
            callback->Done(actor, host);
          },
          [rawCallback] {
            UniquePtr<GetGMPVideoDecoderCallback> callback(rawCallback);
            callback->Done(nullptr, nullptr);
          });

  return NS_OK;
}

NS_IMETHODIMP
GeckoMediaPluginService::GetGMPVideoEncoder(
    GMPCrashHelper* aHelper, nsTArray<nsCString>* aTags,
    const nsACString& aNodeId,
    UniquePtr<GetGMPVideoEncoderCallback>&& aCallback) {
  AssertOnGMPThread();
  NS_ENSURE_ARG(aTags && aTags->Length() > 0);
  NS_ENSURE_ARG(aCallback);

  if (mShuttingDownOnGMPThread) {
    return NS_ERROR_FAILURE;
  }

  GetGMPVideoEncoderCallback* rawCallback = aCallback.release();
  nsCOMPtr<nsISerialEventTarget> thread(GetGMPThread());
  RefPtr<GMPCrashHelper> helper(aHelper);
  GetContentParent(aHelper, NodeIdVariant{nsCString(aNodeId)},
                   nsLiteralCString(GMP_API_VIDEO_ENCODER), *aTags)
      ->Then(
          thread, __func__,
          [rawCallback,
           helper](const RefPtr<GMPContentParentCloseBlocker>& wrapper) {
            RefPtr<GMPContentParent> parent = wrapper->mParent;
            UniquePtr<GetGMPVideoEncoderCallback> callback(rawCallback);
            GMPVideoEncoderParent* actor = nullptr;
            GMPVideoHostImpl* host = nullptr;
            if (parent && NS_SUCCEEDED(parent->GetGMPVideoEncoder(&actor))) {
              host = &(actor->Host());
              actor->SetCrashHelper(helper);
            }
            callback->Done(actor, host);
          },
          [rawCallback] {
            UniquePtr<GetGMPVideoEncoderCallback> callback(rawCallback);
            callback->Done(nullptr, nullptr);
          });

  return NS_OK;
}

void GeckoMediaPluginService::ConnectCrashHelper(uint32_t aPluginId,
                                                 GMPCrashHelper* aHelper) {
  if (!aHelper) {
    return;
  }

  MutexAutoLock lock(mMutex);
  mPluginCrashHelpers.WithEntryHandle(aPluginId, [&](auto&& entry) {
    if (!entry) {
      entry.Insert(MakeUnique<nsTArray<RefPtr<GMPCrashHelper>>>());
    } else if (entry.Data()->Contains(aHelper)) {
      return;
    }
    entry.Data()->AppendElement(aHelper);
  });
}

void GeckoMediaPluginService::DisconnectCrashHelper(GMPCrashHelper* aHelper) {
  if (!aHelper) {
    return;
  }
  MutexAutoLock lock(mMutex);
  for (auto iter = mPluginCrashHelpers.Iter(); !iter.Done(); iter.Next()) {
    nsTArray<RefPtr<GMPCrashHelper>>* helpers = iter.UserData();
    if (!helpers->Contains(aHelper)) {
      continue;
    }
    helpers->RemoveElement(aHelper);
    MOZ_ASSERT(!helpers->Contains(aHelper));  // Ensure there aren't duplicates.
    if (helpers->IsEmpty()) {
      iter.Remove();
    }
  }
}

}  // namespace gmp
}  // namespace mozilla

#undef __CLASS__

Messung V0.5
C=96 H=93 G=94

¤ Dauer der Verarbeitung: 0.1 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.