Title: [212107] trunk
Revision
212107
Author
zandober...@gmail.com
Date
2017-02-10 02:22:50 -0800 (Fri, 10 Feb 2017)

Log Message

[EME] Implement MediaKeySession::updateKeyStatuses(), MediaKeyStatusMap
https://bugs.webkit.org/show_bug.cgi?id=167888

Reviewed by Xabier Rodriguez-Calvar.

Source/WebCore:

Implement MediaKeySession::updateKeyStatuses(), transforming the passed-in
KeyStatusVector into a Vector mapping the key IDs to MediaKeyStatus values.
A keystatuseschange event is fired on the MediaKeySession object afterwards.
The queueing of the task that runs the 'attemp to resume playback' on the
related HTMLMediaElement objects isn't done yet since that algorithm isn't
implemented yet.

The statuses Vector is stored on the MediaKeySession object. That Vector is
then exposed through the MediaKeyStatusMap object, each such object being
unique to one MediaKeySession object. The implementation of MediaKeyStatusMap
thus keeps a reference to the session object as long as that object is alive,
and queries the MediaKeySession::statuses() getter to access the Vector that
contains status information for all the key IDs.

MediaKeyStatusMap::Iterator object keeps a reference to the MediaKeyStatusMap
object and accesses the statuses by indexing into the status Vector of the
related MediaKeySession object.

CDMInstance::updateLicense() now accepts the session ID string as the first
argument, making it possible to specify which session should be updated.

MockCDMFactory::keysForSessionWithID() returns an optional reference to the
Vector value in the session map that lists all the key IDs that are being
stored for that session.

MockCDMInstance::updateLicense() now detects the 'keys-changed' entry in the
passed-in response data, and upon detecting that constructs a KeyStatusVector
object containing all the keys for that session. KeyStatus::Usable is returned
for each object at the moment, but this should be adjustable in the future
through additional parameters passed through the response data. The Vector
object is then passed to the callback and is then passed to the 'update key
statuses' algorithm in MediaKeySession.

Covered by a test case in media/encrypted-media/mock-MediaKeySession-update.html.

* Modules/encryptedmedia/CDMInstance.h:
* Modules/encryptedmedia/MediaKeySession.cpp:
(WebCore::MediaKeySession::MediaKeySession):
(WebCore::MediaKeySession::~MediaKeySession):
(WebCore::MediaKeySession::update):
(WebCore::MediaKeySession::updateKeyStatuses):
* Modules/encryptedmedia/MediaKeySession.h:
* Modules/encryptedmedia/MediaKeyStatusMap.cpp:
(WebCore::MediaKeyStatusMap::MediaKeyStatusMap):
(WebCore::MediaKeyStatusMap::detachSession):
(WebCore::MediaKeyStatusMap::size):
(WebCore::keyIdsMatch):
(WebCore::MediaKeyStatusMap::has):
(WebCore::MediaKeyStatusMap::get):
(WebCore::MediaKeyStatusMap::Iterator::Iterator):
(WebCore::MediaKeyStatusMap::Iterator::next):
* Modules/encryptedmedia/MediaKeyStatusMap.h:
(WebCore::MediaKeyStatusMap::create):
* Modules/encryptedmedia/MediaKeyStatusMap.idl:
* testing/MockCDMFactory.cpp:
(WebCore::MockCDMFactory::keysForSessionWithID):
(WebCore::MockCDMInstance::updateLicense):
* testing/MockCDMFactory.h:

LayoutTests:

Add another test case to the mock-MediaKeySession-update.html test that
ensures the keystatuseschange event is fired on the MediaKeySession object
and that the status of the keys is properly reported through the
MediaKeyStatusMap object associated with this MediaKeySession.

* media/encrypted-media/mock-MediaKeySession-update-expected.txt:
* media/encrypted-media/mock-MediaKeySession-update.html:

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (212106 => 212107)


--- trunk/LayoutTests/ChangeLog	2017-02-10 09:48:40 UTC (rev 212106)
+++ trunk/LayoutTests/ChangeLog	2017-02-10 10:22:50 UTC (rev 212107)
@@ -1,3 +1,18 @@
+2017-02-10  Zan Dobersek  <zdober...@igalia.com>
+
+        [EME] Implement MediaKeySession::updateKeyStatuses(), MediaKeyStatusMap
+        https://bugs.webkit.org/show_bug.cgi?id=167888
+
+        Reviewed by Xabier Rodriguez-Calvar.
+
+        Add another test case to the mock-MediaKeySession-update.html test that
+        ensures the keystatuseschange event is fired on the MediaKeySession object
+        and that the status of the keys is properly reported through the
+        MediaKeyStatusMap object associated with this MediaKeySession.
+
+        * media/encrypted-media/mock-MediaKeySession-update-expected.txt:
+        * media/encrypted-media/mock-MediaKeySession-update.html:
+
 2017-02-09  Carlos Garcia Campos  <cgar...@igalia.com>
 
         Unreviewed GTK+ gardening. Update expectations of tests using CSS3 Text each-line and hanging indent.

Modified: trunk/LayoutTests/media/encrypted-media/mock-MediaKeySession-update-expected.txt (212106 => 212107)


--- trunk/LayoutTests/media/encrypted-media/mock-MediaKeySession-update-expected.txt	2017-02-10 09:48:40 UTC (rev 212106)
+++ trunk/LayoutTests/media/encrypted-media/mock-MediaKeySession-update-expected.txt	2017-02-10 10:22:50 UTC (rev 212107)
@@ -12,13 +12,13 @@
 Using a non-callable MediaKeySession should reject.
 RUN(mediaKeySession = mediaKeys.createSession("temporary"))
 EXPECTED (typeof mediaKeySession == 'object') OK
