[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:


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@212107 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/Modules/encryptedmedia/MediaKeySession.cpp b/Source/WebCore/Modules/encryptedmedia/MediaKeySession.cpp
index 8e0b7ef..e17165d 100644
--- a/Source/WebCore/Modules/encryptedmedia/MediaKeySession.cpp
+++ b/Source/WebCore/Modules/encryptedmedia/MediaKeySession.cpp
@@ -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)