[EME] Bug 25200 - Add optional sessionType parameter to createSession()
authorDavid Dorwin <ddorwin@google.com>
Mon, 21 Apr 2014 15:34:58 -0700
changeset 285 469d33da106f
parent 284 a2a618cc49a5
child 286 b8934774026e
[EME] Bug 25200 - Add optional sessionType parameter to createSession()

Includes a new example using this parameter and loadSession().
encrypted-media/encrypted-media.html
encrypted-media/encrypted-media.xml
--- a/encrypted-media/encrypted-media.html	Mon Apr 21 14:13:13 2014 -0700
+++ b/encrypted-media/encrypted-media.html	Mon Apr 21 15:34:58 2014 -0700
@@ -322,7 +322,6 @@
 
     <pre class="idl">
 enum <dfn id="dom-mediawaitingfor">MediaWaitingFor</dfn> { "<dfn id="dom-waitingfornone">none</dfn>", "<dfn id="dom-waitingfordata">data</dfn>", "<dfn id="dom-waitingforkey">key</dfn>" };
-
 partial interface <dfn id="dom-htmlmediaelement">HTMLMediaElement</dfn> {
   // Encrypted Media
   readonly attribute <a href="#dom-mediakeys">MediaKeys</a> <a href="#dom-attrmediakeys">mediaKeys</a>;
@@ -333,10 +332,11 @@
   readonly attribute <a href="#dom-mediawaitingfor">MediaWaitingFor</a> <a href="#dom-waitingfor">waitingFor</a>;
 };
 
