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