Thanks to visit codestin.com
Credit goes to github.com

Skip to content

script: Add support for modulepreload link elements#42964

Open
Gae24 wants to merge 5 commits intoservo:mainfrom
Gae24:modulepreload
Open

script: Add support for modulepreload link elements#42964
Gae24 wants to merge 5 commits intoservo:mainfrom
Gae24:modulepreload

Conversation

@Gae24
Copy link
Contributor

@Gae24 Gae24 commented Mar 2, 2026

Add support to modulepreload link elements. Currently we only fetch the root module, as I think it is needs to be discussed whether we should always fetch module dependencies, or if it should be limited due to network constraints.
Inside bind_to_tree I ended up calling fetch_and_process_modulepreload inside a delayed task, since it would cause a crash due to DOM not being in a stable state when firing an event (queueing the event seems to also do the trick).

Remaining tests failures are due to not supporting CSS modules and performance entries's transfer_size being 0.

Testing: Covered by existing tests

Gae24 added 5 commits March 1, 2026 21:58
Specification says "A user agent must not delay the load event for this link type".
When starting a modulepreload fetch the module owner will be `ModuleOwner::DynamicModule`,
since a fetch with `ModuleOwner::Window` will be a blocking load.

Signed-off-by: Gae24 <[email protected]>
@Gae24 Gae24 requested a review from gterzian as a code owner March 2, 2026 21:59
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Mar 2, 2026
@Gae24 Gae24 added the T-linux-wpt Do a try run of the WPT label Mar 2, 2026
@github-actions github-actions bot removed the T-linux-wpt Do a try run of the WPT label Mar 2, 2026
@github-actions
Copy link

github-actions bot commented Mar 2, 2026

