<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE bugzilla SYSTEM "https://www.w3.org/Bugs/Public/page.cgi?id=bugzilla.dtd">

<bugzilla version="5.0.4"
          urlbase="https://www.w3.org/Bugs/Public/"
          
          maintainer="sysbot+bugzilla@w3.org"
>

    <bug>
          <bug_id>28875</bug_id>
          
          <creation_ts>2015-07-01 20:32:39 +0000</creation_ts>
          <short_desc>[EnforceRange] accepts an odd set of input values</short_desc>
          <delta_ts>2015-07-02 01:54:06 +0000</delta_ts>
          <reporter_accessible>1</reporter_accessible>
          <cclist_accessible>1</cclist_accessible>
          <classification_id>1</classification_id>
          <classification>Unclassified</classification>
          <product>WebAppsWG</product>
          <component>WebIDL</component>
          <version>unspecified</version>
          <rep_platform>PC</rep_platform>
          <op_sys>Linux</op_sys>
          <bug_status>NEW</bug_status>
          <resolution></resolution>
          
          
          <bug_file_loc></bug_file_loc>
          <status_whiteboard></status_whiteboard>
          <keywords></keywords>
          <priority>P2</priority>
          <bug_severity>normal</bug_severity>
          <target_milestone>---</target_milestone>
          
          
          <everconfirmed>1</everconfirmed>
          <reporter name="Jeffrey Yasskin">jyasskin</reporter>
          <assigned_to name="Cameron McCormack">cam</assigned_to>
          <cc>bzbarsky</cc>
    
    <cc>jonas</cc>
    
    <cc>jsbell</cc>
    
    <cc>mike</cc>
    
    <cc>public-script-coord</cc>
    
    <cc>sleevi</cc>
          
          <qa_contact>public-webapps-bugzilla</qa_contact>

      

      

      

          <comment_sort_order>oldest_to_newest</comment_sort_order>  
          <long_desc isprivate="0" >
    <commentid>121553</commentid>
    <comment_count>0</comment_count>
    <who name="Jeffrey Yasskin">jyasskin</who>
    <bug_when>2015-07-01 20:32:39 +0000</bug_when>
    <thetext>[EnforceRange] delegates to the ECMAScript ToNumber() operation before checking its range, but this makes it accept a strange set of values.

unsigned long enforceRange([EnforceRange] unsigned long arg) { return arg; }

enforceRange({}) throws a TypeError, but enforceRange([]) and enforceRange(&quot;&quot;) return 0.
enforceRange(undefined) throws a TypeError, but enforceRange(null) returns 0.

What&apos;s the &quot;right&quot; behavior?

Looking at uses: I see 3 existing uses in Blink, all in IndexedDB: https://code.google.com/p/chromium/codesearch/#search/&amp;q=enforcerange%20file:.idl%20-file:test&amp;type=cs&amp;sq=package:chromium.
Web Bluetooth is also considering using [EnforceRange] for BluetoothUUID.canonicalUUID().</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>121555</commentid>
    <comment_count>1</comment_count>
    <who name="Boris Zbarsky">bzbarsky</who>
    <bug_when>2015-07-01 20:42:53 +0000</bug_when>
    <thetext>&gt; Looking at uses: I see 3 existing uses in Blink, all in IndexedDB: 

http://mxr.mozilla.org/mozilla-central/search?string=EnforceRange&amp;find=dom%2Fwebidl&amp;findi=&amp;filter=^[^\0]*%24&amp;hitlimit=&amp;tree=mozilla-central shows uses in SubtleCrypto as well.

What exactly is the problem with the current behavior?  Assuming you plan to do a coercion to number at all, you have to do it before checking range.  The only other option would be to immediately throw if the passed-in thing is not a Number value, right?  That would break things like passing &quot;123&quot; (e.g. out of input.value) for cases where the callee wants a range-enforced integer.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>121557</commentid>
    <comment_count>2</comment_count>
    <who name="Jeffrey Yasskin">jyasskin</who>
    <bug_when>2015-07-01 21:08:59 +0000</bug_when>
    <thetext>Thanks for finding the extra uses.

We&apos;re excluding some non-integral arguments, but not a consistent set. (That&apos;s similar to a lot of Javascript, so maybe it&apos;s ok; I&apos;m just checking that it&apos;s intentional.)

If we want to exclude &quot;strange&quot; input values without breaking &quot;123&quot;, I think the appropriate algorithm is something like:

1. Initialize p to ToPrimitive(V, hint Number).
2. If p is &quot;&quot; or null, throw a TypeError.  // Excludes empty arrays.
3. Initialize x to ToNumber(p).
4. If x is NaN, +∞, or −∞, then throw a TypeError.
5. Set x to sign(x) * floor(abs(x)).
6. If x &lt; 0 or x &gt; 2^32 − 1, then throw a TypeError.
7. Return the IDL unsigned long value that represents the same numeric value as x.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>121563</commentid>
    <comment_count>3</comment_count>
    <who name="Jonas Sicking (Not reading bugmail)">jonas</who>
    <bug_when>2015-07-01 22:25:16 +0000</bug_when>
    <thetext>Javascript has a lot of quirks. I think trying to work around those quirks in a random set of DOM APIs is only going to make the platform even more quirky.

What&apos;s special about [EnforceRange] here? It seems like you&apos;d see the same set of quirky behavior for any API that accepts a number. Consider for example

x = [1, 2];
x.length = 0; // Removes all items for x
x.length = {}; // Throws and leaves x unaffected
x.length = []; // Removes all items for x
x.length = &quot;&quot;; // Removes all items for x</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>121565</commentid>
    <comment_count>4</comment_count>
    <who name="Jeffrey Yasskin">jyasskin</who>
    <bug_when>2015-07-01 23:04:29 +0000</bug_when>
    <thetext>[EnforceRange] already works around a quirk of Javascript in a random set of (not primarily DOM) APIs. If it&apos;s going to exist at all, it may as well work around the quirk well.

Most APIs that take integer types uniformly treat all of {}, [], undefined, null, and NaN as 0. Only APIs that try to enforce an integral range pick an arbitrary subset of those to throw an exception on.

array.length is a good precedent for how to enforce ranges, but http://ecma-international.org/ecma-262/6.0/#sec-arraysetlength already behaves differently from [EnforceRange]. It checks that ToNumber(arg) and ToUint32(arg) give the same result, which throws a TypeError on &quot;x.length = 3.2&quot;. enforceRange(3.2) returns 3. Maybe we should just unify those behaviors and call it a day? It wouldn&apos;t be quite the ideal behavior, but it&apos;d be consistently odd.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>121566</commentid>
    <comment_count>5</comment_count>
    <who name="Jonas Sicking (Not reading bugmail)">jonas</who>
    <bug_when>2015-07-01 23:48:45 +0000</bug_when>
    <thetext>I&apos;d definitely go for consistency rather than trying to fix JS quirks.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>121569</commentid>
    <comment_count>6</comment_count>
    <who name="Boris Zbarsky">bzbarsky</who>
    <bug_when>2015-07-02 01:54:06 +0000</bug_when>
    <thetext>&gt; but not a consistent set

Well, it&apos;s consistent with how everything else in JS converts things to numbers...  It&apos;s fairly intentionally matching those other conversions, yes.

You raise a good point about how conversion to integers in JS typically converts non-finite doubles to 0.  That&apos;s a pretty odd thing to do in [EnforceRange], though (at least for the positive and negative Infinity values).  So [EnforceRange] basically just codifies what people would otherwise do in prose: take a double or 53-bit int (depending on whether they wanted to throw or round on non-integers). and then check the range.

&gt; Maybe we should just unify those behaviors

I could live with that, probably; we&apos;d need to think a bit about what the behavior looks like for integer sizes other than 32-bit.</thetext>
  </long_desc>
      
      

    </bug>

</bugzilla>