[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 2b6c60b..f5a62d3 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 a1118d4..523d81d 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 856bbf8..af07178 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 2e95a53..2c9c103 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 85d6cdb..22c8bf7 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 0bd1b3b..f889523 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 6a20653..9e41e63 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 fcfd062..f431fd6 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 f806689..f0a1ec3 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*) { }