Bug 1359822 - Match standard for colSpan/rowSpan draft
authorAryeh Gregor <ayg@aryeh.name>
Wed, 26 Apr 2017 21:22:43 +0300
changeset 568899 3e468dc5d5e89a7cdf550889dae103f06a5e24b1
parent 567252 62a2d3693579fc77b1c510984ae471a860d03302
child 626062 bf57f4cfe607cf6aedc53de790865a7eb6e5920e
push id56018
push userbmo:ayg@aryeh.name
push dateWed, 26 Apr 2017 18:31:16 +0000
bugs1359822
milestone55.0a1
Bug 1359822 - Match standard for colSpan/rowSpan Previously, the spec for these attributes didn't match implementations at all, and that was just fixed: <https://github.com/whatwg/html/issues/1198>. There are two differences to our previous implementation: 1) The IDL property should be set as an unsigned int, not signed. This was just a bug. 2) When parsing the IDL property, the value is now clamped to the minimum/maximum value instead of being set to 1. Negative values still get set to 1 instead of 0, even for rowSpan which allows 0 as a value. The test changes are copied from the wpt test submitted upstream. An additional rendering test using getBoundingClientRect() is also accepted upstream: https://github.com/w3c/web-platform-tests/pull/4115 MozReview-Commit-ID: FQzPhgqNTBy
dom/base/nsAttrValue.cpp
dom/base/nsAttrValue.h
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/html/HTMLTableCellElement.cpp
dom/html/HTMLTableCellElement.h
testing/web-platform/meta/html/dom/reflection-tabular.html.ini
testing/web-platform/tests/html/dom/elements-tabular.js
testing/web-platform/tests/html/dom/reflection.js
--- a/dom/base/nsAttrValue.cpp
+++ b/dom/base/nsAttrValue.cpp
@@ -1529,16 +1529,50 @@ nsAttrValue::ParseIntWithFallback(const 
       (result & nsContentUtils::eParseHTMLInteger_NonStandard) ||
       (result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput)) {
     nonStrict = true;
   }
 
   SetIntValueAndType(val, eInteger, nonStrict ? &aString : nullptr);
 }
 
