ESM Phase Imports: Module sources in import(), new Worker() and serialization #11152
+538
−135
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This draft PR specifies the ESM Phase Imports proposal, supporting JS and Wasm module representations supported in
import()
,new Worker()
and serialization.Background
The Phase 3 WebAssembly ESM Integration and the Stage 3 Source Phase Imports Proposal landed previously in #10380.
This PR extends these to support the Stage 2.7 ESM Phase Imports Proposal.
As well as relying on the ESM Phase Imports spec, this spec also relies on the WebAssembly ESM Integration PR WebAssembly/esm-integration#106.
Summary of the Proposal
The ESM Phase Imports proposal specifies a source phase for JavaScript modules - i.e. while we have support for
import source mod from './mod.wasm'
today, it is extending that support to JavaScript viaimport source mod form './mod.js'
having a higher-order module representation in JavaScript supporting aModuleSource
object like WebAssembly'sWebAssembly.Module
object.Further, the goal in the TC39 module harmony effort is for source phase imports to also form the basis of module declarations and module expressions such that this same
ModuleSource
object can also be the runtime representation formodule foo { export var bar = 5; }
inline modules.Given one of these modules
mod
, either imported in its source phase, or defined as an inline module, the ESM Phase Imports proposal is designed to support the following use cases:postMessage(mod)
,structuedClone(mod)
import(mod)
new Worker(mod)
This specification specifies all of the above for both
WebAssembly.Module
and the new JavaScriptModuleSource
representation specified in the ESM Phase Imports proposal.For more of the semantic detail background for the behaviours of the above, see the slides linked above in the background.
Security Model
Integrating with the web security model is one of the primary questions here, and if done right we can in fact create stronger guarantees around what it means to have the capability to execute a module.
We build off the security guarantee on the web existing already today - if you have already imported a module and got a handle to it (either its namespace or its source phase), then you have already passed security checks to be able to use it. That is,
import(mod)
does not imply any new security checks in the current design.Rather, we effectively design some new security model handling around
postMessage(mod)
andnew Worker(mod)
to integrate into the existing security model for workers.We do this by distinguishing between two cases of module - rooted and unrooted.
Rooted modules are modules that are obtained through static means, such that we know that the module came from a possibly securely checked original URL in the JS module registry. We add a new
rooted source
property on the module record to imply this. In addition, for now, we treat modules whose ResponseURL is different from their RequestURL as unrooted as well to ensure consistency in the URL handling.When we
postMessage
or pass tonew Worker
a rooted module, we not only serialize the module source text or source bytes (for JS and Wasm respectively), but we also serialize the URL of the module, as a secure identifier for that module. A worker constructed from a module takes its URL from this URL as well. We then aim to fully integrate with the existing CSP checks on these URLs (and further review here would help a lot to ensure we're catching all the cases).When we pass an unrooted module, we then fall back to requiring an eval or unsafe-wasm-eval based policy. Unrooted modules effectively being treated as "evalish", that their contents came from user provided bytes or source that has not been verified to come from a URL.
Specification Approach
The specification integrates all aspects of the proposal, from
new Worker(mod)
construction, to serialization toimport(mod)
, supporting the new JSModuleSource
and also adding this same support forWebAssembly.Module
objects.new Worker(module)
Worker construction works by using the new
HostGetModuleSourceModuleRecord
host hook introduced by the ESM Phase Imports proposal which allows identifying when an object is a module source object.The worker constructor is then extended to support this object when provided. Shared workers and worklets are not currently supported but could be nice additions.
The cross-origin isolated capability only applies for rooted sources in worker construction. And a worker created from an unrooted source is treated as having a null URL.
Since source transfer only transfers the direct source and none of its dependencies, in order for module resolution to work in workers, we are seeking to have this constructor also automatically set the
importMap: 'inherit'
option in the worker construction.It is therefore a goal for this spec to land after an
importMap: 'inherit'
option is specified in worker construction. There is already a specification PR for this in #10858. Once landed this specification can be updated to set this option by default in the worker constructor if not otherwise provided.Serialization
We add a new serialization case for the JS
ModuleSource
based on detecting objects with its[[SourceTextModuleRecord]]
internal slot. Wasm serialization remains separately defined in the Wasm Web API, and this is amended to support rooted source transfer in the ESM Integraiton PR WebAssembly/esm-integration#106.To properly support creating a WebAssembly module script eagerly on transfer in that, the Wasm parse function needed to be refactored slightly here as well.
Modules that are not rooted do not transfer their URLs in the current design, so that relative imports would might break if deserializing into a different base URL page context. This was for the convenience in the structure that rooted sources have associated module records, while unrooted sources do not have associated module records. If all sources had module records, or if we separaetly maintained the baseURL it might be possible to relax this, but the tradeoff might be fully requiring every
WebAssembly.Module
to immediately be associated with a module record if it doesn't need to be.import(module)
The ESM Phase Imports proposal adds support for
import(module)
, where the loading pipeline is able to accept a direct module record import. The HTML spec changes here add support for this new path and ensure that it provides the correct module keying semantics.In cases of
import(new WebAssembly.Module(bytes))
and in future maybe cases likeimport(eval('module { }'))
we need to lazily create module records for source objects that weren't previously associated with a module record.The keying semantics for
import(module)
while quite complex do come down to a simple check based on a newModuleSourcesEqual
call in the ESM Phase Imports spec, which is then used here. With these keying semantics importing the same module source always gives the same module instance in the registry. Even with serializing and deserializing, a rooted source will always get the same module instance in the registry when importing it. Unrooted sources on the other hand always have their identity tied to the identity of their unrooted source object. See the slides and notes for more details here.Next Steps
This work is a spec complete draft and ready for early review of the design and security model.
Once we've obtained further implementer and design feedback, then we can complete the remaining steps to creating a full spec PR.
At this point in time, we are primarily seeking interest and feedback from reviewers and implementers.
(See WHATWG Working Mode: Changes for more details.)
/infrastructure.html ( diff )
/references.html ( diff )
/structured-data.html ( diff )
/webappapis.html ( diff )
/workers.html ( diff )