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>