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 25923 - The mechanism for checking key system support should be asynchronous
Summary: The mechanism for checking key system support should be asynchronous
Status: RESOLVED FIXED
Alias: None
Product: HTML WG
Classification: Unclassified
Component: Encrypted Media Extensions (show other bugs)
Version: unspecified
Hardware: PC All
: P2 normal
Target Milestone: LC
Assignee: David Dorwin
QA Contact: HTML WG Bugzilla archive list
URL:
Whiteboard: API_Compatibility
Keywords:
Depends on:
Blocks:
 
Reported: 2014-05-30 06:29 UTC by Anne
Modified: 2014-12-02 01:30 UTC (History)
10 users (show)

See Also:


Attachments

Description Anne 2014-05-30 06:29:30 UTC
isTypeSupported should be asynchronous to allow users to conditionally enable EME.
Comment 1 David Dorwin 2014-05-30 17:16:09 UTC
isTypeSupported() is intended to allow applications use multiple calls to quickly and systematically find a supported key system and media combination. I'm not sure how that would work if it was asynchronous.

Is isTypeSupported() a good time to prompt the user? Applications are likely to call it multiple times for various key systems and media types.

There might be other advantages to it being asynchronous and supporting prompts (i.e. preventing abuse of the information), but I'm not sure how to address the issues above, at least with the current design. Maybe a constraints/capabilities-based design would address this by allowing an application to provide all its supported configurations and get a response in one call.


For the specific scenario in comment #0, how about returning "maybe" from isTypeSupported() and waiting for MediaKeys.create() to prompt?
Comment 2 Anne 2014-05-30 17:33:01 UTC
Can't they make a bunch of calls simultaneously and use Promise.all() or some such to deal with synchronizing the results?

The main problem is that it's unclear when to prompt the user in certain scenarios, so anything that requires synchronous access to knowing whether there's in fact a CDM enabled is bad. We could mostly return "maybe", but that would make the API less useful for developers.
Comment 3 Boris Zbarsky 2014-05-30 17:34:07 UTC
> Is isTypeSupported() a good time to prompt the user?

Well, until the user has agreed (or not) to enabling EME you don't know whether the type is supported.  Will sites trying to use this API be OK if a UA always returns "maybe" from it, no matter what?  Seems unlikely to me.

> I'm not sure how that would work if it was asynchronous.

