Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/eme/MediaKeySystemAccess.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim:set ts=2 sw=2 sts=2 et cindent: */
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
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "mozilla/dom/MediaKeySystemAccess.h"
8
#include "mozilla/dom/MediaKeySystemAccessBinding.h"
9
#include "mozilla/dom/MediaKeySession.h"
10
#include "mozilla/Preferences.h"
11
#include "mozilla/StaticPrefs.h"
12
#include "MediaContainerType.h"
13
#include "nsMimeTypes.h"
14
#ifdef XP_WIN
15
#include "WMFDecoderModule.h"
16
#endif
17
#include "nsContentCID.h"
18
#include "nsServiceManagerUtils.h"
19
#include "mozIGeckoMediaPluginService.h"
20
#include "VideoUtils.h"
21
#include "mozilla/Services.h"
22
#include "nsIObserverService.h"
23
#include "mozilla/EMEUtils.h"
24
#include "GMPUtils.h"
25
#include "nsAppDirectoryServiceDefs.h"
26
#include "nsDirectoryServiceUtils.h"
27
#include "nsDirectoryServiceDefs.h"
28
#include "nsXULAppAPI.h"
29
#include "DecoderDoctorDiagnostics.h"
30
#include "WebMDecoder.h"
31
#include "mozilla/StaticPtr.h"
32
#include "mozilla/ClearOnShutdown.h"
33
#include "nsUnicharUtils.h"
34
#include "mozilla/dom/MediaSource.h"
35
#include "DecoderTraits.h"
36
#ifdef MOZ_WIDGET_ANDROID
37
#include "FennecJNIWrappers.h"
38
#include "GeneratedJNIWrappers.h"
39
#endif
40
#include <functional>
41
42
namespace mozilla {
43
namespace dom {
44
45
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MediaKeySystemAccess,
46
                                      mParent)
47
NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaKeySystemAccess)
48
NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaKeySystemAccess)
49
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaKeySystemAccess)
50
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
51
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
52
0
NS_INTERFACE_MAP_END
53
54
static nsCString
55
ToCString(const MediaKeySystemConfiguration& aConfig);
56
57
MediaKeySystemAccess::MediaKeySystemAccess(nsPIDOMWindowInner* aParent,
58
                                           const nsAString& aKeySystem,
59
                                           const MediaKeySystemConfiguration& aConfig)
60
  : mParent(aParent)
61
  , mKeySystem(aKeySystem)
62
  , mConfig(aConfig)
63
0
{
64
0
  EME_LOG("Created MediaKeySystemAccess for keysystem=%s config=%s",
65
0
          NS_ConvertUTF16toUTF8(mKeySystem).get(), mozilla::dom::ToCString(mConfig).get());
66
0
}
67
68
MediaKeySystemAccess::~MediaKeySystemAccess()
69
0
{
70
0
}
71
72
JSObject*
73
MediaKeySystemAccess::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
74
0
{
75
0
  return MediaKeySystemAccess_Binding::Wrap(aCx, this, aGivenProto);
76
0
}
77
78
nsPIDOMWindowInner*
79
MediaKeySystemAccess::GetParentObject() const
80
0
{
81
0
  return mParent;
82
0
}
83
84
void
85
MediaKeySystemAccess::GetKeySystem(nsString& aOutKeySystem) const
86
0
{
87
0
  aOutKeySystem.Assign(mKeySystem);
88
0
}
89
90
void
91
MediaKeySystemAccess::GetConfiguration(MediaKeySystemConfiguration& aConfig)
92
0
{
93
0
  aConfig = mConfig;
94
0
}
95
96
already_AddRefed<Promise>
97
MediaKeySystemAccess::CreateMediaKeys(ErrorResult& aRv)
98
0
{
99
0
  RefPtr<MediaKeys> keys(new MediaKeys(mParent,
100
0
                                       mKeySystem,
101
0
                                       mConfig));
102
0
  return keys->Init(aRv);
103
0
}
104
105
static bool
106
HavePluginForKeySystem(const nsCString& aKeySystem)
107
0
{
108
0
  nsCString api = NS_LITERAL_CSTRING(CHROMIUM_CDM_API);
109
0
110
0
  bool havePlugin = HaveGMPFor(api, { aKeySystem });
111
#ifdef MOZ_WIDGET_ANDROID
112
  // Check if we can use MediaDrm for this keysystem.
113
  if (!havePlugin) {
114
    havePlugin = mozilla::java::MediaDrmProxy::IsSchemeSupported(aKeySystem);
115
  }
116
#endif
117
  return havePlugin;
118
0
}
119
120
static MediaKeySystemStatus
121
EnsureCDMInstalled(const nsAString& aKeySystem,
122
                    nsACString& aOutMessage)
123
0
{
124
0
  if (!HavePluginForKeySystem(NS_ConvertUTF16toUTF8(aKeySystem))) {
125
0
    aOutMessage = NS_LITERAL_CSTRING("CDM is not installed");
126
0
    return MediaKeySystemStatus::Cdm_not_installed;
127
0
  }
128
0
129
0
  return MediaKeySystemStatus::Available;
130
0
}
131
132
/* static */
133
MediaKeySystemStatus
134
MediaKeySystemAccess::GetKeySystemStatus(const nsAString& aKeySystem,
135
                                         nsACString& aOutMessage)
