<!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>[211550] 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/211550">211550</a></dd>
<dt>Author</dt> <dd>zandobersek@gmail.com</dd>
<dt>Date</dt> <dd>2017-02-01 22:28:03 -0800 (Wed, 01 Feb 2017)</dd>
</dl>

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

Reviewed by Xabier Rodriguez-Calvar.

Source/WebCore:

Implement the MediaKeySession::update() method by following the steps as
they are described in the specification.

In order to sanitize the passed-in response data, CDM::sanitizeResponse()
is added. It passes the SharedBuffer object by reference to the CDMPrivate
interface implementor, which returns a SharedBuffer object containing
sanitized response data.

CDMInstance::updateLicense() virtual method is added to perform the license
update for some specific CDMInstance object. After the update the CDMInstance
invokes the callback that's passed to updateLicense(), providing information
about session being closed, changed keys or expiration value, any message
that has to be enqueued, and whether the update was successful.

After that callback is invoked, MediaKeySession::update() goes on to handle
all the provided information in a future task, finally resolving the promise
(or rejecting it beforehand in case of any failure during response handling
or license update).

Three algorithms that can be invoked from MediaKeySession::update() (key
status update, expiration update and session closure) will be implemented
separately. Placeholder methods are provided until then.

MockCDM::sanitizeResponse() and MockCDMInstance::updateLicense() are
implemented for testing purposes. For now only the response sanitization
and sanitized response format are checked there. Key status update,
expiration update and session closure should be tested once the
implementations for those algorithms are added.

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

* Modules/encryptedmedia/CDM.cpp:
(WebCore::CDM::sanitizeResponse):
* Modules/encryptedmedia/CDM.h:
* Modules/encryptedmedia/CDMInstance.h:
* Modules/encryptedmedia/CDMPrivate.h:
* Modules/encryptedmedia/MediaKeySession.cpp:
(WebCore::MediaKeySession::update):
(WebCore::MediaKeySession::updateKeyStatuses):
(WebCore::MediaKeySession::updateExpiration):
(WebCore::MediaKeySession::sessionClosed):
* Modules/encryptedmedia/MediaKeySession.h:
* testing/MockCDMFactory.cpp:
(WebCore::MockCDM::sanitizeResponse):
(WebCore::MockCDMInstance::updateLicense):
* testing/MockCDMFactory.h:

LayoutTests:

Add the mock-MediaKeySession-update.html test, cotaining a few cases that check
the basic operations of MediaKeySession::update(), focusing on proper promise
resolution and rejection based on the state of the object and the passed-in
response argument. Skip the test on all platforms for now.

