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