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(); }
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(); }
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(); }
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(); }
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(); }
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(); }
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(); }
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(); }
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(); }
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(); }
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(); }
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(); }