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

Quelle  MFCDMParent.cpp   Sprache: C

 
/* 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 "MFCDMParent.h"

#include <mfmediaengine.h>
#include <unknwnbase.h>
#include <wtypes.h>
#define INITGUID          // Enable DEFINE_PROPERTYKEY()
#include <propkeydef.h>   // For DEFINE_PROPERTYKEY() definition
#include <propvarutil.h>  // For InitPropVariantFrom*()

#include "MFCDMProxy.h"
#include "MFMediaEngineUtils.h"
#include "RemoteDecodeUtils.h"       // For GetCurrentSandboxingKind()
#include "SpecialSystemDirectory.h"  // For temp dir
#include "WMFUtils.h"
#include "mozilla/EMEUtils.h"
#include "mozilla/KeySystemConfig.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/StaticPrefs_media.h"
#include "mozilla/WindowsVersion.h"
#include "mozilla/dom/KeySystemNames.h"
#include "mozilla/dom/MediaKeysBinding.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/ipc/UtilityAudioDecoderChild.h"
#include "mozilla/ipc/UtilityProcessManager.h"
#include "mozilla/ipc/UtilityProcessParent.h"
#include "nsTHashMap.h"

#ifdef MOZ_WMF_CDM_LPAC_SANDBOX
#  include "sandboxBroker.h"
#endif

using Microsoft::WRL::ComPtr;
using Microsoft::WRL::MakeAndInitialize;

namespace mozilla {

// See
// https://source.chromium.org/chromium/chromium/src/+/main:media/cdm/win/media_foundation_cdm_util.cc;l=26-40;drc=503535015a7b373cc6185c69c991e01fda5da571
#ifndef EME_CONTENTDECRYPTIONMODULE_ORIGIN_ID
DEFINE_PROPERTYKEY(EME_CONTENTDECRYPTIONMODULE_ORIGIN_ID, 0x1218a3e2, 0xcfb0,
                   0x4c98, 0x90, 0xe5, 0x5f, 0x58, 0x18, 0xd4, 0xb6, 0x7e,
                   PID_FIRST_USABLE);
#endif

#define MFCDM_PARENT_LOG(msg, ...)                                     \
  EME_LOG("MFCDMParent[%p, Id=%" PRIu64 "]@%s: " msg, this, this->mId, \
          __func__, ##__VA_ARGS__)
#define MFCDM_PARENT_SLOG(msg, ...) \
  EME_LOG("MFCDMParent@%s: " msg, __func__, ##__VA_ARGS__)

#define MFCDM_RETURN_IF_FAILED(x)                       \
  do {                                                  \
    HRESULT rv = x;                                     \
    if (MOZ_UNLIKELY(FAILED(rv))) {                     \
      MFCDM_PARENT_SLOG("(" #x ") failed, rv=%lx", rv); \
      return rv;                                        \
    }                                                   \
  } while (false)

#define MFCDM_RETURN_BOOL_IF_FAILED(x)                  \
  do {                                                  \
    HRESULT rv = x;                                     \
    if (MOZ_UNLIKELY(FAILED(rv))) {                     \
      MFCDM_PARENT_SLOG("(" #x ") failed, rv=%lx", rv); \
      return false;                                     \
    }                                                   \
  } while (false)

#define MFCDM_REJECT_IF(pred, rv)                                      \
  do {                                                                 \
    if (MOZ_UNLIKELY(pred)) {                                          \
      MFCDM_PARENT_LOG("reject for [" #pred "], rv=%x", uint32_t(rv)); \
      aResolver(rv);                                                   \
      return IPC_OK();                                                 \
    }                                                                  \
  } while (false)

#define MFCDM_REJECT_IF_FAILED(op, rv)                                       \
  do {                                                                       \
    HRESULT hr = op;                                                         \
    if (MOZ_UNLIKELY(FAILED(hr))) {                                          \
      MFCDM_PARENT_LOG("(" #op ") failed(hr=%lx), rv=%x", hr, uint32_t(rv)); \
      aResolver(rv);                                                         \
      return IPC_OK();                                                       \
    }                                                                        \
  } while (false)

StaticMutex sFactoryMutex;
MOZ_RUNINIT static nsTHashMap<nsStringHashKey,
                              ComPtr<IMFContentDecryptionModuleFactory>>
    sFactoryMap;
MOZ_RUNINIT static CopyableTArray<MFCDMCapabilitiesIPDL> sCapabilities;
StaticMutex sCapabilitesMutex;
MOZ_RUNINIT static ComPtr<IUnknown> sMediaEngineClassFactory;

// RAIIized PROPVARIANT. See
// third_party/libwebrtc/modules/audio_device/win/core_audio_utility_win.h
class AutoPropVar {
 public:
  AutoPropVar() { PropVariantInit(&mVar); }

  ~AutoPropVar() { Reset(); }

  AutoPropVar(const AutoPropVar&) = delete;
  AutoPropVar& operator=(const AutoPropVar&) = delete;
  bool operator==(const AutoPropVar&) const = delete;
  bool operator!=(const AutoPropVar&) const = delete;

  // Returns a pointer to the underlying PROPVARIANT for use as an out param
  // in a function call.
  PROPVARIANT* Receive() {
    MOZ_ASSERT(mVar.vt == VT_EMPTY);
    return &mVar;
  }

  // Clears the instance to prepare it for re-use (e.g., via Receive).
  void Reset() {
    if (mVar.vt != VT_EMPTY) {
      HRESULT hr = PropVariantClear(&mVar);
      MOZ_ASSERT(SUCCEEDED(hr));
      Unused << hr;
    }
  }

  const PROPVARIANT& get() const { return mVar; }
  const PROPVARIANT* ptr() const { return &mVar; }

 private:
  PROPVARIANT mVar;
};

static MF_MEDIAKEYS_REQUIREMENT ToMFRequirement(
    const KeySystemConfig::Requirement aRequirement) {
  switch (aRequirement) {
    case KeySystemConfig::Requirement::NotAllowed:
      return MF_MEDIAKEYS_REQUIREMENT_NOT_ALLOWED;
    case KeySystemConfig::Requirement::Optional:
      return MF_MEDIAKEYS_REQUIREMENT_OPTIONAL;
    case KeySystemConfig::Requirement::Required:
      return MF_MEDIAKEYS_REQUIREMENT_REQUIRED;
  }
};

static inline LPCWSTR InitDataTypeToString(const nsAString& aInitDataType) {
  // The strings are defined in https://www.w3.org/TR/eme-initdata-registry/
  if (aInitDataType.EqualsLiteral("webm")) {
    return L"webm";
  } else if (aInitDataType.EqualsLiteral("cenc")) {
    return L"cenc";
  } else if (aInitDataType.EqualsLiteral("keyids")) {
    return L"keyids";
  } else {
    return L"unknown";
  }
}

// The HDCP value follows the feature value in
// https://docs.microsoft.com/en-us/uwp/api/windows.media.protection.protectioncapabilities.istypesupported?view=winrt-19041
// - 1 (on without HDCP 2.2 Type 1 restriction)
// - 2 (on with HDCP 2.2 Type 1 restriction)
static nsString GetHdcpPolicy(const dom::HDCPVersion& aMinHdcpVersion) {
  if (aMinHdcpVersion == dom::HDCPVersion::_2_2 ||
      aMinHdcpVersion == dom::HDCPVersion::_2_3) {
    return nsString(u"hdcp=2");
  }
  return nsString(u"hdcp=1");
}

static bool RequireClearLead(const nsString& aKeySystem) {
  return aKeySystem.EqualsLiteral(kWidevineExperiment2KeySystemName) ||
         aKeySystem.EqualsLiteral(kPlayReadyHardwareClearLeadKeySystemName);
}

static void BuildCapabilitiesArray(
    const nsTArray<MFCDMMediaCapability>& aCapabilities,
    AutoPropVar& capabilitiesPropOut) {
  PROPVARIANT* capabilitiesArray = (PROPVARIANT*)CoTaskMemAlloc(
      sizeof(PROPVARIANT) * aCapabilities.Length());
  for (size_t idx = 0; idx < aCapabilities.Length(); idx++) {
    ComPtr<IPropertyStore> capabilitiesProperty;
    RETURN_VOID_IF_FAILED(
        PSCreateMemoryPropertyStore(IID_PPV_ARGS(&capabilitiesProperty)));

    AutoPropVar contentType;
    auto* var = contentType.Receive();
    var->vt = VT_BSTR;
    var->bstrVal = SysAllocString(aCapabilities[idx].contentType().get());
    RETURN_VOID_IF_FAILED(
        capabilitiesProperty->SetValue(MF_EME_CONTENTTYPE, contentType.get()));

    AutoPropVar robustness;
    var = robustness.Receive();
    var->vt = VT_BSTR;
    var->bstrVal = SysAllocString(aCapabilities[idx].robustness().get());
    RETURN_VOID_IF_FAILED(
        capabilitiesProperty->SetValue(MF_EME_ROBUSTNESS, robustness.get()));

    capabilitiesArray[idx].vt = VT_UNKNOWN;
    capabilitiesArray[idx].punkVal = capabilitiesProperty.Detach();
  }
  auto* var = capabilitiesPropOut.Receive();
  var->vt = VT_VARIANT | VT_VECTOR;
  var->capropvar.cElems = aCapabilities.Length();
  var->capropvar.pElems = capabilitiesArray;
}

static HRESULT BuildCDMAccessConfig(const MFCDMInitParamsIPDL& aParams,
                                    ComPtr<IPropertyStore>& aConfig) {
  ComPtr<IPropertyStore> mksc;  // EME MediaKeySystemConfiguration
  MFCDM_RETURN_IF_FAILED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(&mksc)));

  // Init type. If we don't set `MF_EME_INITDATATYPES` then we won't be able
  // to create CDM module on Windows 10, which is not documented officially.
  BSTR* initDataTypeArray =
      (BSTR*)CoTaskMemAlloc(sizeof(BSTR) * aParams.initDataTypes().Length());
  for (size_t i = 0; i < aParams.initDataTypes().Length(); i++) {
    initDataTypeArray[i] =
        SysAllocString(InitDataTypeToString(aParams.initDataTypes()[i]));
  }
  AutoPropVar initDataTypes;
  PROPVARIANT* var = initDataTypes.Receive();
  var->vt = VT_VECTOR | VT_BSTR;
  var->cabstr.cElems = static_cast<ULONG>(aParams.initDataTypes().Length());
  var->cabstr.pElems = initDataTypeArray;
  MFCDM_RETURN_IF_FAILED(
      mksc->SetValue(MF_EME_INITDATATYPES, initDataTypes.get()));

  // Audio capabilities
  AutoPropVar audioCapabilities;
  BuildCapabilitiesArray(aParams.audioCapabilities(), audioCapabilities);
  MFCDM_RETURN_IF_FAILED(
      mksc->SetValue(MF_EME_AUDIOCAPABILITIES, audioCapabilities.get()));

  // Video capabilities
  AutoPropVar videoCapabilities;
  BuildCapabilitiesArray(aParams.videoCapabilities(), videoCapabilities);
  MFCDM_RETURN_IF_FAILED(
      mksc->SetValue(MF_EME_VIDEOCAPABILITIES, videoCapabilities.get()));

  // Persist state
  AutoPropVar persistState;
  InitPropVariantFromUInt32(ToMFRequirement(aParams.persistentState()),
                            persistState.Receive());
  MFCDM_RETURN_IF_FAILED(
      mksc->SetValue(MF_EME_PERSISTEDSTATE, persistState.get()));

  // Distintive Id
  AutoPropVar distinctiveID;
  InitPropVariantFromUInt32(ToMFRequirement(aParams.distinctiveID()),
                            distinctiveID.Receive());
  MFCDM_RETURN_IF_FAILED(
      mksc->SetValue(MF_EME_DISTINCTIVEID, distinctiveID.get()));

  aConfig.Swap(mksc);
  return S_OK;
}

static HRESULT BuildCDMProperties(const nsString& aOrigin,
                                  ComPtr<IPropertyStore>& aProps) {
  MOZ_ASSERT(!aOrigin.IsEmpty());

  ComPtr<IPropertyStore> props;
  MFCDM_RETURN_IF_FAILED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(&props)));

  AutoPropVar origin;
  MFCDM_RETURN_IF_FAILED(
      InitPropVariantFromString(aOrigin.get(), origin.Receive()));
  MFCDM_RETURN_IF_FAILED(
      props->SetValue(EME_CONTENTDECRYPTIONMODULE_ORIGIN_ID, origin.get()));

  // TODO: support client token?

  // TODO: CDM store path per profile?
  nsCOMPtr<nsIFile> dir;
  if (NS_FAILED(GetSpecialSystemDirectory(OS_TemporaryDirectory,
                                          getter_AddRefs(dir)))) {
    return E_ACCESSDENIED;
  }
  if (NS_FAILED(dir->AppendNative(nsDependentCString("mfcdm")))) {
    return E_ACCESSDENIED;
  }
  nsresult rv = dir->Create(nsIFile::DIRECTORY_TYPE, 0700);
  if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_FAILED(rv)) {
    return E_ACCESSDENIED;
  }
  nsAutoString cdmStorePath;
  if (NS_FAILED(dir->GetPath(cdmStorePath))) {
    return E_ACCESSDENIED;
  }

  AutoPropVar path;
  MFCDM_RETURN_IF_FAILED(
      InitPropVariantFromString(cdmStorePath.get(), path.Receive()));
  MFCDM_RETURN_IF_FAILED(
      props->SetValue(MF_CONTENTDECRYPTIONMODULE_STOREPATH, path.get()));

  aProps.Swap(props);
  return S_OK;
}

static HRESULT CreateContentDecryptionModule(
    ComPtr<IMFContentDecryptionModuleFactory> aFactory,
    const nsString& aKeySystem, const MFCDMInitParamsIPDL& aParams,
    ComPtr<IMFContentDecryptionModule>& aCDMOut) {
  // Get access object to CDM.
  ComPtr<IPropertyStore> accessConfig;
  RETURN_IF_FAILED(BuildCDMAccessConfig(aParams, accessConfig));

  AutoTArray<IPropertyStore*, 1> configs = {accessConfig.Get()};
  ComPtr<IMFContentDecryptionModuleAccess> cdmAccess;
  RETURN_IF_FAILED(aFactory->CreateContentDecryptionModuleAccess(
      aKeySystem.get(), configs.Elements(), configs.Length(), &cdmAccess));

  // Get CDM.
  ComPtr<IPropertyStore> cdmProps;
  RETURN_IF_FAILED(BuildCDMProperties(aParams.origin(), cdmProps));
  ComPtr<IMFContentDecryptionModule> cdm;
  RETURN_IF_FAILED(
      cdmAccess->CreateContentDecryptionModule(cdmProps.Get(), &cdm));
  aCDMOut.Swap(cdm);
  return S_OK;
}

// Wrapper function for IMFContentDecryptionModuleFactory::IsTypeSupported.
static bool IsTypeSupported(
    const ComPtr<IMFContentDecryptionModuleFactory>& aFactory,
    const nsString& aKeySystem, const nsString* aContentType = nullptr) {
  nsString keySystem;
  // Widevine's factory only takes original key system string.
  if (IsWidevineExperimentKeySystemAndSupported(aKeySystem)) {
    keySystem.AppendLiteral(u"com.widevine.alpha");
  }
  // kPlayReadyHardwareClearLeadKeySystemName is our custom key system name,
  // we should use kPlayReadyKeySystemHardware which is the real key system
  // name.
  else if (aKeySystem.EqualsLiteral(kPlayReadyHardwareClearLeadKeySystemName)) {
    keySystem.AppendLiteral(kPlayReadyKeySystemHardware);
  } else {
    keySystem = aKeySystem;
  }
  return aFactory->IsTypeSupported(
      keySystem.get(), aContentType ? aContentType->get() : nullptr);
}

static nsString MapKeySystem(const nsString& aKeySystem) {
  // When website requests HW secure robustness for video by original Widevine
  // key system name, it would be mapped to this key system which is for HWDRM.
  if (IsWidevineKeySystem(aKeySystem)) {
    return nsString(u"com.widevine.alpha.experiment");
  }
  // kPlayReadyHardwareClearLeadKeySystemName is our custom key system name,
  // we should use kPlayReadyKeySystemHardware which is the real key system
  // name.
  if (aKeySystem.EqualsLiteral(kPlayReadyHardwareClearLeadKeySystemName)) {
    return NS_ConvertUTF8toUTF16(kPlayReadyKeySystemHardware);
  }
  return aKeySystem;
}

/* static */
void MFCDMParent::SetWidevineL1Path(const char* aPath) {
  nsAutoCString path(aPath);
  path.AppendLiteral("\\Google.Widevine.CDM.dll");
  sWidevineL1Path = CreateBSTRFromConstChar(path.get());
  MFCDM_PARENT_SLOG("Set Widevine L1 dll path=%ls\n", sWidevineL1Path);
}

