Bug 19936 - consider allowing non-matching enums to be converted to a particular value
consider allowing non-matching enums to be converted to a particular value
Status: REOPENED
Product: WebAppsWG
Classification: Unclassified
Component: WebIDL
unspecified
All Windows 3.1
: P2 normal
: ---
Assigned To: Cameron McCormack
public-webapps-bugzilla
[v1]
:
Depends on:
Blocks: 25794
  Show dependency treegraph
 
Reported: 2012-11-11 18:23 UTC by Cameron McCormack
Modified: 2014-09-11 07:13 UTC (History)
8 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Cameron McCormack 2012-11-11 18:23:34 UTC
Something like:

  enum CanvasContextName {
    "2d", "webgl", default = ""
  };

  interface HTMLCanvasElement {
    any getContext(CanvasContextName name);
  };

so that bad values get converted into the specified default value rather than resulting in an exception being thrown.
Comment 1 Anne 2012-11-11 18:58:38 UTC
I suggest making it a flag since this is contrary to the pattern we try to establish for methods right?
Comment 2 Jonas Sicking 2012-11-13 09:09:51 UTC
It seems very awkward to me to express the default as part of the enum type.

How common is this pattern? What's the value of using an enum rather than a DOMString here?
Comment 3 Tab Atkins Jr. 2012-11-13 19:08:14 UTC
(In reply to comment #2)
> It seems very awkward to me to express the default as part of the enum type.
> 
> How common is this pattern? What's the value of using an enum rather than a
> DOMString here?

It's common in a lot of enumerated attributes in the DOM, such as <input type>.

The benefit of using an enum over a DOMString+explanatory text is the same benefit of using an enum at all - it's simpler to read and write, and ensures that we'll get consistent behavior.
Comment 4 Travis Leithead [MSFT] 2012-11-13 21:41:00 UTC
I think this is a good idea--I was personally always a little turned off by the throw-on-not-matched behavior of enums (though I understand the rationale for that).

I'd propose a slight syntax change. Instead of:

enum CanvasContextName {
    "2d", "webgl", default = ""
};

the following seems more appropriate:
enum CanvasContextName {
    "2d", "webgl", "" = default
};

which would allow you to re-use enums for input elements:

enum InputTypeEnum {
    "text" = default,
    "file",
    "submit",
    "range",
    ...[etc.]
};
Comment 5 Jan-Ivar Bruaroey [:jib] 2014-04-08 21:34:17 UTC
This would be useful for getUserMedia constraints, which has a WebIDL-compliant proposal on the table (full webidl is at the bottom): http://lists.w3.org/Archives/Public/public-media-capture/2014Apr/0002.html

In short, clients may specify both required and non-required constraints. E.g. { video: { facingMode:'left' } } is a non-required that the UA may ignore.

Unfortunately, it becomes impossible to extend VideoFacingModeEnum with new types later, such as 'up' and 'down', without causing getUserMedia() to throw on old browsers because the "non-required" constraint wasn't recognized.

The workaround is to use DOMString and merely refer to the enums in a comment.

However, the syntax shown here looks promising and would have both implementation benefits (we could keep our lovely binding code, which does a lot of nice parsing for us and prevents us writing bugs), and documentation benefits (the documentation is expressly clear about what's needed, both to caller and implementer, and isn't reduced to prose by this largely backroom compatibility concern).
Comment 6 Jan-Ivar Bruaroey [:jib] 2014-09-10 17:35:42 UTC
Two updates since comment 5: Firstly, good news, constraints are rooted in dictionaries again (yay! a while ago, but leads to the next point).

Secondly, mediacapture (and likely webrtc) has adopted using DOMString for all user-provided enums instead of using webidl enum, with prose linking to the enum for documentation, because of the throw issue (so far just facingMode in gUM, Bug 25793 pull request)

This is clearly inferior, so we have Bug 25794 blocking this issue in hopes that it can be resolved positively.

With the gUM spec nearing last call, the WG is wondering where we're leaning and whether to plan to go out with DOMString. Is more information needed for a decision?
Comment 7 Jan-Ivar Bruaroey [:jib] 2014-09-10 17:41:36 UTC
Btw, I like Travis' syntax in comment 4.
Comment 8 Cameron McCormack 2014-09-11 04:42:25 UTC
For HTMLCanvasElement.getContext, I'm thinking now that a DOMString expresses the intent better: context names can be any string, and the defined behaviour of getContext is to return null for a context type it doesn't support.

For HTMLInputElement.type (an IDL attribute "limited to only known values"), assigning any string causes the content attribute to be given that value, but getting its value will only the default value (or the empty string for some) if the value of the content attribute is not valid.  The behaviour here seems a bit different from HTMLCanvasElement.getContext.

For Navigator.getUserMedia, the facing mode value can be used either directly as the value of dictionary members MediaTrackConstraintSet.facingMode and ConstrainVideoFacingModeParameters.{exact,ideal} or in a sequence that is the value of those dictionary members.

I was going to suggest something like:

  dictionary MediaTrackConstraintSet {
    ...
    [LenientEnum] ConstrainVideoFacingMode facingMode;
  };

which would make

  navigator.getUserMedia({
    video: {
      advanced: [
        {
          facingMode: "blah"
        }
      ]
    }
  })

treat the facingMode dictionary member as "not present" since it doesn't have a matching value.

But this leads to the question of how to treat the values inside the sequence<VideoFacingModeEnum>, e.g. if you write

  navigator.getUserMedia({
    video: {
      advanced: [
        {
          facingMode: ["user", "blah", "left"]
        }
      ]
    }
  })

or

  navigator.getUserMedia({
    video: {
      advanced: [
        {
          facingMode: {
            exact: ["user", "blah", "left"]
          }
        }
      ]
    }
  })

There's no concept of "not present" for sequence values, so prose is going to have to handle the "blah" value.  I think it would be strange to allow the JS Array -> sequence<VideoFacingModeEnum> conversion to skip invalid elements.

So this makes me think that all of this should be done with a DOMString and handled in prose, actually.
Comment 9 Jan-Ivar Bruaroey [:jib] 2014-09-11 05:54:16 UTC
"None of the above" is not the same as "not present". For example:

  navigator.getUserMedia({
    video: {}
  })

means "unconstrained", give me any camera, I don't care which way it faces. But

  navigator.getUserMedia({
    video: {
      facingMode: { exact: "blah" },
    }
  })

must NOT succeed, because the caller is requiring a direction known to them but unknown to the (old) browser. Likewise:

  navigator.getUserMedia({
    video: {
      advanced: [
        { facingMode: "blah", aspectRatio: 16/9}, // multi-constraint
        { facingMode: "user"},                    // single constraint
      ]
    }
  })

is required to satisfy both "blah" and 16/9 or give up and move to the next entry. In other words, if it doesn't know "blah" then it should not limit user-facing cameras to 16/9.

Travis' suggestion doesn't have the this problem, or the problem with sequences. We can define:

  enum VideoFacingModeEnum {
    "user",
    "environment",
    "left",
    "right"
    "other" = default
  };

and blah turns into facingMode: ["user", "other", "left"] just fine.
Comment 10 Jan-Ivar Bruaroey [:jib] 2014-09-11 07:13:52 UTC
(In reply to Cameron McCormack from comment #8)
> For HTMLCanvasElement.getContext, I'm thinking now that a DOMString
> expresses the intent better: context names can be any string, and the
> defined behaviour of getContext is to return null for a context type it
> doesn't support.

Except there are only a few defined and implemented values: "2d", "experimental-webgl" and "webgl". I think in c++ this would have been an enum. To add another value, we'd add another value and recompile everything (because that's c++), and we'd get more structured documentation.

Thank goodness getContext only has three values: 
https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement#Methods

> For HTMLInputElement.type (an IDL attribute "limited to only known values"),
> assigning any string causes the content attribute to be given that value,
> but getting its value will only the default value (or the empty string for
> some) if the value of the content attribute is not valid.  The behaviour
> here seems a bit different from HTMLCanvasElement.getContext.

Having behavior - or how a type is used - influence the validity checking of the type, seems bound to break down since the same type may be used in several places.

Shouldn't the same typing apply regardless of which mechanism I use? Right now I don't see the rationale for why:

  Foo.type = "blah";               // doesn't throw.
  Foo.setType("blah");             // throws.
  var x = new Foo({type: "blah"}); // throws.

when they're all trying to do the same thing.