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