void MFCDMParent::Register() {
  MOZ_ASSERT(!sRegisteredCDMs.Contains(this->mId));
  sRegisteredCDMs.InsertOrUpdate(this->mId, this);
  MFCDM_PARENT_LOG("Registered!");
}

void MFCDMParent::Unregister() {
  MOZ_ASSERT(sRegisteredCDMs.Contains(this->mId));
  sRegisteredCDMs.Remove(this->mId);
  MFCDM_PARENT_LOG("Unregistered!");
}

MFCDMParent::MFCDMParent(const nsAString& aKeySystem,
                         RemoteDecoderManagerParent* aManager,
                         nsISerialEventTarget* aManagerThread)
    : mKeySystem(aKeySystem),
      mManager(aManager),
      mManagerThread(aManagerThread),
      mId(sNextId++),
      mKeyMessageEvents(aManagerThread),
      mKeyChangeEvents(aManagerThread),
      mExpirationEvents(aManagerThread) {
  MOZ_ASSERT(IsPlayReadyKeySystemAndSupported(aKeySystem) ||
             IsWidevineExperimentKeySystemAndSupported(aKeySystem) ||
             IsWidevineKeySystem(mKeySystem) ||
             IsWMFClearKeySystemAndSupported(aKeySystem));
  MOZ_ASSERT(aManager);
  MOZ_ASSERT(aManagerThread);
  MOZ_ASSERT(XRE_IsUtilityProcess());
  MOZ_ASSERT(GetCurrentSandboxingKind() ==
             ipc::SandboxingKind::MF_MEDIA_ENGINE_CDM);
  MFCDM_PARENT_LOG("MFCDMParent created");
  mIPDLSelfRef = this;
  Register();

  mKeyMessageListener = mKeyMessageEvents.Connect(
      mManagerThread, this, &MFCDMParent::SendOnSessionKeyMessage);
  mKeyChangeListener = mKeyChangeEvents.Connect(
      mManagerThread, this, &MFCDMParent::SendOnSessionKeyStatusesChanged);
  mExpirationListener = mExpirationEvents.Connect(
      mManagerThread, this, &MFCDMParent::SendOnSessionKeyExpiration);

  RETURN_VOID_IF_FAILED(GetOrCreateFactory(mKeySystem, mFactory));
}

