[Bug 25199] New: EME should use Promises

https://www.w3.org/Bugs/Public/show_bug.cgi?id=25199

            Bug ID: 25199
           Summary: EME should use Promises
           Product: HTML WG
           Version: unspecified
          Hardware: All
                OS: All
            Status: NEW
          Severity: normal
          Priority: P1
         Component: Encrypted Media Extensions
          Assignee: adrianba@microsoft.com
          Reporter: ddorwin@google.com
        QA Contact: public-html-bugzilla@w3.org
                CC: mike@w3.org, public-html-media@w3.org
            Blocks: 17750, 21798, 24081, 24216, 24771

=== Summary ===
Most EME methods - setMediaKeys(), MediaKeys initialization, createSession(),
loadSession(), update(), and release() - should return a Promise to report the
result of their asynchronous operation.

>From the Abstract of the TAG’s Writing Promise-Using Specifications
(https://github.com/w3ctag/promises-guide): "Promises are now the web
platform's paradigm for all "one and done" asynchronous operations… Going
forward, all asynchronous operations of this type should be specified to
instead return promises..."


=== Analysis ===
All of the EME methods except isTypeSupported() and mediaKeys() are
asynchronous, primarily because they must interact with the CDM.

Although createSession(), loadSession(), update(), and release() calls could
also result in message events (not "one and done"), they do have a well-defined
one-time asynchronous algorithms that either succeed or fail.

Note that "Rejections Should Be Used for Exceptional Situations"
(https://github.com/w3ctag/promises-guide#rejections-should-be-used-for-exceptional-situations).
This means that failing to find a sessionId in loadSession() should not result
in a rejection. Instead, the promise should be rejected with "undefined". Other
failures - for example, how to handle unsupported |contentType| - is unclear
but may fall into the judgement call category.

"message" events are recurring, so they should remain events.
https://github.com/w3ctag/promises-guide#recurring-events
Errors during playback (i.e. resulting from license/key use) should also remain
events.


=== Current State ===
EME currently defines two categories of errors:
1) Errors directly resulting from a method call.
 * When to report these is defined in the algorithms.
 * These are further divided into:
    - Synchronous DOMExceptions reported by the UA.
    - Asynchronous error events (and MediaKeyError objects) generally
originating in the CDM.
2) Errors that may occur at any time and do not directly result from an EME
method call.
 * When to report these is *not* defined in the algorithms. (Like "message" and
"close" events, they may occur at any time for unspecified reasons.)
 * These are always reported asynchronously as error events (and MediaKeyError
objects) from the CDM.
 * This includes errors during decryption/playback of media data.
 * These could be considered status/notifications and/or message events
intended for the application instead of the license server. 

Issues:
* EME may report two different types of errors, meaning applications need to
handle both.
* Errors resulting from method calls (type 1) cannot be tied back to the call.
(This becomes an issue in the discussion of things like a store() method.)
* The use of MediaKeyError and the error event for all asynchronous errors
leads to a very strange error reporting mechanism for the MediaKeys constructor
(saving an error to be reported when a session is created; see
https://www.w3.org/Bugs/Public/show_bug.cgi?id=17750#c26 for further
complications).
* EME is not consistent with the "web platform's [new] paradigm for…
asynchronous operations."


=== Advantages of Using Promises in EME ===
Promises would allow us to:
* Have a consistent error reporting mechanism for method calls, simplifying
applications.
* Directly associate errors with the call that caused them, where applicable.
  - * This is even more important if we add additional methods (i.e. store())
that can report errors.
* Report errors on the right object during MediaKeys/CDM initialization.
  - And thus avoid saving error information and spinning the event loop in
other methods (https://www.w3.org/Bugs/Public/show_bug.cgi?id=17750#c26).
* Address the use case for the "open" event
(https://www.w3.org/Bugs/Public/show_bug.cgi?id=24081#c4).
* Simplify the algorithms - no more synchronous and asynchronous sections or
tasks.
* Explicitly separate playback-related errors from method call failures.
* Potentially simplify lifetime and/or event guarantees (bug 24771).
* Address the asynchronous nature of setMediaKeys (bug 24216).
* MediaKeySession states may be unnecessary.
  - The session object can’t be in the CREATED state because it is not provided
until it is OPEN.
  - ERROR doesn’t appear to be used for anything meaningful.
  - CLOSED could be replaced by "is closed" if necessary.


=== Changes ===

= Methods =
ALL EME methods except isTypeSupported() and mediaKeys() will return a Promise.

HTMLMediaElement.setMediaKeys():
* resolve: "undefined" to indicate a successful change of the attached
MediaKeys object.
   - This would occur after any related changes to the media stack, including
stopping decoders.
* reject: Error indicating the reason for failure, which is likely related to
the MediaKeys object being in use by another element or the UA not supporting
detaching of MediaKeys objects (see bug 24216).

MediaKeys construction:
* (Unless constructors can somehow return a Promise,) we can’t directly use a
Promise in the MediaKeys constructor, which involves asynchronous loading and
initialization of the CDM.
* We have two options:
  1) Add a static creation method that returns a Promise and remove the
constructor.
    Options for where to attach this static method:
       * MediaKeys.create()
       * Window.createMediaKeys()
       * HTMLMediaElement.createMediaKeys()
  2) Move the asynchronous portion of the constructor to an initialize() method