-RUN(promise = mediaKeySession.update(stringToUInt8Array("invalid-state")))
+RUN(promise = mediaKeySession.update(encoder.encode("invalid-state")))
 Promise rejected correctly OK
 
 Using a zero-length response should reject.
 RUN(kids = JSON.stringify({ kids: [ "MTIzNDU=" ] }))
 RUN(mediaKeySession = mediaKeys.createSession("temporary"))
-RUN(promise = mediaKeySession.generateRequest("keyids", stringToUInt8Array(kids)))
+RUN(promise = mediaKeySession.generateRequest("keyids", encoder.encode(kids)))
 Promise resolved OK
 RUN(promise = mediaKeySession.update(new Uint8Array(0)))
 Promise rejected correctly OK
@@ -26,25 +26,101 @@
 Using a non-sanitizable response should reject.
 RUN(kids = JSON.stringify({ kids: [ "MTIzNDU=" ] }))
 RUN(mediaKeySession = mediaKeys.createSession("temporary"))
-RUN(promise = mediaKeySession.generateRequest("keyids", stringToUInt8Array(kids)))
+RUN(promise = mediaKeySession.generateRequest("keyids", encoder.encode(kids)))
 Promise resolved OK
-RUN(promise = mediaKeySession.update(stringToUInt8Array("invalid-response")))
+RUN(promise = mediaKeySession.update(encoder.encode("invalid-response")))
 Promise rejected correctly OK
 
 Using a sanitizable response should resolve.
 RUN(kids = JSON.stringify({ kids: [ "MTIzNDU=" ] }))
 RUN(mediaKeySession = mediaKeys.createSession("temporary"))
-RUN(promise = mediaKeySession.generateRequest("keyids", stringToUInt8Array(kids)))
+RUN(promise = mediaKeySession.generateRequest("keyids", encoder.encode(kids)))
 Promise resolved OK
-RUN(promise = mediaKeySession.update(stringToUInt8Array("valid-response")))
+RUN(promise = mediaKeySession.update(encoder.encode("valid-response")))
 Promise resolved OK
 
 Using a sanitizable response with invalid format should resolve.
 RUN(kids = JSON.stringify({ kids: [ "MTIzNDU=" ] }))
 RUN(mediaKeySession = mediaKeys.createSession("temporary"))
-RUN(promise = mediaKeySession.generateRequest("keyids", stringToUInt8Array(kids)))
+RUN(promise = mediaKeySession.generateRequest("keyids", encoder.encode(kids)))
 Promise resolved OK
-RUN(promise = mediaKeySession.update(stringToUInt8Array("valid-response invalid-format")))
+RUN(promise = mediaKeySession.update(encoder.encode("valid-response invalid-format")))
 Promise rejected correctly OK
