[EME] Add basic implementation of HTMLMediaElement::setMediaKeys()
authorzandobersek@gmail.com <zandobersek@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 21 Aug 2017 06:21:38 +0000 (06:21 +0000)
committerzandobersek@gmail.com <zandobersek@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 21 Aug 2017 06:21:38 +0000 (06:21 +0000)
https://bugs.webkit.org/show_bug.cgi?id=175717

Reviewed by Xabier Rodriguez-Calvar.

Source/WebCore:

Add an initial and incomplete implementation of HTMLMediaElement::setMediaKeys(),
interleaved with the specification wording of how this operation should behave.
The implementation still doesn't cover cases of CDM instances being already
associated with a different HTMLMediaElement, of CDM instances that can't be
disassociated from the current HTMLMediaElement, and of failures during both
association and disassociation of MediaKeys with the HTMLMediaElement.

The HTMLMediaElement (as a CDMClient inheritor) has to be attached or detached
from the MediaKeys object as appropriate. This attachment allows MediaKeys to
initiate an attempt to resume playback whenever the key statuses of the
associated MediaKeys object are updated.

Upon association and disassociation with MediaKeys, the CDMInstance object of
that specific MediaKeys instance is attached to or detached from the MediaPlayer
instance. This allows the platform layer to gather information about the
CDMInstance that will be used for decryption of media content for this specific
media element.

Additionally, the detachment from both MediaKeys and MediaPlayer is done upon
HTMLMediaElement destruction.

Upon setting the MediaKeys object, a task is queued that launches the 'Attempt to
Resume Playback If Necessary' algorithm. A placeholder method is added that will
implement the algorithm in the future.

The HTMLMediaElement::mediaKeys() getter is also implemented, returning pointer
held in m_mediaKeys.

Covered to a degree by existing imported W3C tests, with a setMediaKeys()-oriented
test having WPE-specific baseline update.

* Modules/encryptedmedia/MediaKeys.h:
(WebCore::MediaKeys::cdmInstance const):
* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::~HTMLMediaElement):
(WebCore::HTMLMediaElement::mediaKeys const):
(WebCore::HTMLMediaElement::setMediaKeys):
(WebCore::HTMLMediaElement::attemptToResumePlaybackIfNecessary):
(WebCore::HTMLMediaElement::contextDestroyed):
* html/HTMLMediaElement.h:
* platform/graphics/MediaPlayer.cpp:
(WebCore::MediaPlayer::cdmInstanceAttached):
(WebCore::MediaPlayer::cdmInstanceDetached):
* platform/graphics/MediaPlayer.h:
* platform/graphics/MediaPlayerPrivate.h:
(WebCore::MediaPlayerPrivateInterface::cdmInstanceAttached):
(WebCore::MediaPlayerPrivateInterface::cdmInstanceDetached):

LayoutTests:

* platform/wpe/imported/w3c/web-platform-tests/encrypted-media/clearkey-mp4-setmediakeys-expected.txt:
Update the baseline, with the test no longer timing out but instead
failing with a NotAllowed exception thrown.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@220959 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/platform/wpe/imported/w3c/web-platform-tests/encrypted-media/clearkey-mp4-setmediakeys-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/Modules/encryptedmedia/MediaKeys.h
Source/WebCore/html/HTMLMediaElement.cpp
Source/WebCore/html/HTMLMediaElement.h
Source/WebCore/platform/graphics/MediaPlayer.cpp
Source/WebCore/platform/graphics/MediaPlayer.h
Source/WebCore/platform/graphics/MediaPlayerPrivate.h

index 2b6c60b0cb41d3a332ff82338fb932edb06d2cdf..f5a62d39c3bf0e68fd71d315b5d6b13335699e39 100644 (file)
@@ -1,3 +1,14 @@
+2017-08-20  Zan Dobersek  <zdobersek@igalia.com>
+
+        [EME] Add basic implementation of HTMLMediaElement::setMediaKeys()
+        https://bugs.webkit.org/show_bug.cgi?id=175717
+
+        Reviewed by Xabier Rodriguez-Calvar.
+
+        * platform/wpe/imported/w3c/web-platform-tests/encrypted-media/clearkey-mp4-setmediakeys-expected.txt:
+        Update the baseline, with the test no longer timing out but instead
+        failing with a NotAllowed exception thrown.
+
 2017-08-19  Andy Estes  <aestes@apple.com>
 
         [Payment Request] Add interface stubs