136
0
{
137
0
  MOZ_ASSERT(StaticPrefs::MediaEmeEnabled() ||
138
0
             IsClearkeyKeySystem(aKeySystem));
139
0
140
0
  if (IsClearkeyKeySystem(aKeySystem)) {
141
0
    return EnsureCDMInstalled(aKeySystem, aOutMessage);
142
0
  }
143
0
144
0
  if (IsWidevineKeySystem(aKeySystem)) {
145
0
    if (Preferences::GetBool("media.gmp-widevinecdm.visible", false)) {
146
0
      if (!Preferences::GetBool("media.gmp-widevinecdm.enabled", false)) {
147
0
        aOutMessage = NS_LITERAL_CSTRING("Widevine EME disabled");
148
0
        return MediaKeySystemStatus::Cdm_disabled;
149
0
      }
150
0
      return EnsureCDMInstalled(aKeySystem, aOutMessage);
151
#ifdef MOZ_WIDGET_ANDROID
152
    } else if (Preferences::GetBool("media.mediadrm-widevinecdm.visible", false)) {
153
        nsCString keySystem = NS_ConvertUTF16toUTF8(aKeySystem);
154
        bool supported = mozilla::java::MediaDrmProxy::IsSchemeSupported(keySystem);
155
        if (!supported) {
156
          aOutMessage = NS_LITERAL_CSTRING("KeySystem or Minimum API level not met for Widevine EME");
157
          return MediaKeySystemStatus::Cdm_not_supported;
158
        }
159
        return MediaKeySystemStatus::Available;
160
#endif
161
    }
162
0
  }
163
0
164
0
  return MediaKeySystemStatus::Cdm_not_supported;
165
0
}
166
167
typedef nsCString EMECodecString;
168
169
static NS_NAMED_LITERAL_CSTRING(EME_CODEC_AAC, "aac");
170
static NS_NAMED_LITERAL_CSTRING(EME_CODEC_OPUS, "opus");
171
static NS_NAMED_LITERAL_CSTRING(EME_CODEC_VORBIS, "vorbis");
172
static NS_NAMED_LITERAL_CSTRING(EME_CODEC_FLAC, "flac");
173
static NS_NAMED_LITERAL_CSTRING(EME_CODEC_H264, "h264");
174
static NS_NAMED_LITERAL_CSTRING(EME_CODEC_VP8, "vp8");
175
static NS_NAMED_LITERAL_CSTRING(EME_CODEC_VP9, "vp9");
176
177
EMECodecString
178
ToEMEAPICodecString(const nsString& aCodec)
179
0
{
180
0
  if (IsAACCodecString(aCodec)) {
181
0
    return EME_CODEC_AAC;
182
0
  }
183
0
  if (aCodec.EqualsLiteral("opus")) {
184
0
    return EME_CODEC_OPUS;
185
0
  }
186
0
  if (aCodec.EqualsLiteral("vorbis")) {
187
0
    return EME_CODEC_VORBIS;
188
0
  }
189
0
  if (aCodec.EqualsLiteral("flac")) {
190
0
    return EME_CODEC_FLAC;
191
0
  }
192
0
  if (IsH264CodecString(aCodec)) {
193
0
    return EME_CODEC_H264;
194
0
  }
195
0
  if (IsVP8CodecString(aCodec)) {
196
0
    return EME_CODEC_VP8;
197
0
  }
198
0
  if (IsVP9CodecString(aCodec)) {
199
0
    return EME_CODEC_VP9;
200
0
  }
201
0
  return EmptyCString();
202
0
}
203
204
// A codec can be decrypted-and-decoded by the CDM, or only decrypted
205
// by the CDM and decoded by Gecko. Not both.
206
struct KeySystemContainerSupport
207
{
208
  bool IsSupported() const
209
0
  {
210
0
    return !mCodecsDecoded.IsEmpty() || !mCodecsDecrypted.IsEmpty();
211
0
  }
212
213
  // CDM decrypts and decodes using a DRM robust decoder, and passes decoded
214
  // samples back to Gecko for rendering.
215
  bool DecryptsAndDecodes(EMECodecString aCodec) const
216
0
  {
217
0
    return mCodecsDecoded.Contains(aCodec);
218
0
  }
219
220
  // CDM decrypts and passes the decrypted samples back to Gecko for decoding.
221
  bool Decrypts(EMECodecString aCodec) const
222
0
  {
223
0
    return mCodecsDecrypted.Contains(aCodec);
224
0
  }
225
226
  void SetCanDecryptAndDecode(EMECodecString aCodec)
227
0
  {
228
0
    // Can't both decrypt and decrypt-and-decode a codec.
229
0
    MOZ_ASSERT(!Decrypts(aCodec));
230
0
    // Prevent duplicates.
231
0
    MOZ_ASSERT(!DecryptsAndDecodes(aCodec));
232
0
    mCodecsDecoded.AppendElement(aCodec);
233
0
  }
234
235
  void SetCanDecrypt(EMECodecString aCodec)
236
0
  {
237
0
    // Prevent duplicates.
238
0
    MOZ_ASSERT(!Decrypts(aCodec));
239
0
    // Can't both decrypt and decrypt-and-decode a codec.
240
0
    MOZ_ASSERT(!DecryptsAndDecodes(aCodec));
241
0
    mCodecsDecrypted.AppendElement(aCodec);
242
0
  }
243
244
private:
245
  nsTArray<EMECodecString> mCodecsDecoded;
246
  nsTArray<EMECodecString> mCodecsDecrypted;
247
};
248
249
enum class KeySystemFeatureSupport
250
{
251
  Prohibited = 1,
252
  Requestable = 2,
253
  Required = 3,
254
};
255
256
struct KeySystemConfig
257
{
258
  nsString mKeySystem;
259
  nsTArray<nsString> mInitDataTypes;
260
  KeySystemFeatureSupport mPersistentState = KeySystemFeatureSupport::Prohibited;
261
  KeySystemFeatureSupport mDistinctiveIdentifier = KeySystemFeatureSupport::Prohibited;
262
  nsTArray<MediaKeySessionType> mSessionTypes;
263
  nsTArray<nsString> mVideoRobustness;
264
  nsTArray<nsString> mAudioRobustness;
265
  KeySystemContainerSupport mMP4;
266
  KeySystemContainerSupport mWebM;
267
};
268
269
static nsTArray<KeySystemConfig>
270
GetSupportedKeySystems()
271
0
{
272
0
  nsTArray<KeySystemConfig> keySystemConfigs;
273
0
274
0
  {
275
0
    if (HavePluginForKeySystem(kEMEKeySystemClearkey)) {
276
0
      KeySystemConfig clearkey;
277
0
      clearkey.mKeySystem = NS_ConvertUTF8toUTF16(kEMEKeySystemClearkey);
278
0
      clearkey.mInitDataTypes.AppendElement(NS_LITERAL_STRING("cenc"));
279
0
      clearkey.mInitDataTypes.AppendElement(NS_LITERAL_STRING("keyids"));
280
0
      clearkey.mInitDataTypes.AppendElement(NS_LITERAL_STRING("webm"));
281
0
      clearkey.mPersistentState = KeySystemFeatureSupport::Requestable;
282
0
      clearkey.mDistinctiveIdentifier = KeySystemFeatureSupport::Prohibited;
283
0
      clearkey.mSessionTypes.AppendElement(MediaKeySessionType::Temporary);
284
0
      if (StaticPrefs::MediaClearkeyPersistentLicenseEnabled()) {
285
0
        clearkey.mSessionTypes.AppendElement(MediaKeySessionType::Persistent_license);
286
0
      }
287
#if defined(XP_WIN)
288
      // Clearkey CDM uses WMF's H.264 decoder on Windows.
289
      if (WMFDecoderModule::HasH264()) {
290
        clearkey.mMP4.SetCanDecryptAndDecode(EME_CODEC_H264);
291
      } else {
292
        clearkey.mMP4.SetCanDecrypt(EME_CODEC_H264);
293
      }
294
#else
295
      clearkey.mMP4.SetCanDecrypt(EME_CODEC_H264);
296
0
#endif
297
0
      clearkey.mMP4.SetCanDecrypt(EME_CODEC_AAC);
298
0
      clearkey.mMP4.SetCanDecrypt(EME_CODEC_FLAC);
299
0
      clearkey.mMP4.SetCanDecrypt(EME_CODEC_OPUS);
300
0
      if (Preferences::GetBool("media.eme.vp9-in-mp4.enabled", false)) {
301
0
        clearkey.mMP4.SetCanDecrypt(EME_CODEC_VP9);
302
0
      }
303
0
      clearkey.mWebM.SetCanDecrypt(EME_CODEC_VORBIS);
304
0
      clearkey.mWebM.SetCanDecrypt(EME_CODEC_OPUS);
305
0
      clearkey.mWebM.SetCanDecrypt(EME_CODEC_VP8);
306
0
      clearkey.mWebM.SetCanDecrypt(EME_CODEC_VP9);
307
0
      keySystemConfigs.AppendElement(std::move(clearkey));
308
0
    }
309
0
  }
310
0
  {
311
0
    if (HavePluginForKeySystem(kEMEKeySystemWidevine)) {
312
0
      KeySystemConfig widevine;
313
0
      widevine.mKeySystem = NS_ConvertUTF8toUTF16(kEMEKeySystemWidevine);
314
0
      widevine.mInitDataTypes.AppendElement(NS_LITERAL_STRING("cenc"));
315
0
      widevine.mInitDataTypes.AppendElement(NS_LITERAL_STRING("keyids"));
316
0
      widevine.mInitDataTypes.AppendElement(NS_LITERAL_STRING("webm"));
317
0
      widevine.mPersistentState = KeySystemFeatureSupport::Requestable;
318
0
      widevine.mDistinctiveIdentifier = KeySystemFeatureSupport::Prohibited;
319
0
      widevine.mSessionTypes.AppendElement(MediaKeySessionType::Temporary);
320
#ifdef MOZ_WIDGET_ANDROID
321
      widevine.mSessionTypes.AppendElement(MediaKeySessionType::Persistent_license);
322
#endif
323
0
      widevine.mAudioRobustness.AppendElement(NS_LITERAL_STRING("SW_SECURE_CRYPTO"));
324
0
      widevine.mVideoRobustness.AppendElement(NS_LITERAL_STRING("SW_SECURE_CRYPTO"));
325
0
      widevine.mVideoRobustness.AppendElement(NS_LITERAL_STRING("SW_SECURE_DECODE"));
326
#if defined(XP_WIN)
327
      // Widevine CDM doesn't include an AAC decoder. So if WMF can't
328
      // decode AAC, and a codec wasn't specified, be conservative
329
      // and reject the MediaKeys request, since we assume Widevine
330
      // will be used with AAC.
331
      if (WMFDecoderModule::HasAAC()) {
332
        widevine.mMP4.SetCanDecrypt(EME_CODEC_AAC);
333
      }
334
#elif !defined(MOZ_WIDGET_ANDROID)
335
      widevine.mMP4.SetCanDecrypt(EME_CODEC_AAC);
336
0
#endif
337
0
      widevine.mMP4.SetCanDecrypt(EME_CODEC_FLAC);
338
0
      widevine.mMP4.SetCanDecrypt(EME_CODEC_OPUS);
339
0
340
#if defined(MOZ_WIDGET_ANDROID)
341
      using namespace mozilla::java;
342
      // MediaDrm.isCryptoSchemeSupported only allows passing
343
      // "video/mp4" or "video/webm" for mimetype string.
344
      // See https://developer.android.com/reference/android/media/MediaDrm.html#isCryptoSchemeSupported(java.util.UUID, java.lang.String)
345
      // for more detail.
346
      typedef struct {
347
        const nsCString& mMimeType;
348
        const nsCString& mEMECodecType;
349
        const char16_t* mCodecType;
350
        KeySystemContainerSupport* mSupportType;
351
      } DataForValidation;
352
353
      DataForValidation validationList[] = {
354
        { nsCString(VIDEO_MP4), EME_CODEC_H264, MediaDrmProxy::AVC, &widevine.mMP4 },
355
        { nsCString(VIDEO_MP4), EME_CODEC_VP9, MediaDrmProxy::AVC, &widevine.mMP4 },
356
        { nsCString(AUDIO_MP4), EME_CODEC_AAC, MediaDrmProxy::AAC, &widevine.mMP4 },
357
        { nsCString(AUDIO_MP4), EME_CODEC_FLAC, MediaDrmProxy::FLAC, &widevine.mMP4 },
358
        { nsCString(AUDIO_MP4), EME_CODEC_OPUS, MediaDrmProxy::OPUS, &widevine.mMP4 },
359
        { nsCString(VIDEO_WEBM), EME_CODEC_VP8, MediaDrmProxy::VP8, &widevine.mWebM },
360
        { nsCString(VIDEO_WEBM), EME_CODEC_VP9, MediaDrmProxy::VP9, &widevine.mWebM},
361
        { nsCString(AUDIO_WEBM), EME_CODEC_VORBIS, MediaDrmProxy::VORBIS, &widevine.mWebM},
362
        { nsCString(AUDIO_WEBM), EME_CODEC_OPUS, MediaDrmProxy::OPUS, &widevine.mWebM},
363
      };
364
365
      for (const auto& data: validationList) {
366
        if (MediaDrmProxy::IsCryptoSchemeSupported(kEMEKeySystemWidevine,
367
                                                   data.mMimeType)) {
368
          if (MediaDrmProxy::CanDecode(data.mCodecType)) {
369
            data.mSupportType->SetCanDecryptAndDecode(data.mEMECodecType);
370
          } else {
371
            data.mSupportType->SetCanDecrypt(data.mEMECodecType);
372
          }
373
        }
374
      }
375
#else
376
      widevine.mMP4.SetCanDecryptAndDecode(EME_CODEC_H264);
377
0
      if (Preferences::GetBool("media.eme.vp9-in-mp4.enabled", false)) {
378
0
        widevine.mMP4.SetCanDecryptAndDecode(EME_CODEC_VP9);
379
0
      }
380
0
      widevine.mWebM.SetCanDecrypt(EME_CODEC_VORBIS);
381
0
      widevine.mWebM.SetCanDecrypt(EME_CODEC_OPUS);
382
0
      widevine.mWebM.SetCanDecryptAndDecode(EME_CODEC_VP8);
383
0
      widevine.mWebM.SetCanDecryptAndDecode(EME_CODEC_VP9);
384
0
#endif
385
0
      keySystemConfigs.AppendElement(std::move(widevine));
386
0
    }
387
0
  }
388
0
389
0
  return keySystemConfigs;
390
0
}
391
392
static bool
393
GetKeySystemConfig(const nsAString& aKeySystem, KeySystemConfig& aOutKeySystemConfig)
394
0
{
395
0
  for (auto&& config : GetSupportedKeySystems()) {
396
0
    if (config.mKeySystem.Equals(aKeySystem)) {
397
0
      aOutKeySystemConfig = std::move(config);
398
0
      return true;
399
0
    }
400
0
  }
401
0
  // No matching key system found.
402
0
  return false;
403
0
}
404
405
/* static */
406
bool
407
MediaKeySystemAccess::KeySystemSupportsInitDataType(const nsAString& aKeySystem,
408
                                                    const nsAString& aInitDataType)