+void
+nsAttrValue::ParseClampedNonNegativeInt(const nsAString& aString,
+                                        int32_t aDefault, int32_t aMin,
+                                        int32_t aMax)
+{
+  ResetIfSet();
+
+  nsContentUtils::ParseHTMLIntegerResultFlags result;
+  int32_t val = nsContentUtils::ParseHTMLInteger(aString, &result);
+  bool nonStrict = (result & nsContentUtils::eParseHTMLInteger_IsPercent) ||
+                   (result & nsContentUtils::eParseHTMLInteger_NonStandard) ||
+                   (result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput);
+
+  if (result & nsContentUtils::eParseHTMLInteger_ErrorOverflow) {
+    if (result & nsContentUtils::eParseHTMLInteger_Negative) {
+      val = aDefault;
+    } else {
+      val = aMax;
+    }
+    nonStrict = true;
+  } else if ((result & nsContentUtils::eParseHTMLInteger_Error) || val < 0) {
+    val = aDefault;
+    nonStrict = true;
+  } else if (val < aMin) {
+    val = aMin;
+    nonStrict = true;
+  } else if (val > aMax) {
+    val = aMax;
+    nonStrict = true;
+  }
+
+  SetIntValueAndType(val, eInteger, nonStrict ? &aString : nullptr);
+}
+
 bool
 nsAttrValue::ParseNonNegativeIntValue(const nsAString& aString)
 {
   ResetIfSet();
 
   nsContentUtils::ParseHTMLIntegerResultFlags result;
   int32_t originalVal = nsContentUtils::ParseHTMLInteger(aString, &result);
   if ((result & nsContentUtils::eParseHTMLInteger_Error) || originalVal < 0) {
--- a/dom/base/nsAttrValue.h
+++ b/dom/base/nsAttrValue.h
@@ -358,16 +358,29 @@ public:
    * http://dev.w3.org/html5/spec/infrastructure.html#rules-for-parsing-non-negative-integers
    *
    * @param  aString the string to parse
    * @return whether the value is valid
    */
   bool ParseNonNegativeIntValue(const nsAString& aString);
 
   /**
+   * Parse a string value into a clamped non-negative integer.
+   * This method follows the rules for parsing non-negative integer from:
+   * https://html.spec.whatwg.org/multipage/infrastructure.html#clamped-to-the-range
+   *
+   * @param aString the string to parse
+   * @param aDefault value to return for negative or invalid values
+   * @param aMin minimum value
+   * @param aMax maximum value
+   */
+  void ParseClampedNonNegativeInt(const nsAString& aString, int32_t aDefault,
+                                  int32_t aMin, int32_t aMax);
+
+  /**
    * Parse a string value into a positive integer.
    * This method follows the rules for parsing non-negative integer from:
    * http://dev.w3.org/html5/spec/infrastructure.html#rules-for-parsing-non-negative-integers
    * In addition of these rules, the value has to be greater than zero.
    *
    * This is generally used for parsing content attributes which reflecting IDL
    * attributes are limited to only non-negative numbers greater than zero, see:
    * http://dev.w3.org/html5/spec/common-dom-interfaces.html#limited-to-only-non-negative-numbers-greater-than-zero
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -1114,16 +1114,17 @@ nsContentUtils::ParseHTMLInteger(const n
     result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
     *aResult = (ParseHTMLIntegerResultFlags)result;
     return 0;
   }
 
   int sign = 1;
   if (*iter == char16_t('-')) {
     sign = -1;
+    result |= eParseHTMLInteger_Negative;
     ++iter;
   } else if (*iter == char16_t('+')) {
     result |= eParseHTMLInteger_NonStandard;
     ++iter;
   }
 
   bool foundValue = false;
   CheckedInt32 value = 0;
@@ -10269,9 +10270,9 @@ nsContentUtils::IsLocalRefURL(const nsSt
     if (*current > 0x20) {
       // if the first non-"C0 controls + space" character is '#', this is a
       // local-ref URL.
       return *current == '#';
     }
   }
 
   return false;
-}
\ No newline at end of file
+}
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -477,17 +477,19 @@ public:
     eParseHTMLInteger_IsPercent             = 1 << 0,
     // eParseHTMLInteger_NonStandard is set if the string representation of the
     // integer was not the canonical one (e.g. had extra leading '+' or '0').
     eParseHTMLInteger_NonStandard           = 1 << 1,
     eParseHTMLInteger_DidNotConsumeAllInput = 1 << 2,
     // Set if one or more error flags were set.
     eParseHTMLInteger_Error                 = 1 << 3,
     eParseHTMLInteger_ErrorNoValue          = 1 << 4,
-    eParseHTMLInteger_ErrorOverflow         = 1 << 5
+    eParseHTMLInteger_ErrorOverflow         = 1 << 5,
+    // Use this flag to detect the difference between overflow and underflow
+    eParseHTMLInteger_Negative              = 1 << 6,
   };
   static int32_t ParseHTMLInteger(const nsAString& aValue,
                                   ParseHTMLIntegerResultFlags *aResult);
 
   /**
    * Parse a margin string of format 'top, right, bottom, left' into
    * an nsIntMargin.
    *
--- a/dom/html/HTMLTableCellElement.cpp
+++ b/dom/html/HTMLTableCellElement.cpp
@@ -393,36 +393,26 @@ HTMLTableCellElement::ParseAttribute(int
     /* ignore these attributes, stored simply as strings
        abbr, axis, ch, headers
     */
     if (aAttribute == nsGkAtoms::charoff) {
       /* attributes that resolve to integers with a min of 0 */
       return aResult.ParseIntWithBounds(aValue, 0);
     }
     if (aAttribute == nsGkAtoms::colspan) {
-      bool res = aResult.ParseIntWithBounds(aValue, -1);
-      if (res) {
-        int32_t val = aResult.GetIntegerValue();
-        // reset large colspan values as IE and opera do
-        if (val > MAX_COLSPAN || val <= 0) {
-          aResult.SetTo(1, &aValue);
-        }
-      }
-      return res;
+      aResult.ParseClampedNonNegativeInt(aValue, 1, 1, MAX_COLSPAN);
+      return true;
     }
     if (aAttribute == nsGkAtoms::rowspan) {
-      bool res = aResult.ParseIntWithBounds(aValue, -1, MAX_ROWSPAN);
-      if (res) {
-        int32_t val = aResult.GetIntegerValue();
-        // quirks mode does not honor the special html 4 value of 0
-        if (val < 0 || (0 == val && InNavQuirksMode(OwnerDoc()))) {
-          aResult.SetTo(1, &aValue);
-        }
+      aResult.ParseClampedNonNegativeInt(aValue, 1, 0, MAX_ROWSPAN);
+      // quirks mode does not honor the special html 4 value of 0
+      if (aResult.GetIntegerValue() == 0 && InNavQuirksMode(OwnerDoc())) {
+        aResult.SetTo(1, &aValue);
       }
-      return res;
+      return true;
     }
     if (aAttribute == nsGkAtoms::height) {
       return aResult.ParseSpecialIntValue(aValue);
     }
     if (aAttribute == nsGkAtoms::width) {
       return aResult.ParseSpecialIntValue(aValue);
     }
     if (aAttribute == nsGkAtoms::align) {
--- a/dom/html/HTMLTableCellElement.h
+++ b/dom/html/HTMLTableCellElement.h
@@ -32,25 +32,25 @@ public:
   NS_DECL_NSIDOMHTMLTABLECELLELEMENT
 
   uint32_t ColSpan() const
   {
     return GetIntAttr(nsGkAtoms::colspan, 1);
   }
   void SetColSpan(uint32_t aColSpan, ErrorResult& aError)
   {
-    SetHTMLIntAttr(nsGkAtoms::colspan, aColSpan, aError);
+    SetUnsignedIntAttr(nsGkAtoms::colspan, aColSpan, 1, aError);
   }
   uint32_t RowSpan() const
   {
     return GetIntAttr(nsGkAtoms::rowspan, 1);
   }
   void SetRowSpan(uint32_t aRowSpan, ErrorResult& aError)
   {
-    SetHTMLIntAttr(nsGkAtoms::rowspan, aRowSpan, aError);
+    SetUnsignedIntAttr(nsGkAtoms::rowspan, aRowSpan, 1, aError);
   }
   //already_AddRefed<nsDOMTokenList> Headers() const;
   void GetHeaders(DOMString& aHeaders)
   {
     GetHTMLAttr(nsGkAtoms::headers, aHeaders);
   }
   void SetHeaders(const nsAString& aHeaders, ErrorResult& aError)
   {
--- a/testing/web-platform/meta/html/dom/reflection-tabular.html.ini
+++ b/testing/web-platform/meta/html/dom/reflection-tabular.html.ini
@@ -1,485 +1,14 @@
 [reflection-tabular.html]
   type: testharness
-  [table.tabIndex: setAttribute() to object "3" followed by getAttribute()]
-    expected: FAIL
-
-  [table.tabIndex: setAttribute() to object "3" followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: typeof IDL attribute]
-    expected: FAIL
-
-  [table.sortable: IDL get with DOM attribute unset]
-    expected: FAIL
-
-  [table.sortable: setAttribute() to "" followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: setAttribute() to " foo " followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: setAttribute() to undefined followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: setAttribute() to null followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: setAttribute() to 7 followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: setAttribute() to 1.5 followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: setAttribute() to true followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: setAttribute() to false followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: setAttribute() to object "[object Object\]" followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: setAttribute() to NaN followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: setAttribute() to Infinity followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: setAttribute() to -Infinity followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: setAttribute() to "\\0" followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: setAttribute() to object "test-toString" followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: setAttribute() to object "test-valueOf" followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: setAttribute() to "sortable" followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: IDL set to "" followed by hasAttribute()]
-    expected: FAIL
-
-  [table.sortable: IDL set to "" followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: IDL set to " foo " followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: IDL set to undefined followed by hasAttribute()]
-    expected: FAIL
-
-  [table.sortable: IDL set to undefined followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: IDL set to null followed by hasAttribute()]
-    expected: FAIL
-
-  [table.sortable: IDL set to null followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: IDL set to 7 followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: IDL set to 1.5 followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: IDL set to false followed by hasAttribute()]
-    expected: FAIL
-
-  [table.sortable: IDL set to object "[object Object\]" followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: IDL set to NaN followed by hasAttribute()]
-    expected: FAIL
-
-  [table.sortable: IDL set to NaN followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: IDL set to Infinity followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: IDL set to -Infinity followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: IDL set to "\\0" followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: IDL set to object "test-toString" followed by IDL get]
-    expected: FAIL
-
-  [table.sortable: IDL set to object "test-valueOf" followed by IDL get]
-    expected: FAIL
-
-  [caption.tabIndex: setAttribute() to object "3" followed by getAttribute()]
-    expected: FAIL
-
-  [caption.tabIndex: setAttribute() to object "3" followed by IDL get]
-    expected: FAIL
-
-  [colgroup.tabIndex: setAttribute() to object "3" followed by getAttribute()]
-    expected: FAIL
-
-  [colgroup.tabIndex: setAttribute() to object "3" followed by IDL get]
-    expected: FAIL
-
-  [colgroup.span: setAttribute() to 2147483647 followed by IDL get]
-    expected: FAIL
-
-  [colgroup.span: IDL set to 0 must throw INDEX_SIZE_ERR]
-    expected: FAIL
-
-  [colgroup.span: IDL set to 2147483647 followed by IDL get]
-    expected: FAIL
-
-  [col.tabIndex: setAttribute() to object "3" followed by getAttribute()]
-    expected: FAIL
-
-  [col.tabIndex: setAttribute() to object "3" followed by IDL get]
-    expected: FAIL
-
-  [col.span: setAttribute() to 2147483647 followed by IDL get]
-    expected: FAIL
-
-  [col.span: IDL set to 0 must throw INDEX_SIZE_ERR]
-    expected: FAIL
-
-  [col.span: IDL set to 2147483647 followed by IDL get]
-    expected: FAIL
-
-  [tbody.tabIndex: setAttribute() to object "3" followed by getAttribute()]
-    expected: FAIL
-
-  [tbody.tabIndex: setAttribute() to object "3" followed by IDL get]
-    expected: FAIL
-
-  [thead.tabIndex: setAttribute() to object "3" followed by getAttribute()]
-    expected: FAIL
-
-  [thead.tabIndex: setAttribute() to object "3" followed by IDL get]
-    expected: FAIL
-
-  [tfoot.tabIndex: setAttribute() to object "3" followed by getAttribute()]
-    expected: FAIL
-
-  [tfoot.tabIndex: setAttribute() to object "3" followed by IDL get]
-    expected: FAIL
-
-  [tr.tabIndex: setAttribute() to object "3" followed by getAttribute()]
-    expected: FAIL
-
-  [tr.tabIndex: setAttribute() to object "3" followed by IDL get]
-    expected: FAIL
-
-  [td.tabIndex: setAttribute() to object "3" followed by getAttribute()]
-    expected: FAIL
-
-  [td.tabIndex: setAttribute() to object "3" followed by IDL get]
-    expected: FAIL
-
-  [td.colSpan: setAttribute() to 2147483647 followed by IDL get]
-    expected: FAIL
-
-  [td.colSpan: IDL set to 2147483647 followed by IDL get]
-    expected: FAIL
-
-  [td.rowSpan: setAttribute() to 2147483647 followed by IDL get]
-    expected: FAIL
-
-  [td.rowSpan: IDL set to 2147483647 followed by IDL get]
-    expected: FAIL
-
-  [th.tabIndex: setAttribute() to object "3" followed by getAttribute()]
-    expected: FAIL
-
-  [th.tabIndex: setAttribute() to object "3" followed by IDL get]
-    expected: FAIL
-
-  [th.colSpan: setAttribute() to 2147483647 followed by IDL get]
-    expected: FAIL
-
-  [th.colSpan: IDL set to 2147483647 followed by IDL get]
-    expected: FAIL
-
-  [th.rowSpan: setAttribute() to 2147483647 followed by IDL get]
-    expected: FAIL
-
-  [th.rowSpan: IDL set to 2147483647 followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: typeof IDL attribute]
-    expected: FAIL
-
-  [th.sorted: IDL get with DOM attribute unset]
-    expected: FAIL
-
-  [th.sorted: setAttribute() to "" followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: setAttribute() to " \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07 \\b\\t\\n\\v\\f\\r\\x0e\\x0f \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17 \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f  foo " followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: setAttribute() to undefined followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: setAttribute() to 7 followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: setAttribute() to 1.5 followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: setAttribute() to true followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: setAttribute() to false followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: setAttribute() to object "[object Object\]" followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: setAttribute() to NaN followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: setAttribute() to Infinity followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: setAttribute() to -Infinity followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: setAttribute() to "\\0" followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: setAttribute() to null followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: setAttribute() to object "test-toString" followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: setAttribute() to object "test-valueOf" followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: IDL set to "" followed by getAttribute()]
-    expected: FAIL
-
-  [th.sorted: IDL set to " \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07 \\b\\t\\n\\v\\f\\r\\x0e\\x0f \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17 \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f  foo " followed by getAttribute()]
-    expected: FAIL
-
-  [th.sorted: IDL set to undefined followed by getAttribute()]
-    expected: FAIL
-
-  [th.sorted: IDL set to undefined followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: IDL set to 7 followed by getAttribute()]
-    expected: FAIL
-
-  [th.sorted: IDL set to 7 followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: IDL set to 1.5 followed by getAttribute()]
-    expected: FAIL
-
-  [th.sorted: IDL set to 1.5 followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: IDL set to true followed by getAttribute()]
-    expected: FAIL
-
-  [th.sorted: IDL set to true followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: IDL set to false followed by getAttribute()]
-    expected: FAIL
-
-  [th.sorted: IDL set to false followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: IDL set to object "[object Object\]" followed by getAttribute()]
-    expected: FAIL
-
-  [th.sorted: IDL set to object "[object Object\]" followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: IDL set to NaN followed by getAttribute()]
-    expected: FAIL
-
-  [th.sorted: IDL set to NaN followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: IDL set to Infinity followed by getAttribute()]
-    expected: FAIL
-
-  [th.sorted: IDL set to Infinity followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: IDL set to -Infinity followed by getAttribute()]
-    expected: FAIL
-
-  [th.sorted: IDL set to -Infinity followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: IDL set to "\\0" followed by getAttribute()]
-    expected: FAIL
-
-  [th.sorted: IDL set to null followed by getAttribute()]
-    expected: FAIL
-
-  [th.sorted: IDL set to null followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: IDL set to object "test-toString" followed by getAttribute()]
-    expected: FAIL
-
-  [th.sorted: IDL set to object "test-toString" followed by IDL get]
-    expected: FAIL
-
-  [th.sorted: IDL set to object "test-valueOf" followed by IDL get]
-    expected: FAIL
-
-  [td.colSpan: IDL set to 2147483648 followed by getAttribute()]
-    expected: FAIL
-
-  [td.colSpan: IDL set to 4294967295 followed by getAttribute()]
-    expected: FAIL
-
-  [td.rowSpan: IDL set to 2147483648 followed by getAttribute()]
-    expected: FAIL
-
-  [td.rowSpan: IDL set to 4294967295 followed by getAttribute()]
-    expected: FAIL
-
-  [th.colSpan: IDL set to 2147483648 followed by getAttribute()]
-    expected: FAIL
-
-  [th.colSpan: IDL set to 4294967295 followed by getAttribute()]
-    expected: FAIL
-
-  [th.rowSpan: IDL set to 2147483648 followed by getAttribute()]
-    expected: FAIL
-
-  [th.rowSpan: IDL set to 4294967295 followed by getAttribute()]
-    expected: FAIL
-
-  [td.colSpan: setAttribute() to 0 followed by IDL get]
-    expected: FAIL
-
-  [td.colSpan: setAttribute() to "-0" followed by IDL get]
-    expected: FAIL
-
-  [td.colSpan: setAttribute() to "0" followed by IDL get]
-    expected: FAIL
-
-  [td.colSpan: IDL set to 0 followed by IDL get]
-    expected: FAIL
-
-  [td.colSpan: IDL set to "-0" followed by IDL get]
-    expected: FAIL
-
-  [th.colSpan: setAttribute() to 0 followed by IDL get]
-    expected: FAIL
-
-  [th.colSpan: setAttribute() to "-0" followed by IDL get]
-    expected: FAIL
-
-  [th.colSpan: setAttribute() to "0" followed by IDL get]
-    expected: FAIL
-
-  [th.colSpan: IDL set to 0 followed by IDL get]
-    expected: FAIL
-
-  [th.colSpan: IDL set to "-0" followed by IDL get]
-    expected: FAIL
-
   [colgroup.span: setAttribute() to 2147483647]
     expected: FAIL
 
   [colgroup.span: IDL set to 2147483647]
     expected: FAIL
 
   [col.span: setAttribute() to 2147483647]
     expected: FAIL
 
   [col.span: IDL set to 2147483647]
     expected: FAIL
 
-  [td.colSpan: setAttribute() to 0]
-    expected: FAIL
-
-  [td.colSpan: setAttribute() to 2147483647]
-    expected: FAIL
-
-  [td.colSpan: setAttribute() to "-0"]
-    expected: FAIL
-
-  [td.colSpan: setAttribute() to "0"]
-    expected: FAIL
-
-  [td.colSpan: IDL set to 0]
-    expected: FAIL
-
-  [td.colSpan: IDL set to 2147483647]
-    expected: FAIL
-
-  [td.colSpan: IDL set to "-0"]
-    expected: FAIL
-
-  [td.colSpan: IDL set to 2147483648]
-    expected: FAIL
-
-  [td.colSpan: IDL set to 4294967295]
-    expected: FAIL
-
-  [td.rowSpan: setAttribute() to 2147483647]
-    expected: FAIL
-
-  [td.rowSpan: IDL set to 2147483647]
-    expected: FAIL
-
-  [td.rowSpan: IDL set to 2147483648]
-    expected: FAIL
-
-  [td.rowSpan: IDL set to 4294967295]
-    expected: FAIL
-
-  [th.colSpan: setAttribute() to 0]
-    expected: FAIL
-
-  [th.colSpan: setAttribute() to 2147483647]
-    expected: FAIL
-
-  [th.colSpan: setAttribute() to "-0"]
-    expected: FAIL
-
-  [th.colSpan: setAttribute() to "0"]
-    expected: FAIL
-
-  [th.colSpan: IDL set to 0]
-    expected: FAIL
-
-  [th.colSpan: IDL set to 2147483647]
-    expected: FAIL
-
-  [th.colSpan: IDL set to "-0"]
-    expected: FAIL
-
-  [th.colSpan: IDL set to 2147483648]
-    expected: FAIL
-
-  [th.colSpan: IDL set to 4294967295]
-    expected: FAIL
-
-  [th.rowSpan: setAttribute() to 2147483647]
-    expected: FAIL
-
-  [th.rowSpan: IDL set to 2147483647]
-    expected: FAIL
-
-  [th.rowSpan: IDL set to 2147483648]
-    expected: FAIL
-
-  [th.rowSpan: IDL set to 4294967295]
-    expected: FAIL
-
--- a/testing/web-platform/tests/html/dom/elements-tabular.js
+++ b/testing/web-platform/tests/html/dom/elements-tabular.js
@@ -63,18 +63,18 @@ var tabularElements = {
     align: "string",
     ch: {type: "string", domAttrName: "char"},
     chOff: {type: "string", domAttrName: "charoff"},
     vAlign: "string",
     bgColor: {type: "string", treatNullAsEmptyString: true},
   },
   td: {
     // HTMLTableCellElement (Conforming)
-    colSpan: {type: "unsigned long", defaultVal: 1},
-    rowSpan: {type: "unsigned long", defaultVal: 1},
+    colSpan: {type: "clamped unsigned long", defaultVal: 1, min: 1, max: 1000},
+    rowSpan: {type: "clamped unsigned long", defaultVal: 1, min: 0, max: 65534},
     headers: "settable tokenlist",
     scope: {type: "enum", keywords: ["row", "col", "rowgroup", "colgroup"]},
     abbr: "string",
 
     // HTMLTableCellElement (Obsolete)
     align: "string",
     axis: "string",
     height: "string",
@@ -82,18 +82,18 @@ var tabularElements = {
     ch: {type: "string", domAttrName: "char"},
     chOff: {type: "string", domAttrName: "charoff"},
     noWrap: "boolean",
     vAlign: "string",
     bgColor: {type: "string", treatNullAsEmptyString: true},
   },
   th: {
     // HTMLTableCellElement (Conforming)
-    colSpan: {type: "unsigned long", defaultVal: 1},
-    rowSpan: {type: "unsigned long", defaultVal: 1},
+    colSpan: {type: "clamped unsigned long", defaultVal: 1, min: 1, max: 1000},
+    rowSpan: {type: "clamped unsigned long", defaultVal: 1, min: 0, max: 65534},
     headers: "settable tokenlist",
     scope: {type: "enum", keywords: ["row", "col", "rowgroup", "colgroup"]},
     abbr: "string",
 
     // HTMLTableCellElement (Obsolete)
     align: "string",
     axis: "string",
     height: "string",
--- a/testing/web-platform/tests/html/dom/reflection.js
+++ b/testing/web-platform/tests/html/dom/reflection.js
@@ -450,16 +450,47 @@ ReflectionTests.typeMap = {
                     return null;
                 }
                 return parsed;
             },
             "idlTests":       [0, 1, maxInt, maxInt + 1, maxUnsigned],
             "idlDomExpected": [null, 1, maxInt, null, null]
     },
     /**
+     * "If a reflecting IDL attribute has an unsigned integer type (unsigned
+     * long) that is clamped to the range [min, max], then on getting, the
+     * content attribute must first be parsed according to the rules for
+     * parsing non-negative integers, and if that is successful, and the value
+     * is between min and max inclusive, the resulting value must be returned.
+     * If it fails, the default value must be returned. If it succeeds but the
+     * value is less than min, min must be returned. If it succeeds but the
+     * value is greater than max, max must be returned. On setting, it behaves
+     * the same as a regular reflected unsigned integer."
+     *
+     * The data object passed to reflects must contain the keys defaultVal,
+     * min, and max.  As with enum, domExpected is generated later once we have
+     * access to the min and max.
+     */
+    "clamped unsigned long": {
+        "jsType": "number",
+        "domTests": [minInt - 1, minInt, -36,  -1,   0,    1, maxInt,
+                     maxInt + 1, maxUnsigned, maxUnsigned + 1, "", "-1", "-0", "0", "1",
+                     "\u00097", "\u000B7", "\u000C7", "\u00207", "\u00A07", "\uFEFF7",
+                     "\u000A7", "\u000D7", "\u20287", "\u20297", "\u16807", "\u180E7",
+                     "\u20007", "\u20017", "\u20027", "\u20037", "\u20047", "\u20057",
+                     "\u20067", "\u20077", "\u20087", "\u20097", "\u200A7", "\u202F7",
+                     "\u30007",
+                     " " + binaryString + " foo ", undefined, 1.5, true, false,
+                     {"test": 6}, NaN, +Infinity, -Infinity, "\0",
+                     {toString:function() {return 2;}, valueOf: null},
+                     {valueOf:function() {return 3;}}],
+        "idlTests": [0, 1, 257, maxInt, "-0", maxInt + 1, maxUnsigned],
+        "idlDomExpected": [0, 1, 257, maxInt, 0, null, null],
+    },
+    /**
      * "If a reflecting IDL attribute is a floating point number type (double),
      * then, on getting, the content attribute must be parsed according to the
      * rules for parsing floating point number values, and if that is
      * successful, the resulting value must be returned. If, on the other hand,
      * it fails, or if the attribute is absent, the default value must be
      * returned instead, or 0.0 if there is no default value. On setting, the
      * given value must be converted to the best representation of the number
      * as a floating point number and then that string must be used as the new
@@ -676,16 +707,64 @@ ReflectionTests.reflects = function(data
         if ("treatNullAsEmptyString" in data) {
             for (var i = 0; i < idlTests.length; i++) {
                 if (idlTests[i] === null) {
                     idlDomExpected[i] = idlIdlExpected[i] = "";
                 }
             }
         }
         break;
+
+        case "clamped unsigned long":
+        [data.min - 1, data.min, data.max, data.max + 1].forEach(function(val) {
+          if (domTests.indexOf(val) == -1) {
+            domTests.push(val);
+          }
+          if (idlTests.indexOf(val) == -1 && 0 <= val && val <= maxUnsigned) {
+            idlTests.push(val);
+            if (typeof val != "number") {
+              val = ReflectionTests.parseNonneg(val);
+            }
+            idlDomExpected.push(val > maxInt ? null : val);
+          }
+        });
+
+        // Rewrite expected values
+        domExpected = domTests.map(function(val) {
+            var parsed = ReflectionTests.parseNonneg(String(val));
+            if (parsed === false) {
+                return defaultVal;
+            }
+            if (parsed < data.min) {
+              return data.min;
+            }
+            if (parsed > data.max) {
+              return data.max;
+            }
+            return parsed;
+        });
+        idlIdlExpected = idlTests.map(function(val) {
+            if (typeof val != "number") {
+              val = ReflectionTests.parseNonneg(val);
+            }
+            if (val < 0 || val > maxUnsigned) {
+              throw "Test bug: val should be an unsigned long";
+            }
+            if (val > maxInt) {
+              return defaultVal;
+            }
+            if (val < data.min) {
+              return data.min;
+            }
+            if (val > data.max) {
+              return data.max;
+            }
+            return val;
+        });
+        break;
     }
     if (domObj.tagName.toLowerCase() == "canvas" && (domName == "width" || domName == "height")) {
         // Opera tries to allocate a canvas with the given width and height, so
         // it OOMs when given excessive sizes.  This is permissible under the
         // hardware-limitations clause, so cut out those checks.  TODO: Must be
         // a way to make this more succinct.
         domTests = domTests.filter(function(element, index, array) { return domExpected[index] < 1000; });
         domExpected = domExpected.filter(function(element, index, array) { return element < 1000; });