void MFCDMParent::ShutdownCDM() {
  AssertOnManagerThread();
  if (!mCDM) {
    return;
  }
  auto rv = mCDM->SetPMPHostApp(nullptr);
  if (FAILED(rv)) {
    MFCDM_PARENT_LOG("Failed to clear PMP Host App, rv=%lx", rv);
  }
  SHUTDOWN_IF_POSSIBLE(mCDM);
  mCDM = nullptr;
  MFCDM_PARENT_LOG("Shutdown CDM completed");
}

void MFCDMParent::Destroy() {
  AssertOnManagerThread();
  mKeyMessageEvents.DisconnectAll();
  mKeyChangeEvents.DisconnectAll();
  mExpirationEvents.DisconnectAll();
  mKeyMessageListener.DisconnectIfExists();
  mKeyChangeListener.DisconnectIfExists();
  mExpirationListener.DisconnectIfExists();
  if (mPMPHostWrapper) {
    mPMPHostWrapper->Shutdown();
    mPMPHostWrapper = nullptr;
  }
  ShutdownCDM();
  mFactory = nullptr;
  for (auto& iter : mSessions) {
    iter.second->Close();
  }
  mSessions.clear();
  mIPDLSelfRef = nullptr;
}

MFCDMParent::~MFCDMParent() {
  MFCDM_PARENT_LOG("MFCDMParent detroyed");
  Unregister();
}

/* static */
LPCWSTR MFCDMParent::GetCDMLibraryName(const nsString& aKeySystem) {
  if (IsWMFClearKeySystemAndSupported(aKeySystem) ||
      StaticPrefs::media_eme_wmf_use_mock_cdm_for_external_cdms()) {
    return L"wmfclearkey.dll";
  }
  // PlayReady is a built-in CDM on Windows, no need to load external library.
  if (IsPlayReadyKeySystemAndSupported(aKeySystem)) {
    return L"";
  }
  if (IsWidevineExperimentKeySystemAndSupported(aKeySystem) ||
      IsWidevineKeySystem(aKeySystem)) {
    return sWidevineL1Path ? sWidevineL1Path : L"L1-not-found";
  }
  return L"Unknown";
}

/* static */
void MFCDMParent::Shutdown() {
  StaticMutexAutoLock lock(sCapabilitesMutex);
  sFactoryMap.Clear();
  sCapabilities.Clear();
  sMediaEngineClassFactory.Reset();
}

/* static */
HRESULT MFCDMParent::GetOrCreateFactory(
    const nsString& aKeySystem,
    ComPtr<IMFContentDecryptionModuleFactory>& aFactoryOut) {
  StaticMutexAutoLock lock(sFactoryMutex);
  auto rv = sFactoryMap.MaybeGet(aKeySystem);
  if (!rv) {
    MFCDM_PARENT_SLOG("No factory %s, creating...",
                      NS_ConvertUTF16toUTF8(aKeySystem).get());
    ComPtr<IMFContentDecryptionModuleFactory> factory;
    MFCDM_RETURN_IF_FAILED(LoadFactory(aKeySystem, factory));
    sFactoryMap.InsertOrUpdate(aKeySystem, factory);
    aFactoryOut.Swap(factory);
  } else {
    aFactoryOut = *rv;
  }
  return S_OK;
}

