Title: [225802] trunk/Source/WebCore
Revision
225802
Author
jer.no...@apple.com
Date
2017-12-12 13:35:58 -0800 (Tue, 12 Dec 2017)

Log Message

[EME] Support reporting and restoring persistent usage data.
https://bugs.webkit.org/show_bug.cgi?id=180684

Reviewed by Eric Carlson.

Add support for reading and acknowledging persistent usage data from the MediaKeys storage
directory.

Add a mechanism for passing the storage location down to CDMPrivate and CDMInstance objects
inside of the CDM class itself:

* Modules/encryptedmedia/CDM.cpp:
(WebCore::CDM::createInstance):
(WebCore::CDM::storageDirectory const):
* Modules/encryptedmedia/CDM.h:

Support loading expired session data, acknowledging expired session data, closing sessions
and removing usable key data.

* platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.h:
* platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.mm:
(WebCore::isEqual):
(WebCore::CDMInstanceFairPlayStreamingAVFObjC::updateLicense):
(WebCore::CDMInstanceFairPlayStreamingAVFObjC::loadSession):
(WebCore::CDMInstanceFairPlayStreamingAVFObjC::closeSession):
(WebCore::CDMInstanceFairPlayStreamingAVFObjC::removeSessionData):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (225801 => 225802)


--- trunk/Source/WebCore/ChangeLog	2017-12-12 21:16:36 UTC (rev 225801)
+++ trunk/Source/WebCore/ChangeLog	2017-12-12 21:35:58 UTC (rev 225802)
@@ -1,3 +1,32 @@
+2017-12-12  Jer Noble  <jer.no...@apple.com>
+
+        [EME] Support reporting and restoring persistent usage data.
+        https://bugs.webkit.org/show_bug.cgi?id=180684
+
+        Reviewed by Eric Carlson.
+
+        Add support for reading and acknowledging persistent usage data from the MediaKeys storage
+        directory.
+
+        Add a mechanism for passing the storage location down to CDMPrivate and CDMInstance objects
+        inside of the CDM class itself:
+
+        * Modules/encryptedmedia/CDM.cpp:
+        (WebCore::CDM::createInstance):
+        (WebCore::CDM::storageDirectory const):
+        * Modules/encryptedmedia/CDM.h:
+
+        Support loading expired session data, acknowledging expired session data, closing sessions
+        and removing usable key data.
+
+        * platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.h:
+        * platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.mm:
+        (WebCore::isEqual):
+        (WebCore::CDMInstanceFairPlayStreamingAVFObjC::updateLicense):
+        (WebCore::CDMInstanceFairPlayStreamingAVFObjC::loadSession):
+        (WebCore::CDMInstanceFairPlayStreamingAVFObjC::closeSession):
+        (WebCore::CDMInstanceFairPlayStreamingAVFObjC::removeSessionData):
+
 2017-12-12  Christopher Reid  <chris.r...@sony.com>
 
         WebGL TextureMapperShaderProgram shaders don't compile in MSVC

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/Modules/encryptedmedia/CDM.h (225801 => 225802)


--- trunk/Source/WebCore/Modules/encryptedmedia/CDM.h	2017-12-12 21:16:36 UTC (rev 225801)
+++ trunk/Source/WebCore/Modules/encryptedmedia/CDM.h	2017-12-12 21:35:58 UTC (rev 225802)
@@ -75,6 +75,8 @@
 
     std::optional<String> sanitizeSessionId(const String& sessionId);
 
+    String storageDirectory() const;
+
 private:
     CDM(Document&, const String& keySystem);
 

Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.h (225801 => 225802)


--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.h	2017-12-12 21:16:36 UTC (rev 225801)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.h	2017-12-12 21:35:58 UTC (rev 225802)
@@ -83,11 +83,12 @@
 
     WeakPtrFactory<CDMInstanceFairPlayStreamingAVFObjC> m_weakPtrFactory;
     RefPtr<SharedBuffer> m_serverCertificate;
-    bool m_persistentStateAllowed { false };
+    bool m_persistentStateAllowed { true };
     RetainPtr<NSURL> m_storageDirectory;
     RetainPtr<AVContentKeySession> m_session;
     RetainPtr<AVContentKeyRequest> m_request;
     RetainPtr<WebCoreFPSContentKeySessionDelegate> m_delegate;
+    Vector<RetainPtr<NSData>> m_expiredSessions;
     String m_sessionId;
 
     LicenseCallback m_requestLicenseCallback;

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
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to