409
0
{
410
0
  KeySystemConfig implementation;
411
0
  return GetKeySystemConfig(aKeySystem, implementation) &&
412
0
         implementation.mInitDataTypes.Contains(aInitDataType);
413
0
}
414
415
enum CodecType
416
{
417
  Audio,
418
  Video,
419
  Invalid
420
};
421
422
static bool
423
CanDecryptAndDecode(const nsString& aKeySystem,
424
                    const nsString& aContentType,
425
                    CodecType aCodecType,
426
                    const KeySystemContainerSupport& aContainerSupport,
427
                    const nsTArray<EMECodecString>& aCodecs,
428
                    DecoderDoctorDiagnostics* aDiagnostics)
429
0
{
430
0
  MOZ_ASSERT(aCodecType != Invalid);
431
0
  for (const EMECodecString& codec : aCodecs) {
432
0
    MOZ_ASSERT(!codec.IsEmpty());
433
0
434
0
    if (aContainerSupport.DecryptsAndDecodes(codec)) {
435
0
      // GMP can decrypt-and-decode this codec.
436
0
      continue;
437
0
    }
438
0
439
0
    if (aContainerSupport.Decrypts(codec) &&
440
0
        NS_SUCCEEDED(MediaSource::IsTypeSupported(aContentType, aDiagnostics))) {
441
0
      // GMP can decrypt and is allowed to return compressed samples to
442
0
      // Gecko to decode, and Gecko has a decoder.
443
0
      continue;
444
0
    }
445
0
446
0
    // Neither the GMP nor Gecko can both decrypt and decode. We don't
447
0
    // support this codec.
448
0
449
#if defined(XP_WIN)
450
    // Widevine CDM doesn't include an AAC decoder. So if WMF can't
451
    // decode AAC, and a codec wasn't specified, be conservative
452
    // and reject the MediaKeys request, since we assume Widevine
453
    // will be used with AAC.
454
    if (codec == EME_CODEC_AAC &&
455
        IsWidevineKeySystem(aKeySystem) &&
456
        !WMFDecoderModule::HasAAC()) {
457
      if (aDiagnostics) {
458
        aDiagnostics->SetKeySystemIssue(
459
          DecoderDoctorDiagnostics::eWidevineWithNoWMF);
460
      }
461
    }
462
#endif
463
0
    return false;
464
0
  }
465
0
  return true;
466
0
}
467
468
static bool
469
ToSessionType(const nsAString& aSessionType, MediaKeySessionType& aOutType)
470
0
{
471
0
  if (aSessionType.Equals(ToString(MediaKeySessionType::Temporary))) {
472
0
    aOutType = MediaKeySessionType::Temporary;
473
0
    return true;
474
0
  }
475
0
  if (aSessionType.Equals(ToString(MediaKeySessionType::Persistent_license))) {
476
0
    aOutType = MediaKeySessionType::Persistent_license;
477
0
    return true;
478
0
  }
479
0
  return false;
480
0
}
481
482
// 5.2.1 Is persistent session type?
483
static bool
484
IsPersistentSessionType(MediaKeySessionType aSessionType)
485
0
{
486
0
  return aSessionType == MediaKeySessionType::Persistent_license;
487
0
}
488
489
CodecType
490
GetMajorType(const MediaMIMEType& aMIMEType)
491
0
{
492
0
  if (aMIMEType.HasAudioMajorType()) {
493
0
    return Audio;
494
0
  }
495
0
  if (aMIMEType.HasVideoMajorType()) {
496
0
    return Video;
497
0
  }
498
0
  return Invalid;
499
0
}
500
501
static CodecType
502
GetCodecType(const EMECodecString& aCodec)
503
0
{
504
0
  if (aCodec.Equals(EME_CODEC_AAC) ||
505
0
      aCodec.Equals(EME_CODEC_OPUS) ||
506
0
      aCodec.Equals(EME_CODEC_VORBIS) ||
507
0
      aCodec.Equals(EME_CODEC_FLAC)) {
508
0
    return Audio;
509
0
  }
510
0
  if (aCodec.Equals(EME_CODEC_H264) ||
511
0
      aCodec.Equals(EME_CODEC_VP8) ||
512
0
      aCodec.Equals(EME_CODEC_VP9)) {
513
0
    return Video;
514
0
  }
515
0
  return Invalid;
516
0
}
517
518
static bool
519
AllCodecsOfType(const nsTArray<EMECodecString>& aCodecs, const CodecType aCodecType)
520
0
{
521
0
  for (const EMECodecString& codec : aCodecs) {
522
0
    if (GetCodecType(codec) != aCodecType) {
523
0
      return false;
524
0
    }
525
0
  }
526
0
  return true;
527
0
}
528
529
static bool
530
IsParameterUnrecognized(const nsAString& aContentType)
531
0
{
532
0
  nsAutoString contentType(aContentType);
533
0
  contentType.StripWhitespace();
534
0
535
0
  nsTArray<nsString> params;
536
0
  nsAString::const_iterator start, end, semicolon, equalSign;
537
0
  contentType.BeginReading(start);
538
0
  contentType.EndReading(end);
539
0
  semicolon = start;
540
0
  // Find any substring between ';' & '='.
541
0
  while (semicolon != end) {
542
0
    if (FindCharInReadable(';', semicolon, end)) {
543
0
      equalSign = ++semicolon;
544
0
      if (FindCharInReadable('=', equalSign, end)) {
545
0
        params.AppendElement(Substring(semicolon, equalSign));
546
0
        semicolon = equalSign;
547
0
      }
548
0
    }
549
0
  }
550
0
551
0
  for (auto param : params) {
552
0
    if (!param.LowerCaseEqualsLiteral("codecs") &&
553
0
        !param.LowerCaseEqualsLiteral("profiles")) {
554
0
      return true;
555
0
    }
556
0
  }
557
0
  return false;
558
0
}
559
560
// 3.1.2.3 Get Supported Capabilities for Audio/Video Type
561
static Sequence<MediaKeySystemMediaCapability>
562
GetSupportedCapabilities(
563
  const CodecType aCodecType,
564
  const nsTArray<MediaKeySystemMediaCapability>& aRequestedCapabilities,
565
  const MediaKeySystemConfiguration& aPartialConfig,
566
  const KeySystemConfig& aKeySystem,
567
  DecoderDoctorDiagnostics* aDiagnostics,
568
  const std::function<void(const char*)>& aDeprecationLogFn)
