tor-browser

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

MFCDMParent.cpp (64934B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "MFCDMParent.h"
      6 
      7 #include <mfmediaengine.h>
      8 #include <unknwnbase.h>
      9 #include <wtypes.h>
     10 
     11 #include "mozilla/Likely.h"
     12 #define INITGUID  // Enable DEFINE_PROPERTYKEY()
     13 #include <prinrval.h>
     14 #include <propkeydef.h>   // For DEFINE_PROPERTYKEY() definition
     15 #include <propvarutil.h>  // For InitPropVariantFrom*()
     16 
     17 #include "MFCDMProxy.h"
     18 #include "MFMediaEngineUtils.h"
     19 #include "RemoteDecodeUtils.h"       // For GetCurrentSandboxingKind()
     20 #include "SpecialSystemDirectory.h"  // For temp dir
     21 #include "WMFUtils.h"
     22 #include "mozilla/DataMutex.h"
     23 #include "mozilla/EMEUtils.h"
     24 #include "mozilla/KeySystemConfig.h"
     25 #include "mozilla/ProfilerMarkers.h"
     26 #include "mozilla/RandomNum.h"
     27 #include "mozilla/StaticMutex.h"
     28 #include "mozilla/StaticPrefs_media.h"
     29 #include "mozilla/WindowsVersion.h"
     30 #include "mozilla/dom/KeySystemNames.h"
     31 #include "mozilla/dom/MediaKeysBinding.h"
     32 #include "mozilla/dom/Promise.h"
     33 #include "mozilla/gfx/gfxVars.h"
     34 #include "mozilla/ipc/UtilityMediaServiceChild.h"
     35 #include "mozilla/ipc/UtilityProcessManager.h"
     36 #include "mozilla/ipc/UtilityProcessParent.h"
     37 #include "nsTHashMap.h"
     38 #include "nsTextFormatter.h"
     39 
     40 #ifdef MOZ_WMF_CDM_LPAC_SANDBOX
     41 #  include "sandboxBroker.h"
     42 #endif
     43 
     44 using Microsoft::WRL::ComPtr;
     45 using Microsoft::WRL::MakeAndInitialize;
     46 
     47 namespace mozilla {
     48 
     49 // See
     50 // https://source.chromium.org/chromium/chromium/src/+/main:media/cdm/win/media_foundation_cdm_util.cc;l=26-40;drc=503535015a7b373cc6185c69c991e01fda5da571
     51 #ifndef EME_CONTENTDECRYPTIONMODULE_ORIGIN_ID
     52 DEFINE_PROPERTYKEY(EME_CONTENTDECRYPTIONMODULE_ORIGIN_ID, 0x1218a3e2, 0xcfb0,
     53                   0x4c98, 0x90, 0xe5, 0x5f, 0x58, 0x18, 0xd4, 0xb6, 0x7e,
     54                   PID_FIRST_USABLE);
     55 #endif
     56 
     57 #define MFCDM_PARENT_LOG(msg, ...)                                     \
     58  EME_LOG("MFCDMParent[%p, Id=%" PRIu64 "]@%s: " msg, this, this->mId, \
     59          __func__, ##__VA_ARGS__)
     60 #define MFCDM_PARENT_SLOG(msg, ...) \
     61  EME_LOG("MFCDMParent@%s: " msg, __func__, ##__VA_ARGS__)
     62 
     63 #define MFCDM_RETURN_IF_FAILED(x)                       \
     64  do {                                                  \
     65    HRESULT rv = x;                                     \
     66    if (MOZ_UNLIKELY(FAILED(rv))) {                     \
     67      MFCDM_PARENT_SLOG("(" #x ") failed, rv=%lx", rv); \
     68      return rv;                                        \
     69    }                                                   \
     70  } while (false)
     71 
     72 #define MFCDM_RETURN_BOOL_IF_FAILED(x)                  \
     73  do {                                                  \
     74    HRESULT rv = x;                                     \
     75    if (MOZ_UNLIKELY(FAILED(rv))) {                     \
     76      MFCDM_PARENT_SLOG("(" #x ") failed, rv=%lx", rv); \
     77      return false;                                     \
     78    }                                                   \
     79  } while (false)
     80 
     81 #define MFCDM_REJECT_IF(pred, rv)                                      \
     82  do {                                                                 \
     83    if (MOZ_UNLIKELY(pred)) {                                          \
     84      MFCDM_PARENT_LOG("reject for [" #pred "], rv=%x", uint32_t(rv)); \
     85      aResolver(rv);                                                   \
     86      return IPC_OK();                                                 \
     87    }                                                                  \
     88  } while (false)
     89 
     90 #define MFCDM_REJECT_IF_FAILED(op, rv)                                       \
     91  do {                                                                       \
     92    HRESULT hr = op;                                                         \
     93    if (MOZ_UNLIKELY(FAILED(hr))) {                                          \
     94      MFCDM_PARENT_LOG("(" #op ") failed(hr=%lx), rv=%x", hr, uint32_t(rv)); \
     95      aResolver(rv);                                                         \
     96      return IPC_OK();                                                       \
     97    }                                                                        \
     98  } while (false)
     99 
    100 MOZ_RUNINIT static StaticDataMutex<
    101    nsTHashMap<nsStringHashKey, ComPtr<IMFContentDecryptionModuleFactory>>>
    102    sFactoryMap("sFactoryMap");
    103 MOZ_RUNINIT static StaticDataMutex<CopyableTArray<MFCDMCapabilitiesIPDL>>
    104    sCapabilities("sCapabilities");
    105 MOZ_RUNINIT static StaticDataMutex<ComPtr<IUnknown>> sMediaEngineClassFactory(
    106    "sMediaEngineClassFactory");
    107 
    108 #define ASSERT_CDM_ACCESS_READ_ONLY_ON_MANAGER_THREAD() \
    109  mCDMAccessLock.Target().AssertOnCurrentThread();      \
    110  mCDMAccessLock.NoteOnTarget();
    111 
    112 #define ASSERT_CDM_ACCESS_ON_MANAGER_THREAD() \
    113  mCDMAccessLock.Target().AssertOnCurrentThread();
    114 
    115 // Generate a dummy session ID for resolving the new session promise during
    116 // GenerateRequest() when DRM_E_TEE_INVALID_HWDRM_STATE happens.
    117 // An example of the generated session ID is DUMMY_9F656F4D76BE30D4.
    118 static nsString GenerateDummySessionId() {
    119  nsString sessionId;
    120  sessionId.AppendLiteral(u"DUMMY_");
    121  char buf[17];
    122  uint64_t randomValue = mozilla::RandomUint64OrDie();
    123  SprintfLiteral(buf, "%016" PRIX64, randomValue);
    124  sessionId.AppendASCII(buf);
    125  return sessionId;
    126 }
    127 
    128 // RAIIized PROPVARIANT. See
    129 // third_party/libwebrtc/modules/audio_device/win/core_audio_utility_win.h
    130 class AutoPropVar {
    131 public:
    132  AutoPropVar() { PropVariantInit(&mVar); }
    133 
    134  ~AutoPropVar() { Reset(); }
    135 
    136  AutoPropVar(const AutoPropVar&) = delete;
    137  AutoPropVar& operator=(const AutoPropVar&) = delete;
    138  bool operator==(const AutoPropVar&) const = delete;
    139  bool operator!=(const AutoPropVar&) const = delete;
    140 
    141  // Returns a pointer to the underlying PROPVARIANT for use as an out param
    142  // in a function call.
    143  PROPVARIANT* Receive() {
    144    MOZ_ASSERT(mVar.vt == VT_EMPTY);
    145    return &mVar;
    146  }
    147 
    148  // Clears the instance to prepare it for re-use (e.g., via Receive).
    149  void Reset() {
    150    if (mVar.vt != VT_EMPTY) {
    151      HRESULT hr = PropVariantClear(&mVar);
    152      MOZ_ASSERT(SUCCEEDED(hr));
    153      (void)hr;
    154    }
    155  }
    156 
    157  const PROPVARIANT& get() const { return mVar; }
    158  const PROPVARIANT* ptr() const { return &mVar; }
    159 
    160 private:
    161  PROPVARIANT mVar;
    162 };
    163 
    164 static MF_MEDIAKEYS_REQUIREMENT ToMFRequirement(
    165    const KeySystemConfig::Requirement aRequirement) {
    166  switch (aRequirement) {
    167    case KeySystemConfig::Requirement::NotAllowed:
    168      return MF_MEDIAKEYS_REQUIREMENT_NOT_ALLOWED;
    169    case KeySystemConfig::Requirement::Optional:
    170      return MF_MEDIAKEYS_REQUIREMENT_OPTIONAL;
    171    case KeySystemConfig::Requirement::Required:
    172      return MF_MEDIAKEYS_REQUIREMENT_REQUIRED;
    173  }
    174 };
    175 
    176 static inline LPCWSTR InitDataTypeToString(const nsAString& aInitDataType) {
    177  // The strings are defined in https://www.w3.org/TR/eme-initdata-registry/
    178  if (aInitDataType.EqualsLiteral("webm")) {
    179    return L"webm";
    180  } else if (aInitDataType.EqualsLiteral("cenc")) {
    181    return L"cenc";
    182  } else if (aInitDataType.EqualsLiteral("keyids")) {
    183    return L"keyids";
    184  } else {
    185    return L"unknown";
    186  }
    187 }
    188 
    189 static bool RequireClearLead(const nsString& aKeySystem) {
    190  return aKeySystem.EqualsLiteral(kWidevineExperiment2KeySystemName) ||
    191         aKeySystem.EqualsLiteral(kPlayReadyHardwareClearLeadKeySystemName);
    192 }
    193 
    194 static void BuildCapabilitiesArray(
    195    const nsTArray<MFCDMMediaCapability>& aCapabilities,
    196    AutoPropVar& capabilitiesPropOut) {
    197  PROPVARIANT* capabilitiesArray = (PROPVARIANT*)CoTaskMemAlloc(
    198      sizeof(PROPVARIANT) * aCapabilities.Length());
    199  for (size_t idx = 0; idx < aCapabilities.Length(); idx++) {
    200    ComPtr<IPropertyStore> capabilitiesProperty;
    201    RETURN_VOID_IF_FAILED(
    202        PSCreateMemoryPropertyStore(IID_PPV_ARGS(&capabilitiesProperty)));
    203 
    204    AutoPropVar contentType;
    205    auto* var = contentType.Receive();
    206    var->vt = VT_BSTR;
    207    var->bstrVal = SysAllocString(aCapabilities[idx].contentType().get());
    208    RETURN_VOID_IF_FAILED(
    209        capabilitiesProperty->SetValue(MF_EME_CONTENTTYPE, contentType.get()));
    210 
    211    AutoPropVar robustness;
    212    var = robustness.Receive();
    213    var->vt = VT_BSTR;
    214    var->bstrVal = SysAllocString(aCapabilities[idx].robustness().get());
    215    RETURN_VOID_IF_FAILED(
    216        capabilitiesProperty->SetValue(MF_EME_ROBUSTNESS, robustness.get()));
    217 
    218    capabilitiesArray[idx].vt = VT_UNKNOWN;
    219    capabilitiesArray[idx].punkVal = capabilitiesProperty.Detach();
    220  }
    221  auto* var = capabilitiesPropOut.Receive();
    222  var->vt = VT_VARIANT | VT_VECTOR;
    223  var->capropvar.cElems = aCapabilities.Length();
    224  var->capropvar.pElems = capabilitiesArray;
    225 }
    226 
    227 static HRESULT BuildCDMAccessConfig(const MFCDMInitParamsIPDL& aParams,
    228                                    ComPtr<IPropertyStore>& aConfig) {
    229  ComPtr<IPropertyStore> mksc;  // EME MediaKeySystemConfiguration
    230  MFCDM_RETURN_IF_FAILED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(&mksc)));
    231 
    232  // Init type. If we don't set `MF_EME_INITDATATYPES` then we won't be able
    233  // to create CDM module on Windows 10, which is not documented officially.
    234  BSTR* initDataTypeArray =
    235      (BSTR*)CoTaskMemAlloc(sizeof(BSTR) * aParams.initDataTypes().Length());
    236  for (size_t i = 0; i < aParams.initDataTypes().Length(); i++) {
    237    initDataTypeArray[i] =
    238        SysAllocString(InitDataTypeToString(aParams.initDataTypes()[i]));
    239  }
    240  AutoPropVar initDataTypes;
    241  PROPVARIANT* var = initDataTypes.Receive();
    242  var->vt = VT_VECTOR | VT_BSTR;
    243  var->cabstr.cElems = static_cast<ULONG>(aParams.initDataTypes().Length());
    244  var->cabstr.pElems = initDataTypeArray;
    245  MFCDM_RETURN_IF_FAILED(
    246      mksc->SetValue(MF_EME_INITDATATYPES, initDataTypes.get()));
    247 
    248  // Audio capabilities
    249  AutoPropVar audioCapabilities;
    250  BuildCapabilitiesArray(aParams.audioCapabilities(), audioCapabilities);
    251  MFCDM_RETURN_IF_FAILED(
    252      mksc->SetValue(MF_EME_AUDIOCAPABILITIES, audioCapabilities.get()));
    253 
    254  // Video capabilities
    255  AutoPropVar videoCapabilities;
    256  BuildCapabilitiesArray(aParams.videoCapabilities(), videoCapabilities);
    257  MFCDM_RETURN_IF_FAILED(
    258      mksc->SetValue(MF_EME_VIDEOCAPABILITIES, videoCapabilities.get()));
    259 
    260  // Persist state
    261  AutoPropVar persistState;
    262  InitPropVariantFromUInt32(ToMFRequirement(aParams.persistentState()),
    263                            persistState.Receive());
    264  MFCDM_RETURN_IF_FAILED(
    265      mksc->SetValue(MF_EME_PERSISTEDSTATE, persistState.get()));
    266 
    267  // Distintive Id
    268  AutoPropVar distinctiveID;
    269  InitPropVariantFromUInt32(ToMFRequirement(aParams.distinctiveID()),
    270                            distinctiveID.Receive());
    271  MFCDM_RETURN_IF_FAILED(
    272      mksc->SetValue(MF_EME_DISTINCTIVEID, distinctiveID.get()));
    273 
    274  aConfig.Swap(mksc);
    275  return S_OK;
    276 }
    277 
    278 static HRESULT BuildCDMProperties(const nsString& aOrigin,
    279                                  ComPtr<IPropertyStore>& aProps) {
    280  MOZ_ASSERT(!aOrigin.IsEmpty());
    281 
    282  ComPtr<IPropertyStore> props;
    283  MFCDM_RETURN_IF_FAILED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(&props)));
    284 
    285  AutoPropVar origin;
    286  MFCDM_RETURN_IF_FAILED(
    287      InitPropVariantFromString(aOrigin.get(), origin.Receive()));
    288  MFCDM_RETURN_IF_FAILED(
    289      props->SetValue(EME_CONTENTDECRYPTIONMODULE_ORIGIN_ID, origin.get()));
    290 
    291  // TODO: support client token?
    292 
    293  // TODO: CDM store path per profile?
    294  nsCOMPtr<nsIFile> dir;
    295  if (NS_FAILED(GetSpecialSystemDirectory(OS_TemporaryDirectory,
    296                                          getter_AddRefs(dir)))) {
    297    return E_ACCESSDENIED;
    298  }
    299  if (NS_FAILED(dir->AppendNative(nsDependentCString("mfcdm")))) {
    300    return E_ACCESSDENIED;
    301  }
    302  nsresult rv = dir->Create(nsIFile::DIRECTORY_TYPE, 0700);
    303  if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_FAILED(rv)) {
    304    return E_ACCESSDENIED;
    305  }
    306  nsAutoString cdmStorePath;
    307  if (NS_FAILED(dir->GetPath(cdmStorePath))) {
    308    return E_ACCESSDENIED;
    309  }
    310 
    311  AutoPropVar path;
    312  MFCDM_RETURN_IF_FAILED(
    313      InitPropVariantFromString(cdmStorePath.get(), path.Receive()));
    314  MFCDM_RETURN_IF_FAILED(
    315      props->SetValue(MF_CONTENTDECRYPTIONMODULE_STOREPATH, path.get()));
    316 
    317  aProps.Swap(props);
    318  return S_OK;
    319 }
    320 
    321 static HRESULT CreateContentDecryptionModule(
    322    ComPtr<IMFContentDecryptionModuleFactory> aFactory,
    323    const nsString& aKeySystem, const MFCDMInitParamsIPDL& aParams,
    324    ComPtr<IMFContentDecryptionModule>& aCDMOut) {
    325  // Get access object to CDM.
    326  ComPtr<IPropertyStore> accessConfig;
    327  RETURN_IF_FAILED(BuildCDMAccessConfig(aParams, accessConfig));
    328 
    329  AutoTArray<IPropertyStore*, 1> configs = {accessConfig.Get()};
    330  ComPtr<IMFContentDecryptionModuleAccess> cdmAccess;
    331  RETURN_IF_FAILED(aFactory->CreateContentDecryptionModuleAccess(
    332      aKeySystem.get(), configs.Elements(), configs.Length(), &cdmAccess));
    333 
    334  // Get CDM.
    335  ComPtr<IPropertyStore> cdmProps;
    336  RETURN_IF_FAILED(BuildCDMProperties(aParams.origin(), cdmProps));
    337  ComPtr<IMFContentDecryptionModule> cdm;
    338  RETURN_IF_FAILED(
    339      cdmAccess->CreateContentDecryptionModule(cdmProps.Get(), &cdm));
    340  aCDMOut.Swap(cdm);
    341  return S_OK;
    342 }
    343 
    344 // Wrapper function for IMFContentDecryptionModuleFactory::IsTypeSupported.
    345 static bool IsTypeSupported(
    346    const ComPtr<IMFContentDecryptionModuleFactory>& aFactory,
    347    const nsString& aKeySystem, const nsString* aContentType = nullptr) {
    348  nsString keySystem;
    349  // Widevine's factory only takes original key system string.
    350  if (IsWidevineExperimentKeySystemAndSupported(aKeySystem)) {
    351    keySystem.AppendLiteral(u"com.widevine.alpha");
    352  }
    353  // kPlayReadyHardwareClearLeadKeySystemName is our custom key system name,
    354  // we should use kPlayReadyKeySystemHardware which is the real key system
    355  // name.
    356  else if (aKeySystem.EqualsLiteral(kPlayReadyHardwareClearLeadKeySystemName)) {
    357    keySystem.AppendLiteral(kPlayReadyKeySystemHardware);
    358  } else {
    359    keySystem = aKeySystem;
    360  }
    361  return aFactory->IsTypeSupported(
    362      keySystem.get(), aContentType ? aContentType->get() : nullptr);
    363 }
    364 
    365 static nsString MapKeySystem(const nsString& aKeySystem) {
    366  // When website requests HW secure robustness for video by original Widevine
    367  // key system name, it would be mapped to this key system which is for HWDRM.
    368  if (IsWidevineKeySystem(aKeySystem)) {
    369    return nsString(u"com.widevine.alpha.experiment");
    370  }
    371  // kPlayReadyHardwareClearLeadKeySystemName is our custom key system name,
    372  // we should use kPlayReadyKeySystemHardware which is the real key system
    373  // name.
    374  if (aKeySystem.EqualsLiteral(kPlayReadyHardwareClearLeadKeySystemName)) {
    375    return NS_ConvertUTF8toUTF16(kPlayReadyKeySystemHardware);
    376  }
    377  return aKeySystem;
    378 }
    379 
    380 static bool IsBeingProfiledOrLogEnabled() {
    381  return MOZ_LOG_TEST(GetEMELog(), LogLevel::Info) ||
    382         profiler_thread_is_being_profiled_for_markers();
    383 }
    384 
    385 /* static */
    386 void MFCDMParent::SetWidevineL1Path(const char* aPath) {
    387  nsAutoCString path(aPath);
    388  path.AppendLiteral("\\Google.Widevine.CDM.dll");
    389  sWidevineL1Path = CreateBSTRFromConstChar(path.get());
    390  MFCDM_PARENT_SLOG("Set Widevine L1 dll path=%ls\n", sWidevineL1Path);
    391 }
    392 
    393 void MFCDMParent::Register() {
    394  MOZ_ASSERT(!sRegisteredCDMs.Contains(this->mId));
    395  sRegisteredCDMs.InsertOrUpdate(this->mId, this);
    396  MFCDM_PARENT_LOG("Registered!");
    397 }
    398 
    399 void MFCDMParent::Unregister() {
    400  MOZ_ASSERT(sRegisteredCDMs.Contains(this->mId));
    401  sRegisteredCDMs.Remove(this->mId);
    402  MFCDM_PARENT_LOG("Unregistered!");
    403 }
    404 
    405 MFCDMParent::MFCDMParent(const nsAString& aKeySystem,
    406                         RemoteMediaManagerParent* aManager,
    407                         nsISerialEventTarget* aManagerThread)
    408    : mKeySystem(aKeySystem),
    409      mManager(aManager),
    410      mManagerThread(aManagerThread),
    411      mId(sNextId++),
    412      mKeyMessageEvents(aManagerThread),
    413      mKeyChangeEvents(aManagerThread),
    414      mExpirationEvents(aManagerThread),
    415      mClosedEvents(aManagerThread),
    416      mCDMAccessLock("MFCDMParent", mManagerThread) {
    417  MOZ_ASSERT(IsPlayReadyKeySystemAndSupported(aKeySystem) ||
    418             IsWidevineExperimentKeySystemAndSupported(aKeySystem) ||
    419             IsWidevineKeySystem(mKeySystem) ||
    420             IsWMFClearKeySystemAndSupported(aKeySystem));
    421  MOZ_ASSERT(aManager);
    422  MOZ_ASSERT(aManagerThread);
    423  MOZ_ASSERT(XRE_IsUtilityProcess());
    424  MOZ_ASSERT(GetCurrentSandboxingKind() ==
    425             ipc::SandboxingKind::MF_MEDIA_ENGINE_CDM);
    426 
    427  if (IsBeingProfiledOrLogEnabled()) {
    428    nsPrintfCString msg("MFCDMParent created for %s",
    429                        NS_ConvertUTF16toUTF8(aKeySystem).get());
    430    MFCDM_PARENT_LOG("%s", msg.get());
    431    PROFILER_MARKER_TEXT("MFCDMParent::Ctor", MEDIA_PLAYBACK, {}, msg);
    432  }
    433  mIPDLSelfRef = this;
    434  Register();
    435 
    436  mKeyMessageListener = mKeyMessageEvents.Connect(
    437      mManagerThread, this, &MFCDMParent::SendOnSessionKeyMessage);
    438  mKeyChangeListener = mKeyChangeEvents.Connect(
    439      mManagerThread, this, &MFCDMParent::SendOnSessionKeyStatusesChanged);
    440  mExpirationListener = mExpirationEvents.Connect(
    441      mManagerThread, this, &MFCDMParent::SendOnSessionKeyExpiration);
    442  mClosedListener = mClosedEvents.Connect(mManagerThread, this,
    443                                          &MFCDMParent::SendOnSessionClosed);
    444 
    445  RETURN_VOID_IF_FAILED(GetOrCreateFactory(mKeySystem, mFactory));
    446 }
    447 
    448 void MFCDMParent::ShutdownCDM() {
    449  ASSERT_CDM_ACCESS_ON_MANAGER_THREAD();
    450  MutexAutoLock lock(Mutex());
    451  mCDMAccessLock.NoteExclusiveAccess();
    452  if (!mCDM) {
    453    return;
    454  }
    455  auto rv = mCDM->SetPMPHostApp(nullptr);
    456  if (FAILED(rv)) {
    457    MFCDM_PARENT_LOG("Failed to clear PMP Host App, rv=%lx", rv);
    458  }
    459  if (mCDMProxy) {
    460    mCDMProxy->Shutdown();
    461    mCDMProxy = nullptr;
    462  }
    463  SHUTDOWN_IF_POSSIBLE(mCDM);
    464  mCDM = nullptr;
    465  MFCDM_PARENT_LOG("Shutdown CDM completed");
    466 }
    467 
    468 void MFCDMParent::Destroy() {
    469  ASSERT_CDM_ACCESS_READ_ONLY_ON_MANAGER_THREAD();
    470  PROFILER_MARKER_UNTYPED("MFCDMParent::Destroy", MEDIA_PLAYBACK);
    471  mKeyMessageEvents.DisconnectAll();
    472  mKeyChangeEvents.DisconnectAll();
    473  mExpirationEvents.DisconnectAll();
    474  mClosedEvents.DisconnectAll();
    475  mKeyMessageListener.DisconnectIfExists();
    476  mKeyChangeListener.DisconnectIfExists();
    477  mExpirationListener.DisconnectIfExists();
    478  mClosedListener.DisconnectIfExists();
    479  if (mPMPHostWrapper) {
    480    mPMPHostWrapper->Shutdown();
    481    mPMPHostWrapper = nullptr;
    482  }
    483  ShutdownCDM();
    484  mFactory = nullptr;
    485  for (auto& iter : mSessions) {
    486    iter.second->Close(dom::MediaKeySessionClosedReason::Closed_by_application);
    487  }
    488  mSessions.clear();
    489  mIPDLSelfRef = nullptr;
    490 }
    491 
    492 MFCDMParent::~MFCDMParent() {
    493  MFCDM_PARENT_LOG("MFCDMParent detroyed");
    494  Unregister();
    495 }
    496 
    497 /* static */
    498 LPCWSTR MFCDMParent::GetCDMLibraryName(const nsString& aKeySystem) {
    499  if (IsWMFClearKeySystemAndSupported(aKeySystem) ||
    500      StaticPrefs::media_eme_wmf_use_mock_cdm_for_external_cdms()) {
    501    return L"wmfclearkey.dll";
    502  }
    503  // PlayReady is a built-in CDM on Windows, no need to load external library.
    504  if (IsPlayReadyKeySystemAndSupported(aKeySystem)) {
    505    return L"";
    506  }
    507  if (IsWidevineExperimentKeySystemAndSupported(aKeySystem) ||
    508      IsWidevineKeySystem(aKeySystem)) {
    509    return sWidevineL1Path ? sWidevineL1Path : L"L1-not-found";
    510  }
    511  return L"Unknown";
    512 }
    513 
    514 /* static */
    515 void MFCDMParent::Shutdown() {
    516  {
    517    auto factoryMap = sFactoryMap.Lock();
    518    factoryMap->Clear();
    519  }
    520  {
    521    auto capabilities = sCapabilities.Lock();
    522    capabilities->Clear();
    523  }
    524  {
    525    auto mediaEngineClassFactory = sMediaEngineClassFactory.Lock();
    526    mediaEngineClassFactory->Reset();
    527  }
    528 }
    529 
    530 /* static */
    531 HRESULT MFCDMParent::GetOrCreateFactory(
    532    const nsString& aKeySystem,
    533    ComPtr<IMFContentDecryptionModuleFactory>& aFactoryOut) {
    534  auto factoryMap = sFactoryMap.Lock();
    535  auto rv = factoryMap->MaybeGet(aKeySystem);
    536  if (!rv) {
    537    MFCDM_PARENT_SLOG("No factory %s, creating...",
    538                      NS_ConvertUTF16toUTF8(aKeySystem).get());
    539    ComPtr<IMFContentDecryptionModuleFactory> factory;
    540    MFCDM_RETURN_IF_FAILED(LoadFactory(aKeySystem, factory));
    541    factoryMap->InsertOrUpdate(aKeySystem, factory);
    542    aFactoryOut.Swap(factory);
    543  } else {
    544    aFactoryOut = *rv;
    545  }
    546  return S_OK;
    547 }
    548 
    549 /* static */
    550 HRESULT MFCDMParent::LoadFactory(
    551    const nsString& aKeySystem,
    552    ComPtr<IMFContentDecryptionModuleFactory>& aFactoryOut) {
    553  LPCWSTR libraryName = GetCDMLibraryName(aKeySystem);
    554  const bool loadFromPlatform = wcslen(libraryName) == 0;
    555  MFCDM_PARENT_SLOG("Load factory for %s (libraryName=%ls)",
    556                    NS_ConvertUTF16toUTF8(aKeySystem).get(), libraryName);
    557 
    558  MFCDM_PARENT_SLOG("Create factory for %s",
    559                    NS_ConvertUTF16toUTF8(aKeySystem).get());
    560  ComPtr<IMFContentDecryptionModuleFactory> cdmFactory;
    561  if (loadFromPlatform) {
    562    ComPtr<IMFMediaEngineClassFactory4> clsFactory;
    563    {
    564      auto mediaEngineClassFactory = sMediaEngineClassFactory.Lock();
    565      if (!*mediaEngineClassFactory) {
    566        MFCDM_RETURN_IF_FAILED(CoCreateInstance(
    567            CLSID_MFMediaEngineClassFactory, nullptr, CLSCTX_INPROC_SERVER,
    568            IID_PPV_ARGS(&*mediaEngineClassFactory)));
    569      }
    570      MFCDM_RETURN_IF_FAILED((*mediaEngineClassFactory).As(&clsFactory));
    571    }
    572    MFCDM_RETURN_IF_FAILED(clsFactory->CreateContentDecryptionModuleFactory(
    573        MapKeySystem(aKeySystem).get(), IID_PPV_ARGS(&cdmFactory)));
    574    if (MOZ_UNLIKELY(!cdmFactory)) {
    575      if (IsBeingProfiledOrLogEnabled()) {
    576        nsPrintfCString msg(
    577            "CreateContentDecryptionModuleFactory succeeded, but still no "
    578            "factory?!");
    579        MFCDM_PARENT_SLOG("%s", msg.get());
    580        PROFILER_MARKER_TEXT("MFCDMParent::LoadFactoryFailed", MEDIA_PLAYBACK,
    581                             {}, msg);
    582      }
    583      return E_UNEXPECTED;
    584    }
    585    aFactoryOut.Swap(cdmFactory);
    586    MFCDM_PARENT_SLOG("Created factory for %s from platform!",
    587                      NS_ConvertUTF16toUTF8(aKeySystem).get());
    588    return S_OK;
    589  }
    590 
    591  // The following path is used for testing, loading a clearkey lib. Widevine
    592  // support will be soon removed.
    593  HMODULE handle = LoadLibraryW(libraryName);
    594  if (!handle) {
    595    MFCDM_PARENT_SLOG("Failed to load library %ls! (error=%lx)", libraryName,
    596                      GetLastError());
    597    return E_FAIL;
    598  }
    599  MFCDM_PARENT_SLOG("Loaded external library '%ls'", libraryName);
    600 
    601  using DllGetActivationFactoryFunc =
    602      HRESULT(WINAPI*)(_In_ HSTRING, _COM_Outptr_ IActivationFactory**);
    603  DllGetActivationFactoryFunc pDllGetActivationFactory =
    604      (DllGetActivationFactoryFunc)GetProcAddress(handle,
    605                                                  "DllGetActivationFactory");
    606  if (!pDllGetActivationFactory) {
    607    MFCDM_PARENT_SLOG("Failed to get activation function!");
    608    return E_FAIL;
    609  }
    610 
    611  // The follow classID format is what Widevine's DLL expects
    612  // "<key_system>.ContentDecryptionModuleFactory". In addition, when querying
    613  // factory, need to use original Widevine key system name.
    614  nsString stringId;
    615  if (StaticPrefs::media_eme_wmf_use_mock_cdm_for_external_cdms() ||
    616      IsWMFClearKeySystemAndSupported(aKeySystem)) {
    617    stringId.AppendLiteral("org.w3.clearkey");
    618  } else if (IsWidevineExperimentKeySystemAndSupported(aKeySystem) ||
    619             IsWidevineKeySystem(aKeySystem)) {
    620    // Widevine's DLL expects "<key_system>.ContentDecryptionModuleFactory" for
    621    // the class Id.
    622    stringId.AppendLiteral("com.widevine.alpha.ContentDecryptionModuleFactory");
    623  }
    624  MFCDM_PARENT_SLOG("Query factory by classId '%s'",
    625                    NS_ConvertUTF16toUTF8(stringId).get());
    626  ScopedHString classId(stringId);
    627  ComPtr<IActivationFactory> pFactory = NULL;
    628  MFCDM_RETURN_IF_FAILED(
    629      pDllGetActivationFactory(classId.Get(), pFactory.GetAddressOf()));
    630 
    631  ComPtr<IInspectable> pInspectable;
    632  MFCDM_RETURN_IF_FAILED(pFactory->ActivateInstance(&pInspectable));
    633  MFCDM_RETURN_IF_FAILED(pInspectable.As(&cdmFactory));
    634  {
    635    auto mediaEngineClassFactory = sMediaEngineClassFactory.Lock();
    636    if (!*mediaEngineClassFactory) {
    637      *mediaEngineClassFactory = pInspectable;
    638    }
    639  }
    640  aFactoryOut.Swap(cdmFactory);
    641  MFCDM_PARENT_SLOG("Created factory for %s from external library!",
    642                    NS_ConvertUTF16toUTF8(aKeySystem).get());
    643  return S_OK;
    644 }
    645 
    646 static nsString GetRobustnessStringForKeySystem(const nsString& aKeySystem,
    647                                                const bool aIsHWSecure,
    648                                                const bool aIsVideo = true) {
    649  if (IsPlayReadyKeySystemAndSupported(aKeySystem)) {
    650    // Audio doesn't support SL3000.
    651    return aIsHWSecure && aIsVideo ? nsString(u"3000") : nsString(u"2000");
    652  }
    653  if (IsWidevineExperimentKeySystemAndSupported(aKeySystem)) {
    654    return aIsHWSecure ? nsString(u"HW_SECURE_ALL")
    655                       : nsString(u"SW_SECURE_DECODE");
    656  }
    657  return nsString(u"");
    658 }
    659 
    660 // Use IMFContentDecryptionModuleFactory::IsTypeSupported() to get DRM
    661 // capabilities. The query string is based on following, they are pretty much
    662 // equivalent.
    663 // https://learn.microsoft.com/en-us/uwp/api/windows.media.protection.protectioncapabilities.istypesupported?view=winrt-22621
    664 // https://learn.microsoft.com/en-us/windows/win32/api/mfmediaengine/nf-mfmediaengine-imfextendeddrmtypesupport-istypesupportedex
    665 static bool FactorySupports(
    666    ComPtr<IMFContentDecryptionModuleFactory>& aFactory,
    667    ComPtr<IMFExtendedDRMTypeSupport>& aExtendedDrmTypeSupport,
    668    const nsString& aKeySystem, const nsCString& aVideoCodec,
    669    const nsCString& aAudioCodec = nsCString(""),
    670    const nsString& aAdditionalFeatures = nsString(u""),
    671    bool aIsHWSecure = false) {
    672  // Create query string, MP4 is the only container supported.
    673  nsString contentType(u"video/mp4;codecs=\"");
    674  MOZ_ASSERT(!aVideoCodec.IsEmpty());
    675  contentType.AppendASCII(aVideoCodec);
    676  if (!aAudioCodec.IsEmpty()) {
    677    contentType.AppendLiteral(u",");
    678    contentType.AppendASCII(aAudioCodec);
    679  }
    680  contentType.AppendLiteral(u"\";features=\"");
    681  if (IsWidevineExperimentKeySystemAndSupported(aKeySystem) ||
    682      IsWidevineKeySystem(aKeySystem)) {
    683    // This decoder subsystem settings are only required by Wivevine.
    684    contentType.AppendLiteral(
    685        u"decode-bpc=8,"
    686        "decode-res-x=1920,decode-res-y=1080,"
    687        "decode-bitrate=10000000,decode-fps=30,");
    688    // `encryption-robustness` is for Widevine only.
    689    if (aIsHWSecure) {
    690      contentType.AppendLiteral(u"encryption-robustness=HW_SECURE_ALL,");
    691    } else {
    692      contentType.AppendLiteral(u"encryption-robustness=SW_SECURE_DECODE,");
    693    }
    694  }
    695  if (!aAdditionalFeatures.IsEmpty()) {
    696    contentType.Append(aAdditionalFeatures);
    697  }
    698  contentType.AppendLiteral(u"\"");
    699  // End of the query string
    700 
    701  // PlayReady doesn't implement IsTypeSupported properly, so it requires us to
    702  // use another way to check the capabilities.
    703  if (IsPlayReadyKeySystemAndSupported(aKeySystem)) {
    704    MOZ_ASSERT(aExtendedDrmTypeSupport);
    705    BSTR keySystem = aIsHWSecure
    706                         ? CreateBSTRFromConstChar(kPlayReadyKeySystemHardware)
    707                         : CreateBSTRFromConstChar(kPlayReadyKeySystemName);
    708    MF_MEDIA_ENGINE_CANPLAY canPlay;
    709    aExtendedDrmTypeSupport->IsTypeSupportedEx(
    710        SysAllocString(contentType.get()), keySystem, &canPlay);
    711    bool support =
    712        canPlay !=
    713        MF_MEDIA_ENGINE_CANPLAY::MF_MEDIA_ENGINE_CANPLAY_NOT_SUPPORTED;
    714    MFCDM_PARENT_SLOG(
    715        "IsTypeSupportedEx=%d, canPlay=%d (key-system=%ls, content-type=%s)",
    716        support, canPlay, keySystem, NS_ConvertUTF16toUTF8(contentType).get());
    717    if (aIsHWSecure && support) {
    718      // For HWDRM, `IsTypeSupportedEx` might still return the wrong answer on
    719      // certain devices, so we need to create a dummy CDM to see if the HWDRM
    720      // is really usable or not.
    721      nsTArray<nsString> dummyInitDataType{nsString(u"cenc"),
    722                                           nsString(u"keyids")};
    723      nsString mimeType(u"video/mp4;codecs=\"");
    724      mimeType.AppendASCII(aVideoCodec);
    725      MFCDMMediaCapability dummyVideoCapability{
    726          mimeType,
    727          {CryptoScheme::None},  // No specific scheme
    728          nsString(u"3000")};
    729      MFCDMInitParamsIPDL dummyParam{
    730          nsString(u"dummy"),
    731          dummyInitDataType,
    732          KeySystemConfig::Requirement::Required /* distinctiveID */,
    733          KeySystemConfig::Requirement::Required /* persistent */,
    734          {} /* audio capabilities */,
    735          {dummyVideoCapability} /* video capabilities */,
    736      };
    737      ComPtr<IMFContentDecryptionModule> dummyCDM = nullptr;
    738      if (FAILED(CreateContentDecryptionModule(
    739              aFactory, MapKeySystem(aKeySystem), dummyParam, dummyCDM)) ||
    740          !dummyCDM) {
    741        if (IsBeingProfiledOrLogEnabled()) {
    742          nsPrintfCString msg(
    743              "HWDRM actually not supported (key-system=%ls, content-type=%s)",
    744              keySystem, NS_ConvertUTF16toUTF8(contentType).get());
    745          PROFILER_MARKER_TEXT("MFCDMParent::FailedToUseHWDRM", MEDIA_PLAYBACK,
    746                               {}, msg);
    747          MFCDM_PARENT_SLOG("%s", msg.get());
    748        }
    749        support = false;
    750      }
    751      MFCDM_PARENT_SLOG(
    752          "After HWDRM creation check, support=%d (key-system=%ls, "
    753          "content-type=%s)",
    754          support, keySystem, NS_ConvertUTF16toUTF8(contentType).get());
    755      if (dummyCDM) {
    756        SHUTDOWN_IF_POSSIBLE(dummyCDM);
    757      }
    758    }
    759    return support;
    760  }
    761 
    762  // Checking capabilies from CDM's IsTypeSupported. Widevine implements this
    763  // method well.
    764  bool support = IsTypeSupported(aFactory, aKeySystem, &contentType);
    765  MFCDM_PARENT_SLOG("IsTypeSupport=%d (key-system=%s, content-type=%s)",
    766                    support, NS_ConvertUTF16toUTF8(aKeySystem).get(),
    767                    NS_ConvertUTF16toUTF8(contentType).get());
    768  return support;
    769 }
    770 
    771 static MF_MEDIA_ENGINE_CANPLAY RunHDCPSupportCheck(
    772    const nsString& aKeySystem, const dom::HDCPVersion& aMinHdcpVersion) {
    773  const auto getHDCPPolicyValue = [](const dom::HDCPVersion& aMinHdcpVersion) {
    774    // The HDCP value follows the feature value in
    775    // https://docs.microsoft.com/en-us/uwp/api/windows.media.protection.protectioncapabilities.istypesupported?view=winrt-19041
    776    // - 1 (on without HDCP 2.2 Type 1 restriction)
    777    // - 2 (on with HDCP 2.2 Type 1 restriction)
    778    return (aMinHdcpVersion == dom::HDCPVersion::_2_2 ||
    779            aMinHdcpVersion == dom::HDCPVersion::_2_3)
    780               ? 2
    781               : 1;
    782  };
    783 
    784  // HDCP is indenpendent with codec usage, so we just use H264 in mp4.
    785  const char16_t* kFmt = u"video/mp4;codecs=\"avc1\";features=\"hdcp=%d\"";
    786  nsAutoString contentType;
    787  nsTextFormatter::ssprintf(contentType, kFmt,
    788                            getHDCPPolicyValue(aMinHdcpVersion));
    789  MOZ_ASSERT(!contentType.IsEmpty());
    790  ComPtr<IMFExtendedDRMTypeSupport> spDrmTypeSupport;
    791  auto mediaEngineClassFactory = sMediaEngineClassFactory.Lock();
    792  if (!*mediaEngineClassFactory ||
    793      FAILED((*mediaEngineClassFactory).As(&spDrmTypeSupport))) {
    794    MFCDM_PARENT_SLOG("Failed to get IMFExtendedDRMTypeSupport!");
    795    return MF_MEDIA_ENGINE_CANPLAY_NOT_SUPPORTED;
    796  }
    797 
    798  // Remove clearlead postfix if needed.
    799  nsCString keySystemWithoutPostfix =
    800      NS_ConvertUTF16toUTF8(MapKeySystem(aKeySystem));
    801  BSTR keySystem = CreateBSTRFromConstChar(keySystemWithoutPostfix.get());
    802  MF_MEDIA_ENGINE_CANPLAY canPlay;
    803  spDrmTypeSupport->IsTypeSupportedEx(SysAllocString(contentType.get()),
    804                                      keySystem, &canPlay);
    805  MFCDM_PARENT_SLOG(
    806      "IsTypeSupportedEx for HDCP, canplay=%d (key-system=%ls, "
    807      "content-type=%s)",
    808      static_cast<int32_t>(canPlay), keySystem,
    809      NS_ConvertUTF16toUTF8(contentType).get());
    810  return canPlay;
    811 }
    812 
    813 // Only one HDCP check should run at a time; otherwise, the request response
    814 // may become invalid.
    815 StaticMutex sHDCPMutex;
    816 
    817 static nsresult IsHDCPVersionSupported(const nsString& aKeySystem,
    818                                       const dom::HDCPVersion& aMinHdcpVersion,
    819                                       nsISerialEventTarget* aManagerThread) {
    820  MOZ_ASSERT(!aManagerThread->IsOnCurrentThread(),
    821             "Should not block the manager thread!");
    822 
    823  StaticMutexAutoLock lock(sHDCPMutex);
    824  // https://learn.microsoft.com/en-us/windows/win32/api/mfmediaengine/nf-mfmediaengine-imfextendeddrmtypesupport-istypesupportedex/
    825  // HDCP query may initially return Maybe. It resolves to Probably or
    826  // NotSupported within ~10s, where 10s is the maximum timeout (worst-case)
    827  // rather than a common value. Microsoft recommends a minimum retry interval
    828  // of 500ms.
    829  constexpr auto kPollIntervalMs = 500;
    830  constexpr auto kMaxWaitSec = 10;
    831  const TimeStamp start = TimeStamp::Now();
    832  while ((TimeStamp::Now() - start) < TimeDuration::FromSeconds(kMaxWaitSec)) {
    833    MF_MEDIA_ENGINE_CANPLAY canplay =
    834        RunHDCPSupportCheck(aKeySystem, aMinHdcpVersion);
    835    if (canplay != MF_MEDIA_ENGINE_CANPLAY_MAYBE) {
    836      return canplay == MF_MEDIA_ENGINE_CANPLAY_PROBABLY
    837                 ? NS_OK
    838                 : NS_ERROR_DOM_MEDIA_CDM_HDCP_NOT_SUPPORT;
    839    }
    840    MFCDM_PARENT_SLOG("HDCP support is MAYBE, waiting to check it again...");
    841    PR_Sleep(PR_MillisecondsToInterval(kPollIntervalMs));
    842  }
    843  return NS_ERROR_DOM_MEDIA_CDM_HDCP_NOT_SUPPORT;
    844 }
    845 
    846 static bool IsKeySystemHWSecure(
    847    const nsAString& aKeySystem,
    848    const nsTArray<MFCDMMediaCapability>& aCapabilities) {
    849  if (IsPlayReadyKeySystemAndSupported(aKeySystem)) {
    850    if (aKeySystem.EqualsLiteral(kPlayReadyKeySystemHardware) ||
    851        aKeySystem.EqualsLiteral(kPlayReadyHardwareClearLeadKeySystemName)) {
    852      return true;
    853    }
    854    for (const auto& capabilities : aCapabilities) {
    855      if (capabilities.robustness().EqualsLiteral("3000")) {
    856        return true;
    857      }
    858    }
    859  }
    860  if (IsWidevineExperimentKeySystemAndSupported(aKeySystem) ||
    861      IsWidevineKeySystem(aKeySystem)) {
    862    // We only support Widevine HWDRM.
    863    return true;
    864  }
    865  return false;
    866 }
    867 
    868 /* static */
    869 RefPtr<MFCDMParent::CapabilitiesPromise>
    870 MFCDMParent::GetAllKeySystemsCapabilities() {
    871  MOZ_ASSERT(NS_IsMainThread());
    872  nsCOMPtr<nsISerialEventTarget> backgroundTaskQueue;
    873  if (NS_FAILED(NS_CreateBackgroundTaskQueue(
    874          __func__, getter_AddRefs(backgroundTaskQueue)))) {
    875    MFCDM_PARENT_SLOG(
    876        "Failed to create task queue for all key systems capabilities!");
    877    return CapabilitiesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR,
    878                                                __func__);
    879  }
    880 
    881  RefPtr<CapabilitiesPromise::Private> p =
    882      new CapabilitiesPromise::Private(__func__);
    883  (void)backgroundTaskQueue->Dispatch(NS_NewRunnableFunction(__func__, [p] {
    884    MFCDM_PARENT_SLOG("GetAllKeySystemsCapabilities");
    885    enum SecureLevel : bool {
    886      Software = false,
    887      Hardware = true,
    888    };
    889    const nsTArray<std::pair<nsString, SecureLevel>> kKeySystems{
    890        std::pair<nsString, SecureLevel>(
    891            NS_ConvertUTF8toUTF16(kPlayReadyKeySystemName),
    892            SecureLevel::Software),
    893        std::pair<nsString, SecureLevel>(
    894            NS_ConvertUTF8toUTF16(kPlayReadyKeySystemHardware),
    895            SecureLevel::Hardware),
    896        std::pair<nsString, SecureLevel>(
    897            NS_ConvertUTF8toUTF16(kPlayReadyHardwareClearLeadKeySystemName),
    898            SecureLevel::Hardware),
    899        std::pair<nsString, SecureLevel>(
    900            NS_ConvertUTF8toUTF16(kWidevineExperimentKeySystemName),
    901            SecureLevel::Hardware),
    902        std::pair<nsString, SecureLevel>(
    903            NS_ConvertUTF8toUTF16(kWidevineExperiment2KeySystemName),
    904            SecureLevel::Hardware),
    905    };
    906 
    907    CopyableTArray<MFCDMCapabilitiesIPDL> capabilitiesArr;
    908    for (const auto& keySystem : kKeySystems) {
    909      // Only check the capabilites if the relative prefs for the key system
    910      // are ON.
    911      if (IsPlayReadyKeySystemAndSupported(keySystem.first) ||
    912          IsWidevineExperimentKeySystemAndSupported(keySystem.first)) {
    913        MFCDMCapabilitiesIPDL* c = capabilitiesArr.AppendElement();
    914        CapabilitesFlagSet flags;
    915        if (keySystem.second == SecureLevel::Hardware) {
    916          flags += CapabilitesFlag::HarewareDecryption;
    917        }
    918        if (RequireClearLead(keySystem.first)) {
    919          flags += CapabilitesFlag::NeedClearLeadCheck;
    920        }
    921        GetCapabilities(keySystem.first, flags, nullptr, *c);
    922      }
    923    }
    924 
    925    p->Resolve(std::move(capabilitiesArr), __func__);
    926  }));
    927  return p;
    928 }
    929 
    930 /* static */
    931 void MFCDMParent::GetCapabilities(const nsString& aKeySystem,
    932                                  const CapabilitesFlagSet& aFlags,
    933                                  IMFContentDecryptionModuleFactory* aFactory,
    934                                  MFCDMCapabilitiesIPDL& aCapabilitiesOut) {
    935  aCapabilitiesOut.keySystem() = aKeySystem;
    936  // WMF CDMs usually require these. See
    937  // https://source.chromium.org/chromium/chromium/src/+/main:media/cdm/win/media_foundation_cdm_factory.cc;l=69-73;drc=b3ca5c09fa0aa07b7f9921501f75e43d80f3ba48
    938  aCapabilitiesOut.persistentState() = KeySystemConfig::Requirement::Required;
    939  aCapabilitiesOut.distinctiveID() = KeySystemConfig::Requirement::Required;
    940 
    941  const bool isHardwareDecryption =
    942      aFlags.contains(CapabilitesFlag::HarewareDecryption);
    943  aCapabilitiesOut.isHardwareDecryption() = isHardwareDecryption;
    944  // Return empty capabilites for SWDRM on Windows 10 because it has the process
    945  // leaking problem.
    946  if (!IsWin11OrLater() && !isHardwareDecryption) {
    947    return;
    948  }
    949 
    950  // HWDRM is blocked by gfx downloadable blocklist.
    951  if (isHardwareDecryption && gfx::gfxVars::IsInitialized() &&
    952      !gfx::gfxVars::UseWMFHWDWM()) {
    953    MFCDM_PARENT_SLOG("Block HWDRM for %s",
    954                      NS_ConvertUTF16toUTF8(aKeySystem).get());
    955    return;
    956  }
    957 
    958  // MFCDM requires persistent storage, and can't use in-memory storage, it
    959  // can't be used in private browsing.
    960  if (aFlags.contains(CapabilitesFlag::IsPrivateBrowsing)) {
    961    return;
    962  }
    963 
    964  auto capabilitiesUnlocked = sCapabilities.Lock();
    965  for (auto& capabilities : *capabilitiesUnlocked) {
    966    if (capabilities.keySystem().Equals(aKeySystem) &&
    967        capabilities.isHardwareDecryption() == isHardwareDecryption) {
    968      MFCDM_PARENT_SLOG(
    969          "Return cached capabilities for %s (hardwareDecryption=%d)",
    970          NS_ConvertUTF16toUTF8(aKeySystem).get(), isHardwareDecryption);
    971      aCapabilitiesOut = capabilities;
    972      return;
    973    }
    974  }
    975 
    976  MFCDM_PARENT_SLOG(
    977      "Query capabilities for %s from the factory (hardwareDecryption=%d)",
    978      NS_ConvertUTF16toUTF8(aKeySystem).get(), isHardwareDecryption);
    979 
    980  ComPtr<IMFContentDecryptionModuleFactory> factory = aFactory;
    981  if (!factory) {
    982    RETURN_VOID_IF_FAILED(GetOrCreateFactory(aKeySystem, factory));
    983  }
    984 
    985  ComPtr<IMFExtendedDRMTypeSupport> spDrmTypeSupport;
    986  auto mediaEngineClassFactory = sMediaEngineClassFactory.Lock();
    987  if (!*mediaEngineClassFactory ||
    988      FAILED((*mediaEngineClassFactory).As(&spDrmTypeSupport))) {
    989    MFCDM_PARENT_SLOG("Failed to get IMFExtendedDRMTypeSupport!");
    990    return;
    991  }
    992 
    993  // Widevine requires codec type to be four CC, PlayReady is fine with both.
    994  static auto convertCodecToFourCC =
    995      [](const KeySystemConfig::EMECodecString& aCodec) {
    996        if (aCodec.Equals(KeySystemConfig::EME_CODEC_H264)) {
    997          return "avc1"_ns;
    998        }
    999        if (aCodec.Equals(KeySystemConfig::EME_CODEC_VP8)) {
   1000          return "vp80"_ns;
   1001        }
   1002        if (aCodec.Equals(KeySystemConfig::EME_CODEC_VP9)) {
   1003          return "vp09"_ns;
   1004        }
   1005        if (aCodec.Equals(KeySystemConfig::EME_CODEC_HEVC)) {
   1006          return "hev1"_ns;
   1007        }
   1008        if (aCodec.Equals(KeySystemConfig::EME_CODEC_AV1)) {
   1009          return "av01"_ns;
   1010        }
   1011        if (aCodec.Equals(KeySystemConfig::EME_CODEC_AAC)) {
   1012          return "mp4a"_ns;
   1013        }
   1014        if (aCodec.Equals(KeySystemConfig::EME_CODEC_OPUS)) {
   1015          return "Opus"_ns;
   1016        }
   1017        if (aCodec.Equals(KeySystemConfig::EME_CODEC_VORBIS)) {
   1018          return "vrbs"_ns;
   1019        }
   1020        if (aCodec.Equals(KeySystemConfig::EME_CODEC_FLAC)) {
   1021          return "fLaC"_ns;
   1022        }
   1023        MOZ_ASSERT_UNREACHABLE("Unsupported codec");
   1024        return "none"_ns;
   1025      };
   1026 
   1027  static nsTArray<KeySystemConfig::EMECodecString> kVideoCodecs(
   1028      {KeySystemConfig::EME_CODEC_H264, KeySystemConfig::EME_CODEC_VP8,
   1029       KeySystemConfig::EME_CODEC_VP9, KeySystemConfig::EME_CODEC_HEVC,
   1030       KeySystemConfig::EME_CODEC_AV1});
   1031 
   1032  // Collect schemes supported by all video codecs.
   1033  static nsTArray<CryptoScheme> kSchemes({
   1034      CryptoScheme::Cenc,
   1035      CryptoScheme::Cbcs,
   1036  });
   1037 
   1038  // Remember supported video codecs, which will be used when collecting audio
   1039  // codec support.
   1040  nsTArray<KeySystemConfig::EMECodecString> supportedVideoCodecs;
   1041 
   1042  if (aFlags.contains(CapabilitesFlag::NeedClearLeadCheck)) {
   1043    for (const auto& codec : kVideoCodecs) {
   1044      if (codec == KeySystemConfig::EME_CODEC_HEVC &&
   1045          !StaticPrefs::media_hevc_enabled()) {
   1046        continue;
   1047      }
   1048      CryptoSchemeSet supportedScheme;
   1049      for (const auto& scheme : kSchemes) {
   1050        nsAutoString additionalFeature(u"encryption-type=");
   1051        // If we don't specify 'encryption-iv-size', it would use 8 bytes IV as
   1052        // default [1]. If it's not supported, then we will try 16 bytes later.
   1053        // Since PlayReady 4.0 [2], 8 and 16 bytes IV are both supported. But
   1054        // We're not sure if Widevine supports both or not.
   1055        // [1]
   1056        // https://learn.microsoft.com/en-us/windows/win32/api/mfmediaengine/nf-mfmediaengine-imfextendeddrmtypesupport-istypesupportedex
   1057        // [2]
   1058        // https://learn.microsoft.com/en-us/playready/packaging/content-encryption-modes#initialization-vectors-ivs
   1059        if (scheme == CryptoScheme::Cenc) {
   1060          additionalFeature.AppendLiteral(u"cenc-clearlead,");
   1061        } else {
   1062          additionalFeature.AppendLiteral(u"cbcs-clearlead,");
   1063        }
   1064        bool rv = FactorySupports(factory, spDrmTypeSupport, aKeySystem,
   1065                                  convertCodecToFourCC(codec), nsCString(""),
   1066                                  additionalFeature, isHardwareDecryption);
   1067        MFCDM_PARENT_SLOG("clearlead %s IV 8 bytes %s %s",
   1068                          EnumValueToString(scheme), codec.get(),
   1069                          rv ? "supported" : "not supported");
   1070        if (rv) {
   1071          supportedScheme += scheme;
   1072          continue;
   1073        }
   1074        // Try 16 bytes IV.
   1075        additionalFeature.AppendLiteral(u"encryption-iv-size=16,");
   1076        rv = FactorySupports(factory, spDrmTypeSupport, aKeySystem,
   1077                             convertCodecToFourCC(codec), nsCString(""),
   1078                             additionalFeature, isHardwareDecryption);
   1079        MFCDM_PARENT_SLOG("clearlead %s IV 16 bytes %s %s",
   1080                          EnumValueToString(scheme), codec.get(),
   1081                          rv ? "supported" : "not supported");
   1082 
   1083        if (rv) {
   1084          supportedScheme += scheme;
   1085          continue;
   1086        }
   1087      }
   1088      // Add a capability if supported scheme exists
   1089      if (!supportedScheme.isEmpty()) {
   1090        MFCDMMediaCapability* c =
   1091            aCapabilitiesOut.videoCapabilities().AppendElement();
   1092        c->contentType() = NS_ConvertUTF8toUTF16(codec);
   1093        c->robustness() =
   1094            GetRobustnessStringForKeySystem(aKeySystem, isHardwareDecryption);
   1095        if (supportedScheme.contains(CryptoScheme::Cenc)) {
   1096          c->encryptionSchemes().AppendElement(CryptoScheme::Cenc);
   1097          MFCDM_PARENT_SLOG("%s: +video:%s (cenc)", __func__, codec.get());
   1098        }
   1099        if (supportedScheme.contains(CryptoScheme::Cbcs)) {
   1100          c->encryptionSchemes().AppendElement(CryptoScheme::Cbcs);
   1101          MFCDM_PARENT_SLOG("%s: +video:%s (cbcs)", __func__, codec.get());
   1102        }
   1103        supportedVideoCodecs.AppendElement(codec);
   1104      }
   1105    }
   1106  } else {
   1107    // Non clearlead situation for video codecs
   1108    for (const auto& codec : kVideoCodecs) {
   1109      if (codec == KeySystemConfig::EME_CODEC_HEVC &&
   1110          !StaticPrefs::media_hevc_enabled()) {
   1111        continue;
   1112      }
   1113      if (FactorySupports(factory, spDrmTypeSupport, aKeySystem,
   1114                          convertCodecToFourCC(codec),
   1115                          KeySystemConfig::EMECodecString(""), nsString(u""),
   1116                          isHardwareDecryption)) {
   1117        MFCDMMediaCapability* c =
   1118            aCapabilitiesOut.videoCapabilities().AppendElement();
   1119        c->contentType() = NS_ConvertUTF8toUTF16(codec);
   1120        c->robustness() =
   1121            GetRobustnessStringForKeySystem(aKeySystem, isHardwareDecryption);
   1122        // 'If value is unspecified, default value of "cenc" is used.' See
   1123        // https://learn.microsoft.com/en-us/windows/win32/api/mfmediaengine/nf-mfmediaengine-imfextendeddrmtypesupport-istypesupportedex
   1124        c->encryptionSchemes().AppendElement(CryptoScheme::Cenc);
   1125        MFCDM_PARENT_SLOG("%s: +video:%s (cenc)", __func__, codec.get());
   1126        // Check cbcs scheme support
   1127        if (FactorySupports(
   1128                factory, spDrmTypeSupport, aKeySystem,
   1129                convertCodecToFourCC(codec),
   1130                KeySystemConfig::EMECodecString(""),
   1131                nsString(u"encryption-type=cbcs,encryption-iv-size=16,"),
   1132                isHardwareDecryption)) {
   1133          c->encryptionSchemes().AppendElement(CryptoScheme::Cbcs);
   1134          MFCDM_PARENT_SLOG("%s: +video:%s (cbcs)", __func__, codec.get());
   1135        }
   1136        supportedVideoCodecs.AppendElement(codec);
   1137      }
   1138    }
   1139  }
   1140 
   1141  if (supportedVideoCodecs.IsEmpty()) {
   1142    // Return a capabilities with no codec supported.
   1143    return;
   1144  }
   1145 
   1146  static nsTArray<KeySystemConfig::EMECodecString> kAudioCodecs({
   1147      KeySystemConfig::EME_CODEC_AAC,
   1148      KeySystemConfig::EME_CODEC_FLAC,
   1149      KeySystemConfig::EME_CODEC_OPUS,
   1150      KeySystemConfig::EME_CODEC_VORBIS,
   1151  });
   1152  for (const auto& codec : kAudioCodecs) {
   1153    // Hardware decryption is usually only used for video, so we can just check
   1154    // the software capabilities for audio in order to save some time. As the
   1155    // media foundation would create a new D3D device everytime when we check
   1156    // hardware decryption, which takes way longer time.
   1157    if (FactorySupports(factory, spDrmTypeSupport, aKeySystem,
   1158                        convertCodecToFourCC(supportedVideoCodecs[0]),
   1159                        convertCodecToFourCC(codec), nsString(u""),
   1160                        false /* aIsHWSecure */)) {
   1161      MFCDMMediaCapability* c =
   1162          aCapabilitiesOut.audioCapabilities().AppendElement();
   1163      c->contentType() = NS_ConvertUTF8toUTF16(codec);
   1164      c->robustness() = GetRobustnessStringForKeySystem(
   1165          aKeySystem, false /* aIsHWSecure */, false /* isVideo */);
   1166      c->encryptionSchemes().AppendElement(CryptoScheme::Cenc);
   1167      MFCDM_PARENT_SLOG("%s: +audio:%s", __func__, codec.get());
   1168    }
   1169  }
   1170 
   1171  // TODO: don't hardcode
   1172  aCapabilitiesOut.initDataTypes().AppendElement(u"keyids");
   1173  aCapabilitiesOut.initDataTypes().AppendElement(u"cenc");
   1174  aCapabilitiesOut.sessionTypes().AppendElement(
   1175      KeySystemConfig::SessionType::Temporary);
   1176  aCapabilitiesOut.sessionTypes().AppendElement(
   1177      KeySystemConfig::SessionType::PersistentLicense);
   1178 
   1179  // Cache capabilities for reuse.
   1180  capabilitiesUnlocked->AppendElement(aCapabilitiesOut);
   1181 }
   1182 
   1183 mozilla::ipc::IPCResult MFCDMParent::RecvGetCapabilities(
   1184    const MFCDMCapabilitiesRequest& aRequest,
   1185    GetCapabilitiesResolver&& aResolver) {
   1186  ASSERT_CDM_ACCESS_READ_ONLY_ON_MANAGER_THREAD();
   1187  MFCDM_REJECT_IF(!mFactory, NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   1188  MFCDMCapabilitiesIPDL capabilities;
   1189  CapabilitesFlagSet flags;
   1190  if (aRequest.isHardwareDecryption()) {
   1191    flags += CapabilitesFlag::HarewareDecryption;
   1192  }
   1193  if (RequireClearLead(aRequest.keySystem())) {
   1194    flags += CapabilitesFlag::NeedClearLeadCheck;
   1195  }
   1196  if (aRequest.isPrivateBrowsing()) {
   1197    flags += CapabilitesFlag::IsPrivateBrowsing;
   1198  }
   1199  GetCapabilities(aRequest.keySystem(), flags, mFactory.Get(), capabilities);
   1200  aResolver(std::move(capabilities));
   1201  return IPC_OK();
   1202 }
   1203 
   1204 mozilla::ipc::IPCResult MFCDMParent::RecvInit(
   1205    const MFCDMInitParamsIPDL& aParams, InitResolver&& aResolver) {
   1206  ASSERT_CDM_ACCESS_ON_MANAGER_THREAD();
   1207  static auto RequirementToStr = [](KeySystemConfig::Requirement aRequirement) {
   1208    switch (aRequirement) {
   1209      case KeySystemConfig::Requirement::Required:
   1210        return "Required";
   1211      case KeySystemConfig::Requirement::Optional:
   1212        return "Optional";
   1213      default:
   1214        return "NotAllowed";
   1215    }
   1216  };
   1217 
   1218  const bool isHWSecure =
   1219      IsKeySystemHWSecure(mKeySystem, aParams.videoCapabilities());
   1220  if (IsBeingProfiledOrLogEnabled()) {
   1221    nsPrintfCString msg(
   1222        "(key-system=%s, origin=%s, distinctiveID=%s, "
   1223        "persistentState=%s, "
   1224        "hwSecure=%d)",
   1225        NS_ConvertUTF16toUTF8(mKeySystem).get(),
   1226        NS_ConvertUTF16toUTF8(aParams.origin()).get(),
   1227        RequirementToStr(aParams.distinctiveID()),
   1228        RequirementToStr(aParams.persistentState()), isHWSecure);
   1229    MFCDM_PARENT_LOG("Creating a CDM %s", msg.get());
   1230    PROFILER_MARKER_TEXT("MFCDMParent::RecvInit(creating CDM)", MEDIA_PLAYBACK,
   1231                         {}, msg);
   1232  }
   1233  MFCDM_REJECT_IF(!mFactory, NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   1234  MOZ_ASSERT_IF(isHWSecure, gfx::gfxVars::UseWMFHWDWM());
   1235 
   1236  MOZ_ASSERT(IsTypeSupported(mFactory, mKeySystem));
   1237  {
   1238    MutexAutoLock lock(Mutex());
   1239    mCDMAccessLock.NoteExclusiveAccess();
   1240    MFCDM_REJECT_IF_FAILED(
   1241        CreateContentDecryptionModule(mFactory, MapKeySystem(mKeySystem),
   1242                                      aParams, mCDM),
   1243        NS_ERROR_FAILURE);
   1244    MFCDM_REJECT_IF(!mCDM, NS_ERROR_FAILURE);
   1245    MOZ_ASSERT(!mCDMProxy);
   1246    mCDMProxy = new MFCDMProxy(mCDM.Get(), mId);
   1247 
   1248    MFCDM_PARENT_LOG("Created a CDM!");
   1249    PROFILER_MARKER_UNTYPED("MFCDMParent::RecvInit(created CDM)",
   1250                            MEDIA_PLAYBACK);
   1251    // This is only required by PlayReady.
   1252    if (IsPlayReadyKeySystemAndSupported(mKeySystem)) {
   1253      ComPtr<IMFPMPHost> pmpHost;
   1254      ComPtr<IMFGetService> cdmService;
   1255      MFCDM_REJECT_IF_FAILED(mCDM.As(&cdmService), NS_ERROR_FAILURE);
   1256      MFCDM_REJECT_IF_FAILED(
   1257          cdmService->GetService(MF_CONTENTDECRYPTIONMODULE_SERVICE,
   1258                                 IID_PPV_ARGS(&pmpHost)),
   1259          NS_ERROR_FAILURE);
   1260      MFCDM_REJECT_IF_FAILED(SUCCEEDED(MakeAndInitialize<MFPMPHostWrapper>(
   1261                                 &mPMPHostWrapper, pmpHost)),
   1262                             NS_ERROR_FAILURE);
   1263      MFCDM_REJECT_IF_FAILED(mCDM->SetPMPHostApp(mPMPHostWrapper.Get()),
   1264                             NS_ERROR_FAILURE);
   1265      MFCDM_PARENT_LOG("Set PMPHostWrapper on CDM!");
   1266    }
   1267  }
   1268 
   1269  mIsInited = true;
   1270  aResolver(MFCDMInitIPDL{mId});
   1271  return IPC_OK();
   1272 }
   1273 
   1274 mozilla::ipc::IPCResult MFCDMParent::RecvCreateSessionAndGenerateRequest(
   1275    const MFCDMCreateSessionParamsIPDL& aParams,
   1276    CreateSessionAndGenerateRequestResolver&& aResolver) {
   1277  ASSERT_CDM_ACCESS_READ_ONLY_ON_MANAGER_THREAD();
   1278  MOZ_ASSERT(mIsInited, "Must finish initialization first");
   1279 
   1280  static auto SessionTypeToStr = [](KeySystemConfig::SessionType aSessionType) {
   1281    switch (aSessionType) {
   1282      case KeySystemConfig::SessionType::Temporary:
   1283        return "temporary";
   1284      case KeySystemConfig::SessionType::PersistentLicense:
   1285        return "persistent-license";
   1286      default: {
   1287        MOZ_ASSERT_UNREACHABLE("Unsupported license type!");
   1288        return "invalid";
   1289      }
   1290    }
   1291  };
   1292  if (IsBeingProfiledOrLogEnabled()) {
   1293    nsPrintfCString msg("session for type '%s'",
   1294                        SessionTypeToStr(aParams.sessionType()));
   1295    MFCDM_PARENT_LOG("Creating CDM %s", msg.get());
   1296    PROFILER_MARKER_TEXT(
   1297        "MFCDMParent::RecvCreateSessionAndGenerateRequest(creating)",
   1298        MEDIA_PLAYBACK, {}, msg);
   1299  }
   1300  if (!mCDM) {
   1301    MFCDM_PARENT_LOG("Cannot create CDM session, already shutdown");
   1302    aResolver(NS_ERROR_DOM_MEDIA_CDM_NO_SESSION_ERR);
   1303    return IPC_OK();
   1304  }
   1305  UniquePtr<MFCDMSession> session{
   1306      MFCDMSession::Create(aParams.sessionType(), mCDM.Get(), mManagerThread)};
   1307  if (!session) {
   1308    MFCDM_PARENT_LOG("Failed to create CDM session");
   1309    aResolver(NS_ERROR_DOM_MEDIA_CDM_NO_SESSION_ERR);
   1310    return IPC_OK();
   1311  }
   1312  ConnectSessionEvents(session.get());
   1313 
   1314  HRESULT hr = session->GenerateRequest(aParams.initDataType(),
   1315                                        aParams.initData().Elements(),
   1316                                        aParams.initData().Length());
   1317  if (hr == DRM_E_TEE_INVALID_HWDRM_STATE) {
   1318    MFCDM_PARENT_LOG(
   1319        "Failed to generate request due to DRM_E_TEE_INVALID_HWDRM_STATE");
   1320    mCDMProxy->OnHardwareContextReset();
   1321    session->Close(dom::MediaKeySessionClosedReason::Hardware_context_reset);
   1322    aResolver(GenerateDummySessionId());
   1323    return IPC_OK();
   1324  }
   1325 
   1326  if (FAILED(hr)) {
   1327    MFCDM_PARENT_LOG("Failed to generate request (hr=%lx)!", hr);
   1328    aResolver(NS_ERROR_DOM_MEDIA_CDM_NO_SESSION_ERR);
   1329    // No need to call session's close() because this is not an unrecoverable
   1330    // error for CDM.
   1331    return IPC_OK();
   1332  }
   1333 
   1334  // TODO : now we assume all session ID is available after session is
   1335  // created, but this is not always true. Need to remove this assertion and
   1336  // handle cases where session Id is not available yet.
   1337  const auto& sessionId = session->SessionID();
   1338  MOZ_ASSERT(sessionId);
   1339  mSessions.emplace(*sessionId, std::move(session));
   1340  if (IsBeingProfiledOrLogEnabled()) {
   1341    nsPrintfCString msg("session for type '%s', sessionId=%s",
   1342                        SessionTypeToStr(aParams.sessionType()),
   1343                        NS_ConvertUTF16toUTF8(*sessionId).get());
   1344    MFCDM_PARENT_LOG("Created CDM %s", msg.get());
   1345    PROFILER_MARKER_TEXT(
   1346        "MFCDMParent::RecvCreateSessionAndGenerateRequest(created)",
   1347        MEDIA_PLAYBACK, {}, msg);
   1348  }
   1349  aResolver(*sessionId);
   1350  return IPC_OK();
   1351 }
   1352 
   1353 mozilla::ipc::IPCResult MFCDMParent::RecvLoadSession(
   1354    const KeySystemConfig::SessionType& aSessionType,
   1355    const nsString& aSessionId, LoadSessionResolver&& aResolver) {
   1356  ASSERT_CDM_ACCESS_READ_ONLY_ON_MANAGER_THREAD();
   1357  MOZ_ASSERT(mIsInited, "Must finish initialization first");
   1358 
   1359  nsresult rv = NS_OK;
   1360  auto* session = GetSession(aSessionId);
   1361  if (!session) {
   1362    aResolver(NS_ERROR_DOM_MEDIA_CDM_NO_SESSION_ERR);
   1363    return IPC_OK();
   1364  }
   1365 
   1366  if (IsBeingProfiledOrLogEnabled()) {
   1367    nsPrintfCString msg("Load Session %s",
   1368                        NS_ConvertUTF16toUTF8(aSessionId).get());
   1369    MFCDM_PARENT_LOG("%s", msg.get());
   1370    PROFILER_MARKER_TEXT("MFCDMParent::RecvLoadSession", MEDIA_PLAYBACK, {},
   1371                         msg);
   1372  }
   1373  MFCDM_REJECT_IF_FAILED(session->Load(aSessionId),
   1374                         NS_ERROR_DOM_MEDIA_CDM_SESSION_OPERATION_ERR);
   1375  aResolver(rv);
   1376  return IPC_OK();
   1377 }
   1378 
   1379 mozilla::ipc::IPCResult MFCDMParent::RecvUpdateSession(
   1380    const nsString& aSessionId, const CopyableTArray<uint8_t>& aResponse,
   1381    UpdateSessionResolver&& aResolver) {
   1382  ASSERT_CDM_ACCESS_READ_ONLY_ON_MANAGER_THREAD();
   1383  MOZ_ASSERT(mIsInited, "Must finish initialization first");
   1384  nsresult rv = NS_OK;
   1385  auto* session = GetSession(aSessionId);
   1386  if (!session) {
   1387    aResolver(NS_ERROR_DOM_MEDIA_CDM_NO_SESSION_ERR);
   1388    return IPC_OK();
   1389  }
   1390  if (IsBeingProfiledOrLogEnabled()) {
   1391    nsPrintfCString msg("Update Session %s",
   1392                        NS_ConvertUTF16toUTF8(aSessionId).get());
   1393    MFCDM_PARENT_LOG("%s", msg.get());
   1394    PROFILER_MARKER_TEXT("MFCDMParent::RecvUpdateSession", MEDIA_PLAYBACK, {},
   1395                         msg);
   1396  }
   1397  MFCDM_REJECT_IF_FAILED(session->Update(aResponse),
   1398                         NS_ERROR_DOM_MEDIA_CDM_SESSION_OPERATION_ERR);
   1399  aResolver(rv);
   1400  return IPC_OK();
   1401 }
   1402 
   1403 mozilla::ipc::IPCResult MFCDMParent::RecvCloseSession(
   1404    const nsString& aSessionId, UpdateSessionResolver&& aResolver) {
   1405  ASSERT_CDM_ACCESS_READ_ONLY_ON_MANAGER_THREAD();
   1406  MOZ_ASSERT(mIsInited, "Must finish initialization first");
   1407  nsresult rv = NS_OK;
   1408  auto* session = GetSession(aSessionId);
   1409  if (!session) {
   1410    aResolver(NS_ERROR_DOM_MEDIA_CDM_NO_SESSION_ERR);
   1411    return IPC_OK();
   1412  }
   1413  if (IsBeingProfiledOrLogEnabled()) {
   1414    nsPrintfCString msg("Close Session %s",
   1415                        NS_ConvertUTF16toUTF8(aSessionId).get());
   1416    MFCDM_PARENT_LOG("%s", msg.get());
   1417    PROFILER_MARKER_TEXT("MFCDMParent::RecvCloseSession", MEDIA_PLAYBACK, {},
   1418                         msg);
   1419  }
   1420  MFCDM_REJECT_IF_FAILED(
   1421      session->Close(dom::MediaKeySessionClosedReason::Closed_by_application),
   1422      NS_ERROR_DOM_MEDIA_CDM_SESSION_OPERATION_ERR);
   1423  aResolver(rv);
   1424  return IPC_OK();
   1425 }
   1426 
   1427 mozilla::ipc::IPCResult MFCDMParent::RecvRemoveSession(
   1428    const nsString& aSessionId, UpdateSessionResolver&& aResolver) {
   1429  ASSERT_CDM_ACCESS_READ_ONLY_ON_MANAGER_THREAD();
   1430  MOZ_ASSERT(mIsInited, "Must finish initialization first");
   1431  nsresult rv = NS_OK;
   1432  auto* session = GetSession(aSessionId);
   1433  if (!session) {
   1434    aResolver(NS_ERROR_DOM_MEDIA_CDM_NO_SESSION_ERR);
   1435    return IPC_OK();
   1436  }
   1437  if (IsBeingProfiledOrLogEnabled()) {
   1438    nsPrintfCString msg("Remove Session %s",
   1439                        NS_ConvertUTF16toUTF8(aSessionId).get());
   1440    MFCDM_PARENT_LOG("%s", msg.get());
   1441    PROFILER_MARKER_TEXT("MFCDMParent::RecvRemoveSession", MEDIA_PLAYBACK, {},
   1442                         msg);
   1443  }
   1444  MFCDM_REJECT_IF_FAILED(session->Remove(),
   1445                         NS_ERROR_DOM_MEDIA_CDM_SESSION_OPERATION_ERR);
   1446  aResolver(rv);
   1447  return IPC_OK();
   1448 }
   1449 
   1450 mozilla::ipc::IPCResult MFCDMParent::RecvSetServerCertificate(
   1451    const CopyableTArray<uint8_t>& aCertificate,
   1452    UpdateSessionResolver&& aResolver) {
   1453  ASSERT_CDM_ACCESS_READ_ONLY_ON_MANAGER_THREAD();
   1454  MOZ_ASSERT(mIsInited, "Must finish initialization first");
   1455  nsresult rv = NS_OK;
   1456  MFCDM_PARENT_LOG("Set server certificate");
   1457  PROFILER_MARKER_UNTYPED("MFCDMParent::RecvSetServerCertificate",
   1458                          MEDIA_PLAYBACK);
   1459  MFCDM_REJECT_IF(!mCDM, NS_ERROR_DOM_MEDIA_CDM_ERR);
   1460  MFCDM_REJECT_IF_FAILED(mCDM->SetServerCertificate(
   1461                             static_cast<const BYTE*>(aCertificate.Elements()),
   1462                             aCertificate.Length()),
   1463                         NS_ERROR_DOM_MEDIA_CDM_ERR);
   1464  aResolver(rv);
   1465  return IPC_OK();
   1466 }
   1467 
   1468 mozilla::ipc::IPCResult MFCDMParent::RecvGetStatusForPolicy(
   1469    const dom::HDCPVersion& aMinHdcpVersion,
   1470    GetStatusForPolicyResolver&& aResolver) {
   1471  ASSERT_CDM_ACCESS_READ_ONLY_ON_MANAGER_THREAD();
   1472  nsCOMPtr<nsISerialEventTarget> backgroundTaskQueue;
   1473  if (NS_FAILED(NS_CreateBackgroundTaskQueue(
   1474          __func__, getter_AddRefs(backgroundTaskQueue)))) {
   1475    MFCDM_PARENT_LOG("Failed to create a background task queue");
   1476    aResolver(NS_ERROR_FAILURE);
   1477    return IPC_OK();
   1478  }
   1479  using HDCPPromise = MozPromise<nsresult, nsresult, /* IsExclusive = */ true>;
   1480  RefPtr<HDCPPromise::Private> p = new HDCPPromise::Private(__func__);
   1481  (void)backgroundTaskQueue->Dispatch(NS_NewRunnableFunction(
   1482      __func__, [self = RefPtr<MFCDMParent>(this), this, aMinHdcpVersion, p] {
   1483        auto rv =
   1484            IsHDCPVersionSupported(mKeySystem, aMinHdcpVersion, mManagerThread);
   1485        if (IsBeingProfiledOrLogEnabled()) {
   1486          nsPrintfCString msg("HDCP version=%u, support=%s",
   1487                              static_cast<uint8_t>(aMinHdcpVersion),
   1488                              rv == NS_OK ? "true" : "false");
   1489          MFCDM_PARENT_LOG("%s", msg.get());
   1490          PROFILER_MARKER_TEXT("MFCDMParent::RecvGetStatusForPolicy",
   1491                               MEDIA_PLAYBACK, {}, msg);
   1492        }
   1493        p->Resolve(rv, __func__);
   1494      }));
   1495  p->Then(mManagerThread, __func__,
   1496          [resolver = aResolver](HDCPPromise::ResolveOrRejectValue&& aRv) {
   1497            MOZ_ASSERT(aRv.IsResolve());
   1498            resolver(aRv.ResolveValue());
   1499          });
   1500  return IPC_OK();
   1501 }
   1502 
   1503 void MFCDMParent::ConnectSessionEvents(MFCDMSession* aSession) {
   1504  // TODO : clear session's event source when the session gets removed.
   1505  mKeyMessageEvents.Forward(aSession->KeyMessageEvent());
   1506  mKeyChangeEvents.Forward(aSession->KeyChangeEvent());
   1507  mExpirationEvents.Forward(aSession->ExpirationEvent());
   1508  mClosedEvents.Forward(aSession->ClosedEvent());
   1509 }
   1510 
   1511 MFCDMSession* MFCDMParent::GetSession(const nsString& aSessionId) {
   1512  ASSERT_CDM_ACCESS_READ_ONLY_ON_MANAGER_THREAD();
   1513  auto iter = mSessions.find(aSessionId);
   1514  if (iter == mSessions.end()) {
   1515    return nullptr;
   1516  }
   1517  return iter->second.get();
   1518 }
   1519 
   1520 MFCDMProxy* MFCDMParent::GetMFCDMProxy() {
   1521  MutexAutoLock lock(Mutex());
   1522  mCDMAccessLock.NoteLockHeld();
   1523  return mCDMProxy;
   1524 }
   1525 
   1526 /* static */
   1527 void MFCDMService::GetAllKeySystemsCapabilities(dom::Promise* aPromise) {
   1528  MOZ_ASSERT(XRE_IsParentProcess());
   1529  const static auto kSandboxKind = ipc::SandboxingKind::MF_MEDIA_ENGINE_CDM;
   1530  LaunchMFCDMProcessIfNeeded(kSandboxKind)
   1531      ->Then(
   1532          GetMainThreadSerialEventTarget(), __func__,
   1533          [promise = RefPtr(aPromise)]() {
   1534            RefPtr<ipc::UtilityMediaServiceChild> umsc =
   1535                ipc::UtilityMediaServiceChild::GetSingleton(kSandboxKind);
   1536            if (NS_WARN_IF(!umsc)) {
   1537              promise->MaybeReject(NS_ERROR_FAILURE);
   1538              return;
   1539            }
   1540            umsc->GetKeySystemCapabilities(promise);
   1541          },
   1542          [promise = RefPtr(aPromise)](nsresult aError) {
   1543            promise->MaybeReject(NS_ERROR_FAILURE);
   1544          });
   1545 }
   1546 
   1547 /* static */
   1548 RefPtr<GenericNonExclusivePromise> MFCDMService::LaunchMFCDMProcessIfNeeded(
   1549    ipc::SandboxingKind aSandbox) {
   1550  MOZ_ASSERT(XRE_IsParentProcess());
   1551  MOZ_ASSERT(aSandbox == ipc::SandboxingKind::MF_MEDIA_ENGINE_CDM);
   1552  RefPtr<ipc::UtilityProcessManager> utilityProc =
   1553      ipc::UtilityProcessManager::GetSingleton();
   1554  if (NS_WARN_IF(!utilityProc)) {
   1555    NS_WARNING("Failed to get UtilityProcessManager");
   1556    return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE,
   1557                                                       __func__);
   1558  }
   1559 
   1560  // Check if the MFCDM process exists or not. If not, launch it.
   1561  if (utilityProc->Process(aSandbox)) {
   1562    return GenericNonExclusivePromise::CreateAndResolve(true, __func__);
   1563  }
   1564 
   1565  RefPtr<ipc::UtilityMediaServiceChild> umsc =
   1566      ipc::UtilityMediaServiceChild::GetSingleton(aSandbox);
   1567  if (NS_WARN_IF(!umsc)) {
   1568    NS_WARNING("Failed to get UtilityMediaServiceChild");
   1569    return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE,
   1570                                                       __func__);
   1571  }
   1572  return utilityProc->StartUtility(umsc, aSandbox)
   1573      ->Then(
   1574          GetMainThreadSerialEventTarget(), __func__,
   1575          [umsc, utilityProc, aSandbox]() {
   1576            RefPtr<ipc::UtilityProcessParent> parent =
   1577                utilityProc->GetProcessParent(aSandbox);
   1578            if (!parent) {
   1579              NS_WARNING("UtilityMediaServiceParent lost in the middle");
   1580              return GenericNonExclusivePromise::CreateAndReject(
   1581                  NS_ERROR_FAILURE, __func__);
   1582            }
   1583 
   1584            if (!umsc->CanSend()) {
   1585              NS_WARNING("UtilityMediaServiceChild lost in the middle");
   1586              return GenericNonExclusivePromise::CreateAndReject(
   1587                  NS_ERROR_FAILURE, __func__);
   1588            }
   1589            return GenericNonExclusivePromise::CreateAndResolve(true, __func__);
   1590          },
   1591          [](ipc::LaunchError const& aError) {
   1592            NS_WARNING("Failed to start the MFCDM process!");
   1593            return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE,
   1594                                                               __func__);
   1595          });
   1596 }
   1597 
   1598 /* static */
   1599 void MFCDMService::UpdateWidevineL1Path(nsIFile* aFile) {
   1600  RefPtr<ipc::UtilityProcessManager> utilityProc =
   1601      ipc::UtilityProcessManager::GetSingleton();
   1602  if (NS_WARN_IF(!utilityProc)) {
   1603    NS_WARNING("Failed to get UtilityProcessManager");
   1604    return;
   1605  }
   1606 
   1607  // If the MFCDM process hasn't been created yet, then we will set the path
   1608  // when creating the process later.
   1609  const auto sandboxKind = ipc::SandboxingKind::MF_MEDIA_ENGINE_CDM;
   1610  if (!utilityProc->Process(sandboxKind)) {
   1611    return;
   1612  }
   1613 
   1614  // The MFCDM process has been started, we need to update its L1 path and set
   1615  // the permission for LPAC.
   1616  nsString widevineL1Path;
   1617  MOZ_ASSERT(aFile);
   1618  if (NS_WARN_IF(NS_FAILED(aFile->GetTarget(widevineL1Path)))) {
   1619    NS_WARNING("MFCDMService::UpdateWidevineL1Path, Failed to get L1 path!");
   1620    return;
   1621  }
   1622 
   1623  RefPtr<ipc::UtilityMediaServiceChild> umsc =
   1624      ipc::UtilityMediaServiceChild::GetSingleton(sandboxKind);
   1625  if (NS_WARN_IF(!umsc)) {
   1626    NS_WARNING("Failed to get UtilityMediaServiceChild");
   1627    return;
   1628  }
   1629  (void)umsc->SendUpdateWidevineL1Path(widevineL1Path);
   1630 #ifdef MOZ_WMF_CDM_LPAC_SANDBOX
   1631  SandboxBroker::EnsureLpacPermsissionsOnDir(widevineL1Path);
   1632 #endif
   1633 }
   1634 
   1635 #undef MFCDM_REJECT_IF_FAILED
   1636 #undef MFCDM_REJECT_IF
   1637 #undef MFCDM_RETURN_IF_FAILED
   1638 #undef MFCDM_RETURN_BOOL_IF_FAILED
   1639 #undef MFCDM_PARENT_SLOG
   1640 #undef MFCDM_PARENT_LOG
   1641 #undef ASSERT_CDM_ACCESS_READ_ONLY_ON_MANAGER_THREAD
   1642 #undef ASSERT_CDM_ACCESS_ON_MANAGER_THREAD
   1643 
   1644 }  // namespace mozilla