LibWeb: Address edge case on async module load

Issue #6294 describes an edge case where the browser crash if the same
module is loaded three times in a document, but all attempts fail.

Failure scenario:
1. Module load 1 set the state to "Fetching"
2. Module load 2 registers a callback to `on_complete` since the
   current state is "Fetching"
3. Module load 1 finish with a failure, invoking the callback for load
   number 2
4. Module load 3 cause a crash. The state is neither "Fetching" or
   "ModuleScript", so we'll reset the state to "Fetching". This invokes
   the callback for module load 2 again, now with an unexpected state
   which will cause an assert violation.

Proposed fix is to remove the condition that invokes `on_complete`
immediately for successfully loaded modules only, the callback should
be invoked regardless of whether the fetch succeeded or failed.

This reveals a separate bug in HTMLScriptElement, where
`mark_as_ready()` can be invoked before
`m_steps_to_run_when_the_result_is_ready` is assigned.
This appears to be a spec bug, reported as
https://github.com/whatwg/html/issues/12073 and addressed by delaying
the callback by a task, similar to the issue was resolved for inline
scripts.
This commit is contained in:
Christoffer Haglund
2026-01-05 17:11:01 +01:00
committed by Shannon Booth
parent 1106496d1c
commit 14ccc87190
Notes: github-actions[bot] 2026-01-13 17:13:50 +00:00
5 changed files with 32 additions and 3 deletions

View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<!--
Adapted from https://github.com/LadybirdBrowser/ladybird/issues/6294
Regression test for a crash when target JS modules fail to load
-->
<script src="../../include.js"></script>
<script defer="defer" async type="module" src="file///doesnotexist1.file"></script>
<script defer="defer" async type="module" src="file///doesnotexist1.file"></script>
<script src="file///doesnotexist1.file"></script>
<script defer="defer" async type="module" src="file///doesnotexist1.file"></script>
<script defer="defer">
test(() => println("PASS! (Didn't crash)"));
</script>