already_AddRefed<Promise>
MediaKeySession::Close(ErrorResult& aRv)
{
  RefPtr<DetailedPromise> promise(MakePromise(aRv,
    NS_LITERAL_CSTRING("MediaKeySession.close")));
  if (aRv.Failed()) {
    return nullptr;
  }
  if (!IsCallable()) {
    // If this object's callable value is false, return a promise rejected
    // with a new DOMException whose name is InvalidStateError.
    EME_LOG("MediaKeySession[%p,''] Close() called before sessionId set by CDM", this);
    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
      NS_LITERAL_CSTRING("MediaKeySession.Close() called before sessionId set by CDM"));
    return promise.forget();
  }
  if (IsClosed() || !mKeys->GetCDMProxy()) {
    EME_LOG("MediaKeySession[%p,'%s'] Close() already closed",
            this, NS_ConvertUTF16toUTF8(mSessionId).get());
    promise->MaybeResolve(JS::UndefinedHandleValue);
    return promise.forget();
  }
  PromiseId pid = mKeys->StorePromise(promise);
  mKeys->GetCDMProxy()->CloseSession(mSessionId, pid);

  EME_LOG("MediaKeySession[%p,'%s'] Close() sent to CDM, promiseId=%d",
          this, NS_ConvertUTF16toUTF8(mSessionId).get(), pid);

  return promise.forget();
}
Beispiel #2
0
already_AddRefed<Promise>
MediaKeys::LoadSession(const nsAString& aSessionId, ErrorResult& aRv)
{
  nsRefPtr<Promise> promise(MakePromise(aRv));
  if (aRv.Failed()) {
    return nullptr;
  }

  if (aSessionId.IsEmpty()) {
    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
    // "The sessionId parameter is empty."
    return promise.forget();
  }

  // TODO: The spec doesn't specify what to do in this case...
  if (mKeySessions.Contains(aSessionId)) {
    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
    return promise.forget();
  }

  // Create session.
  nsRefPtr<MediaKeySession> session(
    new MediaKeySession(GetParentObject(), this, mKeySystem, SessionType::Persistent, aRv));
  if (aRv.Failed()) {
    return nullptr;
  }

  session->Init(aSessionId);
  auto pid = StorePromise(promise);
  mPendingSessions.Put(pid, session);
  mProxy->LoadSession(pid, aSessionId);

  return promise.forget();
}
Beispiel #3
0
already_AddRefed<Promise>
MediaKeys::CreateSession(const nsAString& initDataType,
                         const Uint8Array& aInitData,
                         SessionType aSessionType,
                         ErrorResult& aRv)
{
  aInitData.ComputeLengthAndData();
  nsRefPtr<Promise> promise(MakePromise(aRv));
  if (aRv.Failed()) {
    return nullptr;
  }
  nsRefPtr<MediaKeySession> session = new MediaKeySession(GetParentObject(),
                                                          this,
                                                          mKeySystem,
                                                          aSessionType, aRv);
  if (aRv.Failed()) {
    return nullptr;
  }

  auto pid = StorePromise(promise);
  // Hang onto session until the CDM has finished setting it up.
  mPendingSessions.Put(pid, session);
  mProxy->CreateSession(aSessionType,
                        pid,
                        initDataType,
                        aInitData);

  return promise.forget();
}
Beispiel #4
0
already_AddRefed<DetailedPromise>
MediaKeys::SetServerCertificate(const ArrayBufferViewOrArrayBuffer& aCert, ErrorResult& aRv)
{
  RefPtr<DetailedPromise> promise(MakePromise(aRv,
    NS_LITERAL_CSTRING("MediaKeys.setServerCertificate")));
  if (aRv.Failed()) {
    return nullptr;
  }

  if (!mProxy) {
    NS_WARNING("Tried to use a MediaKeys without a CDM");
    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
                         NS_LITERAL_CSTRING("Null CDM in MediaKeys.setServerCertificate()"));
    return promise.forget();
  }

  nsTArray<uint8_t> data;
  CopyArrayBufferViewOrArrayBufferData(aCert, data);
  if (data.IsEmpty()) {
    promise->MaybeReject(NS_ERROR_DOM_TYPE_ERR,
      NS_LITERAL_CSTRING("Empty certificate passed to MediaKeys.setServerCertificate()"));
    return promise.forget();
  }

  mProxy->SetServerCertificate(StorePromise(promise), data);
  return promise.forget();
}
Beispiel #5
0
already_AddRefed<Promise>
MediaKeySession::Remove(ErrorResult& aRv)
{
  nsRefPtr<DetailedPromise> promise(MakePromise(aRv,
    NS_LITERAL_CSTRING("MediaKeySession.remove")));
  if (aRv.Failed()) {
    return nullptr;
  }
  if (mSessionType != SessionType::Persistent) {
    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
                         NS_LITERAL_CSTRING("Calling MediaKeySession.remove() on non-persistent session"));
    // "The operation is not supported on session type sessions."
    EME_LOG("MediaKeySession[%p,'%s'] Remove() failed, sesion not persisrtent.",
            this, NS_ConvertUTF16toUTF8(mSessionId).get());
    return promise.forget();
  }
  if (IsClosed() || !mKeys->GetCDMProxy()) {
    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
                         NS_LITERAL_CSTRING("MediaKeySesison.remove() called but session is not active"));
    // "The session is closed."
    EME_LOG("MediaKeySession[%p,'%s'] Remove() failed, already session closed.",
            this, NS_ConvertUTF16toUTF8(mSessionId).get());
    return promise.forget();
  }
  PromiseId pid = mKeys->StorePromise(promise);
  mKeys->GetCDMProxy()->RemoveSession(mSessionId, pid);
  EME_LOG("MediaKeySession[%p,'%s'] Remove() sent to CDM, promiseId=%d.",
          this, NS_ConvertUTF16toUTF8(mSessionId).get(), pid);

  return promise.forget();
}
Beispiel #6
0
already_AddRefed<Promise>
MediaKeySession::GenerateRequest(const nsAString& aInitDataType,
                                 const ArrayBufferViewOrArrayBuffer& aInitData,
                                 ErrorResult& aRv)
{
  nsRefPtr<DetailedPromise> promise(MakePromise(aRv,
    NS_LITERAL_CSTRING("MediaKeySession.generateRequest")));
  if (aRv.Failed()) {
    return nullptr;
  }

  if (!mUninitialized) {
    EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, uninitialized",
            this, NS_ConvertUTF16toUTF8(mSessionId).get());
    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
                         NS_LITERAL_CSTRING("Session is already initialized in MediaKeySession.generateRequest()"));
    return promise.forget();
  }

  mUninitialized = false;

  nsTArray<uint8_t> data;
  if (aInitDataType.IsEmpty() ||
      !CopyArrayBufferViewOrArrayBufferData(aInitData, data)) {
    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
                         NS_LITERAL_CSTRING("Bad arguments to MediaKeySession.generateRequest()"));
    EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, "
            "invalid initData or initDataType",
      this, NS_ConvertUTF16toUTF8(mSessionId).get());
    return promise.forget();
  }

  // Convert initData to base64 for easier logging.
  // Note: UpdateSession() Move()s the data out of the array, so we have
  // to copy it here.
  nsAutoCString base64InitData;
  if (EME_LOG_ENABLED()) {
    nsDependentCSubstring rawInitData(reinterpret_cast<const char*>(data.Elements()),
      data.Length());
    if (NS_FAILED(Base64Encode(rawInitData, base64InitData))) {
      NS_WARNING("Failed to base64 encode initData for logging");
    }
  }

  PromiseId pid = mKeys->StorePromise(promise);
  mKeys->GetCDMProxy()->CreateSession(Token(),
                                      mSessionType,
                                      pid,
                                      aInitDataType, data);

  EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() sent, "
          "promiseId=%d initData(base64)='%s'",
          this,
          NS_ConvertUTF16toUTF8(mSessionId).get(),
          pid,
          base64InitData.get());

  return promise.forget();
}
already_AddRefed<Promise>
MediaKeySession::GenerateRequest(const nsAString& aInitDataType,
                                 const ArrayBufferViewOrArrayBuffer& aInitData,
                                 ErrorResult& aRv)
{
  RefPtr<DetailedPromise> promise(MakePromise(aRv,
    NS_LITERAL_CSTRING("MediaKeySession.generateRequest")));
  if (aRv.Failed()) {
    return nullptr;
  }

  if (!mUninitialized) {
    EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, uninitialized",
            this, NS_ConvertUTF16toUTF8(mSessionId).get());
    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
      NS_LITERAL_CSTRING("Session is already initialized in MediaKeySession.generateRequest()"));
    return promise.forget();
  }

  mUninitialized = false;

  if (aInitDataType.IsEmpty()) {
    promise->MaybeReject(NS_ERROR_DOM_TYPE_ERR,
      NS_LITERAL_CSTRING("Empty initDataType passed to MediaKeySession.generateRequest()"));
    EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, empty initDataType",
      this, NS_ConvertUTF16toUTF8(mSessionId).get());
    return promise.forget();
  }

  nsTArray<uint8_t> data;
  CopyArrayBufferViewOrArrayBufferData(aInitData, data);
  if (data.IsEmpty()) {
    promise->MaybeReject(NS_ERROR_DOM_TYPE_ERR,
      NS_LITERAL_CSTRING("Empty initData passed to MediaKeySession.generateRequest()"));
    EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, empty initData",
      this, NS_ConvertUTF16toUTF8(mSessionId).get());
    return promise.forget();
  }

  // Convert initData to base64 for easier logging.
  // Note: CreateSession() Move()s the data out of the array, so we have
  // to copy it here.
  nsAutoCString base64InitData(ToBase64(data));
  PromiseId pid = mKeys->StorePromise(promise);
  mKeys->GetCDMProxy()->CreateSession(Token(),
                                      mSessionType,
                                      pid,
                                      aInitDataType, data);

  EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() sent, "
          "promiseId=%d initData(base64)='%s' initDataType='%s'",
          this,
          NS_ConvertUTF16toUTF8(mSessionId).get(),
          pid,
          base64InitData.get(),
          NS_ConvertUTF16toUTF8(aInitDataType).get());

  return promise.forget();
}
already_AddRefed<Promise>
MediaKeySession::Update(const ArrayBufferViewOrArrayBuffer& aResponse, ErrorResult& aRv)
{
  RefPtr<DetailedPromise> promise(MakePromise(aRv,
    NS_LITERAL_CSTRING("MediaKeySession.update")));
  if (aRv.Failed()) {
    return nullptr;
  }

  if (!IsCallable()) {
    // If this object's callable value is false, return a promise rejected
    // with a new DOMException whose name is InvalidStateError.
    EME_LOG("MediaKeySession[%p,''] Update() called before sessionId set by CDM", this);
    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
      NS_LITERAL_CSTRING("MediaKeySession.Update() called before sessionId set by CDM"));
    return promise.forget();
  }

  nsTArray<uint8_t> data;
  if (IsClosed() || !mKeys->GetCDMProxy()) {
    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
                         NS_LITERAL_CSTRING("Session is closed or was not properly initialized"));
    EME_LOG("MediaKeySession[%p,'%s'] Update() failed, session is closed or was not properly initialised.",
            this, NS_ConvertUTF16toUTF8(mSessionId).get());
    return promise.forget();
  }
  CopyArrayBufferViewOrArrayBufferData(aResponse, data);
  if (data.IsEmpty()) {
    promise->MaybeReject(NS_ERROR_DOM_TYPE_ERR,
      NS_LITERAL_CSTRING("Empty response buffer passed to MediaKeySession.update()"));
    EME_LOG("MediaKeySession[%p,'%s'] Update() failed, empty response buffer",
            this, NS_ConvertUTF16toUTF8(mSessionId).get());
    return promise.forget();
  }


  // Convert response to base64 for easier logging.
  // Note: UpdateSession() Move()s the data out of the array, so we have
  // to copy it here.
  nsAutoCString base64Response(ToBase64(data));

  PromiseId pid = mKeys->StorePromise(promise);
  mKeys->GetCDMProxy()->UpdateSession(mSessionId,
                                      pid,
                                      data);

  EME_LOG("MediaKeySession[%p,'%s'] Update() sent to CDM, "
          "promiseId=%d Response(base64)='%s'",
           this,
           NS_ConvertUTF16toUTF8(mSessionId).get(),
           pid,
           base64Response.get());

  return promise.forget();
}
Beispiel #9
0
already_AddRefed<Promise>
MediaKeySession::Update(const ArrayBufferViewOrArrayBuffer& aResponse, ErrorResult& aRv)
{
  nsRefPtr<DetailedPromise> promise(MakePromise(aRv,
    NS_LITERAL_CSTRING("MediaKeySession.update")));
  if (aRv.Failed()) {
    return nullptr;
  }
  nsTArray<uint8_t> data;
  if (IsClosed() || !mKeys->GetCDMProxy()) {
    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
                         NS_LITERAL_CSTRING("Session is closed or was not properly initialized"));
    EME_LOG("MediaKeySession[%p,'%s'] Update() failed, session is closed or was not properly initialised.",
            this, NS_ConvertUTF16toUTF8(mSessionId).get());
    return promise.forget();
  }
  if (!CopyArrayBufferViewOrArrayBufferData(aResponse, data)) {
    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
                         NS_LITERAL_CSTRING("Invalid response buffer"));
    EME_LOG("MediaKeySession[%p,'%s'] Update() failed, invalid response buffer",
            this, NS_ConvertUTF16toUTF8(mSessionId).get());
    return promise.forget();
  }


  // Convert response to base64 for easier logging.
  // Note: UpdateSession() Move()s the data out of the array, so we have
  // to copy it here.
  nsAutoCString base64Response;
  if (EME_LOG_ENABLED()) {
    nsDependentCSubstring rawResponse(reinterpret_cast<const char*>(data.Elements()),
      data.Length());
    if (NS_FAILED(Base64Encode(rawResponse, base64Response))) {
      NS_WARNING("Failed to base64 encode response for logging");
    }
  }

  PromiseId pid = mKeys->StorePromise(promise);
  mKeys->GetCDMProxy()->UpdateSession(mSessionId,
                                      pid,
                                      data);

  EME_LOG("MediaKeySession[%p,'%s'] Update() sent to CDM, "
          "promiseId=%d Response(base64)='%s'",
           this,
           NS_ConvertUTF16toUTF8(mSessionId).get(),
           pid,
           base64Response.get());

  return promise.forget();
}
Beispiel #10
0
already_AddRefed<Promise>
MediaKeys::SetServerCertificate(const ArrayBufferViewOrArrayBuffer& aCert, ErrorResult& aRv)
{
  nsRefPtr<Promise> promise(MakePromise(aRv));
  if (aRv.Failed()) {
    return nullptr;
  }

  nsTArray<uint8_t> data;
  if (!CopyArrayBufferViewOrArrayBufferData(aCert, data)) {
    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
    return promise.forget();
  }

  mProxy->SetServerCertificate(StorePromise(promise), data);
  return promise.forget();
}
already_AddRefed<Promise>
MediaKeySession::Close(ErrorResult& aRv)
{
  RefPtr<DetailedPromise> promise(MakePromise(aRv,
    NS_LITERAL_CSTRING("MediaKeySession.close")));
  if (aRv.Failed()) {
    return nullptr;
  }
  // 1. Let session be the associated MediaKeySession object.
  // 2. If session is closed, return a resolved promise.
  if (IsClosed()) {
    EME_LOG("MediaKeySession[%p,'%s'] Close() already closed",
            this, NS_ConvertUTF16toUTF8(mSessionId).get());
    promise->MaybeResolveWithUndefined();
    return promise.forget();
  }
  // 3. If session's callable value is false, return a promise rejected
  // with an InvalidStateError.
  if (!IsCallable()) {
    EME_LOG("MediaKeySession[%p,''] Close() called before sessionId set by CDM", this);
    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
      NS_LITERAL_CSTRING("MediaKeySession.Close() called before sessionId set by CDM"));
    return promise.forget();
  }
  if (!mKeys->GetCDMProxy()) {
    EME_LOG("MediaKeySession[%p,'%s'] Close() null CDMProxy",
            this, NS_ConvertUTF16toUTF8(mSessionId).get());
    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
      NS_LITERAL_CSTRING("MediaKeySession.Close() lost reference to CDM"));
    return promise.forget();
  }
  // 4. Let promise be a new promise.
  PromiseId pid = mKeys->StorePromise(promise);
  // 5. Run the following steps in parallel:
  // 5.1 Let cdm be the CDM instance represented by session's cdm instance value.
  // 5.2 Use cdm to close the session associated with session.
  mKeys->GetCDMProxy()->CloseSession(mSessionId, pid);

  EME_LOG("MediaKeySession[%p,'%s'] Close() sent to CDM, promiseId=%d",
          this, NS_ConvertUTF16toUTF8(mSessionId).get(), pid);

  // Session Closed algorithm is run when CDM causes us to run OnSessionClosed().

  // 6. Return promise.
  return promise.forget();
}
Beispiel #12
0
already_AddRefed<Promise>
MediaKeySession::Load(const nsAString& aSessionId, ErrorResult& aRv)
{
  nsRefPtr<DetailedPromise> promise(MakePromise(aRv,
    NS_LITERAL_CSTRING("MediaKeySession.load")));
  if (aRv.Failed()) {
    return nullptr;
  }

  if (aSessionId.IsEmpty()) {
    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
                         NS_LITERAL_CSTRING("Trying to load a session with empty session ID"));
    // "The sessionId parameter is empty."
    EME_LOG("MediaKeySession[%p,''] Load() failed, no sessionId", this);
    return promise.forget();
  }

  if (!mUninitialized) {
    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
                         NS_LITERAL_CSTRING("Session is already initialized in MediaKeySession.load()"));
    EME_LOG("MediaKeySession[%p,'%s'] Load() failed, uninitialized",
      this, NS_ConvertUTF16toUTF8(aSessionId).get());
    return promise.forget();
  }

  mUninitialized = false;

  // We now know the sessionId being loaded into this session. Remove the
  // session from its owning MediaKey's set of sessions awaiting a sessionId.
  nsRefPtr<MediaKeySession> session(mKeys->GetPendingSession(Token()));
  MOZ_ASSERT(session == this, "Session should be awaiting id on its own token");

  // Associate with the known sessionId.
  SetSessionId(aSessionId);

  PromiseId pid = mKeys->StorePromise(promise);
  mKeys->GetCDMProxy()->LoadSession(pid, aSessionId);

  EME_LOG("MediaKeySession[%p,'%s'] Load() sent to CDM, promiseId=%d",
    this, NS_ConvertUTF16toUTF8(mSessionId).get(), pid);

  return promise.forget();
}
Beispiel #13
0
already_AddRefed<Promise>
MediaKeySession::Close(ErrorResult& aRv)
{
  nsRefPtr<DetailedPromise> promise(MakePromise(aRv));
  if (aRv.Failed()) {
    return nullptr;
  }
  if (IsClosed() || !mKeys->GetCDMProxy()) {
    EME_LOG("MediaKeySession[%p,'%s'] Close() already closed",
            this, NS_ConvertUTF16toUTF8(mSessionId).get());
    promise->MaybeResolve(JS::UndefinedHandleValue);
    return promise.forget();
  }
  PromiseId pid = mKeys->StorePromise(promise);
  mKeys->GetCDMProxy()->CloseSession(mSessionId, mKeys->StorePromise(promise));

  EME_LOG("MediaKeySession[%p,'%s'] Close() sent to CDM, promiseId=%d",
          this, NS_ConvertUTF16toUTF8(mSessionId).get(), pid);

  return promise.forget();
}
Beispiel #14
0
already_AddRefed<Promise>
MediaKeySession::Remove(ErrorResult& aRv)
{
  RefPtr<DetailedPromise> promise(MakePromise(aRv,
    NS_LITERAL_CSTRING("MediaKeySession.remove")));
  if (aRv.Failed()) {
    return nullptr;
  }
  if (!IsCallable()) {
    // If this object's callable value is false, return a promise rejected
    // with a new DOMException whose name is InvalidStateError.
    EME_LOG("MediaKeySession[%p,''] Remove() called before sessionId set by CDM", this);
    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
      NS_LITERAL_CSTRING("MediaKeySession.Remove() called before sessionId set by CDM"));
    return promise.forget();
  }
  if (mSessionType != MediaKeySessionType::Persistent_license) {
    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
                         NS_LITERAL_CSTRING("Calling MediaKeySession.remove() on non-persistent session"));
    // "The operation is not supported on session type sessions."
    EME_LOG("MediaKeySession[%p,'%s'] Remove() failed, sesion not persisrtent.",
            this, NS_ConvertUTF16toUTF8(mSessionId).get());
    return promise.forget();
  }
  if (IsClosed() || !mKeys->GetCDMProxy()) {
    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
                         NS_LITERAL_CSTRING("MediaKeySesison.remove() called but session is not active"));
    // "The session is closed."
    EME_LOG("MediaKeySession[%p,'%s'] Remove() failed, already session closed.",
            this, NS_ConvertUTF16toUTF8(mSessionId).get());
    return promise.forget();
  }
  PromiseId pid = mKeys->StorePromise(promise);
  mKeys->GetCDMProxy()->RemoveSession(mSessionId, pid);
  EME_LOG("MediaKeySession[%p,'%s'] Remove() sent to CDM, promiseId=%d.",
          this, NS_ConvertUTF16toUTF8(mSessionId).get(), pid);

  return promise.forget();
}
Beispiel #15
0
already_AddRefed<Promise>
MediaKeys::Init(ErrorResult& aRv)
{
  nsRefPtr<Promise> promise(MakePromise(aRv));
  if (aRv.Failed()) {
    return nullptr;
  }

  mProxy = new CDMProxy(this, mKeySystem);

  // Determine principal (at creation time) of the MediaKeys object.
  nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(GetParentObject());
  if (!sop) {
    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
    return promise.forget();
  }
  mPrincipal = sop->GetPrincipal();

  // Determine principal of the "top-level" window; the principal of the
  // page that will display in the URL bar.
  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(GetParentObject());
  if (!window) {
    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
    return promise.forget();
  }
  nsCOMPtr<nsIDOMWindow> topWindow;
  window->GetTop(getter_AddRefs(topWindow));
  nsCOMPtr<nsPIDOMWindow> top = do_QueryInterface(topWindow);
  if (!top || !top->GetExtantDoc()) {
    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
    return promise.forget();
  }

  mTopLevelPrincipal = top->GetExtantDoc()->NodePrincipal();

  if (!mPrincipal || !mTopLevelPrincipal) {
    NS_WARNING("Failed to get principals when creating MediaKeys");
    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
    return promise.forget();
  }

  nsAutoString origin;
  nsresult rv = nsContentUtils::GetUTFOrigin(mPrincipal, origin);
  if (NS_FAILED(rv)) {
    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
    return promise.forget();
  }
  nsAutoString topLevelOrigin;
  rv = nsContentUtils::GetUTFOrigin(mTopLevelPrincipal, topLevelOrigin);
  if (NS_FAILED(rv)) {
    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
    return promise.forget();
  }

  if (!window) {
    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
    return promise.forget();
  }
  nsIDocument* doc = window->GetExtantDoc();
  const bool inPrivateBrowsing = nsContentUtils::IsInPrivateBrowsing(doc);

  EME_LOG("MediaKeys::Create() (%s, %s), %s",
          NS_ConvertUTF16toUTF8(origin).get(),
          NS_ConvertUTF16toUTF8(topLevelOrigin).get(),
          (inPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"));

  // The CDMProxy's initialization is asynchronous. The MediaKeys is
  // refcounted, and its instance is returned to JS by promise once
  // it's been initialized. No external refs exist to the MediaKeys while
  // we're waiting for the promise to be resolved, so we must hold a
  // reference to the new MediaKeys object until it's been created,
  // or its creation has failed. Store the id of the promise returned
  // here, and hold a self-reference until that promise is resolved or
  // rejected.
  MOZ_ASSERT(!mCreatePromiseId, "Should only be created once!");
  mCreatePromiseId = StorePromise(promise);
  AddRef();
  mProxy->Init(mCreatePromiseId,
               origin,
               topLevelOrigin,
               inPrivateBrowsing);

  return promise.forget();
}
already_AddRefed<Promise>
MediaKeySession::Load(const nsAString& aSessionId, ErrorResult& aRv)
{
  RefPtr<DetailedPromise> promise(MakePromise(aRv,
    NS_LITERAL_CSTRING("MediaKeySession.load")));
  if (aRv.Failed()) {
    return nullptr;
  }

  // 1. If this object is closed, return a promise rejected with an InvalidStateError.
  if (IsClosed()) {
    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
                         NS_LITERAL_CSTRING("Session is closed in MediaKeySession.load()"));
    EME_LOG("MediaKeySession[%p,'%s'] Load() failed, closed",
      this, NS_ConvertUTF16toUTF8(aSessionId).get());
    return promise.forget();
  }

  // 2.If this object's uninitialized value is false, return a promise rejected
  // with an InvalidStateError.
  if (!mUninitialized) {
    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
                         NS_LITERAL_CSTRING("Session is already initialized in MediaKeySession.load()"));
    EME_LOG("MediaKeySession[%p,'%s'] Load() failed, uninitialized",
      this, NS_ConvertUTF16toUTF8(aSessionId).get());
    return promise.forget();
  }

  // 3.Let this object's uninitialized value be false.
  mUninitialized = false;

  // 4. If sessionId is the empty string, return a promise rejected with a newly created TypeError.
  if (aSessionId.IsEmpty()) {
    promise->MaybeReject(NS_ERROR_DOM_TYPE_ERR,
                         NS_LITERAL_CSTRING("Trying to load a session with empty session ID"));
    // "The sessionId parameter is empty."
    EME_LOG("MediaKeySession[%p,''] Load() failed, no sessionId", this);
    return promise.forget();
  }

  // 5. If the result of running the Is persistent session type? algorithm
  // on this object's session type is false, return a promise rejected with
  // a newly created TypeError.
  if (mSessionType == MediaKeySessionType::Temporary) {
    promise->MaybeReject(NS_ERROR_DOM_TYPE_ERR,
                         NS_LITERAL_CSTRING("Trying to load() into a non-persistent session"));
    EME_LOG("MediaKeySession[%p,''] Load() failed, can't load in a non-persistent session", this);
    return promise.forget();
  }

  // Note: We don't support persistent sessions in any keysystem, so all calls
  // to Load() should reject with a TypeError in the preceding check. Omitting
  // implementing the rest of the specified MediaKeySession::Load() algorithm.

  // We now know the sessionId being loaded into this session. Remove the
  // session from its owning MediaKey's set of sessions awaiting a sessionId.
  RefPtr<MediaKeySession> session(mKeys->GetPendingSession(Token()));
  MOZ_ASSERT(session == this, "Session should be awaiting id on its own token");

  // Associate with the known sessionId.
  SetSessionId(aSessionId);

  PromiseId pid = mKeys->StorePromise(promise);
  mKeys->GetCDMProxy()->LoadSession(pid, aSessionId);

  EME_LOG("MediaKeySession[%p,'%s'] Load() sent to CDM, promiseId=%d",
    this, NS_ConvertUTF16toUTF8(mSessionId).get(), pid);

  return promise.forget();
}
// Generates a license request based on the initData. A message of type
// "license-request" or "individualization-request" will always be queued
// if the algorithm succeeds and the promise is resolved.
already_AddRefed<Promise>
MediaKeySession::GenerateRequest(const nsAString& aInitDataType,
                                 const ArrayBufferViewOrArrayBuffer& aInitData,
                                 ErrorResult& aRv)
{
  RefPtr<DetailedPromise> promise(MakePromise(aRv,
    NS_LITERAL_CSTRING("MediaKeySession.generateRequest")));
  if (aRv.Failed()) {
    return nullptr;
  }

  // If this object is closed, return a promise rejected with an InvalidStateError.
  if (IsClosed()) {
    EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, closed",
            this, NS_ConvertUTF16toUTF8(mSessionId).get());
    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
      NS_LITERAL_CSTRING("Session is closed in MediaKeySession.generateRequest()"));
    return promise.forget();
  }

  // If this object's uninitialized value is false, return a promise rejected
  // with an InvalidStateError.
  if (!mUninitialized) {
    EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, uninitialized",
            this, NS_ConvertUTF16toUTF8(mSessionId).get());
    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
      NS_LITERAL_CSTRING("Session is already initialized in MediaKeySession.generateRequest()"));
    return promise.forget();
  }

  // Let this object's uninitialized value be false.
  mUninitialized = false;

  // If initDataType is the empty string, return a promise rejected
  // with a newly created TypeError.
  if (aInitDataType.IsEmpty()) {
    promise->MaybeReject(NS_ERROR_DOM_TYPE_ERR,
      NS_LITERAL_CSTRING("Empty initDataType passed to MediaKeySession.generateRequest()"));
    EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, empty initDataType",
      this, NS_ConvertUTF16toUTF8(mSessionId).get());
    return promise.forget();
  }

  // If initData is an empty array, return a promise rejected with
  // a newly created TypeError.
  nsTArray<uint8_t> data;
  CopyArrayBufferViewOrArrayBufferData(aInitData, data);
  if (data.IsEmpty()) {
    promise->MaybeReject(NS_ERROR_DOM_TYPE_ERR,
      NS_LITERAL_CSTRING("Empty initData passed to MediaKeySession.generateRequest()"));
    EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, empty initData",
            this, NS_ConvertUTF16toUTF8(mSessionId).get());
    return promise.forget();
  }

  // If the Key System implementation represented by this object's
  // cdm implementation value does not support initDataType as an
  // Initialization Data Type, return a promise rejected with a
  // NotSupportedError. String comparison is case-sensitive.
  if (!MediaKeySystemAccess::KeySystemSupportsInitDataType(mKeySystem, aInitDataType)) {
    promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
      NS_LITERAL_CSTRING("Unsupported initDataType passed to MediaKeySession.generateRequest()"));
    EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, unsupported initDataType",
            this, NS_ConvertUTF16toUTF8(mSessionId).get());
    return promise.forget();
  }

  // Let init data be a copy of the contents of the initData parameter.
  // Note: Handled by the CopyArrayBufferViewOrArrayBufferData call above.

  // Let session type be this object's session type.

  // Let promise be a new promise.

  // Run the following steps in parallel:

  // If the init data is not valid for initDataType, reject promise with
  // a newly created TypeError.
  if (!ValidateInitData(data, aInitDataType)) {
    // If the preceding step failed, reject promise with a newly created TypeError.
    promise->MaybeReject(NS_ERROR_DOM_TYPE_ERR,
      NS_LITERAL_CSTRING("initData sanitization failed in MediaKeySession.generateRequest()"));
    EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() initData sanitization failed",
            this, NS_ConvertUTF16toUTF8(mSessionId).get());
    return promise.forget();
  }

  // Let sanitized init data be a validated and sanitized version of init data.

  // If sanitized init data is empty, reject promise with a NotSupportedError.

  // Note: Remaining steps of generateRequest method continue in CDM.

  Telemetry::Accumulate(Telemetry::VIDEO_CDM_GENERATE_REQUEST_CALLED,
                        ToCDMTypeTelemetryEnum(mKeySystem));

  // Convert initData to base64 for easier logging.
  // Note: CreateSession() Move()s the data out of the array, so we have
  // to copy it here.
  nsAutoCString base64InitData(ToBase64(data));
  PromiseId pid = mKeys->StorePromise(promise);
  mKeys->ConnectPendingPromiseIdWithToken(pid, Token());
  mKeys->GetCDMProxy()->CreateSession(Token(),
                                      mSessionType,
                                      pid,
                                      aInitDataType, data);

  EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() sent, "
          "promiseId=%d initData(base64)='%s' initDataType='%s'",
          this,
          NS_ConvertUTF16toUTF8(mSessionId).get(),
          pid,
          base64InitData.get(),
          NS_ConvertUTF16toUTF8(aInitDataType).get());

  return promise.forget();
}