Coverage Report

Created: 2018-09-25 14:53

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