tor-browser

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

name-validation.html (12716B)


      1 <!DOCTYPE html>
      2 <meta name=timeout content=long>
      3 <link rel=author href="mailto:jarhar@chromium.org">
      4 <link rel=help href="https://github.com/whatwg/dom/pull/1079">
      5 <script src="/resources/testharness.js"></script>
      6 <script src="/resources/testharnessreport.js"></script>
      7 
      8 <script>
      9 function isAsciiAlpha(codePoint) {
     10  return (codePoint >= 0x41 && codePoint <= 0x5A) || (codePoint >= 0x61 && codePoint <= 0x7A);
     11 }
     12 function isAsciiDigit(codePoint) {
     13  return codePoint >= 0x30 && codePoint <= 0x39;
     14 }
     15 function isAsciiWhitespace(codePoint) {
     16  return codePoint == 0x9 || codePoint == 0xA || codePoint == 0xC || codePoint == 0xD || codePoint == 0x20;
     17 }
     18 
     19 function debugString(str) {
     20  const codePoints = [];
     21  for (let i = 0; i < str.length; i++) {
     22    codePoints.push(str.charCodeAt(i));
     23  }
     24  return `code points: ${JSON.stringify(codePoints)}, string: "${str}"`;
     25 }
     26 
     27 const latin1CodePoint = 100;
     28 const latin1 = String.fromCodePoint(latin1CodePoint);
     29 const smallEmoji = 'smallEmoji🆖';
     30 const bigEmoji = 'bigEmoji🅱️';
     31 
     32 // Testing every variation of a namespace prefix with every variation of a
     33 // local name would make the test take too long to run, so use these instead when
     34 // combining with a namespace prefix.
     35 const validElementLocalNamesShortened = [
     36  'div', `latin1${latin1}`, smallEmoji, bigEmoji
     37 ];
     38 const invalidElementLocalNamesShortened = [
     39  '', 'space ', 'newline\n', 'null\0', `:soh${String.fromCodePoint(1)}`, '5'
     40 ];
     41 const validAttributeLocalNamesShortened = [
     42  'attr', `latin1${latin1}`, smallEmoji, bigEmoji
     43 ];
     44 const invalidAttributeLocalNamesShortened = [
     45  '', 'space ', 'newline\n', 'null\0'
     46 ];
     47 
     48 const validElementLocalNames = validElementLocalNamesShortened.slice();
     49 const invalidElementLocalNames = invalidElementLocalNamesShortened.slice();
     50 const validAttributeLocalNames = validAttributeLocalNamesShortened.slice();
     51 const invalidAttributeLocalNames = invalidAttributeLocalNamesShortened.slice();
     52 const validNamespacePrefixes = [smallEmoji, bigEmoji];
     53 const invalidNamespacePrefixes = [''];
     54 const validDoctypes = [''];
     55 const invalidDoctypes = [];
     56 
     57 const codePoints = [];
     58 for (let i = 0; i < 0x80; i++) {
     59  codePoints.push(i);
     60 }
     61 codePoints.push(latin1CodePoint);
     62 
     63 // attributes and namespaces
     64 for (const codePoint of codePoints) {
     65  const str = String.fromCodePoint(codePoint);
     66  if (codePoint == 0 || isAsciiWhitespace(codePoint) || codePoint == 0x2F || codePoint == 0x3E) {
     67    invalidNamespacePrefixes.push(str);
     68    invalidAttributeLocalNames.push(str);
     69  } else if (codePoint == 0x3A) {
     70    // colons are not valid namespace prefixes, but due to parsing they can
     71    // never be considered as a namespace prefix, only as a separator between the
     72    // prefix and the local name.
     73    validAttributeLocalNames.push(str);
     74  } else if (codePoint == 0x3D) {
     75    validNamespacePrefixes.push(str);
     76    invalidAttributeLocalNames.push(str);
     77  } else {
     78    validNamespacePrefixes.push(str);
     79    validAttributeLocalNames.push(str);
     80  }
     81 }
     82 
     83 // valid element local names
     84 for (const firstChar of codePoints) {
     85  for (const secondChar of codePoints) {
     86    const str = `${String.fromCodePoint(firstChar)}${String.fromCodePoint(secondChar)}`;
     87    if (isAsciiAlpha(firstChar)) {
     88      if (!secondChar || secondChar == 0x2F || secondChar == 0x3E || isAsciiWhitespace(secondChar)) {
     89        invalidElementLocalNames.push(str);
     90      } else {
     91        validElementLocalNames.push(str);
     92      }
     93    } else {
     94      if (firstChar == 0x3A || firstChar == 0x5F || firstChar >= 0x80) {
     95        if (isAsciiAlpha(secondChar) ||
     96            isAsciiDigit(secondChar) ||
     97            secondChar == 0x2D ||
     98            secondChar == 0x2E ||
     99            secondChar == 0x3A ||
    100            secondChar == 0x5F ||
    101            secondChar >= 0x80) {
    102          validElementLocalNames.push(str);
    103        } else {
    104          invalidElementLocalNames.push(str);
    105        }
    106      } else {
    107        invalidElementLocalNames.push(str);
    108      }
    109    }
    110  }
    111 }
    112 
    113 // doctypes
    114 for (const codePoint of codePoints) {
    115  const str = String.fromCodePoint(codePoint);
    116  if (codePoint == 0 || isAsciiWhitespace(codePoint) || codePoint == 0x3E) {
    117    invalidDoctypes.push(str);
    118  } else {
    119    validDoctypes.push(str);
    120  }
    121 }
    122 
    123 test(() => {
    124  // This regex is provided in the spec and is used here to double check our
    125  // test input.
    126  const validNameRegex = /^(?:[A-Za-z][^\0\t\n\f\r\u0020/>]*|[:_\u0080-\u{10FFFF}][A-Za-z0-9-.:_\u0080-\u{10FFFF}]*)$/u;
    127  for (const validName of validElementLocalNames) {
    128    assert_true(
    129      validNameRegex.test(validName),
    130      `Regex should match: ${debugString(validName)}`);
    131    try {
    132      document.createElement(validName);
    133    } catch (error) {
    134      assert_unreached(
    135        `document.createElement should not have thrown an error for: ${debugString(validName)} ${error.toString()}`);
    136    }
    137  }
    138  for (const invalidName of invalidElementLocalNames) {
    139    assert_false(
    140      validNameRegex.test(invalidName),
    141      `Regex should not match: ${debugString(invalidName)}`);
    142    assert_throws_dom(
    143      'InvalidCharacterError',
    144      () => document.createElement(invalidName),
    145      `document.createElement should throw an error for: ${debugString(invalidName)}`);
    146  }
    147 }, 'Valid and invalid characters in createElement.');
    148 
    149 test(() => {
    150  for (const validNamespace of validNamespacePrefixes) {
    151    for (const validName of validElementLocalNamesShortened) {
    152      try {
    153        document.createElementNS('namespaceuri', `${validNamespace}:${validName}`);
    154      } catch (error) {
    155        assert_unreached(
    156          `document.createElementNS should not have thrown an error for: ${debugString(validNamespace)} ${debugString(validName)} ${error.toString()}`);
    157      }
    158      try {
    159        document.implementation.createDocument('namespaceuri', `${validNamespace}:${validName}`);
    160      } catch (error) {
    161        assert_unreached(
    162          `createDocument should not have thrown an error for: ${debugString(validNamespace)} ${debugString(validName)} ${error.toString()}`);
    163      }
    164    }
    165    for (const invalidName of invalidElementLocalNamesShortened) {
    166      assert_throws_dom(
    167        'InvalidCharacterError',
    168        () => document.createElementNS('namespaceuri', `${validNamespace}:${invalidName}`),
    169        `document.createElementNS should throw an error for: ${debugString(validNamespace)} ${debugString(invalidName)}`);
    170      assert_throws_dom(
    171        'InvalidCharacterError',
    172        () => document.implementation.createDocument('namespaceuri', `${validNamespace}:${invalidName}`),
    173        `createDocument should throw an error for: ${debugString(validNamespace)} ${debugString(invalidName)}`);
    174    }
    175  }
    176  for (const invalidNamespace of invalidNamespacePrefixes) {
    177    for (const localName of validElementLocalNamesShortened.concat(invalidElementLocalNamesShortened)) {
    178      assert_throws_dom(
    179        'InvalidCharacterError',
    180        () => document.createElementNS('namespaceuri', `${invalidNamespace}:${localName}`),
    181        `document.createElementNS should throw an error for: ${debugString(invalidNamespace)} ${debugString(localName)}`);
    182      assert_throws_dom(
    183        'InvalidCharacterError',
    184        () => document.implementation.createDocument('namespaceuri', `${invalidNamespace}:${localName}`),
    185        `createDocument should throw an error for: ${debugString(invalidNamespace)} ${debugString(localName)}`);
    186    }
    187  }
    188 }, 'Valid and invalid characters in createElementNS and createDocument.');
    189 
    190 test(() => {
    191  for (const validAttributeName of validAttributeLocalNames) {
    192    const element = document.createElement('div');
    193    try {
    194      element.setAttribute(validAttributeName, 'value');
    195    } catch (error) {
    196      assert_unreached(
    197        `element.setAttribute should not have thrown an error for: ${debugString(validAttributeName)} ${error.toString()}`);
    198    }
    199    try {
    200      element.toggleAttribute(validAttributeName);
    201    } catch (error) {
    202      assert_unreached(
    203        `element.toggleAttribute should not have thrown an error for: ${debugString(validAttributeName)} ${error.toString()}`);
    204    }
    205    try {
    206      element.toggleAttribute(validAttributeName, false);
    207    } catch (error) {
    208      assert_unreached(
    209        `element.toggleAttribute with false should not have thrown an error for: ${debugString(validAttributeName)} ${error.toString()}`);
    210    }
    211    try {
    212      document.createAttribute(validAttributeName);
    213    } catch (error) {
    214      assert_unreached(
    215        `document.createAttribute should not have thrown an error for: ${debugString(validAttributeName)} ${error.toString()}`);
    216    }
    217  }
    218  for (const invalidAttributeName of invalidAttributeLocalNames) {
    219    const element = document.createElement('div');
    220    assert_throws_dom(
    221      'InvalidCharacterError',
    222      () => element.setAttribute(invalidAttributeName, 'value'),
    223      `element.setAttribute should throw an error for: ${debugString(invalidAttributeName)}`);
    224    assert_throws_dom(
    225      'InvalidCharacterError',
    226      () => element.toggleAttribute(invalidAttributeName),
    227      `element.toggleAttribute should throw an error for: ${debugString(invalidAttributeName)}`);
    228    assert_throws_dom(
    229      'InvalidCharacterError',
    230      () => element.toggleAttribute(invalidAttributeName, false),
    231      `element.toggleAttribute with false should throw an error for: ${debugString(invalidAttributeName)}`);
    232    assert_throws_dom(
    233      'InvalidCharacterError',
    234      () => document.createAttribute(invalidAttributeName),
    235      `document.createAttribute should throw an error for: ${debugString(invalidAttributeName)}`);
    236  }
    237 }, 'Valid and invalid characters in setAttribute, toggleAttribute, and createAttribute.');
    238 
    239 test(() => {
    240  for (const validNamespace of validNamespacePrefixes) {
    241    for (const validLocalName of validAttributeLocalNamesShortened) {
    242      const element = document.createElement('div');
    243      try {
    244        element.setAttributeNS('namespaceuri', `${validNamespace}:${validLocalName}`, 'value');
    245      } catch (error) {
    246        assert_unreached(`element.setAttributeNS should not have thrown an error for: ${debugString(validNamespace)} ${debugString(validLocalName)} ${error.toString()}`);
    247      }
    248      try {
    249        document.createAttributeNS('namespaceuri', `${validNamespace}:${validLocalName}`);
    250      } catch (error) {
    251        assert_unreached(`document.createAttributeNS should not have thrown an error for: ${debugString(validNamespace)} ${debugString(validLocalName)} ${error.toString()}`);
    252      }
    253    }
    254    for (const invalidLocalName of invalidAttributeLocalNamesShortened) {
    255      const element = document.createElement('div');
    256      assert_throws_dom(
    257        'InvalidCharacterError',
    258        () => element.setAttributeNS('namespaceuri', `${validNamespace}:${invalidLocalName}`, 'value'),
    259        `element.setAttributeNS should have thrown an error for: ${debugString(validNamespace)} ${debugString(invalidLocalName)}`);
    260      assert_throws_dom(
    261        'InvalidCharacterError',
    262        () => document.createAttributeNS('namespaceuri', `${validNamespace}:${invalidLocalName}`),
    263        `document.createAttributeNS should have thrown an error for: ${debugString(validNamespace)} ${debugString(invalidLocalName)}`);
    264    }
    265  }
    266  for (const invalidNamespace of invalidNamespacePrefixes) {
    267    for (const localName of validAttributeLocalNamesShortened.concat(invalidAttributeLocalNamesShortened)) {
    268      const element = document.createElement('div');
    269      assert_throws_dom(
    270        'InvalidCharacterError',
    271        () => element.setAttributeNS('namespaceuri', `${invalidNamespace}:${localName}`, ''),
    272        `element.setAttributeNS should have thrown an error for: ${debugString(invalidNamespace)} ${debugString(localName)}`);
    273      assert_throws_dom(
    274        'InvalidCharacterError',
    275        () => document.createAttributeNS('namespaceuri', `${invalidNamespace}:${localName}`),
    276        `document.createAttributeNS should have thrown an error for: ${debugString(invalidNamespace)} ${debugString(localName)}`);
    277    }
    278  }
    279 }, 'Valid and invalid characters in setAttributeNS and createAttributeNS.');
    280 
    281 test(() => {
    282  for (const validDoctype of validDoctypes) {
    283    try {
    284      document.implementation.createDocumentType(validDoctype, 'publicid', 'systemid');
    285    } catch (error) {
    286      assert_unreached(`createDocumentType should not have thrown an error for ${debugString(validDoctype)} ${error.toString()}`);
    287    }
    288  }
    289  for (const invalidDoctype of invalidDoctypes) {
    290    assert_throws_dom(
    291      'InvalidCharacterError',
    292      () => document.implementation.createDocumentType(invalidDoctype, 'publicid', 'systemid'),
    293      `createDocumentType should have thrown an error for: ${debugString(invalidDoctype)}`);
    294  }
    295 }, 'Valid and invalid characters in createDocumentType.');
    296 </script>