/* static */
HRESULT MFCDMParent::LoadFactory(
    const nsString& aKeySystem,
    ComPtr<IMFContentDecryptionModuleFactory>& aFactoryOut) {
  LPCWSTR libraryName = GetCDMLibraryName(aKeySystem);
  const bool loadFromPlatform = wcslen(libraryName) == 0;
  MFCDM_PARENT_SLOG("Load factory for %s (libraryName=%ls)",
                    NS_ConvertUTF16toUTF8(aKeySystem).get(), libraryName);

  MFCDM_PARENT_SLOG("Create factory for %s",
                    NS_ConvertUTF16toUTF8(aKeySystem).get());
  ComPtr<IMFContentDecryptionModuleFactory> cdmFactory;
  if (loadFromPlatform) {
    if (!sMediaEngineClassFactory) {
      MFCDM_RETURN_IF_FAILED(CoCreateInstance(
          CLSID_MFMediaEngineClassFactory, nullptr, CLSCTX_INPROC_SERVER,
          IID_PPV_ARGS(&sMediaEngineClassFactory)));
    }
    ComPtr<IMFMediaEngineClassFactory4> clsFactory;
    MFCDM_RETURN_IF_FAILED(sMediaEngineClassFactory.As(&clsFactory));
    MFCDM_RETURN_IF_FAILED(clsFactory->CreateContentDecryptionModuleFactory(
        MapKeySystem(aKeySystem).get(), IID_PPV_ARGS(&cdmFactory)));
    aFactoryOut.Swap(cdmFactory);
    MFCDM_PARENT_SLOG("Created factory for %s from platform!",
                      NS_ConvertUTF16toUTF8(aKeySystem).get());
    return S_OK;
  }

  HMODULE handle = LoadLibraryW(libraryName);
  if (!handle) {
    MFCDM_PARENT_SLOG("Failed to load library %ls! (error=%lx)", libraryName,
                      GetLastError());
    return E_FAIL;
  }
  MFCDM_PARENT_SLOG("Loaded external library '%ls'", libraryName);

  using DllGetActivationFactoryFunc =
      HRESULT(WINAPI*)(_In_ HSTRING, _COM_Outptr_ IActivationFactory**);
  DllGetActivationFactoryFunc pDllGetActivationFactory =
      (DllGetActivationFactoryFunc)GetProcAddress(handle,
                                                  "DllGetActivationFactory");
  if (!pDllGetActivationFactory) {
    MFCDM_PARENT_SLOG("Failed to get activation function!");
    return E_FAIL;
  }

  // The follow classID format is what Widevine's DLL expects
  // "<key_system>.ContentDecryptionModuleFactory". In addition, when querying
  // factory, need to use original Widevine key system name.
  nsString stringId;
  if (StaticPrefs::media_eme_wmf_use_mock_cdm_for_external_cdms() ||
      IsWMFClearKeySystemAndSupported(aKeySystem)) {
    stringId.AppendLiteral("org.w3.clearkey");
  } else if (IsWidevineExperimentKeySystemAndSupported(aKeySystem) ||
             IsWidevineKeySystem(aKeySystem)) {
    // Widevine's DLL expects "<key_system>.ContentDecryptionModuleFactory" for
    // the class Id.
    stringId.AppendLiteral("com.widevine.alpha.ContentDecryptionModuleFactory");
  }
  MFCDM_PARENT_SLOG("Query factory by classId '%s'",
                    NS_ConvertUTF16toUTF8(stringId).get());
  ScopedHString classId(stringId);
  ComPtr<IActivationFactory> pFactory = NULL;
  MFCDM_RETURN_IF_FAILED(
      pDllGetActivationFactory(classId.Get(), pFactory.GetAddressOf()));

  ComPtr<IInspectable> pInspectable;
  MFCDM_RETURN_IF_FAILED(pFactory->ActivateInstance(&pInspectable));
  MFCDM_RETURN_IF_FAILED(pInspectable.As(&cdmFactory));
  aFactoryOut.Swap(cdmFactory);
  MFCDM_PARENT_SLOG("Created factory for %s from external library!",
                    NS_ConvertUTF16toUTF8(aKeySystem).get());
  return S_OK;
}

static nsString GetRobustnessStringForKeySystem(const nsString& aKeySystem,
                                                const bool aIsHWSecure,
                                                const bool aIsVideo = true) {
  if (IsPlayReadyKeySystemAndSupported(aKeySystem)) {
    // Audio doesn't support SL3000.
    return aIsHWSecure && aIsVideo ? nsString(u"3000") : nsString(u"2000");
  }
  if (IsWidevineExperimentKeySystemAndSupported(aKeySystem)) {
    return aIsHWSecure ? nsString(u"HW_SECURE_ALL")
                       : nsString(u"SW_SECURE_DECODE");
  }
  return nsString(u"");
}

// Use IMFContentDecryptionModuleFactory::IsTypeSupported() to get DRM
// capabilities. The query string is based on following, they are pretty much
// equivalent.
// https://learn.microsoft.com/en-us/uwp/api/windows.media.protection.protectioncapabilities.istypesupported?view=winrt-22621
// https://learn.microsoft.com/en-us/windows/win32/api/mfmediaengine/nf-mfmediaengine-imfextendeddrmtypesupport-istypesupportedex
static bool FactorySupports(ComPtr<IMFContentDecryptionModuleFactory>& aFactory,
                            const nsString& aKeySystem,
                            const nsCString& aVideoCodec,
                            const nsCString& aAudioCodec = nsCString(""),
                            const nsString& aAdditionalFeatures = nsString(u""),
                            bool aIsHWSecure = false) {
  // Create query string, MP4 is the only container supported.
  nsString contentType(u"video/mp4;codecs=\"");
  MOZ_ASSERT(!aVideoCodec.IsEmpty());
  contentType.AppendASCII(aVideoCodec);
  if (!aAudioCodec.IsEmpty()) {
    contentType.AppendLiteral(u",");
    contentType.AppendASCII(aAudioCodec);
  }
  contentType.AppendLiteral(u"\";features=\"");
  if (IsWidevineExperimentKeySystemAndSupported(aKeySystem) ||
      IsWidevineKeySystem(aKeySystem)) {
    // This decoder subsystem settings are only required by Wivevine.
    contentType.AppendLiteral(
        u"decode-bpc=8,"
        "decode-res-x=1920,decode-res-y=1080,"
        "decode-bitrate=10000000,decode-fps=30,");
    // `encryption-robustness` is for Widevine only.
    if (aIsHWSecure) {
      contentType.AppendLiteral(u"encryption-robustness=HW_SECURE_ALL,");
    } else {
      contentType.AppendLiteral(u"encryption-robustness=SW_SECURE_DECODE,");
    }
  }
  if (!aAdditionalFeatures.IsEmpty()) {
    contentType.Append(aAdditionalFeatures);
  }
  contentType.AppendLiteral(u"\"");
  // End of the query string

  // PlayReady doesn't implement IsTypeSupported properly, so it requires us to
  // use another way to check the capabilities.
  if (IsPlayReadyKeySystemAndSupported(aKeySystem) &&
      StaticPrefs::media_eme_playready_istypesupportedex()) {
    ComPtr<IMFExtendedDRMTypeSupport> spDrmTypeSupport;
    MFCDM_RETURN_BOOL_IF_FAILED(sMediaEngineClassFactory.As(&spDrmTypeSupport));
    BSTR keySystem = aIsHWSecure
                         ? CreateBSTRFromConstChar(kPlayReadyKeySystemHardware)
                         : CreateBSTRFromConstChar(kPlayReadyKeySystemName);
    MF_MEDIA_ENGINE_CANPLAY canPlay;
    spDrmTypeSupport->IsTypeSupportedEx(SysAllocString(contentType.get()),
                                        keySystem, &canPlay);
    const bool support =
        canPlay !=
        MF_MEDIA_ENGINE_CANPLAY::MF_MEDIA_ENGINE_CANPLAY_NOT_SUPPORTED;
    MFCDM_PARENT_SLOG("IsTypeSupportedEx=%d (key-system=%ls, content-type=%s)",
                      support, keySystem,
                      NS_ConvertUTF16toUTF8(contentType).get());
    return support;
  }

  // Checking capabilies from CDM's IsTypeSupported. Widevine implements this
  // method well.
  bool support = IsTypeSupported(aFactory, aKeySystem, &contentType);
  MFCDM_PARENT_SLOG("IsTypeSupport=%d (key-system=%s, content-type=%s)",
                    support, NS_ConvertUTF16toUTF8(aKeySystem).get(),
                    NS_ConvertUTF16toUTF8(contentType).get());
  return support;
}

static nsresult IsHDCPVersionSupported(
    ComPtr<IMFContentDecryptionModuleFactory>& aFactory,
    const nsString& aKeySystem, const dom::HDCPVersion& aMinHdcpVersion) {
  nsresult rv = NS_OK;
  // Codec doesn't matter when querying the HDCP policy, so use H264.
  if (!FactorySupports(aFactory, aKeySystem, nsCString("avc1"),
                       KeySystemConfig::EMECodecString(""),
                       GetHdcpPolicy(aMinHdcpVersion))) {
    rv = NS_ERROR_DOM_MEDIA_CDM_HDCP_NOT_SUPPORT;
  }
  return rv;
}

