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(…); // 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<DOMString, any> imports; + record<DOMString, record<DOMString, any>> scopes; + record<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<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<DOMString, record<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<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