not-as-classic-script.html (5703B)
1 <!DOCTYPE html> 2 <script src="/resources/testharness.js"></script> 3 <script src="/resources/testharnessreport.js"></script> 4 5 <script> 6 setup({allow_uncaught_exception : true}); 7 8 const t_parse = async_test("Import maps shouldn't be parsed as scripts"); 9 const t_already_started = async_test("The Already Started flag is passed through cloneNode"); 10 const t_evaluate = async_test("Import maps shouldn't be executed as scripts"); 11 const t_external = async_test( 12 "External import maps shouldn't be executed as scripts"); 13 const t_change_type = async_test( 14 "Import maps shouldn't be executed as scripts after changing type"); 15 16 const parseErrorHandler = event => { 17 event.preventDefault(); 18 t_parse.unreached_func("An import map is parsed as a classic script")(); 19 }; 20 21 window.addEventListener("error", parseErrorHandler, {once: true}); 22 </script> 23 24 <!-- This import map causes a parse error when parsed as a classic script. --> 25 <script type="importmap"> 26 { 27 "imports": { 28 } 29 } 30 </script> 31 32 <script> 33 window.removeEventListener("error", parseErrorHandler); 34 t_parse.done(); 35 36 const alreadyStartedErrorHandler = event => { 37 event.preventDefault(); 38 t_already_started.unreached_func("An import map is evaluated as a classic script")(); 39 }; 40 41 window.addEventListener("error", alreadyStartedErrorHandler, {once: true}); 42 </script> 43 44 <script> 45 // Once Already Started is true on a classic script, it cannot be 46 // converted into an import map. 47 const dynamicScriptClassic = document.createElement("script"); 48 dynamicScriptClassic.type = "text/javascript"; 49 dynamicScriptClassic.innerText = "t_already_started.step(function() { assert_true(true, 'Classic script connected dynamically.');});"; 50 51 // Inserting a <script> with contents sets Already Started to true. 52 document.head.appendChild(dynamicScriptClassic); 53 document.head.removeChild(dynamicScriptClassic); 54 55 // The existing innerText would be a parse error if parsed as an import 56 // map, but is valid as a classic script. 57 dynamicScriptClassic.type = "importmap"; 58 document.head.appendChild(dynamicScriptClassic); 59 60 // The Already Started flag on a script is persisted upon cloning, so 61 // the clone won't be parsed as an import map even though its `type` 62 // was set to "importmap" when it was connected. 63 const clonedClassicScript = dynamicScriptClassic.cloneNode(/*deep=*/true); 64 t_already_started.step(function() { 65 assert_equals(clonedClassicScript.type, "importmap"); 66 assert_equals(clonedClassicScript.innerText, dynamicScriptClassic.innerText); 67 }); 68 document.head.appendChild(clonedClassicScript); 69 70 // Creating an empty <script> tag does not set Already Started to true, 71 // so the type can be changed later. 72 const dynamicScriptEmpty = document.createElement("script"); 73 dynamicScriptEmpty.type = "text/javascript"; 74 document.head.appendChild(dynamicScriptEmpty); 75 76 // The Already Started flag is copied onto clones, so the type can be 77 // changed later. 78 const clonedDynamicScriptEmpty = dynamicScriptEmpty.cloneNode(/*deep=*/true); 79 t_already_started.step(function() { 80 assert_equals(clonedDynamicScriptEmpty.type, "text/javascript"); 81 }); 82 clonedDynamicScriptEmpty.setAttribute("type", "importmap"); 83 t_already_started.step(function() { 84 assert_equals(clonedDynamicScriptEmpty.type, "importmap"); 85 }); 86 87 // Since the contents are not empty, connecting the clone will set the 88 // Already Started flag to true and lock the type into an import map. 89 document.head.appendChild(clonedDynamicScriptEmpty); 90 document.head.removeChild(clonedDynamicScriptEmpty); 91 92 // This would cause a parse error when parsed as a classic script. 93 clonedDynamicScriptEmpty.innerText = "{ \"imports\": { } }"; 94 document.head.appendChild(clonedDynamicScriptEmpty); 95 96 // The original element can have its contents set and will executed 97 // as a classic script. Ensure this is executed by completing the test. 98 dynamicScriptEmpty.innerText = "t_already_started.done();"; 99 document.head.appendChild(dynamicScriptEmpty); 100 </script> 101 102 <script> 103 // Remove prior error handler, because the following import maps can 104 // causes parse errors. 105 window.removeEventListener("error", alreadyStartedErrorHandler); 106 </script> 107 108 <script> 109 // A dynamically created import map isn't parsed as a classic script. 110 const dynamicScript = document.createElement("script"); 111 dynamicScript.type = "importmap"; 112 t_evaluate.step(function() { 113 assert_equals(dynamicScript.type, "importmap"); 114 }); 115 dynamicScript.innerText = "t_evaluate.unreached_func('A dynamically-created import map is evaluated')();"; 116 document.head.appendChild(dynamicScript); 117 118 // Changing the type shouldn't execute script. 119 dynamicScript.type = "text/javascript"; 120 t_evaluate.step(function() { 121 assert_equals(dynamicScript.type, "text/javascript"); 122 }); 123 124 // Nor should removing it and re-inserting it. 125 document.head.removeChild(dynamicScript); 126 document.head.appendChild(dynamicScript); 127 </script> 128 129 <!-- Test the same with markup. --> 130 <script type="importmap"> 131 t_evaluate.unreached_func("An import map is evaluated")(); 132 </script> 133 134 <script> 135 t_evaluate.done(); 136 </script> 137 138 <script type="importmap" id="changeType"> 139 t_change_type.unreached_func("An import map is evaluated after changing type")(); 140 </script> 141 142 <script> 143 // Change the type from "importmap" to other valid values and ensure 144 // script never executes. 145 let changeType = document.getElementById("changeType"); 146 changeType.setAttribute("type", "text/javascript"); 147 changeType.setAttribute("type", "module"); 148 changeType.setAttribute("type", ""); 149 changeType.removeAttribute("type"); 150 t_change_type.done(); 151 </script> 152 153 <!-- Import Maps do not execute external scripts. --> 154 <script type="importmap" src="data:text/javascript,t_external.unreached_func('An external import map is evaluated')();"></script> 155 156 <script> 157 t_external.done(); 158 </script>