<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[211856] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/211856">211856</a></dd>
<dt>Author</dt> <dd>zandobersek@gmail.com</dd>
<dt>Date</dt> <dd>2017-02-08 02:25:23 -0800 (Wed, 08 Feb 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>[EME] Implement MediaKeySession::close()
https://bugs.webkit.org/show_bug.cgi?id=167869

Reviewed by Xabier Rodriguez-Calvar.

Source/WebCore:

Implement MediaKeySession::close() as outlined in the specification.

The CDMInstance::closeSession() virtual method, when called, should
close the session that's represented by the passed-in session ID on
the CDMInstance implementor object. That's the same session ID that
the CDMInstance object passes to the MediaKeySession class through
the callback that's provided to the updateLicense call.

The CloseSessionCallback, passed to CDMInstance::closeSession(),
should be invoked by the CDMInstance implementor once the session
is closed. When that is invoked, another task is queued for the
MediaKeySession object that runs the `session closed` algorithm
and resolves the promise.

MockCDMInstance::closeSession() is defined to remove the session
from the MockCDMFactory object and invoke the CloseSessionCallback.

Test: media/encrypted-media/mock-MediaKeySession-close.html

* Modules/encryptedmedia/CDMInstance.h:
* Modules/encryptedmedia/MediaKeySession.cpp:
(WebCore::MediaKeySession::close):
* testing/MockCDMFactory.cpp:
(WebCore::MockCDMInstance::closeSession):
* testing/MockCDMFactory.h:

LayoutTests:

Add the mock-MediaKeySession-close.html test case which checks proper
behavior of MediaKeySession::close(), specifically that under specific
conditions the promise returned by that method is properly resolved or
rejected. The test is skipped on all platforms for now.

* media/encrypted-media/mock-MediaKeySession-close-expected.txt: Added.
* media/encrypted-media/mock-MediaKeySession-close.html: Added.
* platform/efl/TestExpectations:
* platform/mac/TestExpectations:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsplatformeflTestExpectations">trunk/LayoutTests/platform/efl/TestExpectations</a></li>
<li><a href="#trunkLayoutTestsplatformmacTestExpectations">trunk/LayoutTests/platform/mac/TestExpectations</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreModulesencryptedmediaCDMInstanceh">trunk/Source/WebCore/Modules/encryptedmedia/CDMInstance.h</a></li>
<li><a href="#trunkSourceWebCoreModulesencryptedmediaMediaKeySessioncpp">trunk/Source/WebCore/Modules/encryptedmedia/MediaKeySession.cpp</a></li>
<li><a href="#trunkSourceWebCoretestingMockCDMFactorycpp">trunk/Source/WebCore/testing/MockCDMFactory.cpp</a></li>
<li><a href="#trunkSourceWebCoretestingMockCDMFactoryh">trunk/Source/WebCore/testing/MockCDMFactory.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsmediaencryptedmediamockMediaKeySessioncloseexpectedtxt">trunk/LayoutTests/media/encrypted-media/mock-MediaKeySession-close-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediaencryptedmediamockMediaKeySessionclosehtml">trunk/LayoutTests/media/encrypted-media/mock-MediaKeySession-close.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (211855 => 211856)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2017-02-08 09:34:44 UTC (rev 211855)
+++ trunk/LayoutTests/ChangeLog        2017-02-08 10:25:23 UTC (rev 211856)
</span><span class="lines">@@ -1,3 +1,20 @@
</span><ins>+2017-02-08  Zan Dobersek  &lt;zdobersek@igalia.com&gt;
+
+        [EME] Implement MediaKeySession::close()
+        https://bugs.webkit.org/show_bug.cgi?id=167869
+
+        Reviewed by Xabier Rodriguez-Calvar.
+
+        Add the mock-MediaKeySession-close.html test case which checks proper
+        behavior of MediaKeySession::close(), specifically that under specific
+        conditions the promise returned by that method is properly resolved or
+        rejected. The test is skipped on all platforms for now.
+
+        * media/encrypted-media/mock-MediaKeySession-close-expected.txt: Added.
+        * media/encrypted-media/mock-MediaKeySession-close.html: Added.
+        * platform/efl/TestExpectations:
+        * platform/mac/TestExpectations:
+
</ins><span class="cx"> 2017-02-07  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
</span><span class="cx"> 
</span><span class="cx">         WebContent process repeatedly jetsams on BuzzFeed's Another Round page
</span></span></pre></div>
<a id="trunkLayoutTestsmediaencryptedmediamockMediaKeySessioncloseexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/encrypted-media/mock-MediaKeySession-close-expected.txt (0 => 211856)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/encrypted-media/mock-MediaKeySession-close-expected.txt                                (rev 0)
+++ trunk/LayoutTests/media/encrypted-media/mock-MediaKeySession-close-expected.txt        2017-02-08 10:25:23 UTC (rev 211856)
</span><span class="lines">@@ -0,0 +1,44 @@
</span><ins>+RUN(internals.initializeMockMediaSource())
+RUN(mock = internals.registerMockCDM())
+RUN(mock.supportedDataTypes = [&quot;keyids&quot;])
+RUN(capabilities.initDataTypes = [&quot;keyids&quot;])
+RUN(capabilities.videoCapabilities = [{ contentType: 'video/mock; codecs=&quot;mock&quot;' }] )
+RUN(promise = navigator.requestMediaKeySystemAccess(&quot;org.webkit.mock&quot;, [capabilities]))
+Promise resolved OK
+
+RUN(promise = mediaKeySystemAccess.createMediaKeys())
+Promise resolved OK
+
+Closing a non-callable MediaKeySession should reject.
+RUN(mediaKeySession = mediaKeys.createSession(&quot;temporary&quot;))
+EXPECTED (typeof mediaKeySession == 'object') OK
+RUN(promise = mediaKeySession.close())
+Promise rejected correctly OK
+
+Closing a failed MediaKeySession should reject.
+RUN(kids = JSON.stringify({ invalid: &quot;invalid&quot; }))
+RUN(mediaKeySession = mediaKeys.createSession(&quot;temporary&quot;))
+RUN(promise = mediaKeySession.generateRequest(&quot;keyids&quot;, encoder.encode(kids)))
+Promise rejected correctly OK
+RUN(promise = mediaKeySession.close())
+Promise rejected correctly OK
+
+Closing a valid MediaKeySession should resolve.
+RUN(kids = JSON.stringify({ kids: [ &quot;MTIzNDU=&quot; ] }))
+RUN(mediaKeySession = mediaKeys.createSession(&quot;temporary&quot;))
+RUN(promise = mediaKeySession.generateRequest(&quot;keyids&quot;, encoder.encode(kids)))
+Promise resolved OK
+RUN(promise = mediaKeySession.close())
+Promise resolved OK
+
+Closing a closed MediaKeySession should resolve.
+RUN(kids = JSON.stringify({ kids: [ &quot;MTIzNDU=&quot; ] }))
+RUN(mediaKeySession = mediaKeys.createSession(&quot;temporary&quot;))
+RUN(promise = mediaKeySession.generateRequest(&quot;keyids&quot;, encoder.encode(kids)))
+Promise resolved OK
+RUN(promise = mediaKeySession.close())
+Promise resolved OK
+RUN(promise = mediaKeySession.close())
+Promise resolved OK
+END OF TEST
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediaencryptedmediamockMediaKeySessionclosehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/encrypted-media/mock-MediaKeySession-close.html (0 => 211856)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/encrypted-media/mock-MediaKeySession-close.html                                (rev 0)
+++ trunk/LayoutTests/media/encrypted-media/mock-MediaKeySession-close.html        2017-02-08 10:25:23 UTC (rev 211856)
</span><span class="lines">@@ -0,0 +1,107 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;head&gt;
+    &lt;script src=../video-test.js&gt;&lt;/script&gt;
+    &lt;script type=&quot;text/javascript&quot;&gt;
+    var mock;
+    var promise;
+    var mediaKeySystemAccess;
+    var mediaKeys;
+    var mediaKeySession;
+    var capabilities = {};
+    var kids;
+    var encoder = new TextEncoder();
+
+    function doTest()
+    {
+        if (!window.internals) {
+            failTest(&quot;Internals is required for this test.&quot;)
+            return;
+        }
+
+        run('internals.initializeMockMediaSource()');
+        run('mock = internals.registerMockCDM()');
+        run('mock.supportedDataTypes = [&quot;keyids&quot;]');
+        run('capabilities.initDataTypes = [&quot;keyids&quot;]');
+        run(`capabilities.videoCapabilities = [{ contentType: 'video/mock; codecs=&quot;mock&quot;' }] `);
+        run('promise = navigator.requestMediaKeySystemAccess(&quot;org.webkit.mock&quot;, [capabilities])');
+        shouldResolve(promise).then(gotMediaKeySystemAccess, failTest);
+    }
+
+    function next() {
+        if (!tests.length) {
+            mock.unregister();
+            endTest()
+            return;
+        }
+
+        var nextTest = tests.shift();
+        consoleWrite('');
+        nextTest();
+    }
+
+    function gotMediaKeySystemAccess(result) {
+        mediaKeySystemAccess = result;
+        next();
+    }
+
+    function gotMediaKeys(result) {
+        mediaKeys = result;
+        next();
+    }
+
+    tests = [
+        function() {
+            run('promise = mediaKeySystemAccess.createMediaKeys()');
+            shouldResolve(promise).then(gotMediaKeys, failTest);
+        },
+
+        function() {
+            consoleWrite('Closing a non-callable MediaKeySession should reject.');
+            run('mediaKeySession = mediaKeys.createSession(&quot;temporary&quot;)');
+            testExpected('typeof mediaKeySession', 'object');
+            run('promise = mediaKeySession.close()');
+            shouldReject(promise).then(next, next);
+        },
+
+        function() {
+            consoleWrite('Closing a failed MediaKeySession should reject.')
+            run('kids = JSON.stringify({ invalid: &quot;invalid&quot; })');
+            run('mediaKeySession = mediaKeys.createSession(&quot;temporary&quot;)');
+            run('promise = mediaKeySession.generateRequest(&quot;keyids&quot;, encoder.encode(kids))');
+            shouldReject(promise).then(function() {
+                run('promise = mediaKeySession.close()');
+                shouldReject(promise).then(next, next);
+            }, next);
+        },
+
+        function() {
+            consoleWrite('Closing a valid MediaKeySession should resolve.')
+            run('kids = JSON.stringify({ kids: [ &quot;MTIzNDU=&quot; ] })');
+            run('mediaKeySession = mediaKeys.createSession(&quot;temporary&quot;)');
+            run('promise = mediaKeySession.generateRequest(&quot;keyids&quot;, encoder.encode(kids))');
+            shouldResolve(promise).then(function() {
+                run('promise = mediaKeySession.close()');
+                shouldResolve(promise).then(next, next);
+            }, next);
+        },
+
+        function() {
+            consoleWrite('Closing a closed MediaKeySession should resolve.')
+            run('kids = JSON.stringify({ kids: [ &quot;MTIzNDU=&quot; ] })');
+            run('mediaKeySession = mediaKeys.createSession(&quot;temporary&quot;)');
+            run('promise = mediaKeySession.generateRequest(&quot;keyids&quot;, encoder.encode(kids))');
+            shouldResolve(promise).then(function() {
+                run('promise = mediaKeySession.close()');
+                shouldResolve(promise).then(function() {
+                    run('promise = mediaKeySession.close()');
+                    shouldResolve(promise).then(next, next);
+                }, next);
+            }, next);
+        },
+    ];
+    &lt;/script&gt;
+&lt;/head&gt;
+&lt;body onload=&quot;doTest()&quot;&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsplatformeflTestExpectations"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/platform/efl/TestExpectations (211855 => 211856)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/efl/TestExpectations        2017-02-08 09:34:44 UTC (rev 211855)
+++ trunk/LayoutTests/platform/efl/TestExpectations        2017-02-08 10:25:23 UTC (rev 211856)
</span><span class="lines">@@ -2981,6 +2981,7 @@
</span><span class="cx"> Bug(EFL) media/encrypted-media/encrypted-media-is-type-supported.html [ Failure ]
</span><span class="cx"> Bug(EFL) media/encrypted-media/encrypted-media-not-loaded.html [ Failure ]
</span><span class="cx"> Bug(EFL) media/encrypted-media/encrypted-media-syntax.html [ Failure ]
</span><ins>+Bug(EFL) media/encrypted-media/mock-MediaKeySession-close.html [ Failure ]
</ins><span class="cx"> Bug(EFL) media/encrypted-media/mock-MediaKeySession-generateRequest.html [ Failure ]
</span><span class="cx"> Bug(EFL) media/encrypted-media/mock-MediaKeySession-update.html [ Failure ]
</span><span class="cx"> Bug(EFL) media/encrypted-media/mock-MediaKeySystemAccess.html [ Failure ]
</span></span></pre></div>
<a id="trunkLayoutTestsplatformmacTestExpectations"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/platform/mac/TestExpectations (211855 => 211856)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/mac/TestExpectations        2017-02-08 09:34:44 UTC (rev 211855)
+++ trunk/LayoutTests/platform/mac/TestExpectations        2017-02-08 10:25:23 UTC (rev 211856)
</span><span class="lines">@@ -1492,6 +1492,7 @@
</span><span class="cx"> media/encrypted-media/mock-MediaKeySystemAccess.html [ Skip ]
</span><span class="cx"> media/encrypted-media/mock-MediaKeys-setServerCertificate.html [ Skip ]
</span><span class="cx"> media/encrypted-media/mock-MediaKeys-createSession.html [ Skip ]
</span><ins>+media/encrypted-media/mock-MediaKeySession-close.html [ Skip ]
</ins><span class="cx"> media/encrypted-media/mock-MediaKeySession-generateRequest.html [ Skip ]
</span><span class="cx"> media/encrypted-media/mock-MediaKeySession-update.html [ Skip ]
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (211855 => 211856)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2017-02-08 09:34:44 UTC (rev 211855)
+++ trunk/Source/WebCore/ChangeLog        2017-02-08 10:25:23 UTC (rev 211856)
</span><span class="lines">@@ -1,5 +1,38 @@
</span><span class="cx"> 2017-02-08  Zan Dobersek  &lt;zdobersek@igalia.com&gt;
</span><span class="cx"> 
</span><ins>+        [EME] Implement MediaKeySession::close()
+        https://bugs.webkit.org/show_bug.cgi?id=167869
+
+        Reviewed by Xabier Rodriguez-Calvar.
+
+        Implement MediaKeySession::close() as outlined in the specification.
+
+        The CDMInstance::closeSession() virtual method, when called, should
+        close the session that's represented by the passed-in session ID on
+        the CDMInstance implementor object. That's the same session ID that
+        the CDMInstance object passes to the MediaKeySession class through
+        the callback that's provided to the updateLicense call.
+
+        The CloseSessionCallback, passed to CDMInstance::closeSession(),
+        should be invoked by the CDMInstance implementor once the session
+        is closed. When that is invoked, another task is queued for the
+        MediaKeySession object that runs the `session closed` algorithm
+        and resolves the promise.
+
+        MockCDMInstance::closeSession() is defined to remove the session
+        from the MockCDMFactory object and invoke the CloseSessionCallback.
+
+        Test: media/encrypted-media/mock-MediaKeySession-close.html
+
+        * Modules/encryptedmedia/CDMInstance.h:
+        * Modules/encryptedmedia/MediaKeySession.cpp:
+        (WebCore::MediaKeySession::close):
+        * testing/MockCDMFactory.cpp:
+        (WebCore::MockCDMInstance::closeSession):
+        * testing/MockCDMFactory.h:
+
+2017-02-08  Zan Dobersek  &lt;zdobersek@igalia.com&gt;
+
</ins><span class="cx">         [EME] Alias CDMInstance enums to the specification-defined enums
</span><span class="cx">         https://bugs.webkit.org/show_bug.cgi?id=167896
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesencryptedmediaCDMInstanceh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/encryptedmedia/CDMInstance.h (211855 => 211856)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/encryptedmedia/CDMInstance.h        2017-02-08 09:34:44 UTC (rev 211855)
+++ trunk/Source/WebCore/Modules/encryptedmedia/CDMInstance.h        2017-02-08 10:25:23 UTC (rev 211856)
</span><span class="lines">@@ -66,6 +66,9 @@
</span><span class="cx">     using Message = std::pair&lt;MessageType, Ref&lt;SharedBuffer&gt;&gt;;
</span><span class="cx">     using LicenseUpdateCallback = Function&lt;void(bool sessionWasClosed, std::optional&lt;KeyStatusVector&gt;&amp;&amp; changedKeys, std::optional&lt;double&gt;&amp;&amp; changedExpiration, std::optional&lt;Message&gt;&amp;&amp; message, SuccessValue succeeded)&gt;;
</span><span class="cx">     virtual void updateLicense(LicenseType, const SharedBuffer&amp; response, LicenseUpdateCallback) = 0;
</span><ins>+
+    using CloseSessionCallback = Function&lt;void()&gt;;
+    virtual void closeSession(const String&amp; sessionId, CloseSessionCallback) = 0;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesencryptedmediaMediaKeySessioncpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/encryptedmedia/MediaKeySession.cpp (211855 => 211856)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/encryptedmedia/MediaKeySession.cpp        2017-02-08 09:34:44 UTC (rev 211855)
+++ trunk/Source/WebCore/Modules/encryptedmedia/MediaKeySession.cpp        2017-02-08 10:25:23 UTC (rev 211856)
</span><span class="lines">@@ -359,9 +359,45 @@
</span><span class="cx">     // 7. Return promise.
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void MediaKeySession::close(Ref&lt;DeferredPromise&gt;&amp;&amp;)
</del><ins>+void MediaKeySession::close(Ref&lt;DeferredPromise&gt;&amp;&amp; promise)
</ins><span class="cx"> {
</span><del>-    notImplemented();
</del><ins>+    // https://w3c.github.io/encrypted-media/#dom-mediakeysession-close
+    // W3C Editor's Draft 09 November 2016
+
+    // 1. Let session be the associated MediaKeySession object.
+    // 2. If session is closed, return a resolved promise.
+    if (m_closed) {
+        promise-&gt;resolve();
+        return;
+    }
+
+    // 3. If session's callable value is false, return a promise rejected with an InvalidStateError.
+    if (!m_callable) {
+        promise-&gt;reject(INVALID_STATE_ERR);
+        return;
+    }
+
+    // 4. Let promise be a new promise.
+    // 5. Run the following steps in parallel:
+    m_taskQueue.enqueueTask([this, promise = WTFMove(promise)] () mutable {
+        // 5.1. Let cdm be the CDM instance represented by session's cdm instance value.
+        // 5.2. Use cdm to close the key session associated with session.
+        m_instance-&gt;closeSession(m_sessionId, [this, weakThis = m_weakPtrFactory.createWeakPtr(), promise = WTFMove(promise)] () mutable {
+            if (!weakThis)
+                return;
+
+            // 5.3. Queue a task to run the following steps:
+            m_taskQueue.enqueueTask([this, promise = WTFMove(promise)] () mutable {
+                // 5.3.1. Run the Session Closed algorithm on the session.
+                sessionClosed();
+
+                // 5.3.2. Resolve promise.
+                promise-&gt;resolve();
+            });
+        });
+    });
+
+    // 6. Return promise.
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void MediaKeySession::remove(Ref&lt;DeferredPromise&gt;&amp;&amp;)
</span></span></pre></div>
<a id="trunkSourceWebCoretestingMockCDMFactorycpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/testing/MockCDMFactory.cpp (211855 => 211856)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/testing/MockCDMFactory.cpp        2017-02-08 09:34:44 UTC (rev 211855)
+++ trunk/Source/WebCore/testing/MockCDMFactory.cpp        2017-02-08 10:25:23 UTC (rev 211856)
</span><span class="lines">@@ -289,6 +289,18 @@
</span><span class="cx">     callback(false, std::nullopt, std::nullopt, std::nullopt, SuccessValue::Succeeded);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void MockCDMInstance::closeSession(const String&amp; sessionID, CloseSessionCallback callback)
+{
+    MockCDMFactory* factory = m_cdm ? m_cdm-&gt;factory() : nullptr;
+    if (!factory) {
+        callback();
+        return;
+    }
+
+    factory-&gt;removeSessionWithID(sessionID);
+    callback();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+}
+
</ins><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkSourceWebCoretestingMockCDMFactoryh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/testing/MockCDMFactory.h (211855 => 211856)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/testing/MockCDMFactory.h        2017-02-08 09:34:44 UTC (rev 211855)
+++ trunk/Source/WebCore/testing/MockCDMFactory.h        2017-02-08 10:25:23 UTC (rev 211856)
</span><span class="lines">@@ -130,6 +130,7 @@
</span><span class="cx">     SuccessValue setServerCertificate(Ref&lt;SharedBuffer&gt;&amp;&amp;) final;
</span><span class="cx">     void requestLicense(LicenseType, const AtomicString&amp; initDataType, Ref&lt;SharedBuffer&gt;&amp;&amp; initData, LicenseCallback) final;
</span><span class="cx">     void updateLicense(LicenseType, const SharedBuffer&amp;, LicenseUpdateCallback) final;
</span><ins>+    void closeSession(const String&amp;, CloseSessionCallback) final;
</ins><span class="cx"> 
</span><span class="cx">     WeakPtr&lt;MockCDM&gt; m_cdm;
</span><span class="cx">     bool m_distinctiveIdentifiersAllowed { true };
</span></span></pre>
</div>
</div>

</body>
</html>