static bool IsKeySystemHWSecure(
    const nsAString& aKeySystem,
    const nsTArray<MFCDMMediaCapability>& aCapabilities) {
  if (IsPlayReadyKeySystemAndSupported(aKeySystem)) {
    if (aKeySystem.EqualsLiteral(kPlayReadyKeySystemHardware) ||
        aKeySystem.EqualsLiteral(kPlayReadyHardwareClearLeadKeySystemName)) {
      return true;
    }
    for (const auto& capabilities : aCapabilities) {
      if (capabilities.robustness().EqualsLiteral("3000")) {
        return true;
      }
    }
  }
  if (IsWidevineExperimentKeySystemAndSupported(aKeySystem) ||
      IsWidevineKeySystem(aKeySystem)) {
    // We only support Widevine HWDRM.
    return true;
  }
  return false;
}

/* static */
RefPtr<MFCDMParent::CapabilitiesPromise>
MFCDMParent::GetAllKeySystemsCapabilities() {
  MOZ_ASSERT(NS_IsMainThread());
  nsCOMPtr<nsISerialEventTarget> backgroundTaskQueue;
  if (NS_FAILED(NS_CreateBackgroundTaskQueue(
          __func__, getter_AddRefs(backgroundTaskQueue)))) {
    MFCDM_PARENT_SLOG(
        "Failed to create task queue for all key systems capabilities!");
    return CapabilitiesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR,
                                                __func__);
  }

  RefPtr<CapabilitiesPromise::Private> p =
      new CapabilitiesPromise::Private(__func__);
  Unused << backgroundTaskQueue->Dispatch(NS_NewRunnableFunction(__func__, [p] {
    MFCDM_PARENT_SLOG("GetAllKeySystemsCapabilities");
    enum SecureLevel : bool {
      Software = false,
      Hardware = true,
    };
    const nsTArray<std::pair<nsString, SecureLevel>> kKeySystems{
        std::pair<nsString, SecureLevel>(
            NS_ConvertUTF8toUTF16(kPlayReadyKeySystemName),
            SecureLevel::Software),
        std::pair<nsString, SecureLevel>(
            NS_ConvertUTF8toUTF16(kPlayReadyKeySystemHardware),
            SecureLevel::Hardware),
        std::pair<nsString, SecureLevel>(
            NS_ConvertUTF8toUTF16(kPlayReadyHardwareClearLeadKeySystemName),
            SecureLevel::Hardware),
        std::pair<nsString, SecureLevel>(
            NS_ConvertUTF8toUTF16(kWidevineExperimentKeySystemName),
            SecureLevel::Hardware),
        std::pair<nsString, SecureLevel>(
            NS_ConvertUTF8toUTF16(kWidevineExperiment2KeySystemName),
            SecureLevel::Hardware),
    };

    CopyableTArray<MFCDMCapabilitiesIPDL> capabilitiesArr;
    for (const auto& keySystem : kKeySystems) {
      // Only check the capabilites if the relative prefs for the key system
      // are ON.
      if (IsPlayReadyKeySystemAndSupported(keySystem.first) ||
          IsWidevineExperimentKeySystemAndSupported(keySystem.first)) {
        MFCDMCapabilitiesIPDL* c = capabilitiesArr.AppendElement();
        CapabilitesFlagSet flags;
        if (keySystem.second == SecureLevel::Hardware) {
          flags += CapabilitesFlag::HarewareDecryption;
        }
        flags += CapabilitesFlag::NeedHDCPCheck;
        if (RequireClearLead(keySystem.first)) {
          flags += CapabilitesFlag::NeedClearLeadCheck;
        }
        GetCapabilities(keySystem.first, flags, nullptr, *c);
      }
    }

    p->Resolve(std::move(capabilitiesArr), __func__);
  }));
  return p;
}

