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