test_parser_diagnostics_unprintables.html (9832B)
1 <!doctype html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>Test for CSS parser diagnostics escaping unprintable 6 characters correctly</title> 7 <script src="/tests/SimpleTest/SimpleTest.js"></script> 8 <link rel="stylesheet" href="/tests/SimpleTest/test.css"> 9 </head> 10 <body> 11 <a target="_blank" 12 href="https://bugzilla.mozilla.org/show_bug.cgi?id=229827" 13 >Mozilla Bug 229827</a> 14 <style id="testbench"></style> 15 <script type="application/javascript"> 16 // This test has intimate knowledge of how to get the CSS parser to 17 // emit diagnostics that contain text under control of the user. 18 // That's not the point of the test, though; the point is only that 19 // *that text* is properly escaped. 20 21 SpecialPowers.wrap(window).docShell.cssErrorReportingEnabled = true; 22 23 // There is one "pattern" for each code path through the error reporter 24 // that might need to escape some kind of user-supplied text. 25 // Each "pattern" is tested once with each of the "substitution"s below: 26 // <t>, <i>, and <s> are replaced by the t:, i:, and s: fields of 27 // each substitution object in turn. 28 let patterns = [ 29 // REPORT_UNEXPECTED_P (only ever used in contexts where identifier-like 30 // escaping is appropriate) 31 { i: "<t>|x{}", o: "prefix \u2018<i>\u2019" }, 32 // REPORT_UNEXPECTED_TOKEN with: 33 // _Ident 34 { i: "@namespace fnord <t>;", o: "within @namespace: \u2018<i>\u2019" }, 35 // _Ref 36 { i: "@namespace fnord #<t>;", o: "within @namespace: \u2018#<i>\u2019" }, 37 // _Function 38 { i: "@namespace fnord <t>();", o: "within @namespace: \u2018<i>(\u2019" }, 39 // _Dimension 40 { i: "@namespace fnord 14<t>;", o: "within @namespace: \u201814<i>\u2019" }, 41 // _AtKeyword 42 { i: "x{@<t>: }", o: "declaration but found \u2018@<i>\u2019." }, 43 // _String 44 { i: "x{ color: '<t>'}" , o: 'color but found \u2018"<s>"\u2019.' }, 45 // _Bad_String 46 { i: "x{ color: '<t>\n}", o: 'color but found \u2018"<s>\u2019.' }, 47 ]; 48 49 // Blocks of characters to test, and how they should be escaped when 50 // they appear in identifiers and string constants. 51 const substitutions = [ 52 // ASCII printables that _can_ normally appear in identifiers, 53 // so should of course _not_ be escaped. 54 { t: "-_0123456789", i: "-_0123456789", 55 s: "-_0123456789" }, 56 { t: "abcdefghijklmnopqrstuvwxyz", i: "abcdefghijklmnopqrstuvwxyz", 57 s: "abcdefghijklmnopqrstuvwxyz" }, 58 { t: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", i: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 59 s: "ABCDEFGHIJKLMNOPQRSTUVWXYZ" }, 60 61 // ASCII printables that are not normally valid as the first character 62 // of an identifier, or the character immediately after a leading dash, 63 // but can be forced into that position with escapes. 64 { t: "\\-", i: "\\-", s: "-" }, 65 { t: "\\30 ", i: "\\30 ", s: "0" }, 66 { t: "\\31 ", i: "\\31 ", s: "1" }, 67 { t: "\\32 ", i: "\\32 ", s: "2" }, 68 { t: "\\33 ", i: "\\33 ", s: "3" }, 69 { t: "\\34 ", i: "\\34 ", s: "4" }, 70 { t: "\\35 ", i: "\\35 ", s: "5" }, 71 { t: "\\36 ", i: "\\36 ", s: "6" }, 72 { t: "\\37 ", i: "\\37 ", s: "7" }, 73 { t: "\\38 ", i: "\\38 ", s: "8" }, 74 { t: "\\39 ", i: "\\39 ", s: "9" }, 75 { t: "-\\-", i: "--", s: "--" }, 76 { t: "-\\30 ", i: "-\\30 ", s: "-0" }, 77 { t: "-\\31 ", i: "-\\31 ", s: "-1" }, 78 { t: "-\\32 ", i: "-\\32 ", s: "-2" }, 79 { t: "-\\33 ", i: "-\\33 ", s: "-3" }, 80 { t: "-\\34 ", i: "-\\34 ", s: "-4" }, 81 { t: "-\\35 ", i: "-\\35 ", s: "-5" }, 82 { t: "-\\36 ", i: "-\\36 ", s: "-6" }, 83 { t: "-\\37 ", i: "-\\37 ", s: "-7" }, 84 { t: "-\\38 ", i: "-\\38 ", s: "-8" }, 85 { t: "-\\39 ", i: "-\\39 ", s: "-9" }, 86 87 // ASCII printables that must be escaped in identifiers. 88 // Most of these should not be escaped in strings. 89 { t: "\\!\\\"\\#\\$", i: "\\!\\\"\\#\\$", s: "!\\\"#$" }, 90 { t: "\\%\\&\\'\\(", i: "\\%\\&\\'\\(", s: "%&'(" }, 91 { t: "\\)\\*\\+\\,", i: "\\)\\*\\+\\,", s: ")*+," }, 92 { t: "\\.\\/\\:\\;", i: "\\.\\/\\:\\;", s: "./:;" }, 93 { t: "\\<\\=\\>\\?", i: "\\<\\=\\>\\?", s: "<=>?", }, 94 { t: "\\@\\[\\\\\\]", i: "\\@\\[\\\\\\]", s: "@[\\\\]" }, 95 { t: "\\^\\`\\{\\}\\~", i: "\\^\\`\\{\\}\\~", s: "^`{}~" }, 96 97 // U+0000 - U+0020 (C0 controls, space) 98 // U+000A LINE FEED, U+000C FORM FEED, and U+000D CARRIAGE RETURN 99 // cannot be put into a CSS token as escaped literal characters, so 100 // we do them with hex escapes instead. 101 // The parser replaces U+0000 with U+FFFD. 102 { t: "\\\x00\\\x01\\\x02\\\x03", i: "�\\1 \\2 \\3 ", 103 s: "�\\1 \\2 \\3 " }, 104 { t: "\\\x04\\\x05\\\x06\\\x07", i: "\\4 \\5 \\6 \\7 ", 105 s: "\\4 \\5 \\6 \\7 " }, 106 { t: "\\\x08\\\x09\\000A\\\x0B", i: "\\8 \\9 \\a \\b ", 107 s: "\\8 \\9 \\a \\b " }, 108 { t: "\\000C\\000D\\\x0E\\\x0F", i: "\\c \\d \\e \\f ", 109 s: "\\c \\d \\e \\f " }, 110 { t: "\\\x10\\\x11\\\x12\\\x13", i: "\\10 \\11 \\12 \\13 ", 111 s: "\\10 \\11 \\12 \\13 " }, 112 { t: "\\\x14\\\x15\\\x16\\\x17", i: "\\14 \\15 \\16 \\17 ", 113 s: "\\14 \\15 \\16 \\17 " }, 114 { t: "\\\x18\\\x19\\\x1A\\\x1B", i: "\\18 \\19 \\1a \\1b ", 115 s: "\\18 \\19 \\1a \\1b " }, 116 { t: "\\\x1C\\\x1D\\\x1E\\\x1F\\ ", i: "\\1c \\1d \\1e \\1f \\ ", 117 s: "\\1c \\1d \\1e \\1f " }, 118 119 // U+007F (DELETE) and U+0080 - U+009F (C1 controls) 120 { t: "\\\x7f\\\x80\\\x81\\\x82", i: "\\7f \x80\x81\x82", 121 s: "\\7f \x80\x81\x82" }, 122 { t: "\\\x83\\\x84\\\x85\\\x86", i: "\x83\x84\x85\x86", 123 s: "\x83\x84\x85\x86" }, 124 { t: "\\\x87\\\x88\\\x89\\\x8A", i: "\x87\x88\x89\x8A", 125 s: "\x87\x88\x89\x8A" }, 126 { t: "\\\x8B\\\x8C\\\x8D\\\x8E", i: "\x8B\x8C\x8D\x8E", 127 s: "\x8B\x8C\x8D\x8E" }, 128 { t: "\\\x8F\\\x90\\\x91\\\x92", i: "\x8F\x90\x91\x92", 129 s: "\x8F\x90\x91\x92" }, 130 { t: "\\\x93\\\x94\\\x95\\\x96", i: "\x93\x94\x95\x96", 131 s: "\x93\x94\x95\x96" }, 132 { t: "\\\x97\\\x98\\\x99\\\x9A", i: "\x97\x98\x99\x9A", 133 s: "\x97\x98\x99\x9A" }, 134 { t: "\\\x9B\\\x9C\\\x9D\\\x9E\\\x9F", i: "\x9B\x9C\x9D\x9E\x9F", 135 s: "\x9B\x9C\x9D\x9E\x9F" }, 136 137 // CSS doesn't bother with the full Unicode rules for identifiers, 138 // instead declaring that any code point greater than or equal to 139 // U+0080 is a valid identifier character. Test a small handful 140 // of both basic and astral plane characters. 141 142 // Arabic (caution to editors: there is a possibly-invisible U+200E 143 // LEFT-TO-RIGHT MARK in each string, just before the close quote) 144 { t: "أبجدهوزحطيكلمنسعفصقرشتثخذضظغ", 145 i: "أبجدهوزحطيكلمنسعفصقرشتثخذضظغ", 146 s: "أبجدهوزحطيكلمنسعفصقرشتثخذضظغ" }, 147 148 // Box drawing 149 { t: "─│┌┐└┘├┤┬┴┼╭╮╯╰╴╵╶╷", 150 i: "─│┌┐└┘├┤┬┴┼╭╮╯╰╴╵╶╷", 151 s: "─│┌┐└┘├┤┬┴┼╭╮╯╰╴╵╶╷" }, 152 153 // CJK Unified Ideographs 154 { t: "一丁丂七丄丅丆万丈三上下丌不与丏", 155 i: "一丁丂七丄丅丆万丈三上下丌不与丏", 156 s: "一丁丂七丄丅丆万丈三上下丌不与丏" }, 157 158 // CJK Unified Ideographs Extension B (astral) 159 { t: "𠀀𠀁𠀂𠀃𠀄𠀅𠀆𠀇𠀈𠀉𠀊𠀋𠀌𠀍𠀎𠀏", 160 i: "𠀀𠀁𠀂𠀃𠀄𠀅𠀆𠀇𠀈𠀉𠀊𠀋𠀌𠀍𠀎𠀏", 161 s: "𠀀𠀁𠀂𠀃𠀄𠀅𠀆𠀇𠀈𠀉𠀊𠀋𠀌𠀍𠀎𠀏" }, 162 163 // Devanagari 164 { t: "कखगघङचछजझञटठडढणतथदधनपफबभमयरलळवशषसह", 165 i: "कखगघङचछजझञटठडढणतथदधनपफबभमयरलळवशषसह", 166 s: "कखगघङचछजझञटठडढणतथदधनपफबभमयरलळवशषसह" }, 167 168 // Emoticons (astral) 169 { t: "😁😂😃😄😅😆😇😈😉😊😋😌😍😎😏😐", 170 i: "😁😂😃😄😅😆😇😈😉😊😋😌😍😎😏😐", 171 s: "😁😂😃😄😅😆😇😈😉😊😋😌😍😎😏😐" }, 172 173 // Greek 174 { t: "αβγδεζηθικλμνξοπρςστυφχψω", 175 i: "αβγδεζηθικλμνξοπρςστυφχψω", 176 s: "αβγδεζηθικλμνξοπρςστυφχψω" } 177 ]; 178 179 const npatterns = patterns.length; 180 const nsubstitutions = substitutions.length; 181 182 function quotemeta(str) { 183 return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); 184 } 185 function subst(str, sub) { 186 return str.replace("<t>", sub.t) 187 .replace("<i>", sub.i) 188 .replace("<s>", sub.s); 189 } 190 191 var curpat = 0; 192 var cursubst = -1; 193 var testbench = document.getElementById("testbench"); 194 195 function nextTest() { 196 cursubst++; 197 if (cursubst == nsubstitutions) { 198 curpat++; 199 cursubst = 0; 200 } 201 if (curpat == npatterns) { 202 SimpleTest.finish(); 203 return; 204 } 205 206 let css = subst(patterns[curpat].i, substitutions[cursubst]); 207 let msg = quotemeta(subst(patterns[curpat].o, substitutions[cursubst])); 208 209 info(css); 210 info(msg); 211 SimpleTest.expectConsoleMessages(function () { testbench.innerHTML = css }, 212 [{ errorMessage: new RegExp(msg) }], 213 nextTest); 214 } 215 216 SimpleTest.waitForExplicitFinish(); 217 nextTest(); 218 </script> 219 </body> 220 </html>