569
0
{
570
0
  // Let local accumulated configuration be a local copy of partial configuration.
571
0
  // (Note: It's not necessary for us to maintain a local copy, as we don't need
572
0
  // to test whether capabilites from previous calls to this algorithm work with
573
0
  // the capabilities currently being considered in this call. )
574
0
575
0
  // Let supported media capabilities be an empty sequence of
576
0
  // MediaKeySystemMediaCapability dictionaries.
577
0
  Sequence<MediaKeySystemMediaCapability> supportedCapabilities;
578
0
579
0
  // For each requested media capability in requested media capabilities:
580
0
  for (const MediaKeySystemMediaCapability& capabilities : aRequestedCapabilities) {
581
0
    // Let content type be requested media capability's contentType member.
582
0
    const nsString& contentTypeString = capabilities.mContentType;
583
0
    // Let robustness be requested media capability's robustness member.
584
0
    const nsString& robustness = capabilities.mRobustness;
585
0
    // If content type is the empty string, return null.
586
0
    if (contentTypeString.IsEmpty()) {
587
0
      EME_LOG("MediaKeySystemConfiguration (label='%s') "
588
0
              "MediaKeySystemMediaCapability('%s','%s') rejected; "
589
0
              "audio or video capability has empty contentType.",
590
0
              NS_ConvertUTF16toUTF8(aPartialConfig.mLabel).get(),
591
0
              NS_ConvertUTF16toUTF8(contentTypeString).get(),
592
0
              NS_ConvertUTF16toUTF8(robustness).get());
593
0
      return Sequence<MediaKeySystemMediaCapability>();
594
0
    }
595
0
    // If content type is an invalid or unrecognized MIME type, continue
596
0
    // to the next iteration.
597
0
    Maybe<MediaContainerType> maybeContainerType =
598
0
      MakeMediaContainerType(contentTypeString);
599
0
    if (!maybeContainerType) {
600
0
      EME_LOG("MediaKeySystemConfiguration (label='%s') "
601
0
              "MediaKeySystemMediaCapability('%s','%s') unsupported; "
602
0
              "failed to parse contentTypeString as MIME type.",
603
0
              NS_ConvertUTF16toUTF8(aPartialConfig.mLabel).get(),
604
0
              NS_ConvertUTF16toUTF8(contentTypeString).get(),
605
0
              NS_ConvertUTF16toUTF8(robustness).get());
606
0
      continue;
607
0
    }
608
0
    const MediaContainerType& containerType = *maybeContainerType;
609
0
    bool invalid = false;
610
0
    nsTArray<EMECodecString> codecs;
611
0
    for (const auto& codecString : containerType.ExtendedType().Codecs().Range()) {
612
0
      EMECodecString emeCodec = ToEMEAPICodecString(nsString(codecString));
613
0
      if (emeCodec.IsEmpty()) {
614
0
        invalid = true;
615
0
        EME_LOG("MediaKeySystemConfiguration (label='%s') "
616
0
                "MediaKeySystemMediaCapability('%s','%s') unsupported; "
617
0
                "'%s' is an invalid codec string.",
618
0
                NS_ConvertUTF16toUTF8(aPartialConfig.mLabel).get(),
619
0
                NS_ConvertUTF16toUTF8(contentTypeString).get(),
620
0
                NS_ConvertUTF16toUTF8(robustness).get(),
621
0
                NS_ConvertUTF16toUTF8(codecString).get());
622
0
        break;
623
0
      }
624
0
      codecs.AppendElement(emeCodec);
625
0
    }
626
0
    if (invalid) {
627
0
      continue;
628
0
    }
629
0
630
0
    // If the user agent does not support container, continue to the next iteration.
631
0
    // The case-sensitivity of string comparisons is determined by the appropriate RFC.
632
0
    // (Note: Per RFC 6838 [RFC6838], "Both top-level type and subtype names are
633
0
    // case-insensitive."'. We're using nsContentTypeParser and that is
634
0
    // case-insensitive and converts all its parameter outputs to lower case.)
635
0
    const bool isMP4 =
636
0
      DecoderTraits::IsMP4SupportedType(containerType, aDiagnostics);
637
0
    if (isMP4 && !aKeySystem.mMP4.IsSupported()) {
638
0
      EME_LOG("MediaKeySystemConfiguration (label='%s') "
639
0
              "MediaKeySystemMediaCapability('%s','%s') unsupported; "
640
0
              "MP4 requested but unsupported.",
641
0
              NS_ConvertUTF16toUTF8(aPartialConfig.mLabel).get(),
642
0
              NS_ConvertUTF16toUTF8(contentTypeString).get(),
643
0
              NS_ConvertUTF16toUTF8(robustness).get());
644
0
      continue;
645
0
    }
646
0
    const bool isWebM = WebMDecoder::IsSupportedType(containerType);
647
0
    if (isWebM && !aKeySystem.mWebM.IsSupported()) {
648
0
      EME_LOG("MediaKeySystemConfiguration (label='%s') "
649
0
              "MediaKeySystemMediaCapability('%s','%s') unsupported; "
650
0
              "WebM requested but unsupported.",
651
0
              NS_ConvertUTF16toUTF8(aPartialConfig.mLabel).get(),
652
0
              NS_ConvertUTF16toUTF8(contentTypeString).get(),
653
0
              NS_ConvertUTF16toUTF8(robustness).get());
654
0
      continue;
655
0
    }
656
0
    if (!isMP4 && !isWebM) {
657
0
      EME_LOG("MediaKeySystemConfiguration (label='%s') "
658
0
              "MediaKeySystemMediaCapability('%s','%s') unsupported; "
659
0
              "Unsupported or unrecognized container requested.",
660
0
              NS_ConvertUTF16toUTF8(aPartialConfig.mLabel).get(),
661
0
              NS_ConvertUTF16toUTF8(contentTypeString).get(),
662
0
              NS_ConvertUTF16toUTF8(robustness).get());
663
0
      continue;
664
0
    }
665
0
666
0
    // Let parameters be the RFC 6381[RFC6381] parameters, if any, specified by
667
0
    // content type.
668
0
    // If the user agent does not recognize one or more parameters, continue to
669
0
    // the next iteration.
670
0
    if (IsParameterUnrecognized(contentTypeString)) {
671
0
      continue;
672
0
    }
673
0
674
0
    // Let media types be the set of codecs and codec constraints specified by
675
0
    // parameters. The case-sensitivity of string comparisons is determined by
676
0
    // the appropriate RFC or other specification.
677
0
    // (Note: codecs array is 'parameter').
678
0
679
0
    // If media types is empty:
680
0
    if (codecs.IsEmpty()) {
681
0
      // Log deprecation warning to encourage authors to not do this!
682
0
      aDeprecationLogFn("MediaEMENoCodecsDeprecatedWarning");
683
0
      // TODO: Remove this once we're sure it doesn't break the web.
684
0
      // If container normatively implies a specific set of codecs and codec constraints:
685
0
      // Let parameters be that set.
686
0
      if (isMP4) {
687
0
        if (aCodecType == Audio) {
688
0
          codecs.AppendElement(EME_CODEC_AAC);
689
0
        } else if (aCodecType == Video) {
690
0
          codecs.AppendElement(EME_CODEC_H264);
691
0
        }
692
0
      } else if (isWebM) {
693
0
        if (aCodecType == Audio) {
694
0
          codecs.AppendElement(EME_CODEC_VORBIS);
695
0
        } else if (aCodecType == Video) {
696
0
          codecs.AppendElement(EME_CODEC_VP8);
697
0
        }
698
0
      }
699
0
      // Otherwise: Continue to the next iteration.
700
0
      // (Note: all containers we support have implied codecs, so don't continue here.)
701
0
    }
702
0
703
0
    // If container type is not strictly a audio/video type, continue to the next iteration.
704
0
    const auto majorType = GetMajorType(containerType.Type());
705
0
    if (majorType == Invalid) {
706
0
      EME_LOG("MediaKeySystemConfiguration (label='%s') "
707
0
              "MediaKeySystemMediaCapability('%s','%s') unsupported; "
708
0
              "MIME type is not an audio or video MIME type.",
709
0
              NS_ConvertUTF16toUTF8(aPartialConfig.mLabel).get(),
710
0
              NS_ConvertUTF16toUTF8(contentTypeString).get(),
711
0
              NS_ConvertUTF16toUTF8(robustness).get());
712
0
      continue;
713
0
    }
714
0
    if (majorType != aCodecType || !AllCodecsOfType(codecs, aCodecType)) {
715
0
      EME_LOG("MediaKeySystemConfiguration (label='%s') "
716
0
              "MediaKeySystemMediaCapability('%s','%s') unsupported; "
717
0
              "MIME type mixes audio codecs in video capabilities "
718
0
              "or video codecs in audio capabilities.",
719
0
              NS_ConvertUTF16toUTF8(aPartialConfig.mLabel).get(),
720
0
              NS_ConvertUTF16toUTF8(contentTypeString).get(),
721
0
              NS_ConvertUTF16toUTF8(robustness).get());
722
0
      continue;
723
0
    }
724
0
    // If robustness is not the empty string and contains an unrecognized
725
0
    // value or a value not supported by implementation, continue to the
726
0
    // next iteration. String comparison is case-sensitive.
727
0
    if (!robustness.IsEmpty()) {
728
0
      if (majorType == Audio && !aKeySystem.mAudioRobustness.Contains(robustness)) {
729
0
        EME_LOG("MediaKeySystemConfiguration (label='%s') "
730
0
                "MediaKeySystemMediaCapability('%s','%s') unsupported; "
731
0
                "unsupported robustness string.",
732
0
                NS_ConvertUTF16toUTF8(aPartialConfig.mLabel).get(),
733
0
                NS_ConvertUTF16toUTF8(contentTypeString).get(),
734
0
                NS_ConvertUTF16toUTF8(robustness).get());
735
0
        continue;
736
0
      }
737
0
      if (majorType == Video && !aKeySystem.mVideoRobustness.Contains(robustness)) {
738
0
        EME_LOG("MediaKeySystemConfiguration (label='%s') "
739
0
                "MediaKeySystemMediaCapability('%s','%s') unsupported; "
740
0
                "unsupported robustness string.",
741
0
                NS_ConvertUTF16toUTF8(aPartialConfig.mLabel).get(),
742
0
                NS_ConvertUTF16toUTF8(contentTypeString).get(),
743
0
                NS_ConvertUTF16toUTF8(robustness).get());
744
0
        continue;
745
0
      }
746
0
      // Note: specified robustness requirements are satisfied.
747
0
    }
748
0
749
0
    // If the user agent and implementation definitely support playback of
750
0
    // encrypted media data for the combination of container, media types,
751
0
    // robustness and local accumulated configuration in combination with
752
0
    // restrictions...
753
0
    const auto& containerSupport = isMP4 ? aKeySystem.mMP4 : aKeySystem.mWebM;
754
0
    if (!CanDecryptAndDecode(aKeySystem.mKeySystem,
755
0
                             contentTypeString,
756
0
                             majorType,
757
0
                             containerSupport,
758
0
                             codecs,
759
0
                             aDiagnostics)) {
760
0
        EME_LOG("MediaKeySystemConfiguration (label='%s') "
761
0
                "MediaKeySystemMediaCapability('%s','%s') unsupported; "
762
0
                "codec unsupported by CDM requested.",
763
0
                NS_ConvertUTF16toUTF8(aPartialConfig.mLabel).get(),
764
0
                NS_ConvertUTF16toUTF8(contentTypeString).get(),
765
0
                NS_ConvertUTF16toUTF8(robustness).get());
766
0
        continue;
767
0
    }
768
0
769
0
    // ... add requested media capability to supported media capabilities.
770
0
    if (!supportedCapabilities.AppendElement(capabilities, mozilla::fallible)) {
771
0
      NS_WARNING("GetSupportedCapabilities: Malloc failure");
772
0
      return Sequence<MediaKeySystemMediaCapability>();
773
0
    }
774
0
775
0
    // Note: omitting steps 3.13.2, our robustness is not sophisticated enough
776
0
    // to require considering all requirements together.
777
0
  }
778
0
  return supportedCapabilities;
779
0
}
780
781
// "Get Supported Configuration and Consent" algorithm, steps 4-7 for
782
// distinctive identifier, and steps 8-11 for persistent state. The steps
783
// are the same for both requirements/features, so we factor them out into
784
// a single function.
785
static bool
786
CheckRequirement(const MediaKeysRequirement aRequirement,
787
                 const KeySystemFeatureSupport aFeatureSupport,
788
                 MediaKeysRequirement& aOutRequirement)