+
+A valid MediaKeySession properly dispatches keystatuseschange event.
+RUN(kids = JSON.stringify({ kids: [ "MTIzNDU=", "Njc4OTA=" ] }))
+RUN(mediaKeySession = mediaKeys.createSession("temporary"))
+RUN(promise = mediaKeySession.generateRequest("keyids", encoder.encode(kids)))
+Promise resolved OK
+RUN(promise = mediaKeySession.update(encoder.encode("valid-response keys-changed")))
+EXPECTED (mediaKeySession.keyStatuses.size == '2') OK
+EXPECTED (mediaKeySession.keyStatuses.has(encoder.encode("12345")) == 'true') OK
+EXPECTED (mediaKeySession.keyStatuses.get(encoder.encode("12345")) == 'usable') OK
+EXPECTED (mediaKeySession.keyStatuses.has(encoder.encode("67890")) == 'true') OK
+EXPECTED (mediaKeySession.keyStatuses.get(encoder.encode("67890")) == 'usable') OK
+EXPECTED (mediaKeySession.keyStatuses.has(encoder.encode("invalid")) == 'false') OK
+EXPECTED (mediaKeySession.keyStatuses.get(encoder.encode("invalid")) == 'undefined') OK
+EXPECTED (iteratorArray.length == '2') OK
+EXPECTED (iteratorArray[0][0].length == '5') OK
+EXPECTED (iteratorArray[0][0][0] == '49') OK
+EXPECTED (iteratorArray[0][0][1] == '50') OK
+EXPECTED (iteratorArray[0][0][2] == '51') OK
+EXPECTED (iteratorArray[0][0][3] == '52') OK
+EXPECTED (iteratorArray[0][0][4] == '53') OK
+EXPECTED (iteratorArray[0][1] == 'usable') OK
+EXPECTED (iteratorArray[1][0].length == '5') OK
+EXPECTED (iteratorArray[1][0][0] == '54') OK
+EXPECTED (iteratorArray[1][0][1] == '55') OK
+EXPECTED (iteratorArray[1][0][2] == '56') OK
+EXPECTED (iteratorArray[1][0][3] == '57') OK
+EXPECTED (iteratorArray[1][0][4] == '48') OK
+EXPECTED (iteratorArray[1][1] == 'usable') OK
+EXPECTED (iteratorArray.length == '2') OK
+EXPECTED (iteratorArray[0].length == '5') OK
+EXPECTED (iteratorArray[0][0] == '49') OK
+EXPECTED (iteratorArray[0][1] == '50') OK
+EXPECTED (iteratorArray[0][2] == '51') OK
+EXPECTED (iteratorArray[0][3] == '52') OK
+EXPECTED (iteratorArray[0][4] == '53') OK
+EXPECTED (iteratorArray[1].length == '5') OK
+EXPECTED (iteratorArray[1][0] == '54') OK
+EXPECTED (iteratorArray[1][1] == '55') OK
+EXPECTED (iteratorArray[1][2] == '56') OK
+EXPECTED (iteratorArray[1][3] == '57') OK
+EXPECTED (iteratorArray[1][4] == '48') OK
+EXPECTED (iteratorArray.length == '2') OK
+EXPECTED (iteratorArray[0] == 'usable') OK
+EXPECTED (iteratorArray[1] == 'usable') OK
+EXPECTED (iteratorArray.length == '2') OK
+EXPECTED (iteratorArray[0].key.length == '5') OK
+EXPECTED (iteratorArray[0].key[0] == '49') OK
+EXPECTED (iteratorArray[0].key[1] == '50') OK
+EXPECTED (iteratorArray[0].key[2] == '51') OK
+EXPECTED (iteratorArray[0].key[3] == '52') OK
+EXPECTED (iteratorArray[0].key[4] == '53') OK
+EXPECTED (iteratorArray[0].value == 'usable') OK
+EXPECTED (iteratorArray[1].key.length == '5') OK
+EXPECTED (iteratorArray[1].key[0] == '54') OK
+EXPECTED (iteratorArray[1].key[1] == '55') OK
+EXPECTED (iteratorArray[1].key[2] == '56') OK
+EXPECTED (iteratorArray[1].key[3] == '57') OK
+EXPECTED (iteratorArray[1].key[4] == '48') OK
+EXPECTED (iteratorArray[1].value == 'usable') OK
+EXPECTED (iteratorArray.length == '2') OK
+EXPECTED (iteratorArray[0].key.length == '5') OK
+EXPECTED (iteratorArray[0].key[0] == '49') OK
+EXPECTED (iteratorArray[0].key[1] == '50') OK
+EXPECTED (iteratorArray[0].key[2] == '51') OK
+EXPECTED (iteratorArray[0].key[3] == '52') OK
+EXPECTED (iteratorArray[0].key[4] == '53') OK
+EXPECTED (iteratorArray[0].value == 'usable') OK
+EXPECTED (iteratorArray[1].key.length == '5') OK
+EXPECTED (iteratorArray[1].key[0] == '54') OK
+EXPECTED (iteratorArray[1].key[1] == '55') OK
+EXPECTED (iteratorArray[1].key[2] == '56') OK
+EXPECTED (iteratorArray[1].key[3] == '57') OK
+EXPECTED (iteratorArray[1].key[4] == '48') OK
+EXPECTED (iteratorArray[1].value == 'usable') OK
+Promise resolved OK
 END OF TEST
 

Modified: trunk/LayoutTests/media/encrypted-media/mock-MediaKeySession-update.html (212106 => 212107)


--- trunk/LayoutTests/media/encrypted-media/mock-MediaKeySession-update.html	2017-02-10 09:48:40 UTC (rev 212106)
+++ trunk/LayoutTests/media/encrypted-media/mock-MediaKeySession-update.html	2017-02-10 10:22:50 UTC (rev 212107)
@@ -10,6 +10,8 @@
     var mediaKeySession;
     var capabilities = {};
     var kids;
+    var iteratorArray;
+    var encoder = new TextEncoder();
 
     function doTest()
     {
@@ -49,14 +51,6 @@
         next();
     }
 
-    function stringToUInt8Array(str)
-    {
-       var array = new Uint8Array(str.length);
-       for (var i=0; i<str.length; i++)
-            array[i] = str.charCodeAt(i);
-       return array;
-    }
-
     tests = [
         function() {
             run('promise = mediaKeySystemAccess.createMediaKeys()');
@@ -67,7 +61,7 @@
             consoleWrite('Using a non-callable MediaKeySession should reject.');
             run('mediaKeySession = mediaKeys.createSession("temporary")');
             testExpected('typeof mediaKeySession', 'object');
-            run('promise = mediaKeySession.update(stringToUInt8Array("invalid-state"))');
+            run('promise = mediaKeySession.update(encoder.encode("invalid-state"))');
             shouldReject(promise).then(next, next);
         },
 
@@ -75,7 +69,7 @@
             consoleWrite('Using a zero-length response should reject.');
             run('kids = JSON.stringify({ kids: [ "MTIzNDU=" ] })');
             run('mediaKeySession = mediaKeys.createSession("temporary")');
-            run('promise = mediaKeySession.generateRequest("keyids", stringToUInt8Array(kids))');
+            run('promise = mediaKeySession.generateRequest("keyids", encoder.encode(kids))');
             shouldResolve(promise).then(function() {
                 run('promise = mediaKeySession.update(new Uint8Array(0))');
                 shouldReject(promise).then(next, next);
@@ -86,9 +80,9 @@
             consoleWrite('Using a non-sanitizable response should reject.');
             run('kids = JSON.stringify({ kids: [ "MTIzNDU=" ] })');
             run('mediaKeySession = mediaKeys.createSession("temporary")');
-            run('promise = mediaKeySession.generateRequest("keyids", stringToUInt8Array(kids))');
+            run('promise = mediaKeySession.generateRequest("keyids", encoder.encode(kids))');
             shouldResolve(promise).then(function() {
-                run('promise = mediaKeySession.update(stringToUInt8Array("invalid-response"))');
+                run('promise = mediaKeySession.update(encoder.encode("invalid-response"))');
                 shouldReject(promise).then(next, next);
             }, next);
         },
@@ -97,9 +91,9 @@
             consoleWrite('Using a sanitizable response should resolve.');
             run('kids = JSON.stringify({ kids: [ "MTIzNDU=" ] })');
             run('mediaKeySession = mediaKeys.createSession("temporary")');
-            run('promise = mediaKeySession.generateRequest("keyids", stringToUInt8Array(kids))');
+            run('promise = mediaKeySession.generateRequest("keyids", encoder.encode(kids))');
             shouldResolve(promise).then(function() {
-                run('promise = mediaKeySession.update(stringToUInt8Array("valid-response"))');
+                run('promise = mediaKeySession.update(encoder.encode("valid-response"))');
                 shouldResolve(promise).then(next, next);
             }, next);
         },
@@ -108,12 +102,73 @@
             consoleWrite('Using a sanitizable response with invalid format should resolve.');
             run('kids = JSON.stringify({ kids: [ "MTIzNDU=" ] })');
             run('mediaKeySession = mediaKeys.createSession("temporary")');
-            run('promise = mediaKeySession.generateRequest("keyids", stringToUInt8Array(kids))');
+            run('promise = mediaKeySession.generateRequest("keyids", encoder.encode(kids))');
             shouldResolve(promise).then(function() {
-                run('promise = mediaKeySession.update(stringToUInt8Array("valid-response invalid-format"))');
+                run('promise = mediaKeySession.update(encoder.encode("valid-response invalid-format"))');
                 shouldReject(promise).then(next, next);
             }, next);
         },
