MediaKeySession.cpp (24423B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/dom/MediaKeySession.h" 8 9 #include <ctime> 10 #include <utility> 11 12 #include "GMPUtils.h" 13 #include "mozilla/AsyncEventDispatcher.h" 14 #include "mozilla/CDMProxy.h" 15 #include "mozilla/EMEUtils.h" 16 #include "mozilla/Encoding.h" 17 #include "mozilla/dom/HTMLMediaElement.h" 18 #include "mozilla/dom/KeyIdsInitDataBinding.h" 19 #include "mozilla/dom/MediaEncryptedEvent.h" 20 #include "mozilla/dom/MediaKeyError.h" 21 #include "mozilla/dom/MediaKeyMessageEvent.h" 22 #include "mozilla/dom/MediaKeyStatusMap.h" 23 #include "mozilla/dom/MediaKeySystemAccess.h" 24 #include "nsCycleCollectionParticipant.h" 25 #include "nsPrintfCString.h" 26 #include "psshparser/PsshParser.h" 27 28 namespace mozilla::dom { 29 30 NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaKeySession, DOMEventTargetHelper, 31 mMediaKeyError, mKeys, mKeyStatusMap, 32 mClosed) 33 34 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaKeySession) 35 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) 36 37 NS_IMPL_ADDREF_INHERITED(MediaKeySession, DOMEventTargetHelper) 38 NS_IMPL_RELEASE_INHERITED(MediaKeySession, DOMEventTargetHelper) 39 40 // Count of number of instances. Used to give each instance a 41 // unique token. 42 static uint32_t sMediaKeySessionNum = 0; 43 44 // Max length of keyId in EME "keyIds" or WebM init data format, as enforced 45 // by web platform tests. 46 static const uint32_t MAX_KEY_ID_LENGTH = 512; 47 48 // Max length of CENC PSSH init data tolerated, as enforced by web 49 // platform tests. 50 static const uint32_t MAX_CENC_INIT_DATA_LENGTH = 64 * 1024; 51 52 MediaKeySession::MediaKeySession(nsPIDOMWindowInner* aParent, MediaKeys* aKeys, 53 const nsAString& aKeySystem, 54 MediaKeySessionType aSessionType, 55 bool aHardwareDecryption, ErrorResult& aRv) 56 : DOMEventTargetHelper(aParent), 57 mKeys(aKeys), 58 mKeySystem(aKeySystem), 59 mSessionType(aSessionType), 60 mToken(sMediaKeySessionNum++), 61 mIsClosed(false), 62 mUninitialized(true), 63 mKeyStatusMap(new MediaKeyStatusMap(aParent)), 64 mExpiration(JS::GenericNaN()), 65 mHardwareDecryption(aHardwareDecryption), 66 mIsPrivateBrowsing( 67 aParent->GetExtantDoc() && 68 aParent->GetExtantDoc()->NodePrincipal()->GetPrivateBrowsingId() > 69 0) { 70 EME_LOG("MediaKeySession[%p,''] ctor", this); 71 72 MOZ_ASSERT(aParent); 73 if (aRv.Failed()) { 74 return; 75 } 76 mClosed = MakePromise(aRv, "MediaKeys.createSession"_ns); 77 } 78 79 void MediaKeySession::SetSessionId(const nsAString& aSessionId) { 80 EME_LOG("MediaKeySession[%p,'%s'] session Id set", this, 81 NS_ConvertUTF16toUTF8(aSessionId).get()); 82 83 if (NS_WARN_IF(!mSessionId.IsEmpty())) { 84 return; 85 } 86 mSessionId = aSessionId; 87 mKeys->OnSessionIdReady(this); 88 } 89 90 MediaKeySession::~MediaKeySession() { 91 EME_LOG("MediaKeySession[%p,'%s'] dtor", this, 92 NS_ConvertUTF16toUTF8(mSessionId).get()); 93 } 94 95 MediaKeyError* MediaKeySession::GetError() const { return mMediaKeyError; } 96 97 void MediaKeySession::GetSessionId(nsString& aSessionId) const { 98 aSessionId = GetSessionId(); 99 } 100 101 const nsString& MediaKeySession::GetSessionId() const { return mSessionId; } 102 103 JSObject* MediaKeySession::WrapObject(JSContext* aCx, 104 JS::Handle<JSObject*> aGivenProto) { 105 return MediaKeySession_Binding::Wrap(aCx, this, aGivenProto); 106 } 107 108 double MediaKeySession::Expiration() const { return mExpiration; } 109 110 Promise* MediaKeySession::Closed() const { return mClosed; } 111 112 void MediaKeySession::UpdateKeyStatusMap() { 113 MOZ_ASSERT(!IsClosed()); 114 if (!mKeys->GetCDMProxy()) { 115 return; 116 } 117 118 nsTArray<CDMCaps::KeyStatus> keyStatuses; 119 { 120 auto caps = mKeys->GetCDMProxy()->Capabilites().Lock(); 121 caps->GetKeyStatusesForSession(mSessionId, keyStatuses); 122 } 123 124 mKeyStatusMap->Update(keyStatuses); 125 126 if (EME_LOG_ENABLED()) { 127 nsAutoCString message( 128 nsPrintfCString("MediaKeySession[%p,'%s'] key statuses change {", this, 129 NS_ConvertUTF16toUTF8(mSessionId).get())); 130 for (const CDMCaps::KeyStatus& status : keyStatuses) { 131 message.AppendPrintf(" (%s,%s)", ToHexString(status.mId).get(), 132 GetEnumString(status.mStatus).get()); 133 } 134 message.AppendLiteral(" }"); 135 // Use %s so we aren't exposing random strings to printf interpolation. 136 EME_LOG("%s", message.get()); 137 } 138 } 139 140 MediaKeyStatusMap* MediaKeySession::KeyStatuses() const { 141 return mKeyStatusMap; 142 } 143 144 // The user agent MUST thoroughly validate the Initialization Data before 145 // passing it to the CDM. This includes verifying that the length and 146 // values of fields are reasonable, verifying that values are within 147 // reasonable limits, and stripping irrelevant, unsupported, or unknown 148 // data or fields. It is RECOMMENDED that user agents pre-parse, sanitize, 149 // and/or generate a fully sanitized version of the Initialization Data. 150 // If the Initialization Data format specified by initDataType supports 151 // multiple entries, the user agent SHOULD remove entries that are not 152 // needed by the CDM. The user agent MUST NOT re-order entries within 153 // the Initialization Data. 154 static bool ValidateInitData(const nsTArray<uint8_t>& aInitData, 155 const nsAString& aInitDataType) { 156 if (aInitDataType.LowerCaseEqualsLiteral("webm")) { 157 // WebM initData consists of a single keyId. Ensure it's of reasonable 158 // length. 159 return aInitData.Length() <= MAX_KEY_ID_LENGTH; 160 } else if (aInitDataType.LowerCaseEqualsLiteral("cenc")) { 161 // Limit initData to less than 64KB. 162 if (aInitData.Length() > MAX_CENC_INIT_DATA_LENGTH) { 163 return false; 164 } 165 std::vector<std::vector<uint8_t>> keyIds; 166 return ParseCENCInitData(aInitData.Elements(), aInitData.Length(), keyIds); 167 } else if (aInitDataType.LowerCaseEqualsLiteral("keyids")) { 168 if (aInitData.Length() > MAX_KEY_ID_LENGTH) { 169 return false; 170 } 171 // Ensure that init data matches the expected JSON format. 172 mozilla::dom::KeyIdsInitData keyIds; 173 nsString json; 174 nsDependentCSubstring raw( 175 reinterpret_cast<const char*>(aInitData.Elements()), 176 aInitData.Length()); 177 if (NS_FAILED(UTF_8_ENCODING->DecodeWithBOMRemoval(raw, json))) { 178 return false; 179 } 180 if (!keyIds.Init(json)) { 181 return false; 182 } 183 if (keyIds.mKids.Length() == 0) { 184 return false; 185 } 186 for (const auto& kid : keyIds.mKids) { 187 if (kid.IsEmpty()) { 188 return false; 189 } 190 } 191 } 192 return true; 193 } 194 195 // Generates a license request based on the initData. A message of type 196 // "license-request" or "individualization-request" will always be queued 197 // if the algorithm succeeds and the promise is resolved. 198 already_AddRefed<Promise> MediaKeySession::GenerateRequest( 199 const nsAString& aInitDataType, 200 const ArrayBufferViewOrArrayBuffer& aInitData, ErrorResult& aRv) { 201 RefPtr<DetailedPromise> promise( 202 MakePromise(aRv, "MediaKeySession.generateRequest"_ns)); 203 if (aRv.Failed()) { 204 return nullptr; 205 } 206 207 // If this object is closed, return a promise rejected with an 208 // InvalidStateError. 209 if (IsClosed()) { 210 EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, closed", this, 211 NS_ConvertUTF16toUTF8(mSessionId).get()); 212 promise->MaybeRejectWithInvalidStateError( 213 "Session is closed in MediaKeySession.generateRequest()"); 214 return promise.forget(); 215 } 216 217 // If this object's uninitialized value is false, return a promise rejected 218 // with an InvalidStateError. 219 if (!mUninitialized) { 220 EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, uninitialized", 221 this, NS_ConvertUTF16toUTF8(mSessionId).get()); 222 promise->MaybeRejectWithInvalidStateError( 223 "Session is already initialized in MediaKeySession.generateRequest()"); 224 return promise.forget(); 225 } 226 227 // Let this object's uninitialized value be false. 228 mUninitialized = false; 229 230 // If initDataType is the empty string, return a promise rejected 231 // with a newly created TypeError. 232 if (aInitDataType.IsEmpty()) { 233 promise->MaybeRejectWithTypeError( 234 "Empty initDataType passed to MediaKeySession.generateRequest()"); 235 EME_LOG( 236 "MediaKeySession[%p,'%s'] GenerateRequest() failed, empty initDataType", 237 this, NS_ConvertUTF16toUTF8(mSessionId).get()); 238 return promise.forget(); 239 } 240 241 // If initData is an empty array, return a promise rejected with 242 // a newly created TypeError. 243 nsTArray<uint8_t> data; 244 CopyArrayBufferViewOrArrayBufferData(aInitData, data); 245 if (data.IsEmpty()) { 246 promise->MaybeRejectWithTypeError( 247 "Empty initData passed to MediaKeySession.generateRequest()"); 248 EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, empty initData", 249 this, NS_ConvertUTF16toUTF8(mSessionId).get()); 250 return promise.forget(); 251 } 252 253 // If the Key System implementation represented by this object's 254 // cdm implementation value does not support initDataType as an 255 // Initialization Data Type, return a promise rejected with a 256 // NotSupportedError. String comparison is case-sensitive. 257 MediaKeySystemAccess::KeySystemSupportsInitDataType( 258 mKeySystem, aInitDataType, mHardwareDecryption, mIsPrivateBrowsing) 259 ->Then(GetMainThreadSerialEventTarget(), __func__, 260 [self = RefPtr<MediaKeySession>{this}, this, 261 initDataType = nsString{aInitDataType}, 262 initData = std::move(data), promise]( 263 const GenericPromise::ResolveOrRejectValue& aResult) mutable { 264 if (aResult.IsReject()) { 265 promise->MaybeRejectWithNotSupportedError( 266 "Unsupported initDataType passed to " 267 "MediaKeySession.generateRequest()"); 268 EME_LOG( 269 "MediaKeySession[%p,'%s'] GenerateRequest() failed, " 270 "unsupported " 271 "initDataType", 272 this, NS_ConvertUTF16toUTF8(mSessionId).get()); 273 return; 274 } 275 // Run rest of steps in the spec, starting from 6.6.2.7 276 CompleteGenerateRequest(initDataType, initData, promise); 277 }); 278 return promise.forget(); 279 } 280 281 void MediaKeySession::CompleteGenerateRequest(const nsString& aInitDataType, 282 nsTArray<uint8_t>& aData, 283 DetailedPromise* aPromise) { 284 if (!mKeys->GetCDMProxy()) { 285 EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() null CDMProxy", this, 286 NS_ConvertUTF16toUTF8(mSessionId).get()); 287 aPromise->MaybeRejectWithInvalidStateError( 288 "MediaKeySession.GenerateRequest() lost reference to CDM"); 289 return; 290 } 291 292 // Let init data be a copy of the contents of the initData parameter. 293 // Note: Handled by the CopyArrayBufferViewOrArrayBufferData call above. 294 295 // Let session type be this object's session type. 296 297 // Let promise be a new promise. 298 299 // Run the following steps in parallel: 300 301 // If the init data is not valid for initDataType, reject promise with a newly 302 // created TypeError. 303 if (!ValidateInitData(aData, aInitDataType)) { 304 // If the preceding step failed, reject promise with a newly created 305 // TypeError. 306 aPromise->MaybeRejectWithTypeError( 307 "initData sanitization failed in " 308 "MediaKeySession.generateRequest()"); 309 EME_LOG( 310 "MediaKeySession[%p,'%s'] GenerateRequest() initData " 311 "sanitization " 312 "failed", 313 this, NS_ConvertUTF16toUTF8(mSessionId).get()); 314 return; 315 } 316 317 // Let sanitized init data be a validated and sanitized version of init data. 318 319 // If sanitized init data is empty, reject promise with a NotSupportedError. 320 321 // Note: Remaining steps of generateRequest method continue in CDM. 322 323 // Convert initData to hex for easier logging. 324 // Note: CreateSession() std::move()s the data out of the array, so we have to 325 // copy it here. 326 nsAutoCString hexInitData(ToHexString(aData)); 327 PromiseId pid = mKeys->StorePromise(aPromise); 328 mKeys->ConnectPendingPromiseIdWithToken(pid, Token()); 329 mKeys->GetCDMProxy()->CreateSession(Token(), mSessionType, pid, aInitDataType, 330 aData); 331 EME_LOG( 332 "MediaKeySession[%p,'%s'] GenerateRequest() sent, " 333 "promiseId=%d initData='%s' initDataType='%s'", 334 this, NS_ConvertUTF16toUTF8(mSessionId).get(), pid, hexInitData.get(), 335 NS_ConvertUTF16toUTF8(aInitDataType).get()); 336 } 337 338 already_AddRefed<Promise> MediaKeySession::Load(const nsAString& aSessionId, 339 ErrorResult& aRv) { 340 RefPtr<DetailedPromise> promise(MakePromise(aRv, "MediaKeySession.load"_ns)); 341 if (aRv.Failed()) { 342 return nullptr; 343 } 344 345 // 1. If this object is closed, return a promise rejected with an 346 // InvalidStateError. 347 if (IsClosed()) { 348 promise->MaybeRejectWithInvalidStateError( 349 "Session is closed in MediaKeySession.load()"); 350 EME_LOG("MediaKeySession[%p,'%s'] Load() failed, closed", this, 351 NS_ConvertUTF16toUTF8(aSessionId).get()); 352 return promise.forget(); 353 } 354 355 // 2.If this object's uninitialized value is false, return a promise rejected 356 // with an InvalidStateError. 357 if (!mUninitialized) { 358 promise->MaybeRejectWithInvalidStateError( 359 "Session is already initialized in MediaKeySession.load()"); 360 EME_LOG("MediaKeySession[%p,'%s'] Load() failed, uninitialized", this, 361 NS_ConvertUTF16toUTF8(aSessionId).get()); 362 return promise.forget(); 363 } 364 365 // 3.Let this object's uninitialized value be false. 366 mUninitialized = false; 367 368 // 4. If sessionId is the empty string, return a promise rejected with a newly 369 // created TypeError. 370 if (aSessionId.IsEmpty()) { 371 promise->MaybeRejectWithTypeError( 372 "Trying to load a session with empty session ID"); 373 // "The sessionId parameter is empty." 374 EME_LOG("MediaKeySession[%p,''] Load() failed, no sessionId", this); 375 return promise.forget(); 376 } 377 378 // 5. If the result of running the Is persistent session type? algorithm 379 // on this object's session type is false, return a promise rejected with 380 // a newly created TypeError. 381 if (mSessionType == MediaKeySessionType::Temporary) { 382 promise->MaybeRejectWithTypeError( 383 "Trying to load() into a non-persistent session"); 384 EME_LOG( 385 "MediaKeySession[%p,''] Load() failed, can't load in a non-persistent " 386 "session", 387 this); 388 return promise.forget(); 389 } 390 391 // Note: We don't support persistent sessions in any keysystem, so all calls 392 // to Load() should reject with a TypeError in the preceding check. Omitting 393 // implementing the rest of the specified MediaKeySession::Load() algorithm. 394 395 // We now know the sessionId being loaded into this session. Remove the 396 // session from its owning MediaKey's set of sessions awaiting a sessionId. 397 RefPtr<MediaKeySession> session(mKeys->GetPendingSession(Token())); 398 MOZ_ASSERT(session == this, "Session should be awaiting id on its own token"); 399 400 // Associate with the known sessionId. 401 SetSessionId(aSessionId); 402 403 PromiseId pid = mKeys->StorePromise(promise); 404 mKeys->GetCDMProxy()->LoadSession(pid, mSessionType, aSessionId); 405 406 EME_LOG("MediaKeySession[%p,'%s'] Load() sent to CDM, promiseId=%d", this, 407 NS_ConvertUTF16toUTF8(mSessionId).get(), pid); 408 409 return promise.forget(); 410 } 411 412 already_AddRefed<Promise> MediaKeySession::Update( 413 const ArrayBufferViewOrArrayBuffer& aResponse, ErrorResult& aRv) { 414 RefPtr<DetailedPromise> promise( 415 MakePromise(aRv, "MediaKeySession.update"_ns)); 416 if (aRv.Failed()) { 417 return nullptr; 418 } 419 420 if (!IsCallable()) { 421 // If this object's callable value is false, return a promise rejected 422 // with a new DOMException whose name is InvalidStateError. 423 EME_LOG( 424 "MediaKeySession[%p,''] Update() called before sessionId set by CDM", 425 this); 426 promise->MaybeRejectWithInvalidStateError( 427 "MediaKeySession.Update() called before sessionId set by CDM"); 428 return promise.forget(); 429 } 430 431 nsTArray<uint8_t> data; 432 if (IsClosed() || !mKeys->GetCDMProxy()) { 433 promise->MaybeRejectWithInvalidStateError( 434 "Session is closed or was not properly initialized"); 435 EME_LOG( 436 "MediaKeySession[%p,'%s'] Update() failed, session is closed or was " 437 "not properly initialised.", 438 this, NS_ConvertUTF16toUTF8(mSessionId).get()); 439 return promise.forget(); 440 } 441 CopyArrayBufferViewOrArrayBufferData(aResponse, data); 442 if (data.IsEmpty()) { 443 promise->MaybeRejectWithTypeError( 444 "Empty response buffer passed to MediaKeySession.update()"); 445 EME_LOG("MediaKeySession[%p,'%s'] Update() failed, empty response buffer", 446 this, NS_ConvertUTF16toUTF8(mSessionId).get()); 447 return promise.forget(); 448 } 449 450 // Convert response to hex for easier logging. 451 // Note: UpdateSession() std::move()s the data out of the array, so we have 452 // to copy it here. 453 nsAutoCString hexResponse(ToHexString(data)); 454 455 PromiseId pid = mKeys->StorePromise(promise); 456 mKeys->GetCDMProxy()->UpdateSession(mSessionId, pid, data); 457 458 EME_LOG( 459 "MediaKeySession[%p,'%s'] Update() sent to CDM, " 460 "promiseId=%d Response='%s'", 461 this, NS_ConvertUTF16toUTF8(mSessionId).get(), pid, hexResponse.get()); 462 463 return promise.forget(); 464 } 465 466 already_AddRefed<Promise> MediaKeySession::Close(ErrorResult& aRv) { 467 RefPtr<DetailedPromise> promise(MakePromise(aRv, "MediaKeySession.close"_ns)); 468 if (aRv.Failed()) { 469 return nullptr; 470 } 471 // 1. Let session be the associated MediaKeySession object. 472 // 2. If session is closed, return a resolved promise. 473 if (IsClosed()) { 474 EME_LOG("MediaKeySession[%p,'%s'] Close() already closed", this, 475 NS_ConvertUTF16toUTF8(mSessionId).get()); 476 promise->MaybeResolveWithUndefined(); 477 return promise.forget(); 478 } 479 // 3. If session's callable value is false, return a promise rejected 480 // with an InvalidStateError. 481 if (!IsCallable()) { 482 EME_LOG("MediaKeySession[%p,''] Close() called before sessionId set by CDM", 483 this); 484 promise->MaybeRejectWithInvalidStateError( 485 "MediaKeySession.Close() called before sessionId set by CDM"); 486 return promise.forget(); 487 } 488 if (!mKeys->GetCDMProxy()) { 489 EME_LOG("MediaKeySession[%p,'%s'] Close() null CDMProxy", this, 490 NS_ConvertUTF16toUTF8(mSessionId).get()); 491 promise->MaybeRejectWithInvalidStateError( 492 "MediaKeySession.Close() lost reference to CDM"); 493 return promise.forget(); 494 } 495 // 4. Let promise be a new promise. 496 PromiseId pid = mKeys->StorePromise(promise); 497 // 5. Run the following steps in parallel: 498 // 5.1 Let cdm be the CDM instance represented by session's cdm instance 499 // value. 5.2 Use cdm to close the session associated with session. 500 mKeys->GetCDMProxy()->CloseSession(mSessionId, pid); 501 502 EME_LOG("MediaKeySession[%p,'%s'] Close() sent to CDM, promiseId=%d", this, 503 NS_ConvertUTF16toUTF8(mSessionId).get(), pid); 504 505 // Session Closed algorithm is run when CDM causes us to run 506 // OnSessionClosed(). 507 508 // 6. Return promise. 509 return promise.forget(); 510 } 511 512 void MediaKeySession::OnClosed(MediaKeySessionClosedReason aReason) { 513 if (IsClosed()) { 514 return; 515 } 516 EME_LOG( 517 "MediaKeySession[%p,'%s'] session close operation complete due to reason " 518 "'%s'.", 519 this, NS_ConvertUTF16toUTF8(mSessionId).get(), 520 GetEnumString(aReason).get()); 521 mIsClosed = true; 522 mKeys->OnSessionClosed(this); 523 mKeys = nullptr; 524 mClosed->MaybeResolve(aReason); 525 } 526 527 bool MediaKeySession::IsClosed() const { return mIsClosed; } 528 529 already_AddRefed<Promise> MediaKeySession::Remove(ErrorResult& aRv) { 530 RefPtr<DetailedPromise> promise( 531 MakePromise(aRv, "MediaKeySession.remove"_ns)); 532 if (aRv.Failed()) { 533 return nullptr; 534 } 535 if (!IsCallable()) { 536 // If this object's callable value is false, return a promise rejected 537 // with a new DOMException whose name is InvalidStateError. 538 EME_LOG( 539 "MediaKeySession[%p,''] Remove() called before sessionId set by CDM", 540 this); 541 promise->MaybeRejectWithInvalidStateError( 542 "MediaKeySession.Remove() called before sessionId set by CDM"); 543 return promise.forget(); 544 } 545 if (mSessionType != MediaKeySessionType::Persistent_license) { 546 promise->MaybeRejectWithInvalidAccessError( 547 "Calling MediaKeySession.remove() on non-persistent session"); 548 // "The operation is not supported on session type sessions." 549 EME_LOG("MediaKeySession[%p,'%s'] Remove() failed, sesion not persisrtent.", 550 this, NS_ConvertUTF16toUTF8(mSessionId).get()); 551 return promise.forget(); 552 } 553 if (IsClosed() || !mKeys->GetCDMProxy()) { 554 promise->MaybeRejectWithInvalidStateError( 555 "MediaKeySession.remove() called but session is not active"); 556 // "The session is closed." 557 EME_LOG("MediaKeySession[%p,'%s'] Remove() failed, already session closed.", 558 this, NS_ConvertUTF16toUTF8(mSessionId).get()); 559 return promise.forget(); 560 } 561 PromiseId pid = mKeys->StorePromise(promise); 562 mKeys->GetCDMProxy()->RemoveSession(mSessionId, pid); 563 EME_LOG("MediaKeySession[%p,'%s'] Remove() sent to CDM, promiseId=%d.", this, 564 NS_ConvertUTF16toUTF8(mSessionId).get(), pid); 565 566 return promise.forget(); 567 } 568 569 void MediaKeySession::DispatchKeyMessage(MediaKeyMessageType aMessageType, 570 const nsTArray<uint8_t>& aMessage) { 571 if (EME_LOG_ENABLED()) { 572 EME_LOG( 573 "MediaKeySession[%p,'%s'] DispatchKeyMessage() type=%s message='%s'", 574 this, NS_ConvertUTF16toUTF8(mSessionId).get(), 575 GetEnumString(aMessageType).get(), ToHexString(aMessage).get()); 576 } 577 578 RefPtr<MediaKeyMessageEvent> event( 579 MediaKeyMessageEvent::Constructor(this, aMessageType, aMessage)); 580 RefPtr<AsyncEventDispatcher> asyncDispatcher = 581 new AsyncEventDispatcher(this, event.forget()); 582 asyncDispatcher->PostDOMEvent(); 583 } 584 585 void MediaKeySession::DispatchKeyError(uint32_t aSystemCode) { 586 EME_LOG("MediaKeySession[%p,'%s'] DispatchKeyError() systemCode=%u.", this, 587 NS_ConvertUTF16toUTF8(mSessionId).get(), aSystemCode); 588 589 auto event = MakeRefPtr<MediaKeyError>(this, aSystemCode); 590 RefPtr<AsyncEventDispatcher> asyncDispatcher = 591 new AsyncEventDispatcher(this, event.forget()); 592 asyncDispatcher->PostDOMEvent(); 593 } 594 595 void MediaKeySession::DispatchKeyStatusesChange() { 596 if (IsClosed()) { 597 return; 598 } 599 600 UpdateKeyStatusMap(); 601 602 RefPtr<AsyncEventDispatcher> asyncDispatcher = 603 new AsyncEventDispatcher(this, u"keystatuseschange"_ns, CanBubble::eNo); 604 asyncDispatcher->PostDOMEvent(); 605 } 606 607 uint32_t MediaKeySession::Token() const { return mToken; } 608 609 already_AddRefed<DetailedPromise> MediaKeySession::MakePromise( 610 ErrorResult& aRv, const nsACString& aName) { 611 nsCOMPtr<nsIGlobalObject> global = GetParentObject(); 612 if (!global) { 613 NS_WARNING("Passed non-global to MediaKeys ctor!"); 614 aRv.Throw(NS_ERROR_UNEXPECTED); 615 return nullptr; 616 } 617 return DetailedPromise::Create(global, aRv, aName); 618 } 619 620 void MediaKeySession::SetExpiration(double aExpiration) { 621 EME_LOG("MediaKeySession[%p,'%s'] SetExpiry(%.12lf) (%.2lf hours from now)", 622 this, NS_ConvertUTF16toUTF8(mSessionId).get(), aExpiration, 623 (aExpiration - 1000.0 * double(time(0))) / (1000.0 * 60 * 60)); 624 mExpiration = aExpiration; 625 } 626 627 EventHandlerNonNull* MediaKeySession::GetOnkeystatuseschange() { 628 return GetEventHandler(nsGkAtoms::onkeystatuseschange); 629 } 630 631 void MediaKeySession::SetOnkeystatuseschange(EventHandlerNonNull* aCallback) { 632 SetEventHandler(nsGkAtoms::onkeystatuseschange, aCallback); 633 } 634 635 EventHandlerNonNull* MediaKeySession::GetOnmessage() { 636 return GetEventHandler(nsGkAtoms::onmessage); 637 } 638 639 void MediaKeySession::SetOnmessage(EventHandlerNonNull* aCallback) { 640 SetEventHandler(nsGkAtoms::onmessage, aCallback); 641 } 642 643 nsString ToString(MediaKeySessionType aType) { 644 return NS_ConvertUTF8toUTF16(GetEnumString(aType)); 645 } 646 647 } // namespace mozilla::dom