Chromium Code Reviews| 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..9e26a24f79454b0666222801cc94a8ee6bcd288d 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,22 @@ static bool isValidSessionId(const String& sessionId) { |
| return true; |
| } |
| +static bool IsPersistentSessionType(WebEncryptedMediaSessionType sessionType) { |
|
xhwang
2016/10/17 23:13:55
Add a comment that this implements "5.1.1 Is persi
jrummell
2016/10/18 00:50:39
Done.
|
| + 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 +128,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 +451,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 +500,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 +532,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 +586,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 +668,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 +716,18 @@ void MediaKeySession::actionTimerFired(TimerBase*) { |
| switch (action->getType()) { |
| case PendingAction::GenerateRequest: |
|
xhwang
2016/10/17 23:13:55
nit: Not related to this CL. I wonder whether we s
jrummell
2016/10/18 00:50:39
Good idea. Next CL.
|
| - // 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 +741,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; |
| } |