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;