2 * Copyright (C) 2016 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "MockCDMFactory.h"
29 #if ENABLE(ENCRYPTED_MEDIA)
31 #include "InitDataRegistry.h"
32 #include <JavaScriptCore/ArrayBuffer.h>
33 #include <wtf/NeverDestroyed.h>
35 #include <wtf/text/StringHash.h>
36 #include <wtf/text/StringView.h>
40 MockCDMFactory::MockCDMFactory()
41 : m_supportedSessionTypes({ MediaKeySessionType::Temporary, MediaKeySessionType::PersistentUsageRecord, MediaKeySessionType::PersistentLicense })
43 CDMFactory::registerFactory(*this);
46 MockCDMFactory::~MockCDMFactory()
51 void MockCDMFactory::unregister()
54 CDMFactory::unregisterFactory(*this);
59 bool MockCDMFactory::supportsKeySystem(const String& keySystem)
61 return equalIgnoringASCIICase(keySystem, "org.webkit.mock");
64 void MockCDMFactory::addKeysToSessionWithID(const String& id, Vector<Ref<SharedBuffer>>&& keys)
66 auto addResult = m_sessions.add(id, WTFMove(keys));
67 if (addResult.isNewEntry)
70 auto& value = addResult.iterator->value;
71 for (auto& key : keys)
72 value.append(WTFMove(key));
75 Vector<Ref<SharedBuffer>> MockCDMFactory::removeKeysFromSessionWithID(const String& id)
77 auto it = m_sessions.find(id);
78 if (it == m_sessions.end())
81 return WTFMove(it->value);
84 const Vector<Ref<SharedBuffer>>* MockCDMFactory::keysForSessionWithID(const String& id) const
86 auto it = m_sessions.find(id);
87 if (it == m_sessions.end())
92 void MockCDMFactory::setSupportedDataTypes(Vector<String>&& types)
94 m_supportedDataTypes.clear();
95 for (auto& type : types)
96 m_supportedDataTypes.append(type);
99 std::unique_ptr<CDMPrivate> MockCDMFactory::createCDM(const String&)
101 return std::make_unique<MockCDM>(m_weakPtrFactory.createWeakPtr(*this));
104 MockCDM::MockCDM(WeakPtr<MockCDMFactory> factory)
105 : m_factory(WTFMove(factory))
109 bool MockCDM::supportsInitDataType(const AtomicString& initDataType) const
112 return m_factory->supportedDataTypes().contains(initDataType);
116 bool MockCDM::supportsConfiguration(const MediaKeySystemConfiguration&) const
123 bool MockCDM::supportsConfigurationWithRestrictions(const MediaKeySystemConfiguration&, const MediaKeysRestrictions&) const
129 bool MockCDM::supportsSessionTypeWithConfiguration(MediaKeySessionType& sessionType, const MediaKeySystemConfiguration&) const
131 if (!m_factory || !m_factory->supportedSessionTypes().contains(sessionType))
134 // NOTE: Implement configuration checking;
138 bool MockCDM::supportsRobustness(const String& robustness) const
141 return m_factory->supportedRobustness().contains(robustness);
145 MediaKeysRequirement MockCDM::distinctiveIdentifiersRequirement(const MediaKeySystemConfiguration&, const MediaKeysRestrictions&) const
148 return m_factory->distinctiveIdentifiersRequirement();
149 return MediaKeysRequirement::Optional;
152 MediaKeysRequirement MockCDM::persistentStateRequirement(const MediaKeySystemConfiguration&, const MediaKeysRestrictions&) const
155 return m_factory->persistentStateRequirement();
156 return MediaKeysRequirement::Optional;
159 bool MockCDM::distinctiveIdentifiersAreUniquePerOriginAndClearable(const MediaKeySystemConfiguration&) const
165 RefPtr<CDMInstance> MockCDM::createInstance()
167 if (m_factory && !m_factory->canCreateInstances())
169 return adoptRef(new MockCDMInstance(m_weakPtrFactory.createWeakPtr(*this)));
172 void MockCDM::loadAndInitialize()
177 bool MockCDM::supportsServerCertificates() const
179 return m_factory && m_factory->supportsServerCertificates();
182 bool MockCDM::supportsSessions() const
184 return m_factory && m_factory->supportsSessions();
187 bool MockCDM::supportsInitData(const AtomicString& initDataType, const SharedBuffer& initData) const
189 if (!supportsInitDataType(initDataType))
192 UNUSED_PARAM(initData);
196 RefPtr<SharedBuffer> MockCDM::sanitizeResponse(const SharedBuffer& response) const
198 if (!charactersAreAllASCII(reinterpret_cast<const LChar*>(response.data()), response.size()))
201 Vector<String> responseArray;
202 String(response.data(), response.size()).split(ASCIILiteral(" "), responseArray);
204 if (!responseArray.contains(String(ASCIILiteral("valid-response"))))
207 return response.copy();
210 std::optional<String> MockCDM::sanitizeSessionId(const String& sessionId) const
212 if (equalLettersIgnoringASCIICase(sessionId, "valid-loaded-session"))
217 MockCDMInstance::MockCDMInstance(WeakPtr<MockCDM> cdm)
222 CDMInstance::SuccessValue MockCDMInstance::initializeWithConfiguration(const MediaKeySystemConfiguration& configuration)
224 if (!m_cdm || !m_cdm->supportsConfiguration(configuration))
230 CDMInstance::SuccessValue MockCDMInstance::setDistinctiveIdentifiersAllowed(bool distinctiveIdentifiersAllowed)
232 if (m_distinctiveIdentifiersAllowed == distinctiveIdentifiersAllowed)
235 auto* factory = m_cdm ? m_cdm->factory() : nullptr;
237 if (!factory || (!distinctiveIdentifiersAllowed && factory->distinctiveIdentifiersRequirement() == MediaKeysRequirement::Required))
240 m_distinctiveIdentifiersAllowed = distinctiveIdentifiersAllowed;
244 CDMInstance::SuccessValue MockCDMInstance::setPersistentStateAllowed(bool persistentStateAllowed)
246 if (m_persistentStateAllowed == persistentStateAllowed)
249 MockCDMFactory* factory = m_cdm ? m_cdm->factory() : nullptr;
251 if (!factory || (!persistentStateAllowed && factory->persistentStateRequirement() == MediaKeysRequirement::Required))
254 m_persistentStateAllowed = persistentStateAllowed;
258 CDMInstance::SuccessValue MockCDMInstance::setServerCertificate(Ref<SharedBuffer>&& certificate)
260 StringView certificateStringView(reinterpret_cast<const LChar*>(certificate->data()), certificate->size());
262 if (equalIgnoringASCIICase(certificateStringView, "valid"))
267 CDMInstance::SuccessValue MockCDMInstance::setStorageDirectory(const String&)
269 // On disk storage is unused; no-op.
273 void MockCDMInstance::requestLicense(LicenseType licenseType, const AtomicString& initDataType, Ref<SharedBuffer>&& initData, LicenseCallback callback)
275 MockCDMFactory* factory = m_cdm ? m_cdm->factory() : nullptr;
277 callback(SharedBuffer::create(), emptyAtom(), false, SuccessValue::Failed);
281 if (!factory->supportedSessionTypes().contains(licenseType) || !factory->supportedDataTypes().contains(initDataType)) {
282 callback(SharedBuffer::create(), emptyString(), false, SuccessValue::Failed);
286 auto keyIDs = InitDataRegistry::shared().extractKeyIDs(initDataType, initData);
287 if (keyIDs.isEmpty()) {
288 callback(SharedBuffer::create(), emptyString(), false, SuccessValue::Failed);
292 String sessionID = createCanonicalUUIDString();
293 factory->addKeysToSessionWithID(sessionID, WTFMove(keyIDs));
295 CString license { "license" };
296 callback(SharedBuffer::create(license.data(), license.length()), sessionID, false, SuccessValue::Succeeded);
299 void MockCDMInstance::updateLicense(const String& sessionID, LicenseType, const SharedBuffer& response, LicenseUpdateCallback callback)
301 MockCDMFactory* factory = m_cdm ? m_cdm->factory() : nullptr;
303 callback(false, std::nullopt, std::nullopt, std::nullopt, SuccessValue::Failed);
307 Vector<String> responseVector;
308 String(response.data(), response.size()).split(ASCIILiteral(" "), responseVector);
310 if (responseVector.contains(String(ASCIILiteral("invalid-format")))) {
311 callback(false, std::nullopt, std::nullopt, std::nullopt, SuccessValue::Failed);
315 std::optional<KeyStatusVector> changedKeys;
316 if (responseVector.contains(String(ASCIILiteral("keys-changed")))) {
317 const auto* keys = factory->keysForSessionWithID(sessionID);
319 KeyStatusVector keyStatusVector;
320 keyStatusVector.reserveInitialCapacity(keys->size());
321 for (auto& key : *keys)
322 keyStatusVector.uncheckedAppend({ key.copyRef(), KeyStatus::Usable });
324 changedKeys = WTFMove(keyStatusVector);
328 // FIXME: Session closure, expiration and message handling should be implemented
329 // once the relevant algorithms are supported.
331 callback(false, WTFMove(changedKeys), std::nullopt, std::nullopt, SuccessValue::Succeeded);
334 void MockCDMInstance::loadSession(LicenseType, const String&, const String&, LoadSessionCallback callback)
336 MockCDMFactory* factory = m_cdm ? m_cdm->factory() : nullptr;
338 callback(std::nullopt, std::nullopt, std::nullopt, SuccessValue::Failed, SessionLoadFailure::Other);
342 // FIXME: Key status and expiration handling should be implemented once the relevant algorithms are supported.
344 CString messageData { "session loaded" };
345 Message message { MessageType::LicenseRenewal, SharedBuffer::create(messageData.data(), messageData.length()) };
347 callback(std::nullopt, std::nullopt, WTFMove(message), SuccessValue::Succeeded, SessionLoadFailure::None);
350 void MockCDMInstance::closeSession(const String& sessionID, CloseSessionCallback callback)
352 MockCDMFactory* factory = m_cdm ? m_cdm->factory() : nullptr;
358 factory->removeSessionWithID(sessionID);
362 void MockCDMInstance::removeSessionData(const String& id, LicenseType, RemoveSessionDataCallback callback)
364 MockCDMFactory* factory = m_cdm ? m_cdm->factory() : nullptr;
366 callback({ }, std::nullopt, SuccessValue::Failed);
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 });
376 CString message { "remove-message" };
377 callback(WTFMove(keyStatusVector), SharedBuffer::create(message.data(), message.length()), SuccessValue::Succeeded);
380 void MockCDMInstance::storeRecordOfKeyUsage(const String&)
382 // FIXME: This should be implemented along with the support for persistent-usage-record sessions.
385 const String& MockCDMInstance::keySystem() const
387 static const NeverDestroyed<String> s_keySystem = MAKE_STATIC_STRING_IMPL("org.webkit.mock");