789
0
{
790
0
  // Let requirement be the value of candidate configuration's member.
791
0
  MediaKeysRequirement requirement = aRequirement;
792
0
  // If requirement is "optional" and feature is not allowed according to
793
0
  // restrictions, set requirement to "not-allowed".
794
0
  if (aRequirement == MediaKeysRequirement::Optional &&
795
0
      aFeatureSupport == KeySystemFeatureSupport::Prohibited) {
796
0
    requirement = MediaKeysRequirement::Not_allowed;
797
0
  }
798
0
799
0
  // Follow the steps for requirement from the following list:
800
0
  switch (requirement) {
801
0
    case MediaKeysRequirement::Required: {
802
0
      // If the implementation does not support use of requirement in combination
803
0
      // with accumulated configuration and restrictions, return NotSupported.
804
0
      if (aFeatureSupport == KeySystemFeatureSupport::Prohibited) {
805
0
        return false;
806
0
      }
807
0
      break;
808
0
    }
809
0
    case MediaKeysRequirement::Optional: {
810
0
      // Continue with the following steps.
811
0
      break;
812
0
    }
813
0
    case MediaKeysRequirement::Not_allowed: {
814
0
      // If the implementation requires use of feature in combination with
815
0
      // accumulated configuration and restrictions, return NotSupported.
816
0
      if (aFeatureSupport == KeySystemFeatureSupport::Required) {
817
0
        return false;
818
0
      }
819
0
      break;
820
0
    }
821
0
    default: {
822
0
      return false;
823
0
    }
824
0
  }
825
0
826
0
  // Set the requirement member of accumulated configuration to equal
827
0
  // calculated requirement.
828
0
  aOutRequirement = requirement;
829
0
830
0
  return true;
831
0
}
832
833
// 3.1.2.2, step 12
834
// Follow the steps for the first matching condition from the following list:
835
// If the sessionTypes member is present in candidate configuration.
836
// Let session types be candidate configuration's sessionTypes member.
837
// Otherwise let session types be ["temporary"].
838
// Note: This returns an empty array on malloc failure.
839
static Sequence<nsString>
840
UnboxSessionTypes(const Optional<Sequence<nsString>>& aSessionTypes)
841
0
{
842
0
  Sequence<nsString> sessionTypes;
843
0
  if (aSessionTypes.WasPassed()) {
844
0
    sessionTypes = aSessionTypes.Value();
845
0
  } else {
846
0
    // Note: fallible. Results in an empty array.
847
0
    sessionTypes.AppendElement(ToString(MediaKeySessionType::Temporary),
848
0
                               mozilla::fallible);
849
0
  }
850
0
  return sessionTypes;
851
0
}
852
853
// 3.1.2.2 Get Supported Configuration and Consent
854
static bool
855
GetSupportedConfig(const KeySystemConfig& aKeySystem,
856
                   const MediaKeySystemConfiguration& aCandidate,
857
                   MediaKeySystemConfiguration& aOutConfig,
858
                   DecoderDoctorDiagnostics* aDiagnostics,
859
                   bool aInPrivateBrowsing,
860
                   const std::function<void(const char*)>& aDeprecationLogFn)
