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