* media/encrypted-media/mock-MediaKeySession-update-expected.txt: Added.
* media/encrypted-media/mock-MediaKeySession-update.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="#trunkSourceWebCoreModulesencryptedmediaCDMcpp">trunk/Source/WebCore/Modules/encryptedmedia/CDM.cpp</a></li>
<li><a href="#trunkSourceWebCoreModulesencryptedmediaCDMh">trunk/Source/WebCore/Modules/encryptedmedia/CDM.h</a></li>
<li><a href="#trunkSourceWebCoreModulesencryptedmediaCDMInstanceh">trunk/Source/WebCore/Modules/encryptedmedia/CDMInstance.h</a></li>
<li><a href="#trunkSourceWebCoreModulesencryptedmediaCDMPrivateh">trunk/Source/WebCore/Modules/encryptedmedia/CDMPrivate.h</a></li>
<li><a href="#trunkSourceWebCoreModulesencryptedmediaMediaKeySessioncpp">trunk/Source/WebCore/Modules/encryptedmedia/MediaKeySession.cpp</a></li>
<li><a href="#trunkSourceWebCoreModulesencryptedmediaMediaKeySessionh">trunk/Source/WebCore/Modules/encryptedmedia/MediaKeySession.h</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="#trunkLayoutTestsmediaencryptedmediamockMediaKeySessionupdateexpectedtxt">trunk/LayoutTests/media/encrypted-media/mock-MediaKeySession-update-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediaencryptedmediamockMediaKeySessionupdatehtml">trunk/LayoutTests/media/encrypted-media/mock-MediaKeySession-update.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (211549 => 211550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2017-02-02 03:10:58 UTC (rev 211549)
+++ trunk/LayoutTests/ChangeLog        2017-02-02 06:28:03 UTC (rev 211550)
</span><span class="lines">@@ -1,3 +1,20 @@
</span><ins>+2017-02-01  Zan Dobersek  &lt;zdobersek@igalia.com&gt;
+
+        [EME] Implement MediaKeySession::update()
+        https://bugs.webkit.org/show_bug.cgi?id=167636
+
+        Reviewed by Xabier Rodriguez-Calvar.
+
+        Add the mock-MediaKeySession-update.html test, cotaining a few cases that check
+        the basic operations of MediaKeySession::update(), focusing on proper promise
+        resolution and rejection based on the state of the object and the passed-in
+        response argument. Skip the test on all platforms for now.
+
+        * media/encrypted-media/mock-MediaKeySession-update-expected.txt: Added.
+        * media/encrypted-media/mock-MediaKeySession-update.html: Added.
+        * platform/efl/TestExpectations:
+        * platform/mac/TestExpectations:
+
</ins><span class="cx"> 2017-02-01  Ryan Haddad  &lt;ryanhaddad@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Rebaseline compositing/geometry/fixed-in-composited.html for ios-simulator.
</span></span></pre></div>
<a id="trunkLayoutTestsmediaencryptedmediamockMediaKeySessionupdateexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/encrypted-media/mock-MediaKeySession-update-expected.txt (0 => 211550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/encrypted-media/mock-MediaKeySession-update-expected.txt                                (rev 0)
+++ trunk/LayoutTests/media/encrypted-media/mock-MediaKeySession-update-expected.txt        2017-02-02 06:28:03 UTC (rev 211550)
</span><span class="lines">@@ -0,0 +1,50 @@
</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
+
+Using a non-callable MediaKeySession should reject.
+RUN(mediaKeySession = mediaKeys.createSession(&quot;temporary&quot;))
+EXPECTED (typeof mediaKeySession == 'object') OK
+RUN(promise = mediaKeySession.update(stringToUInt8Array(&quot;invalid-state&quot;)))
+Promise rejected correctly OK
+
+Using a zero-length response should reject.
+RUN(kids = JSON.stringify({ kids: [ &quot;MTIzNDU=&quot; ] }))
+RUN(mediaKeySession = mediaKeys.createSession(&quot;temporary&quot;))
+RUN(promise = mediaKeySession.generateRequest(&quot;keyids&quot;, stringToUInt8Array(kids)))
+Promise resolved OK
+RUN(promise = mediaKeySession.update(new Uint8Array(0)))
+Promise rejected correctly OK
+
+Using a non-sanitizable response should reject.
+RUN(kids = JSON.stringify({ kids: [ &quot;MTIzNDU=&quot; ] }))
+RUN(mediaKeySession = mediaKeys.createSession(&quot;temporary&quot;))
+RUN(promise = mediaKeySession.generateRequest(&quot;keyids&quot;, stringToUInt8Array(kids)))
+Promise resolved OK
+RUN(promise = mediaKeySession.update(stringToUInt8Array(&quot;invalid-response&quot;)))
+Promise rejected correctly OK
+
+Using a sanitizable response should resolve.
+RUN(kids = JSON.stringify({ kids: [ &quot;MTIzNDU=&quot; ] }))
+RUN(mediaKeySession = mediaKeys.createSession(&quot;temporary&quot;))
+RUN(promise = mediaKeySession.generateRequest(&quot;keyids&quot;, stringToUInt8Array(kids)))
+Promise resolved OK
+RUN(promise = mediaKeySession.update(stringToUInt8Array(&quot;valid-response&quot;)))
+Promise resolved OK
+
+Using a sanitizable response with invalid format should resolve.
+RUN(kids = JSON.stringify({ kids: [ &quot;MTIzNDU=&quot; ] }))
+RUN(mediaKeySession = mediaKeys.createSession(&quot;temporary&quot;))
+RUN(promise = mediaKeySession.generateRequest(&quot;keyids&quot;, stringToUInt8Array(kids)))
+Promise resolved OK
+RUN(promise = mediaKeySession.update(stringToUInt8Array(&quot;valid-response invalid-format&quot;)))
+Promise rejected correctly OK
+END OF TEST
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediaencryptedmediamockMediaKeySessionupdatehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/encrypted-media/mock-MediaKeySession-update.html (0 => 211550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/encrypted-media/mock-MediaKeySession-update.html                                (rev 0)
+++ trunk/LayoutTests/media/encrypted-media/mock-MediaKeySession-update.html        2017-02-02 06:28:03 UTC (rev 211550)
</span><span class="lines">@@ -0,0 +1,122 @@
</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;
+
+    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();
+    }
+
+    function stringToUInt8Array(str)
+    {
+       var array = new Uint8Array(str.length);
+       for (var i=0; i&lt;str.length; i++)
+            array[i] = str.charCodeAt(i);
+       return array;
+    }
+
+    tests = [
+        function() {
+            run('promise = mediaKeySystemAccess.createMediaKeys()');
+            shouldResolve(promise).then(gotMediaKeys, failTest);
+        },
+
+        function() {
+            consoleWrite('Using a non-callable MediaKeySession should reject.');
+            run('mediaKeySession = mediaKeys.createSession(&quot;temporary&quot;)');
+            testExpected('typeof mediaKeySession', 'object');
+            run('promise = mediaKeySession.update(stringToUInt8Array(&quot;invalid-state&quot;))');
+            shouldReject(promise).then(next, next);
+        },
+
+        function() {
+            consoleWrite('Using a zero-length response should reject.');
+            run('kids = JSON.stringify({ kids: [ &quot;MTIzNDU=&quot; ] })');
+            run('mediaKeySession = mediaKeys.createSession(&quot;temporary&quot;)');
+            run('promise = mediaKeySession.generateRequest(&quot;keyids&quot;, stringToUInt8Array(kids))');
+            shouldResolve(promise).then(function() {
+                run('promise = mediaKeySession.update(new Uint8Array(0))');
+                shouldReject(promise).then(next, next);
+            }, next);
+        },
+
+        function() {
+            consoleWrite('Using a non-sanitizable response should reject.');
+            run('kids = JSON.stringify({ kids: [ &quot;MTIzNDU=&quot; ] })');
+            run('mediaKeySession = mediaKeys.createSession(&quot;temporary&quot;)');
+            run('promise = mediaKeySession.generateRequest(&quot;keyids&quot;, stringToUInt8Array(kids))');
+            shouldResolve(promise).then(function() {
+                run('promise = mediaKeySession.update(stringToUInt8Array(&quot;invalid-response&quot;))');
+                shouldReject(promise).then(next, next);
+            }, next);
+        },
+
+        function() {
+            consoleWrite('Using a sanitizable response should resolve.');
+            run('kids = JSON.stringify({ kids: [ &quot;MTIzNDU=&quot; ] })');
+            run('mediaKeySession = mediaKeys.createSession(&quot;temporary&quot;)');
+            run('promise = mediaKeySession.generateRequest(&quot;keyids&quot;, stringToUInt8Array(kids))');
+            shouldResolve(promise).then(function() {
+                run('promise = mediaKeySession.update(stringToUInt8Array(&quot;valid-response&quot;))');
+                shouldResolve(promise).then(next, next);
+            }, next);
+        },
+
+        function() {
+            consoleWrite('Using a sanitizable response with invalid format should resolve.');
+            run('kids = JSON.stringify({ kids: [ &quot;MTIzNDU=&quot; ] })');
+            run('mediaKeySession = mediaKeys.createSession(&quot;temporary&quot;)');
+            run('promise = mediaKeySession.generateRequest(&quot;keyids&quot;, stringToUInt8Array(kids))');
+            shouldResolve(promise).then(function() {
+                run('promise = mediaKeySession.update(stringToUInt8Array(&quot;valid-response invalid-format&quot;))');
+                shouldReject(promise).then(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 (211549 => 211550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/efl/TestExpectations        2017-02-02 03:10:58 UTC (rev 211549)
+++ trunk/LayoutTests/platform/efl/TestExpectations        2017-02-02 06:28:03 UTC (rev 211550)
</span><span class="lines">@@ -2982,6 +2982,7 @@
</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><span class="cx"> Bug(EFL) media/encrypted-media/mock-MediaKeySession-generateRequest.html [ Failure ]
</span><ins>+Bug(EFL) media/encrypted-media/mock-MediaKeySession-update.html [ Failure ]
</ins><span class="cx"> Bug(EFL) media/encrypted-media/mock-MediaKeySystemAccess.html [ Failure ]
</span><span class="cx"> Bug(EFL) media/encrypted-media/mock-MediaKeys-createSession.html [ Failure ]
</span><span class="cx"> Bug(EFL) media/encrypted-media/mock-MediaKeys-setServerCertificate.html [ Failure ]
</span></span></pre></div>
<a id="trunkLayoutTestsplatformmacTestExpectations"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/platform/mac/TestExpectations (211549 => 211550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/mac/TestExpectations        2017-02-02 03:10:58 UTC (rev 211549)
+++ trunk/LayoutTests/platform/mac/TestExpectations        2017-02-02 06:28:03 UTC (rev 211550)
</span><span class="lines">@@ -1496,6 +1496,7 @@
</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><span class="cx"> media/encrypted-media/mock-MediaKeySession-generateRequest.html [ Skip ]
</span><ins>+media/encrypted-media/mock-MediaKeySession-update.html [ Skip ]
</ins><span class="cx"> 
</span><span class="cx"> webkit.org/b/166025 http/tests/fetch/fetching-same-resource-with-diffferent-options.html [ Pass Failure ]
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (211549 => 211550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2017-02-02 03:10:58 UTC (rev 211549)
+++ trunk/Source/WebCore/ChangeLog        2017-02-02 06:28:03 UTC (rev 211550)
</span><span class="lines">@@ -1,3 +1,57 @@
</span><ins>+2017-02-01  Zan Dobersek  &lt;zdobersek@igalia.com&gt;
+
+        [EME] Implement MediaKeySession::update()
+        https://bugs.webkit.org/show_bug.cgi?id=167636
+
+        Reviewed by Xabier Rodriguez-Calvar.
+
+        Implement the MediaKeySession::update() method by following the steps as
+        they are described in the specification.
+
+        In order to sanitize the passed-in response data, CDM::sanitizeResponse()
+        is added. It passes the SharedBuffer object by reference to the CDMPrivate
+        interface implementor, which returns a SharedBuffer object containing
+        sanitized response data.
+
+        CDMInstance::updateLicense() virtual method is added to perform the license
+        update for some specific CDMInstance object. After the update the CDMInstance
+        invokes the callback that's passed to updateLicense(), providing information
+        about session being closed, changed keys or expiration value, any message
+        that has to be enqueued, and whether the update was successful.
+
+        After that callback is invoked, MediaKeySession::update() goes on to handle
+        all the provided information in a future task, finally resolving the promise
+        (or rejecting it beforehand in case of any failure during response handling
+        or license update).
+
+        Three algorithms that can be invoked from MediaKeySession::update() (key
+        status update, expiration update and session closure) will be implemented
+        separately. Placeholder methods are provided until then.
+
+        MockCDM::sanitizeResponse() and MockCDMInstance::updateLicense() are
+        implemented for testing purposes. For now only the response sanitization
+        and sanitized response format are checked there. Key status update,
+        expiration update and session closure should be tested once the
+        implementations for those algorithms are added.
+
+        Test: media/encrypted-media/mock-MediaKeySession-update.html
+
+        * Modules/encryptedmedia/CDM.cpp:
+        (WebCore::CDM::sanitizeResponse):
+        * Modules/encryptedmedia/CDM.h:
+        * Modules/encryptedmedia/CDMInstance.h:
+        * Modules/encryptedmedia/CDMPrivate.h:
+        * Modules/encryptedmedia/MediaKeySession.cpp:
+        (WebCore::MediaKeySession::update):
+        (WebCore::MediaKeySession::updateKeyStatuses):
+        (WebCore::MediaKeySession::updateExpiration):
+        (WebCore::MediaKeySession::sessionClosed):
+        * Modules/encryptedmedia/MediaKeySession.h:
+        * testing/MockCDMFactory.cpp:
+        (WebCore::MockCDM::sanitizeResponse):
+        (WebCore::MockCDMInstance::updateLicense):
+        * testing/MockCDMFactory.h:
+
</ins><span class="cx"> 2017-02-01  Eric Carlson  &lt;eric.carlson@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [Mac] Update CARingBuffer class
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesencryptedmediaCDMcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/encryptedmedia/CDM.cpp (211549 => 211550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/encryptedmedia/CDM.cpp        2017-02-02 03:10:58 UTC (rev 211549)
+++ trunk/Source/WebCore/Modules/encryptedmedia/CDM.cpp        2017-02-02 06:28:03 UTC (rev 211550)
</span><span class="lines">@@ -644,6 +644,13 @@
</span><span class="cx">     return m_private &amp;&amp; m_private-&gt;supportsInitData(initDataType, initData);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+RefPtr&lt;SharedBuffer&gt; CDM::sanitizeResponse(const SharedBuffer&amp; response)
+{
+    if (!m_private)
+        return nullptr;
+    return m_private-&gt;sanitizeResponse(response);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+}
+
</ins><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesencryptedmediaCDMh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/encryptedmedia/CDM.h (211549 => 211550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/encryptedmedia/CDM.h        2017-02-02 03:10:58 UTC (rev 211549)
+++ trunk/Source/WebCore/Modules/encryptedmedia/CDM.h        2017-02-02 06:28:03 UTC (rev 211550)
</span><span class="lines">@@ -80,6 +80,8 @@
</span><span class="cx">     RefPtr&lt;SharedBuffer&gt; sanitizeInitData(const AtomicString&amp; initDataType, const SharedBuffer&amp;);
</span><span class="cx">     bool supportsInitData(const AtomicString&amp; initDataType, const SharedBuffer&amp;);
</span><span class="cx"> 
</span><ins>+    RefPtr&lt;SharedBuffer&gt; sanitizeResponse(const SharedBuffer&amp;);
+
</ins><span class="cx"> private:
</span><span class="cx">     CDM(Document&amp;, const String&amp; keySystem);
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesencryptedmediaCDMInstanceh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/encryptedmedia/CDMInstance.h (211549 => 211550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/encryptedmedia/CDMInstance.h        2017-02-02 03:10:58 UTC (rev 211549)
+++ trunk/Source/WebCore/Modules/encryptedmedia/CDMInstance.h        2017-02-02 06:28:03 UTC (rev 211550)
</span><span class="lines">@@ -27,8 +27,11 @@
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(ENCRYPTED_MEDIA)
</span><span class="cx"> 
</span><ins>+#include &lt;utility&gt;
</ins><span class="cx"> #include &lt;wtf/Forward.h&gt;
</span><ins>+#include &lt;wtf/Optional.h&gt;
</ins><span class="cx"> #include &lt;wtf/RefCounted.h&gt;
</span><ins>+#include &lt;wtf/Vector.h&gt;
</ins><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><span class="lines">@@ -57,6 +60,28 @@
</span><span class="cx"> 
</span><span class="cx">     using LicenseCallback = Function&lt;void(Ref&lt;SharedBuffer&gt;&amp;&amp; message, const String&amp; sessionId, bool needsIndividualization, SuccessValue succeeded)&gt;;
</span><span class="cx">     virtual void requestLicense(LicenseType, const AtomicString&amp; initDataType, Ref&lt;SharedBuffer&gt;&amp;&amp; initData, LicenseCallback) = 0;
</span><ins>+
+    enum class KeyStatus {
+        Usable,
+        Expired,
+        Released,
+        OutputRestricted,
+        OutputDownscaled,
+        StatusPending,
+        InternalError,
+    };
+
+    enum class MessageType {
+        LicenseRequest,
+        LicenseRenewal,
+        LicenseRelease,
+        IndividualizationRequest,
+    };
+
+    using KeyStatusVector = Vector&lt;std::pair&lt;Ref&lt;SharedBuffer&gt;, KeyStatus&gt;&gt;;
+    using Message = std::pair&lt;MessageType, Ref&lt;SharedBuffer&gt;&gt;;
+    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;;
+    virtual void updateLicense(LicenseType, const SharedBuffer&amp; response, LicenseUpdateCallback) = 0;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesencryptedmediaCDMPrivateh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/encryptedmedia/CDMPrivate.h (211549 => 211550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/encryptedmedia/CDMPrivate.h        2017-02-02 03:10:58 UTC (rev 211549)
+++ trunk/Source/WebCore/Modules/encryptedmedia/CDMPrivate.h        2017-02-02 06:28:03 UTC (rev 211550)
</span><span class="lines">@@ -54,6 +54,7 @@
</span><span class="cx">     virtual bool supportsServerCertificates() const = 0;
</span><span class="cx">     virtual bool supportsSessions() const = 0;
</span><span class="cx">     virtual bool supportsInitData(const AtomicString&amp;, const SharedBuffer&amp;) const = 0;
</span><ins>+    virtual RefPtr&lt;SharedBuffer&gt; sanitizeResponse(const SharedBuffer&amp;) const = 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 (211549 => 211550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/encryptedmedia/MediaKeySession.cpp        2017-02-02 03:10:58 UTC (rev 211549)
+++ trunk/Source/WebCore/Modules/encryptedmedia/MediaKeySession.cpp        2017-02-02 06:28:03 UTC (rev 211550)
</span><span class="lines">@@ -245,9 +245,143 @@
</span><span class="cx">     notImplemented();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void MediaKeySession::update(const BufferSource&amp;, Ref&lt;DeferredPromise&gt;&amp;&amp;)
</del><ins>+void MediaKeySession::update(const BufferSource&amp; response, Ref&lt;DeferredPromise&gt;&amp;&amp; promise)
</ins><span class="cx"> {
</span><del>-    notImplemented();
</del><ins>+    // https://w3c.github.io/encrypted-media/#dom-mediakeysession-update
+    // W3C Editor's Draft 09 November 2016
+
+    // When this method is invoked, the user agent must run the following steps:
+    // 1. If this object is closed, return a promise rejected with an InvalidStateError.
+    // 2. If this object's callable value is false, return a promise rejected with an InvalidStateError.
+    if (m_closed || !m_callable) {
+        promise-&gt;reject(INVALID_STATE_ERR);
+        return;
+    }
+
+    // 3. If response is an empty array, return a promise rejected with a newly created TypeError.
+    if (!response.length()) {
+        promise-&gt;reject(TypeError);
+        return;
+    }
+
+    // 4. Let response copy be a copy of the contents of the response parameter.
+    // 5. Let promise be a new promise.
+    // 6. Run the following steps in parallel:
+    m_taskQueue.enqueueTask([this, response = SharedBuffer::create(response.data(), response.length()), promise = WTFMove(promise)] () mutable {
+        // 6.1. Let sanitized response be a validated and/or sanitized version of response copy.
+        RefPtr&lt;SharedBuffer&gt; sanitizedResponse = m_implementation-&gt;sanitizeResponse(response);
+
+        // 6.2. If the preceding step failed, or if sanitized response is empty, reject promise with a newly created TypeError.
+        if (!sanitizedResponse || sanitizedResponse-&gt;isEmpty()) {
+            promise-&gt;reject(TypeError);
+            return;
+        }
+
+        CDMInstance::LicenseType licenseType;
+        switch (m_sessionType) {
+        case MediaKeySessionType::Temporary:
+            licenseType = CDMInstance::LicenseType::Temporary;
+            break;
+        case MediaKeySessionType::PersistentLicense:
+            licenseType = CDMInstance::LicenseType::Persistable;
+            break;
+        case MediaKeySessionType::PersistentUsageRecord:
+            licenseType = CDMInstance::LicenseType::UsageRecord;
+            break;
+        };
+
+        // 6.3. Let message be null.
+        // 6.4. Let message type be null.
+        // 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-&gt;updateLicense(licenseType, *sanitizedResponse, [this, weakThis = m_weakPtrFactory.createWeakPtr(), promise = WTFMove(promise)] (bool sessionWasClosed, std::optional&lt;CDMInstance::KeyStatusVector&gt;&amp;&amp; changedKeys, std::optional&lt;double&gt;&amp;&amp; changedExpiration, std::optional&lt;CDMInstance::Message&gt;&amp;&amp; message, CDMInstance::SuccessValue succeeded) mutable {
+            if (!weakThis)
+                return;
+
+            // 6.7.1. If the format of sanitized response is invalid in any way, reject promise with a newly created TypeError.
+            // 6.7.2. Process sanitized response, following the stipulation for the first matching condition from the following list:
+            //   ↳ If sanitized response contains a license or key(s)
+            //     Process sanitized response, following the stipulation for the first matching condition from the following list:
+            //     ↳ If sessionType is &quot;temporary&quot; and sanitized response does not specify that session data, including any license, key(s), or similar session data it contains, should be stored
+            //       Process sanitized response, not storing any session data.
+            //     ↳ If sessionType is &quot;persistent-license&quot; and sanitized response contains a persistable license
+            //       Process sanitized response, storing the license/key(s) and related session data contained in sanitized response. Such data must be stored such that only the origin of this object's Document can access it.
+            //     ↳ If sessionType is &quot;persistent-usage-record&quot; and sanitized response contains a non-persistable license
+            //       Run the following steps:
+            //         6.7.2.3.1. Process sanitized response, not storing any session data.
+            //         6.7.2.3.2. If processing sanitized response results in the addition of keys to the set of known keys, add the key IDs of these keys to this object's record of key usage.
+            //     ↳ Otherwise
+            //       Reject promise with a newly created TypeError.
+            //   ↳ If sanitized response contains a record of license destruction acknowledgement and sessionType is &quot;persistent-license&quot;
+            //     Run the following steps:
+            //       6.7.2.1. Close the key session and clear all stored session data associated with this object, including the sessionId and record of license destruction.
+            //       6.7.2.2. Set session closed to true.
+            //   ↳ Otherwise
+            //     Process sanitized response, not storing any session data.
+            // NOTE: Steps 6.7.1. and 6.7.2. should be implemented in CDMInstance.
+
+            if (succeeded == CDMInstance::SuccessValue::Failed) {
+                promise-&gt;reject(TypeError);
+                return;
+            }
+
+            // 6.7.3. If a message needs to be sent to the server, execute the following steps:
+            //   6.7.3.1. Let message be that message.
+            //   6.7.3.2. Let message type be the appropriate MediaKeyMessageType for the message.
+            // 6.8. Queue a task to run the following steps:
+            m_taskQueue.enqueueTask([this, sessionWasClosed, changedKeys = WTFMove(changedKeys), changedExpiration = WTFMove(changedExpiration), message = WTFMove(message), promise = WTFMove(promise)] () mutable {
+                // 6.8.1.
+                if (sessionWasClosed) {
+                    // ↳ If session closed is true:
+                    //   Run the Session Closed algorithm on this object.
+                    sessionClosed();
+                } else {
+                    // ↳ Otherwise:
+                    //   Run the following steps:
+                    //     6.8.1.1. If the set of keys known to the CDM for this object changed or the status of any key(s) changed, run the Update Key Statuses
+                    //              algorithm on the session, providing each known key's key ID along with the appropriate MediaKeyStatus. Should additional
+                    //              processing be necessary to determine with certainty the status of a key, use &quot;status-pending&quot;. Once the additional processing
+                    //              for one or more keys has completed, run the Update Key Statuses algorithm again with the actual status(es).
+                    if (changedKeys)
+                        updateKeyStatuses(WTFMove(*changedKeys));
+
+                    //     6.8.1.2. If the expiration time for the session changed, run the Update Expiration algorithm on the session, providing the new expiration time.
+                    if (changedExpiration)
+                        updateExpiration(*changedExpiration);
+
+                    //     6.8.1.3. If any of the preceding steps failed, reject promise with a new DOMException whose name is the appropriate error name.
+                    // FIXME: At this point the implementations of preceding steps can't fail.
+
+                    //     6.8.1.4. If message is not null, run the Queue a &quot;message&quot; Event algorithm on the session, providing message type and message.
+                    if (message) {
+                        MediaKeyMessageType messageType;
+                        switch (message-&gt;first) {
+                        case CDMInstance::MessageType::LicenseRequest:
+                            messageType = MediaKeyMessageType::LicenseRequest;
+                            break;
+                        case CDMInstance::MessageType::LicenseRenewal:
+                            messageType = MediaKeyMessageType::LicenseRenewal;
+                            break;
+                        case CDMInstance::MessageType::LicenseRelease:
+                            messageType = MediaKeyMessageType::LicenseRelease;
+                            break;
+                        case CDMInstance::MessageType::IndividualizationRequest:
+                            messageType = MediaKeyMessageType::IndividualizationRequest;
+                            break;
+                        }
+
+                        enqueueMessage(messageType, WTFMove(message-&gt;second));
+                    }
+                }
+
+                // 6.8.2. Resolve promise.
+                promise-&gt;resolve();
+            });
+        });
+    });
+
+    // 7. Return promise.
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void MediaKeySession::close(Ref&lt;DeferredPromise&gt;&amp;&amp;)
</span><span class="lines">@@ -275,6 +409,21 @@
</span><span class="cx">     m_eventQueue.enqueueEvent(WTFMove(messageEvent));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void MediaKeySession::updateKeyStatuses(CDMInstance::KeyStatusVector&amp;&amp;)
+{
+    notImplemented();
+}
+
+void MediaKeySession::updateExpiration(double)
+{
+    notImplemented();
+}
+
+void MediaKeySession::sessionClosed()
+{
+    notImplemented();
+}
+
</ins><span class="cx"> bool MediaKeySession::hasPendingActivity() const
</span><span class="cx"> {
</span><span class="cx">     notImplemented();
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesencryptedmediaMediaKeySessionh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/encryptedmedia/MediaKeySession.h (211549 => 211550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/encryptedmedia/MediaKeySession.h        2017-02-02 03:10:58 UTC (rev 211549)
+++ trunk/Source/WebCore/Modules/encryptedmedia/MediaKeySession.h        2017-02-02 06:28:03 UTC (rev 211550)
</span><span class="lines">@@ -31,6 +31,7 @@
</span><span class="cx"> #if ENABLE(ENCRYPTED_MEDIA)
</span><span class="cx"> 
</span><span class="cx"> #include &quot;ActiveDOMObject.h&quot;
</span><ins>+#include &quot;CDMInstance.h&quot;
</ins><span class="cx"> #include &quot;EventTarget.h&quot;
</span><span class="cx"> #include &quot;GenericEventQueue.h&quot;
</span><span class="cx"> #include &quot;GenericTaskQueue.h&quot;
</span><span class="lines">@@ -46,7 +47,6 @@
</span><span class="cx"> 
</span><span class="cx"> class BufferSource;
</span><span class="cx"> class CDM;
</span><del>-class CDMInstance;
</del><span class="cx"> class MediaKeyStatusMap;
</span><span class="cx"> class MediaKeys;
</span><span class="cx"> class SharedBuffer;
</span><span class="lines">@@ -72,6 +72,9 @@
</span><span class="cx"> private:
</span><span class="cx">     MediaKeySession(ScriptExecutionContext&amp;, MediaKeySessionType, bool useDistinctiveIdentifier, Ref&lt;CDM&gt;&amp;&amp;, Ref&lt;CDMInstance&gt;&amp;&amp;);
</span><span class="cx">     void enqueueMessage(MediaKeyMessageType, const SharedBuffer&amp;);
</span><ins>+    void updateKeyStatuses(CDMInstance::KeyStatusVector&amp;&amp;);
+    void updateExpiration(double);
+    void sessionClosed();
</ins><span class="cx"> 
</span><span class="cx">     // EventTarget
</span><span class="cx">     EventTargetInterface eventTargetInterface() const override { return MediaKeySessionEventTargetInterfaceType; }
</span></span></pre></div>
<a id="trunkSourceWebCoretestingMockCDMFactorycpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/testing/MockCDMFactory.cpp (211549 => 211550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/testing/MockCDMFactory.cpp        2017-02-02 03:10:58 UTC (rev 211549)
+++ trunk/Source/WebCore/testing/MockCDMFactory.cpp        2017-02-02 06:28:03 UTC (rev 211550)
</span><span class="lines">@@ -177,6 +177,20 @@
</span><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+RefPtr&lt;SharedBuffer&gt; MockCDM::sanitizeResponse(const SharedBuffer&amp; response) const
+{
+    if (!charactersAreAllASCII(reinterpret_cast&lt;const LChar*&gt;(response.data()), response.size()))
+        return nullptr;
+
+    Vector&lt;String&gt; responseArray;
+    String(response.data(), response.size()).split(ASCIILiteral(&quot; &quot;), responseArray);
+
+    if (!responseArray.contains(String(ASCIILiteral(&quot;valid-response&quot;))))
+        return nullptr;
+
+    return response.copy();
+}
+
</ins><span class="cx"> MockCDMInstance::MockCDMInstance(WeakPtr&lt;MockCDM&gt; cdm)
</span><span class="cx">     : m_cdm(cdm)
</span><span class="cx"> {
</span><span class="lines">@@ -256,6 +270,28 @@
</span><span class="cx">     callback(SharedBuffer::create(license.data(), license.length()), sessionID, false, SuccessValue::Succeeded);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void MockCDMInstance::updateLicense(LicenseType, const SharedBuffer&amp; response, LicenseUpdateCallback callback)
+{
+    MockCDMFactory* factory = m_cdm ? m_cdm-&gt;factory() : nullptr;
+    if (!factory) {
+        callback(false, std::nullopt, std::nullopt, std::nullopt, SuccessValue::Failed);
+        return;
+    }
+
+    Vector&lt;String&gt; responseVector;
+    String(response.data(), response.size()).split(ASCIILiteral(&quot; &quot;), responseVector);
+
+    if (responseVector.contains(String(ASCIILiteral(&quot;invalid-format&quot;)))) {
+        callback(false, std::nullopt, std::nullopt, std::nullopt, SuccessValue::Failed);
+        return;
+    }
+
+    // FIXME: Session closure, key status, expiration and message handling should be implemented
+    // once the relevant algorithms are supported.
+
+    callback(false, std::nullopt, std::nullopt, std::nullopt, SuccessValue::Succeeded);
</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 (211549 => 211550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/testing/MockCDMFactory.h        2017-02-02 03:10:58 UTC (rev 211549)
+++ trunk/Source/WebCore/testing/MockCDMFactory.h        2017-02-02 06:28:03 UTC (rev 211550)
</span><span class="lines">@@ -113,6 +113,7 @@
</span><span class="cx">     bool supportsServerCertificates() const final;
</span><span class="cx">     bool supportsSessions() const final;
</span><span class="cx">     bool supportsInitData(const AtomicString&amp;, const SharedBuffer&amp;) const final;
</span><ins>+    RefPtr&lt;SharedBuffer&gt; sanitizeResponse(const SharedBuffer&amp;) const final;
</ins><span class="cx"> 
</span><span class="cx">     WeakPtr&lt;MockCDMFactory&gt; m_factory;
</span><span class="cx">     WeakPtrFactory&lt;MockCDM&gt; m_weakPtrFactory;
</span><span class="lines">@@ -128,6 +129,7 @@
</span><span class="cx">     SuccessValue setPersistentStateAllowed(bool) final;
</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><ins>+    void updateLicense(LicenseType, const SharedBuffer&amp;, LicenseUpdateCallback) 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>