The same way it does with the sync version, but you make the next call from the then() handler, no?
Comment 4 David Dorwin 2014-05-30 18:11:53 UTC
(In reply to Anne from comment #2)
> Can't they make a bunch of calls simultaneously and use Promise.all() or
> some such to deal with synchronizing the results?

It seems that with Promise.all(), the checked value and handling would be separated, which could be difficult to maintain. Is there a better solution to the following? (Some names have been shortened for readability.)

Promise.all([iTS("system1", "webm"), iTS("system1", "cenc"), iTS("system2", "webm"), ...]).then(
  function(responses) {
    if (responses[0]) {
      keySystem = "system1";
      mediaType = "webm";
    } else if (responses[1]) {
      keySystem = "system1";
      mediaType = "cenc";
    } else if ...
    ...
});

If one of the calls is rejected for whatever reason, the application will just fail because it will have no information. The algorithm would need to use reject only for syntax problems.


Another advantage of the current implementation is the ability to drill down to a specific selection without trying all possible combinations. For example:

var keySystem;
if (MediaKeys.isTypeSupported("com.example")) {
  if (MediaKeys.isTypeSupported("com.example.somesystem")) {
    keySystem = "com.example.somesystem";
    if (MediaKeys.isTypeSupported("com.example.somesystem", "webm")) {
      if (MediaKeys.isTypeSupported("com.example.somesystem", "webm", "video/webm; codecs='vp8, vorbis'")) {
      ...
    } else if (MediaKeys.isTypeSupported("com.example.somesystem", "cenc")) {
      ...
  } else if (MediaKeys.isTypeSupported("com.example.othersystem")) {
    ...
  }
} else if (MediaKeys.isTypeSupported("com.foobar")) {
  keySystem = "com.foobar";
  ...
}


The ability to short circuit and not try all combinations would be an advantage if each system involved a separate prompt. There's no reason to prompt the user (as is the case with simultaneous calls) for something that isn't actually going to be used.
Comment 5 Anne 2014-05-30 18:32:09 UTC
I suspect there would only be a single prompt for enabling EME in general.

As for rejecting the promise, it seems like that should only happen in case of syntax errors, indeed. I.e. whenever the synchronous version would throw.
Comment 6 Domenic Denicola 2014-06-01 05:48:30 UTC
Promises work best in collaboration with other ES6 features. So the example code becomes much simpler and easier to read in that case:

Promise.all([iTS("system1", "webm"), iTS("system1", "cenc"), iTS("system2", "webm"), ...]).then(
  ([webm1, cenc1, webm2, ...]) => {
    if (webm1) {
      [keySystem, mediaType] = ["system1", "webm"];
    } else if (cenc1) {
      [keySystem, mediaType] = ["system1", "cenc"];
    } else if ...
    ...
  });
});

Of course I would imagine most programmers would abstract this:

var combos = [["system1", "webm"], ["system1", "cenc"], ["system2", "webm"]];

Promise.all(combos.map(combo => iTS(...combo))).then(results => {
  for (var i = 0; i < results.length; ++i) {
    if (results[i]) {
      [keySystem, mediaType] = combos[i];
      break;
    }
  }
});
Comment 7 David Dorwin 2014-07-08 00:09:53 UTC
There are potentially other capability detection changes (i.e. bug 26207) that will be made. I think we are waiting to see whether other changes emerge before changing the existing spec text to avoid unnecessary churn.

Separately, since an asynchronous method would no longer resemble MSE's isTypeSupported() and "type" is only one part of the question, we may also want to rename the method. Perhaps isSupported().
Comment 8 Steve Heffernan 2014-08-11 19:18:23 UTC
Just my two cents as a JS dev, but an API method that's named is[Something] I'd expect to be a synchronous boolean response. And this function specifically I'd expect to work like MSE's isTypeSupported and the video element's canPlayType.

If it is going to be a single prompt to enable all systems for a user, I might expect a more verbose process like a document property encryptedMediaEnabled to check and an async enableEncryptedMedia().
Comment 9 David Dorwin 2014-08-20 02:20:18 UTC
Jerry said he would look into this in the telecon today.
Comment 10 Jerry Smith 2014-08-22 16:52:56 UTC
I agree that IsTypeSupported() mirrors functionality for IsTypeSupported() in MSE and canPlayType() for HTMLMediaElements.  Both of those seem well suited to a simple call and synchronous response.  

I'm not clear why the API would be better for triggering UI to enable EME if it was asynchronous.  Is the intent to hold the response until UI is presented and resolved, and then allow IsTypeSupported() to return?
Comment 11 Anne 2014-08-22 16:57:58 UTC
Basically, we don't want to give an answer until the user has decided whether the origin in question is allowed to use DRM. And we don't want this method to hang page interaction like alert(). Therefore, it needs to be asynchronous.
Comment 12 Jerry Smith 2014-08-27 19:34:51 UTC
I think we are mixing two topics in this bug:

1.  Whether IsTypeSupported() should be synchronous or asynchronous
2.  Whether and how to allow user authorizations to use EME

I believe that implementers have different intentions on item 2.  Some may provide a control setting, and others may prompt on first use, or per use.  You appear to be interested in the latter and are looking for ways to trigger the UI.

Item 1, IsTypeSupported() may somehow be made to support your needs, but it is intended to be a simple query for a few EME related capabilities.  It is conceptually similar to some other HTML5 features in media that are all synchronous.  I believe it should remain so.

In Tuesday's call, we discussed opening a bug on the topic of user authorization.  It would benefit from a thorough discussion.  Anne: Given your direct interest, you would be an ideal person to open this bug, with your thoughts on the importance and options for it to be implemented that you think would work best for the range of needs.  

I recommend that this specific bug be closed and we link it to the new general bug on authorization when that opens.
Comment 13 David Dorwin 2014-08-27 22:11:35 UTC
I agree with comment #12.

On Topic #2:
We should work through and standardize the expected flow, including any possible prompts. In particular, how should isTypeSupported() respond in the no permissions case and should prompts always occur in MediaKeys.create()?

On Topic #1: 
If we do not need to worry about user interaction, we can simply look at the appropriate design for this simple query. While asynchronous APIs are generally preferred and isTypeSupported() may need to query the platform or make other IPCs (the first time it is called), it is probably better for authors, implementors, and possibly even the user experience to make a few synchronous calls instead of potentially 10s of asynchronous calls.
Comment 14 Boris Zbarsky 2014-08-27 23:44:23 UTC
Jerry, David, with all due respect I think you're missing the point.

In order for some UAs to implement the user authorization prompts they want to implement, they need to have isTypeSupported asynchronous.  The reason for that is that they don't know whether the type is supported until they have downloaded parts of a CDM that they may not be willing to download without explicit user permission.  I'm not sure how to make this clearer.  Even if the CDM download happens without a user prompt, the isTypeSupported call obviously can't block on a download of a CDM over the network.

There is no "mixing up" involved, just a simple logical implication: if the UA does not know at the time isTypeSupported is called whether the type is in fact supported, then it cannot answer the question being asked.

This is not about looking for ways to trigger the UI.  This is just about the fact that if the UA does not ship with the CDM to start with, then there is no sane way to implement a synchronous isTypeSupported.

This is why discussion should happen on mailing lists (or in this case in bugs), not calls, by the way; had you done that you would have gotten your misconceptions corrected earlier.
Comment 15 Boris Zbarsky 2014-08-27 23:46:05 UTC
And to make it clear, in case it wasn't, the current form of the API, with a synchronous boolean return value, is completely unacceptable to Mozilla.  If this remains in the spec as-is, we will likely have to always return false or some such nonsense.
Comment 16 David Dorwin 2014-08-28 00:08:18 UTC
(In reply to Boris Zbarsky from comment #15)
> And to make it clear, in case it wasn't, the current form of the API, with a
> synchronous boolean return value, is completely unacceptable to Mozilla.  If
> this remains in the spec as-is, we will likely have to always return false
> or some such nonsense.

This does not describe the current form of the API. See: https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html#extensions

(In reply to Boris Zbarsky from comment #14)
> Jerry, David, with all due respect I think you're missing the point.

I fully understand your scenario. You appear to be jumping to a specific solution while we want to have a discussion about the overall behavior and use of multiple methods. For example, one possible solution would be to return "maybe" (or some other relevant value) in the case where the CDM is not yet downloaded.

> This is why discussion should happen on mailing lists (or in this case in
> bugs), not calls, by the way; had you done that you would have gotten your
> misconceptions corrected earlier.

Jerry was asked to summarize his comments in the call in the bug, which he did in comment #12.
Comment 17 Boris Zbarsky 2014-08-28 00:13:33 UTC
> This does not describe the current form of the API.

Ah, thank you.  So I'm not sure what the point of the API in the form it's in is, then.  A UA can just always return "maybe", which is not helpful.

> For example, one possible solution would be to return "maybe" (or some other
> relevant value) in the case where the CDM is not yet downloaded.

Sure, but then the page would want to know when the download is done so it can ask again, right?

I'm not tied to a particular solution, but I do think that we want an API here that is actually useful to web pages and that can handle the "answer is not known yet, but will be known pretty soon" situation.  I'm quite open to how that API should look, though typically for modern web APIs it would involve returning a Promise for the value.
Comment 18 David Dorwin 2014-08-28 00:28:51 UTC
(In reply to Boris Zbarsky from comment #17)
> > This does not describe the current form of the API.
> 
> Ah, thank you.  So I'm not sure what the point of the API in the form it's
> in is, then.  A UA can just always return "maybe", which is not helpful.

If the user agent does not support a key system or the user has completely disallowed it, the user agent can return "". "maybe" means the application should try it if there are no "probably" responses. (The application could still prefer "maybe" if it really liked that combination, but it should be able to handle failure.)

> > For example, one possible solution would be to return "maybe" (or some other
> > relevant value) in the case where the CDM is not yet downloaded.
> 
> Sure, but then the page would want to know when the download is done so it
> can ask again, right?

In such a design, the application might call MediaKeys.create(), which would generate the prompt and download as appropriate. If the prompt was declined, the promise would be rejected. Otherwise, the promise would be resolved when the CDM has been installed.

> I'm not tied to a particular solution, but I do think that we want an API
> here that is actually useful to web pages and that can handle the "answer is
> not known yet, but will be known pretty soon" situation.  I'm quite open to
> how that API should look, though typically for modern web APIs it would
> involve returning a Promise for the value.

I mostly agree. I think the discussion is whether isTypeSupported() should return the promise or create(), which already returns a promise, should handle this. (Or perhaps there is some other solution.)
Comment 19 Jerry Smith 2014-08-29 20:10:39 UTC
I think it makes sense for IsTypeSupported() to return "maybe" for a CDM that must be downloaded, and use the create() promise on MediaKeys as an opportunity to prompt and complete the download.
Comment 20 Anne 2014-08-31 15:28:59 UTC
I don't understand what the problem with a promise is here. In fact, we already agreed to it during a teleconference I attended.

canPlayType() and such are for non-user-conditional formats. DRM is user-conditional and as such APIs around it need to be asynchronous.
Comment 21 Mark Watson 2014-09-09 18:40:07 UTC
I don't think we should assume that any user-permission for the CDM is obtained on creation of the MediaKeys object. UAs should have flexibility to handle user-permission on whatever basis they deem that appropriate (for example when the browser is installed / upgraded).

I think we should ensure, though, that if a process is going to take considerable time (e.g. download) then it should be clear whether it is the UA or the site that is responsible for progress indications (and probably it should be the UA).
Comment 22 Jerry Smith 2014-09-12 20:06:21 UTC
I agree the UA should be responsible for indicating CDM install progress.

I looked at two other specs that have permission aspects:

1.  The Geolocation API Spec puts responsibility on the UA to get user permissions before specific APIs can be called.  The mechanism for doing that is not defined in the spec.  Presumably, an app attempting to access these APIs would trigger the UA to request access permission.
2.  The Web Notifications spec has a requestPermission command that must be completed with a "granted" response before notification APIs may be used.

One reason we want to keep IsTypeSupported synchronous is that in the general case it should execute quickly and simply, so that media download can be started quickly.  A synchronous command assures that.

One possible solution that retains this synchronous nature is to have IsTypeSupported return a "maybe" (or perhaps a new response) for cases where the keySystem is nominally supported, but not available.  Media could be speculatively downloaded to start playback while user permissions are requested and obtained.  This would require we add a command specifically to activate the CDM.  Perhaps:

Promise<void> ActivateCDM((DOMstring keySystem);

The processing steps for this might be something like this:

1.  Confirm the keySystem is valid, supported and allowed.  If not, return DOMExceptions.
2.  Let promise be a new promise
3.  Process the CDM activation
4.  Resolve promise with activation outcome
5.  Return promise.
Comment 23 Anne 2014-09-13 07:44:05 UTC
(In reply to Jerry Smith from comment #22)
> One reason we want to keep IsTypeSupported synchronous is that in the
> general case it should execute quickly and simply, so that media download
> can be started quickly.  A synchronous command assures that.

Given that a promise's handlers would execute end-of-task and a download would at best be next-task, the quickness seems equal.


> Promise<void> ActivateCDM((DOMstring keySystem);

Would we require this to be always invoked, for all implementations? Also, how can you activate it if isTypeSupported returns "maybe"?
Comment 24 David Dorwin 2014-09-15 17:38:15 UTC
(In reply to Anne from comment #23)
> (In reply to Jerry Smith from comment #22)
> > One reason we want to keep IsTypeSupported synchronous is that in the
> > general case it should execute quickly and simply, so that media download
> > can be started quickly.  A synchronous command assures that.
> 
> Given that a promise's handlers would execute end-of-task and a download
> would at best be next-task, the quickness seems equal.

Neither task could be executed until the user has responded - this would add an unknown delay. (You could argue that this is once per user, but we also want to handle any other permission dialogs in the same way.)

> > Promise<void> ActivateCDM((DOMstring keySystem);
> 
> Would we require this to be always invoked, for all implementations? Also,
> how can you activate it if isTypeSupported returns "maybe"?

This is basically the same signature as create():
  Promise<MediaKeys> create(DOMString keySystem);

Why do we need an additional method when we already have one that all applications must call on all implementations?

Reusing "maybe" and create() also avoids leaking state, including whether the CDM was previous installed (by another origin).
Comment 25 Anne 2014-09-15 17:53:51 UTC
(In reply to David Dorwin from comment #24)
> Neither task could be executed until the user has responded - this would add
> an unknown delay. (You could argue that this is once per user, but we also
> want to handle any other permission dialogs in the same way.)

That depends on the user agent implementation.


> Reusing "maybe" and create() also avoids leaking state, including whether
> the CDM was previous installed (by another origin).

Is state not leaked either way due to timing attacks? Not having "maybe" seems to leak less state if the site treats the browser as an opaque entity. Or is the proposal to always return "maybe" in all implementations before create() is invoked?
Comment 26 David Dorwin 2014-09-15 18:00:57 UTC
(In reply to Anne from comment #25)
> (In reply to David Dorwin from comment #24)
> > Neither task could be executed until the user has responded - this would add
> > an unknown delay. (You could argue that this is once per user, but we also
> > want to handle any other permission dialogs in the same way.)
> 
> That depends on the user agent implementation.

Can you give an example of an implementation where this is not the case? How can an implementation reply before the user has accepted/declined downloading the CDM?

> > Reusing "maybe" and create() also avoids leaking state, including whether
> > the CDM was previous installed (by another origin).
> 
> Is state not leaked either way due to timing attacks? Not having "maybe"
> seems to leak less state if the site treats the browser as an opaque entity.
> Or is the proposal to always return "maybe" in all implementations before
> create() is invoked?

I was specifically thinking of the case where an implementation prompts for both download and first use on an origin. In this case, the timing attack would not work. If the prompt was only for download on the first origin, I don't see a way to address the timing attack.

Implementations could choose to always return "maybe" (instead of probably) to avoid leaking information, but I'm not sure that's a requirement. In the absence of any "probably" results, "maybe" is effectively the same - the app should try one of those combinations.
Comment 27 Anne 2014-09-15 18:06:13 UTC
Well, the CDM could have been bundled.

Anyway, at this point it's no longer clear to me what the revised proposal is. During the telcon I attended the idea was to have this method be asynchronous and not have a "maybe" token. That still works.
Comment 28 David Dorwin 2014-09-15 18:25:40 UTC
I (and maybe others) consider the download case a special case of permissions/authorization, and we are trying to determine where and how to handle authorization. (This is why we had proposed opening a separate bug to address authorization (comment #12).)

Stepping back, Jerry has identified two different approaches to permissions in comment #22. I think we need to pick one before focusing on specific proposals.
 1. The UA handles permissions implicitly as part of some method call.
 2. The spec provides an explicit method to request permissions.

Asynchronous isTypeSupported() and reuse of create() fall in category #1.
ActivateCDM() falls in #2 (but probably needs to be refined to be more generic).

#1 offers more implementation flexibility and avoids making the application do more work than will be unnecessary in most cases (i.e. due to saved permissions).

I think we can still be explicit about handling permissions without adding an explicit method (#2).
Comment 29 Jerry Smith 2014-09-15 18:36:26 UTC
Of the two options, I would prefer one that extends an existing method call, and agree with David that create() looks like a very good fit.

It may be worth discussing returning "probably" on IsTypeSupported when the CDM is not currently authorized.  Attempting to create a MediaKeys object on it could subsequently fail, should the user not approve the CDM use.
Comment 30 Anne 2014-09-16 07:45:10 UTC
What I care about is what's observable and whether the programming flow is identical across user agents.

If isTypeSupported() starts returning something other than "maybe" in some user agents before create() is invoked, that would bad.

If all user agents required create() (which I assume is asynchronous) before isTypeSupported() started returning something other than "maybe", and that was stipulated in the specification, that would work.

It seems however that you likely want to invoke isTypeSupported() before you invoke create() at which point I'm back to comment 0.
Comment 31 David Dorwin 2014-09-16 20:53:36 UTC
(In reply to Anne from comment #30)
> What I care about is what's observable and whether the programming flow is
> identical across user agents.
> 
> If isTypeSupported() starts returning something other than "maybe" in some
> user agents before create() is invoked, that would bad.

If it is a standard value with defined expectations/behavior, then I don't think it is necessarily bad. I would be more concerned about information leakage.

> If all user agents required create() (which I assume is asynchronous) before
> isTypeSupported() started returning something other than "maybe", and that
> was stipulated in the specification, that would work.

This is not one of the options - isTypeSupported() is static and should not depend on loading the CDM, which is the purpose of create().

> It seems however that you likely want to invoke isTypeSupported() before you
> invoke create() at which point I'm back to comment 0.

I think this assumes that the value returned by isTypeSupported() is different depending on whether the CDM is permitted/installed.

If the user does not permit the CDM, the UA could return "". Alternatively, if the UA always returned "maybe", the application would try MediaKeys.create(), which would be rejected with "NotSupportedError" per step 3 of the create() algorithm. The effect is the same - the user does not get to play the encrypted content.

Things get more complicated if a user agent supports multiple such key systems. In that case, the application could make use of the difference between not allowed ("") and allowed ("maybe" or "probably"). A combination that returns "maybe" does not guarantee it will work, and a robust application _should_ be able to try other "maybe" combinations that it supports. I acknowledge that this is probably not common.


One possible solution for this case would be to add "deferred" to the IsTypeSupportedResult enum. This would be an indication that yes the combination can be supported by the user agent but the application must try MediaKeys.create(). This also avoids leaking information since the UA would always return this value, at least the first time an origin attempts to use a key system. Applications would prefer "probably" combinations to "maybe" combinations to "deferred" combinations. If "deferred" is returned for multiple key systems, the application would need to try each of them. One issue is that the two key systems might support different media streams, which might mean the pre-fetched streams would need to be discarded.


Alternatively, the application could return "maybe"/"probably" the first time isTypeSupported() is called on an origin then prompt the user on create(). In most cases, the prompt will be accepted and things will work as expected (without complicating every playback by making isTypeSupported() asynchronous). If the user declines the prompt, create() would fail. Again, supporting multiple key systems (or even flavors of a single key system) causes problems here - the user would likely need to be told to refresh the page.


I can imagine cases where on key system [flavor] is declined and the application should try another. Maybe we should specify an iterative process for applications rather than check-and-expect-success. Naming can help imply the expected behavior - maybe we should replace create() with something like requestKeySystem().
Comment 32 David Dorwin 2014-09-16 21:15:33 UTC
Building on the last paragraph of comment #31, maybe we should use a model similar to Web MIDI's requestMIDIAccess() [1] and MIDIAccess Interface [2].

All isTypeSupported() queries are dependent on the key system, of which there are likely to be few positive responses, so asynchronously checking for key system support of the key system (but not media types) might be okay.

Below is a rough proposal based (somewhat) on the MidiAccess model.

navigator.requestKeySystemAccess("org.w3.clearkey").then(
  function(keySystemAccess) {
    // isTypeSupported() remains synchronous.
    if (keySystemAccess.isTypeSupported("keyids", "video/foo; codecs='bar,baz'") ||
        keySystemAccess.isTypeSupported("keyids", "video/foo; codecs='bar2,baz'")) {
      // This must be a separate asynchronous call so we do not need to load the CDM in requestKeySystemAccess().
      return keySystemAccess.createMediaKeys();
    }
    // Throw to fall into the catch.
    ...
  }
).catch(
  function(error) {
    // Try the next key system.
    ...
  }
)


[1] http://www.w3.org/TR/webmidi/#obtaining-access-to-midi-devices
[2] http://www.w3.org/TR/webmidi/#midiaccess-interface
Comment 33 Anne 2014-09-17 08:02:58 UTC
(In reply to David Dorwin from comment #31)
> I think this assumes that the value returned by isTypeSupported() is
> different depending on whether the CDM is permitted/installed.

Yes, this bug is about that :-)


> If the user does not permit the CDM, the UA could return "". Alternatively,
> if the UA always returned "maybe", the application would try
> MediaKeys.create(), which would be rejected with "NotSupportedError" per
> step 3 of the create() algorithm. The effect is the same - the user does not
> get to play the encrypted content.

The problem is if the programming flow is different across user agents that might use different deployment models for DRM. Some content will start assuming a particular flow and therefore not be compatible with a different model.


> requestKeySystem().

That would work.
Comment 34 David Dorwin 2014-09-22 20:57:38 UTC
I'll have a more detailed proposal based on comment #32 soon.
Comment 35 David Dorwin 2014-09-24 18:18:13 UTC
For any solution where the type support checking mechanism triggers or is only available after prompting, implementing support for more than one promptable key system would result in either complex UX or potentially multiple prompts if the first key system does not support the types the application wants.

For example, if an application requests (supported) key system A, the user may be prompted, and only after the user accepts would the application find out that the key system does not support codec foo. The application would then request (supported) key system B, potentially prompting the user again.

This is a problem for both the original proposal (asynchronous isTypeSupported(), which would be called multiple times in parallel) and the proposal in comment #32.

I see two possible solutions to this problem:
1) The application can check for the best case support without prompting for permissions or download.
 * In other words, the equivalent of the current isTypeSupported() reports the best case for the user agent implementation not what is actually supported.
 * The application can call this before requesting access to the CDM, which would trigger the prompt and/or download.
2) Some type of request involving multiple key systems (and probably types) that allows the user agent to make a reasonable decision.
 * The user agent may not do what the application wants, though, and such decisions have generally been left to the application.
Comment 36 Anne 2014-09-24 20:03:56 UTC
We could make it so that the UA has to pick the first it supports. But I don't really see the problem with multiple calls to isTypeSupported(). Also, I suspect the switch in UAs to be on/off for the feature in general, not a per key basis. That seems way complicated.
Comment 37 David Dorwin 2014-09-24 20:40:13 UTC
(In reply to Anne from comment #36)
> We could make it so that the UA has to pick the first it supports. But I
> don't really see the problem with multiple calls to isTypeSupported(). Also,
> I suspect the switch in UAs to be on/off for the feature in general, not a
> per key basis. That seems way complicated.

I don't expect this to be a major issue in practice, but it would be nice if we could specify something that provides more flexibility without being overly complex.

I assume you mean "a per key *system* basis."
CDM implementations can vary greatly, so it is possible that two CDMs will have very different security and privacy properties. It's also possible that one is installed while another must be downloaded. In such scenarios, the UA might actually want to provide a different UX for two key systems.
Comment 38 David Dorwin 2014-09-25 00:46:25 UTC
(In reply to David Dorwin from comment #34)
> I'll have a more detailed proposal based on comment #32 soon.

See http://lists.w3.org/Archives/Public/public-html-media/2014Sep/0021.html.
Comment 39 David Dorwin 2014-10-03 22:22:10 UTC
Updated proposal at http://lists.w3.org/Archives/Public/public-html-media/2014Oct/0002.html.

Anne and Boris, does this meet your needs?
Comment 40 Boris Zbarsky 2014-10-04 02:14:57 UTC
I have one nit about the text.

>         1. If the member's name is unrecognized or unhandled 

Then the implementation won't see it.  Dictionaries in Web IDL work by taking arbitrary objects and snapshotting the values of the properties the dictionary cares about.  Other properties are ignored, because they have nothing to do with the dictionary.

What this means is that if you pass some non-standard value in this dictionary to an implementation that follows the standard, it will just ignore it.

Apart from that I like this in terms of implementation: this puts the information about what's being requested up front so the implementation can figure out how best to communicate that to the user.

Using a sequence here is absolutely correct.  You shouldn't use IDL arrays for anything; they're on the chopping block to be removed altogether.  But for reference, since the mail asked, the reason that IDL array of sequence/dictionary is not allowed is that sequence/dictionary are pass-by-value, so converting them to/from JS values always makes a copy.  So if you had an IDL array of them, every read out of the IDL array would create a new object.  That would be surprising, to say the least.
Comment 41 David Dorwin 2014-10-06 17:34:53 UTC
(In reply to Boris Zbarsky from comment #40)
> I have one nit about the text.
> 
> >         1. If the member's name is unrecognized or unhandled 
> 
> Then the implementation won't see it.  Dictionaries in Web IDL work by
> taking arbitrary objects and snapshotting the values of the properties the
> dictionary cares about.  Other properties are ignored, because they have
> nothing to do with the dictionary.
> 
> What this means is that if you pass some non-standard value in this
> dictionary to an implementation that follows the standard, it will just
> ignore it.

Thanks. I'll remove this when implementing. I may add a note that such values are ignored and not used in the decision, meaning an application using them will somehow need to verify the returned object. Better to just not use them, though.

Thanks for the details on sequence too.
Comment 42 David Dorwin 2014-10-13 23:32:21 UTC
https://dvcs.w3.org/hg/html-media/rev/5e1803bb51e7 implements the proposal referenced in comment #39.

Open issues:
1. Specifying types in MediaKeySystemOptions (from the proposal referenced in comment #39):
 a. The inclusion of (a single) initDataType in the dictionary means each combination may need to be duplicated (i.e. for "keyids" and "cenc").
  * We could make this an array, but that would be inconsistent with the audio/video types (i.e. all codecs must be supported).
 b. Checking multiple types requires multiple configuration entries.
 c. To address the two previous items, perhaps we should replace the type-capability pairs with an array of pairs, allowing any matching pair to represent a positive result. That might require defining another Dictionary or other type.
 d. Depending on the outcome of the above items, we may need to add methods like isTypeSupported() and isRequirementSupported() to the MediaKeySystemAccess object.
2. It is TBD whether rejecting the promise is the correct behavior or whether the promise should instead be resolved with null. See the thread beginning at http://lists.w3.org/Archives/Public/public-whatwg-archive/2014Oct/0011.html.
3. We may want to add a MediaKeysRequirement value like "prefernot". Most use cases would probably want this behavior rather than "disallowed".
Comment 43 David Dorwin 2014-10-13 23:33:48 UTC
Changing the summary to match the subsequent discussion and changes being made.
Comment 44 David Dorwin 2014-11-21 23:24:47 UTC
The five commits ending with https://github.com/w3c/encrypted-media/commit/707e404fe80c85b1141582a55e8dd8a1012fa7d8 add more details to the implementation in comment #42.

https://github.com/w3c/encrypted-media/commit/f349e811b7f615e8cc03cf63f787b799082a96f6 adds support for arrays of initData types and media capabilities.

https://github.com/w3c/encrypted-media/commit/2d80213910402f958a7f7f9baf42a1ca8aae503c renames the "capability" string to "robustness".

https://github.com/w3c/encrypted-media/commit/aaf69e43337c08f316c6fd68a389a1009fdb9dfb exposes the resulting configuration from MediaKeySystemAccess. It is exposed via a method because a dictionary cannot be an attribute.


Altogether, the twelve commits ending in https://github.com/w3c/encrypted-media/commit/5f42f3e8c2e473ab9f29188156c44fb55d49266d improve the clarity and address open issue #1 from comment #42. Informal feedback on #2 is that rejecting the promise is acceptable, so I have also removed that issue box.
Comment 45 David Dorwin 2014-11-21 23:35:59 UTC
https://github.com/w3c/encrypted-media/commit/aaf69e43337c08f316c6fd68a389a1009fdb9dfb made "optional" effectively "prefer not". If the implementation of a media capability requires something, an "optional" requirement will be required/used, but otherwise it is disallowed/not used. Thus, open issue #3 from comment #42 is also resolved.


All open issues have been resolved, so I consider this bug resolved.

For any issues with the current implementation, please file specific bugs in GitHub.
Comment 46 David Dorwin 2014-11-21 23:44:26 UTC
We could consider following the Constrainable Pattern (http://w3c.github.io/mediacapture-main/getusermedia.html#constrainable-interface), but I think it is overly complex (i.e. supporting changing constraints) for our purposes. I considered using similar names (i.e "Constraints"), but that might be confusing as long as we don't follow the pattern.
Comment 47 David Dorwin 2014-11-22 00:09:46 UTC
Note: The replacement of isTypeSupported() with requestMediaKeySystemAccess() means it is no longer practical to "discover" key systems by walking a tree. For example, checking "com.example" then "com.example.somesystem" then "com.example.somesystem.1_5".

This seemed unlikely to be supported or used much in practice, so this probably isn't a big loss.
Comment 48 David Dorwin 2014-11-25 00:19:45 UTC
https://github.com/w3c/encrypted-media/commit/06fa433dbd882a0718b27e8613cd0198777e8374 improves the way values from MediaKeySystemAccess and CDM instances are referenced.

https://github.com/w3c/encrypted-media/commit/b3bc8fef1c21b245e52ef46bfedb2c1c5e9c3223 changes the MediaKeysRequirement enum value "disallowed" to "not-allowed" and adds descriptions for both uses of the MediaKeysRequirement enum values.

Naming suggestions for MediaKeysRequirement are welcome. We're using them for two different things because we reuse MediaKeySystemConfiguration for input and output.
Comment 49 David Dorwin 2014-12-02 01:30:59 UTC
https://github.com/w3c/encrypted-media/commit/0173c012d540991aa158c1ab45a2733af342775f replaces the persistentUniqueIdentifier member of MediaKeySystemConfiguration with distinctiveIdentifier.

https://github.com/w3c/encrypted-media/commit/43483715ae46779b937300e9690d247fa300dcff adds explicit enforcement of this value in the Queue a "message" Event algorithm.