I recently made a Pull Request to Servo that fixes the implementation of importing modules in Worklets.
I already made a high level existing post about the differences in Workers and Worklets, and what I was doing. This posts gets into more details.
addModule(moduleURL, options)
When initialization a Worklet in a web browser, the specification requires the web developers to call addModule method. The implementation of addModule method contains the code responsible for getting all the JavaScript files, executing them, and resolving or rejecting the promise.
In the step 6.4 of the addModule specs, a browser is required to queue a global task on fetch a worklet script graph with different arguments like outsideSettings (it is essentially the context of the current scope we are in), the workletInstance, the worklet destination type, and other settings.
The fetch a worklet script graph algorithm defines a bunch of steps, my work does not implement those steps because the current worklet implementation in Servo does not have a module response map
As a side note, implementations in Servo often do not completely match the specification because some steps might require underlying infrastructure to implement, which can be hard or we try to understand the spirit of the implementation and do it as close to the spec as possible.
If we look at the fetch a worklet script graph algorithm, it calls an onComplete, to quote the specification:
and an algorithm onComplete, fetch a worklet/module worker script graph given url, fetchClient, destination, credentialsMode, settingsObject, onComplete
The fetch a worklet/module worker script graph is common between worklet and workers, and this is the algorithm we decided to use for the worklets.
With the above context in mind, I believe most of the code inside the on_complete code follows the specification pretty well. And as part of the PR, all the code are annotated with their respective steps, my mentors helped a lot with annotations and explaining me different concepts.
The steps that differ from the specification are annotated with notes, and that's what I want to talk about.
The code that couldn't follow the specs
To me, a lot of interesting parts about my work lies here. I found that these parts often described different limitations of the current Servo architecture. With that said, it's likely that someone will work at these parts and we will do things the right way in the future.
// Step 6.4.2. If script's error to rethrow is not null: // NOTE: The
AddModulespecification in the Step 6.4.2.1.1.2. requires the promise to be rejected with the script's “rethrow error”. // However, theJSValfromget_rethrow_errorcannot be used with thepromise_taskhere because they are from different runtimes. // So we throw an AbortError instead.
We had to deviate from the specification here because we could not pass the rethrow error between the different runtimes.
Some background, Worklets allow web developers to run tasks in parallel to the main event loop. JavaScript is a single-threaded language. How do you run a Worklet? You create a different runtime for the Worklet, which has its own global scope and event loop.
A RethrowError is a JavaScript value (represented by the type JSVal in Servo).
A browser rendering engine require careful orchestration between the JavaScript engine (spider monkey in this case) and the browser rendering engine (Servo). Both of them are in different programming languages and act as different programs.
A JavaScript engine uses garbage collection, so it needs to track the lifecycle of different JavaScript values so it can free the memory when an object is not being used anymore.
And that's exactly where we get the problem of sharing RethrowError between runtimes, if we were to pass a RethrowError to the main thread when rejecting the promise, it can lead to weird situations, so it requires more underlying infrastructure to do well.