Enable Pointer Events on watchOS
[WebKit-https.git] / Source / WebCore / testing / MockCDMFactory.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 "MockCDMFactory.h"
28
29 #if ENABLE(ENCRYPTED_MEDIA)
30
31 #include "InitDataRegistry.h"
32 #include <JavaScriptCore/ArrayBuffer.h>
33 #include <wtf/Algorithms.h>
34 #include <wtf/NeverDestroyed.h>
35 #include <wtf/UUID.h>
36 #include <wtf/text/StringHash.h>
37 #include <wtf/text/StringView.h>
38
39 namespace WebCore {
40
41 MockCDMFactory::MockCDMFactory()
42     : m_supportedSessionTypes({ MediaKeySessionType::Temporary, MediaKeySessionType::PersistentUsageRecord, MediaKeySessionType::PersistentLicense })
43     , m_supportedEncryptionSchemes({ MediaKeyEncryptionScheme::cenc })
44 {
45     CDMFactory::registerFactory(*this);
46 }
47
48 MockCDMFactory::~MockCDMFactory()
49 {
50     unregister();
51 }
52
53 void MockCDMFactory::unregister()
54 {
55     if (m_registered) {
56         CDMFactory::unregisterFactory(*this);
57         m_registered = false;
58     }
59 }
60
61 bool MockCDMFactory::supportsKeySystem(const String& keySystem)
62 {
63     return equalIgnoringASCIICase(keySystem, "org.webkit.mock");
64 }
65
66 void MockCDMFactory::addKeysToSessionWithID(const String& id, Vector<Ref<SharedBuffer>>&& keys)
67 {
68     auto addResult = m_sessions.add(id, WTFMove(keys));
69     if (addResult.isNewEntry)
70         return;
71
72     auto& value = addResult.iterator->value;
73     for (auto& key : keys)
74         value.append(WTFMove(key));
75 }
76
77 Vector<Ref<SharedBuffer>> MockCDMFactory::removeKeysFromSessionWithID(const String& id)
78 {
79     auto it = m_sessions.find(id);
80     if (it == m_sessions.end())
81         return { };
82
83     return WTFMove(it->value);
84 }
85
86 const Vector<Ref<SharedBuffer>>* MockCDMFactory::keysForSessionWithID(const String& id) const
87 {
88     auto it = m_sessions.find(id);
89     if (it == m_sessions.end())
90         return nullptr;
91     return &it->value;
92 }
93
94 void MockCDMFactory::setSupportedDataTypes(Vector<String>&& types)
95 {
96     m_supportedDataTypes.clear();
97     for (auto& type : types)
98         m_supportedDataTypes.append(type);
99 }
100
101 std::unique_ptr<CDMPrivate> MockCDMFactory::createCDM(const String&)
102 {
103     return std::make_unique<MockCDM>(makeWeakPtr(*this));
104 }
105
106 MockCDM::MockCDM(WeakPtr<MockCDMFactory> factory)
107     : m_factory(WTFMove(factory))
108 {
109 }
110
111 bool MockCDM::supportsInitDataType(const AtomicString& initDataType) const
112 {
113     if (m_factory)
114         return m_factory->supportedDataTypes().contains(initDataType);
115     return false;
116 }
117
118 bool MockCDM::supportsConfiguration(const MediaKeySystemConfiguration& configuration) const
119 {
120     auto capabilityHasSupportedEncryptionScheme = [&] (auto& capability) {
121         if (capability.encryptionScheme)
122             return m_factory->supportedEncryptionSchemes().contains(capability.encryptionScheme.value());
123         return true;
124     };
125
126     if (!configuration.audioCapabilities.isEmpty() && !anyOf(configuration.audioCapabilities, capabilityHasSupportedEncryptionScheme))
127         return false;
128
129     if (!configuration.videoCapabilities.isEmpty() && !anyOf(configuration.videoCapabilities, capabilityHasSupportedEncryptionScheme))
130         return false;
131
132     return true;
133
134 }
135
136 bool MockCDM::supportsConfigurationWithRestrictions(const MediaKeySystemConfiguration&, const MediaKeysRestrictions&) const
137 {
138     // NOTE: Implement;
139     return true;
140 }
141
142 bool MockCDM::supportsSessionTypeWithConfiguration(MediaKeySessionType& sessionType, const MediaKeySystemConfiguration&) const
143 {
144     if (!m_factory || !m_factory->supportedSessionTypes().contains(sessionType))
145         return false;
146
147     // NOTE: Implement configuration checking;
148     return true;
149 }
150
151 bool MockCDM::supportsRobustness(const String& robustness) const
152 {
153     if (m_factory)
154         return m_factory->supportedRobustness().contains(robustness);
155     return false;
156 }
157
158 MediaKeysRequirement MockCDM::distinctiveIdentifiersRequirement(const MediaKeySystemConfiguration&, const MediaKeysRestrictions&) const
159 {
160     if (m_factory)
161         return m_factory->distinctiveIdentifiersRequirement();
162     return MediaKeysRequirement::Optional;
163 }
164
165 MediaKeysRequirement MockCDM::persistentStateRequirement(const MediaKeySystemConfiguration&, const MediaKeysRestrictions&) const
166 {
167     if (m_factory)
168         return m_factory->persistentStateRequirement();
169     return MediaKeysRequirement::Optional;
170 }
171
172 bool MockCDM::distinctiveIdentifiersAreUniquePerOriginAndClearable(const MediaKeySystemConfiguration&) const
173 {
174     // NOTE: Implement;
175     return true;
176 }
177
178 RefPtr<CDMInstance> MockCDM::createInstance()
179 {
180     if (m_factory && !m_factory->canCreateInstances())
181         return nullptr;
182     return adoptRef(new MockCDMInstance(makeWeakPtr(*this)));
183 }
184
185 void MockCDM::loadAndInitialize()
186 {
187     // No-op.
188 }
189
190 bool MockCDM::supportsServerCertificates() const
191 {
192     return m_factory && m_factory->supportsServerCertificates();
193 }
194
195 bool MockCDM::supportsSessions() const
196 {
197     return m_factory && m_factory->supportsSessions();
198 }
199
200 bool MockCDM::supportsInitData(const AtomicString& initDataType, const SharedBuffer& initData) const
201 {
202     if (!supportsInitDataType(initDataType))
203         return false;
204
205     UNUSED_PARAM(initData);
206     return true;
207 }
208
209 RefPtr<SharedBuffer> MockCDM::sanitizeResponse(const SharedBuffer& response) const
210 {
211     if (!charactersAreAllASCII(reinterpret_cast<const LChar*>(response.data()), response.size()))
212         return nullptr;
213
214     Vector<String> responseArray = String(response.data(), response.size()).split(' ');
215
216     if (!responseArray.contains(String("valid-response"_s)))
217         return nullptr;
218
219     return response.copy();
220 }
221
222 Optional<String> MockCDM::sanitizeSessionId(const String& sessionId) const
223 {
224     if (equalLettersIgnoringASCIICase(sessionId, "valid-loaded-session"))
225         return sessionId;
226     return WTF::nullopt;
227 }
228
229 MockCDMInstance::MockCDMInstance(WeakPtr<MockCDM> cdm)
230     : m_cdm(cdm)
231 {
232 }
233
234 CDMInstance::SuccessValue MockCDMInstance::initializeWithConfiguration(const MediaKeySystemConfiguration& configuration)
235 {
236     if (!m_cdm || !m_cdm->supportsConfiguration(configuration))
237         return Failed;
238
239     return Succeeded;
240 }
241
242 CDMInstance::SuccessValue MockCDMInstance::setDistinctiveIdentifiersAllowed(bool distinctiveIdentifiersAllowed)
243 {
244     if (m_distinctiveIdentifiersAllowed == distinctiveIdentifiersAllowed)
245         return Succeeded;
246
247     auto* factory = m_cdm ? m_cdm->factory() : nullptr;
248
249     if (!factory || (!distinctiveIdentifiersAllowed && factory->distinctiveIdentifiersRequirement() == MediaKeysRequirement::Required))
250         return Failed;
251
252     m_distinctiveIdentifiersAllowed = distinctiveIdentifiersAllowed;
253     return Succeeded;
254 }
255
256 CDMInstance::SuccessValue MockCDMInstance::setPersistentStateAllowed(bool persistentStateAllowed)
257 {
258     if (m_persistentStateAllowed == persistentStateAllowed)
259         return Succeeded;
260
261     MockCDMFactory* factory = m_cdm ? m_cdm->factory() : nullptr;
262
263     if (!factory || (!persistentStateAllowed && factory->persistentStateRequirement() == MediaKeysRequirement::Required))
264         return Failed;
265
266     m_persistentStateAllowed = persistentStateAllowed;
267     return Succeeded;
268 }
269
270 CDMInstance::SuccessValue MockCDMInstance::setServerCertificate(Ref<SharedBuffer>&& certificate)
271 {
272     StringView certificateStringView(reinterpret_cast<const LChar*>(certificate->data()), certificate->size());
273
274     if (equalIgnoringASCIICase(certificateStringView, "valid"))
275         return Succeeded;
276     return Failed;
277 }
278
279 CDMInstance::SuccessValue MockCDMInstance::setStorageDirectory(const String&)
280 {
281     // On disk storage is unused; no-op.
282     return Succeeded;
283 }
284
285 const String& MockCDMInstance::keySystem() const
286 {
287     static const NeverDestroyed<String> s_keySystem = MAKE_STATIC_STRING_IMPL("org.webkit.mock");
288
289     return s_keySystem;
290 }
291
292 RefPtr<CDMInstanceSession> MockCDMInstance::createSession()
293 {
294     return adoptRef(new MockCDMInstanceSession(makeWeakPtr(*this)));
295 }
296
297 MockCDMInstanceSession::MockCDMInstanceSession(WeakPtr<MockCDMInstance>&& instance)
298     : m_instance(WTFMove(instance))
299 {
300 }
301
302 void MockCDMInstanceSession::requestLicense(LicenseType licenseType, const AtomicString& initDataType, Ref<SharedBuffer>&& initData, LicenseCallback&& callback)
303 {
304     MockCDMFactory* factory = m_instance ? m_instance->factory() : nullptr;
305     if (!factory) {
306         callback(SharedBuffer::create(), emptyAtom(), false, SuccessValue::Failed);
307         return;
308     }
309
310     if (!factory->supportedSessionTypes().contains(licenseType) || !factory->supportedDataTypes().contains(initDataType)) {
311         callback(SharedBuffer::create(), emptyString(), false, SuccessValue::Failed);
312         return;
313     }
314
315     auto keyIDs = InitDataRegistry::shared().extractKeyIDs(initDataType, initData);
316     if (!keyIDs || keyIDs.value().isEmpty()) {
317         callback(SharedBuffer::create(), emptyString(), false, SuccessValue::Failed);
318         return;
319     }
320
321     String sessionID = createCanonicalUUIDString();
322     factory->addKeysToSessionWithID(sessionID, WTFMove(keyIDs.value()));
323
324     CString license { "license" };
325     callback(SharedBuffer::create(license.data(), license.length()), sessionID, false, SuccessValue::Succeeded);
326 }
327
328 void MockCDMInstanceSession::updateLicense(const String& sessionID, LicenseType, const SharedBuffer& response, LicenseUpdateCallback&& callback)
329 {
330     MockCDMFactory* factory = m_instance ? m_instance->factory() : nullptr;
331     if (!factory) {
332         callback(false, WTF::nullopt, WTF::nullopt, WTF::nullopt, SuccessValue::Failed);
333         return;
334     }
335
336     Vector<String> responseVector = String(response.data(), response.size()).split(' ');
337
338     if (responseVector.contains(String("invalid-format"_s))) {
339         callback(false, WTF::nullopt, WTF::nullopt, WTF::nullopt, SuccessValue::Failed);
340         return;
341     }
342
343     Optional<KeyStatusVector> changedKeys;
344     if (responseVector.contains(String("keys-changed"_s))) {
345         const auto* keys = factory->keysForSessionWithID(sessionID);
346         if (keys) {
347             KeyStatusVector keyStatusVector;
348             keyStatusVector.reserveInitialCapacity(keys->size());
349             for (auto& key : *keys)
350                 keyStatusVector.uncheckedAppend({ key.copyRef(), KeyStatus::Usable });
351
352             changedKeys = WTFMove(keyStatusVector);
353         }
354     }
355
356     // FIXME: Session closure, expiration and message handling should be implemented
357     // once the relevant algorithms are supported.
358
359     callback(false, WTFMove(changedKeys), WTF::nullopt, WTF::nullopt, SuccessValue::Succeeded);
360 }
361
362 void MockCDMInstanceSession::loadSession(LicenseType, const String&, const String&, LoadSessionCallback&& callback)
363 {
364     MockCDMFactory* factory = m_instance ? m_instance->factory() : nullptr;
365     if (!factory) {
366         callback(WTF::nullopt, WTF::nullopt, WTF::nullopt, SuccessValue::Failed, SessionLoadFailure::Other);
367         return;
368     }
369
370     // FIXME: Key status and expiration handling should be implemented once the relevant algorithms are supported.
371
372     CString messageData { "session loaded" };
373     Message message { MessageType::LicenseRenewal, SharedBuffer::create(messageData.data(), messageData.length()) };
374
375     callback(WTF::nullopt, WTF::nullopt, WTFMove(message), SuccessValue::Succeeded, SessionLoadFailure::None);
376 }
377
378 void MockCDMInstanceSession::closeSession(const String& sessionID, CloseSessionCallback&& callback)
379 {
380     MockCDMFactory* factory = m_instance ? m_instance->factory() : nullptr;
381     if (!factory) {
382         callback();
383         return;
384     }
385
386     factory->removeSessionWithID(sessionID);
387     callback();
388 }
389
390 void MockCDMInstanceSession::removeSessionData(const String& id, LicenseType, RemoveSessionDataCallback&& callback)
391 {
392     MockCDMFactory* factory = m_instance ? m_instance->factory() : nullptr;
393     if (!factory) {
394         callback({ }, WTF::nullopt, SuccessValue::Failed);
395         return;
396     }
397
398     auto keys = factory->removeKeysFromSessionWithID(id);
399     KeyStatusVector keyStatusVector;
400     keyStatusVector.reserveInitialCapacity(keys.size());
401     for (auto& key : keys)
402         keyStatusVector.uncheckedAppend({ WTFMove(key), KeyStatus::Released });
403
404     CString message { "remove-message" };
405     callback(WTFMove(keyStatusVector), SharedBuffer::create(message.data(), message.length()), SuccessValue::Succeeded);
406 }
407
408 void MockCDMInstanceSession::storeRecordOfKeyUsage(const String&)
409 {
410     // FIXME: This should be implemented along with the support for persistent-usage-record sessions.
411 }
412
413 }
414
415 #endif