index a1118d46f4befa963b29874436ce3373eb371dfe..523d81da9ae2e454613f3e1a72d7d2150598593b 100644 (file)
@@ -1,5 +1,3 @@
 
-Harness Error (TIMEOUT), message = null
-
-TIMEOUT org.w3.clearkey, setMediaKeys Test timed out
+FAIL org.w3.clearkey, setMediaKeys assert_unreached: NotAllowedError: The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission. Reached unreachable code
 
index 856bbf888b9937dc2f0063ce1a748424b4661227..af07178fd14cab558470c502c39669bd69df45e8 100644 (file)
@@ -1,3 +1,58 @@
+2017-08-20  Zan Dobersek  <zdobersek@igalia.com>
+
+        [EME] Add basic implementation of HTMLMediaElement::setMediaKeys()
+        https://bugs.webkit.org/show_bug.cgi?id=175717
+
+        Reviewed by Xabier Rodriguez-Calvar.
+
+        Add an initial and incomplete implementation of HTMLMediaElement::setMediaKeys(),
+        interleaved with the specification wording of how this operation should behave.
+        The implementation still doesn't cover cases of CDM instances being already
+        associated with a different HTMLMediaElement, of CDM instances that can't be
+        disassociated from the current HTMLMediaElement, and of failures during both
+        association and disassociation of MediaKeys with the HTMLMediaElement.
+
+        The HTMLMediaElement (as a CDMClient inheritor) has to be attached or detached
+        from the MediaKeys object as appropriate. This attachment allows MediaKeys to
+        initiate an attempt to resume playback whenever the key statuses of the
+        associated MediaKeys object are updated.
+
+        Upon association and disassociation with MediaKeys, the CDMInstance object of
+        that specific MediaKeys instance is attached to or detached from the MediaPlayer
+        instance. This allows the platform layer to gather information about the
+        CDMInstance that will be used for decryption of media content for this specific
+        media element.
+
+        Additionally, the detachment from both MediaKeys and MediaPlayer is done upon
+        HTMLMediaElement destruction.
+
+        Upon setting the MediaKeys object, a task is queued that launches the 'Attempt to
+        Resume Playback If Necessary' algorithm. A placeholder method is added that will
+        implement the algorithm in the future.
+
+        The HTMLMediaElement::mediaKeys() getter is also implemented, returning pointer
+        held in m_mediaKeys.
+
+        Covered to a degree by existing imported W3C tests, with a setMediaKeys()-oriented
+        test having WPE-specific baseline update.
+
+        * Modules/encryptedmedia/MediaKeys.h:
+        (WebCore::MediaKeys::cdmInstance const):
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::~HTMLMediaElement):
+        (WebCore::HTMLMediaElement::mediaKeys const):
+        (WebCore::HTMLMediaElement::setMediaKeys):
+        (WebCore::HTMLMediaElement::attemptToResumePlaybackIfNecessary):
+        (WebCore::HTMLMediaElement::contextDestroyed):
+        * html/HTMLMediaElement.h:
+        * platform/graphics/MediaPlayer.cpp:
+        (WebCore::MediaPlayer::cdmInstanceAttached):
+        (WebCore::MediaPlayer::cdmInstanceDetached):
+        * platform/graphics/MediaPlayer.h:
+        * platform/graphics/MediaPlayerPrivate.h:
+        (WebCore::MediaPlayerPrivateInterface::cdmInstanceAttached):
+        (WebCore::MediaPlayerPrivateInterface::cdmInstanceDetached):
+
 2017-08-20  Chris Dumez  <cdumez@apple.com>
 
         Simplify calls to LoaderStrategy::startPingLoad()
index 2e95a531df7e1401dea2a6391fc396c7e58b2122..2c9c103525ed61d5afe74137e9e041df31540780 100644 (file)
@@ -63,6 +63,8 @@ public:
     void detachCDMClient(CDMClient&);
     void attemptToResumePlaybackOnClients();
 
+    const CDMInstance& cdmInstance() const { return m_instance; }
+
 protected:
     MediaKeys(bool useDistinctiveIdentifier, bool persistentStateAllowed, const Vector<MediaKeySessionType>&, Ref<CDM>&&, Ref<CDMInstance>&&);
 
index 85d6cdb78563ddcaf8ea7252179ea8776c2fd0b7..22c8bf78df4347a0f256d3747d177dcaa096cf21 100644 (file)
 #include "WebKitMediaKeys.h"
 #endif
 
