Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (C) 2013 Apple Inc. All rights reserved. | 2 * Copyright (C) 2013 Apple Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
| 6 * are met: | 6 * are met: |
| 7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
| 8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
| 9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
| 10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 31 #include "bindings/core/v8/ScriptPromiseResolver.h" | 31 #include "bindings/core/v8/ScriptPromiseResolver.h" |
| 32 #include "bindings/core/v8/ScriptState.h" | 32 #include "bindings/core/v8/ScriptState.h" |
| 33 #include "core/dom/ExceptionCode.h" | 33 #include "core/dom/ExceptionCode.h" |
| 34 #include "core/events/Event.h" | 34 #include "core/events/Event.h" |
| 35 #include "core/events/GenericEventQueue.h" | 35 #include "core/events/GenericEventQueue.h" |
| 36 #include "core/html/MediaKeyError.h" | 36 #include "core/html/MediaKeyError.h" |
| 37 #include "modules/encryptedmedia/MediaKeyMessageEvent.h" | 37 #include "modules/encryptedmedia/MediaKeyMessageEvent.h" |
| 38 #include "modules/encryptedmedia/MediaKeys.h" | 38 #include "modules/encryptedmedia/MediaKeys.h" |
| 39 #include "modules/encryptedmedia/SimpleContentDecryptionModuleResult.h" | 39 #include "modules/encryptedmedia/SimpleContentDecryptionModuleResult.h" |
| 40 #include "platform/ContentDecryptionModuleResult.h" | 40 #include "platform/ContentDecryptionModuleResult.h" |
| 41 #include "platform/ContentType.h" | |
| 41 #include "platform/Logging.h" | 42 #include "platform/Logging.h" |
| 43 #include "platform/MIMETypeRegistry.h" | |
| 42 #include "platform/Timer.h" | 44 #include "platform/Timer.h" |
| 43 #include "public/platform/WebContentDecryptionModule.h" | 45 #include "public/platform/WebContentDecryptionModule.h" |
| 44 #include "public/platform/WebContentDecryptionModuleException.h" | 46 #include "public/platform/WebContentDecryptionModuleException.h" |
| 45 #include "public/platform/WebContentDecryptionModuleSession.h" | 47 #include "public/platform/WebContentDecryptionModuleSession.h" |
| 46 #include "public/platform/WebString.h" | 48 #include "public/platform/WebString.h" |
| 47 #include "public/platform/WebURL.h" | 49 #include "public/platform/WebURL.h" |
| 48 #include "wtf/ArrayBuffer.h" | 50 #include "wtf/ArrayBuffer.h" |
| 49 #include "wtf/ArrayBufferView.h" | 51 #include "wtf/ArrayBufferView.h" |
| 52 #include <cmath> | |
| 50 | 53 |
| 51 namespace blink { | 54 namespace blink { |
| 52 | 55 |
| 56 static bool isKeySystemSupportedWithInitDataType(const String& keySystem, const String& initDataType) | |
| 57 { | |
| 58 ASSERT(!keySystem.isEmpty()); | |
| 59 | |
| 60 // FIXME: initDataType != contentType. Implement this properly. | |
| 61 // http://crbug.com/385874. | |
| 62 String contentType = initDataType; | |
| 63 if (initDataType == "webm") { | |
| 64 contentType = "video/webm"; | |
| 65 } else if (initDataType == "cenc") { | |
| 66 contentType = "video/mp4"; | |
| 67 } | |
| 68 | |
| 69 ContentType type(contentType); | |
| 70 String codecs = type.parameter("codecs"); | |
|
ddorwin
2014/09/09 00:44:35
nit: Do we need the local variable?
jrummell
2014/09/09 19:55:59
Done.
| |
| 71 return MIMETypeRegistry::isSupportedEncryptedMediaMIMEType(keySystem, type.t ype(), codecs); | |
| 72 } | |
| 73 | |
| 53 // A class holding a pending action. | 74 // A class holding a pending action. |
| 54 class MediaKeySession::PendingAction : public GarbageCollectedFinalized<MediaKey Session::PendingAction> { | 75 class MediaKeySession::PendingAction : public GarbageCollectedFinalized<MediaKey Session::PendingAction> { |
| 55 public: | 76 public: |
| 56 enum Type { | 77 enum Type { |
| 78 GenerateRequest, | |
| 57 Update, | 79 Update, |
| 58 Release, | 80 Release |
| 59 Message | |
| 60 }; | 81 }; |
| 61 | 82 |
| 62 Type type() const { return m_type; } | 83 Type type() const { return m_type; } |
| 63 | 84 |
| 64 const Persistent<ContentDecryptionModuleResult> result() const | 85 const Persistent<ContentDecryptionModuleResult> result() const |
| 65 { | 86 { |
| 66 ASSERT(m_type == Update || m_type == Release); | |
| 67 return m_result; | 87 return m_result; |
| 68 } | 88 } |
| 69 | 89 |
| 70 const RefPtr<ArrayBuffer> data() const | 90 const RefPtr<ArrayBuffer> data() const |
| 71 { | 91 { |
| 72 ASSERT(m_type == Update); | 92 ASSERT(m_type == GenerateRequest || m_type == Update); |
| 73 return m_data; | 93 return m_data; |
| 74 } | 94 } |
| 75 | 95 |
| 76 RefPtrWillBeRawPtr<Event> event() | 96 const String& initDataType() const |
| 77 { | 97 { |
| 78 ASSERT(m_type == Message); | 98 ASSERT(m_type == GenerateRequest); |
| 79 return m_event; | 99 return m_initDataType; |
| 100 } | |
| 101 | |
| 102 static PendingAction* CreatePendingGenerateRequest(ContentDecryptionModuleRe sult* result, const String& initDataType, PassRefPtr<ArrayBuffer> initData) | |
| 103 { | |
| 104 ASSERT(result); | |
| 105 ASSERT(initData); | |
| 106 return new PendingAction(GenerateRequest, result, initDataType, initData ); | |
| 80 } | 107 } |
| 81 | 108 |
| 82 static PendingAction* CreatePendingUpdate(ContentDecryptionModuleResult* res ult, PassRefPtr<ArrayBuffer> data) | 109 static PendingAction* CreatePendingUpdate(ContentDecryptionModuleResult* res ult, PassRefPtr<ArrayBuffer> data) |
| 83 { | 110 { |
| 84 ASSERT(result); | 111 ASSERT(result); |
| 85 ASSERT(data); | 112 ASSERT(data); |
| 86 return new PendingAction(Update, result, data); | 113 return new PendingAction(Update, result, String(), data); |
| 87 } | 114 } |
| 88 | 115 |
| 89 static PendingAction* CreatePendingRelease(ContentDecryptionModuleResult* re sult) | 116 static PendingAction* CreatePendingRelease(ContentDecryptionModuleResult* re sult) |
| 90 { | 117 { |
| 91 ASSERT(result); | 118 ASSERT(result); |
| 92 return new PendingAction(Release, result, PassRefPtr<ArrayBuffer>()); | 119 return new PendingAction(Release, result, String(), PassRefPtr<ArrayBuff er>()); |
| 93 } | |
| 94 | |
| 95 static PendingAction* CreatePendingMessage(PassRefPtrWillBeRawPtr<Event> eve nt) | |
| 96 { | |
| 97 ASSERT(event); | |
| 98 return new PendingAction(Message, event); | |
| 99 } | 120 } |
| 100 | 121 |
| 101 ~PendingAction() | 122 ~PendingAction() |
| 102 { | 123 { |
| 103 } | 124 } |
| 104 | 125 |
| 105 void trace(Visitor* visitor) | 126 void trace(Visitor* visitor) |
| 106 { | 127 { |
| 107 visitor->trace(m_result); | 128 visitor->trace(m_result); |
| 108 visitor->trace(m_event); | |
| 109 } | 129 } |
| 110 | 130 |
| 111 private: | 131 private: |
| 112 PendingAction(Type type, ContentDecryptionModuleResult* result, PassRefPtr<A rrayBuffer> data) | 132 PendingAction(Type type, ContentDecryptionModuleResult* result, const String & initDataType, PassRefPtr<ArrayBuffer> data) |
| 113 : m_type(type) | 133 : m_type(type) |
| 114 , m_result(result) | 134 , m_result(result) |
| 135 , m_initDataType(initDataType) | |
| 115 , m_data(data) | 136 , m_data(data) |
| 116 { | 137 { |
| 117 } | 138 } |
| 118 | 139 |
| 119 PendingAction(Type type, PassRefPtrWillBeRawPtr<Event> event) | |
| 120 : m_type(type) | |
| 121 , m_event(event) | |
| 122 { | |
| 123 } | |
| 124 | |
| 125 const Type m_type; | 140 const Type m_type; |
| 126 const Member<ContentDecryptionModuleResult> m_result; | 141 const Member<ContentDecryptionModuleResult> m_result; |
| 142 const String m_initDataType; | |
| 127 const RefPtr<ArrayBuffer> m_data; | 143 const RefPtr<ArrayBuffer> m_data; |
| 128 const RefPtrWillBeMember<Event> m_event; | |
| 129 }; | 144 }; |
| 130 | 145 |
| 131 // This class allows a MediaKeySession object to be created asynchronously. | 146 // This class wraps the promise resolver used when initializing a new session |
| 132 class MediaKeySessionInitializer : public ScriptPromiseResolver { | 147 // and is passed to Chromium to fullfill the promise. This implementation of |
| 133 WTF_MAKE_NONCOPYABLE(MediaKeySessionInitializer); | 148 // completeWithSession() will resolve the promise with undefined, while |
|
ddorwin
2014/09/09 00:44:35
"undefined" is not specified by the spec. Please a
jrummell
2014/09/09 19:55:59
I asked yhirano@ a while back, and he responded "c
| |
| 149 // completeWithError() will reject the promise with an exception. complete() | |
| 150 // is not expected to be called, and will reject the promise. | |
| 151 class NewSessionResult : public ContentDecryptionModuleResult { | |
| 152 public: | |
| 153 NewSessionResult(ScriptState* scriptState, MediaKeySession* session) | |
| 154 : m_resolver(ScriptPromiseResolver::create(scriptState)) | |
| 155 , m_session(session) | |
| 156 { | |
| 157 WTF_LOG(Media, "NewSessionResult(%p)", this); | |
| 158 } | |
| 134 | 159 |
| 135 public: | 160 ~NewSessionResult() |
|
ddorwin
2014/09/09 00:44:36
If any superclass's destructor is virtual, make th
jrummell
2014/09/09 19:55:59
Done.
| |
| 136 static ScriptPromise create(ScriptState*, MediaKeys*, const String& initData Type, PassRefPtr<ArrayBuffer> initData, const String& sessionType); | |
| 137 virtual ~MediaKeySessionInitializer(); | |
| 138 | |
| 139 void completeWithSession(WebContentDecryptionModuleResult::SessionStatus); | |
| 140 void completeWithDOMException(ExceptionCode, const String& errorMessage); | |
| 141 | |
| 142 private: | |
| 143 MediaKeySessionInitializer(ScriptState*, MediaKeys*, const String& initDataT ype, PassRefPtr<ArrayBuffer> initData, const String& sessionType); | |
| 144 void timerFired(Timer<MediaKeySessionInitializer>*); | |
| 145 | |
| 146 Persistent<MediaKeys> m_mediaKeys; | |
| 147 OwnPtr<WebContentDecryptionModuleSession> m_cdmSession; | |
| 148 | |
| 149 // The next 3 values are simply the initialization data saved so that the | |
| 150 // asynchronous creation has the data needed. | |
| 151 String m_initDataType; | |
| 152 RefPtr<ArrayBuffer> m_initData; | |
| 153 String m_sessionType; | |
| 154 | |
| 155 Timer<MediaKeySessionInitializer> m_timer; | |
| 156 }; | |
| 157 | |
| 158 // Represents the result used when a new WebContentDecryptionModuleSession | |
| 159 // object has been created. Needed as MediaKeySessionInitializer can't be both | |
| 160 // a ScriptPromiseResolver and ContentDecryptionModuleResult at the same time. | |
| 161 class NewMediaKeySessionResult FINAL : public ContentDecryptionModuleResult { | |
| 162 public: | |
| 163 NewMediaKeySessionResult(MediaKeySessionInitializer* initializer) | |
| 164 : m_initializer(initializer) | |
| 165 { | 161 { |
| 162 WTF_LOG(Media, "~NewSessionResult(%p)", this); | |
| 166 } | 163 } |
| 167 | 164 |
| 168 // ContentDecryptionModuleResult implementation. | 165 // ContentDecryptionModuleResult implementation. |
| 169 virtual void complete() OVERRIDE | 166 virtual void complete() OVERRIDE |
| 170 { | 167 { |
| 171 ASSERT_NOT_REACHED(); | 168 ASSERT_NOT_REACHED(); |
| 172 m_initializer->completeWithDOMException(InvalidStateError, "Unexpected c ompletion."); | 169 completeWithDOMException(InvalidStateError, "Unexpected completion."); |
| 173 } | 170 } |
| 174 | 171 |
| 175 virtual void completeWithSession(WebContentDecryptionModuleResult::SessionSt atus status) OVERRIDE | 172 virtual void completeWithSession(WebContentDecryptionModuleResult::SessionSt atus status) OVERRIDE |
| 176 { | 173 { |
| 177 m_initializer->completeWithSession(status); | 174 if (status != WebContentDecryptionModuleResult::NewSession) { |
| 175 ASSERT_NOT_REACHED(); | |
| 176 completeWithDOMException(InvalidStateError, "Unexpected completion." ); | |
| 177 } | |
| 178 | |
| 179 m_session->finishGenerateRequest(); | |
| 180 m_resolver->resolve(V8UndefinedType()); | |
|
ddorwin
2014/09/09 00:44:35
See the comment on 148.
jrummell
2014/09/09 19:55:59
Added comment.
| |
| 181 m_resolver.clear(); | |
| 178 } | 182 } |
| 179 | 183 |
| 180 virtual void completeWithError(WebContentDecryptionModuleException code, uns igned long systemCode, const WebString& message) OVERRIDE | 184 virtual void completeWithError(WebContentDecryptionModuleException exception Code, unsigned long systemCode, const WebString& errorMessage) OVERRIDE |
| 181 { | 185 { |
| 182 m_initializer->completeWithDOMException(WebCdmExceptionToExceptionCode(c ode), message); | 186 completeWithDOMException(WebCdmExceptionToExceptionCode(exceptionCode), errorMessage); |
| 187 } | |
| 188 | |
| 189 // It is only valid to call this before completion. | |
| 190 ScriptPromise promise() { return m_resolver->promise(); } | |
| 191 | |
| 192 void trace(Visitor* visitor) | |
| 193 { | |
| 194 visitor->trace(m_session); | |
| 195 ContentDecryptionModuleResult::trace(visitor); | |
| 183 } | 196 } |
| 184 | 197 |
| 185 private: | 198 private: |
| 186 MediaKeySessionInitializer* m_initializer; | 199 // Reject the promise with a DOMException. |
| 200 void completeWithDOMException(ExceptionCode code, const String& errorMessage ) | |
| 201 { | |
| 202 m_resolver->reject(DOMException::create(code, errorMessage)); | |
| 203 m_resolver.clear(); | |
| 204 } | |
| 205 | |
| 206 RefPtr<ScriptPromiseResolver> m_resolver; | |
|
ddorwin
2014/09/09 00:44:36
OOC, why is this still a RefPtr?
jrummell
2014/09/09 19:55:58
ScriptPromiseResolver::create() returns PassRefPtr
| |
| 207 Member<MediaKeySession> m_session; | |
| 187 }; | 208 }; |
| 188 | 209 |
| 189 ScriptPromise MediaKeySessionInitializer::create(ScriptState* scriptState, Media Keys* mediaKeys, const String& initDataType, PassRefPtr<ArrayBuffer> initData, c onst String& sessionType) | 210 MediaKeySession* MediaKeySession::create(ScriptState* scriptState, MediaKeys* me diaKeys, const String& sessionType) |
| 190 { | 211 { |
| 191 RefPtr<MediaKeySessionInitializer> initializer = adoptRef(new MediaKeySessio nInitializer(scriptState, mediaKeys, initDataType, initData, sessionType)); | 212 RefPtrWillBeRawPtr<MediaKeySession> session = adoptRefCountedGarbageCollecte dWillBeNoop(new MediaKeySession(scriptState, mediaKeys, sessionType)); |
| 192 initializer->suspendIfNeeded(); | 213 session->suspendIfNeeded(); |
| 193 initializer->keepAliveWhilePending(); | 214 return session.get(); |
| 194 return initializer->promise(); | |
| 195 } | 215 } |
| 196 | 216 |
| 197 MediaKeySessionInitializer::MediaKeySessionInitializer(ScriptState* scriptState, MediaKeys* mediaKeys, const String& initDataType, PassRefPtr<ArrayBuffer> initD ata, const String& sessionType) | 217 MediaKeySession::MediaKeySession(ScriptState* scriptState, MediaKeys* keys, cons t String& sessionType) |
|
ddorwin
2014/09/09 00:44:36
nit: media_keys. keys seems a bit ambiguous.
jrummell
2014/09/09 19:55:58
Done.
| |
| 198 : ScriptPromiseResolver(scriptState) | 218 : ActiveDOMObject(scriptState->executionContext()) |
| 199 , m_mediaKeys(mediaKeys) | |
| 200 , m_initDataType(initDataType) | |
| 201 , m_initData(initData) | |
| 202 , m_sessionType(sessionType) | |
| 203 , m_timer(this, &MediaKeySessionInitializer::timerFired) | |
| 204 { | |
| 205 WTF_LOG(Media, "MediaKeySessionInitializer::MediaKeySessionInitializer"); | |
| 206 | |
| 207 // Start the timer so that MediaKeySession can be created asynchronously. | |
| 208 m_timer.startOneShot(0, FROM_HERE); | |
| 209 } | |
| 210 | |
| 211 MediaKeySessionInitializer::~MediaKeySessionInitializer() | |
| 212 { | |
| 213 WTF_LOG(Media, "MediaKeySessionInitializer::~MediaKeySessionInitializer"); | |
| 214 } | |
| 215 | |
| 216 void MediaKeySessionInitializer::timerFired(Timer<MediaKeySessionInitializer>*) | |
| 217 { | |
| 218 WTF_LOG(Media, "MediaKeySessionInitializer::timerFired"); | |
| 219 | |
| 220 // Continue MediaKeys::createSession() at step 7. | |
| 221 // 7.1 Let request be null. (Request provided by cdm in message event). | |
| 222 // 7.2 Let default URL be null. (Also provided by cdm in message event). | |
| 223 | |
| 224 // 7.3 Let cdm be the cdm loaded in create(). | |
| 225 WebContentDecryptionModule* cdm = m_mediaKeys->contentDecryptionModule(); | |
| 226 | |
| 227 // 7.4 Use the cdm to execute the following steps: | |
| 228 // 7.4.1 If the init data is not valid for initDataType, reject promise | |
| 229 // with a new DOMException whose name is "InvalidAccessError". | |
| 230 // 7.4.2 If the init data is not supported by the cdm, reject promise with | |
| 231 // a new DOMException whose name is "NotSupportedError". | |
| 232 // 7.4.3 Let request be a request (e.g. a license request) generated based | |
| 233 // on the init data, which is interpreteted per initDataType, and | |
| 234 // sessionType. If sessionType is "temporary", the request is for a | |
| 235 // temporary non-persisted license. If sessionType is "persistent", | |
| 236 // the request is for a persistable license. | |
| 237 // 7.4.4 If the init data indicates a default URL, let default URL be | |
| 238 // that URL. The URL may be validated and/or normalized. | |
| 239 m_cdmSession = adoptPtr(cdm->createSession()); | |
| 240 NewMediaKeySessionResult* result = new NewMediaKeySessionResult(this); | |
| 241 m_cdmSession->initializeNewSession(m_initDataType, static_cast<unsigned char *>(m_initData->data()), m_initData->byteLength(), m_sessionType, result->result( )); | |
| 242 | |
| 243 WTF_LOG(Media, "MediaKeySessionInitializer::timerFired done"); | |
| 244 // Note: As soon as the promise is resolved (or rejected), the | |
| 245 // ScriptPromiseResolver object (|this|) is freed. So if | |
| 246 // initializeNewSession() is synchronous, access to any members will crash. | |
| 247 } | |
| 248 | |
| 249 void MediaKeySessionInitializer::completeWithSession(WebContentDecryptionModuleR esult::SessionStatus status) | |
| 250 { | |
| 251 WTF_LOG(Media, "MediaKeySessionInitializer::completeWithSession"); | |
| 252 | |
| 253 switch (status) { | |
| 254 case WebContentDecryptionModuleResult::NewSession: { | |
| 255 // Resume MediaKeys::createSession(). | |
| 256 // 7.5 Let the session ID be a unique Session ID string. It may be | |
| 257 // obtained from cdm (it is). | |
| 258 // 7.6 Let session be a new MediaKeySession object, and initialize it. | |
| 259 // (Object was created previously, complete the steps for 7.6). | |
| 260 RefPtrWillBeRawPtr<MediaKeySession> session = adoptRefCountedGarbageColl ectedWillBeNoop(new MediaKeySession(executionContext(), m_mediaKeys, m_cdmSessio n.release())); | |
| 261 session->suspendIfNeeded(); | |
| 262 | |
| 263 // 7.7 If any of the preceding steps failed, reject promise with a | |
| 264 // new DOMException whose name is the appropriate error name | |
| 265 // and that has an appropriate message. | |
| 266 // (Implemented by CDM/Chromium calling completeWithError()). | |
| 267 | |
| 268 // 7.8 Add an entry for the value of the sessionId attribute to the | |
| 269 // list of active session IDs for this object. | |
| 270 // (Implemented in SessionIdAdapter). | |
| 271 | |
| 272 // 7.9 Run the Queue a "message" Event algorithm on the session, | |
| 273 // providing request and default URL. | |
| 274 // (Done by the CDM). | |
| 275 | |
| 276 // 7.10 Resolve promise with session. | |
| 277 resolve(session.release()); | |
| 278 WTF_LOG(Media, "MediaKeySessionInitializer::completeWithSession done w/s ession"); | |
| 279 return; | |
| 280 } | |
| 281 | |
| 282 case WebContentDecryptionModuleResult::SessionNotFound: | |
| 283 // Step 4.7.1 of MediaKeys::loadSession(): If there is no data | |
| 284 // stored for the sessionId in the origin, resolve promise with | |
| 285 // undefined. | |
| 286 resolve(V8UndefinedType()); | |
| 287 WTF_LOG(Media, "MediaKeySessionInitializer::completeWithSession done w/u ndefined"); | |
| 288 return; | |
| 289 | |
| 290 case WebContentDecryptionModuleResult::SessionAlreadyExists: | |
| 291 // If a session already exists, resolve the promise with null. | |
| 292 resolve(V8NullType()); | |
| 293 WTF_LOG(Media, "MediaKeySessionInitializer::completeWithSession done w/n ull"); | |
| 294 return; | |
| 295 } | |
| 296 ASSERT_NOT_REACHED(); | |
| 297 } | |
| 298 | |
| 299 void MediaKeySessionInitializer::completeWithDOMException(ExceptionCode code, co nst String& errorMessage) | |
| 300 { | |
| 301 WTF_LOG(Media, "MediaKeySessionInitializer::completeWithDOMException"); | |
| 302 reject(DOMException::create(code, errorMessage)); | |
| 303 } | |
| 304 | |
| 305 ScriptPromise MediaKeySession::create(ScriptState* scriptState, MediaKeys* media Keys, const String& initDataType, PassRefPtr<ArrayBuffer> initData, const String & sessionType) | |
| 306 { | |
| 307 // Since creation is done asynchronously, use MediaKeySessionInitializer | |
| 308 // to do it. | |
| 309 return MediaKeySessionInitializer::create(scriptState, mediaKeys, initDataTy pe, initData, sessionType); | |
| 310 } | |
| 311 | |
| 312 MediaKeySession::MediaKeySession(ExecutionContext* context, MediaKeys* keys, Pas sOwnPtr<WebContentDecryptionModuleSession> cdmSession) | |
| 313 : ActiveDOMObject(context) | |
| 314 , m_keySystem(keys->keySystem()) | 219 , m_keySystem(keys->keySystem()) |
|
ddorwin
2014/09/09 00:44:36
We should not be saving this - it's no longer a me
jrummell
2014/09/09 19:55:58
Will be done in a CL that cleans up this class.
| |
| 315 , m_asyncEventQueue(GenericEventQueue::create(this)) | 220 , m_asyncEventQueue(GenericEventQueue::create(this)) |
| 316 , m_session(cdmSession) | 221 , m_mediaKeys(keys) |
| 317 , m_keys(keys) | 222 , m_expiration(nan("")) |
|
ddorwin
2014/09/09 00:44:35
Why does this take a string?
FYI, I also see NaN()
jrummell
2014/09/09 19:55:59
I just did a search for C++ nan, and it pointed to
ddorwin
2014/09/09 21:35:24
double.h is in Blink's WTF IIRC.
jrummell
2014/09/10 01:18:24
But that is for "class Double", which is different
| |
| 223 , m_sessionType(sessionType) | |
| 224 , m_uninitialized(true) | |
| 225 , m_callable(false) | |
| 318 , m_isClosed(false) | 226 , m_isClosed(false) |
| 319 , m_closedPromise(new ClosedPromise(context, this, ClosedPromise::Closed)) | 227 , m_closedPromise(new ClosedPromise(scriptState->executionContext(), this, C losedPromise::Closed)) |
| 320 , m_actionTimer(this, &MediaKeySession::actionTimerFired) | 228 , m_actionTimer(this, &MediaKeySession::actionTimerFired) |
| 321 { | 229 { |
| 322 WTF_LOG(Media, "MediaKeySession(%p)::MediaKeySession", this); | 230 WTF_LOG(Media, "MediaKeySession(%p)::MediaKeySession", this); |
| 323 ScriptWrappable::init(this); | 231 ScriptWrappable::init(this); |
| 324 m_session->setClientInterface(this); | |
| 325 | 232 |
| 326 // Resume MediaKeys::createSession() at step 7.6. | 233 // MediaKeys::createSession(), step 2. |
| 327 // 7.6.1 Set the error attribute to null. | 234 // 2.1 Let the sessionId attribute be the empty string. |
| 328 ASSERT(!m_error); | 235 ASSERT(sessionId().isEmpty()); |
|
ddorwin
2014/09/09 00:44:36
nit: If we are going to keep these as members, we
jrummell
2014/09/09 19:55:59
Now that m_session is created above, member is gon
| |
| 329 | 236 |
| 330 // 7.6.2 Set the sessionId attribute to session ID. | 237 // 2.2 Let the expiration attribute be NaN. |
| 331 ASSERT(!sessionId().isEmpty()); | 238 ASSERT(isnan(expiration())); |
| 332 | 239 |
| 333 // 7.6.3 Let expiration be NaN. | 240 // 2.3 Let the closed attribute be a new promise. |
| 334 // 7.6.4 Let closed be a new promise. | 241 ASSERT(!closed(scriptState).isUndefinedOrNull()); |
| 335 // 7.6.5 Let the session type be sessionType. | 242 |
| 336 // FIXME: Implement the previous 3 values. | 243 // 2.4 Let the session type be sessionType. |
| 244 ASSERT(sessionType == m_sessionType); | |
| 245 | |
| 246 // 2.5 Let uninitialized be true. | |
| 247 ASSERT(m_uninitialized); | |
| 248 | |
| 249 // 2.6 Let callable be false. | |
| 250 ASSERT(!m_callable); | |
| 337 } | 251 } |
| 338 | 252 |
| 339 MediaKeySession::~MediaKeySession() | 253 MediaKeySession::~MediaKeySession() |
| 340 { | 254 { |
| 341 WTF_LOG(Media, "MediaKeySession(%p)::~MediaKeySession", this); | 255 WTF_LOG(Media, "MediaKeySession(%p)::~MediaKeySession", this); |
| 342 m_session.clear(); | 256 m_session.clear(); |
| 343 #if !ENABLE(OILPAN) | 257 #if !ENABLE(OILPAN) |
| 344 // MediaKeySession and m_asyncEventQueue always become unreachable | 258 // MediaKeySession and m_asyncEventQueue always become unreachable |
| 345 // together. So MediaKeySession and m_asyncEventQueue are destructed in the | 259 // together. So MediaKeySession and m_asyncEventQueue are destructed in the |
| 346 // same GC. We don't need to call cancelAllEvents explicitly in Oilpan. | 260 // same GC. We don't need to call cancelAllEvents explicitly in Oilpan. |
| 347 m_asyncEventQueue->cancelAllEvents(); | 261 m_asyncEventQueue->cancelAllEvents(); |
| 348 #endif | 262 #endif |
| 349 } | 263 } |
| 350 | 264 |
| 351 void MediaKeySession::setError(MediaKeyError* error) | 265 void MediaKeySession::setError(MediaKeyError* error) |
| 352 { | 266 { |
| 353 m_error = error; | 267 m_error = error; |
| 354 } | 268 } |
| 355 | 269 |
| 356 String MediaKeySession::sessionId() const | |
| 357 { | |
| 358 return m_session->sessionId(); | |
| 359 } | |
| 360 | |
| 361 ScriptPromise MediaKeySession::closed(ScriptState* scriptState) | 270 ScriptPromise MediaKeySession::closed(ScriptState* scriptState) |
| 362 { | 271 { |
| 363 return m_closedPromise->promise(scriptState->world()); | 272 return m_closedPromise->promise(scriptState->world()); |
| 364 } | 273 } |
| 365 | 274 |
| 275 ScriptPromise MediaKeySession::generateRequest(ScriptState* scriptState, const S tring& initDataType, ArrayBuffer* initData) | |
| 276 { | |
| 277 RefPtr<ArrayBuffer> initDataCopy = ArrayBuffer::create(initData->data(), ini tData->byteLength()); | |
| 278 return generateRequestInternal(scriptState, initDataType, initDataCopy.relea se()); | |
| 279 } | |
| 280 | |
| 281 ScriptPromise MediaKeySession::generateRequest(ScriptState* scriptState, const S tring& initDataType, ArrayBufferView* initData) | |
| 282 { | |
| 283 RefPtr<ArrayBuffer> initDataCopy = ArrayBuffer::create(initData->baseAddress (), initData->byteLength()); | |
| 284 return generateRequestInternal(scriptState, initDataType, initDataCopy.relea se()); | |
| 285 } | |
| 286 | |
| 287 ScriptPromise MediaKeySession::generateRequestInternal(ScriptState* scriptState, const String& initDataType, PassRefPtr<ArrayBuffer> initData) | |
| 288 { | |
| 289 WTF_LOG(Media, "MediaKeySession(%p)::generateRequest %s", this, initDataType .ascii().data()); | |
| 290 | |
| 291 // From https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/e ncrypted-media.html#dom-generaterequest: | |
|
ddorwin
2014/09/09 00:44:35
nit: Why the URL here? I don't think we have these
jrummell
2014/09/09 19:55:59
Just followed the existing pattern in update() and
| |
| 292 // The generateRequest(initDataType, initData) method creates a new session | |
| 293 // for the specified initData. It must run the following steps: | |
| 294 | |
| 295 // 1. If this object's uninitialized value is false, return a promise reject ed | |
| 296 // with a new DOMException whose name is "InvalidStateError". | |
| 297 if (!m_uninitialized) { | |
| 298 return ScriptPromise::rejectWithDOMException( | |
| 299 scriptState, DOMException::create(InvalidStateError, "The session al ready exists.")); | |
|
ddorwin
2014/09/09 00:44:36
s/exists/initialized/
OR s/already exists/has alre
jrummell
2014/09/09 19:55:58
Done.
| |
| 300 } | |
| 301 | |
| 302 // 2. Let this object's uninitialized be false. | |
| 303 m_uninitialized = false; | |
| 304 | |
| 305 // 3. If initDataType is an empty string, return a promise rejected with a | |
| 306 // new DOMException whose name is "InvalidAccessError". | |
| 307 if (initDataType.isEmpty()) { | |
| 308 return ScriptPromise::rejectWithDOMException( | |
| 309 scriptState, DOMException::create(InvalidAccessError, "The initDataT ype parameter is empty.")); | |
| 310 } | |
| 311 | |
| 312 // 4. If initData is an empty array, return a promise rejected with a new | |
| 313 // DOMException whose name is"InvalidAccessError". | |
| 314 if (!initData->byteLength()) { | |
| 315 return ScriptPromise::rejectWithDOMException( | |
| 316 scriptState, DOMException::create(InvalidAccessError, "The initData parameter is empty.")); | |
| 317 } | |
| 318 | |
| 319 // 5. Let media keys be the MediaKeys object that created this object. | |
| 320 // (Done in constructor.) | |
|
ddorwin
2014/09/09 00:44:36
nit: This isn't actually about saving it as a memb
jrummell
2014/09/09 19:55:58
Done.
| |
| 321 | |
| 322 // 6. If the content decryption module corresponding to media keys's keySyst em | |
| 323 // attribute does not support initDataType as an initialization data type , | |
| 324 // return a promise rejected with a new DOMException whose name is | |
| 325 // "NotSupportedError". String comparison is case-sensitive. | |
| 326 if (!isKeySystemSupportedWithInitDataType(m_keySystem, initDataType)) { | |
|
ddorwin
2014/09/09 00:44:35
Per earlier comments and the algorithm, this shoul
jrummell
2014/09/09 19:55:59
But then we would have the issue that it is possib
ddorwin
2014/09/09 21:35:24
Good point! I probably need to update the spec too
| |
| 327 return ScriptPromise::rejectWithDOMException( | |
| 328 scriptState, DOMException::create(NotSupportedError, "The initializa tion data type '" + initDataType + "' is not supported by the key system.")); | |
| 329 } | |
| 330 | |
| 331 // 7. Let init data be a copy of the contents of the initData parameter. | |
| 332 // (Done before calling this method.) | |
| 333 | |
| 334 // 8. Let session type be this object's session type. | |
| 335 // (Done in constructor.) | |
| 336 | |
| 337 // 9. Let promise be a new promise. | |
| 338 NewSessionResult* result = new NewSessionResult(scriptState, this); | |
| 339 ScriptPromise promise = result->promise(); | |
| 340 | |
| 341 // 10. Run the following steps asynchronously (documented in | |
| 342 // actionTimerFired()) | |
| 343 m_pendingActions.append(PendingAction::CreatePendingGenerateRequest(result, initDataType, initData)); | |
| 344 if (!m_actionTimer.isActive()) | |
|
ddorwin
2014/09/09 00:44:36
I think this is an ASSERT - nothing else should ha
jrummell
2014/09/09 19:55:59
Done.
| |
| 345 m_actionTimer.startOneShot(0, FROM_HERE); | |
| 346 | |
| 347 // 11. Return promise. | |
| 348 return promise; | |
| 349 } | |
| 350 | |
| 366 ScriptPromise MediaKeySession::update(ScriptState* scriptState, ArrayBuffer* res ponse) | 351 ScriptPromise MediaKeySession::update(ScriptState* scriptState, ArrayBuffer* res ponse) |
| 367 { | 352 { |
| 368 RefPtr<ArrayBuffer> responseCopy = ArrayBuffer::create(response->data(), res ponse->byteLength()); | 353 RefPtr<ArrayBuffer> responseCopy = ArrayBuffer::create(response->data(), res ponse->byteLength()); |
| 369 return updateInternal(scriptState, responseCopy.release()); | 354 return updateInternal(scriptState, responseCopy.release()); |
| 370 } | 355 } |
| 371 | 356 |
| 372 ScriptPromise MediaKeySession::update(ScriptState* scriptState, ArrayBufferView* response) | 357 ScriptPromise MediaKeySession::update(ScriptState* scriptState, ArrayBufferView* response) |
| 373 { | 358 { |
| 374 RefPtr<ArrayBuffer> responseCopy = ArrayBuffer::create(response->baseAddress (), response->byteLength()); | 359 RefPtr<ArrayBuffer> responseCopy = ArrayBuffer::create(response->baseAddress (), response->byteLength()); |
| 375 return updateInternal(scriptState, responseCopy.release()); | 360 return updateInternal(scriptState, responseCopy.release()); |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 449 // Resolving promises now run synchronously and may result in additional | 434 // Resolving promises now run synchronously and may result in additional |
| 450 // actions getting added to the queue. As a result, swap the queue to | 435 // actions getting added to the queue. As a result, swap the queue to |
| 451 // a local copy to avoid problems if this happens. | 436 // a local copy to avoid problems if this happens. |
| 452 HeapDeque<Member<PendingAction> > pendingActions; | 437 HeapDeque<Member<PendingAction> > pendingActions; |
| 453 pendingActions.swap(m_pendingActions); | 438 pendingActions.swap(m_pendingActions); |
| 454 | 439 |
| 455 while (!pendingActions.isEmpty()) { | 440 while (!pendingActions.isEmpty()) { |
| 456 PendingAction* action = pendingActions.takeFirst(); | 441 PendingAction* action = pendingActions.takeFirst(); |
| 457 | 442 |
| 458 switch (action->type()) { | 443 switch (action->type()) { |
| 444 case PendingAction::GenerateRequest: | |
| 445 WTF_LOG(Media, "MediaKeySession(%p)::actionTimerFired: GenerateReque st", this); | |
| 446 | |
| 447 { | |
| 448 // 10.1 Let request be null. | |
| 449 // 10.2 Let cdm be the CDM loaded during the initialization of | |
| 450 // media keys. | |
| 451 WebContentDecryptionModule* cdm = m_mediaKeys->contentDecryption Module(); | |
| 452 | |
| 453 // 10.3 Use the cdm to execute the following steps: | |
| 454 // 10.3.1 If the init data is not valid for initDataType, reject | |
| 455 // promise with a new DOMException whose name is | |
| 456 // "InvalidAccessError". | |
| 457 // 10.3.2 If the init data is not supported by the cdm, reject | |
| 458 // promise with a new DOMException whose name is | |
| 459 // "NotSupportedError". | |
| 460 // 10.3.3 Let request be a request (e.g. a license request) | |
| 461 // generated based on the init data, which is interpreted | |
| 462 // per initDataType, and session type. | |
| 463 m_session = adoptPtr(cdm->createSession()); | |
|
ddorwin
2014/09/09 00:44:36
We should do this during creation.
jrummell
2014/09/09 19:55:58
Done.
| |
| 464 m_session->setClientInterface(this); | |
| 465 m_session->initializeNewSession(action->initDataType(), static_c ast<unsigned char*>(action->data()->data()), action->data()->byteLength(), m_ses sionType, action->result()->result()); | |
| 466 } | |
| 467 | |
| 468 // Remainder of steps executed in finishGenerateRequest(), called | |
| 469 // when |result| is resolved. | |
| 470 break; | |
| 471 | |
| 459 case PendingAction::Update: | 472 case PendingAction::Update: |
| 460 WTF_LOG(Media, "MediaKeySession(%p)::actionTimerFired: Update", this ); | 473 WTF_LOG(Media, "MediaKeySession(%p)::actionTimerFired: Update", this ); |
| 461 // NOTE: Continued from step 4 of MediaKeySession::update(). | 474 // NOTE: Continued from step 4 of MediaKeySession::update(). |
| 462 // Continue the update call by passing message to the cdm. Once | 475 // Continue the update call by passing message to the cdm. Once |
| 463 // completed, it will resolve/reject the promise. | 476 // completed, it will resolve/reject the promise. |
| 464 m_session->update(static_cast<unsigned char*>(action->data()->data() ), action->data()->byteLength(), action->result()->result()); | 477 m_session->update(static_cast<unsigned char*>(action->data()->data() ), action->data()->byteLength(), action->result()->result()); |
| 465 break; | 478 break; |
| 479 | |
| 466 case PendingAction::Release: | 480 case PendingAction::Release: |
| 467 WTF_LOG(Media, "MediaKeySession(%p)::actionTimerFired: Release", thi s); | 481 WTF_LOG(Media, "MediaKeySession(%p)::actionTimerFired: Release", thi s); |
| 468 // NOTE: Continued from step 3 of MediaKeySession::release(). | 482 // NOTE: Continued from step 3 of MediaKeySession::release(). |
| 469 // 3.1 Let cdm be the cdm loaded in create(). | 483 // 3.1 Let cdm be the cdm loaded in create(). |
| 470 // 3.2 Use the cdm to execute the following steps: | 484 // 3.2 Use the cdm to execute the following steps: |
| 471 // 3.2.1 Process the close request. Do not remove stored session dat a. | 485 // 3.2.1 Process the close request. Do not remove stored session dat a. |
| 472 // 3.2.2 If the previous step caused the session to be closed, run t he | 486 // 3.2.2 If the previous step caused the session to be closed, run t he |
| 473 // Session Close algorithm on this object. | 487 // Session Close algorithm on this object. |
| 474 // 3.3 Resolve promise with undefined. | 488 // 3.3 Resolve promise with undefined. |
| 475 m_session->release(action->result()->result()); | 489 m_session->release(action->result()->result()); |
| 476 break; | 490 break; |
| 477 case PendingAction::Message: | |
| 478 WTF_LOG(Media, "MediaKeySession(%p)::actionTimerFired: Message", thi s); | |
| 479 m_asyncEventQueue->enqueueEvent(action->event().release()); | |
| 480 break; | |
| 481 } | 491 } |
| 482 } | 492 } |
| 483 } | 493 } |
| 484 | 494 |
| 495 void MediaKeySession::finishGenerateRequest() | |
| 496 { | |
| 497 // 10.4 Set the sessionId attribute to a unique Session ID string. | |
| 498 // It may be obtained from cdm. | |
| 499 m_sessionId = m_session->sessionId(); | |
|
ddorwin
2014/09/09 00:44:35
ASSERT(!empty())
Also, I'm not sure we should save
jrummell
2014/09/09 19:55:59
sessionId() now calls this function as needed.
| |
| 500 | |
| 501 // 10.5 If any of the preceding steps failed, reject promise with a new | |
| 502 // DOMException whose name is the appropriate error name. | |
| 503 // (Done by call to completeWithError()). | |
| 504 | |
| 505 // 10.6 Add an entry for the value of the sessionId attribute to | |
| 506 // media keys's list of active session IDs. | |
| 507 // FIXME: Is this required? | |
|
ddorwin
2014/09/09 00:44:35
Probably not: https://www.w3.org/Bugs/Public/show_
jrummell
2014/09/09 19:55:59
Acknowledged.
| |
| 508 | |
| 509 // 10.7 Run the Queue a "message" Event algorithm on the session, | |
| 510 // providing request and null. | |
| 511 // (Done by the CDM). | |
|
ddorwin
2014/09/09 00:44:35
Hmm. We need to somehow guarantee that this messag
jrummell
2014/09/09 19:55:59
Added ASSERT in message() to verify this.
| |
| 512 | |
| 513 // 10.8 Let this object's callable be true. | |
| 514 m_callable = true; | |
| 515 | |
| 516 // Additionally, we no longer need to keep MediaKeys around. Switch to | |
| 517 // WeakMember. | |
| 518 m_keys = m_mediaKeys; | |
| 519 m_mediaKeys.clear(); | |
| 520 } | |
| 521 | |
| 485 // Queue a task to fire a simple event named keymessage at the new object | 522 // Queue a task to fire a simple event named keymessage at the new object |
| 486 void MediaKeySession::message(const unsigned char* message, size_t messageLength , const WebURL& destinationURL) | 523 void MediaKeySession::message(const unsigned char* message, size_t messageLength , const WebURL& destinationURL) |
| 487 { | 524 { |
| 488 WTF_LOG(Media, "MediaKeySession(%p)::message", this); | 525 WTF_LOG(Media, "MediaKeySession(%p)::message", this); |
| 489 | 526 |
| 490 MediaKeyMessageEventInit init; | 527 MediaKeyMessageEventInit init; |
| 491 init.bubbles = false; | 528 init.bubbles = false; |
| 492 init.cancelable = false; | 529 init.cancelable = false; |
| 493 init.message = ArrayBuffer::create(static_cast<const void*>(message), messag eLength); | 530 init.message = ArrayBuffer::create(static_cast<const void*>(message), messag eLength); |
| 494 init.destinationURL = destinationURL.string(); | 531 init.destinationURL = destinationURL.string(); |
| 495 | 532 |
| 496 RefPtrWillBeRawPtr<MediaKeyMessageEvent> event = MediaKeyMessageEvent::creat e(EventTypeNames::message, init); | 533 RefPtrWillBeRawPtr<MediaKeyMessageEvent> event = MediaKeyMessageEvent::creat e(EventTypeNames::message, init); |
| 497 event->setTarget(this); | 534 event->setTarget(this); |
| 498 | |
| 499 if (!hasEventListeners()) { | |
| 500 // Since this event may be generated immediately after resolving the | |
| 501 // CreateSession() promise, it is possible that the JavaScript hasn't | |
| 502 // had time to run the .then() action and bind any necessary event | |
| 503 // handlers. If there are no event handlers connected, delay enqueuing | |
| 504 // this message to provide time for the JavaScript to run. This will | |
| 505 // also affect the (rare) case where there is no message handler | |
| 506 // attched during normal operation. | |
| 507 m_pendingActions.append(PendingAction::CreatePendingMessage(event.releas e())); | |
| 508 if (!m_actionTimer.isActive()) | |
| 509 m_actionTimer.startOneShot(0, FROM_HERE); | |
| 510 return; | |
| 511 } | |
| 512 | |
| 513 m_asyncEventQueue->enqueueEvent(event.release()); | 535 m_asyncEventQueue->enqueueEvent(event.release()); |
| 514 } | 536 } |
| 515 | 537 |
| 516 void MediaKeySession::ready() | 538 void MediaKeySession::ready() |
| 517 { | 539 { |
| 518 WTF_LOG(Media, "MediaKeySession(%p)::ready", this); | 540 WTF_LOG(Media, "MediaKeySession(%p)::ready", this); |
| 519 | 541 |
| 520 RefPtrWillBeRawPtr<Event> event = Event::create(EventTypeNames::ready); | 542 RefPtrWillBeRawPtr<Event> event = Event::create(EventTypeNames::ready); |
| 521 event->setTarget(this); | 543 event->setTarget(this); |
| 522 m_asyncEventQueue->enqueueEvent(event.release()); | 544 m_asyncEventQueue->enqueueEvent(event.release()); |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 618 m_actionTimer.stop(); | 640 m_actionTimer.stop(); |
| 619 m_pendingActions.clear(); | 641 m_pendingActions.clear(); |
| 620 m_asyncEventQueue->close(); | 642 m_asyncEventQueue->close(); |
| 621 } | 643 } |
| 622 | 644 |
| 623 void MediaKeySession::trace(Visitor* visitor) | 645 void MediaKeySession::trace(Visitor* visitor) |
| 624 { | 646 { |
| 625 visitor->trace(m_error); | 647 visitor->trace(m_error); |
| 626 visitor->trace(m_asyncEventQueue); | 648 visitor->trace(m_asyncEventQueue); |
| 627 visitor->trace(m_pendingActions); | 649 visitor->trace(m_pendingActions); |
| 650 visitor->trace(m_mediaKeys); | |
| 628 visitor->trace(m_keys); | 651 visitor->trace(m_keys); |
| 629 visitor->trace(m_closedPromise); | 652 visitor->trace(m_closedPromise); |
| 630 EventTargetWithInlineData::trace(visitor); | 653 EventTargetWithInlineData::trace(visitor); |
| 631 } | 654 } |
| 632 | 655 |
| 633 } // namespace blink | 656 } // namespace blink |
| OLD | NEW |