Beispiel #1
0
void
CDMProxy::gmp_InitDone(GMPDecryptorProxy* aCDM, nsAutoPtr<InitData>&& aData)
{
  EME_LOG("CDMProxy::gmp_InitDone");
  if (mShutdownCalled) {
    if (aCDM) {
      aCDM->Close();
    }
    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                  NS_LITERAL_CSTRING("CDMProxy was shut down before init could complete"));
    return;
  }
  if (!aCDM) {
    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                  NS_LITERAL_CSTRING("GetGMPDecryptor failed to return a CDM"));
    return;
  }

  mCDM = aCDM;
  mCallback = new CDMCallbackProxy(this);
  mCDM->Init(mCallback);
  nsCOMPtr<nsIRunnable> task(
    NewRunnableMethod<uint32_t>(this,
                                &CDMProxy::OnCDMCreated,
                                aData->mPromiseId));
  NS_DispatchToMainThread(task);
}
Beispiel #2
0
void
CDMProxy::Init(PromiseId aPromiseId)
{
  MOZ_ASSERT(NS_IsMainThread());

  nsresult rv = mKeys->GetOrigin(mOrigin);
  if (NS_FAILED(rv)) {
    RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
    return;
  }
  EME_LOG("Creating CDMProxy for origin='%s'",
          NS_ConvertUTF16toUTF8(GetOrigin()).get());

  if (!mGMPThread) {
    nsCOMPtr<mozIGeckoMediaPluginService> mps =
      do_GetService("@mozilla.org/gecko-media-plugin-service;1");
    if (!mps) {
      RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
      return;
    }
    mps->GetThread(getter_AddRefs(mGMPThread));
    if (!mGMPThread) {
      RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
      return;
    }
  }

  nsRefPtr<nsIRunnable> task(NS_NewRunnableMethodWithArg<uint32_t>(this, &CDMProxy::gmp_Init, aPromiseId));
  mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
}
Beispiel #3
0
void
CDMProxy::gmp_Init(nsAutoPtr<InitData>&& aData)
{
  MOZ_ASSERT(IsOnGMPThread());

  nsCOMPtr<mozIGeckoMediaPluginService> mps =
    do_GetService("@mozilla.org/gecko-media-plugin-service;1");
  if (!mps) {
    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                  NS_LITERAL_CSTRING("Couldn't get MediaPluginService in CDMProxy::gmp_Init"));
    return;
  }

  // Make a copy before we transfer ownership of aData to the
  // gmp_InitGetGMPDecryptorCallback.
  InitData data(*aData);
  UniquePtr<GetNodeIdCallback> callback(
    new gmp_InitGetGMPDecryptorCallback(this, Move(aData)));
  nsresult rv = mps->GetNodeId(data.mOrigin,
                               data.mTopLevelOrigin,
                               data.mGMPName,
                               data.mInPrivateBrowsing,
                               Move(callback));
  if (NS_FAILED(rv)) {
    RejectPromise(data.mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                  NS_LITERAL_CSTRING("Call to GetNodeId() failed early"));
  }
}
void
GMPCDMProxy::gmp_InitDone(GMPDecryptorProxy* aCDM, UniquePtr<InitData>&& aData)
{
  EME_LOG("GMPCDMProxy::gmp_InitDone");
  if (mShutdownCalled) {
    if (aCDM) {
      aCDM->Close();
    }
    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                  NS_LITERAL_CSTRING("GMPCDMProxy was shut down before init could complete"));
    return;
  }
  if (!aCDM) {
    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                  NS_LITERAL_CSTRING("GetGMPDecryptor failed to return a CDM"));
    return;
  }

  mCDM = aCDM;
  mCallback.reset(new GMPCDMCallbackProxy(this, mMainThread));
  mCDM->Init(mCallback.get(),
             mDistinctiveIdentifierRequired,
             mPersistentStateRequired);

  // Await the OnSetDecryptorId callback.
  mCreatePromiseId = aData->mPromiseId;
}
Beispiel #5
0
void
CDMProxy::Init(PromiseId aPromiseId,
               const nsAString& aOrigin,
               const nsAString& aTopLevelOrigin,
               const nsAString& aGMPName,
               bool aInPrivateBrowsing)
{
  MOZ_ASSERT(NS_IsMainThread());
  NS_ENSURE_TRUE_VOID(!mKeys.IsNull());

  EME_LOG("CDMProxy::Init (%s, %s) %s",
          NS_ConvertUTF16toUTF8(aOrigin).get(),
          NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
          (aInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"));

  nsCString pluginVersion;
  if (!mGMPThread) {
    nsCOMPtr<mozIGeckoMediaPluginService> mps =
      do_GetService("@mozilla.org/gecko-media-plugin-service;1");
    if (!mps) {
      RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                    NS_LITERAL_CSTRING("Couldn't get MediaPluginService in CDMProxy::Init"));
      return;
    }
    mps->GetThread(getter_AddRefs(mGMPThread));
    if (!mGMPThread) {
      RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                    NS_LITERAL_CSTRING("Couldn't get GMP thread CDMProxy::Init"));
      return;
    }
  }

  if (aGMPName.IsEmpty()) {
    RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
      nsPrintfCString("Unknown GMP for keysystem '%s'", NS_ConvertUTF16toUTF8(mKeySystem).get()));
    return;
  }

  nsAutoPtr<InitData> data(new InitData());
  data->mPromiseId = aPromiseId;
  data->mOrigin = aOrigin;
  data->mTopLevelOrigin = aTopLevelOrigin;
  data->mGMPName = aGMPName;
  data->mInPrivateBrowsing = aInPrivateBrowsing;
  nsCOMPtr<nsIRunnable> task(
    NewRunnableMethod<nsAutoPtr<InitData>>(this,
                                           &CDMProxy::gmp_Init,
                                           Move(data)));
  mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
}
void
ChromiumCDMProxy::UpdateSession(const nsAString& aSessionId,
                                PromiseId aPromiseId,
                                nsTArray<uint8_t>& aResponse)
{
  MOZ_ASSERT(NS_IsMainThread());
  EME_LOG("ChromiumCDMProxy::UpdateSession(sid='%s', pid=%u) responseLen=%zu",
          NS_ConvertUTF16toUTF8(aSessionId).get(),
          aPromiseId,
          aResponse.Length());

  RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
  if (!cdm) {
    RejectPromise(aPromiseId,
                  NS_ERROR_DOM_INVALID_STATE_ERR,
                  NS_LITERAL_CSTRING("Null CDM in UpdateSession"));
    return;
  }
  mGMPThread->Dispatch(
    NewRunnableMethod<nsCString, uint32_t, nsTArray<uint8_t>>(
      cdm,
      &gmp::ChromiumCDMParent::UpdateSession,
      NS_ConvertUTF16toUTF8(aSessionId),
      aPromiseId,
      Move(aResponse)));
}
Beispiel #7
0
void
CDMProxy::OnRejectPromise(uint32_t aPromiseId,
                          nsresult aDOMException,
                          const nsCString& aMsg)
{
  MOZ_ASSERT(NS_IsMainThread());
  RejectPromise(aPromiseId, aDOMException, aMsg);
}
void
GMPCDMProxy::gmp_InitGetGMPDecryptor(nsresult aResult,
                                     const nsACString& aNodeId,
                                     UniquePtr<InitData>&& aData)
{
  uint32_t promiseID = aData->mPromiseId;
  if (NS_FAILED(aResult)) {
    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR,
                  NS_LITERAL_CSTRING("GetNodeId() called back, but with a failure result"));
    return;
  }

  mNodeId = aNodeId;
  MOZ_ASSERT(!GetNodeId().IsEmpty());

  nsCOMPtr<mozIGeckoMediaPluginService> mps =
    do_GetService("@mozilla.org/gecko-media-plugin-service;1");
  if (!mps) {
    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR,
                  NS_LITERAL_CSTRING("Couldn't get MediaPluginService in GMPCDMProxy::gmp_InitGetGMPDecryptor"));
    return;
  }

  EME_LOG("GMPCDMProxy::gmp_Init (%s, %s) NodeId=%s",
          NS_ConvertUTF16toUTF8(aData->mOrigin).get(),
          NS_ConvertUTF16toUTF8(aData->mTopLevelOrigin).get(),
          GetNodeId().get());

  nsTArray<nsCString> tags;
  tags.AppendElement(NS_ConvertUTF16toUTF8(mKeySystem));

  // Note: must capture helper refptr here, before the Move()
  // when we create the GetGMPDecryptorCallback below.
  RefPtr<GMPCrashHelper> crashHelper = Move(aData->mCrashHelper);
  UniquePtr<GetGMPDecryptorCallback> callback(new gmp_InitDoneCallback(this,
                                                                       Move(aData)));
  nsresult rv = mps->GetGMPDecryptor(crashHelper,
                                     &tags,
                                     GetNodeId(),
                                     Move(callback));
  if (NS_FAILED(rv)) {
    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR,
                  NS_LITERAL_CSTRING("Call to GetGMPDecryptor() failed early"));
  }
}
Beispiel #9
0
void
CDMProxy::gmp_SetServerCertificate(nsAutoPtr<SetServerCertificateData> aData)
{
  MOZ_ASSERT(IsOnGMPThread());
  if (!mCDM) {
    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
    return;
  }
  mCDM->SetServerCertificate(aData->mPromiseId, aData->mCert);
}
Beispiel #10
0
void
CDMProxy::gmp_RemoveSession(nsAutoPtr<SessionOpData> aData)
{
  MOZ_ASSERT(IsOnGMPThread());
  if (!mCDM) {
    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
    return;
  }
  mCDM->RemoveSession(aData->mPromiseId, aData->mSessionId);
}
Beispiel #11
0
void
GMPCDMProxy::gmp_RemoveSession(UniquePtr<SessionOpData>&& aData)
{
  MOZ_ASSERT(IsOnOwnerThread());
  if (!mCDM) {
    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                  NS_LITERAL_CSTRING("Null CDM in gmp_RemoveSession"));
    return;
  }
  mCDM->RemoveSession(aData->mPromiseId, aData->mSessionId);
}
Beispiel #12
0
void
GMPCDMProxy::gmp_SetServerCertificate(UniquePtr<SetServerCertificateData>&& aData)
{
  MOZ_ASSERT(IsOnOwnerThread());
  if (!mCDM) {
    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                  NS_LITERAL_CSTRING("Null CDM in gmp_SetServerCertificate"));
    return;
  }
  mCDM->SetServerCertificate(aData->mPromiseId, aData->mCert);
}
Beispiel #13
0
void
CDMProxy::gmp_CloseSession(nsAutoPtr<SessionOpData> aData)
{
  MOZ_ASSERT(IsOnGMPThread());
  if (!mCDM) {
    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                  NS_LITERAL_CSTRING("Null CDM in gmp_CloseSession"));
    return;
  }
  mCDM->CloseSession(aData->mPromiseId, aData->mSessionId);
}
Beispiel #14
0
void
CDMProxy::gmp_UpdateSession(nsAutoPtr<UpdateSessionData> aData)
{
  MOZ_ASSERT(IsOnGMPThread());
  if (!mCDM) {
    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
    return;
  }
  mCDM->UpdateSession(aData->mPromiseId,
                      aData->mSessionId,
                      aData->mResponse);
}
Beispiel #15
0
void
CDMProxy::gmp_InitGetGMPDecryptor(nsresult aResult,
                                  const nsACString& aNodeId,
                                  nsAutoPtr<InitData>&& aData)
{
  uint32_t promiseID = aData->mPromiseId;
  if (NS_FAILED(aResult)) {
    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR,
                  NS_LITERAL_CSTRING("GetNodeId() called back, but with a failure result"));
    return;
  }

  mNodeId = aNodeId;
  MOZ_ASSERT(!GetNodeId().IsEmpty());

  nsCOMPtr<mozIGeckoMediaPluginService> mps =
    do_GetService("@mozilla.org/gecko-media-plugin-service;1");
  if (!mps) {
    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR,
                  NS_LITERAL_CSTRING("Couldn't get MediaPluginService in CDMProxy::gmp_InitGetGMPDecryptor"));
    return;
  }

  EME_LOG("CDMProxy::gmp_Init (%s, %s) %s NodeId=%s",
          NS_ConvertUTF16toUTF8(aData->mOrigin).get(),
          NS_ConvertUTF16toUTF8(aData->mTopLevelOrigin).get(),
          (aData->mInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"),
          GetNodeId().get());

  nsTArray<nsCString> tags;
  tags.AppendElement(NS_ConvertUTF16toUTF8(mKeySystem));

  UniquePtr<GetGMPDecryptorCallback> callback(new gmp_InitDoneCallback(this,
                                                                       Move(aData)));
  nsresult rv = mps->GetGMPDecryptor(&tags, GetNodeId(), Move(callback));
  if (NS_FAILED(rv)) {
    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR,
                  NS_LITERAL_CSTRING("Call to GetGMPDecryptor() failed early"));
  }
}
void
CDMProxy::Init(PromiseId aPromiseId)
{
  MOZ_ASSERT(NS_IsMainThread());
  if (!mGMPThread) {
    nsCOMPtr<mozIGeckoMediaPluginService> mps =
      do_GetService("@mozilla.org/gecko-media-plugin-service;1");
    if (!mps) {
      RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
      return;
    }
    mps->GetThread(getter_AddRefs(mGMPThread));
    if (!mGMPThread) {
      RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
      return;
    }
  }

  // TODO: Dispatch task to GMPThread to initialize CDM via IPC.

  mKeys->OnCDMCreated(aPromiseId);
}
Beispiel #17
0
void
CDMProxy::gmp_CreateSession(nsAutoPtr<CreateSessionData> aData)
{
  MOZ_ASSERT(IsOnGMPThread());
  if (!mCDM) {
    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
    return;
  }
  mCDM->CreateSession(aData->mPromiseId,
                      aData->mInitDataType,
                      aData->mInitData,
                      ToGMPSessionType(aData->mSessionType));
}
Beispiel #18
0
void
CDMProxy::Init(PromiseId aPromiseId,
               const nsAString& aOrigin,
               const nsAString& aTopLevelOrigin,
               bool aInPrivateBrowsing)
{
  MOZ_ASSERT(NS_IsMainThread());
  NS_ENSURE_TRUE_VOID(!mKeys.IsNull());

  EME_LOG("CDMProxy::Init (%s, %s) %s",
          NS_ConvertUTF16toUTF8(aOrigin).get(),
          NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
          (aInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"));

  if (!mGMPThread) {
    nsCOMPtr<mozIGeckoMediaPluginService> mps =
      do_GetService("@mozilla.org/gecko-media-plugin-service;1");
    if (!mps) {
      RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
      return;
    }
    mps->GetThread(getter_AddRefs(mGMPThread));
    if (!mGMPThread) {
      RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
      return;
    }
  }
  nsAutoPtr<InitData> data(new InitData());
  data->mPromiseId = aPromiseId;
  data->mOrigin = aOrigin;
  data->mTopLevelOrigin = aTopLevelOrigin;
  data->mInPrivateBrowsing = aInPrivateBrowsing;
  nsCOMPtr<nsIRunnable> task(
    NS_NewRunnableMethodWithArg<nsAutoPtr<InitData>>(this,
                                                     &CDMProxy::gmp_Init,
                                                     Move(data)));
  mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
}
Beispiel #19
0
// ECMA262 25.4.1.4 Promise Resolve Functions 
static ejsval
resolve(ejsval env, ejsval _this, uint32_t argc, ejsval *args)
{
    ejsval resolution = _ejs_undefined;
    if (argc > 0) resolution = args[0];

    // 1. Assert: F has a [[Promise]] internal slot whose value is an Object. 
    // 2. Let promise be the value of F's [[Promise]] internal slot. 
    ejsval promise = _ejs_closureenv_get_slot(env, 1);

    // 3. Let alreadyResolved by be the value of F's [[AlreadyResolved]] internal slot. 
    ejsval alreadyResolved = _ejs_closureenv_get_slot(env, 0);
    // 4. If alreadyResolved. [[value]] is true, then return undefined. 
    if (EJSVAL_TO_BOOLEAN(alreadyResolved)) {
        return _ejs_undefined;
    }
    // 5. Set alreadyResolved.[[value]] to true. 
    *_ejs_closureenv_get_slot_ref(env, 0) = _ejs_true;

    // 6. If SameValue(resolution, promise) is true, then 
    if (SameValue(resolution, promise)) {
        //    a. Let selfResolutionError be a newly-created TypeError object. 
        ejsval selfResolutionError = _ejs_nativeerror_new_utf8(EJS_TYPE_ERROR, ""); // XXX
        //    b. Return RejectPromise(promise, selfResolutionError). 
        return RejectPromise(promise, selfResolutionError);
    }
    // 7. If Type(resolution) is not Object, then 
    if (!EJSVAL_IS_OBJECT(resolution)) {
        //    a. Return FulfillPromise(promise, resolution). 
        return FulfillPromise(promise, resolution);
    }
    // 8. Let then be Get(resolution, "then"). 
    ejsval then = Get(resolution, _ejs_atom_then);
    // 9. If then is an abrupt completion, then 
    //    a. Return RejectPromise(promise, then.[[value]]). 
    
    // XXX

    // 10. Let then be then.[[value]]. 
    // 11. If IsCallable(then) is false, then 
    if (!EJSVAL_IS_CALLABLE(then)) {
        //     a. Return FulfillPromise(promise, resolution). 
        return FulfillPromise(promise, resolution);
    }
    // 12. Perform EnqueueTask ("PromiseTasks", PromiseResolveThenableTask, (promise, resolution, then))
    EnqueuePromiseResolveThenableTask (promise, resolution, then);

    // 13. Return undefined. 
    return _ejs_undefined;
}
Beispiel #20
0
void
GMPCDMProxy::Terminated()
{
  MOZ_ASSERT(NS_IsMainThread());
  NS_WARNING("CDM terminated");
  if (mCreatePromiseId) {
    RejectPromise(mCreatePromiseId,
                  NS_ERROR_DOM_MEDIA_FATAL_ERR,
                  NS_LITERAL_CSTRING("Crashed waiting for CDM to initialize"));
    mCreatePromiseId = 0;
  }
  if (!mKeys.IsNull()) {
    mKeys->Terminated();
  }
}
Beispiel #21
0
void
CDMProxy::gmp_Init(uint32_t aPromiseId)
{
  MOZ_ASSERT(IsOnGMPThread());

  nsCOMPtr<mozIGeckoMediaPluginService> mps =
    do_GetService("@mozilla.org/gecko-media-plugin-service;1");
  if (!mps) {
    RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
    return;
  }

  nsTArray<nsCString> tags;
  tags.AppendElement(NS_ConvertUTF16toUTF8(mKeySystem));
  nsresult rv = mps->GetGMPDecryptor(&tags, GetOrigin(), &mCDM);
  if (NS_FAILED(rv) || !mCDM) {
    RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
  } else {
    mCallback = new CDMCallbackProxy(this);
    mCDM->Init(mCallback);
    nsRefPtr<nsIRunnable> task(NS_NewRunnableMethodWithArg<uint32_t>(this, &CDMProxy::OnCDMCreated, aPromiseId));
    NS_DispatchToMainThread(task);
  }
}
Beispiel #22
0
void
GMPCDMProxy::gmp_CreateSession(UniquePtr<CreateSessionData>&& aData)
{
  MOZ_ASSERT(IsOnOwnerThread());
  if (!mCDM) {
    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                  NS_LITERAL_CSTRING("Null CDM in gmp_CreateSession"));
    return;
  }
  mCDM->CreateSession(aData->mCreateSessionToken,
                      aData->mPromiseId,
                      aData->mInitDataType,
                      aData->mInitData,
                      ToGMPSessionType(aData->mSessionType));
}
Beispiel #23
0
// ECMA262 25.4.1.3.1 Promise Reject Functions 
static ejsval
reject(ejsval env, ejsval _this, uint32_t argc, ejsval *args)
{
    ejsval reason = _ejs_undefined;
    if (argc > 0) reason = args[0];

    // 1. Assert: F has a [[Promise]] internal slot whose value is an Object. 
    // 2. Let promise be the value of F's [[Promise]] internal slot. 
    ejsval promise = _ejs_closureenv_get_slot(env, 1);
    // 3. Let alreadyResolved by be the value of F's [[AlreadyResolved]] internal slot. 
    ejsval alreadyResolved = _ejs_closureenv_get_slot(env, 0);
    // 4. If alreadyResolved.[[value]] is true, then return undefined. 
    if (EJSVAL_TO_BOOLEAN(alreadyResolved))
        return _ejs_undefined;

    // 5. Set alreadyResolved.[[value]] to true. 
    *_ejs_closureenv_get_slot_ref(env, 0) = _ejs_true;

    // 6. Return RejectPromise(promise, reason). 
    return RejectPromise(promise, reason);
}
Beispiel #24
0
void
CDMProxy::gmp_InitDone(GMPDecryptorProxy* aCDM, nsAutoPtr<InitData>&& aData)
{
  EME_LOG("CDMProxy::gmp_InitDone");
  if (!aCDM || mShutdownCalled) {
    if (aCDM) {
      aCDM->Close();
    }
    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
    return;
  }

  mCDM = aCDM;
  mCallback = new CDMCallbackProxy(this);
  mCDM->Init(mCallback);
  nsCOMPtr<nsIRunnable> task(
    NS_NewRunnableMethodWithArg<uint32_t>(this,
                                          &CDMProxy::OnCDMCreated,
                                          aData->mPromiseId));
  NS_DispatchToMainThread(task);
}
void
ChromiumCDMProxy::CreateSession(uint32_t aCreateSessionToken,
                                dom::MediaKeySessionType aSessionType,
                                PromiseId aPromiseId,
                                const nsAString& aInitDataType,
                                nsTArray<uint8_t>& aInitData)
{
  MOZ_ASSERT(NS_IsMainThread());
  EME_LOG("ChromiumCDMProxy::CreateSession(token=%u, type=%d, pid=%u) "
          "initDataLen=%zu",
          aCreateSessionToken,
          (int)aSessionType,
          aPromiseId,
          aInitData.Length());

  uint32_t sessionType = ToCDMSessionType(aSessionType);
  uint32_t initDataType = ToCDMInitDataType(aInitDataType);

  RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
  if (!cdm) {
    RejectPromise(aPromiseId,
                  NS_ERROR_DOM_INVALID_STATE_ERR,
                  NS_LITERAL_CSTRING("Null CDM in CreateSession"));
    return;
  }

  mGMPThread->Dispatch(
    NewRunnableMethod<uint32_t,
                      uint32_t,
                      uint32_t,
                      uint32_t,
                      nsTArray<uint8_t>>(cdm,
                                         &gmp::ChromiumCDMParent::CreateSession,
                                         aCreateSessionToken,
                                         sessionType,
                                         initDataType,
                                         aPromiseId,
                                         Move(aInitData)));
}
void
ChromiumCDMProxy::LoadSession(PromiseId aPromiseId,
                              dom::MediaKeySessionType aSessionType,
                              const nsAString& aSessionId)
{
  MOZ_ASSERT(NS_IsMainThread());

  RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
  if (!cdm) {
    RejectPromise(aPromiseId,
                  NS_ERROR_DOM_INVALID_STATE_ERR,
                  NS_LITERAL_CSTRING("Null CDM in LoadSession"));
    return;
  }

  mGMPThread->Dispatch(NewRunnableMethod<uint32_t, uint32_t, nsString>(
    cdm,
    &gmp::ChromiumCDMParent::LoadSession,
    aPromiseId,
    ToCDMSessionType(aSessionType),
    aSessionId));
}
void
ChromiumCDMProxy::RemoveSession(const nsAString& aSessionId,
                                PromiseId aPromiseId)
{
  MOZ_ASSERT(NS_IsMainThread());
  EME_LOG("ChromiumCDMProxy::RemoveSession(sid='%s', pid=%u)",
          NS_ConvertUTF16toUTF8(aSessionId).get(),
          aPromiseId);

  RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
  if (!cdm) {
    RejectPromise(aPromiseId,
                  NS_ERROR_DOM_INVALID_STATE_ERR,
                  NS_LITERAL_CSTRING("Null CDM in RemoveSession"));
    return;
  }
  mGMPThread->Dispatch(NewRunnableMethod<nsCString, uint32_t>(
    cdm,
    &gmp::ChromiumCDMParent::RemoveSession,
    NS_ConvertUTF16toUTF8(aSessionId),
    aPromiseId));
}
void
ChromiumCDMProxy::SetServerCertificate(PromiseId aPromiseId,
                                       nsTArray<uint8_t>& aCert)
{
  MOZ_ASSERT(NS_IsMainThread());
  EME_LOG("ChromiumCDMProxy::SetServerCertificate(pid=%u) certLen=%zu",
          aPromiseId,
          aCert.Length());

  RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
  if (!cdm) {
    RejectPromise(aPromiseId,
                  NS_ERROR_DOM_INVALID_STATE_ERR,
                  NS_LITERAL_CSTRING("Null CDM in SetServerCertificate"));
    return;
  }

  mGMPThread->Dispatch(NewRunnableMethod<uint32_t, nsTArray<uint8_t>>(
    cdm,
    &gmp::ChromiumCDMParent::SetServerCertificate,
    aPromiseId,
    Move(aCert)));
}
void
ChromiumCDMProxy::Init(PromiseId aPromiseId,
                       const nsAString& aOrigin,
                       const nsAString& aTopLevelOrigin,
                       const nsAString& aGMPName)
{
  MOZ_ASSERT(NS_IsMainThread());
  NS_ENSURE_TRUE_VOID(!mKeys.IsNull());

  EME_LOG(
    "ChromiumCDMProxy::Init (pid=%u, origin=%s, topLevelOrigin=%s, gmp=%s)",
    aPromiseId,
    NS_ConvertUTF16toUTF8(aOrigin).get(),
    NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
    NS_ConvertUTF16toUTF8(aGMPName).get());

  if (!mGMPThread) {
    RejectPromise(
      aPromiseId,
      NS_ERROR_DOM_INVALID_STATE_ERR,
      NS_LITERAL_CSTRING("Couldn't get GMP thread ChromiumCDMProxy::Init"));
    return;
  }

  if (aGMPName.IsEmpty()) {
    RejectPromise(aPromiseId,
                  NS_ERROR_DOM_INVALID_STATE_ERR,
                  nsPrintfCString("Unknown GMP for keysystem '%s'",
                                  NS_ConvertUTF16toUTF8(mKeySystem).get()));
    return;
  }

  gmp::NodeId nodeId(aOrigin, aTopLevelOrigin, aGMPName);
  RefPtr<AbstractThread> thread = mGMPThread;
  RefPtr<GMPCrashHelper> helper(mCrashHelper);
  RefPtr<ChromiumCDMProxy> self(this);
  nsCString keySystem = NS_ConvertUTF16toUTF8(mKeySystem);
  RefPtr<Runnable> task(NS_NewRunnableFunction(
    [self, nodeId, helper, aPromiseId, thread, keySystem]() -> void {
      MOZ_ASSERT(self->IsOnOwnerThread());

      RefPtr<gmp::GeckoMediaPluginService> service =
        gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
      if (!service) {
        self->RejectPromise(
          aPromiseId,
          NS_ERROR_DOM_INVALID_STATE_ERR,
          NS_LITERAL_CSTRING(
            "Couldn't get GeckoMediaPluginService in ChromiumCDMProxy::Init"));
        return;
      }
      RefPtr<gmp::GetCDMParentPromise> promise =
        service->GetCDM(nodeId, { keySystem }, helper);
      promise->Then(
        thread,
        __func__,
        [self, aPromiseId](RefPtr<gmp::ChromiumCDMParent> cdm) {
          if (!cdm->Init(self,
                         self->mDistinctiveIdentifierRequired,
                         self->mPersistentStateRequired)) {
            self->RejectPromise(aPromiseId,
                                NS_ERROR_FAILURE,
                                NS_LITERAL_CSTRING("GetCDM failed."));
            return;
          }
          {
            MutexAutoLock lock(self->mCDMMutex);
            self->mCDM = cdm;
          }
          self->OnCDMCreated(aPromiseId);
        },
        [self, aPromiseId](nsresult rv) {
          self->RejectPromise(
            aPromiseId, NS_ERROR_FAILURE, NS_LITERAL_CSTRING("GetCDM failed."));
        });
    }));

  mGMPThread->Dispatch(task.forget());
}