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