+#if ENABLE(ENCRYPTED_MEDIA)
+#include "MediaKeys.h"
+#endif
+
 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
 #include "JSMediaControlsHost.h"
 #include "MediaControlsHost.h"
@@ -563,6 +567,14 @@ HTMLMediaElement::~HTMLMediaElement()
     webkitSetMediaKeys(nullptr);
 #endif
 
+#if ENABLE(ENCRYPTED_MEDIA)
+    if (m_mediaKeys) {
+        m_mediaKeys->detachCDMClient(*this);
+        if (m_player)
+            m_player->cdmInstanceDetached(m_mediaKeys->cdmInstance());
+    }
+#endif
+
 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
     if (m_isolatedWorld)
         m_isolatedWorld->clearWrappers();
@@ -583,6 +595,9 @@ HTMLMediaElement::~HTMLMediaElement()
     m_updatePlaybackControlsManagerQueue.close();
     m_playbackControlsManagerBehaviorRestrictionsQueue.close();
     m_resourceSelectionTaskQueue.close();
+#if ENABLE(ENCRYPTED_MEDIA)
+    m_encryptedMediaQueue.close();
+#endif
 
     m_completelyLoaded = true;
 
@@ -2557,11 +2572,87 @@ void HTMLMediaElement::keyAdded()
 
 MediaKeys* HTMLMediaElement::mediaKeys() const
 {
-    return nullptr;
+    return m_mediaKeys.get();
+}
+
+void HTMLMediaElement::setMediaKeys(MediaKeys* mediaKeys, Ref<DeferredPromise>&& promise)
+{
+    // https://w3c.github.io/encrypted-media/#dom-htmlmediaelement-setmediakeys
+    // W3C Editor's Draft 23 June 2017
+
+    // 1. If this object's attaching media keys value is true, return a promise rejected with an InvalidStateError.
+    if (m_attachingMediaKeys) {
+        promise->reject(InvalidStateError);
+        return;
+    }
+
+    // 2. If mediaKeys and the mediaKeys attribute are the same object, return a resolved promise.
+    if (mediaKeys == m_mediaKeys) {
+        promise->resolve();
+        return;
+    }
+
+    // 3. Let this object's attaching media keys value be true.
+    m_attachingMediaKeys = true;
+
+    // 4. Let promise be a new promise.
+    // 5. Run the following steps in parallel:
+    m_encryptedMediaQueue.enqueueTask([this, mediaKeys = RefPtr<MediaKeys>(mediaKeys), promise = WTFMove(promise)]() mutable {
+        // 5.1. If all the following conditions hold:
+        //      - mediaKeys is not null,
+        //      - the CDM instance represented by mediaKeys is already in use by another media element
+        //      - the user agent is unable to use it with this element
+        //      then let this object's attaching media keys value be false and reject promise with a QuotaExceededError.
+        // FIXME: ^
+
+        // 5.2. If the mediaKeys attribute is not null, run the following steps:
+        if (m_mediaKeys) {
+            // 5.2.1. If the user agent or CDM do not support removing the association, let this object's attaching media keys value be false and reject promise with a NotSupportedError.
+            // 5.2.2. If the association cannot currently be removed, let this object's attaching media keys value be false and reject promise with an InvalidStateError.
+            // 5.2.3. Stop using the CDM instance represented by the mediaKeys attribute to decrypt media data and remove the association with the media element.
+            // 5.2.4. If the preceding step failed, let this object's attaching media keys value be false and reject promise with the appropriate error name.
+            // FIXME: ^
+
+            m_mediaKeys->detachCDMClient(*this);
+            if (m_player)
+                m_player->cdmInstanceDetached(m_mediaKeys->cdmInstance());
+        }
+
+        // 5.3. If mediaKeys is not null, run the following steps:
+        if (mediaKeys) {
+            // 5.3.1. Associate the CDM instance represented by mediaKeys with the media element for decrypting media data.
+            mediaKeys->attachCDMClient(*this);
+            if (m_player)
+                m_player->cdmInstanceAttached(m_mediaKeys->cdmInstance());
+
+            // 5.3.2. If the preceding step failed, run the following steps:
+            //   5.3.2.1. Set the mediaKeys attribute to null.
+            //   5.3.2.2. Let this object's attaching media keys value be false.
+            //   5.3.2.3. Reject promise with a new DOMException whose name is the appropriate error name.
+            // FIXME: ^
+
+            // 5.3.3. Queue a task to run the Attempt to Resume Playback If Necessary algorithm on the media element.
+            m_encryptedMediaQueue.enqueueTask([this] {
+                attemptToResumePlaybackIfNecessary();
+            });
+        }
+
+        // 5.4. Set the mediaKeys attribute to mediaKeys.
+        // 5.5. Let this object's attaching media keys value be false.
+        // 5.6. Resolve promise.
+        m_mediaKeys = WTFMove(mediaKeys);
+        m_attachingMediaKeys = false;
+        promise->resolve();
+    });
+
+    // 6. Return promise.
 }
 
