Coverage Report

Created: 2018-09-25 14:53

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