/* static */
void MFCDMParent::GetCapabilities(const nsString& aKeySystem,
                                  const CapabilitesFlagSet& aFlags,
                                  IMFContentDecryptionModuleFactory* aFactory,
                                  MFCDMCapabilitiesIPDL& aCapabilitiesOut) {
  aCapabilitiesOut.keySystem() = aKeySystem;
  // WMF CDMs usually require these. See
  // https://source.chromium.org/chromium/chromium/src/+/main:media/cdm/win/media_foundation_cdm_factory.cc;l=69-73;drc=b3ca5c09fa0aa07b7f9921501f75e43d80f3ba48
  aCapabilitiesOut.persistentState() = KeySystemConfig::Requirement::Required;
  aCapabilitiesOut.distinctiveID() = KeySystemConfig::Requirement::Required;

  const bool isHardwareDecryption =
      aFlags.contains(CapabilitesFlag::HarewareDecryption);
  aCapabilitiesOut.isHardwareDecryption() = isHardwareDecryption;
  // Return empty capabilites for SWDRM on Windows 10 because it has the process
  // leaking problem.
  if (!IsWin11OrLater() && !isHardwareDecryption) {
    return;
  }

  // MFCDM requires persistent storage, and can't use in-memory storage, it
  // can't be used in private browsing.
  if (aFlags.contains(CapabilitesFlag::IsPrivateBrowsing)) {
    return;
  }

  ComPtr<IMFContentDecryptionModuleFactory> factory = aFactory;
  if (!factory) {
    RETURN_VOID_IF_FAILED(GetOrCreateFactory(aKeySystem, factory));
  }

  StaticMutexAutoLock lock(sCapabilitesMutex);
  for (auto& capabilities : sCapabilities) {
    if (capabilities.keySystem().Equals(aKeySystem) &&
        capabilities.isHardwareDecryption() == isHardwareDecryption) {
      MFCDM_PARENT_SLOG(
          "Return cached capabilities for %s (hardwareDecryption=%d)",
          NS_ConvertUTF16toUTF8(aKeySystem).get(), isHardwareDecryption);
      if (capabilities.isHDCP22Compatible().isNothing() &&
          aFlags.contains(CapabilitesFlag::NeedHDCPCheck)) {
        const bool rv = IsHDCPVersionSupported(factory, aKeySystem,
                                               dom::HDCPVersion::_2_2) == NS_OK;
        MFCDM_PARENT_SLOG(
            "Check HDCP 2.2 compatible (%d) for the cached capabilites", rv);
        capabilities.isHDCP22Compatible() = Some(rv);
      }
      aCapabilitiesOut = capabilities;
      return;
    }
  }

  MFCDM_PARENT_SLOG(
      "Query capabilities for %s from the factory (hardwareDecryption=%d)",
      NS_ConvertUTF16toUTF8(aKeySystem).get(), isHardwareDecryption);

  // Widevine requires codec type to be four CC, PlayReady is fine with both.
  static auto convertCodecToFourCC =
      [](const KeySystemConfig::EMECodecString& aCodec) {
        if (aCodec.Equals(KeySystemConfig::EME_CODEC_H264)) {
          return "avc1"_ns;
        }
        if (aCodec.Equals(KeySystemConfig::EME_CODEC_VP8)) {
          return "vp80"_ns;
        }
        if (aCodec.Equals(KeySystemConfig::EME_CODEC_VP9)) {
          return "vp09"_ns;
        }
        if (aCodec.Equals(KeySystemConfig::EME_CODEC_HEVC)) {
          return "hev1"_ns;
        }
        if (aCodec.Equals(KeySystemConfig::EME_CODEC_AV1)) {
          return "av01"_ns;
        }
        if (aCodec.Equals(KeySystemConfig::EME_CODEC_AAC)) {
          return "mp4a"_ns;
        }
        if (aCodec.Equals(KeySystemConfig::EME_CODEC_OPUS)) {
          return "Opus"_ns;
        }
        if (aCodec.Equals(KeySystemConfig::EME_CODEC_VORBIS)) {
          return "vrbs"_ns;
        }
        if (aCodec.Equals(KeySystemConfig::EME_CODEC_FLAC)) {
          return "fLaC"_ns;
        }
        MOZ_ASSERT_UNREACHABLE("Unsupported codec");
        return "none"_ns;
      };

  static nsTArray<KeySystemConfig::EMECodecString> kVideoCodecs(
      {KeySystemConfig::EME_CODEC_H264, KeySystemConfig::EME_CODEC_VP8,
       KeySystemConfig::EME_CODEC_VP9, KeySystemConfig::EME_CODEC_HEVC,
       KeySystemConfig::EME_CODEC_AV1});

  // Collect schemes supported by all video codecs.
  static nsTArray<CryptoScheme> kSchemes({
      CryptoScheme::Cenc,
      CryptoScheme::Cbcs,
  });

  // Remember supported video codecs, which will be used when collecting audio
  // codec support.
  nsTArray<KeySystemConfig::EMECodecString> supportedVideoCodecs;

  if (aFlags.contains(CapabilitesFlag::NeedClearLeadCheck)) {
    for (const auto& codec : kVideoCodecs) {
      if (codec == KeySystemConfig::EME_CODEC_HEVC &&
          !StaticPrefs::media_wmf_hevc_enabled()) {
        continue;
      }
      CryptoSchemeSet supportedScheme;
      for (const auto& scheme : kSchemes) {
        nsAutoString additionalFeature(u"encryption-type=");
        // If we don't specify 'encryption-iv-size', it would use 8 bytes IV as
        // default [1]. If it's not supported, then we will try 16 bytes later.
        // Since PlayReady 4.0 [2], 8 and 16 bytes IV are both supported. But
        // We're not sure if Widevine supports both or not.
        // [1]
        // https://learn.microsoft.com/en-us/windows/win32/api/mfmediaengine/nf-mfmediaengine-imfextendeddrmtypesupport-istypesupportedex
        // [2]
        // https://learn.microsoft.com/en-us/playready/packaging/content-encryption-modes#initialization-vectors-ivs
        if (scheme == CryptoScheme::Cenc) {
          additionalFeature.AppendLiteral(u"cenc-clearlead,");
        } else {
          additionalFeature.AppendLiteral(u"cbcs-clearlead,");
        }
        bool rv = FactorySupports(factory, aKeySystem,
                                  convertCodecToFourCC(codec), nsCString(""),
                                  additionalFeature, isHardwareDecryption);
        MFCDM_PARENT_SLOG("clearlead %s IV 8 bytes %s %s",
                          EnumValueToString(scheme), codec.get(),
                          rv ? "supported" : "not supported");
        if (rv) {
          supportedScheme += scheme;
          break;
        }
        // Try 16 bytes IV.
        additionalFeature.AppendLiteral(u"encryption-iv-size=16,");
        rv = FactorySupports(factory, aKeySystem, convertCodecToFourCC(codec),
                             nsCString(""), additionalFeature,
                             isHardwareDecryption);
        MFCDM_PARENT_SLOG("clearlead %s IV 16 bytes %s %s",
                          EnumValueToString(scheme), codec.get(),
                          rv ? "supported" : "not supported");

        if (rv) {
          supportedScheme += scheme;
          break;
        }
      }
      // Add a capability if supported scheme exists
      if (!supportedScheme.isEmpty()) {
        MFCDMMediaCapability* c =
            aCapabilitiesOut.videoCapabilities().AppendElement();
        c->contentType() = NS_ConvertUTF8toUTF16(codec);
        c->robustness() =
            GetRobustnessStringForKeySystem(aKeySystem, isHardwareDecryption);
        if (supportedScheme.contains(CryptoScheme::Cenc)) {
          c->encryptionSchemes().AppendElement(CryptoScheme::Cenc);
          MFCDM_PARENT_SLOG("%s: +video:%s (cenc)", __func__, codec.get());
        }
        if (supportedScheme.contains(CryptoScheme::Cbcs)) {
          c->encryptionSchemes().AppendElement(CryptoScheme::Cbcs);
          MFCDM_PARENT_SLOG("%s: +video:%s (cbcs)", __func__, codec.get());
        }
        supportedVideoCodecs.AppendElement(codec);
      }
    }
  } else {
    // Non clearlead situation for video codecs
    for (const auto& codec : kVideoCodecs) {
      if (codec == KeySystemConfig::EME_CODEC_HEVC &&
          !StaticPrefs::media_wmf_hevc_enabled()) {
        continue;
      }
      if (FactorySupports(factory, aKeySystem, convertCodecToFourCC(codec),
                          KeySystemConfig::EMECodecString(""), nsString(u""),
                          isHardwareDecryption)) {
        MFCDMMediaCapability* c =
            aCapabilitiesOut.videoCapabilities().AppendElement();
        c->contentType() = NS_ConvertUTF8toUTF16(codec);
        c->robustness() =
            GetRobustnessStringForKeySystem(aKeySystem, isHardwareDecryption);
        // 'If value is unspecified, default value of "cenc" is used.' See
        // https://learn.microsoft.com/en-us/windows/win32/api/mfmediaengine/nf-mfmediaengine-imfextendeddrmtypesupport-istypesupportedex
        c->encryptionSchemes().AppendElement(CryptoScheme::Cenc);
        MFCDM_PARENT_SLOG("%s: +video:%s (cenc)", __func__, codec.get());
        // Check cbcs scheme support
        if (FactorySupports(
                factory, aKeySystem, convertCodecToFourCC(codec),
                KeySystemConfig::EMECodecString(""),
                nsString(u"encryption-type=cbcs,encryption-iv-size=16,"),
                isHardwareDecryption)) {
          c->encryptionSchemes().AppendElement(CryptoScheme::Cbcs);
          MFCDM_PARENT_SLOG("%s: +video:%s (cbcs)", __func__, codec.get());
        }
        supportedVideoCodecs.AppendElement(codec);
      }
    }
  }

  if (supportedVideoCodecs.IsEmpty()) {
    // Return a capabilities with no codec supported.
    return;
  }

  static nsTArray<KeySystemConfig::EMECodecString> kAudioCodecs({
      KeySystemConfig::EME_CODEC_AAC,
      KeySystemConfig::EME_CODEC_FLAC,
      KeySystemConfig::EME_CODEC_OPUS,
      KeySystemConfig::EME_CODEC_VORBIS,
  });
  for (const auto& codec : kAudioCodecs) {
    // Hardware decryption is usually only used for video, so we can just check
    // the software capabilities for audio in order to save some time. As the
    // media foundation would create a new D3D device everytime when we check
    // hardware decryption, which takes way longer time.
    if (FactorySupports(factory, aKeySystem,
                        convertCodecToFourCC(supportedVideoCodecs[0]),
                        convertCodecToFourCC(codec), nsString(u""),
                        false /* aIsHWSecure */)) {
      MFCDMMediaCapability* c =
          aCapabilitiesOut.audioCapabilities().AppendElement();
      c->contentType() = NS_ConvertUTF8toUTF16(codec);
      c->robustness() = GetRobustnessStringForKeySystem(
          aKeySystem, false /* aIsHWSecure */, false /* isVideo */);
      c->encryptionSchemes().AppendElement(CryptoScheme::Cenc);
      MFCDM_PARENT_SLOG("%s: +audio:%s", __func__, codec.get());
    }
  }

  // Only perform HDCP if necessary, "The hdcp query (item 4) has a
  // computationally expensive first invocation cost". See
  // https://learn.microsoft.com/en-us/windows/win32/api/mfmediaengine/nf-mfmediaengine-imfextendeddrmtypesupport-istypesupportedex
  if (aFlags.contains(CapabilitesFlag::NeedHDCPCheck) &&
      IsHDCPVersionSupported(factory, aKeySystem, dom::HDCPVersion::_2_2) ==
          NS_OK) {
    MFCDM_PARENT_SLOG("Capabilites is compatible with HDCP 2.2");
    aCapabilitiesOut.isHDCP22Compatible() = Some(true);
  }

  // TODO: don't hardcode
  aCapabilitiesOut.initDataTypes().AppendElement(u"keyids");
  aCapabilitiesOut.initDataTypes().AppendElement(u"cenc");
  aCapabilitiesOut.sessionTypes().AppendElement(
      KeySystemConfig::SessionType::Temporary);
  aCapabilitiesOut.sessionTypes().AppendElement(
      KeySystemConfig::SessionType::PersistentLicense);

  // Cache capabilities for reuse.
  sCapabilities.AppendElement(aCapabilitiesOut);
}

