From cec5fcca61595dbaab2235d24a1e4e8655e4569f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= <nribaudo@igalia.com>
Date: Sat, 23 Nov 2024 12:49:07 +0100
Subject: [PATCH 1/2] Allow dedicated Workers to inherit the creator's import
 map

This option adds an `importMap: "none" | "clone"` option to the `Worker`
constructor, which defaults to `"none"`. When this option is set to
`"clone"`, the `Worker` constructor will take a snapshot of the current
import map and use it as the import map inside the worker.

Passing the `importMap` option to `SharedWorker` is an error, to make it
possible to give it a meaning in the future.
---
 source | 74 +++++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 55 insertions(+), 19 deletions(-)

diff --git a/source b/source
index f17c3694ad9..92ffce1972b 100644
--- a/source
+++ b/source
@@ -106488,10 +106488,11 @@ new PaymentRequest(&hellip;); // Allowed to use
   <p>A <span>global object</span> has an <dfn data-x="concept-global-import-map">import map</dfn>,
   initially an <span>empty import map</span>.</p>
 
-  <p class="note">For now, only <code>Window</code> <span data-x="global object">global
-  objects</span> have their <span data-x="concept-global-import-map">import map</span> modified
-  from the initial empty one. The <span data-x="concept-global-import-map">import map</span> is
-  only accessed for the resolution of a root <span>module script</span>.</p>
+  <p class="note">For now, only <code>Window</code> and <code>DedicatedWorkerGlobalScope</code>
+  <span data-x="global object">global objects</span> have their <span
+  data-x="concept-global-import-map">import map</span> modified from the initial empty one. The
+  <span data-x="concept-global-import-map">import map</span> is only accessed for the resolution of
+  a root <span>module script</span>.</p>
 
   <p>A <span>global object</span> has a <dfn>resolved module set</dfn>, a <span>set</span>
   of <span data-x="specifier resolution record">specifier resolution records</span>, initially
@@ -109215,12 +109216,9 @@ dictionary <dfn dictionary>PromiseRejectionEventInit</dfn> : <span>EventInit</sp
     </ol>
    </li>
 
-   <li><p>Let <var>importMap</var> be an <span>empty import map</span>.</p></li>
-
-   <li><p>If <var>settingsObject</var>'s <span data-x="concept-settings-object-global">global
-   object</span> implements <code>Window</code>, then set <var>importMap</var> to
-   <var>settingsObject</var>'s <span data-x="concept-settings-object-global">global object</span>'s
-   <span data-x="concept-global-import-map">import map</span>.</p></li>
+   <li><p>Let <var>importMap</var> be <var>settingsObject</var>'s <span
+   data-x="concept-settings-object-global">global object</span>'s <span
+   data-x="concept-global-import-map">import map</span>.</p></li>
 
    <li><p>Let <var>serializedBaseURL</var> be <var>baseURL</var>, <span
    data-x="concept-url-serializer">serialized</span>.</p></li>
@@ -120547,8 +120545,8 @@ interface <dfn interface>SharedWorkerGlobalScope</dfn> : <span>WorkerGlobalScope
   <p>When a user agent is to <dfn export>run a worker</dfn> for a script with <code>Worker</code> or
   <code>SharedWorker</code> object <var>worker</var>, <span>URL</span> <var>url</var>,
   <span>environment settings object</span> <var>outside settings</var>, <code>MessagePort</code>
-  <var>outside port</var>, and a <code>WorkerOptions</code> dictionary <var>options</var>, it must
-  run the following steps.</p>
+  <var>outside port</var>, a <code>WorkerOptions</code> dictionary <var>options</var>, and an
+  <span>import map</span> <var>importMap</var>, it must run the following steps.</p>
 
   <ol>
    <li><p>Let <var>is shared</var> be true if <var>worker</var> is a <code>SharedWorker</code>
@@ -120591,6 +120589,9 @@ interface <dfn interface>SharedWorkerGlobalScope</dfn> : <span>WorkerGlobalScope
    data-x="concept-WorkerGlobalScope-name">name</span> to the value of <var>options</var>'s
    <code data-x="">name</code> member.</p></li>
 
