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