tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

reflect.js (28750B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 /**
      5 * reflect.js is a collection of methods to test HTML attribute reflection.
      6 * Each of attribute is reflected differently, depending on various parameters,
      7 * see:
      8 * http://www.whatwg.org/html/#reflecting-content-attributes-in-idl-attributes
      9 *
     10 * Do not forget to add these line at the beginning of each new reflect* method:
     11 * ok(attr in element, attr + " should be an IDL attribute of this element");
     12 * is(typeof element[attr], <type>, attr + " IDL attribute should be a <type>");
     13 */
     14 
     15 /**
     16 * Checks that a given attribute is correctly reflected as a string.
     17 *
     18 * @param aParameters   Object    object containing the parameters, which are:
     19 *  - element           Element   node to test
     20 *  - attribute         String    name of the attribute
     21 *     OR
     22 *    attribute         Object    object containing two attributes, 'content' and 'idl'
     23 *  - otherValues       Array     [optional] other values to test in addition of the default ones
     24 *  - extendedAttributes Object   object which can have 'TreatNullAs': "EmptyString"
     25 */
     26 function reflectString(aParameters) {
     27  var element = aParameters.element;
     28  var contentAttr =
     29    typeof aParameters.attribute === "string"
     30      ? aParameters.attribute
     31      : aParameters.attribute.content;
     32  var idlAttr =
     33    typeof aParameters.attribute === "string"
     34      ? aParameters.attribute
     35      : aParameters.attribute.idl;
     36  var otherValues =
     37    aParameters.otherValues !== undefined ? aParameters.otherValues : [];
     38  var treatNullAs = aParameters.extendedAttributes
     39    ? aParameters.extendedAttributes.TreatNullAs
     40    : null;
     41 
     42  ok(
     43    idlAttr in element,
     44    idlAttr + " should be an IDL attribute of this element"
     45  );
     46  is(
     47    typeof element[idlAttr],
     48    "string",
     49    "'" + idlAttr + "' IDL attribute should be a string"
     50  );
     51 
     52  // Tests when the attribute isn't set.
     53  is(
     54    element.getAttribute(contentAttr),
     55    null,
     56    "When not set, the content attribute should be null."
     57  );
     58  is(
     59    element[idlAttr],
     60    "",
     61    "When not set, the IDL attribute should return the empty string"
     62  );
     63 
     64  /**
     65   * TODO: as long as null stringification doesn't follow the WebIDL
     66   * specifications, don't add it to the loop below and keep it here.
     67   */
     68  element.setAttribute(contentAttr, null);
     69  is(
     70    element.getAttribute(contentAttr),
     71    "null",
     72    "null should have been stringified to 'null' for '" + contentAttr + "'"
     73  );
     74  is(
     75    element[idlAttr],
     76    "null",
     77    "null should have been stringified to 'null' for '" + idlAttr + "'"
     78  );
     79  element.removeAttribute(contentAttr);
     80 
     81  element[idlAttr] = null;
     82  if (treatNullAs == "EmptyString") {
     83    is(
     84      element.getAttribute(contentAttr),
     85      "",
     86      "null should have been stringified to '' for '" + contentAttr + "'"
     87    );
     88    is(
     89      element[idlAttr],
     90      "",
     91      "null should have been stringified to '' for '" + idlAttr + "'"
     92    );
     93  } else {
     94    is(
     95      element.getAttribute(contentAttr),
     96      "null",
     97      "null should have been stringified to 'null' for '" + contentAttr + "'"
     98    );
     99    is(
    100      element[idlAttr],
    101      "null",
    102      "null should have been stringified to 'null' for '" + contentAttr + "'"
    103    );
    104  }
    105  element.removeAttribute(contentAttr);
    106 
    107  // Tests various strings.
    108  var stringsToTest = [
    109    // [ test value, expected result ]
    110    ["", ""],
    111    ["null", "null"],
    112    ["undefined", "undefined"],
    113    ["foo", "foo"],
    114    [contentAttr, contentAttr],
    115    [idlAttr, idlAttr],
    116    // TODO: uncomment this when null stringification will follow the specs.
    117    // [ null, "null" ],
    118    [undefined, "undefined"],
    119    [true, "true"],
    120    [false, "false"],
    121    [42, "42"],
    122    // ES5, verse 8.12.8.
    123    [
    124      {
    125        toString() {
    126          return "foo";
    127        },
    128      },
    129      "foo",
    130    ],
    131    [
    132      {
    133        valueOf() {
    134          return "foo";
    135        },
    136      },
    137      "[object Object]",
    138    ],
    139    [
    140      {
    141        valueOf() {
    142          return "quux";
    143        },
    144        toString: undefined,
    145      },
    146      "quux",
    147    ],
    148    [
    149      {
    150        valueOf() {
    151          return "foo";
    152        },
    153        toString() {
    154          return "bar";
    155        },
    156      },
    157      "bar",
    158    ],
    159  ];
    160 
    161  otherValues.forEach(function (v) {
    162    stringsToTest.push([v, v]);
    163  });
    164 
    165  stringsToTest.forEach(function ([v, r]) {
    166    element.setAttribute(contentAttr, v);
    167    is(
    168      element[idlAttr],
    169      r,
    170      "IDL attribute '" +
    171        idlAttr +
    172        "' should return the value it has been set to."
    173    );
    174    is(
    175      element.getAttribute(contentAttr),
    176      r,
    177      "Content attribute '" +
    178        contentAttr +
    179        "'should return the value it has been set to."
    180    );
    181    element.removeAttribute(contentAttr);
    182 
    183    element[idlAttr] = v;
    184    is(
    185      element[idlAttr],
    186      r,
    187      "IDL attribute '" +
    188        idlAttr +
    189        "' should return the value it has been set to."
    190    );
    191    is(
    192      element.getAttribute(contentAttr),
    193      r,
    194      "Content attribute '" +
    195        contentAttr +
    196        "' should return the value it has been set to."
    197    );
    198    element.removeAttribute(contentAttr);
    199  });
    200 
    201  // Tests after removeAttribute() is called. Should be equivalent with not set.
    202  is(
    203    element.getAttribute(contentAttr),
    204    null,
    205    "When not set, the content attribute should be null."
    206  );
    207  is(
    208    element[idlAttr],
    209    "",
    210    "When not set, the IDL attribute should return the empty string"
    211  );
    212 }
    213 
    214 /**
    215 * Checks that a given attribute name for a given element is correctly reflected
    216 * as an unsigned int.
    217 *
    218 * @param aParameters   Object    object containing the parameters, which are:
    219 *  - element           Element   node to test on
    220 *  - attribute         String    name of the attribute
    221 *  - nonZero           Boolean   whether the attribute should be non-null
    222 *  - defaultValue      Integer   [optional] default value, if different from the default one
    223 */
    224 function reflectUnsignedInt(aParameters) {
    225  var element = aParameters.element;
    226  var attr = aParameters.attribute;
    227  var nonZero = aParameters.nonZero;
    228  var defaultValue = aParameters.defaultValue;
    229  var fallback = aParameters.fallback;
    230 
    231  if (defaultValue === undefined) {
    232    if (nonZero) {
    233      defaultValue = 1;
    234    } else {
    235      defaultValue = 0;
    236    }
    237  }
    238 
    239  if (fallback === undefined) {
    240    fallback = false;
    241  }
    242 
    243  ok(attr in element, attr + " should be an IDL attribute of this element");
    244  is(
    245    typeof element[attr],
    246    "number",
    247    attr + " IDL attribute should be a number"
    248  );
    249 
    250  // Check default value.
    251  is(element[attr], defaultValue, "default value should be " + defaultValue);
    252  ok(!element.hasAttribute(attr), attr + " shouldn't be present");
    253 
    254  var values = [1, 3, 42, 2147483647];
    255 
    256  for (var value of values) {
    257    element[attr] = value;
    258    is(element[attr], value, "." + attr + " should be equals " + value);
    259    is(
    260      element.getAttribute(attr),
    261      String(value),
    262      "@" + attr + " should be equals " + value
    263    );
    264 
    265    element.setAttribute(attr, value);
    266    is(element[attr], value, "." + attr + " should be equals " + value);
    267    is(
    268      element.getAttribute(attr),
    269      String(value),
    270      "@" + attr + " should be equals " + value
    271    );
    272  }
    273 
    274  // -3000000000 is equivalent to 1294967296 when using the IDL attribute.
    275  element[attr] = -3000000000;
    276  is(element[attr], 1294967296, "." + attr + " should be equals to 1294967296");
    277  is(
    278    element.getAttribute(attr),
    279    "1294967296",
    280    "@" + attr + " should be equals to 1294967296"
    281  );
    282 
    283  // When setting the content attribute, it's a string so it will be invalid.
    284  element.setAttribute(attr, -3000000000);
    285  is(
    286    element.getAttribute(attr),
    287    "-3000000000",
    288    "@" + attr + " should be equals to " + -3000000000
    289  );
    290  is(
    291    element[attr],
    292    defaultValue,
    293    "." + attr + " should be equals to " + defaultValue
    294  );
    295 
    296  // When interpreted as unsigned 32-bit integers, all of these fall between
    297  // 2^31 and 2^32 - 1, so per spec they return the default value.
    298  var nonValidValues = [-2147483648, -1, 3147483647];
    299 
    300  for (var value of nonValidValues) {
    301    element[attr] = value;
    302    is(
    303      element.getAttribute(attr),
    304      String(defaultValue),
    305      "@" + attr + " should be equals to " + defaultValue
    306    );
    307    is(
    308      element[attr],
    309      defaultValue,
    310      "." + attr + " should be equals to " + defaultValue
    311    );
    312  }
    313 
    314  for (var values of nonValidValues) {
    315    element.setAttribute(attr, values[0]);
    316    is(
    317      element.getAttribute(attr),
    318      String(values[0]),
    319      "@" + attr + " should be equals to " + values[0]
    320    );
    321    is(
    322      element[attr],
    323      defaultValue,
    324      "." + attr + " should be equals to " + defaultValue
    325    );
    326  }
    327 
    328  // Setting to 0 should throw an error if nonZero is true.
    329  var caught = false;
    330  try {
    331    element[attr] = 0;
    332  } catch (e) {
    333    caught = true;
    334    is(e.name, "IndexSizeError", "exception should be IndexSizeError");
    335    is(
    336      e.code,
    337      DOMException.INDEX_SIZE_ERR,
    338      "exception code should be INDEX_SIZE_ERR"
    339    );
    340  }
    341 
    342  if (nonZero && !fallback) {
    343    ok(caught, "an exception should have been caught");
    344  } else {
    345    ok(!caught, "no exception should have been caught");
    346  }
    347 
    348  // If 0 is set in @attr, it will be ignored when calling .attr.
    349  element.setAttribute(attr, "0");
    350  is(element.getAttribute(attr), "0", "@" + attr + " should be equals to 0");
    351  if (nonZero) {
    352    is(
    353      element[attr],
    354      defaultValue,
    355      "." + attr + " should be equals to " + defaultValue
    356    );
    357  } else {
    358    is(element[attr], 0, "." + attr + " should be equals to 0");
    359  }
    360 }
    361 
    362 /**
    363 * Checks that a given attribute is correctly reflected as limited to known
    364 * values enumerated attribute.
    365 *
    366 * @param aParameters     Object   object containing the parameters, which are:
    367 *  - element             Element  node to test on
    368 *  - attribute           String   name of the attribute
    369 *     OR
    370 *    attribute           Object   object containing two attributes, 'content' and 'idl'
    371 *  - validValues         Array    valid values we support
    372 *  - invalidValues       Array    invalid values
    373 *  - defaultValue        String   [optional] default value when no valid value is set
    374 *     OR
    375 *    defaultValue        Object   [optional] object containing two attributes, 'invalid' and 'missing'
    376 *  - unsupportedValues   Array    [optional] valid values we do not support
    377 *  - nullable            boolean  [optional] whether the attribute is nullable
    378 */
    379 function reflectLimitedEnumerated(aParameters) {
    380  var element = aParameters.element;
    381  var contentAttr =
    382    typeof aParameters.attribute === "string"
    383      ? aParameters.attribute
    384      : aParameters.attribute.content;
    385  var idlAttr =
    386    typeof aParameters.attribute === "string"
    387      ? aParameters.attribute
    388      : aParameters.attribute.idl;
    389  var validValues = aParameters.validValues;
    390  var invalidValues = aParameters.invalidValues;
    391  var defaultValueInvalid =
    392    aParameters.defaultValue === undefined
    393      ? ""
    394      : typeof aParameters.defaultValue === "string"
    395        ? aParameters.defaultValue
    396        : aParameters.defaultValue.invalid;
    397  var defaultValueMissing =
    398    aParameters.defaultValue === undefined
    399      ? ""
    400      : typeof aParameters.defaultValue === "string"
    401        ? aParameters.defaultValue
    402        : aParameters.defaultValue.missing;
    403  var unsupportedValues =
    404    aParameters.unsupportedValues !== undefined
    405      ? aParameters.unsupportedValues
    406      : [];
    407  var nullable = aParameters.nullable;
    408 
    409  ok(
    410    idlAttr in element,
    411    idlAttr + " should be an IDL attribute of this element"
    412  );
    413  if (nullable) {
    414    // The missing value default is null, which is typeof == "object"
    415    is(
    416      typeof element[idlAttr],
    417      "object",
    418      "'" +
    419        idlAttr +
    420        "' IDL attribute should be null, which has typeof == object"
    421    );
    422    is(
    423      element[idlAttr],
    424      null,
    425      "'" + idlAttr + "' IDL attribute should be null"
    426    );
    427  } else {
    428    is(
    429      typeof element[idlAttr],
    430      "string",
    431      "'" + idlAttr + "' IDL attribute should be a string"
    432    );
    433  }
    434 
    435  if (nullable) {
    436    element.setAttribute(contentAttr, "something");
    437    // Now it will be a string
    438    is(
    439      typeof element[idlAttr],
    440      "string",
    441      "'" + idlAttr + "' IDL attribute should be a string"
    442    );
    443  }
    444 
    445  // Explicitly check the default value.
    446  element.removeAttribute(contentAttr);
    447  is(
    448    element[idlAttr],
    449    defaultValueMissing,
    450    "When no attribute is set, the value should be the default value."
    451  );
    452 
    453  // Check valid values.
    454  validValues.forEach(function (v) {
    455    element.setAttribute(contentAttr, v);
    456    is(
    457      element[idlAttr],
    458      v,
    459      "'" + v + "' should be accepted as a valid value for " + idlAttr
    460    );
    461    is(
    462      element.getAttribute(contentAttr),
    463      v,
    464      "Content attribute should return the value it has been set to."
    465    );
    466    element.removeAttribute(contentAttr);
    467 
    468    element.setAttribute(contentAttr, v.toUpperCase());
    469    is(
    470      element[idlAttr],
    471      v,
    472      "Enumerated attributes should be case-insensitive."
    473    );
    474    is(
    475      element.getAttribute(contentAttr),
    476      v.toUpperCase(),
    477      "Content attribute should not be lower-cased."
    478    );
    479    element.removeAttribute(contentAttr);
    480 
    481    element[idlAttr] = v;
    482    is(
    483      element[idlAttr],
    484      v,
    485      "'" + v + "' should be accepted as a valid value for " + idlAttr
    486    );
    487    is(
    488      element.getAttribute(contentAttr),
    489      v,
    490      "Content attribute should return the value it has been set to."
    491    );
    492    element.removeAttribute(contentAttr);
    493 
    494    element[idlAttr] = v.toUpperCase();
    495    is(
    496      element[idlAttr],
    497      v,
    498      "Enumerated attributes should be case-insensitive."
    499    );
    500    is(
    501      element.getAttribute(contentAttr),
    502      v.toUpperCase(),
    503      "Content attribute should not be lower-cased."
    504    );
    505    element.removeAttribute(contentAttr);
    506  });
    507 
    508  // Check invalid values.
    509  invalidValues.forEach(function (v) {
    510    element.setAttribute(contentAttr, v);
    511    is(
    512      element[idlAttr],
    513      defaultValueInvalid,
    514      "When the content attribute is set to an invalid value, the default value should be returned."
    515    );
    516    is(
    517      element.getAttribute(contentAttr),
    518      v,
    519      "Content attribute should not have been changed."
    520    );
    521    element.removeAttribute(contentAttr);
    522 
    523    element[idlAttr] = v;
    524    is(
    525      element[idlAttr],
    526      defaultValueInvalid,
    527      "When the value is set to an invalid value, the default value should be returned."
    528    );
    529    is(
    530      element.getAttribute(contentAttr),
    531      v,
    532      "Content attribute should not have been changed."
    533    );
    534    element.removeAttribute(contentAttr);
    535  });
    536 
    537  // Check valid values we currently do not support.
    538  // Basically, it's like the checks for the valid values but with some todo's.
    539  unsupportedValues.forEach(function (v) {
    540    element.setAttribute(contentAttr, v);
    541    todo_is(
    542      element[idlAttr],
    543      v,
    544      "'" + v + "' should be accepted as a valid value for " + idlAttr
    545    );
    546    is(
    547      element.getAttribute(contentAttr),
    548      v,
    549      "Content attribute should return the value it has been set to."
    550    );
    551    element.removeAttribute(contentAttr);
    552 
    553    element.setAttribute(contentAttr, v.toUpperCase());
    554    todo_is(
    555      element[idlAttr],
    556      v,
    557      "Enumerated attributes should be case-insensitive."
    558    );
    559    is(
    560      element.getAttribute(contentAttr),
    561      v.toUpperCase(),
    562      "Content attribute should not be lower-cased."
    563    );
    564    element.removeAttribute(contentAttr);
    565 
    566    element[idlAttr] = v;
    567    todo_is(
    568      element[idlAttr],
    569      v,
    570      "'" + v + "' should be accepted as a valid value for " + idlAttr
    571    );
    572    is(
    573      element.getAttribute(contentAttr),
    574      v,
    575      "Content attribute should return the value it has been set to."
    576    );
    577    element.removeAttribute(contentAttr);
    578 
    579    element[idlAttr] = v.toUpperCase();
    580    todo_is(
    581      element[idlAttr],
    582      v,
    583      "Enumerated attributes should be case-insensitive."
    584    );
    585    is(
    586      element.getAttribute(contentAttr),
    587      v.toUpperCase(),
    588      "Content attribute should not be lower-cased."
    589    );
    590    element.removeAttribute(contentAttr);
    591  });
    592 
    593  if (nullable) {
    594    is(
    595      defaultValueMissing,
    596      null,
    597      "Missing default value should be null for nullable attributes"
    598    );
    599    ok(validValues.length, "We better have at least one valid value");
    600    element.setAttribute(contentAttr, validValues[0]);
    601    ok(
    602      element.hasAttribute(contentAttr),
    603      "Should have content attribute: we just set it"
    604    );
    605    element[idlAttr] = null;
    606    ok(
    607      !element.hasAttribute(contentAttr),
    608      "Should have removed content attribute"
    609    );
    610  }
    611 }
    612 
    613 /**
    614 * Checks that a given attribute is correctly reflected as a boolean.
    615 *
    616 * @param aParameters    Object    object containing the parameters, which are:
    617 *  - element            Element   node to test on
    618 *  - attribute          String    name of the attribute
    619 *     OR
    620 *    attribute          Object    object containing two attributes, 'content' and 'idl'
    621 */
    622 function reflectBoolean(aParameters) {
    623  var element = aParameters.element;
    624  var contentAttr =
    625    typeof aParameters.attribute === "string"
    626      ? aParameters.attribute
    627      : aParameters.attribute.content;
    628  var idlAttr =
    629    typeof aParameters.attribute === "string"
    630      ? aParameters.attribute
    631      : aParameters.attribute.idl;
    632 
    633  ok(
    634    idlAttr in element,
    635    idlAttr + " should be an IDL attribute of this element"
    636  );
    637  is(
    638    typeof element[idlAttr],
    639    "boolean",
    640    idlAttr + " IDL attribute should be a boolean"
    641  );
    642 
    643  // Tests when the attribute isn't set.
    644  is(
    645    element.getAttribute(contentAttr),
    646    null,
    647    "When not set, the content attribute should be null."
    648  );
    649  is(
    650    element[idlAttr],
    651    false,
    652    "When not set, the IDL attribute should return false"
    653  );
    654 
    655  /**
    656   * Test various values.
    657   * Each value to test is actually an object containing a 'value' property
    658   * containing the value to actually test, a 'stringified' property containing
    659   * the stringified value and a 'result' property containing the expected
    660   * result when the value is set to the IDL attribute.
    661   */
    662  var valuesToTest = [
    663    { value: true, stringified: "true", result: true },
    664    { value: false, stringified: "false", result: false },
    665    { value: "true", stringified: "true", result: true },
    666    { value: "false", stringified: "false", result: true },
    667    { value: "foo", stringified: "foo", result: true },
    668    { value: idlAttr, stringified: idlAttr, result: true },
    669    { value: contentAttr, stringified: contentAttr, result: true },
    670    { value: "null", stringified: "null", result: true },
    671    { value: "undefined", stringified: "undefined", result: true },
    672    { value: "", stringified: "", result: false },
    673    { value: undefined, stringified: "undefined", result: false },
    674    { value: null, stringified: "null", result: false },
    675    { value: +0, stringified: "0", result: false },
    676    { value: -0, stringified: "0", result: false },
    677    { value: NaN, stringified: "NaN", result: false },
    678    { value: 42, stringified: "42", result: true },
    679    { value: Infinity, stringified: "Infinity", result: true },
    680    { value: -Infinity, stringified: "-Infinity", result: true },
    681    // ES5, verse 9.2.
    682    {
    683      value: {
    684        toString() {
    685          return "foo";
    686        },
    687      },
    688      stringified: "foo",
    689      result: true,
    690    },
    691    {
    692      value: {
    693        valueOf() {
    694          return "foo";
    695        },
    696      },
    697      stringified: "[object Object]",
    698      result: true,
    699    },
    700    {
    701      value: {
    702        valueOf() {
    703          return "quux";
    704        },
    705        toString: undefined,
    706      },
    707      stringified: "quux",
    708      result: true,
    709    },
    710    {
    711      value: {
    712        valueOf() {
    713          return "foo";
    714        },
    715        toString() {
    716          return "bar";
    717        },
    718      },
    719      stringified: "bar",
    720      result: true,
    721    },
    722    {
    723      value: {
    724        valueOf() {
    725          return false;
    726        },
    727      },
    728      stringified: "[object Object]",
    729      result: true,
    730    },
    731    {
    732      value: { foo: false, bar: false },
    733      stringified: "[object Object]",
    734      result: true,
    735    },
    736    { value: {}, stringified: "[object Object]", result: true },
    737  ];
    738 
    739  valuesToTest.forEach(function (v) {
    740    element.setAttribute(contentAttr, v.value);
    741    is(
    742      element[idlAttr],
    743      true,
    744      "IDL attribute should return always return 'true' if the content attribute has been set"
    745    );
    746    is(
    747      element.getAttribute(contentAttr),
    748      v.stringified,
    749      "Content attribute should return the stringified value it has been set to."
    750    );
    751    element.removeAttribute(contentAttr);
    752 
    753    element[idlAttr] = v.value;
    754    is(element[idlAttr], v.result, "IDL attribute should return " + v.result);
    755    is(
    756      element.getAttribute(contentAttr),
    757      v.result ? "" : null,
    758      v.result
    759        ? "Content attribute should return the empty string."
    760        : "Content attribute should return null."
    761    );
    762    is(
    763      element.hasAttribute(contentAttr),
    764      v.result,
    765      v.result
    766        ? contentAttr + " should not be present"
    767        : contentAttr + " should be present"
    768    );
    769    element.removeAttribute(contentAttr);
    770  });
    771 
    772  // Tests after removeAttribute() is called. Should be equivalent with not set.
    773  is(
    774    element.getAttribute(contentAttr),
    775    null,
    776    "When not set, the content attribute should be null."
    777  );
    778  is(
    779    element[contentAttr],
    780    false,
    781    "When not set, the IDL attribute should return false"
    782  );
    783 }
    784 
    785 /**
    786 * Checks that a given attribute name for a given element is correctly reflected
    787 * as an signed integer.
    788 *
    789 * @param aParameters   Object    object containing the parameters, which are:
    790 *  - element           Element   node to test on
    791 *  - attribute         String    name of the attribute
    792 *  - nonNegative       Boolean   true if the attribute is limited to 'non-negative numbers', false otherwise
    793 *  - defaultValue      Integer   [optional] default value, if one exists
    794 */
    795 function reflectInt(aParameters) {
    796  // Expected value returned by .getAttribute() when |value| has been previously passed to .setAttribute().
    797  function expectedGetAttributeResult(value) {
    798    return String(value);
    799  }
    800 
    801  function stringToInteger(value, nonNegative, defaultValue) {
    802    // Parse: Ignore leading whitespace, find [+/-][numbers]
    803    var result = /^[ \t\n\f\r]*([\+\-]?[0-9]+)/.exec(value);
    804    if (result) {
    805      var resultInt = parseInt(result[1], 10);
    806      if (
    807        (nonNegative ? 0 : -0x80000000) <= resultInt &&
    808        resultInt <= 0x7fffffff
    809      ) {
    810        // If the value is within allowed value range for signed/unsigned
    811        // integer, return it -- but add 0 to it to convert a possible -0 into
    812        // +0, the only zero present in the signed integer range.
    813        return resultInt + 0;
    814      }
    815    }
    816    return defaultValue;
    817  }
    818 
    819  // Expected value returned by .getAttribute(attr) or .attr if |value| has been set via the IDL attribute.
    820  function expectedIdlAttributeResult(value) {
    821    // This returns the result of calling the ES ToInt32 algorithm on value.
    822    return value << 0;
    823  }
    824 
    825  var element = aParameters.element;
    826  var attr = aParameters.attribute;
    827  var nonNegative = aParameters.nonNegative;
    828 
    829  var defaultValue =
    830    aParameters.defaultValue !== undefined
    831      ? aParameters.defaultValue
    832      : nonNegative
    833        ? -1
    834        : 0;
    835 
    836  ok(attr in element, attr + " should be an IDL attribute of this element");
    837  is(
    838    typeof element[attr],
    839    "number",
    840    attr + " IDL attribute should be a number"
    841  );
    842 
    843  // Check default value.
    844  is(element[attr], defaultValue, "default value should be " + defaultValue);
    845  ok(!element.hasAttribute(attr), attr + " shouldn't be present");
    846 
    847  /**
    848   * Test various values.
    849   * value: The test value that will be set using both setAttribute(value) and
    850   *        element[attr] = value
    851   */
    852  var valuesToTest = [
    853    // Test numeric inputs up to max signed integer
    854    0,
    855    1,
    856    55555,
    857    2147483647,
    858    +42,
    859    // Test string inputs up to max signed integer
    860    "0",
    861    "1",
    862    "777777",
    863    "2147483647",
    864    "+42",
    865    // Test negative numeric inputs up to min signed integer
    866    -0,
    867    -1,
    868    -3333,
    869    -2147483648,
    870    // Test negative string inputs up to min signed integer
    871    "-0",
    872    "-1",
    873    "-222",
    874    "-2147483647",
    875    "-2147483648",
    876    // Test numeric inputs that are outside legal 32 bit signed values
    877    -2147483649,
    878    -3000000000,
    879    -4294967296,
    880    2147483649,
    881    4000000000,
    882    -4294967297,
    883    // Test string inputs with extra padding
    884    "     1111111",
    885    "  23456   ",
    886    // Test non-numeric string inputs
    887    "",
    888    " ",
    889    "+",
    890    "-",
    891    "foo",
    892    "+foo",
    893    "-foo",
    894    "+     foo",
    895    "-     foo",
    896    "+-2",
    897    "-+2",
    898    "++2",
    899    "--2",
    900    "hello1234",
    901    "1234hello",
    902    "444 world 555",
    903    "why 567 what",
    904    "-3 nots",
    905    "2e5",
    906    "300e2",
    907    "42+-$",
    908    "+42foo",
    909    "-514not",
    910    "\vblah",
    911    "0x10FFFF",
    912    "-0xABCDEF",
    913    // Test decimal numbers
    914    1.2345,
    915    42.0,
    916    3456789.1,
    917    -2.3456,
    918    -6789.12345,
    919    -2147483649.1234,
    920    // Test decimal strings
    921    "1.2345",
    922    "42.0",
    923    "3456789.1",
    924    "-2.3456",
    925    "-6789.12345",
    926    "-2147483649.1234",
    927    // Test special values
    928    undefined,
    929    null,
    930    NaN,
    931    Infinity,
    932    -Infinity,
    933  ];
    934 
    935  valuesToTest.forEach(function (v) {
    936    var intValue = stringToInteger(v, nonNegative, defaultValue);
    937 
    938    element.setAttribute(attr, v);
    939 
    940    is(
    941      element.getAttribute(attr),
    942      expectedGetAttributeResult(v),
    943      element.localName +
    944        ".setAttribute(" +
    945        attr +
    946        ", " +
    947        v +
    948        "), " +
    949        element.localName +
    950        ".getAttribute(" +
    951        attr +
    952        ") "
    953    );
    954 
    955    is(
    956      element[attr],
    957      intValue,
    958      element.localName +
    959        ".setAttribute(" +
    960        attr +
    961        ", " +
    962        v +
    963        "), " +
    964        element.localName +
    965        "[" +
    966        attr +
    967        "] "
    968    );
    969    element.removeAttribute(attr);
    970 
    971    if (nonNegative && expectedIdlAttributeResult(v) < 0) {
    972      try {
    973        element[attr] = v;
    974        ok(
    975          false,
    976          element.localName +
    977            "[" +
    978            attr +
    979            "] = " +
    980            v +
    981            " should throw IndexSizeError"
    982        );
    983      } catch (e) {
    984        is(
    985          e.name,
    986          "IndexSizeError",
    987          element.localName +
    988            "[" +
    989            attr +
    990            "] = " +
    991            v +
    992            " should throw IndexSizeError"
    993        );
    994        is(
    995          e.code,
    996          DOMException.INDEX_SIZE_ERR,
    997          element.localName +
    998            "[" +
    999            attr +
   1000            "] = " +
   1001            v +
   1002            " should throw INDEX_SIZE_ERR"
   1003        );
   1004      }
   1005    } else {
   1006      element[attr] = v;
   1007      is(
   1008        element[attr],
   1009        expectedIdlAttributeResult(v),
   1010        element.localName +
   1011          "[" +
   1012          attr +
   1013          "] = " +
   1014          v +
   1015          ", " +
   1016          element.localName +
   1017          "[" +
   1018          attr +
   1019          "] "
   1020      );
   1021      is(
   1022        element.getAttribute(attr),
   1023        String(expectedIdlAttributeResult(v)),
   1024        element.localName +
   1025          "[" +
   1026          attr +
   1027          "] = " +
   1028          v +
   1029          ", " +
   1030          element.localName +
   1031          ".getAttribute(" +
   1032          attr +
   1033          ") "
   1034      );
   1035    }
   1036    element.removeAttribute(attr);
   1037  });
   1038 
   1039  // Tests after removeAttribute() is called. Should be equivalent with not set.
   1040  is(
   1041    element.getAttribute(attr),
   1042    null,
   1043    "When not set, the content attribute should be null."
   1044  );
   1045  is(
   1046    element[attr],
   1047    defaultValue,
   1048    "When not set, the IDL attribute should return default value."
   1049  );
   1050 }
   1051 
   1052 /**
   1053 * Checks that a given attribute is correctly reflected as a url.
   1054 *
   1055 * @param aParameters   Object    object containing the parameters, which are:
   1056 *  - element           Element   node to test
   1057 *  - attribute         String    name of the attribute
   1058 *     OR
   1059 *    attribute         Object    object containing two attributes, 'content' and 'idl'
   1060 */
   1061 function reflectURL(aParameters) {
   1062  var element = aParameters.element;
   1063  var contentAttr =
   1064    typeof aParameters.attribute === "string"
   1065      ? aParameters.attribute
   1066      : aParameters.attribute.content;
   1067  var idlAttr =
   1068    typeof aParameters.attribute === "string"
   1069      ? aParameters.attribute
   1070      : aParameters.attribute.idl;
   1071 
   1072  element[idlAttr] = "";
   1073  is(
   1074    element[idlAttr],
   1075    document.URL,
   1076    "Empty string should resolve to document URL"
   1077  );
   1078 }