🔨 Triggering try run (#22597613616) for Linux (WPT)

@github-actions
Copy link

github-actions bot commented Mar 2, 2026

Test results for linux-wpt from try job (#22597613616):

Flaky unexpected result (28)
  • TIMEOUT /FileAPI/url/url-in-tags-revoke.window.html (#19978)
    • TIMEOUT [expected PASS] subtest: Fetching a blob URL immediately before revoking it works in &lt;script&gt; tags.

      Test timed out
      

  • OK /FileAPI/url/url-with-fetch.any.html (#21517)
    • FAIL [expected PASS] subtest: Revoke blob URL after calling fetch, fetch should succeed

      promise_test: Unhandled rejection with value: object "TypeError: Network error: Blob URL store error: InvalidFileID"
      

  • OK [expected TIMEOUT] /IndexedDB/idbfactory_open.any.worker.html
    • PASS [expected FAIL] subtest: Calling open() with version argument 1.5 should not throw.
    • PASS [expected TIMEOUT] subtest: Calling open() with version argument 9007199254740991 should not throw.
    • PASS [expected TIMEOUT] subtest: Calling open() with version argument undefined should not throw.
  • FAIL [expected PASS] /_mozilla/css/img_size_a.html (#42908)
  • OK /_mozilla/css/offset_properties_inline.html (#40543)
    • FAIL [expected PASS] subtest: offsetTop

      assert_equals: offsetTop of #inline-1 should be 0. expected 0 but got -1
      

    • FAIL [expected PASS] subtest: offsetLeft

      assert_equals: offsetLeft of #inline-2 should be 40. expected 40 but got 25
      

  • ERROR [expected OK] /_webgl/conformance/glsl/bugs/floored-division-accuracy.html
  • OK /_webgl/conformance/textures/misc/texture-upload-size.html (#21770)
    • PASS [expected FAIL] subtest: WebGL test #45
    • PASS [expected FAIL] subtest: WebGL test #47
    • PASS [expected FAIL] subtest: WebGL test #49
    • PASS [expected FAIL] subtest: WebGL test #51
    • FAIL [expected PASS] subtest: WebGL test #53

      assert_true: Texture was smaller than the expected size 2x2 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #55

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : when calling texSubImage2D with the same texture upload with offset 1, 1 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #57

      assert_true: Texture was smaller than the expected size 2x2 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #59

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : when calling texSubImage2D with the same texture upload with offset 1, 1 expected true got false
      

    • PASS [expected FAIL] subtest: WebGL test #61
    • PASS [expected FAIL] subtest: WebGL test #63
    • And 10 more unexpected results...
  • FAIL [expected PASS] /css/css-backgrounds/background-size-042.html
  • OK /fetch/content-length/api-and-duplicate-headers.any.worker.html (#35197)
    • FAIL [expected PASS] subtest: fetch() and duplicate Content-Length/Content-Type headers

      promise_test: Unhandled rejection with value: object "TypeError: Network error: HTTP failure: client error (SendRequest)"
      

  • OK /fetch/metadata/generated/css-font-face.https.sub.tentative.html (#32732)
    • PASS [expected FAIL] subtest: sec-fetch-mode
    • PASS [expected FAIL] subtest: sec-fetch-dest
  • OK [expected ERROR] /fetch/metadata/window-open.https.sub.html (#40339)
  • ERROR [expected TIMEOUT] /html/browsers/browsing-the-web/history-traversal/pageswap/pageswap-initial-navigation.html (#40387)
  • OK /html/browsers/browsing-the-web/navigating-across-documents/008.html (#24456)
    • PASS [expected FAIL] subtest: Link with onclick form submit to javascript url and href navigation
  • OK /html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-window-open.html (#28691)
    • FAIL [expected PASS] subtest: load event does not fire on window.open('about:blank')

      assert_unreached: load should not be fired Reached unreachable code
      

  • OK /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-cross-origin.sub.window.html (#29056)
    • PASS [expected FAIL] subtest: Cross-origin navigation started from unload handler must be ignored
  • OK /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment.html (#20768)
    • FAIL [expected PASS] subtest: Tests that a fragment navigation in the unload handler will not block the initial navigation

      assert_equals: expected "" but got "#fragment"
      

  • CRASH [expected TIMEOUT] /html/browsers/history/the-location-interface/location_replace_session_history.html (#41896)
  • CRASH [expected OK] /html/canvas/element/color-type/2d.color.type.u8srgb.to.f16p3.to.u8srgb.html
  • OK /html/semantics/embedded-content/media-elements/media_fragment_seek.html (#24114)
    • PASS [expected FAIL] subtest: Video should seek to time specified in media fragment syntax
  • TIMEOUT [expected OK] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_other_frame_popup.sub.html (#39702)
    • TIMEOUT [expected FAIL] subtest: Sandboxed iframe can not navigate other frame's popup

      Test timed out
      

  • TIMEOUT [expected OK] /html/user-activation/navigation-state-reset-sameorigin.html
    • TIMEOUT [expected PASS] subtest: Post-navigation state reset.

      Test timed out
      

  • OK /mixed-content/tentative/autoupgrades/mixed-content-cors.https.sub.html (#41123)
    • FAIL [expected PASS] subtest: Cross-Origin audio should get upgraded even if CORS is set

      assert_equals: Length of other host audio is correct expected 1 but got Infinity
      

  • PASS [expected FAIL] /png/apng/fcTL-dispose-none.html (#41817)
  • OK /preload/prefetch-document.html (#37210)
    • FAIL [expected PASS] subtest: different-site document prefetch with 'as=document' should not be consumed

      assert_equals: expected 2 but got 1
      

  • OK /resource-timing/buffer-full-add-then-clear.html (#40819)
    • FAIL [expected PASS] subtest: Test that if the buffer is cleared after entries were added to the secondary buffer, those entries make it into the primary one

      assert_equals: Number of entries does not match the expected value. expected 3 but got 0
      

  • TIMEOUT /trusted-types/trusted-types-navigation.html?06-10 (#37920)
    • PASS [expected FAIL] subtest: Navigate a frame via anchor with javascript:-urls in report-only mode.
  • OK /webdriver/tests/classic/take_screenshot/iframe.py
    • ERROR [expected PASS] subtest: test_always_captures_top_browsing_context

      setup error: ConnectionRefusedError: [Errno 111] Connection refused
      

    • ERROR [expected PASS] subtest: test_source_origin[same_origin]

      setup error: ConnectionRefusedError: [Errno 111] Connection refused
      

    • ERROR [expected PASS] subtest: test_source_origin[cross_origin]

      setup error: ConnectionRefusedError: [Errno 111] Connection refused
      

  • CRASH [expected OK] /webxr/navigator_xr_sameObject.https.html
Stable unexpected results that are known to be intermittent (21)
  • OK /_mozilla/mozilla/getBoundingClientRect.html (#39668)
    • FAIL [expected PASS] subtest: getBoundingClientRect 1

      assert_equals: expected 62 but got 60.35
      

  • FAIL [expected PASS] /_mozilla/mozilla/sslfail.html (#10760)
  • TIMEOUT [expected OK] /_mozilla/mozilla/window_resize_event.html (#36741)
    • TIMEOUT [expected PASS] subtest: Popup onresize event fires after resizeTo

      Test timed out
      

  • ERROR [expected CRASH] /_webgl/conformance2/textures/misc/tex-3d-size-limit.html (#42881)
    • PASS [expected FAIL] subtest: WebGL test #1
    • FAIL [expected PASS] subtest: WebGL test #3

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : texImage3D should fail for dimension out of range. expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #4

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : texImage3D should fail for dimension out of range. expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #5

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : texImage3D should fail for dimension out of range. expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #6

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : texImage3D should fail for dimension out of range. expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #7

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : texImage3D should fail for dimension out of range. expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #8

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : texImage3D should fail for dimension out of range. expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #9

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : texImage3D should fail for dimension out of range. expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #10

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : texImage3D should fail for dimension out of range. expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #11

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : texImage3D should fail for dimension out of range. expected true got false
      

    • And 27 more unexpected results...
  • OK /beacon/beacon-basic.https.window.html (#41723)
    • PASS [expected FAIL] subtest: Payload size restriction should be accumulated: type = string
    • FAIL [expected PASS] subtest: Payload size restriction should be accumulated: type = arraybuffer

      assert_false: expected false got true
      

  • OK /content-security-policy/frame-ancestors/frame-ancestors-path-ignored.window.html (#36468)
    • PASS [expected FAIL] subtest: A 'frame-ancestors' CSP directive with a URL that includes a path should be ignored.
  • OK /css/css-fonts/generic-family-keywords-001.html (#37467)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(fangsong)
  • OK /css/css-fonts/generic-family-keywords-003.html (#38994)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted sans-serif (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted fantasy (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted monospace (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted system-ui (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(fangsong) (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(khmer-mul) (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted ui-serif (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted ui-sans-serif (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted ui-monospace (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted ui-rounded (drawing text in a canvas)
  • ERROR [expected OK] /fetch/fetch-later/quota/same-origin-iframe/accumulated-oversized-payload.https.window.html (#41705)
  • OK /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin.window.html (#29049)
    • FAIL [expected PASS] subtest: Same-origin navigation started from unload handler must be ignored

      assert_equals: expected "?pass" but got "?fail"
      

  • OK [expected TIMEOUT] /html/interaction/focus/the-autofocus-attribute/supported-elements.html (#24145)
    • FAIL [expected NOTRUN] subtest: Host element with delegatesFocus should support autofocus

      assert_equals: expected Element node &lt;div autofocus=""&gt;&lt;/div&gt; but got Element node &lt;body&gt;&lt;/body&gt;
      

    • FAIL [expected NOTRUN] subtest: Host element with delegatesFocus including no focusable descendants should be skipped

      assert_equals: expected Element node &lt;input autofocus=""&gt;&lt;/input&gt; but got Element node &lt;body&gt;&lt;div autofocus=""&gt;&lt;/div&gt;&lt;input autofocus=""&gt;&lt;/body&gt;
      

    • FAIL [expected NOTRUN] subtest: Area element should support autofocus

      promise_test: Unhandled rejection with value: object "TypeError: can't access property "appendChild", w.document.querySelector(...) is null"
      

  • OK [expected TIMEOUT] /html/interaction/focus/the-autofocus-attribute/update-the-rendering.html (#24145)
    • FAIL [expected TIMEOUT] subtest: "Flush autofocus candidates" should be happen before a scroll event and animation frame callbacks

      assert_array_equals: animationFrame lengths differ, expected array ["autofocus", "scroll", "animationFrame"] length 3, got ["animationFrame"] length 1
      

  • OK /html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-1.html (#39694)
    • PASS [expected FAIL] subtest: Meta refresh is blocked by the allow-scripts sandbox flag at its creation time, not when refresh comes due
  • OK /html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-2.html (#39703)
    • FAIL [expected PASS] subtest: Meta refresh of the original iframe is not blocked if moved into a sandboxed iframe

      uncaught exception: Error: assert_unreached: The iframe into which the meta was moved must not refresh Reached unreachable code
      

  • OK /html/webappapis/user-prompts/print-during-unload.html (#35944)
    • PASS [expected FAIL] subtest: print() during unload
  • OK /resource-timing/test_resource_timing.html (#25720)
    • PASS [expected FAIL] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (iframe)
    • PASS [expected FAIL] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (link)
  • OK /touch-events/single-tap-when-touchend-listener-use-sync-xhr.html (#41175)
    • PASS [expected FAIL] subtest: Click event should be fired when touchend opens synchronous XHR
  • TIMEOUT /trusted-types/trusted-types-navigation.html?31-35 (#38034)
    • TIMEOUT [expected NOTRUN] subtest: Navigate a frame via form-submission with javascript:-urls w/ default policy in report-only mode.

      Test timed out
      

  • OK /visual-viewport/resize-event-order.html (#41981)
    • PASS [expected FAIL] subtest: Popup: DOMWindow resize fired before VisualViewport.
  • OK [expected TIMEOUT] /webstorage/localstorage-about-blank-3P-iframe-opens-3P-window.partitioned.html (#29053)
    • PASS [expected TIMEOUT] subtest: StorageKey: test 3P about:blank window opened from a 3P iframe
  • OK [expected ERROR] /webxr/render_state_update.https.html (#27535)
Stable unexpected results (13)
  • OK /html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports-credentials.sub.html
    • PASS [expected FAIL] subtest: Cross-origin dynamic import from classic script (crossOrigin=use-credentials)
  • OK [expected TIMEOUT] /html/semantics/scripting-1/the-script-element/module/modulepreload-cross-origin-referrerpolicy.sub.html
  • OK [expected TIMEOUT] /html/semantics/scripting-1/the-script-element/module/modulepreload-referrer-check.html
    • PASS [expected TIMEOUT] subtest: Modulepreload should send a referrer header
  • OK [expected TIMEOUT] /html/semantics/scripting-1/the-script-element/module/modulepreload-referrerpolicy.html
    • PASS [expected TIMEOUT] subtest: Modulepreload should use default referrer policy
    • PASS [expected NOTRUN] subtest: Modulepreload with no-referrer policy should not send referrer
    • PASS [expected NOTRUN] subtest: Modulepreload with origin policy should send origin-only referrer
    • PASS [expected NOTRUN] subtest: Modulepreload with same-origin policy should send full referrer for same-origin requests
    • PASS [expected NOTRUN] subtest: Modulepreload with strict-origin policy should send origin-only referrer
    • PASS [expected NOTRUN] subtest: Modulepreload with strict-origin-when-cross-origin policy should send full referrer for same-origin requests
    • PASS [expected NOTRUN] subtest: Modulepreload with unsafe-url policy should send full referrer
  • OK [expected TIMEOUT] /import-maps/nonimport-integrity.html
    • PASS [expected TIMEOUT] subtest: Modulepreload was not loaded as its integrity check was not ignored
    • PASS [expected NOTRUN] subtest: Modulepreload was loaded as its correct integrity attribute was not ignored
    • PASS [expected NOTRUN] subtest: Modulepreload was loaded as its empty integrity attribute was not ignored
    • PASS [expected NOTRUN] subtest: Modulepreload was not loaded as its bad integrity attribute was not ignored
  • OK [expected TIMEOUT] /preload/modulepreload-as.html
    • PASS [expected TIMEOUT] subtest: Modulepreload should fire onload with as=""
    • PASS [expected NOTRUN] subtest: Modulepreload should fire onerror with as="audio"
    • PASS [expected NOTRUN] subtest: Modulepreload should fire onload with as="audioworklet"
    • PASS [expected NOTRUN] subtest: Modulepreload should fire onerror with as="document"
    • PASS [expected NOTRUN] subtest: Modulepreload should fire onerror with as="embed"
    • PASS [expected NOTRUN] subtest: Modulepreload should fire onerror with as="font"
    • PASS [expected NOTRUN] subtest: Modulepreload should fire onerror with as="frame"
    • PASS [expected NOTRUN] subtest: Modulepreload should fire onerror with as="iframe"
    • PASS [expected NOTRUN] subtest: Modulepreload should fire onerror with as="image"
    • PASS [expected NOTRUN] subtest: Modulepreload should fire onerror with as="manifest"
    • And 17 more unexpected results...
  • OK [expected TIMEOUT] /preload/modulepreload-json.html
    • PASS [expected TIMEOUT] subtest: link rel=modulepreload with as=json should preload JSON module
  • OK [expected TIMEOUT] /preload/modulepreload-multiple.html
    • FAIL [expected TIMEOUT] subtest: multiple style preloads of the same file do not cause errors

      promise_test: Unhandled rejection with value: object "[object Event]"
      

    • PASS [expected NOTRUN] subtest: multiple json preloads of the same file do not cause errors
    • PASS [expected NOTRUN] subtest: multiple script preloads of the same file do not cause errors
  • TIMEOUT [expected OK] /preload/modulepreload-sri-importmap.html
    • TIMEOUT [expected FAIL] subtest: Script should not be loaded if modulepreload's integrity is invalid

      Test timed out
      

  • TIMEOUT [expected OK] /preload/modulepreload-sri.html
    • TIMEOUT [expected FAIL] subtest: Script should not be loaded if modulepreload's integrity is invalid

      Test timed out
      

  • OK [expected TIMEOUT] /preload/modulepreload-style.html
    • FAIL [expected TIMEOUT] subtest: link rel=modulepreload with as=style should preload CSS module

      promise_test: Unhandled rejection with value: object "[object Event]"
      

  • OK [expected TIMEOUT] /preload/modulepreload.html
    • FAIL [expected TIMEOUT] subtest: link rel=modulepreload with script shares a cache with &lt;script&gt; tags

      assert_equals: resources/dummy-module.js?unique expected 1 but got 0
      

    • FAIL [expected NOTRUN] subtest: same-origin link rel=modulepreload crossorigin=anonymous

      assert_equals: resources/dummy-module.js?sameOriginAnonymous expected 1 but got 0
      

    • FAIL [expected NOTRUN] subtest: same-origin link rel=modulepreload crossorigin=use-credentials

      assert_equals: resources/dummy-module.js?sameOriginUseCredentials expected 1 but got 0
      

    • PASS [expected NOTRUN] subtest: cross-origin link rel=modulepreload
    • PASS [expected NOTRUN] subtest: cross-origin link rel=modulepreload crossorigin=anonymous
    • PASS [expected NOTRUN] subtest: cross-origin link rel=modulepreload crossorigin=use-credentials
    • FAIL [expected NOTRUN] subtest: link rel=modulepreload with submodules

      assert_equals: resources/module1.js?submodule expected 1 but got 0
      

    • PASS [expected NOTRUN] subtest: link rel=modulepreload with bad href attribute
    • PASS [expected NOTRUN] subtest: link rel=modulepreload as=script
    • PASS [expected NOTRUN] subtest: link rel=modulepreload with non-script-like as= value (image)
    • And 37 more unexpected results...
  • OK [expected TIMEOUT] /wasm/webapi/esm-integration/source-phase-preload.tentative.html
    • FAIL [expected TIMEOUT] subtest: Static source phase import.

      promise_test: Unhandled rejection with value: object "[object Event]"
      

    • FAIL [expected NOTRUN] subtest: Dynamic source phase import.

      promise_test: Unhandled rejection with value: object "[object Event]"
      

@github-actions
Copy link

github-actions bot commented Mar 2, 2026

⚠️ Try run (#22597613616) failed!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-awaiting-review There is new code that needs to be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants