Line data Source code
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/MediaKeys.h"
8 : #include "GMPCrashHelper.h"
9 : #include "mozilla/dom/HTMLMediaElement.h"
10 : #include "mozilla/dom/MediaKeysBinding.h"
11 : #include "mozilla/dom/MediaKeyMessageEvent.h"
12 : #include "mozilla/dom/MediaKeyError.h"
13 : #include "mozilla/dom/MediaKeySession.h"
14 : #include "mozilla/dom/DOMException.h"
15 : #include "mozilla/dom/UnionTypes.h"
16 : #include "mozilla/Telemetry.h"
17 : #include "GMPCDMProxy.h"
18 : #ifdef MOZ_WIDGET_ANDROID
19 : #include "mozilla/MediaDrmCDMProxy.h"
20 : #endif
21 : #include "mozilla/EMEUtils.h"
22 : #include "nsContentUtils.h"
23 : #include "nsIScriptObjectPrincipal.h"
24 : #include "nsContentTypeParser.h"
25 : #ifdef MOZ_FMP4
26 : #include "MP4Decoder.h"
27 : #endif
28 : #ifdef XP_WIN
29 : #include "mozilla/WindowsVersion.h"
30 : #endif
31 : #include "nsContentCID.h"
32 : #include "nsServiceManagerUtils.h"
33 : #include "mozilla/dom/MediaKeySystemAccess.h"
34 : #include "nsPrintfCString.h"
35 : #include "ChromiumCDMProxy.h"
36 :
37 : namespace mozilla {
38 :
39 : namespace dom {
40 :
41 0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MediaKeys,
42 : mElement,
43 : mParent,
44 : mKeySessions,
45 : mPromises,
46 : mPendingSessions);
47 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaKeys)
48 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaKeys)
49 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaKeys)
50 0 : NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
51 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
52 0 : NS_INTERFACE_MAP_END
53 :
54 0 : MediaKeys::MediaKeys(nsPIDOMWindowInner* aParent,
55 : const nsAString& aKeySystem,
56 0 : const MediaKeySystemConfiguration& aConfig)
57 : : mParent(aParent)
58 : , mKeySystem(aKeySystem)
59 : , mCreatePromiseId(0)
60 0 : , mConfig(aConfig)
61 : {
62 0 : EME_LOG("MediaKeys[%p] constructed keySystem=%s",
63 : this, NS_ConvertUTF16toUTF8(mKeySystem).get());
64 0 : }
65 :
66 0 : MediaKeys::~MediaKeys()
67 : {
68 0 : Shutdown();
69 0 : EME_LOG("MediaKeys[%p] destroyed", this);
70 0 : }
71 :
72 : void
73 0 : MediaKeys::Terminated()
74 : {
75 0 : EME_LOG("MediaKeys[%p] CDM crashed unexpectedly", this);
76 :
77 0 : KeySessionHashMap keySessions;
78 : // Remove entries during iteration will screw it. Make a copy first.
79 0 : for (auto iter = mKeySessions.Iter(); !iter.Done(); iter.Next()) {
80 0 : RefPtr<MediaKeySession>& session = iter.Data();
81 0 : keySessions.Put(session->GetSessionId(), session);
82 : }
83 0 : for (auto iter = keySessions.Iter(); !iter.Done(); iter.Next()) {
84 0 : RefPtr<MediaKeySession>& session = iter.Data();
85 0 : session->OnClosed();
86 : }
87 0 : keySessions.Clear();
88 0 : MOZ_ASSERT(mKeySessions.Count() == 0);
89 :
90 : // Notify the element about that CDM has terminated.
91 0 : if (mElement) {
92 0 : mElement->DecodeError(NS_ERROR_DOM_MEDIA_CDM_ERR);
93 : }
94 :
95 0 : Shutdown();
96 0 : }
97 :
98 : void
99 0 : MediaKeys::Shutdown()
100 : {
101 0 : if (mProxy) {
102 0 : mProxy->Shutdown();
103 0 : mProxy = nullptr;
104 : }
105 :
106 0 : RefPtr<MediaKeys> kungFuDeathGrip = this;
107 :
108 0 : for (auto iter = mPromises.Iter(); !iter.Done(); iter.Next()) {
109 0 : RefPtr<dom::DetailedPromise>& promise = iter.Data();
110 0 : promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
111 0 : NS_LITERAL_CSTRING("Promise still outstanding at MediaKeys shutdown"));
112 0 : Release();
113 : }
114 0 : mPromises.Clear();
115 0 : }
116 :
117 : nsPIDOMWindowInner*
118 0 : MediaKeys::GetParentObject() const
119 : {
120 0 : return mParent;
121 : }
122 :
123 : JSObject*
124 0 : MediaKeys::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
125 : {
126 0 : return MediaKeysBinding::Wrap(aCx, this, aGivenProto);
127 : }
128 :
129 : void
130 0 : MediaKeys::GetKeySystem(nsString& aOutKeySystem) const
131 : {
132 0 : aOutKeySystem.Assign(mKeySystem);
133 0 : }
134 :
135 : already_AddRefed<DetailedPromise>
136 0 : MediaKeys::SetServerCertificate(const ArrayBufferViewOrArrayBuffer& aCert, ErrorResult& aRv)
137 : {
138 0 : RefPtr<DetailedPromise> promise(MakePromise(aRv,
139 0 : NS_LITERAL_CSTRING("MediaKeys.setServerCertificate")));
140 0 : if (aRv.Failed()) {
141 0 : return nullptr;
142 : }
143 :
144 0 : if (!mProxy) {
145 0 : NS_WARNING("Tried to use a MediaKeys without a CDM");
146 0 : promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
147 0 : NS_LITERAL_CSTRING("Null CDM in MediaKeys.setServerCertificate()"));
148 0 : return promise.forget();
149 : }
150 :
151 0 : nsTArray<uint8_t> data;
152 0 : CopyArrayBufferViewOrArrayBufferData(aCert, data);
153 0 : if (data.IsEmpty()) {
154 0 : promise->MaybeReject(NS_ERROR_DOM_TYPE_ERR,
155 0 : NS_LITERAL_CSTRING("Empty certificate passed to MediaKeys.setServerCertificate()"));
156 0 : return promise.forget();
157 : }
158 :
159 0 : mProxy->SetServerCertificate(StorePromise(promise), data);
160 0 : return promise.forget();
161 : }
162 :
163 : already_AddRefed<DetailedPromise>
164 0 : MediaKeys::MakePromise(ErrorResult& aRv, const nsACString& aName)
165 : {
166 0 : nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
167 0 : if (!global) {
168 0 : NS_WARNING("Passed non-global to MediaKeys ctor!");
169 0 : aRv.Throw(NS_ERROR_UNEXPECTED);
170 0 : return nullptr;
171 : }
172 0 : return DetailedPromise::Create(global, aRv, aName);
173 : }
174 :
175 : PromiseId
176 0 : MediaKeys::StorePromise(DetailedPromise* aPromise)
177 : {
178 : static uint32_t sEMEPromiseCount = 1;
179 0 : MOZ_ASSERT(aPromise);
180 0 : uint32_t id = sEMEPromiseCount++;
181 :
182 0 : EME_LOG("MediaKeys[%p]::StorePromise() id=%d", this, id);
183 :
184 : // Keep MediaKeys alive for the lifetime of its promises. Any still-pending
185 : // promises are rejected in Shutdown().
186 0 : AddRef();
187 :
188 : #ifdef DEBUG
189 : // We should not have already stored this promise!
190 0 : for (auto iter = mPromises.ConstIter(); !iter.Done(); iter.Next()) {
191 0 : MOZ_ASSERT(iter.Data() != aPromise);
192 : }
193 : #endif
194 :
195 0 : mPromises.Put(id, aPromise);
196 0 : return id;
197 : }
198 :
199 : void
200 0 : MediaKeys::ConnectPendingPromiseIdWithToken(PromiseId aId, uint32_t aToken)
201 : {
202 : // Should only be called from MediaKeySession::GenerateRequest.
203 0 : mPromiseIdToken.Put(aId, aToken);
204 0 : EME_LOG("MediaKeys[%p]::ConnectPendingPromiseIdWithToken() id=%u => token(%u)",
205 : this, aId, aToken);
206 0 : }
207 :
208 : already_AddRefed<DetailedPromise>
209 0 : MediaKeys::RetrievePromise(PromiseId aId)
210 : {
211 0 : if (!mPromises.Contains(aId)) {
212 0 : NS_WARNING(nsPrintfCString("Tried to retrieve a non-existent promise id=%d", aId).get());
213 0 : return nullptr;
214 : }
215 0 : RefPtr<DetailedPromise> promise;
216 0 : mPromises.Remove(aId, getter_AddRefs(promise));
217 0 : Release();
218 0 : return promise.forget();
219 : }
220 :
221 : void
222 0 : MediaKeys::RejectPromise(PromiseId aId, nsresult aExceptionCode,
223 : const nsCString& aReason)
224 : {
225 0 : EME_LOG("MediaKeys[%p]::RejectPromise(%d, 0x%" PRIx32 ")",
226 : this, aId, static_cast<uint32_t>(aExceptionCode));
227 :
228 0 : RefPtr<DetailedPromise> promise(RetrievePromise(aId));
229 0 : if (!promise) {
230 0 : return;
231 : }
232 :
233 : // This promise could be a createSession or loadSession promise,
234 : // so we might have a pending session waiting to be resolved into
235 : // the promise on success. We've been directed to reject to promise,
236 : // so we can throw away the corresponding session object.
237 0 : uint32_t token = 0;
238 0 : if (mPromiseIdToken.Get(aId, &token)) {
239 0 : MOZ_ASSERT(mPendingSessions.Contains(token));
240 0 : mPendingSessions.Remove(token);
241 0 : mPromiseIdToken.Remove(aId);
242 : }
243 :
244 0 : MOZ_ASSERT(NS_FAILED(aExceptionCode));
245 0 : promise->MaybeReject(aExceptionCode, aReason);
246 :
247 0 : if (mCreatePromiseId == aId) {
248 : // Note: This will probably destroy the MediaKeys object!
249 0 : Release();
250 : }
251 : }
252 :
253 : void
254 0 : MediaKeys::OnSessionIdReady(MediaKeySession* aSession)
255 : {
256 0 : if (!aSession) {
257 0 : NS_WARNING("Invalid MediaKeySession passed to OnSessionIdReady()");
258 0 : return;
259 : }
260 0 : if (mKeySessions.Contains(aSession->GetSessionId())) {
261 0 : NS_WARNING("MediaKeySession's made ready multiple times!");
262 0 : return;
263 : }
264 0 : if (mPendingSessions.Contains(aSession->Token())) {
265 0 : NS_WARNING("MediaKeySession made ready when it wasn't waiting to be ready!");
266 0 : return;
267 : }
268 0 : if (aSession->GetSessionId().IsEmpty()) {
269 0 : NS_WARNING("MediaKeySession with invalid sessionId passed to OnSessionIdReady()");
270 0 : return;
271 : }
272 0 : mKeySessions.Put(aSession->GetSessionId(), aSession);
273 : }
274 :
275 : void
276 0 : MediaKeys::ResolvePromise(PromiseId aId)
277 : {
278 0 : EME_LOG("MediaKeys[%p]::ResolvePromise(%d)", this, aId);
279 :
280 0 : RefPtr<DetailedPromise> promise(RetrievePromise(aId));
281 0 : MOZ_ASSERT(!mPromises.Contains(aId));
282 0 : if (!promise) {
283 0 : return;
284 : }
285 :
286 0 : uint32_t token = 0;
287 0 : if (!mPromiseIdToken.Get(aId, &token)) {
288 0 : promise->MaybeResolveWithUndefined();
289 0 : return;
290 0 : } else if (!mPendingSessions.Contains(token)) {
291 : // Pending session for CreateSession() should be removed when sessionId
292 : // is ready.
293 0 : promise->MaybeResolveWithUndefined();
294 0 : mPromiseIdToken.Remove(aId);
295 0 : return;
296 : }
297 0 : mPromiseIdToken.Remove(aId);
298 :
299 : // We should only resolve LoadSession calls via this path,
300 : // not CreateSession() promises.
301 0 : RefPtr<MediaKeySession> session;
302 0 : mPendingSessions.Remove(token, getter_AddRefs(session));
303 0 : if (!session || session->GetSessionId().IsEmpty()) {
304 0 : NS_WARNING("Received activation for non-existent session!");
305 0 : promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
306 0 : NS_LITERAL_CSTRING("CDM LoadSession() returned a different session ID than requested"));
307 0 : return;
308 : }
309 0 : mKeySessions.Put(session->GetSessionId(), session);
310 0 : promise->MaybeResolve(session);
311 : }
312 :
313 0 : class MediaKeysGMPCrashHelper : public GMPCrashHelper
314 : {
315 : public:
316 0 : explicit MediaKeysGMPCrashHelper(MediaKeys* aMediaKeys)
317 0 : : mMediaKeys(aMediaKeys)
318 : {
319 0 : MOZ_ASSERT(NS_IsMainThread()); // WeakPtr isn't thread safe.
320 0 : }
321 : already_AddRefed<nsPIDOMWindowInner>
322 0 : GetPluginCrashedEventTarget() override
323 : {
324 0 : MOZ_ASSERT(NS_IsMainThread()); // WeakPtr isn't thread safe.
325 0 : EME_LOG("MediaKeysGMPCrashHelper::GetPluginCrashedEventTarget()");
326 0 : return (mMediaKeys && mMediaKeys->GetParentObject()) ?
327 0 : do_AddRef(mMediaKeys->GetParentObject()) : nullptr;
328 : }
329 : private:
330 : WeakPtr<MediaKeys> mMediaKeys;
331 : };
332 :
333 : already_AddRefed<CDMProxy>
334 0 : MediaKeys::CreateCDMProxy(nsIEventTarget* aMainThread)
335 : {
336 0 : RefPtr<CDMProxy> proxy;
337 : #ifdef MOZ_WIDGET_ANDROID
338 : if (IsWidevineKeySystem(mKeySystem)) {
339 : proxy = new MediaDrmCDMProxy(this,
340 : mKeySystem,
341 : mConfig.mDistinctiveIdentifier == MediaKeysRequirement::Required,
342 : mConfig.mPersistentState == MediaKeysRequirement::Required,
343 : aMainThread);
344 : } else
345 : #endif
346 : {
347 0 : if (MediaPrefs::EMEChromiumAPIEnabled()) {
348 : proxy = new ChromiumCDMProxy(
349 : this,
350 : mKeySystem,
351 0 : new MediaKeysGMPCrashHelper(this),
352 0 : mConfig.mDistinctiveIdentifier == MediaKeysRequirement::Required,
353 0 : mConfig.mPersistentState == MediaKeysRequirement::Required,
354 0 : aMainThread);
355 : } else {
356 : proxy = new GMPCDMProxy(
357 : this,
358 : mKeySystem,
359 0 : new MediaKeysGMPCrashHelper(this),
360 0 : mConfig.mDistinctiveIdentifier == MediaKeysRequirement::Required,
361 0 : mConfig.mPersistentState == MediaKeysRequirement::Required,
362 0 : aMainThread);
363 : }
364 : }
365 0 : return proxy.forget();
366 : }
367 :
368 : already_AddRefed<DetailedPromise>
369 0 : MediaKeys::Init(ErrorResult& aRv)
370 : {
371 0 : RefPtr<DetailedPromise> promise(MakePromise(aRv,
372 0 : NS_LITERAL_CSTRING("MediaKeys::Init()")));
373 0 : if (aRv.Failed()) {
374 0 : return nullptr;
375 : }
376 :
377 : // Determine principal (at creation time) of the MediaKeys object.
378 0 : nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(GetParentObject());
379 0 : if (!sop) {
380 0 : promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
381 0 : NS_LITERAL_CSTRING("Couldn't get script principal in MediaKeys::Init"));
382 0 : return promise.forget();
383 : }
384 0 : mPrincipal = sop->GetPrincipal();
385 :
386 : // Determine principal of the "top-level" window; the principal of the
387 : // page that will display in the URL bar.
388 0 : nsCOMPtr<nsPIDOMWindowInner> window = GetParentObject();
389 0 : if (!window) {
390 0 : promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
391 0 : NS_LITERAL_CSTRING("Couldn't get top-level window in MediaKeys::Init"));
392 0 : return promise.forget();
393 : }
394 0 : nsCOMPtr<nsPIDOMWindowOuter> top = window->GetOuterWindow()->GetTop();
395 0 : if (!top || !top->GetExtantDoc()) {
396 0 : promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
397 0 : NS_LITERAL_CSTRING("Couldn't get document in MediaKeys::Init"));
398 0 : return promise.forget();
399 : }
400 :
401 0 : mTopLevelPrincipal = top->GetExtantDoc()->NodePrincipal();
402 :
403 0 : if (!mPrincipal || !mTopLevelPrincipal) {
404 0 : NS_WARNING("Failed to get principals when creating MediaKeys");
405 0 : promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
406 0 : NS_LITERAL_CSTRING("Couldn't get principal(s) in MediaKeys::Init"));
407 0 : return promise.forget();
408 : }
409 :
410 0 : nsAutoCString origin;
411 0 : nsresult rv = mPrincipal->GetOrigin(origin);
412 0 : if (NS_FAILED(rv)) {
413 0 : promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
414 0 : NS_LITERAL_CSTRING("Couldn't get principal origin string in MediaKeys::Init"));
415 0 : return promise.forget();
416 : }
417 0 : nsAutoCString topLevelOrigin;
418 0 : rv = mTopLevelPrincipal->GetOrigin(topLevelOrigin);
419 0 : if (NS_FAILED(rv)) {
420 0 : promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
421 0 : NS_LITERAL_CSTRING("Couldn't get top-level principal origin string in MediaKeys::Init"));
422 0 : return promise.forget();
423 : }
424 :
425 0 : EME_LOG("MediaKeys[%p]::Create() (%s, %s)",
426 : this,
427 : origin.get(),
428 : topLevelOrigin.get());
429 :
430 0 : mProxy = CreateCDMProxy(top->GetExtantDoc()->EventTargetFor(TaskCategory::Other));
431 :
432 : // The CDMProxy's initialization is asynchronous. The MediaKeys is
433 : // refcounted, and its instance is returned to JS by promise once
434 : // it's been initialized. No external refs exist to the MediaKeys while
435 : // we're waiting for the promise to be resolved, so we must hold a
436 : // reference to the new MediaKeys object until it's been created,
437 : // or its creation has failed. Store the id of the promise returned
438 : // here, and hold a self-reference until that promise is resolved or
439 : // rejected.
440 0 : MOZ_ASSERT(!mCreatePromiseId, "Should only be created once!");
441 0 : mCreatePromiseId = StorePromise(promise);
442 0 : AddRef();
443 0 : mProxy->Init(mCreatePromiseId,
444 0 : NS_ConvertUTF8toUTF16(origin),
445 0 : NS_ConvertUTF8toUTF16(topLevelOrigin),
446 0 : KeySystemToGMPName(mKeySystem));
447 :
448 0 : return promise.forget();
449 : }
450 :
451 : void
452 0 : MediaKeys::OnCDMCreated(PromiseId aId, const uint32_t aPluginId)
453 : {
454 0 : RefPtr<DetailedPromise> promise(RetrievePromise(aId));
455 0 : if (!promise) {
456 0 : return;
457 : }
458 0 : RefPtr<MediaKeys> keys(this);
459 0 : EME_LOG("MediaKeys[%p]::OnCDMCreated() resolve promise id=%d", this, aId);
460 0 : promise->MaybeResolve(keys);
461 0 : if (mCreatePromiseId == aId) {
462 0 : Release();
463 : }
464 :
465 0 : MediaKeySystemAccess::NotifyObservers(mParent,
466 : mKeySystem,
467 0 : MediaKeySystemStatus::Cdm_created);
468 :
469 0 : Telemetry::Accumulate(Telemetry::VIDEO_CDM_CREATED, ToCDMTypeTelemetryEnum(mKeySystem));
470 : }
471 :
472 : static bool
473 0 : IsSessionTypeSupported(const MediaKeySessionType aSessionType,
474 : const MediaKeySystemConfiguration& aConfig)
475 : {
476 0 : if (aSessionType == MediaKeySessionType::Temporary) {
477 : // Temporary is always supported.
478 0 : return true;
479 : }
480 0 : if (!aConfig.mSessionTypes.WasPassed()) {
481 : // No other session types supported.
482 0 : return false;
483 : }
484 0 : return aConfig.mSessionTypes.Value().Contains(ToString(aSessionType));
485 : }
486 :
487 : already_AddRefed<MediaKeySession>
488 0 : MediaKeys::CreateSession(JSContext* aCx,
489 : MediaKeySessionType aSessionType,
490 : ErrorResult& aRv)
491 : {
492 0 : if (!IsSessionTypeSupported(aSessionType, mConfig)) {
493 0 : EME_LOG("MediaKeys[%p] CreateSession() failed, unsupported session type", this);
494 0 : aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
495 0 : return nullptr;
496 : }
497 :
498 0 : if (!mProxy) {
499 0 : NS_WARNING("Tried to use a MediaKeys which lost its CDM");
500 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
501 0 : return nullptr;
502 : }
503 :
504 0 : EME_LOG("MediaKeys[%p] Creating session", this);
505 :
506 : RefPtr<MediaKeySession> session = new MediaKeySession(aCx,
507 0 : GetParentObject(),
508 : this,
509 : mKeySystem,
510 : aSessionType,
511 0 : aRv);
512 :
513 0 : if (aRv.Failed()) {
514 0 : return nullptr;
515 : }
516 :
517 : // Add session to the set of sessions awaiting their sessionId being ready.
518 0 : mPendingSessions.Put(session->Token(), session);
519 :
520 0 : return session.forget();
521 : }
522 :
523 : void
524 0 : MediaKeys::OnSessionLoaded(PromiseId aId, bool aSuccess)
525 : {
526 0 : RefPtr<DetailedPromise> promise(RetrievePromise(aId));
527 0 : if (!promise) {
528 0 : return;
529 : }
530 0 : EME_LOG("MediaKeys[%p]::OnSessionLoaded() resolve promise id=%d", this, aId);
531 :
532 0 : promise->MaybeResolve(aSuccess);
533 : }
534 :
535 : void
536 0 : MediaKeys::OnSessionClosed(MediaKeySession* aSession)
537 : {
538 0 : nsAutoString id;
539 0 : aSession->GetSessionId(id);
540 0 : mKeySessions.Remove(id);
541 0 : }
542 :
543 : already_AddRefed<MediaKeySession>
544 0 : MediaKeys::GetSession(const nsAString& aSessionId)
545 : {
546 0 : RefPtr<MediaKeySession> session;
547 0 : mKeySessions.Get(aSessionId, getter_AddRefs(session));
548 0 : return session.forget();
549 : }
550 :
551 : already_AddRefed<MediaKeySession>
552 0 : MediaKeys::GetPendingSession(uint32_t aToken)
553 : {
554 0 : RefPtr<MediaKeySession> session;
555 0 : mPendingSessions.Get(aToken, getter_AddRefs(session));
556 0 : mPendingSessions.Remove(aToken);
557 0 : return session.forget();
558 : }
559 :
560 : bool
561 0 : MediaKeys::IsBoundToMediaElement() const
562 : {
563 0 : MOZ_ASSERT(NS_IsMainThread());
564 0 : return mElement != nullptr;
565 : }
566 :
567 : nsresult
568 0 : MediaKeys::Bind(HTMLMediaElement* aElement)
569 : {
570 0 : MOZ_ASSERT(NS_IsMainThread());
571 0 : if (IsBoundToMediaElement()) {
572 0 : return NS_ERROR_FAILURE;
573 : }
574 :
575 0 : mElement = aElement;
576 :
577 0 : return NS_OK;
578 : }
579 :
580 : void
581 0 : MediaKeys::Unbind()
582 : {
583 0 : MOZ_ASSERT(NS_IsMainThread());
584 0 : mElement = nullptr;
585 0 : }
586 :
587 : void
588 0 : MediaKeys::GetSessionsInfo(nsString& sessionsInfo)
589 : {
590 0 : for (KeySessionHashMap::Iterator it = mKeySessions.Iter();
591 0 : !it.Done();
592 0 : it.Next()) {
593 0 : MediaKeySession* keySession = it.Data();
594 0 : nsString sessionID;
595 0 : keySession->GetSessionId(sessionID);
596 0 : sessionsInfo.AppendLiteral("(sid=");
597 0 : sessionsInfo.Append(sessionID);
598 0 : MediaKeyStatusMap* keyStatusMap = keySession->KeyStatuses();
599 0 : for (uint32_t i = 0; i < keyStatusMap->GetIterableLength(); i++) {
600 0 : nsString keyID = keyStatusMap->GetKeyIDAsHexString(i);
601 0 : sessionsInfo.AppendLiteral("(kid=");
602 0 : sessionsInfo.Append(keyID);
603 : using IntegerType = typename std::underlying_type<MediaKeyStatus>::type;
604 0 : auto idx = static_cast<IntegerType>(keyStatusMap->GetValueAtIndex(i));
605 0 : const char* keyStatus = MediaKeyStatusValues::strings[idx].value;
606 0 : sessionsInfo.AppendLiteral(" status=");
607 0 : sessionsInfo.Append(
608 0 : NS_ConvertUTF8toUTF16((nsDependentCString(keyStatus))));
609 0 : sessionsInfo.AppendLiteral(")");
610 : }
611 0 : sessionsInfo.AppendLiteral(")");
612 : }
613 0 : }
614 :
615 : } // namespace dom
616 9 : } // namespace mozilla
|