tor-browser

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

valid-custom-element-names.html (3907B)


      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/html/pull/7991">
      5 <script src="/resources/testharness.js"></script>
      6 <script src="/resources/testharnessreport.js"></script>
      7 
      8 <script>
      9 function isAsciiLowerAlpha(codePoint) {
     10  return codePoint >= 0x61 && codePoint <= 0x7A;
     11 }
     12 function isAsciiUpperAlpha(codePoint) {
     13  return codePoint >= 0x41 && codePoint <= 0x5A;
     14 }
     15 function isAsciiAlpha(codePoint) {
     16  return isAsciiLowerAlpha(codePoint) || isAsciiUpperAlpha(codePoint);
     17 }
     18 function isAsciiDigit(codePoint) {
     19  return codePoint >= 0x30 && codePoint <= 0x39;
     20 }
     21 function isAsciiWhitespace(codePoint) {
     22  return codePoint == 0x9 || codePoint == 0xA || codePoint == 0xC || codePoint == 0xD || codePoint == 0x20;
     23 }
     24 
     25 function debugString(str) {
     26  const codePoints = [];
     27  for (const c of str) {
     28    codePoints.push(c.codePointAt(0));
     29  }
     30  return `code points: ${JSON.stringify(codePoints)}, string: "${str}"`;
     31 }
     32 
     33 const validCustomElementNames = [
     34  'annotation-xml-custom',
     35 ];
     36 const invalidCustomElementNames = [
     37  '',
     38  'annotation-xml',
     39  'color-profile',
     40  'font-face',
     41  'font-face-src',
     42  'font-face-uri',
     43  'font-face-format',
     44  'font-face-name',
     45  'missing-glyph',
     46 ];
     47 
     48 const testCodePoints = [0x1F171, 0x1F196, 0x10000];
     49 for (let i = 0; i < 0x80; i++) {
     50  testCodePoints.push(i);
     51 }
     52 
     53 const elementLocalNameRegex = /^(?:[A-Za-z][^\0\t\n\f\r\u0020/>]*|[:_\u0080-\u{10FFFF}][A-Za-z0-9-.:_\u0080-\u{10FFFF}]*)$/u;
     54 
     55 function isValidCustomElementName(str) {
     56  if (!str.length) {
     57    return false;
     58  }
     59 
     60  if (!str.includes('-')) {
     61    return false;
     62  }
     63 
     64  let first = true;
     65  for (const c of str) {
     66    const codePoint = c.codePointAt(0);
     67    if (first) {
     68      if (!isAsciiLowerAlpha(codePoint)) {
     69        return false;
     70      }
     71      first = false;
     72    }
     73    if (isAsciiUpperAlpha(codePoint)) {
     74      return false;
     75    }
     76  }
     77 
     78  return elementLocalNameRegex.test(str);
     79 }
     80 
     81 // In order to test the branching logic of valid element local names and the
     82 // requirement of having a '-' character, this method generates different
     83 // variations of potential custom element names given two code points.
     84 function createStringWithSeparatorMode(codePoint, prefix, separatorMode) {
     85  const str = String.fromCodePoint(codePoint);
     86  if (separatorMode == 0) {
     87    return `${prefix}${str}`;
     88  } else if (separatorMode == 1) {
     89    return `${prefix}-${str}`;
     90  } else if (separatorMode == 2) {
     91    return `${prefix}${str}-element`;
     92  }
     93 }
     94 
     95 for (const prefix of ['', 'a', 'A', ' ', '\0']) {
     96  for (const codePoint of testCodePoints) {
     97    for (const separatorMode of [0, 1, 2]) {
     98      const str = createStringWithSeparatorMode(
     99        codePoint, prefix, separatorMode);
    100      if (isValidCustomElementName(str)) {
    101        validCustomElementNames.push(str);
    102      } else {
    103        invalidCustomElementNames.push(str);
    104      }
    105    }
    106  }
    107 }
    108 
    109 let nextClassNumber = 1;
    110 function createElementClass() {
    111  const name = `CustomElement${nextClassNumber++}`;
    112  const newClass = function() {};
    113  newClass.prototype = HTMLElement;
    114  return newClass;
    115 }
    116 
    117 for (const validName of validCustomElementNames) {
    118  promise_test(async t => {
    119    const newClass = createElementClass();
    120    customElements.define(validName, newClass);
    121    await customElements.whenDefined(validName);
    122  }, `Valid custom element name: ${debugString(validName)}`);
    123 }
    124 
    125 for (const invalidName of invalidCustomElementNames) {
    126  promise_test(async t => {
    127    const newClass = createElementClass();
    128    assert_throws_dom(
    129      'SyntaxError',
    130      () => customElements.define(invalidName, newClass),
    131      `customElements.define should have thrown`);
    132    await promise_rejects_dom(t, 'SyntaxError',
    133      customElements.whenDefined(invalidName),
    134      `customElements.whenDefined should have thrown`);
    135  }, `Invalid custom element name: ${debugString(invalidName)}`);
    136 }
    137 
    138 </script>