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