This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 26372 - Report issues/events not related to a specific method call
Summary: Report issues/events not related to a specific method call
Status: VERIFIED FIXED
Alias: None
Product: HTML WG
Classification: Unclassified
Component: Encrypted Media Extensions (show other bugs)
Version: unspecified
Hardware: All All
: P2 normal
Target Milestone: LC
Assignee: David Dorwin
QA Contact: HTML WG Bugzilla archive list
URL:
Whiteboard: API_Compatibility
Keywords:
: 21798 (view as bug list)
Depends on:
Blocks:
 
Reported: 2014-07-17 18:20 UTC by David Dorwin
Modified: 2017-04-13 23:12 UTC (History)
16 users (show)

See Also:


Attachments

Description David Dorwin 2014-07-17 18:20:38 UTC
This bug is a reboot of (and replaces) bug 21798 in light of the many changes to the spec since that bug was filed.

*** Summary ***
 * Many scenarios are now covered by promises and DOMException names (section I.A.).
 * MediaKeyError (in its current form) will be eliminated (bug 25896).
 * There are a few remaining event-type scenarios to handle (section I.B.).
 * A simple “error” event with a corresponding attribute does not seem to make sense (section II.).
 * There are few, if any, new EME-specific DOMException names needed (section I.). However, we may need to define a new set of enum values if we adopt a non-error solution (section II.).
 * We can always start simple / with no new names or enums and add functionality if implementation experience shows it is useful and possible (see also comment #19). It will be easier to add such functionality than to remove it once it's deployed.


*** I. Scenarios ***
The following sections list some of the errors and other events that UAs and CDMs might want to report to applications. Many of the previous use cases for MediaKeyError have been replaced with promises and normative text in the algorithms that defines which DOMException name to use. These are covered in the first section.

** A. Errors reported via promises **
The following scenarios are handled by promises returned by EME methods, especially MediaKeys.create() and MediaKey.createSession(). Where relevant, the normatively-defined error name or the normative text that allows the proposed error name is specified. For these "common" scenarios, we may want to add explicit normative steps with these error names to avoid relying on “appropriate error name”.

Note that most conditions that result in NotSupportedError should also result in a negative response from isTypeSupported(), especially if it is made asynchronous (bug 25923), providing the opportunity to check more of these conditions.

1. CDM is not supported: NotSupportedError (normative)
2. CDM is not installed/available: NotSupportedError (“appropriate error name” when “cdm fails to load or initialize”)
3. CDM is disabled: NotSupportedError (“appropriate error name” when “cdm fails to load or initialize”)
4. CDM was denied necessary permissions (e.g. unique identifier): NotSupportedError (“appropriate error name” when “cdm fails to load or initialize”)
5. CDM could not be loaded/instantiated: NotSupportedError? (“appropriate error name” when “cdm fails to load or initialize”)
6. Invalid license (or other response) format: InvalidAccessError (normative)
7. Other failures (i.e. fatally failed to find or use root of trust): TBD - If no other appropriate value, potentially UnknownError (defined by Indexed DB)

** B. Issues reported asynchronously during playback ***
The following are issues/errors that will occur during media playback rather than as the result of EME calls. These would (currently - see section II.) be reported via the “error” attribute when an “error” event is reported.

Note that these are not fatal to playback. Playback can resume when the issue is addressed.
Note also that some are or will be addressed in other ways.
We might be able to replace the generic “error” event with some other more specific and useful mechanisms.

Key availability / valid window:
1. No key (key not available): addressed by waiting and waitingFor.
2. Expired key (key was available but its use has expired): TimeoutError? Do we need an error or is this the same as “no key”?
3. Expired key due to unsuccessful key renewal or heartbeat: same as #2
4. Outside the allowed window of time: Key is available but it’s valid time is in the future: TBD. (#2 "Expired key" could also fall into this category or absorb the future case.)
5. Number of playbacks exceeded (and similar policies): treat as #2 “expired”?
6. Instructed to stop by license server: treat as #2 “expired”?

Note that all of the above _could_ be handled by waiting/waitingFor, at least for now.

Output protection:
7. Key available but could not be used due to output protection requirements that are not met: TBD. Maybe we should see how the other output protection-related issues are addressed before defining a new EME-specific name.
8. Content is being downscaled due to output protection requirements of an available key that are not currently met: TBD, but not really an error.


*** II. Error/Event Reporting ***
As discussed in section I.B., such events are not fatal “errors” and may potentially be covered by other EME APIs. In addition, it’s unclear when the “error” attribute should be cleared or how to handle/report multiple errors.

We could make the “error” event a non-simple event so that it can include a DOMException. I’m not sure how this fits with the architectural direction for error reporting. We could decide that these are really informational and thus do not need to use DOMException.

With the key availability / valid window scenarios being potentially covered by waiting/waitingFor and no clarity in how to handle output protection, maybe it is easiest to skip reporting such events for now.
Comment 1 David Dorwin 2014-07-17 18:22:17 UTC
*** Bug 21798 has been marked as a duplicate of this bug. ***
Comment 2 David Dorwin 2014-07-19 01:27:46 UTC
https://dvcs.w3.org/hg/html-media/rev/3368787fe08e updates the issue boxes to refer to this issue and removes related definitions that seem likely to change once this bug is resolved.
Comment 3 Joe Steele 2014-07-21 16:18:11 UTC
Re: key availability -- 
I think cases 2, 3 and 5 will all result in the same action by the application e.g. a new key request. 
I think case 1 will result in the same action, but is useful to keep separate because it can tell the application the difference between never having received a key and having received a bad key. 
I think cases 4 and 7 are unique, as the application may be able to do something to allow playing but what will vary depending on the application. 
Case 6 is the hardest. I believe in most cases this means the content will never be playable with the current configuration and an update to the UA/CDM is required.  

Re: error reporting -- 
I don't like the general error attribute. It was useful before the Promises changes were implemented, but now I think it makes more sense to have status information per key (as you described) and then messaging when that status changes.
Comment 4 Joe Steele 2014-07-21 16:39:21 UTC
For the CDM loading error conditions, how would a systemCode get propagated?
Comment 5 David Dorwin 2014-08-12 23:36:20 UTC
(In reply to Joe Steele from comment #4)
> For the CDM loading error conditions, how would a systemCode get propagated?

Without subclassing DOMException, which is discouraged (bug 25896), it's not possible to report anything other than the error name and message in the rejected promise.
Comment 6 David Dorwin 2014-08-12 23:54:40 UTC
Since the eventual solution is unlikely to involve an error event, an error attribute, or MediaKeyError object, I removed them entirely (along with all but one of the related Issue boxes) in https://dvcs.w3.org/hg/html-media/rev/c9bcd3f67fc2.

I've also changed the summary of this bug to reflect the main open issue.
Comment 7 David Dorwin 2014-08-19 15:39:20 UTC
Since there are only a few such issues/events, the resolution of this issue might be one or more specific solutions rather than a generic event with a code.
Comment 8 Joe Steele 2014-08-25 16:59:13 UTC
I am not sure where we are on this, but I am thinking that an informational error event as discussed in section II is still a good option. One that does not include DOMException, but does include a system code in addition to the generic error it represents.
Comment 9 Mark Watson 2014-08-26 15:01:25 UTC
Some way to report a systemCode is essential. Our implementation experience is that it would have been impossible to debug our site without access to low level error information.
Comment 10 David Dorwin 2014-09-02 21:42:36 UTC
Since the remaining events (key availability / valid window, output protection) affect the usable keys, we could add a reason enum to the "keyschange" event (it would become a custom event instead of a simple event).

Playback issues are already handled by HTMLMediaElement. That leaves key issues as the only remaining types of issues for MediaKeySession (MediaKeys) to handle. Since an issue with the key will result in it being unusable, it makes sense to report this along with the "keyschange" event. (If a different license has the same key ID and it is usable, there is no reason to report anything.)
Comment 11 Joe Steele 2014-09-02 22:44:33 UTC
(In reply to David Dorwin from comment #10)
> Since the remaining events (key availability / valid window, output
> protection) affect the usable keys, we could add a reason enum to the
> "keyschange" event (it would become a custom event instead of a simple
> event).
> 
> Playback issues are already handled by HTMLMediaElement. That leaves key
> issues as the only remaining types of issues for MediaKeySession (MediaKeys)
> to handle. Since an issue with the key will result in it being unusable, it
> makes sense to report this along with the "keyschange" event. (If a
> different license has the same key ID and it is usable, there is no reason
> to report anything.)

This sounds like it would conflict with the current definition of the getUsableKeyIds() method, section 3.2.1. "usable key ids must not contain IDs for keys that may not currently be usable.". Those keys which are not usable due to an error would presumably not be in the list, so it would be up to the application to figure out which key caused the error the reason refers to. 

Do we think there are cases where knowing which key caused the error is relevant?

Assuming the answer is no, this sounds reasonable to me. I would like to include "downscaling" as one of the options (perhaps as a subset of output protection).
Comment 12 David Dorwin 2014-09-02 23:10:53 UTC
(In reply to Joe Steele from comment #11)
> (In reply to David Dorwin from comment #10)
> > Since the remaining events (key availability / valid window, output
> > protection) affect the usable keys, we could add a reason enum to the
> > "keyschange" event (it would become a custom event instead of a simple
> > event).
> > 
> > Playback issues are already handled by HTMLMediaElement. That leaves key
> > issues as the only remaining types of issues for MediaKeySession (MediaKeys)
> > to handle. Since an issue with the key will result in it being unusable, it
> > makes sense to report this along with the "keyschange" event. (If a
> > different license has the same key ID and it is usable, there is no reason
> > to report anything.)
> 
> This sounds like it would conflict with the current definition of the
> getUsableKeyIds() method, section 3.2.1. "usable key ids must not contain
> IDs for keys that may not currently be usable.". Those keys which are not
> usable due to an error would presumably not be in the list, so it would be
> up to the application to figure out which key caused the error the reason
> refers to. 

There is no real indication of which key caused the problem, and I do not think it is guaranteed that an application could determine this in all cases (i.e. multiple successive events).

> Do we think there are cases where knowing which key caused the error is
> relevant?

We have never had such a capability, so I do not think we would be losing anything.

> Assuming the answer is no, this sounds reasonable to me. I would like to
> include "downscaling" as one of the options (perhaps as a subset of output
> protection).

That would potentially address bug 25092.

However, it would imply that downscaling means that a key is "not usable", which could be confusing since the CDM is actually using it for decryption.
Comment 13 Joe Steele 2014-09-04 18:24:25 UTC
(In reply to David Dorwin from comment #12)
> (In reply to Joe Steele from comment #11)
> > (In reply to David Dorwin from comment #10)
> > Assuming the answer is no, this sounds reasonable to me. I would like to
> > include "downscaling" as one of the options (perhaps as a subset of output
> > protection).
> 
> That would potentially address bug 25092.
> 
> However, it would imply that downscaling means that a key is "not usable",
> which could be confusing since the CDM is actually using it for decryption.

I don't think it implies that. I think it is "less usable", but still usable. The ability to include a status with the usableKeys gives us a scale of usability - from completely unfettered, to usable but with restrictions, to completely unusable. 

This is the only example of "less usable" that I know I would use, but I can imagine others. For example, the CDM could report that output protection is required or that the expiration of the key is immanent. Those could be useful to the application when deciding how to handle errors.
Comment 14 David Dorwin 2014-09-08 21:08:20 UTC
(In reply to Joe Steele from comment #13)
> (In reply to David Dorwin from comment #12)
> > (In reply to Joe Steele from comment #11)
> > > (In reply to David Dorwin from comment #10)
> > > Assuming the answer is no, this sounds reasonable to me. I would like to
> > > include "downscaling" as one of the options (perhaps as a subset of output
> > > protection).
> > 
> > That would potentially address bug 25092.
> > 
> > However, it would imply that downscaling means that a key is "not usable",
> > which could be confusing since the CDM is actually using it for decryption.
> 
> I don't think it implies that. I think it is "less usable", but still
> usable. The ability to include a status with the usableKeys gives us a scale
> of usability - from completely unfettered, to usable but with restrictions,
> to completely unusable. 

I was referring to the key's removal from the array returned by getUsableKeyIds(). A keyschange event implies that the list has changed. Firing keyschange just to report an enum value does not seem appropriate.

> This is the only example of "less usable" that I know I would use, but I can
> imagine others. For example, the CDM could report that output protection is
> required or that the expiration of the key is immanent. Those could be
> useful to the application when deciding how to handle errors.

Wouldn't output protection required and not present result in an unusable key?
I don't believe "imminent expiration" should be handled by this mechanism. We already have the expiration attribute, and we'd have to define "imminent."


Reporting downscaling with just an enum wouldn't be future-proof. Today, downscaled probably means the application should fetch a 480p stream instead, but what if there were multiple levels of downscaling? (See bug 25092#c21 for another possible solution.)
Comment 15 David Dorwin 2014-09-08 21:43:39 UTC
(In reply to David Dorwin from comment #10)
> Since the remaining events (key availability / valid window, output
> protection) affect the usable keys, we could add a reason enum to the
> "keyschange" event (it would become a custom event instead of a simple
> event).

An alternative would be to replace getUsableKeyIds() with something like getKeyStatuses(), which would resolve with an array of key ID and status enum pairs. For simplicity, it might make more sense to have separate arrays of key IDs and matching status enums.

This would allow applications to match the status to individual key(s), but it also requires a bit more logic to simply determine whether a key is "usable."
Comment 16 Joe Steele 2014-09-08 21:57:20 UTC
(In reply to David Dorwin from comment #14)
> (In reply to Joe Steele from comment #13)
> > (In reply to David Dorwin from comment #12)
> > > (In reply to Joe Steele from comment #11)
> > > > (In reply to David Dorwin from comment #10)
> > > > Assuming the answer is no, this sounds reasonable to me. I would like to
> > > > include "downscaling" as one of the options (perhaps as a subset of output
> > > > protection).
> > > 
> > > That would potentially address bug 25092.
> > > 
> > > However, it would imply that downscaling means that a key is "not usable",
> > > which could be confusing since the CDM is actually using it for decryption.
> > 
> > I don't think it implies that. I think it is "less usable", but still
> > usable. The ability to include a status with the usableKeys gives us a scale
> > of usability - from completely unfettered, to usable but with restrictions,
> > to completely unusable. 
> 
> I was referring to the key's removal from the array returned by
> getUsableKeyIds(). A keyschange event implies that the list has changed.
> Firing keyschange just to report an enum value does not seem appropriate.

This seems like an appropriate use to me. The usability of the key has changed. It is not a binary change, but more of a sliding scale. 

> 
> > This is the only example of "less usable" that I know I would use, but I can
> > imagine others. For example, the CDM could report that output protection is
> > required or that the expiration of the key is immanent. Those could be
> > useful to the application when deciding how to handle errors.
> 
> Wouldn't output protection required and not present result in an unusable
> key? I don't believe "imminent expiration" should be handled by this mechanism.
> We already have the expiration attribute, and we'd have to define "imminent."

Fair enough -- I was just throwing these out there. 
 
> Reporting downscaling with just an enum wouldn't be future-proof. Today,
> downscaled probably means the application should fetch a 480p stream
> instead, but what if there were multiple levels of downscaling? (See bug
> 25092#c21 for another possible solution.)

I think we should talk about the proposal in bug 25092 in the telco tomorrow.
Comment 17 David Dorwin 2014-09-16 23:50:39 UTC
There are currently two proposals (comment #15), both of which involve an enum. The first provides an indication of the reason for the change in status. The second provides the current status for each key ID. We should pick one and move forward. I will probably do so soon.

#1) Add an enum to the "keyschange" event that indicates the reason it was fired.
Pros:
 * Simple to implement and use.
Cons:
 * No indication of which key(s) the reason refers to.
 * Requires defining another custom event type instead of reusing Event.
 * Multiple such events:
  - They may need to be correlated to get a full picture.
  - getUsableKeyIds() may not reflect the reason in the event.

#2) Replace getUsableKeyIds() with a function that returns a list of all known key IDs along with their current status.
For example: Promise<sequence<Object>> getKeyStatuses();
Pros:
 * Exposes detailed information about the current state to the application.
 * No loss of information due to multiple changes and related events.
 * It seems more extensible.
Cons:
 * More complexity for CDM implementations.
 * More processing for applications.
 * Must return a Sequence of pairs or two Sequences. (The correct solution for returning such data in a Promise is TBD.)

In the example above the Object would be a JSON object with two fields: the base64url-encoded key ID and the current status as an enum value.


In either case, we also need to determine the enum values. At a minimum, we need some type of "license window exceeded" value and "outputprotection".

The former could be broken out into "expired", "renewalfailed", "outsidewindow" or "notyetvalid", "playbacksexceeded", and/or other values. We need to provide sufficient granularity for applications without baking in specific policies or implicitly ruling out other policies in the future.

We could also have a value for key IDs that have been encountered in media data but are unknown to the session. This would indicate that the correct license has not yet been provided or the license contained the wrong key IDs.

The default value should be the empty string, which will allow it to be used as a Boolean just like the canPlayType() result.
Comment 18 Mark Watson 2014-09-17 00:50:47 UTC
Can't this be an attribute with type Sequence<KeyStatus> where KeyStatus is an interface with attributes keyid (ArrayBuffer) and status (enum) ?

There could then be a simple event when this changes.

Or (IIUC) the attribute could be [Observable], if that is not too new-fangled.
Comment 19 Joe Steele 2014-09-17 17:04:52 UTC
I propose something between #1 and #2. 

Let's create a keystatus event which includes a status enum, a key ID and a system code. The CDM would send this event any time a keys status enum changes. This would supersede keyschange and getUsableKeyIds().

Pro - 
* Easy to consume on the app side
* More flexible for apps
* Easier to implement for browsers/CDMs

Con - 
* App has to track all such events if it cares
* App has to maintain state structure if it cares
* Key statuses might overlap with other errors?

I suggest the following enum values:
"acquired",
"expired", 
"notyetvalid",
"renewalfailed", 
"playbacksexceeded",
"authorizationfailed",
"outputnotallowed",
"downscaling",
"released"
Comment 20 David Dorwin 2014-09-17 17:26:02 UTC
(In reply to Mark Watson from comment #18)
> Can't this be an attribute with type Sequence<KeyStatus> where KeyStatus is
> an interface with attributes keyid (ArrayBuffer) and status (enum) ?

In bug 25594, we decided to use a Promise-returning method instead of an attribute. See that bug for the relevant discussion.

That said, we could return Promise<sequence<KeyStatus>> where KeyStatus is as you defined. It seems a bit of an overkill to define an interface just for this, but some architectural guidance would be helpful here.

> There could then be a simple event when this changes.
> 
> Or (IIUC) the attribute could be [Observable], if that is not too
> new-fangled.

Do you have a reference for [Observable]? I'd like to understand how that might apply.
Comment 21 Mark Watson 2014-09-17 17:40:08 UTC
(In reply to David Dorwin from comment #20)
> (In reply to Mark Watson from comment #18)
> > Can't this be an attribute with type Sequence<KeyStatus> where KeyStatus is
> > an interface with attributes keyid (ArrayBuffer) and status (enum) ?
> 
> In bug 25594, we decided to use a Promise-returning method instead of an
> attribute. See that bug for the relevant discussion.
> 
> That said, we could return Promise<sequence<KeyStatus>> where KeyStatus is
> as you defined. It seems a bit of an overkill to define an interface just
> for this, but some architectural guidance would be helpful here.

Agreed.

In 25594, the problem was that you cannot have a variable-length array or a sequence as an attribute type: you have to define a getter method instead (I believe to make it clear that you will get a copy of the object, not a reference to a 'live' object).

I guess we could still have a synchronous getter method and an event to indicate when the value is going to change.

It just seems the promise model is wrong for something which can asynchronously change multiple times, rather than something which just takes time to compute.

> 
> > There could then be a simple event when this changes.
> > 
> > Or (IIUC) the attribute could be [Observable], if that is not too
> > new-fangled.
> 
> Do you have a reference for [Observable]? I'd like to understand how that
> might apply.

I forget where I saw a reference to it. I believe http://wiki.ecmascript.org/doku.php?id=harmony:observe is getting attention right now and [Observable] is an idea for an IDL keyword that would indicate that the Object.observe pattern can be used with an attribute. I expect Boris and Anne know more.
Comment 22 David Dorwin 2014-09-17 18:28:11 UTC
There are a lot of options even within the three main proposals. I think we need some input from authors that intend to use this information on which approach, formats, etc. would be most useful.

(In reply to Joe Steele from comment #19)
> I propose something between #1 and #2. 
> 
> Let's create a keystatus event which includes a status enum, a key ID and a
> system code. The CDM would send this event any time a keys status enum
> changes. This would supersede keyschange and getUsableKeyIds().

Thanks. This is definitely an approach to consider. Let's call it #3.

For reference, below is possible IDL for this custom event type.

enum MediaKeyStatus { ... };
interface MediaKeysChangeEvent : Event {
  readonly attribute MediaKeyStatus status;
  readonly attribute sequence<ArrayBuffer> keyIds;
};

Note that I did not include the systemCode (see below) and I've used a sequence of keyIds (as discussed in my concerns below). Also, I'm not sure whether we should represent key IDs with an ArrayBuffer or base64url-encoded string (see below). Any of these could be addressed with minimal changes to the IDL below.

Regarding system code, let's see how bug 26776 is resolved. Any interface-based solutions would allow it to be added if that is deemed appropriate. However, the system code seems less important here, especially if we have fairly detailed enum values, than category I.A. errors.

Most of the proposals use an ArrayBuffer to return the key ID in binary. This makes sense on the surface, but I'm not sure it's the best solution for applications. If the application wants to track events, it might be simpler to just compare and/or index by a base64url-encoded representation of the key ID. It may even use this format to communicate to the server. Returning a DOMString or string representation in a JSON Object may make more sense for applications and avoid unnecessary conversions. Since the purpose of this event/method is to allow the application to understand and manage the returned data, I think it's important that the data is easy for the application to use.


> Pro - 
> * Easy to consume on the app side
> * More flexible for apps
> * Easier to implement for browsers/CDMs
> 
> Con - 
> * App has to track all such events if it cares
> * App has to maintain state structure if it cares
> * Key statuses might overlap with other errors?
Do you have specific overlaps in mind? I don't think we have overlap between I.A. and I.B. of comment #0.

Other concerns:
* The number of events that would be fired, especially when a license is first acquired and there are many keys (i.e. key rotation scenario). At least these events should be fired rarely.
* While per-key events might be helpful in some cases (i.e. "I can use the high resolution stream now"), it could be difficult for applications to get a "snapshot" of the current state. When handling any specific instance of the event, the application won't know if there are more events to come. Specifically, a single change in state (i.e. expiration or release) could cause multiple events to be fired.
* Using an array for the key ID (see above).
* The CDM must always do the work to generate the event details even if the application is not listening to the event. (This is one reason we decided to use a Promise in bug 25594 rather than just returning the array.) I'm not sure how much of a burden this is (beyond the work involved in triggering the basic "keyschange" event).

One possible solution to my second concern would be for the custom event type to have a Sequence of key IDs instead of a single ID.

> I suggest the following enum values:
> "acquired",
Is this the default valid case? How about "valid" or "usable"?
> "expired", 
> "notyetvalid",
> "renewalfailed", 
> "playbacksexceeded",
> "authorizationfailed",
What does this mean? (Remember, this is a per key status.)
> "outputnotallowed",
> "downscaling",
> "released"
Comment 23 Joe Steele 2014-09-17 19:06:40 UTC
(In reply to David Dorwin from comment #22)
> There are a lot of options even within the three main proposals. I think we
> need some input from authors that intend to use this information on which
> approach, formats, etc. would be most useful.
> 
> (In reply to Joe Steele from comment #19)
> > I propose something between #1 and #2. 
> > 
> > Let's create a keystatus event which includes a status enum, a key ID and a
> > system code. The CDM would send this event any time a keys status enum
> > changes. This would supersede keyschange and getUsableKeyIds().
> 
> Thanks. This is definitely an approach to consider. Let's call it #3.
> 
> For reference, below is possible IDL for this custom event type.
> 
> enum MediaKeyStatus { ... };
> interface MediaKeysChangeEvent : Event {
>   readonly attribute MediaKeyStatus status;
>   readonly attribute sequence<ArrayBuffer> keyIds;
> };
> 
> Note that I did not include the systemCode (see below) and I've used a
> sequence of keyIds (as discussed in my concerns below). Also, I'm not sure
> whether we should represent key IDs with an ArrayBuffer or base64url-encoded
> string (see below). Any of these could be addressed with minimal changes to
> the IDL below.
> 
> Regarding system code, let's see how bug 26776 is resolved. Any
> interface-based solutions would allow it to be added if that is deemed
> appropriate. However, the system code seems less important here, especially
> if we have fairly detailed enum values, than category I.A. errors.

Ok.

> 
> Most of the proposals use an ArrayBuffer to return the key ID in binary.
> This makes sense on the surface, but I'm not sure it's the best solution for
> applications. If the application wants to track events, it might be simpler
> to just compare and/or index by a base64url-encoded representation of the
> key ID. It may even use this format to communicate to the server. Returning
> a DOMString or string representation in a JSON Object may make more sense
> for applications and avoid unnecessary conversions. Since the purpose of
> this event/method is to allow the application to understand and manage the
> returned data, I think it's important that the data is easy for the
> application to use.

I agree. I think base64 would work for us. 

> 
> 
> > Pro - 
> > * Easy to consume on the app side
> > * More flexible for apps
> > * Easier to implement for browsers/CDMs
> > 
> > Con - 
> > * App has to track all such events if it cares
> > * App has to maintain state structure if it cares
> > * Key statuses might overlap with other errors?
> Do you have specific overlaps in mind? I don't think we have overlap between
> I.A. and I.B. of comment #0.

I was thinking of DOMExceptions like InvalidAccessError. In the case of a corrupted response, the Adobe CDM could throw the DOMException and/or it could send a keystatus event with "renewalfailed". Probably don't want to do both, and I would favor the former, but that might be true for everyone.

> 
> Other concerns:
> * The number of events that would be fired, especially when a license is
> first acquired and there are many keys (i.e. key rotation scenario). At
> least these events should be fired rarely.
> * While per-key events might be helpful in some cases (i.e. "I can use the
> high resolution stream now"), it could be difficult for applications to get
> a "snapshot" of the current state. When handling any specific instance of
> the event, the application won't know if there are more events to come.
> Specifically, a single change in state (i.e. expiration or release) could
> cause multiple events to be fired.
> * Using an array for the key ID (see above).
> * The CDM must always do the work to generate the event details even if the
> application is not listening to the event. (This is one reason we decided to
> use a Promise in bug 25594 rather than just returning the array.) I'm not
> sure how much of a burden this is (beyond the work involved in triggering
> the basic "keyschange" event).

This does not seem like much additional work for the CDM. I believe the browser can choose not to propagate this event if there are no listeners, but you may have more insight there. 

> 
> One possible solution to my second concern would be for the custom event
> type to have a Sequence of key IDs instead of a single ID.
> 
> > I suggest the following enum values:
> > "acquired",
> Is this the default valid case? How about "valid" or "usable"?

Yes - this is the default "all is good" case. "valid" is probably better than "acquired" since it would make more sense to fire when a key transitioned from "notyetvalid" to "valid". 

> > "expired", 
> > "notyetvalid",
> > "renewalfailed", 
> > "playbacksexceeded",
> > "authorizationfailed",
> What does this mean? (Remember, this is a per key status.)

This is a way for the CDM to report that a key request failed due to an authorization error. None of the current DOMExceptions handle this case. E.g. - the content you are trying to play is no longer allowed on your platform.

> > "outputnotallowed",
> > "downscaling",
> > "released"
Comment 24 David Dorwin 2014-09-19 18:27:59 UTC
(In reply to Mark Watson from comment #21)
> (In reply to David Dorwin from comment #20)
> > (In reply to Mark Watson from comment #18)
> > > Or (IIUC) the attribute could be [Observable], if that is not too
> > > new-fangled.
> > 
> > Do you have a reference for [Observable]? I'd like to understand how that
> > might apply.
> 
> I forget where I saw a reference to it. I believe
> http://wiki.ecmascript.org/doku.php?id=harmony:observe is getting attention
> right now and [Observable] is an idea for an IDL keyword that would indicate
> that the Object.observe pattern can be used with an attribute. I expect
> Boris and Anne know more.

Although not currently in WebIDL, it sounds like it will be added soon so this may be an option. It's unclear how we would use it for this case, though.

Object.observe() allows observation of changes to objects, including property modification and array modification. I could be wrong, but it doesn't appear appropriate to make the entire MediaKeySession object observable when we really want to monitor elements of only one of its properties. (It might be useful for replacing the |expiration| and |closed| attributes, though.) Therefore, I think we'd need some other object exposed by MediaKeySession to be observable.

We could expose an observable array of key IDs from MediaKeySession. That would allow the application to observe 'add' and 'delete'. However, we'd either need an array of pairs or a map to handle 'update' of the MediaKeyStatus.

That might work, but WebIDL states that "any sequence returned from a platform object will be a copy" [1] and "if an array is read only, then it is also implicitly fixed length" [2]. It doesn't make sense for the application to be able to modify the array, but it seems unlikely that a copy could observe the underlying changes and the array must be variable length.

Maybe someone more familiar with Object.observe() and/or WebIDL can comment.

[1] http://heycam.github.io/webidl/#idl-sequence
[2] http://heycam.github.io/webidl/#idl-array
Comment 25 Boris Zbarsky 2014-09-19 18:39:01 UTC
> Although not currently in WebIDL, it sounds like it will be added soon

There's not really anything Web IDL can do with [Observable] (other than helpfully annotating which things should be).  The important part is JS engine support for Object.observe and the actual prose that defines the attribute in question creating observer records as needed.

> I could be wrong, but it doesn't appear
> appropriate to make the entire MediaKeySession object observable when we really
> want to monitor elements of only one of its properties.

There is no such thing as an "observable object".  The thing that can be observed is an (object, property name) pair.

It sounds like this case if falling under bug 23682 comment 0 item B.  The value of the property would be a JS array object.  If the value is supposed to change, the underlying implementation would create a new JS array object, set it, and send the corresponding change notification as needed.
Comment 26 David Dorwin 2014-09-22 21:13:08 UTC
(In reply to Boris Zbarsky from comment #25)
> > Although not currently in WebIDL, it sounds like it will be added soon
> 
> There's not really anything Web IDL can do with [Observable] (other than
> helpfully annotating which things should be).  The important part is JS
> engine support for Object.observe and the actual prose that defines the
> attribute in question creating observer records as needed.
> 
> > I could be wrong, but it doesn't appear
> > appropriate to make the entire MediaKeySession object observable when we really
> > want to monitor elements of only one of its properties.
> 
> There is no such thing as an "observable object".  The thing that can be
> observed is an (object, property name) pair.
> 
> It sounds like this case if falling under bug 23682 comment 0 item B.  The
> value of the property would be a JS array object.  If the value is supposed
> to change, the underlying implementation would create a new JS array object,
> set it, and send the corresponding change notification as needed.

In this case, this seems equivalent to a simple event with an attribute or method to query, right? We could use [observable] to indicate changes to the |expiration|, closed status, and keys, but is such a solution really better than having a simple event (or promise, as is the case for |closed|) for each one?


Another idea: What if MediaKeySession had a method named getKeys() that returned (a reference to) an array of key IDs that could then be observed. Would this allow the returned (reference to the) array to receive updates? I still think we'd need to have something like a map to allow us to report added/removed key IDs as well as changes to their status. Is there something like a map already defined that we could use?

Object.observe() allows applications to be informed of the exact change and take action, but it probably adds some TBD amount of complexity to the spec. Application developers, do you think the complexity is worthwhile?
Comment 27 Boris Zbarsky 2014-09-23 00:26:02 UTC
> In this case, this seems equivalent to a simple event with an attribute or
> method to query, right?

Yes, except it acts like "normal" JS objects from the API consumer's point of view.

> but is such a solution really better than having a simple event (or promise, as
> is the case for |closed|) for each one?

That's an API design decision in the end.

I would say that for state that is synchronously available and easily computed using Object.observe is probably the right way forward in terms of creating developer-friendly APIs.

For state that is not synchronously available, promises seem like the right approach.

> What if MediaKeySession had a method named getKeys() that
> returned (a reference to) an array of key IDs that could then be observed.

API like this is possible to create, but there are some gotchas that you need to be careful with.  For example, what would happen if the script mutated this array?  Would this affect other callers of getKeys(), or would each getKeys() call return a new array?  Both answers have some problems, fwiw: in the former case, if the return value of getKeys() can change you have to define exactly how the array is updated, while in the latter case if the value changes you have to update all the different arrays you ever returned.

> Is there something like a map already defined that we could use?

ES has a Map object, but I don't believe changes to the map are observable sanely via Object.observe.

> Object.observe() allows applications to be informed of the exact change and
> take action, but it probably adds some TBD amount of complexity to the spec.

I don't think it adds any more complexity than firing a DOM event, at least once someone writes a generic "send an observer notification" hook in the DOM spec.
Comment 28 David Dorwin 2014-09-24 01:15:04 UTC
(In reply to Boris Zbarsky from comment #27)
> > In this case, this seems equivalent to a simple event with an attribute or
> > method to query, right?
> 
> Yes, except it acts like "normal" JS objects from the API consumer's point
> of view.
> 
> > but is such a solution really better than having a simple event (or promise, as
> > is the case for |closed|) for each one?
> 
> That's an API design decision in the end.
> 
> I would say that for state that is synchronously available and easily
> computed using Object.observe is probably the right way forward in terms of
> creating developer-friendly APIs.
> 
> For state that is not synchronously available, promises seem like the right
> approach.

Thanks. My takeaway is that Object.observe() might be a good replacement for events that indicate a specific attribute has changed but probably does *not* fit the keys list use case well (if only due to the array mutability issues).

We could use it to add/replace simple events or our attributes (i.e. expiration), but it may still be better to stick with events for such a complex object and because it is more common for DOM objects.

> > What if MediaKeySession had a method named getKeys() that
> > returned (a reference to) an array of key IDs that could then be observed.
> 
> API like this is possible to create, but there are some gotchas that you
> need to be careful with.  For example, what would happen if the script
> mutated this array?  Would this affect other callers of getKeys(), or would
> each getKeys() call return a new array?  Both answers have some problems,
> fwiw: in the former case, if the return value of getKeys() can change you
> have to define exactly how the array is updated, while in the latter case if
> the value changes you have to update all the different arrays you ever
> returned.

Good points. Unless/until bug 23682 comment 0 item B is resolved, we should probably avoid this for the keys list.

> > Is there something like a map already defined that we could use?
> 
> ES has a Map object, but I don't believe changes to the map are observable
> sanely via Object.observe.
> 
> > Object.observe() allows applications to be informed of the exact change and
> > take action, but it probably adds some TBD amount of complexity to the spec.
> 
> I don't think it adds any more complexity than firing a DOM event, at least
> once someone writes a generic "send an observer notification" hook in the
> DOM spec.

The complexity I was referring to is the potential for new types (see below), observing a change and checking the new value (doesn't work - see below), gotchas like you described above, etc.


Can an observable object define the structure of its records? As an example, the array model doesn't work because we need names/values not positions and old values. (Without such information, you still need to get the entire array because the position and value could have changed again before you could query based on a record.)

If so, we might be able to solve some of these problems by defining a custom observable MediaKeyStatusMap interface whose records include the keyId (rather than index) and for type 'update' would include the new status value in addition to the old value.

(FYI, my main reference is the the examples at http://wiki.ecmascript.org/doku.php?id=harmony:observe_api_usage.)
Comment 29 Boris Zbarsky 2014-09-24 02:17:01 UTC
> As an example, the array model doesn't work because we need names/values not
> positions and old values.

In the array model, the change record would presumably have the old array and the new array, and the actual change would be a change in which array object is returned, not in-place mutations of an array object.
Comment 30 David Dorwin 2014-09-24 02:28:49 UTC
(In reply to Boris Zbarsky from comment #29)
> > As an example, the array model doesn't work because we need names/values not
> > positions and old values.
> 
> In the array model, the change record would presumably have the old array
> and the new array, and the actual change would be a change in which array
> object is returned, not in-place mutations of an array object.

That's one possible solution (if MediaKeySession was observable). However, it doesn't really help applications much. I'm assuming the main benefit of observable would be if the UA could tell applications that keyY was added and keyX's status changed. Otherwise, the application can just as easily track the old and new states using the current simple event and method model.

I think the ideal solution is something like the array model in the example [1] (not returning an actual array from MediaKeySession) but that provides different types of information. It might help to ignore MediaKeySession and focus on an object that represents a map of names (key IDs) to values (status enums). Could it be designed such that Object.observe() be used with that object to find out when a name is added/removed and when its value changes. If so, and once that's designed, we'd figure out how to expose that object from mediaKeySession.

[1] http://wiki.ecmascript.org/doku.php?id=harmony:observe_api_usage#array
Comment 31 Boris Zbarsky 2014-09-24 02:39:48 UTC
That question is better asked on public-script-coord, but that sounds a lot more like a Map than array....
Comment 32 David Dorwin 2014-09-25 21:35:48 UTC
I sent http://lists.w3.org/Archives/Public/public-html-media/2014Sep/0023.html seeking input on various options discussed in this bug along with some example possibilities. If the "change records" option is preferred, we can further investigate how best to implement it.
Comment 33 David Dorwin 2014-10-14 15:18:57 UTC
I will try to implement the maplike solution (http://lists.w3.org/Archives/Public/public-html-media/2014Oct/0003.html).
Comment 34 David Dorwin 2014-11-08 00:43:04 UTC
The current proposal is to remove getUsableKeyIds() and add the following:

enum MediaKeyStatus { "valid", "expired", "outputprohibited", ... };

interface MediaKeySession : EventTarget {
  ...
  readonly maplike<BufferSource, MediaKeyStatus>;
};

Note: BufferSource is a typedef for ArrayBuffer or ArrayBufferView.

"maplike" essentially adds a set of properties and methods to the object that contains it. An interface may only have one such declaration. (See http://heycam.github.io/webidl/#idl-maplike for details.) The effect is that MediaKeySession becomes a "collection of keys" (represented by key IDs and statuses.

Specifically, the following are implicitly added to the MediaKeySession interface. (Each item is presented as it would be used on a MediaKeySession named session.)

* session.size returns the number of keys the session is currently tracking.
* session.entries() returns an Iterator object for the pairs
  - Provides full access to all the key IDs and their associated key statuses.
* session.forEach(callback) calls the |callback| for each pair.
  - Another way to iterate through the pairs.
* session.get(keyId) returns the MediaKeyStatus of the key specified by the BufferSource |keyId|.
  - This could be used to check the status of a specific key.
* session.has(keyId) returns whether the session currently contains information about the key specified by the BufferSource |keyId|.
  - This could be used to determine whether the session has a key.
* session.keys() returns an Iterator object for the key IDs (ArrayBuffers).
  - The name "keys" could be confusing.
  - Fortunately, this is probably not very useful.
* session.values() returns an Iterator object for the statuses (MediaKeyStatus).
  - This could be used to quickly determine, for example, whether there are any keys that are not valid.
* symbol.iterator probably returns something similar to session.entries().

Note: The members could change while using an Iterator or in the middle of forEach()
Comment 35 David Dorwin 2014-11-10 23:58:14 UTC
(In reply to David Dorwin from comment #34)

Another option is to make the maplike object a member of MediaKeySession or to return it from a MediaKeySession method. This would separate the collection from the session. For example:

enum MediaKeyStatus { "valid", "expired", "outputprohibited", ... };

interface MediaKeyStatuses {
  readonly maplike<BufferSource, MediaKeyStatus>;
};

interface MediaKeySession : EventTarget {
  ...
  readonly MediaKeyStatuses keyStatuses;
};

This is the Web IDL equivalent of adding the following attribute:
  readonly map<BufferSource, MediaKeyStatus> keyStatuses;

Usage would then be, for example, session.keyStatuses.get(keyId).


This proposal is essentially the same as providing a sequence (discussed previously), but it provides a more useful interface for authors. It is also similar to what Boris suggested in http://lists.w3.org/Archives/Public/public-html-media/2014Sep/0025.html but we now have Web IDL support.

One of the issues with exposing the map as a member or from a member method is whether such a member returns the actual object, which can be updated, or a copy, which will not receive updates. (This is similar to the discussion in comment #24.) However, even with the proposal in comment #34, we were relying on the keyschange event to tell the application to look at the map. Thus, a copy doesn't seem so bad, especially if it prevents the members from changing as I noted in the last paragraph of comment #34.

Authors could still get unexpected results if they wrote iterative code that referenced the member repeatedly. For this reason and to allow asynchronous communication with the CDM (rather than the user agent caching the statuses), we should probably use a promise-returning method:

interface MediaKeySession : EventTarget {
  ...
  Promise<MediaKeyStatuses> getKeyStatuses();
};
Comment 36 Joe Steele 2014-11-11 04:37:14 UTC
(In reply to David Dorwin from comment #35)
> (In reply to David Dorwin from comment #34)
> Authors could still get unexpected results if they wrote iterative code that
> referenced the member repeatedly. For this reason and to allow asynchronous
> communication with the CDM (rather than the user agent caching the
> statuses), we should probably use a promise-returning method:
> 
> interface MediaKeySession : EventTarget {
>   ...
>   Promise<MediaKeyStatuses> getKeyStatuses();
> };

I think I like the maplike returning method better than adding these methods directly as part of the MediaKeySession. Having the Promise is nice, and it avoids the unfortunate "key" method you mentioned in the other case. I can't see any case where we would not be ok with a copy. 

However -- am I correct in assuming that the application needs to periodically poll this structure to determine if key status has changed? Or can this be monitored with Object.observe()?
Comment 37 David Dorwin 2014-11-12 22:52:36 UTC
(In reply to Joe Steele from comment #36)
> However -- am I correct in assuming that the application needs to
> periodically poll this structure to determine if key status has changed? Or
> can this be monitored with Object.observe()?

The keyschange event would still exist. Applications would check the new member in response to this event (or whenever they wish).

The MediaKeyStatuses object could be provided as part of the keyschange event, but that would require defining a custom event type (rather than just using a simple Event). I think Event is generally preferred.

If we used a member attribute like the following from comment #35:
  readonly MediaKeyStatuses keyStatuses;
it would theoretically be possible to (in the future) use Object.observe on the member as long we we did not specify that accesses to the member return a copy.
Comment 38 Boris Zbarsky 2014-11-12 22:58:04 UTC
You can't really Object.observe changes to a Map normally, no?   I mean, you could define that this maplike queues some kind of nonstandard change records...
Comment 39 David Dorwin 2014-11-12 23:31:15 UTC
I think we have agreement that a maplike structure mapping key ID to status should be exposed to applications and that it should be separate from MediaKeySession. The remaining question is how best to expose it to applications.

The basic questions we need to answer are:
1. Do we want to provide a copy?
 Pros:
  * A copy avoids changes while iterating through the list.
 Cons:
  * Either of the synchronous solutions could reintroduce this problem (i.e. repeated calls to session.getKeyStatuses() rather than calling it once and using the returned object).
  * Applications must continually obtain a copy if they want to check the statuses independent of the keyschange event (i.e. stream change logic).
  * Prevents observing the object (in the future).

2. Should access be synchronous?
 Pros:
  * Simple for applications.
  * Allows the statuses to be used in synchronous logic (i.e. stream changes) in addition to in response to keyschange events.
 Cons:
  * Forces user agent implementations to always receive/obtain the key statuses whenever there is a change (before firing the keyschange event).
   - How problematic would this be?
   - Are there implementations that would not want to do this?
  * Based on the Priority of Constituencies, the pro should outweigh the con. However, the answer might be different if the con results in a bad experience for users (i.e. performance).


We have the following three options, each of which has pros and cons related to the questions above.

A. Attribute: readonly MediaKeyStatuses keyStatuses;
Pros:
 * Simple for applications to use and can be used in synchronous logic.
 * Can provide a reference instead of a copy (if we choose to specify this in the algorithms).
  - This would allow MediaKeyStatuses to be made observable (in the future).
Cons:
 * It is not clear whether a copy is provided just by looking at the IDL.
 * Implementations must always have the latest list from the CDM, regardless of whether the application uses it.

B. Synchronous method: MediaKeyStatuses getKeyStatuses();
Pros:
 * Still simple for applications to use and can be used in synchronous logic.
 * Slightly more clear that a copy is being provided.
Cons:
 * It is still possible to write code where the list changes while iterating.
 * Implementations must always have the latest list from the CDM, regardless of whether the application uses it.
 * This would be the only MediaKeySession method that does not return a promise.
  - Would that be confusing for authors?

C. Asynchronous method: Promise<MediaKeyStatuses> getKeyStatuses();
 Pros:
  * Clear that a copy is being provided.
  * Implementations can choose to obtain the key statuses only when the application requests them.
  * Returning a promise is consistent with all other MediaKeySession methods.
 Cons:
  * Adds (likely) unnecessary complexity to applications.
   - In response to an asynchronous event, the application must make an asynchronous request for the data related to that event.
  * Could this add a very minor delay to the application?
Comment 40 David Dorwin 2014-11-12 23:37:09 UTC
(In reply to David Dorwin from comment #39)
> 2. Should access be synchronous?
...
I did not mean for this to appear under Cons. It is a general statement comparing the pro and con.
>   * Based on the Priority of Constituencies, the pro should outweigh the
> con. However, the answer might be different if the con results in a bad
> experience for users (i.e. performance).
Comment 41 Boris Zbarsky 2014-11-13 00:36:53 UTC
> A. Attribute: readonly MediaKeyStatuses keyStatuses;

Having this provide a copy (i.e. return a new object each time the getter is called) is an API antipattern that we should not perpetuate.  So this option presupposes that keyStatuses is a reference.  What it's a reference _to_ might change, though not until the event loop spins.

> B. Synchronous method: MediaKeyStatuses getKeyStatuses();

This could be returning a copy or a reference.  There is an IDL annotation for making it clear if it's a copy (i.e. a new object each time).

> C. Asynchronous method: Promise<MediaKeyStatuses> getKeyStatuses();

This, too, could be returning a copy or a reference, fwiw.  Both for the Promise itself and for its fulfillment value, so there are actually 3 possible behaviors here (can't really have a reference to a promise but a copy of the fulfillment value).
Comment 42 David Dorwin 2014-11-26 19:45:49 UTC
https://github.com/w3c/encrypted-media/commit/a7c7cb1d1d7140db2b7bbf61367e2ff8c120d278 implements option A from comment #39. It also explains that the attribute is a reference and when it can be updated.

I included a few status enum values. We can discuss additional values and any other concerns in separate GitHub issues.