that returns a Promise
* I prefer the first option because it is more consistent with how
MediaKeySession objects are created and all applications will immediately call
initialize() anyway.
* resolve: Provides a MediaKeys object that is initialized and ready for
immediate use. (The CDM instance is also initialized.)
* reject: Error indicating the reason for failure, likely a CDM initialization
error.

MediaKeys.createSession():
* resolve: Provides a MediaKeySession object in the OPEN state with a valid
sessionId.
* reject: Error indicating the reason for failure.
* Note: The "message" event is fired *after* the Promise is resolved, allowing
the application to add an event listener.

MediaKeys.loadSession():
* resolve: Provides a MediaKeySession object in the OPEN state with a valid
sessionId and all session data loaded.
  - The provided value will be "undefined" if the sessionId was not found (per
https://github.com/w3ctag/promises-guide#rejections-should-be-used-for-exceptional-situations).
* reject: Error indicating the reason for failure.
* Note: Any "message" event is fired *after* the Promise is resolved, allowing
the application to add an event listener.

MediaKeySession.update():
* resolve: "undefined" to indicate successful processing of the |response|.
* reject: Error indicating the reason for failing to process |response|.

MediaKeySession.release():
* resolve: "undefined" to acknowledge the call. This includes when the session
is already CLOSED.
* reject: Error indicating the reason for failure.
* Note: The "close" event would be fired whenever the CDM decides and does not
delay settling the Promise.

= Events =
The "ready"-now-"open" event is removed from MediaKeySession.

The second category of errors should remain unchanged, continuing to fire an
"error" event at the session. There is no way to use a Promise for these in the
same way as the first category.

= Errors =
"Rejection Reasons Should Be _Error_s" and "you should never use DOMError, but
instead use DOMException, which per WebIDL extends Error."
  -- https://github.com/w3ctag/promises-guide#rejection-reasons-should-be-error
(There is also this WebIDL bug 21740, which would appear to be resolved by the
previous link.)

It appears that DOMExceptions can have any name (see the second Note at
http://heycam.github.io/webidl/#idl-exceptions). If we still need to report
system codes in the Promise rejection, we may be able to further extend
DOMException.

The "error" event would continue to report a MediaKeyError, though this might
now inherit from DOMException. (We could - but do not necessarily need to - use
the same type of object for the two categories of errors.)

Since the use of Promises means that not all categories of errors will result
in an error event and corresponding setting of the error attribute, we may want
to reconsider the current behavior of using a simple event with error
attribute. We may instead want to fire a complex event or use some other
mechanism.


=== Other Possible Changes ===
The following are changes that are not strictly necessary but that we may wish
to consider while changing the APIs.

= Session Creation =
We may also want to reconsider session creation and allow an empty
MediaKeySession to be created without generating a license request, loading,
etc.

In this scenario:
* void MediaKeys.createSession(), which would take no parameters, returns an
empty MediaKeySession in the CREATED state.
* Promise<TBD> session.generateRequest(DOMString contentType, Uint8Array
initData) would execute the existing createSession() algorithm except for the
construction of the new MediaKeySession object (step 6).
* Promise<TBD> session.load(DOMString sessionId) would likewise execute the
existing loadSession() algorithm except for the construction of the new
MediaKeySession object (step 3).

I’m not sure which is better for applications. If, for some reason, the
resolving of createSession()’s Promise is too late for applications to add
event listeners and still receive the "message" event, we might be forced into
this model.

If we went with this model, we might also consider separating MediaKeys
construction from initialization to maintain consistency.

= Events =
* The "close" event could potentially become a One-Time "Event"
(https://github.com/w3ctag/promises-guide#one-time-events)
* If we do end up with some type of session ready event, it should be a
One-Time "Event".
* The "error" event could instead be a "status" event, especially depending on
the outcome of bug 21798. (The remaining "errors" may not actually indicate
fatal errors.)
* We could convert the "close" event into a "status"/"error" event, leaving
just this event and the the "message" event.

-- 
You are receiving this mail because:
You are the QA Contact for the bug.

Received on Saturday, 29 March 2014 00:39:07 UTC