+
+        function() {
+            consoleWrite('A valid MediaKeySession properly dispatches keystatuseschange event.');
+            run('kids = JSON.stringify({ kids: [ "MTIzNDU=", "Njc4OTA=" ] })');
+            run('mediaKeySession = mediaKeys.createSession("temporary")');
+            run('promise = mediaKeySession.generateRequest("keyids", encoder.encode(kids))');
+            shouldResolve(promise).then(function() {
+                run('promise = mediaKeySession.update(encoder.encode("valid-response keys-changed"))');
+                mediaKeySession.addEventListener('keystatuseschange', function(event) {
+                    testExpected('mediaKeySession.keyStatuses.size', 2);
+                    testExpected('mediaKeySession.keyStatuses.has(encoder.encode("12345"))', true);
+                    testExpected('mediaKeySession.keyStatuses.get(encoder.encode("12345"))', 'usable');
+                    testExpected('mediaKeySession.keyStatuses.has(encoder.encode("67890"))', true);
+                    testExpected('mediaKeySession.keyStatuses.get(encoder.encode("67890"))', 'usable');
+                    testExpected('mediaKeySession.keyStatuses.has(encoder.encode("invalid"))', false);
+                    testExpected('mediaKeySession.keyStatuses.get(encoder.encode("invalid"))', undefined);
+
+                    iteratorArray = [];
+                    for (var pair of mediaKeySession.keyStatuses)
+                        iteratorArray.push([ new Uint8Array(pair[0]), pair[1] ]);
+                    testExpected('iteratorArray.length', 2);
+                    testArraysEqual('iteratorArray[0][0]', encoder.encode('12345'));
+                    testExpected('iteratorArray[0][1]', 'usable');
+                    testArraysEqual('iteratorArray[1][0]', encoder.encode('67890'));
+                    testExpected('iteratorArray[1][1]', 'usable');
+
+                    iteratorArray = [];
+                    for (var key of mediaKeySession.keyStatuses.keys())
+                        iteratorArray.push(new Uint8Array(key));
+                    testExpected('iteratorArray.length', 2);
+                    testArraysEqual('iteratorArray[0]', encoder.encode('12345'));
+                    testArraysEqual('iteratorArray[1]', encoder.encode('67890'));
+
+                    iteratorArray = [];
+                    for (var value of mediaKeySession.keyStatuses.values())
+                        iteratorArray.push(value);
+                    testArraysEqual('iteratorArray', [ 'usable', 'usable' ]);
+
+                    iteratorArray = [];
+                    for (var entry of mediaKeySession.keyStatuses.entries())
+                        iteratorArray.push({ key: new Uint8Array(entry[0]), value: entry[1] });
+                    testExpected('iteratorArray.length', 2);
+                    testArraysEqual('iteratorArray[0].key', encoder.encode('12345'));
+                    testExpected('iteratorArray[0].value', 'usable');
+                    testArraysEqual('iteratorArray[1].key', encoder.encode('67890'));
+                    testExpected('iteratorArray[1].value', 'usable');
+
+                    iteratorArray = [];
+                    mediaKeySession.keyStatuses.forEach(function(value, key) {
+                        iteratorArray.push({ key: new Uint8Array(key), value: value });
+                    });
+                    testExpected('iteratorArray.length', 2);
+                    testArraysEqual('iteratorArray[0].key', encoder.encode('12345'));
+                    testExpected('iteratorArray[0].value', 'usable');
+                    testArraysEqual('iteratorArray[1].key', encoder.encode('67890'));
+                    testExpected('iteratorArray[1].value', 'usable');
+
+                    shouldResolve(promise).then(next, next);
+                }, false);
+            }, next);
+        },
     ];
     </script>
 </head>

Modified: trunk/Source/WebCore/ChangeLog (212106 => 212107)


