ChromiumCDMProxy.cpp (24277B)
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 "ChromiumCDMProxy.h" 8 9 #include "ChromiumCDMCallbackProxy.h" 10 #include "GMPService.h" 11 #include "GMPUtils.h" 12 #include "MediaResult.h" 13 #include "content_decryption_module.h" 14 #include "mozilla/StaticPrefs_media.h" 15 #include "mozilla/dom/MediaKeySession.h" 16 #include "mozilla/dom/MediaKeysBinding.h" 17 #include "nsPrintfCString.h" 18 19 #define NS_DispatchToMainThread(...) CompileError_UseAbstractMainThreadInstead 20 21 namespace mozilla { 22 23 ChromiumCDMProxy::ChromiumCDMProxy(dom::MediaKeys* aKeys, 24 const nsAString& aKeySystem, 25 GMPCrashHelper* aCrashHelper, 26 bool aDistinctiveIdentifierRequired, 27 bool aPersistentStateRequired) 28 : CDMProxy(aKeys, aKeySystem, aDistinctiveIdentifierRequired, 29 aPersistentStateRequired), 30 mCrashHelper(aCrashHelper), 31 mCDMMutex("ChromiumCDMProxy"), 32 mGMPThread(GetGMPThread()) { 33 MOZ_ASSERT(NS_IsMainThread()); 34 } 35 36 ChromiumCDMProxy::~ChromiumCDMProxy() { 37 EME_LOG("ChromiumCDMProxy::~ChromiumCDMProxy(this=%p)", this); 38 } 39 40 void ChromiumCDMProxy::Init(PromiseId aPromiseId, const nsAString& aOrigin, 41 const nsAString& aTopLevelOrigin, 42 const nsAString& aGMPName) { 43 MOZ_ASSERT(NS_IsMainThread()); 44 45 RefPtr<GMPCrashHelper> helper(std::move(mCrashHelper)); 46 47 NS_ENSURE_TRUE_VOID(!mKeys.IsNull()); 48 49 EME_LOG("ChromiumCDMProxy::Init(this=%p, pid=%" PRIu32 50 ", origin=%s, topLevelOrigin=%s, " 51 "gmp=%s)", 52 this, aPromiseId, NS_ConvertUTF16toUTF8(aOrigin).get(), 53 NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(), 54 NS_ConvertUTF16toUTF8(aGMPName).get()); 55 56 if (!mGMPThread) { 57 RejectPromiseWithStateError( 58 aPromiseId, "Couldn't get GMP thread ChromiumCDMProxy::Init"_ns); 59 return; 60 } 61 62 if (aGMPName.IsEmpty()) { 63 RejectPromiseWithStateError( 64 aPromiseId, nsPrintfCString("Unknown GMP for keysystem '%s'", 65 NS_ConvertUTF16toUTF8(mKeySystem).get())); 66 return; 67 } 68 69 gmp::NodeIdParts nodeIdParts{nsString(aOrigin), nsString(aTopLevelOrigin), 70 nsString(aGMPName)}; 71 nsCOMPtr<nsISerialEventTarget> thread = mGMPThread; 72 RefPtr<ChromiumCDMProxy> self(this); 73 nsCString keySystem = NS_ConvertUTF16toUTF8(mKeySystem); 74 RefPtr<Runnable> task(NS_NewRunnableFunction( 75 "ChromiumCDMProxy::Init", 76 [self, nodeIdParts, helper, aPromiseId, thread, keySystem]() -> void { 77 MOZ_ASSERT(self->IsOnOwnerThread()); 78 79 RefPtr<gmp::GeckoMediaPluginService> service = 80 gmp::GeckoMediaPluginService::GetGeckoMediaPluginService(); 81 if (!service) { 82 self->RejectPromiseWithStateError( 83 aPromiseId, 84 nsLiteralCString("Couldn't get GeckoMediaPluginService in " 85 "ChromiumCDMProxy::Init")); 86 return; 87 } 88 RefPtr<gmp::GetCDMParentPromise> promise = 89 service->GetCDM(nodeIdParts, keySystem, helper); 90 promise->Then( 91 thread, __func__, 92 [self, aPromiseId, thread](RefPtr<gmp::ChromiumCDMParent> cdm) { 93 // service->GetCDM succeeded 94 self->mCallback = 95 MakeUnique<ChromiumCDMCallbackProxy>(self, self->mMainThread); 96 cdm->Init(self->mCallback.get(), 97 self->mDistinctiveIdentifierRequired, 98 self->mPersistentStateRequired, self->mMainThread) 99 ->Then( 100 self->mMainThread, __func__, 101 [self, aPromiseId, cdm](bool /* unused */) { 102 // CDM init succeeded 103 { 104 MutexAutoLock lock(self->mCDMMutex); 105 self->mCDM = cdm; 106 } 107 if (self->mIsShutdown) { 108 self->RejectPromiseWithStateError( 109 aPromiseId, nsLiteralCString( 110 "ChromiumCDMProxy shutdown " 111 "during ChromiumCDMProxy::Init")); 112 // If shutdown happened while waiting to init, we 113 // need to explicitly shutdown the CDM to avoid it 114 // referencing this proxy which is on its way out. 115 self->ShutdownCDMIfExists(); 116 return; 117 } 118 self->OnCDMCreated(aPromiseId); 119 }, 120 [self, aPromiseId](MediaResult aResult) { 121 // CDM init failed. 122 ErrorResult rv; 123 // XXXbz MediaResult should really store a 124 // CopyableErrorResult or something. See 125 // <https://bugzilla.mozilla.org/show_bug.cgi?id=1612216>. 126 rv.Throw(aResult.Code()); 127 self->RejectPromise(aPromiseId, std::move(rv), 128 aResult.Message()); 129 }); 130 }, 131 [self, aPromiseId](MediaResult rv) { 132 // service->GetCDM failed 133 ErrorResult result; 134 // XXXbz MediaResult should really store a CopyableErrorResult or 135 // something. See 136 // <https://bugzilla.mozilla.org/show_bug.cgi?id=1612216>. 137 result.Throw(rv.Code()); 138 self->RejectPromise(aPromiseId, std::move(result), 139 rv.Description()); 140 }); 141 })); 142 143 mGMPThread->Dispatch(task.forget()); 144 } 145 146 void ChromiumCDMProxy::OnCDMCreated(uint32_t aPromiseId) { 147 EME_LOG("ChromiumCDMProxy::OnCDMCreated(this=%p, pid=%" PRIu32 148 ") isMainThread=%d", 149 this, aPromiseId, NS_IsMainThread()); 150 MOZ_ASSERT(NS_IsMainThread()); 151 if (mKeys.IsNull()) { 152 return; 153 } 154 RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent(); 155 // This should only be called once the CDM has been created. 156 MOZ_ASSERT(cdm); 157 if (cdm) { 158 mKeys->OnCDMCreated(aPromiseId, cdm->PluginId()); 159 } else { 160 // No CDM? Shouldn't be possible, but reject the promise anyway... 161 constexpr auto err = "Null CDM in OnCDMCreated()"_ns; 162 ErrorResult rv; 163 rv.ThrowInvalidStateError(err); 164 mKeys->RejectPromise(aPromiseId, std::move(rv), err); 165 } 166 } 167 168 void ChromiumCDMProxy::ShutdownCDMIfExists() { 169 EME_LOG( 170 "ChromiumCDMProxy::ShutdownCDMIfExists(this=%p) mCDM=%p, mIsShutdown=%s", 171 this, mCDM.get(), mIsShutdown ? "true" : "false"); 172 MOZ_ASSERT(NS_IsMainThread()); 173 MOZ_ASSERT(mGMPThread); 174 MOZ_ASSERT(mIsShutdown, 175 "Should only shutdown the CDM if the proxy is shutting down"); 176 RefPtr<gmp::ChromiumCDMParent> cdm; 177 { 178 MutexAutoLock lock(mCDMMutex); 179 cdm.swap(mCDM); 180 } 181 if (cdm) { 182 // We need to keep this proxy alive until the parent has finished its 183 // Shutdown (as it may still try to use the proxy until then). 184 RefPtr<ChromiumCDMProxy> self(this); 185 nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction( 186 "ChromiumCDMProxy::Shutdown", [self, cdm]() { cdm->Shutdown(); }); 187 mGMPThread->Dispatch(task.forget()); 188 } 189 } 190 191 #ifdef DEBUG 192 bool ChromiumCDMProxy::IsOnOwnerThread() { 193 return mGMPThread && mGMPThread->IsOnCurrentThread(); 194 } 195 #endif 196 197 static uint32_t ToCDMSessionType(dom::MediaKeySessionType aSessionType) { 198 switch (aSessionType) { 199 case dom::MediaKeySessionType::Temporary: 200 return static_cast<uint32_t>(cdm::kTemporary); 201 case dom::MediaKeySessionType::Persistent_license: 202 return static_cast<uint32_t>(cdm::kPersistentLicense); 203 default: 204 return static_cast<uint32_t>(cdm::kTemporary); 205 }; 206 }; 207 208 static uint32_t ToCDMInitDataType(const nsAString& aInitDataType) { 209 if (aInitDataType.EqualsLiteral("cenc")) { 210 return static_cast<uint32_t>(cdm::kCenc); 211 } 212 if (aInitDataType.EqualsLiteral("webm")) { 213 return static_cast<uint32_t>(cdm::kWebM); 214 } 215 if (aInitDataType.EqualsLiteral("keyids")) { 216 return static_cast<uint32_t>(cdm::kKeyIds); 217 } 218 return static_cast<uint32_t>(cdm::kCenc); 219 } 220 221 void ChromiumCDMProxy::CreateSession(uint32_t aCreateSessionToken, 222 dom::MediaKeySessionType aSessionType, 223 PromiseId aPromiseId, 224 const nsAString& aInitDataType, 225 nsTArray<uint8_t>& aInitData) { 226 MOZ_ASSERT(NS_IsMainThread()); 227 EME_LOG("ChromiumCDMProxy::CreateSession(this=%p, token=%" PRIu32 228 ", type=%d, pid=%" PRIu32 229 ") " 230 "initDataLen=%zu", 231 this, aCreateSessionToken, (int)aSessionType, aPromiseId, 232 aInitData.Length()); 233 234 uint32_t sessionType = ToCDMSessionType(aSessionType); 235 uint32_t initDataType = ToCDMInitDataType(aInitDataType); 236 237 RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent(); 238 if (!cdm) { 239 RejectPromiseWithStateError(aPromiseId, "Null CDM in CreateSession"_ns); 240 return; 241 } 242 243 mGMPThread->Dispatch(NewRunnableMethod<uint32_t, uint32_t, uint32_t, uint32_t, 244 nsTArray<uint8_t>>( 245 "gmp::ChromiumCDMParent::CreateSession", cdm, 246 &gmp::ChromiumCDMParent::CreateSession, aCreateSessionToken, sessionType, 247 initDataType, aPromiseId, std::move(aInitData))); 248 } 249 250 void ChromiumCDMProxy::LoadSession(PromiseId aPromiseId, 251 dom::MediaKeySessionType aSessionType, 252 const nsAString& aSessionId) { 253 MOZ_ASSERT(NS_IsMainThread()); 254 255 RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent(); 256 if (!cdm) { 257 RejectPromiseWithStateError(aPromiseId, "Null CDM in LoadSession"_ns); 258 return; 259 } 260 261 mGMPThread->Dispatch(NewRunnableMethod<uint32_t, uint32_t, nsString>( 262 "gmp::ChromiumCDMParent::LoadSession", cdm, 263 &gmp::ChromiumCDMParent::LoadSession, aPromiseId, 264 ToCDMSessionType(aSessionType), aSessionId)); 265 } 266 267 void ChromiumCDMProxy::SetServerCertificate(PromiseId aPromiseId, 268 nsTArray<uint8_t>& aCert) { 269 MOZ_ASSERT(NS_IsMainThread()); 270 EME_LOG("ChromiumCDMProxy::SetServerCertificate(this=%p, pid=%" PRIu32 271 ") certLen=%zu", 272 this, aPromiseId, aCert.Length()); 273 274 RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent(); 275 if (!cdm) { 276 RejectPromiseWithStateError(aPromiseId, 277 "Null CDM in SetServerCertificate"_ns); 278 return; 279 } 280 281 mGMPThread->Dispatch(NewRunnableMethod<uint32_t, nsTArray<uint8_t>>( 282 "gmp::ChromiumCDMParent::SetServerCertificate", cdm, 283 &gmp::ChromiumCDMParent::SetServerCertificate, aPromiseId, 284 std::move(aCert))); 285 } 286 287 void ChromiumCDMProxy::UpdateSession(const nsAString& aSessionId, 288 PromiseId aPromiseId, 289 nsTArray<uint8_t>& aResponse) { 290 MOZ_ASSERT(NS_IsMainThread()); 291 EME_LOG("ChromiumCDMProxy::UpdateSession(this=%p, sid='%s', pid=%" PRIu32 292 ") " 293 "responseLen=%zu", 294 this, NS_ConvertUTF16toUTF8(aSessionId).get(), aPromiseId, 295 aResponse.Length()); 296 297 RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent(); 298 if (!cdm) { 299 RejectPromiseWithStateError(aPromiseId, "Null CDM in UpdateSession"_ns); 300 return; 301 } 302 mGMPThread->Dispatch( 303 NewRunnableMethod<nsCString, uint32_t, nsTArray<uint8_t>>( 304 "gmp::ChromiumCDMParent::UpdateSession", cdm, 305 &gmp::ChromiumCDMParent::UpdateSession, 306 NS_ConvertUTF16toUTF8(aSessionId), aPromiseId, std::move(aResponse))); 307 } 308 309 void ChromiumCDMProxy::CloseSession(const nsAString& aSessionId, 310 PromiseId aPromiseId) { 311 MOZ_ASSERT(NS_IsMainThread()); 312 EME_LOG("ChromiumCDMProxy::CloseSession(this=%p, sid='%s', pid=%" PRIu32 ")", 313 this, NS_ConvertUTF16toUTF8(aSessionId).get(), aPromiseId); 314 315 RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent(); 316 if (!cdm) { 317 RejectPromiseWithStateError(aPromiseId, "Null CDM in CloseSession"_ns); 318 return; 319 } 320 mGMPThread->Dispatch(NewRunnableMethod<nsCString, uint32_t>( 321 "gmp::ChromiumCDMParent::CloseSession", cdm, 322 &gmp::ChromiumCDMParent::CloseSession, NS_ConvertUTF16toUTF8(aSessionId), 323 aPromiseId)); 324 } 325 326 void ChromiumCDMProxy::RemoveSession(const nsAString& aSessionId, 327 PromiseId aPromiseId) { 328 MOZ_ASSERT(NS_IsMainThread()); 329 EME_LOG("ChromiumCDMProxy::RemoveSession(this=%p, sid='%s', pid=%" PRIu32 ")", 330 this, NS_ConvertUTF16toUTF8(aSessionId).get(), aPromiseId); 331 332 RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent(); 333 if (!cdm) { 334 RejectPromiseWithStateError(aPromiseId, "Null CDM in RemoveSession"_ns); 335 return; 336 } 337 mGMPThread->Dispatch(NewRunnableMethod<nsCString, uint32_t>( 338 "gmp::ChromiumCDMParent::RemoveSession", cdm, 339 &gmp::ChromiumCDMParent::RemoveSession, NS_ConvertUTF16toUTF8(aSessionId), 340 aPromiseId)); 341 } 342 343 void ChromiumCDMProxy::QueryOutputProtectionStatus() { 344 MOZ_ASSERT(NS_IsMainThread()); 345 EME_LOG("ChromiumCDMProxy::QueryOutputProtectionStatus(this=%p)", this); 346 347 if (mKeys.IsNull()) { 348 EME_LOG( 349 "ChromiumCDMProxy::QueryOutputProtectionStatus(this=%p), mKeys " 350 "missing!", 351 this); 352 // If we can't get mKeys, we're probably in shutdown. But do our best to 353 // respond to the request and indicate the check failed. 354 NotifyOutputProtectionStatus(OutputProtectionCheckStatus::CheckFailed, 355 OutputProtectionCaptureStatus::Unused); 356 return; 357 } 358 // The keys will call back via `NotifyOutputProtectionStatus` to notify the 359 // result of the check. 360 mKeys->CheckIsElementCapturePossible(); 361 } 362 363 void ChromiumCDMProxy::NotifyOutputProtectionStatus( 364 OutputProtectionCheckStatus aCheckStatus, 365 OutputProtectionCaptureStatus aCaptureStatus) { 366 MOZ_ASSERT(NS_IsMainThread()); 367 // If the check failed aCaptureStatus should be unused, otherwise not. 368 MOZ_ASSERT_IF(aCheckStatus == OutputProtectionCheckStatus::CheckFailed, 369 aCaptureStatus == OutputProtectionCaptureStatus::Unused); 370 MOZ_ASSERT_IF(aCheckStatus == OutputProtectionCheckStatus::CheckSuccessful, 371 aCaptureStatus != OutputProtectionCaptureStatus::Unused); 372 EME_LOG( 373 "ChromiumCDMProxy::NotifyOutputProtectionStatus(this=%p) " 374 "aCheckStatus=%" PRIu8 " aCaptureStatus=%" PRIu8, 375 this, static_cast<uint8_t>(aCheckStatus), 376 static_cast<uint8_t>(aCaptureStatus)); 377 378 RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent(); 379 if (!cdm) { 380 // If we're in shutdown the CDM may have been cleared while a notification 381 // is in flight. If this happens outside of shutdown we have a bug. 382 MOZ_ASSERT(mIsShutdown); 383 return; 384 } 385 386 uint32_t linkMask{}; 387 uint32_t protectionMask{}; 388 if (aCheckStatus == OutputProtectionCheckStatus::CheckSuccessful && 389 aCaptureStatus == OutputProtectionCaptureStatus::CapturePossilbe) { 390 // The result indicates the capture is possible, so set the mask 391 // to indicate this. 392 linkMask |= cdm::OutputLinkTypes::kLinkTypeNetwork; 393 } 394 // `kProtectionNone` can cause playback to stop if HDCP_V1 is required. Report 395 // HDCP protection if there's no potential capturing. 396 if (linkMask == cdm::OutputLinkTypes::kLinkTypeNone && 397 StaticPrefs::media_widevine_hdcp_protection_mask()) { 398 protectionMask = cdm::OutputProtectionMethods::kProtectionHDCP; 399 } 400 mGMPThread->Dispatch(NewRunnableMethod<bool, uint32_t, uint32_t>( 401 "gmp::ChromiumCDMParent::NotifyOutputProtectionStatus", cdm, 402 &gmp::ChromiumCDMParent::NotifyOutputProtectionStatus, 403 aCheckStatus == OutputProtectionCheckStatus::CheckSuccessful, linkMask, 404 protectionMask)); 405 } 406 407 void ChromiumCDMProxy::Shutdown() { 408 MOZ_ASSERT(NS_IsMainThread()); 409 EME_LOG("ChromiumCDMProxy::Shutdown(this=%p) mCDM=%p, mIsShutdown=%s", this, 410 mCDM.get(), mIsShutdown ? "true" : "false"); 411 if (mIsShutdown) { 412 return; 413 } 414 mIsShutdown = true; 415 mKeys.Clear(); 416 ShutdownCDMIfExists(); 417 } 418 419 void ChromiumCDMProxy::RejectPromise(PromiseId aId, ErrorResult&& aException, 420 const nsCString& aReason) { 421 if (!NS_IsMainThread()) { 422 // Use CopyableErrorResult to store our exception in the runnable, 423 // because ErrorResult is not OK to move across threads. 424 mMainThread->Dispatch( 425 NewRunnableMethod<PromiseId, StoreCopyPassByRRef<CopyableErrorResult>, 426 nsCString>( 427 "ChromiumCDMProxy::RejectPromise", this, 428 &ChromiumCDMProxy::RejectPromiseOnMainThread, aId, 429 std::move(aException), aReason), 430 NS_DISPATCH_NORMAL); 431 return; 432 } 433 EME_LOG("ChromiumCDMProxy::RejectPromise(this=%p, pid=%" PRIu32 434 ", code=0x%x, " 435 "reason='%s')", 436 this, aId, aException.ErrorCodeAsInt(), aReason.get()); 437 if (!mKeys.IsNull()) { 438 mKeys->RejectPromise(aId, std::move(aException), aReason); 439 } else { 440 // We don't have a MediaKeys object to pass the exception to, so silence 441 // the exception to avoid it asserting due to being unused. 442 aException.SuppressException(); 443 } 444 } 445 446 void ChromiumCDMProxy::RejectPromiseWithStateError(PromiseId aId, 447 const nsCString& aReason) { 448 ErrorResult rv; 449 rv.ThrowInvalidStateError(aReason); 450 RejectPromise(aId, std::move(rv), aReason); 451 } 452 453 void ChromiumCDMProxy::RejectPromiseOnMainThread( 454 PromiseId aId, CopyableErrorResult&& aException, const nsCString& aReason) { 455 // Moving into or out of a non-copyable ErrorResult will assert that both 456 // ErorResults are from our current thread. Avoid the assertion by moving 457 // into a current-thread CopyableErrorResult first. Note that this is safe, 458 // because CopyableErrorResult never holds state that can't move across 459 // threads. 460 CopyableErrorResult rv(std::move(aException)); 461 RejectPromise(aId, std::move(rv), aReason); 462 } 463 464 void ChromiumCDMProxy::ResolvePromise(PromiseId aId) { 465 if (!NS_IsMainThread()) { 466 mMainThread->Dispatch( 467 NewRunnableMethod<PromiseId>("ChromiumCDMProxy::ResolvePromise", this, 468 &ChromiumCDMProxy::ResolvePromise, aId), 469 NS_DISPATCH_NORMAL); 470 return; 471 } 472 473 EME_LOG("ChromiumCDMProxy::ResolvePromise(this=%p, pid=%" PRIu32 ")", this, 474 aId); 475 if (!mKeys.IsNull()) { 476 mKeys->ResolvePromise(aId); 477 } else { 478 NS_WARNING("ChromiumCDMProxy unable to resolve promise!"); 479 } 480 } 481 482 void ChromiumCDMProxy::OnSetSessionId(uint32_t aCreateSessionToken, 483 const nsAString& aSessionId) { 484 MOZ_ASSERT(NS_IsMainThread()); 485 EME_LOG("ChromiumCDMProxy::OnSetSessionId(this=%p, token=%" PRIu32 486 ", sid='%s')", 487 this, aCreateSessionToken, NS_ConvertUTF16toUTF8(aSessionId).get()); 488 489 if (mKeys.IsNull()) { 490 return; 491 } 492 RefPtr<dom::MediaKeySession> session( 493 mKeys->GetPendingSession(aCreateSessionToken)); 494 if (session) { 495 session->SetSessionId(aSessionId); 496 } 497 } 498 499 void ChromiumCDMProxy::OnResolveLoadSessionPromise(uint32_t aPromiseId, 500 bool aSuccess) { 501 MOZ_ASSERT(NS_IsMainThread()); 502 if (mKeys.IsNull()) { 503 return; 504 } 505 mKeys->OnSessionLoaded(aPromiseId, aSuccess); 506 } 507 508 void ChromiumCDMProxy::OnResolvePromiseWithKeyStatus( 509 uint32_t aPromiseId, dom::MediaKeyStatus aKeyStatus) { 510 MOZ_ASSERT(NS_IsMainThread()); 511 if (mKeys.IsNull()) { 512 return; 513 } 514 mKeys->ResolvePromiseWithKeyStatus(aPromiseId, aKeyStatus); 515 } 516 517 void ChromiumCDMProxy::OnSessionMessage(const nsAString& aSessionId, 518 dom::MediaKeyMessageType aMessageType, 519 const nsTArray<uint8_t>& aMessage) { 520 MOZ_ASSERT(NS_IsMainThread()); 521 if (mKeys.IsNull()) { 522 return; 523 } 524 RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId)); 525 if (session) { 526 session->DispatchKeyMessage(aMessageType, aMessage); 527 } 528 } 529 530 void ChromiumCDMProxy::OnKeyStatusesChange(const nsAString& aSessionId) { 531 MOZ_ASSERT(NS_IsMainThread()); 532 if (mKeys.IsNull()) { 533 return; 534 } 535 RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId)); 536 if (session) { 537 session->DispatchKeyStatusesChange(); 538 } 539 } 540 541 void ChromiumCDMProxy::OnExpirationChange(const nsAString& aSessionId, 542 UnixTime aExpiryTime) { 543 MOZ_ASSERT(NS_IsMainThread()); 544 if (mKeys.IsNull()) { 545 return; 546 } 547 RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId)); 548 if (session) { 549 // Expiry of 0 is interpreted as "never expire". See bug 1345341. 550 double t = (aExpiryTime == 0) ? std::numeric_limits<double>::quiet_NaN() 551 : static_cast<double>(aExpiryTime); 552 session->SetExpiration(t); 553 } 554 } 555 556 void ChromiumCDMProxy::OnSessionClosed( 557 const nsAString& aSessionId, dom::MediaKeySessionClosedReason aReason) { 558 MOZ_ASSERT(NS_IsMainThread()); 559 560 bool keyStatusesChange = false; 561 { 562 auto caps = Capabilites().Lock(); 563 keyStatusesChange = caps->RemoveKeysForSession(nsString(aSessionId)); 564 } 565 if (keyStatusesChange) { 566 OnKeyStatusesChange(aSessionId); 567 } 568 if (mKeys.IsNull()) { 569 return; 570 } 571 RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId)); 572 if (session) { 573 session->OnClosed(aReason); 574 } 575 } 576 577 void ChromiumCDMProxy::OnDecrypted(uint32_t aId, DecryptStatus aResult, 578 const nsTArray<uint8_t>& aDecryptedData) {} 579 580 void ChromiumCDMProxy::OnSessionError(const nsAString& aSessionId, 581 nsresult aException, uint32_t aSystemCode, 582 const nsAString& aMsg) { 583 MOZ_ASSERT(NS_IsMainThread()); 584 if (mKeys.IsNull()) { 585 return; 586 } 587 RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId)); 588 if (session) { 589 session->DispatchKeyError(aSystemCode); 590 } 591 LogToConsole(aMsg); 592 } 593 594 void ChromiumCDMProxy::OnRejectPromise(uint32_t aPromiseId, 595 ErrorResult&& aException, 596 const nsCString& aMsg) { 597 MOZ_ASSERT(NS_IsMainThread()); 598 RejectPromise(aPromiseId, std::move(aException), aMsg); 599 } 600 601 RefPtr<DecryptPromise> ChromiumCDMProxy::Decrypt(MediaRawData* aSample) { 602 RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent(); 603 if (!cdm) { 604 return DecryptPromise::CreateAndReject( 605 DecryptResult(eme::AbortedErr, aSample), __func__); 606 } 607 RefPtr<MediaRawData> sample = aSample; 608 return InvokeAsync(mGMPThread, __func__, 609 [cdm, sample]() { return cdm->Decrypt(sample); }); 610 } 611 612 void ChromiumCDMProxy::GetStatusForPolicy( 613 PromiseId aPromiseId, const dom::HDCPVersion& aMinHdcpVersion) { 614 MOZ_ASSERT(NS_IsMainThread()); 615 EME_LOG("ChromiumCDMProxy::GetStatusForPolicy(this=%p, pid=%" PRIu32 616 ") minHdcpVersion=%s", 617 this, aPromiseId, dom::GetEnumString(aMinHdcpVersion).get()); 618 619 RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent(); 620 if (!cdm) { 621 RejectPromiseWithStateError(aPromiseId, 622 "Null CDM in GetStatusForPolicy"_ns); 623 return; 624 } 625 626 mGMPThread->Dispatch(NewRunnableMethod<uint32_t, dom::HDCPVersion>( 627 "gmp::ChromiumCDMParent::GetStatusForPolicy", cdm, 628 &gmp::ChromiumCDMParent::GetStatusForPolicy, aPromiseId, 629 aMinHdcpVersion)); 630 } 631 632 void ChromiumCDMProxy::Terminated() { 633 if (!mKeys.IsNull()) { 634 mKeys->Terminated(); 635 } 636 } 637 638 already_AddRefed<gmp::ChromiumCDMParent> ChromiumCDMProxy::GetCDMParent() { 639 MutexAutoLock lock(mCDMMutex); 640 RefPtr<gmp::ChromiumCDMParent> cdm = mCDM; 641 return cdm.forget(); 642 } 643 644 } // namespace mozilla 645 646 #undef NS_DispatchToMainThread