Modified: trunk/Source/WebCore/Modules/encryptedmedia/CDM.cpp (225801 => 225802)
--- trunk/Source/WebCore/Modules/encryptedmedia/CDM.cpp 2017-12-12 21:16:36 UTC (rev 225801)
+++ trunk/Source/WebCore/Modules/encryptedmedia/CDM.cpp 2017-12-12 21:35:58 UTC (rev 225802)
@@ -31,13 +31,17 @@
#include "CDMFactory.h"
#include "CDMPrivate.h"
#include "Document.h"
+#include "FileSystem.h"
#include "InitDataRegistry.h"
#include "MediaKeysRequirement.h"
#include "MediaPlayer.h"
#include "NotImplemented.h"
+#include "Page.h"
#include "ParsedContentType.h"
#include "ScriptExecutionContext.h"
#include "SecurityOrigin.h"
+#include "SecurityOriginData.h"
+#include "Settings.h"
#include <wtf/NeverDestroyed.h>
namespace WebCore {
@@ -590,7 +594,9 @@
{
if (!m_private)
return nullptr;
- return m_private->createInstance();
+ auto instance = m_private->createInstance();
+ instance->setStorageDirectory(storageDirectory());
+ return instance;
}
bool CDM::supportsServerCertificates() const
@@ -632,6 +638,23 @@
return m_private->sanitizeSessionId(sessionId);
}
+String CDM::storageDirectory() const
+{
+ auto* document = downcast<Document>(scriptExecutionContext());
+ if (!document)
+ return emptyString();
+
+ auto* page = document->page();
+ if (!page || page->usesEphemeralSession())
+ return emptyString();
+
+ auto storageDirectory = document->settings().mediaKeysStorageDirectory();
+ if (storageDirectory.isEmpty())
+ return emptyString();
+
+ return FileSystem::pathByAppendingComponent(storageDirectory, SecurityOriginData::fromSecurityOrigin(document->securityOrigin()).databaseIdentifier());
}
+}
+
#endif
Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.mm (225801 => 225802)
--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.mm 2017-12-12 21:16:36 UTC (rev 225801)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.mm 2017-12-12 21:35:58 UTC (rev 225802)
@@ -32,6 +32,7 @@
#import "CDMKeySystemConfiguration.h"
#import "NotImplemented.h"
#import "SharedBuffer.h"
+#import "TextDecoder.h"
#import <AVFoundation/AVContentKeySession.h>
#import <pal/spi/mac/AVFoundationSPI.h>
#import <wtf/SoftLinking.h>
@@ -47,6 +48,8 @@
SOFT_LINK_CLASS_OPTIONAL(AVFoundation, AVPersistableContentKeyRequest);
#endif
+static const NSString *PlaybackSessionIdKey = @"PlaybackSessionID";
+
@interface WebCoreFPSContentKeySessionDelegate : NSObject<AVContentKeySessionDelegate> {
WebCore::CDMInstanceFairPlayStreamingAVFObjC* _parent;
}
@@ -239,8 +242,36 @@
[m_session processContentKeyRequestWithIdentifier:nil initializationData:initData->createNSData().get() options:nil];
}
+static bool isEqual(const SharedBuffer& data, const String& value)
+{
+ auto arrayBuffer = data.tryCreateArrayBuffer();
+ if (!arrayBuffer)
+ return false;
+
+ auto exceptionOrDecoder = TextDecoder::create(ASCIILiteral("utf8"), TextDecoder::Options());
+ if (exceptionOrDecoder.hasException())
+ return false;
+
+ Ref<TextDecoder> decoder = exceptionOrDecoder.releaseReturnValue();
+ auto stringOrException = decoder->decode(BufferSource::VariantType(WTFMove(arrayBuffer)), TextDecoder::DecodeOptions());
+ if (stringOrException.hasException())
+ return false;
+
+ return stringOrException.returnValue() == value;
+}
+
void CDMInstanceFairPlayStreamingAVFObjC::updateLicense(const String&, LicenseType, const SharedBuffer& responseData, LicenseUpdateCallback callback)
{
+ if (!m_expiredSessions.isEmpty() && isEqual(responseData, ASCIILiteral("acknowledged"))) {
+ auto expiredSessions = adoptNS([[NSMutableArray alloc] init]);
+ for (auto& session : m_expiredSessions)
+ [expiredSessions addObject:session.get()];
+ RetainPtr<NSData> appIdentifier = m_serverCertificate->createNSData();
+ [getAVContentKeySessionClass() removePendingExpiredSessionReports:expiredSessions.get() withAppIdentifier:appIdentifier.get() storageDirectoryAtURL:m_storageDirectory.get()];
+ callback(false, { }, std::nullopt, std::nullopt, Succeeded);
+ return;
+ }
+
if (!m_request) {
callback(false, std::nullopt, std::nullopt, std::nullopt, Failed);
return;
@@ -262,24 +293,98 @@
callback(false, std::make_optional(WTFMove(keyStatuses)), std::nullopt, std::nullopt, Succeeded);
}
-void CDMInstanceFairPlayStreamingAVFObjC::loadSession(LicenseType, const String&, const String&, LoadSessionCallback)
+void CDMInstanceFairPlayStreamingAVFObjC::loadSession(LicenseType licenseType, const String& sessionId, const String& origin, LoadSessionCallback callback)
{
- notImplemented();
+ UNUSED_PARAM(origin);
+ if (licenseType == LicenseType::PersistentUsageRecord) {
+ if (!m_persistentStateAllowed || !m_storageDirectory) {
+ callback(std::nullopt, std::nullopt, std::nullopt, Failed, SessionLoadFailure::MismatchedSessionType);
+ return;
+ }
+ if (!m_serverCertificate) {
+ callback(std::nullopt, std::nullopt, std::nullopt, Failed, SessionLoadFailure::Other);
+ return;
+ }
+
+ RetainPtr<NSData> appIdentifier = m_serverCertificate->createNSData();
+ KeyStatusVector changedKeys;
+ for (NSData* expiredSessionData in [getAVContentKeySessionClass() pendingExpiredSessionReportsWithAppIdentifier:appIdentifier.get() storageDirectoryAtURL:m_storageDirectory.get()]) {
+ NSDictionary *expiredSession = [NSPropertyListSerialization propertyListWithData:expiredSessionData options:kCFPropertyListImmutable format:nullptr error:nullptr];
+ NSString *playbackSessionIdValue = (NSString *)[expiredSession objectForKey:PlaybackSessionIdKey];
+ if (![playbackSessionIdValue isKindOfClass:[NSString class]])
+ continue;
+
+ if (sessionId == String(playbackSessionIdValue)) {
+ // FIXME(rdar://problem/35934922): use key values stored in expired session report once available
+ changedKeys.append((KeyStatusVector::ValueType){ SharedBuffer::create(), KeyStatus::Released });
+ m_expiredSessions.append(expiredSessionData);
+ }
+ }
+
+ if (changedKeys.isEmpty()) {
+ callback(std::nullopt, std::nullopt, std::nullopt, Failed, SessionLoadFailure::NoSessionData);
+ return;
+ }
+
+ callback(WTFMove(changedKeys), std::nullopt, std::nullopt, Succeeded, SessionLoadFailure::None);
+ }
}
-void CDMInstanceFairPlayStreamingAVFObjC::closeSession(const String&, CloseSessionCallback)
+void CDMInstanceFairPlayStreamingAVFObjC::closeSession(const String&, CloseSessionCallback callback)
{
- notImplemented();
+ if (m_requestLicenseCallback) {
+ m_requestLicenseCallback(SharedBuffer::create(), m_sessionId, false, Failed);
+ m_requestLicenseCallback = nullptr;
+ }
+ if (m_updateLicenseCallback) {
+ m_updateLicenseCallback(true, std::nullopt, std::nullopt, std::nullopt, Failed);
+ m_updateLicenseCallback = nullptr;
+ }
+ if (m_removeSessionDataCallback) {
+ m_removeSessionDataCallback({ }, std::nullopt, Failed);
+ m_removeSessionDataCallback = nullptr;
+ }
+ m_session = nullptr;
+ m_request = nullptr;
+ callback();
}
-void CDMInstanceFairPlayStreamingAVFObjC::removeSessionData(const String&, LicenseType, RemoveSessionDataCallback)
+void CDMInstanceFairPlayStreamingAVFObjC::removeSessionData(const String& sessionId, LicenseType licenseType, RemoveSessionDataCallback callback)
{
- notImplemented();
+ [m_session expire];
+
+ if (licenseType == LicenseType::PersistentUsageRecord) {
+ if (!m_persistentStateAllowed || !m_storageDirectory || !m_serverCertificate) {
+ callback({ }, std::nullopt, Failed);
+ return;
+ }
+
+ RetainPtr<NSData> appIdentifier = m_serverCertificate->createNSData();
+ RetainPtr<NSMutableArray> expiredSessionsArray = adoptNS([[NSMutableArray alloc] init]);
+ KeyStatusVector changedKeys;
+ for (NSData* expiredSessionData in [getAVContentKeySessionClass() pendingExpiredSessionReportsWithAppIdentifier:appIdentifier.get() storageDirectoryAtURL:m_storageDirectory.get()]) {
+ NSDictionary *expiredSession = [NSPropertyListSerialization propertyListWithData:expiredSessionData options:kCFPropertyListImmutable format:nullptr error:nullptr];
+ NSString *playbackSessionIdValue = (NSString *)[expiredSession objectForKey:PlaybackSessionIdKey];
+ if (![playbackSessionIdValue isKindOfClass:[NSString class]])
+ continue;
+
+ if (sessionId == String(playbackSessionIdValue)) {
+ // FIXME(rdar://problem/35934922): use key values stored in expired session report once available
+ changedKeys.append((KeyStatusVector::ValueType){ SharedBuffer::create(), KeyStatus::Released });
+ m_expiredSessions.append(expiredSessionData);
+ [expiredSessionsArray addObject:expiredSession];
+ }
+ }
+
+ RetainPtr<NSData> expiredSessionsData = [NSPropertyListSerialization dataWithPropertyList:expiredSessionsArray.get() format:NSPropertyListBinaryFormat_v1_0 options:kCFPropertyListImmutable error:nullptr];
+
+ callback(WTFMove(changedKeys), SharedBuffer::create(expiredSessionsData.get()), Succeeded);
+ }
}
void CDMInstanceFairPlayStreamingAVFObjC::storeRecordOfKeyUsage(const String&)
{
- notImplemented();
+ // no-op; key usage data is stored automatically.
}
const String& CDMInstanceFairPlayStreamingAVFObjC::keySystem() const