--- trunk/Source/WebCore/ChangeLog	2017-02-10 09:48:40 UTC (rev 212106)
+++ trunk/Source/WebCore/ChangeLog	2017-02-10 10:22:50 UTC (rev 212107)
@@ -1,3 +1,69 @@
+2017-02-10  Zan Dobersek  <zdober...@igalia.com>
+
+        [EME] Implement MediaKeySession::updateKeyStatuses(), MediaKeyStatusMap
+        https://bugs.webkit.org/show_bug.cgi?id=167888
+
+        Reviewed by Xabier Rodriguez-Calvar.
+
+        Implement MediaKeySession::updateKeyStatuses(), transforming the passed-in
+        KeyStatusVector into a Vector mapping the key IDs to MediaKeyStatus values.
+        A keystatuseschange event is fired on the MediaKeySession object afterwards.
+        The queueing of the task that runs the 'attemp to resume playback' on the
+        related HTMLMediaElement objects isn't done yet since that algorithm isn't
+        implemented yet.
+
+        The statuses Vector is stored on the MediaKeySession object. That Vector is
+        then exposed through the MediaKeyStatusMap object, each such object being
+        unique to one MediaKeySession object. The implementation of MediaKeyStatusMap
+        thus keeps a reference to the session object as long as that object is alive,
+        and queries the MediaKeySession::statuses() getter to access the Vector that
+        contains status information for all the key IDs.
+
+        MediaKeyStatusMap::Iterator object keeps a reference to the MediaKeyStatusMap
+        object and accesses the statuses by indexing into the status Vector of the
+        related MediaKeySession object.
+
+        CDMInstance::updateLicense() now accepts the session ID string as the first
+        argument, making it possible to specify which session should be updated.
+
+        MockCDMFactory::keysForSessionWithID() returns an optional reference to the
+        Vector value in the session map that lists all the key IDs that are being
+        stored for that session.
+
+        MockCDMInstance::updateLicense() now detects the 'keys-changed' entry in the
+        passed-in response data, and upon detecting that constructs a KeyStatusVector
+        object containing all the keys for that session. KeyStatus::Usable is returned
+        for each object at the moment, but this should be adjustable in the future
+        through additional parameters passed through the response data. The Vector
+        object is then passed to the callback and is then passed to the 'update key
+        statuses' algorithm in MediaKeySession.
+
+        Covered by a test case in media/encrypted-media/mock-MediaKeySession-update.html.
+
+        * Modules/encryptedmedia/CDMInstance.h:
+        * Modules/encryptedmedia/MediaKeySession.cpp:
+        (WebCore::MediaKeySession::MediaKeySession):
+        (WebCore::MediaKeySession::~MediaKeySession):
+        (WebCore::MediaKeySession::update):
+        (WebCore::MediaKeySession::updateKeyStatuses):
+        * Modules/encryptedmedia/MediaKeySession.h:
+        * Modules/encryptedmedia/MediaKeyStatusMap.cpp:
+        (WebCore::MediaKeyStatusMap::MediaKeyStatusMap):
+        (WebCore::MediaKeyStatusMap::detachSession):
+        (WebCore::MediaKeyStatusMap::size):
+        (WebCore::keyIdsMatch):
+        (WebCore::MediaKeyStatusMap::has):
+        (WebCore::MediaKeyStatusMap::get):
+        (WebCore::MediaKeyStatusMap::Iterator::Iterator):
+        (WebCore::MediaKeyStatusMap::Iterator::next):
+        * Modules/encryptedmedia/MediaKeyStatusMap.h:
+        (WebCore::MediaKeyStatusMap::create):
+        * Modules/encryptedmedia/MediaKeyStatusMap.idl:
+        * testing/MockCDMFactory.cpp:
+        (WebCore::MockCDMFactory::keysForSessionWithID):
+        (WebCore::MockCDMInstance::updateLicense):
+        * testing/MockCDMFactory.h:
+
 2017-02-09  Simon Fraser  <simon.fra...@apple.com>
 
         Improve IOSurfacePool logging

Modified: trunk/Source/WebCore/Modules/encryptedmedia/CDMInstance.h (212106 => 212107)


--- trunk/Source/WebCore/Modules/encryptedmedia/CDMInstance.h	2017-02-10 09:48:40 UTC (rev 212106)
+++ trunk/Source/WebCore/Modules/encryptedmedia/CDMInstance.h	2017-02-10 10:22:50 UTC (rev 212107)
@@ -65,7 +65,7 @@
     using KeyStatusVector = Vector<std::pair<Ref<SharedBuffer>, KeyStatus>>;
     using Message = std::pair<MessageType, Ref<SharedBuffer>>;
     using LicenseUpdateCallback = Function<void(bool sessionWasClosed, std::optional<KeyStatusVector>&& changedKeys, std::optional<double>&& changedExpiration, std::optional<Message>&& message, SuccessValue succeeded)>;
-    virtual void updateLicense(LicenseType, const SharedBuffer& response, LicenseUpdateCallback) = 0;
+    virtual void updateLicense(const String& sessionId, LicenseType, const SharedBuffer& response, LicenseUpdateCallback) = 0;
 
     using CloseSessionCallback = Function<void()>;
     virtual void closeSession(const String& sessionId, CloseSessionCallback) = 0;

Modified: trunk/Source/WebCore/Modules/encryptedmedia/MediaKeySession.cpp (212106 => 212107)


