| 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 8c283efbcc93489110347e8b01ba582087bb7070..b7f92d23268da99790262b38cb4cbc30a509f567 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/V8Binding.h"
|
| #include "core/dom/DOMArrayBuffer.h"
|
| #include "core/dom/DOMException.h"
|
| #include "core/dom/ExceptionCode.h"
|
| @@ -85,6 +86,23 @@ static bool isValidSessionId(const String& sessionId)
|
| return true;
|
| }
|
|
|
| +static bool IsPersistentSessionType(WebEncryptedMediaSessionType sessionType)
|
| +{
|
| + 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) {
|
| @@ -114,6 +132,12 @@ static ScriptPromise CreateRejectedPromiseNotCallable(ScriptState* scriptState)
|
| scriptState, 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(
|
| @@ -400,29 +424,34 @@ ScriptPromise MediaKeySession::generateRequest(ScriptState* scriptState, const S
|
| // 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(
|
| - scriptState, DOMException::create(InvalidAccessError, "The initDataType parameter is empty."));
|
| + return ScriptPromise::reject(
|
| + scriptState, v8::Exception::TypeError(v8String(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, "The initData parameter is empty."));
|
| + return ScriptPromise::reject(
|
| + scriptState, v8::Exception::TypeError(v8String(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
|
| @@ -436,23 +465,23 @@ ScriptPromise MediaKeySession::generateRequest(ScriptState* scriptState, const S
|
| scriptState, DOMException::create(NotSupportedError, "The initialization data type '" + 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;
|
| }
|
|
|
| @@ -464,35 +493,34 @@ ScriptPromise MediaKeySession::load(ScriptState* scriptState, const String& sess
|
| // 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, "The sessionId parameter is empty."));
|
| + return ScriptPromise::reject(
|
| + scriptState, v8::Exception::TypeError(v8String(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(
|
| - scriptState, DOMException::create(InvalidAccessError, "The session type is not persistent."));
|
| + // 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, v8::Exception::TypeError(v8String(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.)
|
|
|
| @@ -513,38 +541,42 @@ ScriptPromise MediaKeySession::load(ScriptState* scriptState, const String& sess
|
| 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, "The response parameter is empty."));
|
| + return ScriptPromise::reject(
|
| + scriptState, v8::Exception::TypeError(v8String(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.
|
| SimpleContentDecryptionModuleResultPromise* result = new SimpleContentDecryptionModuleResultPromise(scriptState);
|
| 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;
|
| }
|
|
|
| @@ -589,25 +621,22 @@ 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(
|
| - 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."));
|
| + // 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, v8::Exception::TypeError(v8String(scriptState->isolate(), "The session type is not persistent.")));
|
| }
|
|
|
| // 4. Let promise be a new promise.
|
| @@ -639,13 +668,13 @@ 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;
|
|
|
| @@ -658,10 +687,10 @@ 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, "Invalid sessionId");
|
| + action->result()->completeWithError(WebContentDecryptionModuleExceptionTypeError, 0, "Invalid sessionId");
|
| return;
|
| }
|
|
|
|
|