-void HTMLMediaElement::setMediaKeys(MediaKeys*, Ref<DeferredPromise>&&)
+void HTMLMediaElement::attemptToResumePlaybackIfNecessary()
 {
+    // https://w3c.github.io/encrypted-media/#resume-playback
+    // W3C Editor's Draft 23 June 2017
+
     notImplemented();
 }
 
@@ -5288,6 +5379,9 @@ void HTMLMediaElement::contextDestroyed()
     m_promiseTaskQueue.close();
     m_pauseAfterDetachedTaskQueue.close();
     m_updatePlaybackControlsManagerQueue.close();
+#if ENABLE(ENCRYPTED_MEDIA)
+    m_encryptedMediaQueue.close();
+#endif
 
     m_pendingPlayPromises.clear();
 
index 0bd1b3b214f283246ed358eee475cfb2b8524426..f88952319adf7ffc281a23fdecf17ea95d83a7d2 100644 (file)
@@ -636,6 +636,8 @@ private:
 #endif
 
 #if ENABLE(ENCRYPTED_MEDIA)
+    void attemptToResumePlaybackIfNecessary();
+
     // CDMClient
     void cdmClientAttemptToResumePlaybackIfNecessary() override;
 #endif
@@ -1091,6 +1093,11 @@ private:
 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
     RefPtr<WebKitMediaKeys> m_webKitMediaKeys;
 #endif
+#if ENABLE(ENCRYPTED_MEDIA)
+    RefPtr<MediaKeys> m_mediaKeys;
+    bool m_attachingMediaKeys { false };
+    GenericTaskQueue<Timer> m_encryptedMediaQueue;
+#endif
 
     std::unique_ptr<MediaElementSession> m_mediaSession;
     size_t m_reportedExtraMemoryCost { 0 };
index 6a20653dc7323152456beac98f439878434e095e..9e41e632f114acce926028b9aae3dc820980d6a7 100644 (file)
@@ -584,6 +584,18 @@ void MediaPlayer::keyAdded()
 }
 #endif
     
+#if ENABLE(ENCRYPTED_MEDIA)
+void MediaPlayer::cdmInstanceAttached(const CDMInstance& instance)
+{
+    m_private->cdmInstanceAttached(instance);
+}
+
+void MediaPlayer::cdmInstanceDetached(const CDMInstance& instance)
+{
+    m_private->cdmInstanceDetached(instance);
+}
+#endif
+
 MediaTime MediaPlayer::duration() const
 {
     return m_private->durationMediaTime();
index fcfd0626b4f9276b7c0d596c1cd38a2bc66b82ca..f431fd605bde103ecfc8e9aafe689c8ca71bfa14 100644 (file)
@@ -71,6 +71,9 @@ namespace WebCore {
 
 class AudioSourceProvider;
 class AuthenticationChallenge;
+#if ENABLE(ENCRYPTED_MEDIA)
+class CDMInstance;
+#endif
 class MediaPlaybackTarget;
 #if ENABLE(MEDIA_SOURCE)
 class MediaSourcePrivateClient;
@@ -365,6 +368,11 @@ public:
     void keyAdded();
 #endif
 
+#if ENABLE(ENCRYPTED_MEDIA)
+    void cdmInstanceAttached(const CDMInstance&);
+    void cdmInstanceDetached(const CDMInstance&);
+#endif
+
     bool paused() const;
     bool seeking() const;
 
index f806689364625b74d4679adcabb8fa7b098cab82..f0a1ec30fcb6a19071a8b1331ce82516aa627256 100644 (file)
@@ -236,6 +236,11 @@ public:
     virtual void keyAdded() { }
 #endif
 
+#if ENABLE(ENCRYPTED_MEDIA)
+    virtual void cdmInstanceAttached(const CDMInstance&) { }
+    virtual void cdmInstanceDetached(const CDMInstance&) { }
+#endif
+
 #if ENABLE(VIDEO_TRACK)
     virtual bool requiresTextTrackRepresentation() const { return false; }
     virtual void setTextTrackRepresentation(TextTrackRepresentation*) { }