| Index: third_party/WebKit/Source/modules/encryptedmedia/MediaKeySession.cpp
|
| diff --git a/third_party/WebKit/Source/modules/encryptedmedia/MediaKeySession.cpp b/third_party/WebKit/Source/modules/encryptedmedia/MediaKeySession.cpp
|
| index 4a2156406347405f106e4e61514867f112cfc714..b248dba590c4c12ab5a98bc0f81211dc2207e22d 100644
|
| --- a/third_party/WebKit/Source/modules/encryptedmedia/MediaKeySession.cpp
|
| +++ b/third_party/WebKit/Source/modules/encryptedmedia/MediaKeySession.cpp
|
| @@ -29,6 +29,7 @@
|
| #include "bindings/core/v8/ScriptPromise.h"
|
| #include "bindings/core/v8/ScriptPromiseResolver.h"
|
| #include "bindings/core/v8/ScriptState.h"
|
| +#include "bindings/core/v8/V8ThrowException.h"
|
| #include "core/dom/DOMArrayBuffer.h"
|
| #include "core/dom/DOMException.h"
|
| #include "core/dom/ExceptionCode.h"
|
| @@ -81,6 +82,24 @@ static bool isValidSessionId(const String& sessionId) {
|
| return true;
|
| }
|
|
|
| +static bool IsPersistentSessionType(WebEncryptedMediaSessionType sessionType) {
|
| + // This implements section 5.1.1 Is persistent session type? from
|
| + // https://w3c.github.io/encrypted-media/#is-persistent-session-type
|
| + switch (sessionType) {
|
| + case WebEncryptedMediaSessionType::Temporary:
|
| + return false;
|
| + case WebEncryptedMediaSessionType::PersistentLicense:
|
| + return true;
|
| + case WebEncryptedMediaSessionType::PersistentReleaseMessage:
|
| + return true;
|
| + case blink::WebEncryptedMediaSessionType::Unknown:
|
| + break;
|
| + }
|
| +
|
| + NOTREACHED();
|
| + return false;
|
| +}
|
| +
|
| static String ConvertKeyStatusToString(
|
| const WebEncryptedMediaKeyInformation::KeyStatus status) {
|
| switch (status) {
|
| @@ -111,6 +130,13 @@ static ScriptPromise CreateRejectedPromiseNotCallable(
|
| DOMException::create(InvalidStateError, "The session is not callable."));
|
| }
|
|
|
| +static ScriptPromise CreateRejectedPromiseAlreadyClosed(
|
| + ScriptState* scriptState) {
|
| + return ScriptPromise::rejectWithDOMException(
|
| + scriptState, DOMException::create(InvalidStateError,
|
| + "The session is already closed."));
|
| +}
|
| +
|
| static ScriptPromise CreateRejectedPromiseAlreadyInitialized(
|
| ScriptState* scriptState) {
|
| return ScriptPromise::rejectWithDOMException(
|
| @@ -427,32 +453,38 @@ ScriptPromise MediaKeySession::generateRequest(ScriptState* scriptState,
|
| // Generates a request based on the initData. When this method is invoked,
|
| // the user agent must run the following steps:
|
|
|
| - // 1. If this object's uninitialized value is false, return a promise
|
| - // rejected with a new DOMException whose name is "InvalidStateError".
|
| + // 1. If this object is closed, return a promise rejected with an
|
| + // InvalidStateError.
|
| + if (m_isClosed)
|
| + return CreateRejectedPromiseAlreadyClosed(scriptState);
|
| +
|
| + // 2. If this object's uninitialized value is false, return a promise
|
| + // rejected with an InvalidStateError.
|
| if (!m_isUninitialized)
|
| return CreateRejectedPromiseAlreadyInitialized(scriptState);
|
|
|
| - // 2. Let this object's uninitialized be false.
|
| + // 3. Let this object's uninitialized be false.
|
| m_isUninitialized = false;
|
|
|
| - // 3. If initDataType is an empty string, return a promise rejected with a
|
| - // new DOMException whose name is "InvalidAccessError".
|
| + // 4. If initDataType is the empty string, return a promise rejected
|
| + // with a newly created TypeError.
|
| if (initDataTypeString.isEmpty()) {
|
| - return ScriptPromise::rejectWithDOMException(
|
| + return ScriptPromise::reject(
|
| scriptState,
|
| - DOMException::create(InvalidAccessError,
|
| - "The initDataType parameter is empty."));
|
| + V8ThrowException::createTypeError(
|
| + scriptState->isolate(), "The initDataType parameter is empty."));
|
| }
|
|
|
| - // 4. If initData is an empty array, return a promise rejected with a new
|
| - // DOMException whose name is"InvalidAccessError".
|
| + // 5. If initData is an empty array, return a promise rejected with a
|
| + // newly created TypeError.
|
| if (!initData.byteLength()) {
|
| - return ScriptPromise::rejectWithDOMException(
|
| - scriptState, DOMException::create(InvalidAccessError,
|
| + return ScriptPromise::reject(
|
| + scriptState,
|
| + V8ThrowException::createTypeError(scriptState->isolate(),
|
| "The initData parameter is empty."));
|
| }
|
|
|
| - // 5. If the Key System implementation represented by this object's cdm
|
| + // 6. 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 new
|
| // DOMException whose name is NotSupportedError. String comparison
|
| @@ -470,26 +502,26 @@ ScriptPromise MediaKeySession::generateRequest(ScriptState* scriptState,
|
| initDataTypeString + "' is not supported."));
|
| }
|
|
|
| - // 6. Let init data be a copy of the contents of the initData parameter.
|
| + // 7. Let init data be a copy of the contents of the initData parameter.
|
| DOMArrayBuffer* initDataBuffer =
|
| DOMArrayBuffer::create(initData.data(), initData.byteLength());
|
|
|
| - // 7. Let session type be this object's session type.
|
| + // 8. Let session type be this object's session type.
|
| // (Done in constructor.)
|
|
|
| - // 8. Let promise be a new promise.
|
| + // 9. Let promise be a new promise.
|
| NewSessionResultPromise* result =
|
| new NewSessionResultPromise(scriptState, this);
|
| ScriptPromise promise = result->promise();
|
|
|
| - // 9. Run the following steps asynchronously (documented in
|
| - // actionTimerFired())
|
| + // 10. Run the following steps asynchronously (documented in
|
| + // actionTimerFired())
|
| m_pendingActions.append(PendingAction::CreatePendingGenerateRequest(
|
| result, initDataType, initDataBuffer));
|
| DCHECK(!m_actionTimer.isActive());
|
| m_actionTimer.startOneShot(0, BLINK_FROM_HERE);
|
|
|
| - // 10. Return promise.
|
| + // 11. Return promise.
|
| return promise;
|
| }
|
|
|
| @@ -502,39 +534,38 @@ ScriptPromise MediaKeySession::load(ScriptState* scriptState,
|
| // Loads the data stored for the specified session into this object. When
|
| // this method is invoked, the user agent must run the following steps:
|
|
|
| - // 1. If this object's uninitialized value is false, return a promise
|
| - // rejected with a new DOMException whose name is "InvalidStateError".
|
| + // 1. If this object is closed, return a promise rejected with an
|
| + // InvalidStateError.
|
| + if (m_isClosed)
|
| + return CreateRejectedPromiseAlreadyClosed(scriptState);
|
| +
|
| + // 2. If this object's uninitialized value is false, return a promise
|
| + // rejected with an InvalidStateError.
|
| if (!m_isUninitialized)
|
| return CreateRejectedPromiseAlreadyInitialized(scriptState);
|
|
|
| - // 2. Let this object's uninitialized be false.
|
| + // 3. Let this object's uninitialized value be false.
|
| m_isUninitialized = false;
|
|
|
| - // 3. If sessionId is an empty string, return a promise rejected with a
|
| - // new DOMException whose name is "InvalidAccessError".
|
| + // 4. If sessionId is the empty string, return a promise rejected with
|
| + // a newly created TypeError.
|
| if (sessionId.isEmpty()) {
|
| - return ScriptPromise::rejectWithDOMException(
|
| - scriptState, DOMException::create(InvalidAccessError,
|
| + return ScriptPromise::reject(
|
| + scriptState,
|
| + V8ThrowException::createTypeError(scriptState->isolate(),
|
| "The sessionId parameter is empty."));
|
| }
|
|
|
| - // 4. If this object's session type is not "persistent-license" or
|
| - // "persistent-release-message", return a promise rejected with a
|
| - // new DOMException whose name is InvalidAccessError.
|
| - if (m_sessionType != WebEncryptedMediaSessionType::PersistentLicense &&
|
| - m_sessionType != WebEncryptedMediaSessionType::PersistentReleaseMessage) {
|
| - return ScriptPromise::rejectWithDOMException(
|
| + // 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 (!IsPersistentSessionType(m_sessionType)) {
|
| + return ScriptPromise::reject(
|
| scriptState,
|
| - DOMException::create(InvalidAccessError,
|
| - "The session type is not persistent."));
|
| + V8ThrowException::createTypeError(
|
| + scriptState->isolate(), "The session type is not persistent."));
|
| }
|
|
|
| - // 5. If the Key System implementation represented by this object's cdm
|
| - // implementation value does not support loading previous sessions,
|
| - // return a promise rejected with a new DOMException whose name is
|
| - // NotSupportedError.
|
| - // FIXME: Implement this (http://crbug.com/448922).
|
| -
|
| // 6. Let origin be the origin of this object's Document.
|
| // (Available as getExecutionContext()->getSecurityOrigin() anytime.)
|
|
|
| @@ -557,41 +588,46 @@ ScriptPromise MediaKeySession::load(ScriptState* scriptState,
|
| ScriptPromise MediaKeySession::update(ScriptState* scriptState,
|
| const DOMArrayPiece& response) {
|
| DVLOG(MEDIA_KEY_SESSION_LOG_LEVEL) << __func__ << "(" << this << ")";
|
| - DCHECK(!m_isClosed);
|
|
|
| // From https://w3c.github.io/encrypted-media/#update:
|
| // Provides messages, including licenses, to the CDM. When this method is
|
| // invoked, the user agent must run the following steps:
|
|
|
| - // 1. If this object's callable value is false, return a promise rejected
|
| - // with a new DOMException whose name is InvalidStateError.
|
| + // 1. If this object is closed, return a promise rejected with an
|
| + // InvalidStateError.
|
| + if (m_isClosed)
|
| + return CreateRejectedPromiseAlreadyClosed(scriptState);
|
| +
|
| + // 2. If this object's callable value is false, return a promise
|
| + // rejected with an InvalidStateError.
|
| if (!m_isCallable)
|
| return CreateRejectedPromiseNotCallable(scriptState);
|
|
|
| - // 2. If response is an empty array, return a promise rejected with a
|
| - // new DOMException whose name is InvalidAccessError.
|
| + // 3. If response is an empty array, return a promise rejected with a
|
| + // newly created TypeError.
|
| if (!response.byteLength()) {
|
| - return ScriptPromise::rejectWithDOMException(
|
| - scriptState, DOMException::create(InvalidAccessError,
|
| + return ScriptPromise::reject(
|
| + scriptState,
|
| + V8ThrowException::createTypeError(scriptState->isolate(),
|
| "The response parameter is empty."));
|
| }
|
|
|
| - // 3. Let response copy be a copy of the contents of the response parameter.
|
| + // 4. Let response copy be a copy of the contents of the response parameter.
|
| DOMArrayBuffer* responseCopy =
|
| DOMArrayBuffer::create(response.data(), response.byteLength());
|
|
|
| - // 4. Let promise be a new promise.
|
| + // 5. Let promise be a new promise.
|
| SimpleResultPromise* result = new SimpleResultPromise(scriptState, this);
|
| ScriptPromise promise = result->promise();
|
|
|
| - // 5. Run the following steps asynchronously (documented in
|
| + // 6. Run the following steps asynchronously (documented in
|
| // actionTimerFired())
|
| m_pendingActions.append(
|
| PendingAction::CreatePendingUpdate(result, responseCopy));
|
| if (!m_actionTimer.isActive())
|
| m_actionTimer.startOneShot(0, BLINK_FROM_HERE);
|
|
|
| - // 6. Return promise.
|
| + // 7. Return promise.
|
| return promise;
|
| }
|
|
|
| @@ -634,29 +670,24 @@ ScriptPromise MediaKeySession::remove(ScriptState* scriptState) {
|
| // Removes stored session data associated with this object. When this
|
| // method is invoked, the user agent must run the following steps:
|
|
|
| - // 1. If this object's callable value is false, return a promise rejected
|
| - // with a new DOMException whose name is "InvalidStateError".
|
| + // 1. If this object is closed, return a promise rejected with an
|
| + // InvalidStateError.
|
| + if (m_isClosed)
|
| + return CreateRejectedPromiseAlreadyClosed(scriptState);
|
| +
|
| + // 2. If this object's callable value is false, return a promise rejected
|
| + // with an InvalidStateError.
|
| if (!m_isCallable)
|
| return CreateRejectedPromiseNotCallable(scriptState);
|
|
|
| - // 2. If this object's session type is not "persistent-license" or
|
| - // "persistent-release-message", return a promise rejected with a
|
| - // new DOMException whose name is InvalidAccessError.
|
| - if (m_sessionType != WebEncryptedMediaSessionType::PersistentLicense &&
|
| - m_sessionType != WebEncryptedMediaSessionType::PersistentReleaseMessage) {
|
| - return ScriptPromise::rejectWithDOMException(
|
| + // 3. 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 (!IsPersistentSessionType(m_sessionType)) {
|
| + return ScriptPromise::reject(
|
| scriptState,
|
| - DOMException::create(InvalidAccessError,
|
| - "The session type is not persistent."));
|
| - }
|
| -
|
| - // 3. If the Session Close algorithm has been run on this object, return a
|
| - // promise rejected with a new DOMException whose name is
|
| - // "InvalidStateError".
|
| - if (m_isClosed) {
|
| - return ScriptPromise::rejectWithDOMException(
|
| - scriptState, DOMException::create(InvalidStateError,
|
| - "The session is already closed."));
|
| + V8ThrowException::createTypeError(
|
| + scriptState->isolate(), "The session type is not persistent."));
|
| }
|
|
|
| // 4. Let promise be a new promise.
|
| @@ -687,18 +718,18 @@ void MediaKeySession::actionTimerFired(TimerBase*) {
|
|
|
| switch (action->getType()) {
|
| case PendingAction::GenerateRequest:
|
| - // NOTE: Continue step 9 of MediaKeySession::generateRequest().
|
| + // NOTE: Continue step 10 of MediaKeySession::generateRequest().
|
| DVLOG(MEDIA_KEY_SESSION_LOG_LEVEL) << __func__ << "(" << this
|
| << ") GenerateRequest";
|
|
|
| - // initializeNewSession() in Chromium will execute steps 9.1 to 9.7.
|
| + // initializeNewSession() in Chromium will execute steps 10.1 to 10.9.
|
| m_session->initializeNewSession(
|
| action->initDataType(),
|
| static_cast<unsigned char*>(action->data()->data()),
|
| action->data()->byteLength(), m_sessionType,
|
| action->result()->result());
|
|
|
| - // Remaining steps (from 9.8) executed in finishGenerateRequest(),
|
| + // Remaining steps (from 10.10) executed in finishGenerateRequest(),
|
| // called when |result| is resolved.
|
| break;
|
|
|
| @@ -712,11 +743,11 @@ void MediaKeySession::actionTimerFired(TimerBase*) {
|
| // validate the sessionId value before passing it to the CDM.
|
| // At a minimum, this should include checking that the length
|
| // and value (e.g. alphanumeric) are reasonable.
|
| - // 8.2 If the previous step failed, reject promise with a new
|
| - // DOMException whose name is "InvalidAccessError".
|
| + // 8.2 If the preceding step failed, or if sanitized session ID
|
| + // is empty, reject promise with a newly created TypeError.
|
| if (!isValidSessionId(action->sessionId())) {
|
| action->result()->completeWithError(
|
| - WebContentDecryptionModuleExceptionInvalidAccessError, 0,
|
| + WebContentDecryptionModuleExceptionTypeError, 0,
|
| "Invalid sessionId");
|
| return;
|
| }
|
|
|