--- trunk/Source/WebCore/Modules/encryptedmedia/MediaKeySession.cpp	2017-02-10 09:48:40 UTC (rev 212106)
+++ trunk/Source/WebCore/Modules/encryptedmedia/MediaKeySession.cpp	2017-02-10 10:22:50 UTC (rev 212107)
@@ -53,7 +53,7 @@
 MediaKeySession::MediaKeySession(ScriptExecutionContext& context, MediaKeySessionType sessionType, bool useDistinctiveIdentifier, Ref<CDM>&& implementation, Ref<CDMInstance>&& instance)
     : ActiveDOMObject(&context)
     , m_expiration(std::numeric_limits<double>::quiet_NaN())
-    , m_keyStatuses(MediaKeyStatusMap::create())
+    , m_keyStatuses(MediaKeyStatusMap::create(*this))
     , m_useDistinctiveIdentifier(useDistinctiveIdentifier)
     , m_sessionType(sessionType)
     , m_implementation(WTFMove(implementation))
@@ -84,7 +84,10 @@
     UNUSED_PARAM(m_uninitialized);
 }
 
-MediaKeySession::~MediaKeySession() = default;
+MediaKeySession::~MediaKeySession()
+{
+    m_keyStatuses->detachSession();
+}
 
 const String& MediaKeySession::sessionId() const
 {
@@ -270,7 +273,7 @@
         // 6.5. Let session closed be false.
         // 6.6. Let cdm be the CDM instance represented by this object's cdm instance value.
         // 6.7. Use the cdm to execute the following steps:
-        m_instance->updateLicense(m_sessionType, *sanitizedResponse, [this, weakThis = m_weakPtrFactory.createWeakPtr(), promise = WTFMove(promise)] (bool sessionWasClosed, std::optional<CDMInstance::KeyStatusVector>&& changedKeys, std::optional<double>&& changedExpiration, std::optional<CDMInstance::Message>&& message, CDMInstance::SuccessValue succeeded) mutable {
+        m_instance->updateLicense(m_sessionId, m_sessionType, *sanitizedResponse, [this, weakThis = m_weakPtrFactory.createWeakPtr(), promise = WTFMove(promise)] (bool sessionWasClosed, std::optional<CDMInstance::KeyStatusVector>&& changedKeys, std::optional<double>&& changedExpiration, std::optional<CDMInstance::Message>&& message, CDMInstance::SuccessValue succeeded) mutable {
             if (!weakThis)
                 return;
 
@@ -481,9 +484,49 @@
     m_eventQueue.enqueueEvent(WTFMove(messageEvent));
 }
 
-void MediaKeySession::updateKeyStatuses(CDMInstance::KeyStatusVector&&)
+void MediaKeySession::updateKeyStatuses(CDMInstance::KeyStatusVector&& inputStatuses)
 {
-    notImplemented();
+    // https://w3c.github.io/encrypted-media/#update-key-statuses
+    // W3C Editor's Draft 09 November 2016
+
+    // 1. Let the session be the associated MediaKeySession object.
+    // 2. Let the input statuses be the sequence of pairs key ID and associated MediaKeyStatus pairs.
+    // 3. Let the statuses be session's keyStatuses attribute.
+    // 4. Run the following steps to replace the contents of statuses:
+    //   4.1. Empty statuses.
+    //   4.2. For each pair in input statuses.
+    //     4.2.1. Let pair be the pair.
+    //     4.2.2. Insert an entry for pair's key ID into statuses with the value of pair's MediaKeyStatus value.
+
+    static auto toMediaKeyStatus = [] (CDMInstance::KeyStatus status) -> MediaKeyStatus {
+        switch (status) {
+        case CDMInstance::KeyStatus::Usable:
+            return MediaKeyStatus::Usable;
+        case CDMInstance::KeyStatus::Expired:
+            return MediaKeyStatus::Expired;
+        case CDMInstance::KeyStatus::Released:
+            return MediaKeyStatus::Released;
+        case CDMInstance::KeyStatus::OutputRestricted:
+            return MediaKeyStatus::OutputRestricted;
+        case CDMInstance::KeyStatus::OutputDownscaled:
+            return MediaKeyStatus::OutputDownscaled;
+        case CDMInstance::KeyStatus::StatusPending:
+            return MediaKeyStatus::StatusPending;
+        case CDMInstance::KeyStatus::InternalError:
+            return MediaKeyStatus::InternalError;
+        };
+    };
+
+    m_statuses.clear();
+    m_statuses.reserveCapacity(inputStatuses.size());
+    for (auto& status : inputStatuses)
+        m_statuses.uncheckedAppend({ WTFMove(status.first), toMediaKeyStatus(status.second) });
+
+    // 5. Queue a task to fire a simple event named keystatuseschange at the session.
+    m_eventQueue.enqueueEvent(Event::create(eventNames().keystatuseschangeEvent, false, false));
+
+    // 6. Queue a task to run the Attempt to Resume Playback If Necessary algorithm on each of the media element(s) whose mediaKeys attribute is the MediaKeys object that created the session.
+    // FIXME: Implement.
 }
 
 void MediaKeySession::updateExpiration(double)

Modified: trunk/Source/WebCore/Modules/encryptedmedia/MediaKeySession.h (212106 => 212107)


--- trunk/Source/WebCore/Modules/encryptedmedia/MediaKeySession.h	2017-02-10 09:48:40 UTC (rev 212106)
+++ trunk/Source/WebCore/Modules/encryptedmedia/MediaKeySession.h	2017-02-10 10:22:50 UTC (rev 212107)
@@ -38,6 +38,7 @@
 #include "JSDOMPromise.h"
 #include "MediaKeyMessageType.h"
 #include "MediaKeySessionType.h"
+#include "MediaKeyStatus.h"
 #include <wtf/RefCounted.h>
 #include <wtf/Vector.h>
 #include <wtf/WeakPtr.h>
@@ -69,6 +70,8 @@
     void close(Ref<DeferredPromise>&&);
     void remove(Ref<DeferredPromise>&&);
 
+    const Vector<std::pair<Ref<SharedBuffer>, MediaKeyStatus>>& statuses() const { return m_statuses; }
+
 private:
     MediaKeySession(ScriptExecutionContext&, MediaKeySessionType, bool useDistinctiveIdentifier, Ref<CDM>&&, Ref<CDMInstance>&&);
     void enqueueMessage(MediaKeyMessageType, const SharedBuffer&);
@@ -103,6 +106,7 @@
     Vector<Ref<SharedBuffer>> m_recordOfKeyUsage;
     double m_firstDecryptTime { 0 };
     double m_latestDecryptTime { 0 };
+    Vector<std::pair<Ref<SharedBuffer>, MediaKeyStatus>> m_statuses;
     WeakPtrFactory<MediaKeySession> m_weakPtrFactory;
 };
 

Modified: trunk/Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.cpp (212106 => 212107)


--- trunk/Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.cpp	2017-02-10 09:48:40 UTC (rev 212106)
+++ trunk/Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.cpp	2017-02-10 10:22:50 UTC (rev 212107)
@@ -31,39 +31,80 @@
 
 #if ENABLE(ENCRYPTED_MEDIA)
 
-#include "NotImplemented.h"
+#include "JSMediaKeyStatusMap.h"
+#include "MediaKeySession.h"
+#include "SharedBuffer.h"
 
 namespace WebCore {
 
-MediaKeyStatusMap::MediaKeyStatusMap() = default;
+MediaKeyStatusMap::MediaKeyStatusMap(const MediaKeySession& session)
+    : m_session(&session)
+{
+}
 
 MediaKeyStatusMap::~MediaKeyStatusMap() = default;
 
+void MediaKeyStatusMap::detachSession()
+{
+    m_session = nullptr;
+}
+
 unsigned long MediaKeyStatusMap::size()
 {
-    notImplemented();
-    return 0;
+    if (!m_session)
+        return 0;
+    return m_session->statuses().size();
 }
 
-bool MediaKeyStatusMap::has(const BufferSource&)
+static bool keyIdsMatch(const SharedBuffer& a, const BufferSource& b)
 {
-    notImplemented();
-    return false;
+    auto length = a.size();
+    if (!length || length != b.length())
+        return false;
+    return !std::memcmp(a.data(), b.data(), length);
 }
 
-JSC::JSValue MediaKeyStatusMap::get(const BufferSource&)
+bool MediaKeyStatusMap::has(const BufferSource& keyId)
 {
-    notImplemented();
-    return JSC::jsUndefined();
+    if (!m_session)
+        return false;
+
+    auto& statuses = m_session->statuses();
+    return std::any_of(statuses.begin(), statuses.end(),
+        [&keyId] (auto& it) { return keyIdsMatch(it.first, keyId); });
 }
 
-MediaKeyStatusMap::Iterator::Iterator(MediaKeyStatusMap&)
+JSC::JSValue MediaKeyStatusMap::get(JSC::ExecState& state, const BufferSource& keyId)
 {
+    if (!m_session)
+        return JSC::jsUndefined();
+
+    auto& statuses = m_session->statuses();
+    auto it = std::find_if(statuses.begin(), statuses.end(),
+        [&keyId] (auto& it) { return keyIdsMatch(it.first, keyId); });
+
+    if (it == statuses.end())
+        return JSC::jsUndefined();
+    return convertEnumerationToJS(state, it->second);
 }
 
+MediaKeyStatusMap::Iterator::Iterator(MediaKeyStatusMap& map)
+    : m_map(map)
+{
+}
+
 std::optional<WTF::KeyValuePair<BufferSource::VariantType, MediaKeyStatus>> MediaKeyStatusMap::Iterator::next()
 {
-    return std::nullopt;
+    if (!m_map->m_session)
+        return std::nullopt;
+
+    auto& statuses = m_map->m_session->statuses();
+    if (m_index >= statuses.size())
+        return std::nullopt;
+
+    auto& pair = statuses[m_index++];
+    auto buffer = ArrayBuffer::create(pair.first->data(), pair.first->size());
+    return WTF::KeyValuePair<BufferSource::VariantType, MediaKeyStatus> { RefPtr<ArrayBuffer>(WTFMove(buffer)), pair.second };
 }
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.h (212106 => 212107)


--- trunk/Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.h	2017-02-10 09:48:40 UTC (rev 212106)
+++ trunk/Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.h	2017-02-10 10:22:50 UTC (rev 212107)
@@ -33,7 +33,6 @@
 #include "BufferSource.h"
 #include "MediaKeyStatus.h"
 #include <runtime/JSCJSValueInlines.h>
-#include <wtf/HashTraits.h>
 #include <wtf/Optional.h>
 #include <wtf/Ref.h>
 #include <wtf/RefCounted.h>
@@ -40,30 +39,41 @@
 
 namespace WebCore {
 
+class MediaKeySession;
+class SharedBuffer;
+
 class MediaKeyStatusMap : public RefCounted<MediaKeyStatusMap> {
 public:
     using Status = MediaKeyStatus;
 
-    static Ref<MediaKeyStatusMap> create()
+    static Ref<MediaKeyStatusMap> create(const MediaKeySession& session)
     {
-        return adoptRef(*new MediaKeyStatusMap);
+        return adoptRef(*new MediaKeyStatusMap(session));
     }
 
     virtual ~MediaKeyStatusMap();
 
+    void detachSession();
+
     unsigned long size();
     bool has(const BufferSource&);
-    JSC::JSValue get(const BufferSource&);
+    JSC::JSValue get(JSC::ExecState&, const BufferSource&);
 
     class Iterator {
     public:
         explicit Iterator(MediaKeyStatusMap&);
         std::optional<WTF::KeyValuePair<BufferSource::VariantType, MediaKeyStatus>> next();
+
+    private:
+        Ref<MediaKeyStatusMap> m_map;
+        size_t m_index { 0 };
     };
     Iterator createIterator() { return Iterator(*this); }
 
 private:
-    MediaKeyStatusMap();
+    MediaKeyStatusMap(const MediaKeySession&);
+
+    const MediaKeySession* m_session;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.idl (212106 => 212107)


--- trunk/Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.idl	2017-02-10 09:48:40 UTC (rev 212106)
+++ trunk/Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.idl	2017-02-10 10:22:50 UTC (rev 212107)
@@ -43,5 +43,5 @@
     iterable<BufferSource, MediaKeyStatus>;
     readonly attribute unsigned long size;
     boolean has(BufferSource keyId);
-    any get(BufferSource keyId);
+    [CallWith=ScriptState] any get(BufferSource keyId);
 };

Modified: trunk/Source/WebCore/testing/MockCDMFactory.cpp (212106 => 212107)


--- trunk/Source/WebCore/testing/MockCDMFactory.cpp	2017-02-10 09:48:40 UTC (rev 212106)
+++ trunk/Source/WebCore/testing/MockCDMFactory.cpp	2017-02-10 10:22:50 UTC (rev 212107)
@@ -81,6 +81,14 @@
     return WTFMove(it->value);
 }
 
+std::optional<const Vector<Ref<SharedBuffer>>&> MockCDMFactory::keysForSessionWithID(const String& id) const
+{
+    auto it = m_sessions.find(id);
+    if (it == m_sessions.end())
+        return std::nullopt;
+    return it->value;
+}
+
 void MockCDMFactory::setSupportedDataTypes(Vector<String>&& types)
 {
     m_supportedDataTypes.clear();
@@ -276,7 +284,7 @@
     callback(SharedBuffer::create(license.data(), license.length()), sessionID, false, SuccessValue::Succeeded);
 }
 
-void MockCDMInstance::updateLicense(LicenseType, const SharedBuffer& response, LicenseUpdateCallback callback)
+void MockCDMInstance::updateLicense(const String& sessionID, LicenseType, const SharedBuffer& response, LicenseUpdateCallback callback)
 {
     MockCDMFactory* factory = m_cdm ? m_cdm->factory() : nullptr;
     if (!factory) {
@@ -292,10 +300,23 @@
         return;
     }
 
-    // FIXME: Session closure, key status, expiration and message handling should be implemented
+    std::optional<KeyStatusVector> changedKeys;
+    if (responseVector.contains(String(ASCIILiteral("keys-changed")))) {
+        std::optional<const Vector<Ref<SharedBuffer>>&> keys = factory->keysForSessionWithID(sessionID);
+        if (keys) {
+            KeyStatusVector keyStatusVector;
+            keyStatusVector.reserveInitialCapacity(keys->size());
+            for (auto& key : *keys)
+                keyStatusVector.uncheckedAppend({ key.copyRef(), KeyStatus::Usable });
+
+            changedKeys = WTFMove(keyStatusVector);
+        }
+    }
+
+    // FIXME: Session closure, expiration and message handling should be implemented
     // once the relevant algorithms are supported.
 
-    callback(false, std::nullopt, std::nullopt, std::nullopt, SuccessValue::Succeeded);
+    callback(false, WTFMove(changedKeys), std::nullopt, std::nullopt, SuccessValue::Succeeded);
 }
 
 void MockCDMInstance::closeSession(const String& sessionID, CloseSessionCallback callback)

Modified: trunk/Source/WebCore/testing/MockCDMFactory.h (212106 => 212107)


--- trunk/Source/WebCore/testing/MockCDMFactory.h	2017-02-10 09:48:40 UTC (rev 212106)
+++ trunk/Source/WebCore/testing/MockCDMFactory.h	2017-02-10 10:22:50 UTC (rev 212107)
@@ -72,6 +72,7 @@
     bool hasSessionWithID(const String& id) { return m_sessions.contains(id); }
     void removeSessionWithID(const String& id) { m_sessions.remove(id); }
     void addKeysToSessionWithID(const String& id, Vector<Ref<SharedBuffer>>&&);
+    std::optional<const Vector<Ref<SharedBuffer>>&> keysForSessionWithID(const String& id) const;
     Vector<Ref<SharedBuffer>> removeKeysFromSessionWithID(const String& id);
 
 private:
@@ -130,7 +131,7 @@
     SuccessValue setPersistentStateAllowed(bool) final;
     SuccessValue setServerCertificate(Ref<SharedBuffer>&&) final;
     void requestLicense(LicenseType, const AtomicString& initDataType, Ref<SharedBuffer>&& initData, LicenseCallback) final;
-    void updateLicense(LicenseType, const SharedBuffer&, LicenseUpdateCallback) final;
+    void updateLicense(const String&, LicenseType, const SharedBuffer&, LicenseUpdateCallback) final;
     void closeSession(const String&, CloseSessionCallback) final;
     void removeSessionData(const String&, LicenseType, RemoveSessionDataCallback) final;
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to