861
0
{
862
0
  // Let accumulated configuration be a new MediaKeySystemConfiguration dictionary.
863
0
  MediaKeySystemConfiguration config;
864
0
  // Set the label member of accumulated configuration to equal the label member of
865
0
  // candidate configuration.
866
0
  config.mLabel = aCandidate.mLabel;
867
0
  // If the initDataTypes member of candidate configuration is non-empty, run the
868
0
  // following steps:
869
0
  if (!aCandidate.mInitDataTypes.IsEmpty()) {
870
0
    // Let supported types be an empty sequence of DOMStrings.
871
0
    nsTArray<nsString> supportedTypes;
872
0
    // For each value in candidate configuration's initDataTypes member:
873
0
    for (const nsString& initDataType : aCandidate.mInitDataTypes) {
874
0
      // Let initDataType be the value.
875
0
      // If the implementation supports generating requests based on initDataType,
876
0
      // add initDataType to supported types. String comparison is case-sensitive.
877
0
      // The empty string is never supported.
878
0
      if (aKeySystem.mInitDataTypes.Contains(initDataType)) {
879
0
        supportedTypes.AppendElement(initDataType);
880
0
      }
881
0
    }
882
0
    // If supported types is empty, return NotSupported.
883
0
    if (supportedTypes.IsEmpty()) {
884
0
      EME_LOG("MediaKeySystemConfiguration (label='%s') rejected; "
885
0
              "no supported initDataTypes provided.",
886
0
              NS_ConvertUTF16toUTF8(aCandidate.mLabel).get());
887
0
      return false;
888
0
    }
889
0
    // Set the initDataTypes member of accumulated configuration to supported types.
890
0
    if (!config.mInitDataTypes.Assign(supportedTypes)) {
891
0
      return false;
892
0
    }
893
0
  }
894
0
895
0
  if (!CheckRequirement(aCandidate.mDistinctiveIdentifier,
896
0
                        aKeySystem.mDistinctiveIdentifier,
897
0
                        config.mDistinctiveIdentifier)) {
898
0
    EME_LOG("MediaKeySystemConfiguration (label='%s') rejected; "
899
0
            "distinctiveIdentifier requirement not satisfied.",
900
0
            NS_ConvertUTF16toUTF8(aCandidate.mLabel).get());
901
0
    return false;
902
0
  }
903
0
904
0
  if (!CheckRequirement(aCandidate.mPersistentState,
905
0
                        aKeySystem.mPersistentState,
906
0
                        config.mPersistentState)) {
907
0
    EME_LOG("MediaKeySystemConfiguration (label='%s') rejected; "
908
0
            "persistentState requirement not satisfied.",
909
0
            NS_ConvertUTF16toUTF8(aCandidate.mLabel).get());
910
0
    return false;
911
0
  }
912
0
913
0
  if (config.mPersistentState == MediaKeysRequirement::Required &&
914
0
      aInPrivateBrowsing) {
915
0
    EME_LOG("MediaKeySystemConfiguration (label='%s') rejected; "
916
0
            "persistentState requested in Private Browsing window.",
917
0
            NS_ConvertUTF16toUTF8(aCandidate.mLabel).get());
918
0
    return false;
919
0
  }
920
0
921
0
  Sequence<nsString> sessionTypes(UnboxSessionTypes(aCandidate.mSessionTypes));
922
0
  if (sessionTypes.IsEmpty()) {
923
0
    // Malloc failure.
924
0
    return false;
925
0
  }
926
0
927
0
  // For each value in session types:
928
0
  for (const auto& sessionTypeString : sessionTypes) {
929
0
    // Let session type be the value.
930
0
    MediaKeySessionType sessionType;
931
0
    if (!ToSessionType(sessionTypeString, sessionType)) {
932
0
      // (Assume invalid sessionType is unsupported as per steps below).
933
0
      EME_LOG("MediaKeySystemConfiguration (label='%s') rejected; "
934
0
              "invalid session type specified.",
935
0
              NS_ConvertUTF16toUTF8(aCandidate.mLabel).get());
936
0
      return false;
937
0
    }
938
0
    // If accumulated configuration's persistentState value is "not-allowed"
939
0
    // and the Is persistent session type? algorithm returns true for session
940
0
    // type return NotSupported.
941
0
    if (config.mPersistentState == MediaKeysRequirement::Not_allowed &&
942
0
        IsPersistentSessionType(sessionType)) {
943
0
      EME_LOG("MediaKeySystemConfiguration (label='%s') rejected; "
944
0
              "persistent session requested but keysystem doesn't"
945
0
              "support persistent state.",
946
0
              NS_ConvertUTF16toUTF8(aCandidate.mLabel).get());
947
0
      return false;
948
0
    }
949
0
    // If the implementation does not support session type in combination
950
0
    // with accumulated configuration and restrictions for other reasons,
951
0
    // return NotSupported.
952
0
    if (!aKeySystem.mSessionTypes.Contains(sessionType)) {
953
0
      EME_LOG("MediaKeySystemConfiguration (label='%s') rejected; "
954
0
              "session type '%s' unsupported by keySystem.",
955
0
              NS_ConvertUTF16toUTF8(aCandidate.mLabel).get(),
956
0
              NS_ConvertUTF16toUTF8(sessionTypeString).get());
957
0
      return false;
958
0
    }
959
0
    // If accumulated configuration's persistentState value is "optional"
960
0
    // and the result of running the Is persistent session type? algorithm
961
0
    // on session type is true, change accumulated configuration's
962
0
    // persistentState value to "required".
963
0
    if (config.mPersistentState == MediaKeysRequirement::Optional &&
964
0
        IsPersistentSessionType(sessionType)) {
965
0
      config.mPersistentState = MediaKeysRequirement::Required;
966
0
    }
967
0
  }
968
0
  // Set the sessionTypes member of accumulated configuration to session types.
969
0
  config.mSessionTypes.Construct(std::move(sessionTypes));
970
0
971
0
  // If the videoCapabilities and audioCapabilities members in candidate
972
0
  // configuration are both empty, return NotSupported.
973
0
  if (aCandidate.mAudioCapabilities.IsEmpty() &&
974
0
      aCandidate.mVideoCapabilities.IsEmpty()) {
975
0
    // TODO: Most sites using EME still don't pass capabilities, so we
976
0
    // can't reject on it yet without breaking them. So add this later.
977
0
    // Log deprecation warning to encourage authors to not do this!
978
0
    aDeprecationLogFn("MediaEMENoCapabilitiesDeprecatedWarning");
979
0
  }
980
0
981
0
  // If the videoCapabilities member in candidate configuration is non-empty:
982
0
  if (!aCandidate.mVideoCapabilities.IsEmpty()) {
983
0
    // Let video capabilities be the result of executing the Get Supported
984
0
    // Capabilities for Audio/Video Type algorithm on Video, candidate
985
0
    // configuration's videoCapabilities member, accumulated configuration,
986
0
    // and restrictions.
987
0
    Sequence<MediaKeySystemMediaCapability> caps =
988
0
      GetSupportedCapabilities(Video,
989
0
                               aCandidate.mVideoCapabilities,
990
0
                               config,
991
0
                               aKeySystem,
992
0
                               aDiagnostics,
993
0
                               aDeprecationLogFn);
994
0
    // If video capabilities is null, return NotSupported.
995
0
    if (caps.IsEmpty()) {
996
0
      EME_LOG("MediaKeySystemConfiguration (label='%s') rejected; "
997
0
              "no supported video capabilities.",
998
0
              NS_ConvertUTF16toUTF8(aCandidate.mLabel).get());
999
0
      return false;
1000
0
    }
1001
0
    // Set the videoCapabilities member of accumulated configuration to video capabilities.
1002
0
    config.mVideoCapabilities = std::move(caps);
1003
0
  } else {
1004
0
    // Otherwise:
1005
0
    // Set the videoCapabilities member of accumulated configuration to an empty sequence.
1006
0
  }
1007
0
1008
0
  // If the audioCapabilities member in candidate configuration is non-empty:
1009
0
  if (!aCandidate.mAudioCapabilities.IsEmpty()) {
1010
0
    // Let audio capabilities be the result of executing the Get Supported Capabilities
1011
0
    // for Audio/Video Type algorithm on Audio, candidate configuration's audioCapabilities
1012
0
    // member, accumulated configuration, and restrictions.
1013
0
    Sequence<MediaKeySystemMediaCapability> caps =
1014
0
      GetSupportedCapabilities(Audio,
1015
0
                               aCandidate.mAudioCapabilities,
1016
0
                               config,
1017
0
                               aKeySystem,
1018
0
                               aDiagnostics,
1019
0
                               aDeprecationLogFn);
1020
0
    // If audio capabilities is null, return NotSupported.
1021
0
    if (caps.IsEmpty()) {
1022
0
      EME_LOG("MediaKeySystemConfiguration (label='%s') rejected; "
1023
0
              "no supported audio capabilities.",
1024
0
              NS_ConvertUTF16toUTF8(aCandidate.mLabel).get());
1025
0
      return false;
1026
0
    }
1027
0
    // Set the audioCapabilities member of accumulated configuration to audio capabilities.
1028
0
    config.mAudioCapabilities = std::move(caps);
1029
0
  } else {
1030
0
    // Otherwise:
1031
0
    // Set the audioCapabilities member of accumulated configuration to an empty sequence.
1032
0
  }
1033
0
1034
0
  // If accumulated configuration's distinctiveIdentifier value is "optional", follow the
1035
0
  // steps for the first matching condition from the following list:
1036
0
  if (config.mDistinctiveIdentifier == MediaKeysRequirement::Optional) {
1037
0
    // If the implementation requires use Distinctive Identifier(s) or
1038
0
    // Distinctive Permanent Identifier(s) for any of the combinations
1039
0
    // in accumulated configuration
1040
0
    if (aKeySystem.mDistinctiveIdentifier == KeySystemFeatureSupport::Required) {
1041
0
      // Change accumulated configuration's distinctiveIdentifier value to "required".
1042
0
      config.mDistinctiveIdentifier = MediaKeysRequirement::Required;
1043
0
    } else {
1044
0
      // Otherwise, change accumulated configuration's distinctiveIdentifier
1045
0
      // value to "not-allowed".
1046
0
      config.mDistinctiveIdentifier = MediaKeysRequirement::Not_allowed;
1047
0
    }
1048
0
  }
1049
0
1050
0
  // If accumulated configuration's persistentState value is "optional", follow the
1051
0
  // steps for the first matching condition from the following list:
1052
0
  if (config.mPersistentState == MediaKeysRequirement::Optional) {
1053
0
    // If the implementation requires persisting state for any of the combinations
1054
0
    // in accumulated configuration
1055
0
    if (aKeySystem.mPersistentState == KeySystemFeatureSupport::Required) {
1056
0
      // Change accumulated configuration's persistentState value to "required".
1057
0
      config.mPersistentState = MediaKeysRequirement::Required;
1058
0
    } else {
1059
0
      // Otherwise, change accumulated configuration's persistentState
1060
0
      // value to "not-allowed".
1061
0
      config.mPersistentState = MediaKeysRequirement::Not_allowed;
1062
0
    }
1063
0
  }
1064
0
1065
0
  // Note: Omitting steps 20-22. We don't ask for consent.
1066
0
1067
#if defined(XP_WIN)
1068
  // Widevine CDM doesn't include an AAC decoder. So if WMF can't decode AAC,
1069
  // and a codec wasn't specified, be conservative and reject the MediaKeys request.
1070
  if (IsWidevineKeySystem(aKeySystem.mKeySystem) &&
1071
      (aCandidate.mAudioCapabilities.IsEmpty() ||
1072
       aCandidate.mVideoCapabilities.IsEmpty()) &&
1073
     !WMFDecoderModule::HasAAC()) {
1074
    if (aDiagnostics) {
1075
      aDiagnostics->SetKeySystemIssue(
1076
        DecoderDoctorDiagnostics::eWidevineWithNoWMF);
1077
    }
1078
    EME_LOG("MediaKeySystemConfiguration (label='%s') rejected; "
1079
            "WMF required for Widevine decoding, but it's not available.",
1080
            NS_ConvertUTF16toUTF8(aCandidate.mLabel).get());
1081
    return false;
1082
  }
1083
#endif
1084
1085
0
  // Return accumulated configuration.
1086
0
  aOutConfig = config;
1087
0
1088
0
  return true;
1089
0
}
1090
1091
/* static */
1092
bool
1093
MediaKeySystemAccess::GetSupportedConfig(
1094
  const nsAString& aKeySystem,
1095
  const Sequence<MediaKeySystemConfiguration>& aConfigs,
1096
  MediaKeySystemConfiguration& aOutConfig,
1097
  DecoderDoctorDiagnostics* aDiagnostics,
1098
  bool aIsPrivateBrowsing,
1099
  const std::function<void(const char*)>& aDeprecationLogFn)