+enum SessionType { "temporary", "persistent" };
 interface <dfn id="dom-mediakeys">MediaKeys</dfn> {
   readonly attribute DOMString <a href="#dom-keysystem">keySystem</a>;
 
-  Promise&lt;<a href="#dom-mediakeysession">MediaKeySession</a>&gt; <a href="#dom-createsession">createSession</a>(DOMString initDataType, Uint8Array initData);
+  Promise&lt;<a href="#dom-mediakeysession">MediaKeySession</a>&gt; <a href="#dom-createsession">createSession</a>(DOMString initDataType, Uint8Array initData, optional SessionType sessionType = "temporary");
   Promise&lt;<a href="#dom-mediakeysession">MediaKeySession</a>&gt; <a href="#dom-loadsession">loadSession</a>(DOMString sessionId);
 
   static Promise&lt;<a href="#dom-mediakeys">MediaKeys</a>&gt; <a href="#dom-create">create</a>(DOMString <a href="#key-system">keySystem</a>)
@@ -451,7 +451,7 @@
 
     <p>The <dfn id="dom-keysystem"><code>keySystem</code></dfn> attribute identifies the <a href="#key-system">Key System</a> being used.</p>
 
-    <p>The <dfn id="dom-createsession"><code>createSession(initDataType, initData)</code></dfn> method must run the following steps:</p>
+    <p>The <dfn id="dom-createsession"><code>createSession(initDataType, initData, sessionType)</code></dfn> method must run the following steps:</p>
     <p class="non-normative">Note: The contents of <var title="true">initData</var> are container-specific <a href="#initialization-data">Initialization Data</a>.
     <var title="true">initDataType</var> is the <a href="#initialization-data-type">initialization data type</a> that indicates how to interpret <var title="true">initData</var>. 
     </p>
@@ -463,6 +463,7 @@
       <li><p>If <var title="true">initDataType</var> is an empty string, return a promise rejected with a new <code><a href="http://www.w3.org/TR/dom/#exception-domexception">DOMException</a></code> whose name is <code><a href="#dfn-InvalidAccessError">"InvalidAccessError"</a></code> and that has the message "The initDataType parameter is empty."</p></li>
       <li><p>If <var title="true">initData</var> is an empty array, return a promise rejected with a new <code><a href="http://www.w3.org/TR/dom/#exception-domexception">DOMException</a></code> whose name is<code><a href="#dfn-InvalidAccessError">"InvalidAccessError"</a></code> and that has the message "The initData parameter is empty."</p></li>
       <li><p>If <var title="true">initDataType</var> is not an <a href="#initialization-data-type">initialization data type</a> supported by the <a href="#cdm">content decryption module</a> corresponding to the <code><a href="#dom-keysystem">keySystem</a></code>, return a promise rejected with a new <code><a href="http://www.w3.org/TR/dom/#exception-domexception">DOMException</a></code> whose name is <code><a href="#dfn-NotSupportedError">"NotSupportedError"</a></code> and that has the message "The initialization data type <var title="true">initDataType</var> is not supported by the key system."</p></li>
+      <li><p>If <var title="true">sessionType</var> is not supported by the <a href="#cdm">content decryption module</a> corresponding to the <code><a href="#dom-keysystem">keySystem</a></code>, return a promise rejected with a new <code><a href="http://www.w3.org/TR/dom/#exception-domexception">DOMException</a></code> whose name is <code><a href="#dfn-NotSupportedError">"NotSupportedError"</a></code> and that has the message "<var title="true">sessionType</var> sessions are not supported by the key system."</p></li>
       <li><p>Let <var>promise</var> be a new promise.</p></li>
       <li>
 <p>Run the following steps asynchronously:</p>
@@ -478,7 +479,10 @@
 <p>If a message exchange <span class="non-normative">(e.g. a license request)</span> is required:</p>
                 <ol>
                   <li>
-<p>Let <var title="true">request</var> be a request generated by the <a href="#cdm">CDM</a> using the <var title="true">initData</var>.</p>
+<p>Let <var title="true">request</var> be a request generated by the <a href="#cdm">CDM</a> using the <var title="true">initData</var> and <var title="true">sessionType</var>.</p>
+                    <p>If <var title="true">sessionType</var> is <code>"temporary"</code>, the request is for a temporary non-persisted license.
+                    If <var title="true">sessionType</var> is <code>"persistent"</code>, the request is for a persistable license.</p>
+                    <p class="non-normative">Note: The license server may reject the requested type. It should not issue a different type.</p>
                     <p><var title="true">cdm</var> must not use any stream-specific data, including <a href="http://www.w3.org/TR/html5/embedded-content-0.html#media-data">media data</a>, not provided via the <var title="true">initData</var>.</p>
                   </li>
                   <li><p>If the <var title="true">initData</var> indicates a default URL relevant to <var title="true">keySystem</var>, let <var title="true">default URL</var> be that URL.</p></li>
@@ -493,6 +497,7 @@
               <li><p>Set the <code><a href="#dom-error">error</a></code> attribute to null.</p></li>
               <li><p>Set the <code><a href="#dom-sessionkeysystem">keySystem</a></code> attribute to the value of the <code><a href="#dom-mediakeys">MediaKeys</a></code> object's <code><a href="#dom-keysystem">keySystem</a></code> attribute.</p></li>
               <li><p>Set the <code><a href="#dom-sessionid">sessionId</a></code> attribute to <var title="true">session ID</var>.</p></li>
+              <li><p>Let the session type be <var title="true">sessionType</var>.</p></li>
             </ol>
           </li>
           <li><p>If any of the preceding steps failed, reject <var>promise</var> with a new <code><a href="http://www.w3.org/TR/dom/#exception-domexception">DOMException</a></code> whose name is the appropriate <a href="#mediakeyerror-names">error name</a> and that has an appropriate message.</p></li>
@@ -537,6 +542,7 @@
               <li><p>Set the <code><a href="#dom-error">error</a></code> attribute to null.</p></li>
               <li><p>Set the <code><a href="#dom-sessionkeysystem">keySystem</a></code> attribute to the value of the <code><a href="#dom-mediakeys">MediaKeys</a></code> object's <code><a href="#dom-keysystem">keySystem</a></code> attribute.</p></li>
               <li><p>Set the <code><a href="#dom-sessionid">sessionId</a></code> attribute to <var title="true">sessionId</var>.</p></li>
+              <li><p>Let the session type be <code>"persistent"</code>.</p></li>
             </ol>
           </li>
           <li><p>If any of the preceding steps failed, reject <var>promise</var> with a new <code><a href="http://www.w3.org/TR/dom/#exception-domexception">DOMException</a></code> whose name is the appropriate <a href="#mediakeyerror-names">error name</a> and that has an appropriate message.</p></li>
@@ -611,7 +617,16 @@
 <p>Use the <var title="true">cdm</var> to execute the following steps:</p>
             <ol>
               <li>
-<p>Process <var title="true">response</var>.</p>
+<p>Process <var title="true">response</var></p>
+                <p>If <var title="true">response</var> contains keys, a license, or similar data, follow the stipulation for the first matching condition from the following list:</p>
+                <dl class="switch">
+                  <dt>If <var title="true">sessionType</var> is <code>"temporary"</code> and the license does not specify it should be stored</dt>
+                  <dd>Do not store the license or other data contained in <var title="true">response</var>.</dd>
+                  <dt>If <var title="true">sessionType</var> is <code>"persistent"</code> and the license permits storage</dt>
+                  <dd>Store the license or other data contained in <var title="true">response</var>.</dd>
+                  <dt>Otherwise</dt>
+                  <dd>Fail with an appropriate <a href="#mediakeyerror-names">error name</a> and message.</dd>
+                </dl>
                 <p class="non-normative">Note: When <var title="true">response</var> contains key(s) and/or related data, <var title="true">cdm</var> will likely cache the key and related data indexed by key ID.</p>
                 <p class="non-normative">Note: The replacement algorithm within a session is <a href="#key-system">Key System</a>-dependent.</p>
                 <p class="non-normative">Note: Keys from different sessions should be cached independently such that closing one session does not affect keys in other sessions, even if they have overlapping key IDs.</p>
@@ -694,6 +709,7 @@
             The existing MediaKeys object cannot be removed.<br>
             The key system <em>name</em> is not supported.<br>
             The initialization data type <em>type</em> is not supported by the key system.<br>
+            <em>type</em> sessions are not supported by the key system.<br>
             The operation is not supported by the key system.
           </td>
         </tr>
@@ -1074,6 +1090,7 @@
     <p class="non-normative">The CDM may close a session at any point, such as in response to a <code><a href="#dom-release">release()</a></code> call, when the session is no longer needed, or when resources are lost.
     Keys in other sessions should be unaffected, even if they have overlapping key IDs.
     </p>
+    <p>If <var title="true">sessionType</var> is <code>"persistent"</code>, the CDM must have cleared all stored session data associated with the <code><a href="#dom-mediakeysession">MediaKeySession</a></code> object.</p>
     <p>The following steps are run:</p>
     <ol>
       <li><p>Let <var>promise</var> be the <code><a href="#dom-close">close</a></code> attribute of the associated <code><a href="#dom-mediakeysession">MediaKeySession</a></code> object.</p></li>
@@ -1494,6 +1511,92 @@
 &lt;video id="v" autoplay on<a href="#dom-needkey">needkey</a>="handleKeyNeeded(event)"&gt;&lt;/video&gt;</pre>
     </div>
 
+    <h3 id="example-stored-license" class="exampleheader">8.5. Stored License</h3>
+    <p class="exampledescription">This example requests a persistent license for future use and stores it. It also provides functions for later retrieving the license and for destroying it.</p>
+
+    <div class="example">
+      <pre class="code">
+&lt;script&gt;
+  var keySystem;
+  var licenseUrl;
+  var mediaKeys;
+
+  // See the previous examples for implementations of these functions.
+  function selectKeySystem() { ... }
+  function sendMessage(message, keySession) { ... }
+  function handleMessage(event) { ... }
+  function handleError(event) { ... }
+
+  // Called if the application does not have a stored sessionId for the media resource.
+  function createSession(mediaKeys, initDataType, initData) {
+    mediaKeys.<a href="#dom-createsession">createSession</a>(initDataType, initData, "persistent").then(
+      function(keySession) {
+        keySession.addEventListener("<a href="#dom-eventmessage">message</a>", handleMessage, false);
+        keySession.addEventListener("<a href="#dom-eventerror">error</a>", handleError, false);
+        keySession.close.then(
+          console.log.bind(console, "Session closed")
+        );
+        // Store keySession.<a href="#dom-sessionid">sessionId</a> in the application.
+      }
+    ).catch(
+      console.error.bind(console, "Unable to create or initialize a persistable key session")
+    );
+  }
+
+  // Called if the application has a stored sessionId for the media resource.
+  function loadStoredSession(mediaKeys, sessionId) {
+    mediaKeys.<a href="#dom-loadsession">loadSession</a>(sessionId).then(
+      function(keySession) {
+        if (!keySession) {
+          console.error("No stored session with the ID " + sessionId + " was found.");
+          return;
+        }
+        keySession.addEventListener("<a href="#dom-eventmessage">message</a>", handleMessage, false);
+        keySession.addEventListener("<a href="#dom-eventerror">error</a>", handleError, false);
+        keySession.close.then(
+          console.log.bind(console, "Session closed")
+        );
+      }
+    ).catch(
+      console.error.bind(console, "Unable to load or initialize the stored session with the ID " + sessionId)
+    );
+  }
+
+  // Called when the application wants to remove the license for the media resource.
+  removeStoredSession(keySession) {
+    keySession.<a href="#dom-release">release</a>().catch(
+      console.error.bind(console, "Unable to release the session")
+    );
+  }
+
+  // This replaces the implementation in the previous example.
+  function handleMessageResponse(keySession, response) {
+    var license = new Uint8Array(response);
+    keySession.<a href="#dom-update">update</a>(license).then(
+      function() {
+        // If this was the last required message from the server, the license is
+        // now stored. Update the application state as appropriate.
+      }
+    ).catch(
+      console.error.bind(console, "update() failed")
+    );
+  }
+
+  selectKeySystem();
+  <a href="#dom-mediakeys">MediaKeys</a>.<a href="#dom-create">create</a>(keySystem).then(
+    function(createdMediaKeys) {
+      mediaKeys = createdMediaKeys;
+      var video = document.getElementById("v");
+      return video.<a href="#dom-setmediakeys">setMediaKeys</a>(mediaKeys);
+    }
+  ).catch(
+    console.error.bind(console, "Unable to create or use new MediaKeys")
+  );
+&lt;/script&gt;
+
+&lt;video id="v" src="foo.webm" autoplay&gt;&lt;/video&gt;</pre>
+    </div>
+
 
     <h2 id="revision-history">9. Revision History</h2>
     <table>
--- a/encrypted-media/encrypted-media.xml	Mon Apr 21 14:13:13 2014 -0700
+++ b/encrypted-media/encrypted-media.xml	Mon Apr 21 15:34:58 2014 -0700
@@ -319,7 +319,6 @@
 
     <pre class="idl">
 enum <precodedfn>MediaWaitingFor</precodedfn> { "<precodedfn prefix="waitingfor">none</precodedfn>", "<precodedfn prefix="waitingfor">data</precodedfn>", "<precodedfn prefix="waitingfor">key</precodedfn>" };
-
 partial interface <precodedfn>HTMLMediaElement</precodedfn> {
   // Encrypted Media
   readonly attribute <precoderef>MediaKeys</precoderef> <precoderef prefix="attr">mediaKeys</precoderef>;
@@ -330,10 +329,11 @@
   readonly attribute <precoderef>MediaWaitingFor</precoderef> <precoderef>waitingFor</precoderef>;
 };
 
+enum SessionType { "temporary", "persistent" };
 interface <precodedfn>MediaKeys</precodedfn> {
   readonly attribute DOMString <precoderef>keySystem</precoderef>;
 
-  Promise&lt;<precoderef>MediaKeySession</precoderef>&gt; <premethodref>createSession</premethodref>(DOMString initDataType, Uint8Array initData);
+  Promise&lt;<precoderef>MediaKeySession</precoderef>&gt; <premethodref>createSession</premethodref>(DOMString initDataType, Uint8Array initData, optional SessionType sessionType = "temporary");
   Promise&lt;<precoderef>MediaKeySession</precoderef>&gt; <premethodref>loadSession</premethodref>(DOMString sessionId);
 
   static Promise&lt;<precoderef>MediaKeys</precoderef>&gt; <premethodref>create</premethodref>(DOMString <a href="#key-system">keySystem</a>)
@@ -438,7 +438,7 @@
 
     <p>The <codedfn>keySystem</codedfn> attribute identifies the <a href="#key-system">Key System</a> being used.</p>
 
-    <p>The <methoddfn name="createSession">createSession(<var title="true">initDataType</var>, <var title="true">initData</var>)</methoddfn> method must run the following steps:</p>
+    <p>The <methoddfn name="createSession">createSession(<var title="true">initDataType</var>, <var title="true">initData</var>, <var title="true">sessionType</var>)</methoddfn> method must run the following steps:</p>
     <p class="non-normative">Note: The contents of <var title="true">initData</var> are container-specific <a href="#initialization-data">Initialization Data</a>.
     <var title="true">initDataType</var> is the <a href="#initialization-data-type">initialization data type</a> that indicates how to interpret <var title="true">initData</var>. 
     </p>
@@ -450,6 +450,7 @@
       <li><p>If <var title="true">initDataType</var> is an empty string, return a promise rejected with a new <code><dom4ref name="exception-domexception">DOMException</dom4ref></code> whose name is <code><a href="#dfn-InvalidAccessError">"InvalidAccessError"</a></code> and that has the message "The initDataType parameter is empty."</p></li>
       <li><p>If <var title="true">initData</var> is an empty array, return a promise rejected with a new <code><dom4ref name="exception-domexception">DOMException</dom4ref></code> whose name is<code><a href="#dfn-InvalidAccessError">"InvalidAccessError"</a></code> and that has the message "The initData parameter is empty."</p></li>
       <li><p>If <var title="true">initDataType</var> is not an <a href="#initialization-data-type">initialization data type</a> supported by the <a href="#cdm">content decryption module</a> corresponding to the <coderef>keySystem</coderef>, return a promise rejected with a new <code><dom4ref name="exception-domexception">DOMException</dom4ref></code> whose name is <code><a href="#dfn-NotSupportedError">"NotSupportedError"</a></code> and that has the message "The initialization data type <var title="true">initDataType</var> is not supported by the key system."</p></li>
+      <li><p>If <var title="true">sessionType</var> is not supported by the <a href="#cdm">content decryption module</a> corresponding to the <coderef>keySystem</coderef>, return a promise rejected with a new <code><dom4ref name="exception-domexception">DOMException</dom4ref></code> whose name is <code><a href="#dfn-NotSupportedError">"NotSupportedError"</a></code> and that has the message "<var title="true">sessionType</var> sessions are not supported by the key system."</p></li>
       <li><p>Let <var>promise</var> be a new promise.</p></li>
       <li><p>Run the following steps asynchronously:</p>
         <ol>
@@ -461,7 +462,10 @@
               <li><p>Process the <var title="true">initData</var>, interpreting it per <var title="true">initDataType</var>.</p></li>
               <li><p>If a message exchange <span class="non-normative">(e.g. a license request)</span> is required:</p>
                 <ol>
-                  <li><p>Let <var title="true">request</var> be a request generated by the <a href="#cdm">CDM</a> using the <var title="true">initData</var>.</p>
+                  <li><p>Let <var title="true">request</var> be a request generated by the <a href="#cdm">CDM</a> using the <var title="true">initData</var> and <var title="true">sessionType</var>.</p>
+                    <p>If <var title="true">sessionType</var> is <code>"temporary"</code>, the request is for a temporary non-persisted license.
+                    If <var title="true">sessionType</var> is <code>"persistent"</code>, the request is for a persistable license.</p>
+                    <p class="non-normative">Note: The license server may reject the requested type. It should not issue a different type.</p>
                     <p><var title="true">cdm</var> must not use any stream-specific data, including <videoanchor name="media-data">media data</videoanchor>, not provided via the <var title="true">initData</var>.</p>
                   </li>
                   <li><p>If the <var title="true">initData</var> indicates a default URL relevant to <var title="true">keySystem</var>, let <var title="true">default URL</var> be that URL.</p></li>
@@ -475,6 +479,7 @@
               <li><p>Set the <coderef>error</coderef> attribute to null.</p></li>
               <li><p>Set the <coderef prefix="session">keySystem</coderef> attribute to the value of the <coderef>MediaKeys</coderef> object's <coderef>keySystem</coderef> attribute.</p></li>
               <li><p>Set the <coderef>sessionId</coderef> attribute to <var title="true">session ID</var>.</p></li>
+              <li><p>Let the session type be <var title="true">sessionType</var>.</p></li>
             </ol>
           </li>
           <li><p>If any of the preceding steps failed, reject <var>promise</var> with a new <code><dom4ref name="exception-domexception">DOMException</dom4ref></code> whose name is the appropriate <a href="#mediakeyerror-names">error name</a> and that has an appropriate message.</p></li>
@@ -515,6 +520,7 @@
               <li><p>Set the <coderef>error</coderef> attribute to null.</p></li>
               <li><p>Set the <coderef prefix="session">keySystem</coderef> attribute to the value of the <coderef>MediaKeys</coderef> object's <coderef>keySystem</coderef> attribute.</p></li>
               <li><p>Set the <coderef>sessionId</coderef> attribute to <var title="true">sessionId</var>.</p></li>
+              <li><p>Let the session type be <code>"persistent"</code>.</p></li>
             </ol>
           </li>
           <li><p>If any of the preceding steps failed, reject <var>promise</var> with a new <code><dom4ref name="exception-domexception">DOMException</dom4ref></code> whose name is the appropriate <a href="#mediakeyerror-names">error name</a> and that has an appropriate message.</p></li>
@@ -583,7 +589,16 @@
           <li><p>Let <var title="true">destination URL</var> be null.</p></li>
           <li><p>Use the <var title="true">cdm</var> to execute the following steps:</p>
             <ol>
-              <li><p>Process <var title="true">response</var>.</p>
+              <li><p>Process <var title="true">response</var></p>
+                <p>If <var title="true">response</var> contains keys, a license, or similar data, follow the stipulation for the first matching condition from the following list:</p>
+                <dl class="switch">
+                  <dt>If <var title="true">sessionType</var> is <code>"temporary"</code> and the license does not specify it should be stored</dt>
+                  <dd>Do not store the license or other data contained in <var title="true">response</var>.</dd>
+                  <dt>If <var title="true">sessionType</var> is <code>"persistent"</code> and the license permits storage</dt>
+                  <dd>Store the license or other data contained in <var title="true">response</var>.</dd>
+                  <dt>Otherwise</dt>
+                  <dd>Fail with an appropriate <a href="#mediakeyerror-names">error name</a> and message.</dd>
+                </dl>
                 <p class="non-normative">Note: When <var title="true">response</var> contains key(s) and/or related data, <var title="true">cdm</var> will likely cache the key and related data indexed by key ID.</p>
                 <p class="non-normative">Note: The replacement algorithm within a session is <a href="#key-system">Key System</a>-dependent.</p>
                 <p class="non-normative">Note: Keys from different sessions should be cached independently such that closing one session does not affect keys in other sessions, even if they have overlapping key IDs.</p>
@@ -663,6 +678,7 @@
             The existing MediaKeys object cannot be removed.<br/>
             The key system <em>name</em> is not supported.<br/>
             The initialization data type <em>type</em> is not supported by the key system.<br/>
+            <em>type</em> sessions are not supported by the key system.<br/>
             The operation is not supported by the key system.
           </td>
         </tr>
@@ -1019,6 +1035,7 @@
     <p class="non-normative">The CDM may close a session at any point, such as in response to a <methodref>release</methodref> call, when the session is no longer needed, or when resources are lost.
     Keys in other sessions should be unaffected, even if they have overlapping key IDs.
     </p>
+    <p>If <var title="true">sessionType</var> is <code>"persistent"</code>, the CDM must have cleared all stored session data associated with the <coderef>MediaKeySession</coderef> object.</p>
     <p>The following steps are run:</p>
     <ol>
       <li><p>Let <var>promise</var> be the <coderef>close</coderef> attribute of the associated <coderef>MediaKeySession</coderef> object.</p></li>
@@ -1437,6 +1454,92 @@
 &lt;video id="v" autoplay on<precoderef>needkey</precoderef>="handleKeyNeeded(event)"&gt;&lt;/video&gt;</pre>
     </div>
 
+    <h3 id="example-stored-license" class="exampleheader">8.5. Stored License</h3>
+    <p class="exampledescription">This example requests a persistent license for future use and stores it. It also provides functions for later retrieving the license and for destroying it.</p>
+
+    <div class="example">
+      <pre class="code">
+&lt;script&gt;
+  var keySystem;
+  var licenseUrl;
+  var mediaKeys;
+
+  // See the previous examples for implementations of these functions.
+  function selectKeySystem() { ... }
+  function sendMessage(message, keySession) { ... }
+  function handleMessage(event) { ... }
+  function handleError(event) { ... }
+
+  // Called if the application does not have a stored sessionId for the media resource.
+  function createSession(mediaKeys, initDataType, initData) {
+    mediaKeys.<premethodref>createSession</premethodref>(initDataType, initData, "persistent").then(
+      function(keySession) {
+        keySession.addEventListener("<precoderef prefix="event">message</precoderef>", handleMessage, false);
+        keySession.addEventListener("<precoderef prefix="event">error</precoderef>", handleError, false);
+        keySession.close.then(
+          console.log.bind(console, "Session closed")
+        );
+        // Store keySession.<precoderef>sessionId</precoderef> in the application.
+      }
+    ).catch(
+      console.error.bind(console, "Unable to create or initialize a persistable key session")
+    );
+  }
+
+  // Called if the application has a stored sessionId for the media resource.
+  function loadStoredSession(mediaKeys, sessionId) {
+    mediaKeys.<premethodref>loadSession</premethodref>(sessionId).then(
+      function(keySession) {
+        if (!keySession) {
+          console.error("No stored session with the ID " + sessionId + " was found.");
+          return;
+        }
+        keySession.addEventListener("<precoderef prefix="event">message</precoderef>", handleMessage, false);
+        keySession.addEventListener("<precoderef prefix="event">error</precoderef>", handleError, false);
+        keySession.close.then(
+          console.log.bind(console, "Session closed")
+        );
+      }
+    ).catch(
+      console.error.bind(console, "Unable to load or initialize the stored session with the ID " + sessionId)
+    );
+  }
+
+  // Called when the application wants to remove the license for the media resource.
+  removeStoredSession(keySession) {
+    keySession.<premethodref>release</premethodref>().catch(
+      console.error.bind(console, "Unable to release the session")
+    );
+  }
+
+  // This replaces the implementation in the previous example.
+  function handleMessageResponse(keySession, response) {
+    var license = new Uint8Array(response);
+    keySession.<premethodref>update</premethodref>(license).then(
+      function() {
+        // If this was the last required message from the server, the license is
+        // now stored. Update the application state as appropriate.
+      }
+    ).catch(
+      console.error.bind(console, "update() failed")
+    );
+  }
+
+  selectKeySystem();
+  <precoderef>MediaKeys</precoderef>.<premethodref>create</premethodref>(keySystem).then(
+    function(createdMediaKeys) {
+      mediaKeys = createdMediaKeys;
+      var video = document.getElementById("v");
+      return video.<premethodref>setMediaKeys</premethodref>(mediaKeys);
+    }
+  ).catch(
+    console.error.bind(console, "Unable to create or use new MediaKeys")
+  );
+&lt;/script&gt;
+
+&lt;video id="v" src="foo.webm" autoplay&gt;&lt;/video&gt;</pre>
+    </div>
+
 
     <h2 id="revision-history">9. Revision History</h2>
     <table>