33104596703aa2b55db90e00844a7b42bf72031e
[WebKit-https.git] / Source / WebCore / Modules / encryptedmedia / CDM.cpp
1 /*
2  * Copyright (C) 2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "CDM.h"
28
29 #if ENABLE(ENCRYPTED_MEDIA)
30
31 #include "CDMPrivate.h"
32 #include "Document.h"
33 #include "InitDataRegistry.h"
34 #include "MediaKeysRestrictions.h"
35 #include "MediaPlayer.h"
36 #include "NotImplemented.h"
37 #include "ParsedContentType.h"
38 #include "ScriptExecutionContext.h"
39 #include "SecurityOrigin.h"
40 #include <wtf/NeverDestroyed.h>
41
42 namespace WebCore {
43
44 static Vector<CDMFactory*>& cdmFactories()
45 {
46     static NeverDestroyed<Vector<CDMFactory*>> factories;
47     return factories;
48 }
49
50 static std::unique_ptr<CDMPrivate> createCDMPrivateForKeySystem(const String& keySystem, CDM& cdm)
51 {
52     for (auto* factory : cdmFactories()) {
53         if (factory->supportsKeySystem(keySystem))
54             return factory->createCDM(cdm);
55     }
56     ASSERT_NOT_REACHED();
57     return nullptr;
58 }
59
60 void CDM::registerCDMFactory(CDMFactory& factory)
61 {
62     ASSERT(!cdmFactories().contains(&factory));
63     cdmFactories().append(&factory);
64 }
65
66 void CDM::unregisterCDMFactory(CDMFactory& factory)
67 {
68     ASSERT(cdmFactories().contains(&factory));
69     cdmFactories().removeAll(&factory);
70 }
71
72 bool CDM::supportsKeySystem(const String& keySystem)
73 {
74     for (auto* factory : cdmFactories()) {
75         if (factory->supportsKeySystem(keySystem))
76             return true;
77     }
78     return false;
79 }
80
81 Ref<CDM> CDM::create(Document& document, const String& keySystem)
82 {
83     return adoptRef(*new CDM(document, keySystem));
84 }
85
86 CDM::CDM(Document& document, const String& keySystem)
87     : ContextDestructionObserver(&document)
88     , m_keySystem(keySystem)
89     , m_private(createCDMPrivateForKeySystem(keySystem, *this))
90     , m_weakPtrFactory(this)
91 {
92     ASSERT(supportsKeySystem(keySystem));
93     for (auto* factory : cdmFactories()) {
94         if (!factory->supportsKeySystem(keySystem))
95             continue;
96         m_private = factory->createCDM(*this);
97     }
98 }
99
100 CDM::~CDM() = default;
101
102 void CDM::getSupportedConfiguration(MediaKeySystemConfiguration&& candidateConfiguration, SupportedConfigurationCallback&& callback)
103 {
104     // https://w3c.github.io/encrypted-media/#get-supported-configuration
105     // W3C Editor's Draft 09 November 2016
106
107     // 3.1.1.1 Get Supported Configuration
108     // Given a Key Systems implementation implementation, MediaKeySystemConfiguration candidate configuration, and origin,
109     // this algorithm returns a supported configuration or NotSupported as appropriate.
110
111     // 1. Let supported configuration be ConsentDenied.
112     // 2. Initialize restrictions to indicate that no configurations have had user consent denied.
113     MediaKeysRestrictions restrictions { };
114     doSupportedConfigurationStep(WTFMove(candidateConfiguration), WTFMove(restrictions), WTFMove(callback));
115 }
116
117 void CDM::doSupportedConfigurationStep(MediaKeySystemConfiguration&& candidateConfiguration, MediaKeysRestrictions&& restrictions, SupportedConfigurationCallback&& callback)
118 {
119     // https://w3c.github.io/encrypted-media/#get-supported-configuration
120     // W3C Editor's Draft 09 November 2016, ctd.
121
122     // 3.1.1.1 Get Supported Configuration
123     // 3. Repeat the following step while supported configuration is ConsentDenied:
124     // 3.1. Let supported configuration and, if provided, restrictions be the result of executing the
125     // Get Supported Configuration and Consent algorithm with implementation, candidate configuration,
126     // restrictions and origin.
127     auto optionalConfiguration = getSupportedConfiguration(candidateConfiguration, restrictions);
128     if (!optionalConfiguration) {
129         callback(std::nullopt);
130         return;
131     }
132
133     auto consentCallback = [weakThis = createWeakPtr(), callback = WTFMove(callback)] (ConsentStatus status, MediaKeySystemConfiguration&& configuration, MediaKeysRestrictions&& restrictions) mutable {
134         if (!weakThis) {
135             callback(std::nullopt);
136             return;
137         }
138         // 3.1.1.2 Get Supported Configuration and Consent, ctd.
139         // 22. Let consent status and updated restrictions be the result of running the Get Consent Status algorithm on accumulated configuration,
140         //     restrictions and origin and follow the steps for the value of consent status from the following list:
141         switch (status) {
142         case ConsentStatus::ConsentDenied:
143             // ↳ ConsentDenied:
144             //    Return ConsentDenied and updated restrictions.
145             weakThis->doSupportedConfigurationStep(WTFMove(configuration), WTFMove(restrictions), WTFMove(callback));
146             return;
147
148         case ConsentStatus::InformUser:
149             // ↳ InformUser
150             //    Inform the user that accumulated configuration is in use in the origin including, specifically, the information that
151             //    Distinctive Identifier(s) and/or Distinctive Permanent Identifier(s) as appropriate will be used if the
152             //    distinctiveIdentifier member of accumulated configuration is "required". Continue to the next step.
153             // NOTE: Implement.
154             break;
155
156         case ConsentStatus::Allowed:
157             // ↳ Allowed:
158             // Continue to the next step.
159             break;
160         }
161         // 23. Return accumulated configuration.
162         callback(WTFMove(configuration));
163     };
164     getConsentStatus(WTFMove(optionalConfiguration.value()), WTFMove(restrictions), consentCallback);
165 }
166
167 bool CDM::isPersistentType(MediaKeySessionType sessionType)
168 {
169     // https://w3c.github.io/encrypted-media/#is-persistent-session-type
170     // W3C Editor's Draft 09 November 2016
171
172     // 5.1.1. Is persistent session type?
173     // 1. Let the session type be the specified MediaKeySessionType value.
174     // 2. Follow the steps for the value of session type from the following list:
175     switch (sessionType) {
176     case MediaKeySessionType::Temporary:
177         // ↳ "temporary"
178         return false;
179     case MediaKeySessionType::PersistentLicense:
180     case MediaKeySessionType::PersistentUsageRecord:
181         // ↳ "persistent-license"
182         return true;
183     }
184 }
185
186 std::optional<MediaKeySystemConfiguration> CDM::getSupportedConfiguration(const MediaKeySystemConfiguration& candidateConfiguration, MediaKeysRestrictions& restrictions)
187 {
188     // https://w3c.github.io/encrypted-media/#get-supported-configuration-and-consent
189     // W3C Editor's Draft 09 November 2016
190
191     ASSERT(m_private);
192     if (!m_private)
193         return std::nullopt;
194
195     // 3.1.1.2 Get Supported Configuration and Consent
196     // Given a Key Systems implementation implementation, MediaKeySystemConfiguration candidate configuration,
197     // restrictions and origin, this algorithm returns a supported configuration, NotSupported, or ConsentDenied
198     // as appropriate and, in the ConsentDenied case, restrictions.
199
200     // 1. Let accumulated configuration be a new MediaKeySystemConfiguration dictionary.
201     MediaKeySystemConfiguration accumulatedConfiguration { };
202
203     // 2. Set the label member of accumulated configuration to equal the label member of candidate configuration.
204     accumulatedConfiguration.label = candidateConfiguration.label;
205
206     // 3. If the initDataTypes member of candidate configuration is non-empty, run the following steps:
207     if (!candidateConfiguration.initDataTypes.isEmpty()) {
208         // 3.1. Let supported types be an empty sequence of DOMStrings.
209         Vector<String> supportedTypes;
210
211         // 3.2. For each value in candidate configuration's initDataTypes member:
212         for (auto initDataType : candidateConfiguration.initDataTypes) {
213             // 3.2.1. Let initDataType be the value.
214             // 3.2.2. If the implementation supports generating requests based on initDataType, add initDataType
215             //        to supported types. String comparison is case-sensitive. The empty string is never supported.
216             if (initDataType.isEmpty())
217                 continue;
218
219             if (m_private && m_private->supportsInitDataType(initDataType))
220                 supportedTypes.append(initDataType);
221         }
222
223         // 3.3. If supported types is empty, return NotSupported.
224         if (supportedTypes.isEmpty())
225             return std::nullopt;
226
227         // 3.4. Set the initDataTypes member of accumulated configuration to supported types.
228         accumulatedConfiguration.initDataTypes = WTFMove(supportedTypes);
229     }
230
231     // 4. Let distinctive identifier requirement be the value of candidate configuration's distinctiveIdentifier member.
232     MediaKeysRequirement distinctiveIdentifierRequirement = candidateConfiguration.distinctiveIdentifier;
233
234     // 5. If distinctive identifier requirement is "optional" and Distinctive Identifiers are not allowed according to
235     //    restrictions, set distinctive identifier requirement to "not-allowed".
236     if (distinctiveIdentifierRequirement == MediaKeysRequirement::Optional && restrictions.distinctiveIdentifierDenied)
237         distinctiveIdentifierRequirement = MediaKeysRequirement::NotAllowed;
238
239     // 6. Follow the steps for distinctive identifier requirement from the following list:
240     switch (distinctiveIdentifierRequirement) {
241     case MediaKeysRequirement::Required:
242         // ↳ "required"
243         // If the implementation does not support use of Distinctive Identifier(s) in combination
244         // with accumulated configuration and restrictions, return NotSupported.
245         if (m_private->distinctiveIdentifiersRequirement(accumulatedConfiguration, restrictions) == MediaKeysRequirement::NotAllowed)
246             return std::nullopt;
247         break;
248
249     case MediaKeysRequirement::Optional:
250         // ↳ "optional"
251         // Continue with the following steps.
252         break;
253
254     case MediaKeysRequirement::NotAllowed:
255         // ↳ "not-allowed"
256         // If the implementation requires use Distinctive Identifier(s) or Distinctive Permanent Identifier(s)
257         // in combination with accumulated configuration and restrictions, return NotSupported.
258         if (m_private->distinctiveIdentifiersRequirement(accumulatedConfiguration, restrictions) == MediaKeysRequirement::Required)
259             return std::nullopt;
260         break;
261     }
262
263     // 7. Set the distinctiveIdentifier member of accumulated configuration to equal distinctive identifier requirement.
264     accumulatedConfiguration.distinctiveIdentifier = distinctiveIdentifierRequirement;
265
266     // 8. Let persistent state requirement be equal to the value of candidate configuration's persistentState member.
267     MediaKeysRequirement persistentStateRequirement = candidateConfiguration.persistentState;
268
269     // 9. If persistent state requirement is "optional" and persisting state is not allowed according to restrictions,
270     //    set persistent state requirement to "not-allowed".
271     if (persistentStateRequirement == MediaKeysRequirement::Optional && restrictions.persistentStateDenied)
272         persistentStateRequirement =  MediaKeysRequirement::NotAllowed;
273
274     // 10. Follow the steps for persistent state requirement from the following list:
275     switch (persistentStateRequirement) {
276     case MediaKeysRequirement::Required:
277         // ↳ "required"
278         // If the implementation does not support persisting state in combination with accumulated configuration
279         // and restrictions, return NotSupported.
280         if (m_private->persistentStateRequirement(accumulatedConfiguration, restrictions) == MediaKeysRequirement::NotAllowed)
281             return std::nullopt;
282         break;
283
284     case MediaKeysRequirement::Optional:
285         // ↳ "optional"
286         // Continue with the following steps.
287         break;
288
289     case MediaKeysRequirement::NotAllowed:
290         // ↳ "not-allowed"
291         // If the implementation requires persisting state in combination with accumulated configuration
292         // and restrictions, return NotSupported
293         if (m_private->persistentStateRequirement(accumulatedConfiguration, restrictions) == MediaKeysRequirement::Required)
294             return std::nullopt;
295         break;
296     }
297
298     // 11. Set the persistentState member of accumulated configuration to equal the value of persistent state requirement.
299     accumulatedConfiguration.persistentState = persistentStateRequirement;
300
301     // 12. Follow the steps for the first matching condition from the following list:
302     Vector<MediaKeySessionType> sessionTypes;
303
304     if (!candidateConfiguration.sessionTypes.isEmpty()) {
305         // ↳ If the sessionTypes member is present [WebIDL] in candidate configuration
306         // Let session types be candidate configuration's sessionTypes member.
307         sessionTypes = candidateConfiguration.sessionTypes;
308     } else {
309         // ↳ Otherwise
310         // Let session types be [ "temporary" ].
311         sessionTypes = { MediaKeySessionType::Temporary };
312     }
313
314     // 13. For each value in session types:
315     for (auto& sessionType : sessionTypes) {
316         // 13.1. Let session type be the value.
317         // 13.2. If accumulated configuration's persistentState value is "not-allowed" and the
318         //       Is persistent session type? algorithm returns true for session type return NotSupported.
319         if (accumulatedConfiguration.persistentState == MediaKeysRequirement::NotAllowed && isPersistentType(sessionType))
320             return std::nullopt;
321
322         // 13.3. If the implementation does not support session type in combination with accumulated configuration
323         //       and restrictions for other reasons, return NotSupported.
324         if (!m_private->supportsSessionTypeWithConfiguration(sessionType, accumulatedConfiguration))
325             return std::nullopt;
326
327         // 13.4 If accumulated configuration's persistentState value is "optional" and the result of running the Is
328         //      persistent session type? algorithm on session type is true, change accumulated configuration's persistentState
329         //      value to "required".
330         if (accumulatedConfiguration.persistentState == MediaKeysRequirement::Optional && isPersistentType(sessionType))
331             accumulatedConfiguration.persistentState = MediaKeysRequirement::Required;
332     }
333
334     // 14. Set the sessionTypes member of accumulated configuration to session types.
335     accumulatedConfiguration.sessionTypes = sessionTypes;
336
337     // 15. If the videoCapabilities and audioCapabilities members in candidate configuration are both empty, return NotSupported.
338     if (candidateConfiguration.videoCapabilities.isEmpty() && candidateConfiguration.audioCapabilities.isEmpty())
339         return std::nullopt;
340
341     // 16. ↳ If the videoCapabilities member in candidate configuration is non-empty:
342     if (!candidateConfiguration.videoCapabilities.isEmpty()) {
343         // 16.1. Let video capabilities be the result of executing the Get Supported Capabilities for Audio/Video Type algorithm on
344         //       Video, candidate configuration's videoCapabilities member, accumulated configuration, and restrictions.
345         auto videoCapabilities = getSupportedCapabilitiesForAudioVideoType(AudioVideoType::Video, candidateConfiguration.videoCapabilities, accumulatedConfiguration, restrictions);
346
347         // 16.2. If video capabilities is null, return NotSupported.
348         if (!videoCapabilities)
349             return std::nullopt;
350
351         // 16.3 Set the videoCapabilities member of accumulated configuration to video capabilities.
352         accumulatedConfiguration.videoCapabilities = WTFMove(videoCapabilities.value());
353     } else {
354         // 16. ↳ Otherwise:
355         //     Set the videoCapabilities member of accumulated configuration to an empty sequence.
356         accumulatedConfiguration.videoCapabilities = { };
357     }
358
359     // 17. ↳ If the audioCapabilities member in candidate configuration is non-empty:
360     if (!candidateConfiguration.audioCapabilities.isEmpty()) {
361         // 17.1. Let audio capabilities be the result of executing the Get Supported Capabilities for Audio/Video Type algorithm on
362         //       Audio, candidate configuration's audioCapabilities member, accumulated configuration, and restrictions.
363         auto audioCapabilities = getSupportedCapabilitiesForAudioVideoType(AudioVideoType::Audio, candidateConfiguration.audioCapabilities, accumulatedConfiguration, restrictions);
364
365         // 17.2. If audio capabilities is null, return NotSupported.
366         if (!audioCapabilities)
367             return std::nullopt;
368
369         // 17.3 Set the audioCapabilities member of accumulated configuration to audio capabilities.
370         accumulatedConfiguration.audioCapabilities = WTFMove(audioCapabilities.value());
371     } else {
372         // 17. ↳ Otherwise:
373         //     Set the audioCapabilities member of accumulated configuration to an empty sequence.
374         accumulatedConfiguration.audioCapabilities = { };
375     }
376
377     // 18. If accumulated configuration's distinctiveIdentifier value is "optional", follow the steps for the first matching
378     //     condition from the following list:
379     if (accumulatedConfiguration.distinctiveIdentifier == MediaKeysRequirement::Optional) {
380         // ↳ If the implementation requires use Distinctive Identifier(s) or Distinctive Permanent Identifier(s) for any of the
381         //    combinations in accumulated configuration
382         if (m_private->distinctiveIdentifiersRequirement(accumulatedConfiguration, restrictions) == MediaKeysRequirement::Required) {
383             // Change accumulated configuration's distinctiveIdentifier value to "required".
384             accumulatedConfiguration.distinctiveIdentifier = MediaKeysRequirement::Required;
385         } else {
386             // ↳ Otherwise
387             //    Change accumulated configuration's distinctiveIdentifier value to "not-allowed".
388             accumulatedConfiguration.distinctiveIdentifier = MediaKeysRequirement::NotAllowed;
389         }
390     }
391
392     // 19. If accumulated configuration's persistentState value is "optional", follow the steps for the first matching
393     //     condition from the following list:
394     if (accumulatedConfiguration.persistentState == MediaKeysRequirement::Optional) {
395         // ↳ If the implementation requires persisting state for any of the combinations in accumulated configuration
396         if (m_private->persistentStateRequirement(accumulatedConfiguration, restrictions) == MediaKeysRequirement::Required) {
397             // Change accumulated configuration's persistentState value to "required".
398             accumulatedConfiguration.persistentState = MediaKeysRequirement::Required;
399         } else {
400             // ↳ Otherwise
401             //    Change accumulated configuration's persistentState value to "not-allowed".
402             accumulatedConfiguration.persistentState = MediaKeysRequirement::NotAllowed;
403         }
404     }
405
406     // 20. If implementation in the configuration specified by the combination of the values in accumulated configuration
407     //     is not supported or not allowed in the origin, return NotSupported.
408     if (!m_private->supportsConfiguration(accumulatedConfiguration))
409         return std::nullopt;
410
411     Document* document = downcast<Document>(m_scriptExecutionContext);
412     if (!document)
413         return std::nullopt;
414
415     SecurityOrigin* origin = document->securityOrigin();
416     SecurityOrigin* topOrigin = document->topOrigin();
417     if (!origin || !topOrigin)
418         return std::nullopt;
419
420     if ((accumulatedConfiguration.distinctiveIdentifier == MediaKeysRequirement::Required || accumulatedConfiguration.persistentState == MediaKeysRequirement::Required) && !origin->canAccessLocalStorage(topOrigin))
421         return std::nullopt;
422
423     return WTFMove(accumulatedConfiguration);
424     // NOTE: Continued in getConsentStatus().
425 }
426
427 std::optional<Vector<MediaKeySystemMediaCapability>> CDM::getSupportedCapabilitiesForAudioVideoType(CDM::AudioVideoType type, const Vector<MediaKeySystemMediaCapability>& requestedCapabilities, const MediaKeySystemConfiguration& partialConfiguration, MediaKeysRestrictions& restrictions)
428 {
429     // https://w3c.github.io/encrypted-media/#get-supported-capabilities-for-audio-video-type
430     // W3C Editor's Draft 09 November 2016
431
432     ASSERT(m_private);
433     if (!m_private)
434         return std::nullopt;
435
436     // 3.1.1.3 Get Supported Capabilities for Audio/Video Type
437
438     // Given an audio/video type, MediaKeySystemMediaCapability sequence requested media capabilities, MediaKeySystemConfiguration
439     // partial configuration, and restrictions, this algorithm returns a sequence of supported MediaKeySystemMediaCapability values
440     // for this audio/video type or null as appropriate.
441
442     // 1. Let local accumulated configuration be a local copy of partial configuration.
443     MediaKeySystemConfiguration accumulatedConfiguration = partialConfiguration;
444
445     // 2. Let supported media capabilities be an empty sequence of MediaKeySystemMediaCapability dictionaries.
446     Vector<MediaKeySystemMediaCapability> supportedMediaCapabilities { };
447
448     // 3. For each requested media capability in requested media capabilities:
449     for (auto& requestedCapability : requestedCapabilities) {
450         // 3.1. Let content type be requested media capability's contentType member.
451         // 3.2. Let robustness be requested media capability's robustness member.
452         String robustness = requestedCapability.robustness;
453
454         // 3.3. If content type is the empty string, return null.
455         if (requestedCapability.contentType.isEmpty())
456             return std::nullopt;
457
458         // 3.4. If content type is an invalid or unrecognized MIME type, continue to the next iteration.
459         if (!isValidContentType(requestedCapability.contentType))
460             continue;
461
462         // 3.5. Let container be the container type specified by content type.
463         ParsedContentType contentType { requestedCapability.contentType };
464         String container = contentType.mimeType();
465
466         // 3.6. If the user agent does not support container, continue to the next iteration. The case-sensitivity
467         //      of string comparisons is determined by the appropriate RFC.
468         // 3.7. Let parameters be the RFC 6381 [RFC6381] parameters, if any, specified by content type.
469         // 3.8. If the user agent does not recognize one or more parameters, continue to the next iteration.
470         // 3.9. Let media types be the set of codecs and codec constraints specified by parameters. The case-sensitivity
471         //      of string comparisons is determined by the appropriate RFC or other specification.
472         String codecs = contentType.parameterValueForName("codecs");
473         if (contentType.parameterCount() > (codecs.isEmpty() ? 0 : 1))
474             continue;
475
476         // 3.10. If media types is empty:
477         if (codecs.isEmpty()) {
478             // ↳ If container normatively implies a specific set of codecs and codec constraints:
479             // ↳ Otherwise:
480             notImplemented();
481         }
482
483         // 3.11. If content type is not strictly a audio/video type, continue to the next iteration.
484         // 3.12. If robustness is not the empty string and contains an unrecognized value or a value not supported by
485         //       implementation, continue to the next iteration. String comparison is case-sensitive.
486         if (!robustness.isEmpty() && !m_private->supportsRobustness(robustness))
487             continue;
488
489         // 3.13. If the user agent and implementation definitely support playback of encrypted media data for the
490         //       combination of container, media types, robustness and local accumulated configuration in combination
491         //       with restrictions:
492         MediaEngineSupportParameters parameters;
493         parameters.type = contentType.mimeType();
494         parameters.codecs = codecs;
495         if (!MediaPlayer::supportsType(parameters, nullptr)) {
496             // Try with Media Source:
497             parameters.isMediaSource = true;
498             if (!MediaPlayer::supportsType(parameters, nullptr))
499                 continue;
500         }
501
502         if (!m_private->supportsConfigurationWithRestrictions(accumulatedConfiguration, restrictions))
503             continue;
504
505         // 3.13.1. Add requested media capability to supported media capabilities.
506         supportedMediaCapabilities.append(requestedCapability);
507
508         // 3.13.2. ↳ If audio/video type is Video:
509         //         Add requested media capability to the videoCapabilities member of local accumulated configuration.
510         if (type == AudioVideoType::Video)
511             accumulatedConfiguration.videoCapabilities.append(requestedCapability);
512         // 3.13.2. ↳ If audio/video type is Audio:
513         //         Add requested media capability to the audioCapabilities member of local accumulated configuration.
514         else
515             accumulatedConfiguration.audioCapabilities.append(requestedCapability);
516     }
517
518     // 4. If supported media capabilities is empty, return null.
519     if (supportedMediaCapabilities.isEmpty())
520         return std::nullopt;
521
522     // 5. Return supported media capabilities.
523     return supportedMediaCapabilities;
524 }
525
526 void CDM::getConsentStatus(MediaKeySystemConfiguration&& accumulatedConfiguration, MediaKeysRestrictions&& restrictions, ConsentStatusCallback&& callback)
527 {
528     // https://w3c.github.io/encrypted-media/#get-supported-configuration-and-consent
529     // W3C Editor's Draft 09 November 2016
530     if (!m_scriptExecutionContext) {
531         callback(ConsentStatus::ConsentDenied, WTFMove(accumulatedConfiguration), WTFMove(restrictions));
532         return;
533     }
534
535     // NOTE: In the future, these checks belowe will involve asking the page client, possibly across a process boundary.
536     // They will by necessity be asynchronous with callbacks. For now, imply this behavior by performing it in an async task.
537
538     m_scriptExecutionContext->postTask([this, weakThis = createWeakPtr(), accumulatedConfiguration = WTFMove(accumulatedConfiguration), restrictions = WTFMove(restrictions), callback = WTFMove(callback)] (ScriptExecutionContext&) mutable {
539         if (!weakThis || !m_private) {
540             callback(ConsentStatus::ConsentDenied, WTFMove(accumulatedConfiguration), WTFMove(restrictions));
541             return;
542         }
543
544         Document* document = downcast<Document>(m_scriptExecutionContext);
545         if (!document) {
546             callback(ConsentStatus::ConsentDenied, WTFMove(accumulatedConfiguration), WTFMove(restrictions));
547             return;
548         }
549
550         SecurityOrigin* origin = document->securityOrigin();
551         SecurityOrigin* topOrigin = document->topOrigin();
552         if (!origin || !topOrigin) {
553             callback(ConsentStatus::ConsentDenied, WTFMove(accumulatedConfiguration), WTFMove(restrictions));
554             return;
555         }
556
557         // 3.1.1.2 Get Supported Configuration and Consent, ctd.
558         // 21. If accumulated configuration's distinctiveIdentifier value is "required" and the Distinctive Identifier(s) associated
559         //     with accumulated configuration are not unique per origin and profile and clearable:
560         if (accumulatedConfiguration.distinctiveIdentifier == MediaKeysRequirement::Required && !m_private->distinctiveIdentifiersAreUniquePerOriginAndClearable(accumulatedConfiguration)) {
561             // 21.1. Update restrictions to reflect that all configurations described by accumulated configuration do not have user consent.
562             restrictions.distinctiveIdentifierDenied = true;
563             callback(ConsentStatus::ConsentDenied, WTFMove(accumulatedConfiguration), WTFMove(restrictions));
564             return;
565         }
566
567         // https://w3c.github.io/encrypted-media/#get-consent-status
568         // 3.1.1.4 Get Consent Status
569         // Given an accumulated configuration, restrictions and origin, this algorithm returns the consent status for accumulated
570         // configuration and origin as one of ConsentDenied, InformUser or Allowed, together with an updated value for restrictions
571         // in the ConsentDenied case.
572
573         // 1. If there is persisted denial for origin indicating that accumulated configuration is not allowed, run the following steps:
574         // 1.1. Update restrictions to reflect the configurations for which consent has been denied.
575         // 1.2. Return ConsentDenied and restrictions.
576         // 2. If there is persisted consent for origin indicating accumulated configuration is allowed, return Allowed.
577         // NOTE: persisted denial / consent unimplemented.
578
579         // 3. If any of the following are true:
580         //    ↳ The distinctiveIdentifier member of accumulated configuration is not "not-allowed" and the combination of the User Agent,
581         //       implementation and accumulated configuration does not follow all the recommendations of Allow Persistent Data to Be Cleared
582         //       with respect to Distinctive Identifier(s).
583         // NOTE: assume that implementations follow all recommendations.
584
585         //    ↳ The user agent requires explicit user consent for the accumulated configuration for other reasons.
586         // NOTE: assume the user agent does not require explicit user consent.
587
588         // 3.1. Request user consent to use accumulated configuration in the origin and wait for the user response.
589         //      The consent must include consent to use a Distinctive Identifier(s) and/or Distinctive Permanent Identifier(s) as appropriate
590         //      if accumulated configuration's distinctiveIdentifier member is "required".
591         // 3.2. If consent was denied, run the following steps:
592         // 3.2.1. Update restrictions to reflect the configurations for which consent was denied.
593         // 3.2.1. Return ConsentDenied and restrictions.
594         // NOTE: assume implied consent if the combination of origin and topOrigin allows it.
595         if (accumulatedConfiguration.distinctiveIdentifier == MediaKeysRequirement::Required && !origin->canAccessLocalStorage(topOrigin)) {
596             restrictions.distinctiveIdentifierDenied = true;
597             callback(ConsentStatus::ConsentDenied, WTFMove(accumulatedConfiguration), WTFMove(restrictions));
598             return;
599         }
600
601         // 4. If the distinctiveIdentifier member of accumulated configuration is not "not-allowed", return InformUser.
602         if (accumulatedConfiguration.distinctiveIdentifier != MediaKeysRequirement::NotAllowed) {
603             callback(ConsentStatus::InformUser, WTFMove(accumulatedConfiguration), WTFMove(restrictions));
604             return;
605         }
606
607         // 5. If the user agent requires informing the user for the accumulated configuration for other reasons, return InformUser.
608         // NOTE: assume the user agent does not require informing the user.
609
610         // 6. Return Allowed.
611         callback(ConsentStatus::Allowed, WTFMove(accumulatedConfiguration), WTFMove(restrictions));
612     });
613 }
614
615 void CDM::loadAndInitialize()
616 {
617     if (m_private)
618         m_private->loadAndInitialize();
619 }
620
621 RefPtr<CDMInstance> CDM::createInstance()
622 {
623     if (!m_private)
624         return nullptr;
625     return m_private->createInstance();
626 }
627
628 bool CDM::supportsServerCertificates() const
629 {
630     return m_private && m_private->supportsServerCertificates();
631 }
632
633 bool CDM::supportsSessions() const
634 {
635     return m_private && m_private->supportsSessions();
636 }
637
638 bool CDM::supportsInitDataType(const AtomicString& initDataType) const
639 {
640     return m_private && m_private->supportsInitDataType(initDataType);
641 }
642
643 RefPtr<SharedBuffer> CDM::sanitizeInitData(const AtomicString& initDataType, const SharedBuffer& initData)
644 {
645     return InitDataRegistry::shared().sanitizeInitData(initDataType, initData);
646 }
647
648 bool CDM::supportsInitData(const AtomicString& initDataType, const SharedBuffer& initData)
649 {
650     return m_private && m_private->supportsInitData(initDataType, initData);
651 }
652
653 }
654
655 #endif