AllJavascriptTypes.mjs (10812B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 /* eslint-disable object-shorthand */ 5 6 // eslint-disable-next-line mozilla/reject-import-system-module-from-non-system 7 import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; 8 9 // Try replicating real world environment, by using 10 // * a true HTML document 11 // * served from http (https isn't yet supported by nsHttpServer) 12 // * with a regular domain name (example.com) 13 export const TEST_PAGE_HTML = String.raw`<!DOCTYPE html> 14 <html lang="en"> 15 <head> 16 <meta charset="UTF-8"> 17 <title></title> 18 </head> 19 <body> 20 <span id="span">Hello there.</span> 21 <script> 22 globalThis.myPolicy = trustedTypes.createPolicy("myPolicy", { 23 createHTML: s => "<my-policy>" + s + "</my-policy>", 24 createScript: s => "/* myPolicy */ " + s, 25 createScriptURL: s => s + "?myPolicy", 26 }); 27 </script> 28 </body> 29 </html>`; 30 31 export const CONTEXTS = { 32 JS: "js", 33 PAGE: "page", 34 CHROME: "chrome" 35 }; 36 37 /** 38 * Manifest covering all the possible JavaScript value types that gecko may create. 39 * This consist in JavaScript code instantiating one of more example values for all of these types. 40 * 41 * See README.md 42 */ 43 const BasicPrimitives = [ 44 { 45 context: CONTEXTS.JS, 46 expression: "undefined", 47 }, 48 { 49 context: CONTEXTS.JS, 50 expression: "null", 51 }, 52 { 53 context: CONTEXTS.JS, 54 expression: "true", 55 }, 56 { 57 context: CONTEXTS.JS, 58 expression: "false", 59 }, 60 { 61 context: CONTEXTS.JS, 62 expression: "NaN", 63 }, 64 ]; 65 66 const Strings = [ 67 { 68 context: CONTEXTS.JS, 69 expression: `"abc"`, 70 }, 71 { 72 context: CONTEXTS.JS, 73 expression: `"\u9f2c\xFA"`, 74 }, 75 ]; 76 77 const Numbers = [ 78 { 79 context: CONTEXTS.JS, 80 expression: "42", 81 }, 82 { 83 context: CONTEXTS.JS, 84 expression: "-42", 85 }, 86 { 87 context: CONTEXTS.JS, 88 expression: "-0", 89 }, 90 { 91 context: CONTEXTS.JS, 92 expression: "Infinity", 93 }, 94 { 95 context: CONTEXTS.JS, 96 expression: "BigInt(1000000000000000000)", 97 }, 98 { 99 context: CONTEXTS.JS, 100 expression: "1n", 101 }, 102 { 103 context: CONTEXTS.JS, 104 expression: "-2n", 105 }, 106 { 107 context: CONTEXTS.JS, 108 expression: "0n", 109 }, 110 ]; 111 112 const Primitives = [...BasicPrimitives, ...Strings, ...Numbers]; 113 114 const PlainObjects = [ 115 { 116 context: CONTEXTS.JS, 117 expression: "({})" 118 }, 119 { 120 context: CONTEXTS.JS, 121 expression: `({ foo: "bar"})` 122 } 123 ]; 124 125 const Arrays = [ 126 { 127 context: CONTEXTS.JS, 128 expression: "[]" 129 }, 130 { 131 context: CONTEXTS.JS, 132 expression: "[1]" 133 }, 134 { 135 context: CONTEXTS.JS, 136 expression: '["foo"]' 137 } 138 ]; 139 140 const TypedArrays = [ 141 { 142 context: CONTEXTS.JS, 143 expression: "new BigInt64Array()" 144 }, 145 { 146 context: CONTEXTS.JS, 147 expression: ` 148 const a = new BigInt64Array(1); 149 a[0] = BigInt(42); 150 a; 151 `, 152 }, 153 ]; 154 155 const Maps = [ 156 { 157 context: CONTEXTS.JS, 158 expression: `new Map( 159 Array.from({ length: 2 }).map((el, i) => [ 160 { key: i }, 161 { object: 42 }, 162 ]) 163 )`, 164 }, 165 { 166 context: CONTEXTS.JS, 167 expression: `new Map(Array.from({ length: 20 }).map((el, i) => [Symbol(i), i]))`, 168 }, 169 { 170 context: CONTEXTS.JS, 171 expression: `new Map(Array.from({ length: 331 }).map((el, i) => [Symbol(i), i]))`, 172 }, 173 ]; 174 175 const Sets = [ 176 { 177 context: CONTEXTS.JS, 178 expression: `new Set(Array.from({ length: 2 }).map((el, i) => ({ value: i })))`, 179 }, 180 { 181 context: CONTEXTS.JS, 182 expression: `new Set(Array.from({ length: 20 }).map((el, i) => i))` 183 }, 184 { 185 context: CONTEXTS.JS, 186 expression: `new Set(Array.from({ length: 222 }).map((el, i) => i))` 187 }, 188 ]; 189 190 const Temporals = [ 191 { 192 context: CONTEXTS.JS, 193 expression: `new Temporal.Instant(355924804000000000n)` 194 }, 195 { 196 context: CONTEXTS.JS, 197 expression: `new Temporal.PlainDate(2021, 7, 1, "coptic")` 198 }, 199 { 200 context: CONTEXTS.JS, 201 expression: `new Temporal.PlainDateTime(2021, 7, 1, 0, 0, 0, 0, 0, 0, "gregory")`, 202 }, 203 { 204 context: CONTEXTS.JS, 205 expression: `new Temporal.PlainMonthDay(7, 1, "chinese")` 206 }, 207 { 208 context: CONTEXTS.JS, 209 expression: `new Temporal.PlainTime(4, 20)` 210 }, 211 { 212 context: CONTEXTS.JS, 213 expression: `new Temporal.PlainYearMonth(2021, 7, "indian")` 214 }, 215 { 216 context: CONTEXTS.JS, 217 expression: `new Temporal.ZonedDateTime(0n, "America/New_York")` 218 }, 219 { 220 context: CONTEXTS.JS, 221 expression: `Temporal.Duration.from({ years: 1 })` 222 }, 223 ]; 224 225 const DOMAPIs = [ 226 { 227 context: CONTEXTS.PAGE, 228 expression: `myPolicy.createHTML("hello")`, 229 prefs: [["dom.security.trusted_types.enabled", true]], 230 }, 231 { 232 context: CONTEXTS.PAGE, 233 expression: `myPolicy.createScript("const hello = 'world'")` 234 }, 235 { 236 context: CONTEXTS.PAGE, 237 expression: `myPolicy.createScriptURL("https://example.com/trusted")` 238 }, 239 240 { 241 context: CONTEXTS.PAGE, 242 expression: ` 243 const formData = new FormData(); 244 formData.append("a", 1); 245 formData.append("a", 2); 246 formData.append("b", 3); 247 formData; 248 `, 249 }, 250 /* midi API requires https (See Bug 1967917) 251 { 252 context: CONTEXTS.PAGE, 253 expression: ` 254 const midiAccess = await navigator.requestMIDIAccess(); 255 midiAccess.inputs; 256 `, 257 prefs: [ 258 // This will make it so we'll have stable MIDI devices reported 259 ["midi.testing", true], 260 ["dom.webmidi.enabled", true], 261 ["midi.prompt.testing", true], 262 ["media.navigator.permission.disabled", true], 263 ], 264 }, 265 */ 266 { 267 context: CONTEXTS.PAGE, 268 expression: ` 269 customElements.define("fx-test", class extends HTMLElement {}); 270 const { states } = document.createElement("fx-test").attachInternals(); 271 states.add("custom-state"); 272 states.add("another-custom-state"); 273 states; 274 `, 275 }, 276 277 { 278 context: CONTEXTS.PAGE, 279 expression: ` 280 CSS.highlights.set("search", new Highlight()); 281 CSS.highlights.set("glow", new Highlight()); 282 CSS.highlights.set("anchor", new Highlight()); 283 CSS.highlights; 284 `, 285 prefs: [["dom.customHighlightAPI.enabled", true]], 286 }, 287 { 288 context: CONTEXTS.PAGE, 289 expression: `new URLSearchParams([ 290 ["a", 1], 291 ["a", 2], 292 ["b", 3], 293 ["b", 3], 294 ["b", 5], 295 ["c", "this is 6"], 296 ["d", 7], 297 ["e", 8], 298 ["f", 9], 299 ["g", 10], 300 ["h", 11], 301 ])`, 302 }, 303 ]; 304 305 const Errors = [ 306 { 307 context: CONTEXTS.JS, 308 expression: `new Error("foo")` 309 }, 310 { 311 context: CONTEXTS.JS, 312 expression: `throw new Error("Long error ".repeat(10000));`, 313 }, 314 { 315 context: CONTEXTS.JS, 316 expression: ` 317 throw \`“https://evil.com/?${"a".repeat( 318 200 319 )}“ is evil and “https://not-so-evil.com/?${"b".repeat( 320 200 321 )}“ is not good either\`; 322 `, 323 }, 324 { 325 context: CONTEXTS.JS, 326 expression: `Error("bar")` 327 }, 328 { 329 context: CONTEXTS.JS, 330 expression: ` 331 function bar() { 332 asdf(); 333 } 334 function foo() { 335 bar(); 336 } 337 338 foo(); 339 `, 340 }, 341 342 { 343 context: CONTEXTS.JS, 344 // Use nested `eval()` as syntax error would make the test framework throw on its own eval call 345 expression: `eval("let a, a")`, 346 }, 347 348 { 349 context: CONTEXTS.JS, 350 expression: `throw "";` 351 }, 352 { 353 context: CONTEXTS.JS, 354 expression: `throw false;` 355 }, 356 { 357 context: CONTEXTS.JS, 358 expression: `throw undefined;` 359 }, 360 { 361 context: CONTEXTS.JS, 362 expression: `throw 0;` 363 }, 364 { 365 context: CONTEXTS.JS, 366 expression: `throw { vegetable: "cucumber" };` 367 }, 368 { 369 context: CONTEXTS.JS, 370 expression: `throw Symbol("potato");` 371 }, 372 373 { 374 context: CONTEXTS.JS, 375 expression: ` 376 var err = new Error("pineapple"); 377 err.name = "JuicyError"; 378 err.flavor = "delicious"; 379 throw err; 380 `, 381 }, 382 { 383 context: CONTEXTS.JS, 384 expression: ` 385 var originalError = new SyntaxError("original error"); 386 var err = new Error("something went wrong", { 387 cause: originalError, 388 }); 389 throw err; 390 `, 391 }, 392 393 { 394 context: CONTEXTS.JS, 395 expression: ` 396 var a = new Error("err-a"); 397 var b = new Error("err-b", { cause: a }); 398 var c = new Error("err-c", { cause: b }); 399 var d = new Error("err-d", { cause: c }); 400 throw d; 401 `, 402 }, 403 { 404 context: CONTEXTS.JS, 405 expression: ` 406 var a = new Error("err-a", { cause: b }); 407 var b = new Error("err-b", { cause: a }); 408 throw b; 409 `, 410 }, 411 { 412 context: CONTEXTS.JS, 413 expression: `throw new Error("null cause", { cause: null });` 414 }, 415 { 416 context: CONTEXTS.JS, 417 expression: `throw new Error("number cause", { cause: 0 });` 418 }, 419 { 420 context: CONTEXTS.JS, 421 expression: `throw new Error("string cause", { cause: "cause message" });` 422 }, 423 { 424 context: CONTEXTS.JS, 425 expression: ` 426 throw new Error("object cause", { 427 cause: { code: 234, message: "ERR_234" }, 428 }); 429 `, 430 }, 431 432 { 433 context: CONTEXTS.JS, 434 expression: `Promise.reject("")` 435 }, 436 { 437 context: CONTEXTS.JS, 438 expression: `Promise.reject("tomato")` 439 }, 440 { 441 context: CONTEXTS.JS, 442 expression: `Promise.reject(false)` 443 }, 444 { 445 context: CONTEXTS.JS, 446 expression: `Promise.reject(0)` 447 }, 448 { 449 context: CONTEXTS.JS, 450 expression: `Promise.reject(null)` 451 }, 452 { 453 context: CONTEXTS.JS, 454 expression: `Promise.reject(undefined)` 455 }, 456 { 457 context: CONTEXTS.JS, 458 expression: `Promise.reject(Symbol("potato"))` 459 }, 460 { 461 context: CONTEXTS.JS, 462 expression: `Promise.reject({vegetable: "cucumber"})` 463 }, 464 { 465 context: CONTEXTS.JS, 466 expression: `Promise.reject(new Error("pumpkin"))` 467 }, 468 { 469 context: CONTEXTS.JS, 470 expression: ` 471 var err = new Error("pineapple"); 472 err.name = "JuicyError"; 473 err.flavor = "delicious"; 474 Promise.reject(err); 475 `, 476 }, 477 { 478 context: CONTEXTS.JS, 479 expression: `Promise.resolve().then(() => { 480 try { 481 unknownFunc(); 482 } catch(e) { 483 throw new Error("something went wrong", { cause: e }) 484 } 485 })`, 486 }, 487 { 488 context: CONTEXTS.JS, 489 expression: ` 490 throw new SuppressedError( 491 new Error("foo"), 492 new Error("bar"), 493 "the suppressed error message" 494 ); 495 `, 496 prefs: [ 497 ["javascript.options.experimental.explicit_resource_management", true], 498 ], 499 // eslint-disable-next-line no-constant-binary-expression 500 disabled: true || !AppConstants.ENABLE_EXPLICIT_RESOURCE_MANAGEMENT, 501 }, 502 ]; 503 504 const Privileged = [ 505 { 506 context: CONTEXTS.CHROME, 507 expression: `Components.Exception("foo")` 508 } 509 ]; 510 511 512 export const AllObjects = [ 513 ...Primitives, 514 ...PlainObjects, 515 ...Arrays, 516 ...TypedArrays, 517 ...Maps, 518 ...Sets, 519 ...Temporals, 520 ...DOMAPIs, 521 ...Errors, 522 ...Privileged, 523 ];