+   <li><p>Set <var>worker global scope</var>'s <span
+   data-x="concept-global-import-map">import map</span> to <var>importMap</var>.</p></li>
+
    <li><p><span data-x="set append">Append</span> <var>owner</var> to <var>worker global
    scope</var>'s <span>owner set</span>.</p></li>
 
@@ -121005,7 +121006,7 @@ interface <dfn interface>SharedWorkerGlobalScope</dfn> : <span>WorkerGlobalScope
 
   <pre><code class="idl">[Exposed=(Window,DedicatedWorker,SharedWorker)]
 interface <dfn interface>Worker</dfn> : <span>EventTarget</span> {
-  <span data-x="dom-Worker">constructor</span>((<code data-x="tt-trustedscripturl">TrustedScriptURL</code> or USVString) scriptURL, optional <span>WorkerOptions</span> options = {});
+  <span data-x="dom-Worker">constructor</span>((<code data-x="tt-trustedscripturl">TrustedScriptURL</code> or USVString) scriptURL, optional <span>DedicatedWorkerOptions</span> options = {});
 
   undefined <span data-x="dom-Worker-terminate">terminate</span>();
 
@@ -121015,12 +121016,18 @@ interface <dfn interface>Worker</dfn> : <span>EventTarget</span> {
 
 dictionary <dfn dictionary>WorkerOptions</dfn> {
   <span>WorkerType</span> type = "classic";
-  <span>RequestCredentials</span> credentials = "same-origin"; // credentials is only used if type is "module"
+  <span>RequestCredentials</span>  credentials = "same-origin"; // credentials is only used if type is "module"
   DOMString name = "";
 };
 
+dictionary <dfn dictionary>DedicatedWorkerOptions</dfn> : <span>WorkerOptions</span> {
+  <span>WorkerImportMapInheritance</span> importMap = "none";
+};
+
 enum <dfn enum>WorkerType</dfn> { "classic", "module" };
 
+enum <dfn enum>WorkerImportMapInheritance</dfn> { "none", "clone" };
+
 <span>Worker</span> includes <span>AbstractWorker</span>;
 <span>Worker</span> includes <span>MessageEventTarget</span>;</code></pre>
 
@@ -121108,6 +121115,27 @@ enum <dfn enum>WorkerType</dfn> { "classic", "module" };
    <li><p>If <var>worker URL</var> is failure, then throw a <span>"<code>SyntaxError</code>"</span>
    <code>DOMException</code>.</p></li>
 
+   <li><p>Let <var>importMap</var> be an <span>empty import map</span>.</p></li>
+
+   <li>
+    <p>If <var>options</var>'s <code data-x="">importMap</code> member is "<code
+    data-x="">clone</code>", then:</p>
+
+    <ol>
+     <li><p>If the <span>current global object</span> implements <code>Window</code> or
+     <code>DedicatedWorkerGlobalScope</code>, set <var>importMap</var> to a deep copy of the
+     <span>current global object</span>'s <span data-x="concept-global-import-map">import
+     map</span>.</p></li>
+
+     <li><p>Else, throw a <code>TypeError</code> exception.</p></li>
+    </ol>
+
+    <p class="note">The <span>current global object</span>'s <span
+    data-x="concept-global-import-map">import map</span> could be modified, by <span
+    data-x="merge existing and new import maps">merging it with a new import map</span>. Such
+    updates will not be reflected in the cloned import map.</p>
+   </li>
+
    <li><p>Let <var>worker</var> be a new <code>Worker</code> object.</p></li>
 
    <li><p>Let <var>outside port</var> be a <span>new</span> <code>MessagePort</code> in <var>outside
@@ -121123,7 +121151,7 @@ enum <dfn enum>WorkerType</dfn> { "classic", "module" };
 
     <ol>
      <li><p><span>Run a worker</span> given <var>worker</var>, <var>worker URL</var>, <var>outside
-     settings</var>, <var>outside port</var>, and <var>options</var>.</p></li>
+     settings</var>, <var>outside port</var> <var>options</var>, and <var>importMap</var>.</p></li>
     </ol>
    </li>
 
@@ -121137,10 +121165,15 @@ enum <dfn enum>WorkerType</dfn> { "classic", "module" };
 
   <pre><code class="idl">[Exposed=Window]
 interface <dfn interface>SharedWorker</dfn> : <span>EventTarget</span> {
-  <span data-x="dom-SharedWorker">constructor</span>((<code data-x="tt-trustedscripturl">TrustedScriptURL</code> or USVString) scriptURL, optional (DOMString or <span>WorkerOptions</span>) options = {});
+  <span data-x="dom-SharedWorker">constructor</span>((<code data-x="tt-trustedscripturl">TrustedScriptURL</code> or USVString) scriptURL, optional (DOMString or <span>SharedWorkerOptions</span>) options = {});
 
   readonly attribute <span>MessagePort</span> <span data-x="dom-SharedWorker-port">port</span>;
 };
+
+dictionary <dfn dictionary>SharedWorkerOptions</dfn> : <span>WorkerOptions</span> {
+ any importMap; // used to prevent passing DedicatedWorkerOptions' importMap option
+};
+
 <span>SharedWorker</span> includes <span>AbstractWorker</span>;</code></pre>
 
   <dl class="domintro">
@@ -121193,7 +121226,7 @@ interface <dfn interface>SharedWorker</dfn> : <span>EventTarget</span> {
    "<code data-x="">script</code>".</p></li>
 
    <li><p>If <var>options</var> is a <code data-x="idl-DOMString">DOMString</code>, set
-   <var>options</var> to a new <code>WorkerOptions</code> dictionary whose <code
+   <var>options</var> to a new <code>SharedWorkerOptions</code> dictionary whose <code
    data-x="">name</code> member is set to the value of <var>options</var> and whose other members
    are set to their default values.</p></li>
 
@@ -121212,6 +121245,9 @@ interface <dfn interface>SharedWorker</dfn> : <span>EventTarget</span> {
    <li><p>If <var>urlRecord</var> is failure, then throw a <span>"<code>SyntaxError</code>"</span>
    <code>DOMException</code>.</p></li>
 
+   <li><p>If <var>options</var>'s <code data-x="">importMap</code> member is not undefined, throw
+   a <code>TypeError</code> exception.</p></li>
+
    <li><p>Let <var>worker</var> be a new <code>SharedWorker</code> object.</p></li>
 
    <li><p>Let <var>outside port</var> be a <span>new</span> <code>MessagePort</code> in <var>outside
@@ -121338,8 +121374,8 @@ interface <dfn interface>SharedWorker</dfn> : <span>EventTarget</span> {
      </li>
 
      <li><p>Otherwise, <span>in parallel</span>, <span>run a worker</span> given <var>worker</var>,
-     <var>urlRecord</var>, <var>outside settings</var>, <var>outside port</var>, and
-     <var>options</var>.</p></li>
+     <var>urlRecord</var>, <var>outside settings</var>, <var>outside port</var>, <var>options</var>,
+     and an <span>empty import map</span>.</p></li>
     </ol>
    </li>
 

From 6533032eb502af6210e57c6efaeb4b8f208ed8d5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= <nribaudo@igalia.com>
Date: Sat, 23 Nov 2024 16:51:18 +0100
Subject: [PATCH 2/2] Support passing an object as the import map to `Worker`

This commit allows passing a JavaScript object to `Worker`'s `importMap`
option, which is validated and used as the worker's import map.

The existing validation of the JSON import map has been delegated to
WebIDL, so that it can be re-used for this option's value.
---
 source | 94 +++++++++++++++++++++++++++++++---------------------------
 1 file changed, 51 insertions(+), 43 deletions(-)

diff --git a/source b/source
index 92ffce1972b..527854c9155 100644
--- a/source
+++ b/source
@@ -2318,6 +2318,7 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
      <li>The <dfn data-x-href="https://infra.spec.whatwg.org/#forgiving-base64-encode">forgiving-base64 encode</dfn> and
              <dfn data-x-href="https://infra.spec.whatwg.org/#forgiving-base64-decode">forgiving-base64 decode</dfn> algorithms</li>
      <li><dfn data-x-href="https://infra.spec.whatwg.org/#the-exclusive-range">exclusive range</dfn></li>
+     <li><dfn data-x-href="https://infra.spec.whatwg.org/#parse-a-json-string-to-a-javascript-value">parse a JSON string to a JavaScript value</dfn></li>
      <li><dfn data-x-href="https://infra.spec.whatwg.org/#parse-a-json-string-to-an-infra-value">parse a JSON string to an Infra value</dfn></li>
      <li><dfn data-x-href="https://infra.spec.whatwg.org/#html-namespace">HTML namespace</dfn></li>
      <li><dfn data-x-href="https://infra.spec.whatwg.org/#mathml-namespace">MathML namespace</dfn></li>
@@ -2878,6 +2879,7 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
      <li><dfn data-x="LegacyUnenumerableNamedProperties" data-x-href="https://webidl.spec.whatwg.org/#LegacyUnenumerableNamedProperties"><code>[LegacyUnenumerableNamedProperties]</code></dfn></li>
      <li><dfn data-x="LegacyUnforgeable" data-x-href="https://webidl.spec.whatwg.org/#LegacyUnforgeable"><code>[LegacyUnforgeable]</code></dfn></li>
      <li><dfn data-x-href="https://webidl.spec.whatwg.org/#dfn-set-entries">set entries</dfn></li>
+     <li><dfn data-x-href="https://webidl.spec.whatwg.org/#dfn-convert-ecmascript-to-idl-value">convert to an IDL value</dfn></li>
     </ul>
 
     <p><cite>Web IDL</cite> also defines the following types that are used in Web IDL fragments in
@@ -109747,27 +109749,51 @@ dictionary <dfn dictionary>PromiseRejectionEventInit</dfn> : <span>EventInit</sp
   </ol>
   <hr>
 
+  <pre><code class="idl">dictionary <dfn dictionary>ImportMap</dfn> {
+    record&lt;DOMString, any> imports;
+    record&lt;DOMString, record&lt;DOMString, any>> scopes;
+    record&lt;DOMString, any> integrity;
+  };</code></pre>
+
   <p>To <dfn>parse an import map string</dfn>, given a <span>string</span> <var>input</var> and a
   <span>URL</span> <var>baseURL</var>:</p>
 
   <ol>
-   <li><p>Let <var>parsed</var> be the result of <span data-x="parse a JSON string to an Infra
-   value">parsing a JSON string to an Infra value</span> given <var>input</var>.</p></li>
+   <li><p>Let <var>jsVal</var> be the result of <span data-x="parse a JSON string to a JavaScript
+   value">parsing a JSON string to a JavaScript value</span> given <var>input</var>.</p></li>
+
+   <li><p>If <var>jsVal</var> is null or undefined, throw a <code>TypeError</code> indicating that
+   the top-level value needs to be a JSON object.</p></li>
+
+   <li><p>Let <var>parsed</var> be <var>jsVal</var> <span data-x="convert to an IDL
+   value">converted</span> to an <code>ImportMap</code>.</p>
+
+   <li><p>Let <var>importMap</var> be the result of <span data-x="create an import map">creating an
+   import map</span> given <var>parsed</var> and <var>baseURL</var>.</p></li>
 
-   <li><p>If <var>parsed</var> is not an <span>ordered map</span>, then throw a
-   <code>TypeError</code> indicating that the top-level value needs to be a JSON object.</p></li>
+   <li>
+    <p>If <var>jsVal</var>.[[OwnPropertyKeys]]() <span data-x="list contains">contains</span> any
+    items besides "<code data-x="">imports</code>", "<code data-x="">scopes</code>", or "<code
+    data-x="">integrity</code>", then the user agent should <span>report a warning to the
+    console</span> indicating that an invalid top-level key was present in the import map.</p>
 
+    <p class="note">This can help detect typos. It is not an error, because that would prevent any
+    future extensions from being added backward-compatibly.</p>
+   </li>
+
+   <li><p>Return <var>importMap</var>.</p></li>
+  </ol>
+
+  <p>To <dfn>create an import map</dfn>, given an <code>ImportMap</code> <var>parsed</var> and a
+  <span>URL</span> <var>baseURL</var>:</p>
+
+  <ol>
    <li><p>Let <var>sortedAndNormalizedImports</var> be an empty <span>ordered map</span>.</p></li>
 
    <li>
-    <p>If <var>parsed</var>["<code data-x="">imports</code>"] <span data-x="map
-    exists">exists</span>, then:</p>
+    <p>If <var>parsed</var>["<code data-x="">imports</code>"] is not undefined, then:</p>
 
     <ol>
-     <li><p>If <var>parsed</var>["<code data-x="">imports</code>"] is not an <span>ordered
-     map</span>, then throw a <code>TypeError</code> indicating that the value for the "<code
-     data-x="">imports</code>" top-level key needs to be a JSON object.</p></li>
-
      <li><p>Set <var>sortedAndNormalizedImports</var> to the result of <span>sorting and
      normalizing a module specifier map</span> given <var>parsed</var>["<code
      data-x="">imports</code>"] and <var>baseURL</var>.</p></li>
@@ -109777,14 +109803,9 @@ dictionary <dfn dictionary>PromiseRejectionEventInit</dfn> : <span>EventInit</sp
    <li><p>Let <var>sortedAndNormalizedScopes</var> be an empty <span>ordered map</span>.</p></li>
 
    <li>
-    <p>If <var>parsed</var>["<code data-x="">scopes</code>"] <span data-x="map
-    exists">exists</span>, then:</p>
+    <p>If <var>parsed</var>["<code data-x="">scopes</code>"] is not undefined, then:</p>
 
     <ol>
-     <li><p>If <var>parsed</var>["<code data-x="">scopes</code>"] is not an <span>ordered
-     map</span>, then throw a <code>TypeError</code> indicating that the value for the "<code
-     data-x="">scopes</code>" top-level key needs to be a JSON object.</p></li>
-
      <li><p>Set <var>sortedAndNormalizedScopes</var> to the result of <span>sorting and normalizing
      scopes</span> given <var>parsed</var>["<code data-x="">scopes</code>"] and
      <var>baseURL</var>.</p></li>
@@ -109794,31 +109815,15 @@ dictionary <dfn dictionary>PromiseRejectionEventInit</dfn> : <span>EventInit</sp
    <li><p>Let <var>normalizedIntegrity</var> be an empty <span>ordered map</span>.</p></li>
 
    <li>
-    <p>If <var>parsed</var>["<code data-x="">integrity</code>"] <span data-x="map
-    exists">exists</span>, then:</p>
+    <p>If <var>parsed</var>["<code data-x="">integrity</code>"] is not undefined, then:</p>
 
     <ol>
-     <li><p>If <var>parsed</var>["<code data-x="">integrity</code>"] is not an <span>ordered
-     map</span>, then throw a <code>TypeError</code> indicating that the value for the "<code
-     data-x="">integrity</code>" top-level key needs to be a JSON object.</p></li>
-
      <li><p>Set <var>normalizedIntegrity</var> to the result of <span>normalizing a module
      integrity map</span> given <var>parsed</var>["<code data-x="">integrity</code>"] and
      <var>baseURL</var>.</p></li>
     </ol>
    </li>
 
-   <li>
-    <p>If <var>parsed</var>'s <span data-x="map key">keys</span> <span data-x="list
-    contains">contains</span> any items besides "<code data-x="">imports</code>", "<code
-    data-x="">scopes</code>", or "<code data-x="">integrity</code>", then the user agent should
-    <span>report a warning to the console</span> indicating that an invalid top-level key was
-    present in the import map.</p>
-
-    <p class="note">This can help detect typos. It is not an error, because that would prevent any
-    future extensions from being added backward-compatibly.</p>
-   </li>
-
    <li><p>Return an <span>import map</span> whose <span
    data-x="concept-import-map-imports">imports</span> are <var>sortedAndNormalizedImports</var>,
    whose <span data-x="concept-import-map-scopes">scopes</span> are
@@ -110197,8 +110202,8 @@ dictionary <dfn dictionary>PromiseRejectionEventInit</dfn> : <span>EventInit</sp
   </div>
 
   <p>To <dfn data-x="sorting and normalizing a module specifier map">sort and normalize a module
-  specifier map</dfn>, given an <span>ordered map</span> <var>originalMap</var> and a
-  <span>URL</span> <var>baseURL</var>:</p>
+  specifier map</dfn>, given a <code data-x="">record&lt;DOMString, any></code>
+  <var>originalMap</var> and a <span>URL</span> <var>baseURL</var>:</p>
 
   <ol>
    <li><p>Let <var>normalized</var> be an empty <span>ordered map</span>.</p></li>
@@ -110269,8 +110274,9 @@ dictionary <dfn dictionary>PromiseRejectionEventInit</dfn> : <span>EventInit</sp
    <var>b</var>'s <span data-x="map key">key</span>.</p></li>
   </ol>
 
-  <p>To <dfn data-x="sorting and normalizing scopes">sort and normalize scopes</dfn>, given an
-  <span>ordered map</span> <var>originalMap</var> and a <span>URL</span> <var>baseURL</var>:</p>
+  <p>To <dfn data-x="sorting and normalizing scopes">sort and normalize scopes</dfn>, given a <code
+  data-x="">record&lt;DOMString, record&lt;DOMString, any>></code> <var>originalMap</var> and a
+  <span>URL</span> <var>baseURL</var>:</p>
 
   <ol>
    <li><p>Let <var>normalized</var> be an empty <span>ordered map</span>.</p></li>
@@ -110280,10 +110286,6 @@ dictionary <dfn dictionary>PromiseRejectionEventInit</dfn> : <span>EventInit</sp
     <var>potentialSpecifierMap</var> of <var>originalMap</var>:</p>
 
     <ol>
-     <li><p>If <var>potentialSpecifierMap</var> is not an <span>ordered map</span>, then throw a
-     <code>TypeError</code> indicating that the value of the scope with prefix
-     <var>scopePrefix</var> needs to be a JSON object.</p></li>
-
      <li><p>Let <var>scopePrefixURL</var> be the result of <span data-x="URL parser">URL
      parsing</span> <var>scopePrefix</var> with <var>baseURL</var>.</p></li>
 
@@ -110319,7 +110321,7 @@ dictionary <dfn dictionary>PromiseRejectionEventInit</dfn> : <span>EventInit</sp
   during <span data-x="resolve a module specifier">module specifier resolution</span>.</p>
 
   <p>To <dfn data-x="normalizing a module integrity map">normalize a module integrity map</dfn>,
-  given an <span>ordered map</span> <var>originalMap</var>:</p>
+  given a <code data-x="">record&lt;DOMString, any></code> <var>originalMap</var>:</p>
 
   <ol>
    <li><p>Let <var>normalized</var> be an empty <span>ordered map</span>.</p></li>
@@ -121021,7 +121023,7 @@ dictionary <dfn dictionary>WorkerOptions</dfn> {
 };
 
 dictionary <dfn dictionary>DedicatedWorkerOptions</dfn> : <span>WorkerOptions</span> {
-  <span>WorkerImportMapInheritance</span> importMap = "none";
+  (<span>WorkerImportMapInheritance</span> or <span>ImportMap</span>) importMap = "none";
 };
 
 enum <dfn enum>WorkerType</dfn> { "classic", "module" };
@@ -121136,6 +121138,12 @@ enum <dfn enum>WorkerImportMapInheritance</dfn> { "none", "clone" };
     updates will not be reflected in the cloned import map.</p>
    </li>
 
+   <li><p>Else, if <var>options</var>'s <code data-x="">importMap</code> member is an
+   <code>ImportMap</code>, set <var>importMap</var> to the result of <span data-x="create an import
+   map">creating an import map</span> given <var>options</var>'s <code data-x="">importMap</code>
+   member and <span class="XXX">TODO: which base URL? The worker's, or the
+   creator's?</span>.</p></li>
+
    <li><p>Let <var>worker</var> be a new <code>Worker</code> object.</p></li>
 
    <li><p>Let <var>outside port</var> be a <span>new</span> <code>MessagePort</code> in <var>outside