mozilla::ipc::IPCResult MFCDMParent::RecvGetCapabilities(
    const MFCDMCapabilitiesRequest& aRequest,
    GetCapabilitiesResolver&& aResolver) {
  MFCDM_REJECT_IF(!mFactory, NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  MFCDMCapabilitiesIPDL capabilities;
  CapabilitesFlagSet flags;
  if (aRequest.isHardwareDecryption()) {
    flags += CapabilitesFlag::HarewareDecryption;
  }
  if (RequireClearLead(aRequest.keySystem())) {
    flags += CapabilitesFlag::NeedClearLeadCheck;
  }
  if (aRequest.isPrivateBrowsing()) {
    flags += CapabilitesFlag::IsPrivateBrowsing;
  }
  GetCapabilities(aRequest.keySystem(), flags, mFactory.Get(), capabilities);
  aResolver(std::move(capabilities));
  return IPC_OK();
}

mozilla::ipc::IPCResult MFCDMParent::RecvInit(
    const MFCDMInitParamsIPDL& aParams, InitResolver&& aResolver) {
  static auto RequirementToStr = [](KeySystemConfig::Requirement aRequirement) {
    switch (aRequirement) {
      case KeySystemConfig::Requirement::Required:
        return "Required";
      case KeySystemConfig::Requirement::Optional:
        return "Optional";
      default:
        return "NotAllowed";
    }
  };

  MFCDM_PARENT_LOG(
      "Creating a CDM (key-system=%s, origin=%s, distinctiveID=%s, "
      "persistentState=%s, "
      "hwSecure=%d)",
      NS_ConvertUTF16toUTF8(mKeySystem).get(),
      NS_ConvertUTF16toUTF8(aParams.origin()).get(),
      RequirementToStr(aParams.distinctiveID()),
      RequirementToStr(aParams.persistentState()),
      IsKeySystemHWSecure(mKeySystem, aParams.videoCapabilities()));

  MFCDM_REJECT_IF(!mFactory, NS_ERROR_DOM_NOT_SUPPORTED_ERR);

  MOZ_ASSERT(IsTypeSupported(mFactory, mKeySystem));
  MFCDM_REJECT_IF_FAILED(CreateContentDecryptionModule(
                             mFactory, MapKeySystem(mKeySystem), aParams, mCDM),
                         NS_ERROR_FAILURE);
  MOZ_ASSERT(mCDM);
  MFCDM_PARENT_LOG("Created a CDM!");

  // This is only required by PlayReady.
  if (IsPlayReadyKeySystemAndSupported(mKeySystem)) {
    ComPtr<IMFPMPHost> pmpHost;
    ComPtr<IMFGetService> cdmService;
    MFCDM_REJECT_IF_FAILED(mCDM.As(&cdmService), NS_ERROR_FAILURE);
    MFCDM_REJECT_IF_FAILED(
        cdmService->GetService(MF_CONTENTDECRYPTIONMODULE_SERVICE,
                               IID_PPV_ARGS(&pmpHost)),
        NS_ERROR_FAILURE);
    MFCDM_REJECT_IF_FAILED(SUCCEEDED(MakeAndInitialize<MFPMPHostWrapper>(
                               &mPMPHostWrapper, pmpHost)),
                           NS_ERROR_FAILURE);
    MFCDM_REJECT_IF_FAILED(mCDM->SetPMPHostApp(mPMPHostWrapper.Get()),
                           NS_ERROR_FAILURE);
    MFCDM_PARENT_LOG("Set PMPHostWrapper on CDM!");
  }

  aResolver(MFCDMInitIPDL{mId});
  return IPC_OK();
}

mozilla::ipc::IPCResult MFCDMParent::RecvCreateSessionAndGenerateRequest(
    const MFCDMCreateSessionParamsIPDL& aParams,
    CreateSessionAndGenerateRequestResolver&& aResolver) {
  MOZ_ASSERT(mCDM, "RecvInit() must be called and waited on before this call");

  static auto SessionTypeToStr = [](KeySystemConfig::SessionType aSessionType) {
    switch (aSessionType) {
      case KeySystemConfig::SessionType::Temporary:
        return "temporary";
      case KeySystemConfig::SessionType::PersistentLicense:
        return "persistent-license";
      default: {
        MOZ_ASSERT_UNREACHABLE("Unsupported license type!");
        return "invalid";
      }
    }
  };
  MFCDM_PARENT_LOG("Creating session for type '%s'",
                   SessionTypeToStr(aParams.sessionType()));
  UniquePtr<MFCDMSession> session{
      MFCDMSession::Create(aParams.sessionType(), mCDM.Get(), mManagerThread)};
  if (!session) {
    MFCDM_PARENT_LOG("Failed to create CDM session");
    aResolver(NS_ERROR_DOM_MEDIA_CDM_NO_SESSION_ERR);
    return IPC_OK();
  }

  MFCDM_REJECT_IF_FAILED(session->GenerateRequest(aParams.initDataType(),
                                                  aParams.initData().Elements(),
                                                  aParams.initData().Length()),
                         NS_ERROR_DOM_MEDIA_CDM_SESSION_OPERATION_ERR);
  ConnectSessionEvents(session.get());

  // TODO : now we assume all session ID is available after session is
  // created, but this is not always true. Need to remove this assertion and
  // handle cases where session Id is not available yet.
  const auto& sessionId = session->SessionID();
  MOZ_ASSERT(sessionId);
  mSessions.emplace(*sessionId, std::move(session));
  MFCDM_PARENT_LOG("Created a CDM session!");
  aResolver(*sessionId);
  return IPC_OK();
}

mozilla::ipc::IPCResult MFCDMParent::RecvLoadSession(
    const KeySystemConfig::SessionType& aSessionType,
    const nsString& aSessionId, LoadSessionResolver&& aResolver) {
  MOZ_ASSERT(mCDM, "RecvInit() must be called and waited on before this call");

  nsresult rv = NS_OK;
  auto* session = GetSession(aSessionId);
  if (!session) {
    aResolver(NS_ERROR_DOM_MEDIA_CDM_NO_SESSION_ERR);
    return IPC_OK();
  }
  MFCDM_REJECT_IF_FAILED(session->Load(aSessionId),
                         NS_ERROR_DOM_MEDIA_CDM_SESSION_OPERATION_ERR);
  aResolver(rv);
  return IPC_OK();
}

mozilla::ipc::IPCResult MFCDMParent::RecvUpdateSession(
    const nsString& aSessionId, const CopyableTArray<uint8_t>& aResponse,
    UpdateSessionResolver&& aResolver) {
  MOZ_ASSERT(mCDM, "RecvInit() must be called and waited on before this call");
  nsresult rv = NS_OK;
  auto* session = GetSession(aSessionId);
  if (!session) {
    aResolver(NS_ERROR_DOM_MEDIA_CDM_NO_SESSION_ERR);
    return IPC_OK();
  }
  MFCDM_REJECT_IF_FAILED(session->Update(aResponse),
                         NS_ERROR_DOM_MEDIA_CDM_SESSION_OPERATION_ERR);
  aResolver(rv);
  return IPC_OK();
}

mozilla::ipc::IPCResult MFCDMParent::RecvCloseSession(
    const nsString& aSessionId, UpdateSessionResolver&& aResolver) {
  MOZ_ASSERT(mCDM, "RecvInit() must be called and waited on before this call");
  nsresult rv = NS_OK;
  auto* session = GetSession(aSessionId);
  if (!session) {
    aResolver(NS_ERROR_DOM_MEDIA_CDM_NO_SESSION_ERR);
    return IPC_OK();
  }
  MFCDM_REJECT_IF_FAILED(session->Close(),
                         NS_ERROR_DOM_MEDIA_CDM_SESSION_OPERATION_ERR);
  aResolver(rv);
  return IPC_OK();
}

mozilla::ipc::IPCResult MFCDMParent::RecvRemoveSession(
    const nsString& aSessionId, UpdateSessionResolver&& aResolver) {
  MOZ_ASSERT(mCDM, "RecvInit() must be called and waited on before this call");
  nsresult rv = NS_OK;
  auto* session = GetSession(aSessionId);
  if (!session) {
    aResolver(NS_ERROR_DOM_MEDIA_CDM_NO_SESSION_ERR);
    return IPC_OK();
  }
  MFCDM_REJECT_IF_FAILED(session->Remove(),
                         NS_ERROR_DOM_MEDIA_CDM_SESSION_OPERATION_ERR);
  aResolver(rv);
  return IPC_OK();
}

mozilla::ipc::IPCResult MFCDMParent::RecvSetServerCertificate(
    const CopyableTArray<uint8_t>& aCertificate,
    UpdateSessionResolver&& aResolver) {
  MOZ_ASSERT(mCDM, "RecvInit() must be called and waited on before this call");
  nsresult rv = NS_OK;
  MFCDM_PARENT_LOG("Set server certificate");
  MFCDM_REJECT_IF_FAILED(mCDM->SetServerCertificate(
                             static_cast<const BYTE*>(aCertificate.Elements()),
                             aCertificate.Length()),
                         NS_ERROR_DOM_MEDIA_CDM_ERR);
  aResolver(rv);
  return IPC_OK();
}

mozilla::ipc::IPCResult MFCDMParent::RecvGetStatusForPolicy(
    const dom::HDCPVersion& aMinHdcpVersion,
    GetStatusForPolicyResolver&& aResolver) {
  MOZ_ASSERT(mCDM, "RecvInit() must be called and waited on before this call");
  aResolver(IsHDCPVersionSupported(mFactory, mKeySystem, aMinHdcpVersion));
  return IPC_OK();
}

void MFCDMParent::ConnectSessionEvents(MFCDMSession* aSession) {
  // TODO : clear session's event source when the session gets removed.
  mKeyMessageEvents.Forward(aSession->KeyMessageEvent());
  mKeyChangeEvents.Forward(aSession->KeyChangeEvent());
  mExpirationEvents.Forward(aSession->ExpirationEvent());
}

MFCDMSession* MFCDMParent::GetSession(const nsString& aSessionId) {
  AssertOnManagerThread();
  auto iter = mSessions.find(aSessionId);
  if (iter == mSessions.end()) {
    return nullptr;
  }
  return iter->second.get();
}

already_AddRefed<MFCDMProxy> MFCDMParent::GetMFCDMProxy() {
  if (!mCDM) {
    return nullptr;
  }
  RefPtr<MFCDMProxy> proxy = new MFCDMProxy(mCDM.Get(), mId);
  return proxy.forget();
}

/* static */
void MFCDMService::GetAllKeySystemsCapabilities(dom::Promise* aPromise) {
  MOZ_ASSERT(XRE_IsParentProcess());
  const static auto kSandboxKind = ipc::SandboxingKind::MF_MEDIA_ENGINE_CDM;
  LaunchMFCDMProcessIfNeeded(kSandboxKind)
      ->Then(
          GetMainThreadSerialEventTarget(), __func__,
          [promise = RefPtr(aPromise)]() {
            RefPtr<ipc::UtilityAudioDecoderChild> uadc =
                ipc::UtilityAudioDecoderChild::GetSingleton(kSandboxKind);
            if (NS_WARN_IF(!uadc)) {
              promise->MaybeReject(NS_ERROR_FAILURE);
              return;
            }
            uadc->GetKeySystemCapabilities(promise);
          },
          [promise = RefPtr(aPromise)](nsresult aError) {
            promise->MaybeReject(NS_ERROR_FAILURE);
          });
}

/* static */
RefPtr<GenericNonExclusivePromise> MFCDMService::LaunchMFCDMProcessIfNeeded(
    ipc::SandboxingKind aSandbox) {
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(aSandbox == ipc::SandboxingKind::MF_MEDIA_ENGINE_CDM);
  RefPtr<ipc::UtilityProcessManager> utilityProc =
      ipc::UtilityProcessManager::GetSingleton();
  if (NS_WARN_IF(!utilityProc)) {
    NS_WARNING("Failed to get UtilityProcessManager");
    return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE,
                                                       __func__);
  }

  // Check if the MFCDM process exists or not. If not, launch it.
  if (utilityProc->Process(aSandbox)) {
    return GenericNonExclusivePromise::CreateAndResolve(true, __func__);
  }

  RefPtr<ipc::UtilityAudioDecoderChild> uadc =
      ipc::UtilityAudioDecoderChild::GetSingleton(aSandbox);
  if (NS_WARN_IF(!uadc)) {
    NS_WARNING("Failed to get UtilityAudioDecoderChild");
    return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE,
                                                       __func__);
  }
  return utilityProc->StartUtility(uadc, aSandbox)
      ->Then(
          GetMainThreadSerialEventTarget(), __func__,
          [uadc, utilityProc, aSandbox]() {
            RefPtr<ipc::UtilityProcessParent> parent =
                utilityProc->GetProcessParent(aSandbox);
            if (!parent) {
              NS_WARNING("UtilityAudioDecoderParent lost in the middle");
              return GenericNonExclusivePromise::CreateAndReject(
                  NS_ERROR_FAILURE, __func__);
            }

            if (!uadc->CanSend()) {
              NS_WARNING("UtilityAudioDecoderChild lost in the middle");
              return GenericNonExclusivePromise::CreateAndReject(
                  NS_ERROR_FAILURE, __func__);
            }
            return GenericNonExclusivePromise::CreateAndResolve(true, __func__);
          },
          [](ipc::LaunchError const& aError) {
            NS_WARNING("Failed to start the MFCDM process!");
            return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE,
                                                               __func__);
          });
}