1100
0
{
1101
0
  KeySystemConfig implementation;
1102
0
  if (!GetKeySystemConfig(aKeySystem, implementation)) {
1103
0
    return false;
1104
0
  }
1105
0
  for (const MediaKeySystemConfiguration& candidate : aConfigs) {
1106
0
    if (mozilla::dom::GetSupportedConfig(implementation,
1107
0
                                         candidate,
1108
0
                                         aOutConfig,
1109
0
                                         aDiagnostics,
1110
0
                                         aIsPrivateBrowsing,
1111
0
                                         aDeprecationLogFn)) {
1112
0
      return true;
1113
0
    }
1114
0
  }
1115
0
1116
0
  return false;
1117
0
}
1118
1119
1120
/* static */
1121
void
1122
MediaKeySystemAccess::NotifyObservers(nsPIDOMWindowInner* aWindow,
1123
                                      const nsAString& aKeySystem,
1124
                                      MediaKeySystemStatus aStatus)
1125
0
{
1126
0
  RequestMediaKeySystemAccessNotification data;
1127
0
  data.mKeySystem = aKeySystem;
1128
0
  data.mStatus = aStatus;
1129
0
  nsAutoString json;
1130
0
  data.ToJSON(json);
1131
0
  EME_LOG("MediaKeySystemAccess::NotifyObservers() %s", NS_ConvertUTF16toUTF8(json).get());
1132
0
  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1133
0
  if (obs) {
1134
0
    obs->NotifyObservers(aWindow, "mediakeys-request", json.get());
1135
0
  }
1136
0
}
1137
1138
static nsCString
1139
ToCString(const nsString& aString)
1140
0
{
1141
0
  nsCString str("'");
1142
0
  str.Append(NS_ConvertUTF16toUTF8(aString));
1143
0
  str.AppendLiteral("'");
1144
0
  return str;
1145
0
}
1146
1147
static nsCString
1148
ToCString(const MediaKeysRequirement aValue)
1149
0
{
1150
0
  nsCString str("'");
1151
0
  str.Append(nsDependentCString(
1152
0
    MediaKeysRequirementValues::strings[static_cast<uint32_t>(aValue)].value));
1153
0
  str.AppendLiteral("'");
1154
0
  return str;
1155
0
}
1156
1157
static nsCString
1158
ToCString(const MediaKeySystemMediaCapability& aValue)
1159
0
{
1160
0
  nsCString str;
1161
0
  str.AppendLiteral("{contentType=");
1162
0
  str.Append(ToCString(aValue.mContentType));
1163
0
  str.AppendLiteral(", robustness=");
1164
0
  str.Append(ToCString(aValue.mRobustness));
1165
0
  str.AppendLiteral("}");
1166
0
  return str;
1167
0
}
1168
1169
template<class Type>
1170
static nsCString
1171
ToCString(const Sequence<Type>& aSequence)
1172
0
{
1173
0
  nsCString str;
1174
0
  str.AppendLiteral("[");
1175
0
  for (size_t i = 0; i < aSequence.Length(); i++) {
1176
0
    if (i != 0) {
1177
0
      str.AppendLiteral(",");
1178
0
    }
1179
0
    str.Append(ToCString(aSequence[i]));
1180
0
  }
1181
0
  str.AppendLiteral("]");
1182
0
  return str;
1183
0
}
Unexecuted instantiation: Unified_cpp_dom_media_eme0.cpp:nsTString<char> mozilla::dom::ToCString<nsTString<char16_t> >(mozilla::dom::Sequence<nsTString<char16_t> > const&)
Unexecuted instantiation: Unified_cpp_dom_media_eme0.cpp:nsTString<char> mozilla::dom::ToCString<mozilla::dom::MediaKeySystemMediaCapability>(mozilla::dom::Sequence<mozilla::dom::MediaKeySystemMediaCapability> const&)
Unexecuted instantiation: Unified_cpp_dom_media_eme0.cpp:nsTString<char> mozilla::dom::ToCString<mozilla::dom::MediaKeySystemConfiguration>(mozilla::dom::Sequence<mozilla::dom::MediaKeySystemConfiguration> const&)
1184
1185
template<class Type>
1186
static nsCString
1187
ToCString(const Optional<Sequence<Type>>& aOptional)
1188
0
{
1189
0
  nsCString str;
1190
0
  if (aOptional.WasPassed()) {
1191
0
    str.Append(ToCString(aOptional.Value()));
1192
0
  } else {
1193
0
    str.AppendLiteral("[]");
1194
0
  }
1195
0
  return str;
1196
0
}
1197
1198
static nsCString
1199
ToCString(const MediaKeySystemConfiguration& aConfig)
1200
0
{
1201
0
  nsCString str;
1202
0
  str.AppendLiteral("{label=");
1203
0
  str.Append(ToCString(aConfig.mLabel));
1204
0
1205
0
  str.AppendLiteral(", initDataTypes=");
1206
0
  str.Append(ToCString(aConfig.mInitDataTypes));
1207
0
1208
0
  str.AppendLiteral(", audioCapabilities=");
1209
0
  str.Append(ToCString(aConfig.mAudioCapabilities));
1210
0
1211
0
  str.AppendLiteral(", videoCapabilities=");
1212
0
  str.Append(ToCString(aConfig.mVideoCapabilities));
1213
0
1214
0
  str.AppendLiteral(", distinctiveIdentifier=");
1215
0
  str.Append(ToCString(aConfig.mDistinctiveIdentifier));
1216
0
1217
0
  str.AppendLiteral(", persistentState=");
1218
0
  str.Append(ToCString(aConfig.mPersistentState));
1219
0
1220
0
  str.AppendLiteral(", sessionTypes=");
1221
0
  str.Append(ToCString(aConfig.mSessionTypes));
1222
0
1223
0
  str.AppendLiteral("}");
1224
0
1225
0
  return str;
1226
0
}
1227
1228
/* static */
1229
nsCString
1230
MediaKeySystemAccess::ToCString(
1231
  const Sequence<MediaKeySystemConfiguration>& aConfig)
1232
0
{
1233
0
  return mozilla::dom::ToCString(aConfig);
1234
0
}
1235
1236
} // namespace dom
1237
} // namespace mozilla