/* static */
void MFCDMService::UpdateWidevineL1Path(nsIFile* aFile) {
  RefPtr<ipc::UtilityProcessManager> utilityProc =
      ipc::UtilityProcessManager::GetSingleton();
  if (NS_WARN_IF(!utilityProc)) {
    NS_WARNING("Failed to get UtilityProcessManager");
    return;
  }

  // If the MFCDM process hasn't been created yet, then we will set the path
  // when creating the process later.
  const auto sandboxKind = ipc::SandboxingKind::MF_MEDIA_ENGINE_CDM;
  if (!utilityProc->Process(sandboxKind)) {
    return;
  }

  // The MFCDM process has been started, we need to update its L1 path and set
  // the permission for LPAC.
  nsString widevineL1Path;
  MOZ_ASSERT(aFile);
  if (NS_WARN_IF(NS_FAILED(aFile->GetTarget(widevineL1Path)))) {
    NS_WARNING("MFCDMService::UpdateWidevineL1Path, Failed to get L1 path!");
    return;
  }

  RefPtr<ipc::UtilityAudioDecoderChild> uadc =
      ipc::UtilityAudioDecoderChild::GetSingleton(sandboxKind);
  if (NS_WARN_IF(!uadc)) {
    NS_WARNING("Failed to get UtilityAudioDecoderChild");
    return;
  }
  Unused << uadc->SendUpdateWidevineL1Path(widevineL1Path);
#ifdef MOZ_WMF_CDM_LPAC_SANDBOX
  SandboxBroker::EnsureLpacPermsissionsOnDir(widevineL1Path);
#endif
}

#undef MFCDM_REJECT_IF_FAILED
#undef MFCDM_REJECT_IF
#undef MFCDM_RETURN_IF_FAILED
#undef MFCDM_RETURN_BOOL_IF_FAILED
#undef MFCDM_PARENT_SLOG
#undef MFCDM_PARENT_LOG

}  // namespace mozilla

Messung V0.5
C=95 H=96 G=95

¤ Dauer der Verarbeitung: 0.17 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.