Skip to content

Conversation

@mrobinson
Copy link
Member

Currently, a single surfman::Device is used for all WebGL contexts.
This cannot work when we have multiple rendering contexts. So, extract
the surfman Connection and Adapter into a per-painter data structure
(PainterSurfmanDetailsMap) and store the Device directly in the
WebGL context.

This patch also modifies the WebXR traits so that the Device doesn't
need to be explictly passed into most methods.

This is a reland of #40594 with the following changes:

  • Do not remove WebGL contexts from the context map before cleaning up the
    WebXR layers. The layer cleanup process consults the map.
  • When cleanup up layers, be sure to replace the WebXRBridge in the
    WebGLThread data structure.
  • Allow failing to the Device when processing WebXR commands. WebXR
    sometimes tries to access contexts after they have been removed.

Testing: Should be covered by existing tests.

mukilan and others added 2 commits November 18, 2025 16:41
Currently, a single `surfman::Device` is used for all WebGL contexts.
This cannot work when we have multiple rendering contexts. So, extract
the surfman `Connection` and `Adapter` into a per-painter data structure
(`PainterSurfmanDetailsMap`) and store the `Device` directly in the
WebGL context.

This patch also modifies the WebXR traits so that the `Device` doesn't
need to be explictly passed into most methods.

Testing: Should be covered by existing tests.

---------

Signed-off-by: Mukilan Thiyagarajan <[email protected]>
Co-authored-by: Martin Robinson <[email protected]>
- Do not remove WebGL contexts from the context map before cleaning up the
  WebXR layers. The layer cleanup process consults the map.
- When cleanup up layers, be sure to replace the WebXRBridge in the
  WebGLThread data structure.
- Allow failing to the Device when processing WebXR commands. WebXR
  sometimes tries to access contexts after they have been removed.

Signed-off-by: Martin Robinson <[email protected]>
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Nov 18, 2025
@mrobinson mrobinson requested a review from mukilan November 18, 2025 15:43
@mrobinson mrobinson added the T-linux-wpt Do a try run of the WPT label Nov 18, 2025
@github-actions github-actions bot removed the T-linux-wpt Do a try run of the WPT label Nov 18, 2025
@github-actions
Copy link

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

@codecov-commenter
Copy link

⚠️ JUnit XML file not found

The CLI was unable to find any JUnit XML files to upload.
For more help, visit our troubleshooting guide.

@github-actions
Copy link

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

Flaky unexpected result (35)
  • OK /IndexedDB/idbfactory_open.any.html
    • FAIL [expected PASS] subtest: Calling open() with version argument 1.5 should not throw.

      assert_equals: version expected 1 but got 9007199254740991
      

  • CRASH [expected PASS] /_mozilla/shadow-dom/move-element-with-ua-shadow-tree-crash.html (#39473)
  • OK /_mozilla/webxr/create_session.https.html
    • FAIL [expected PASS] subtest: create_session

      can't access property "simulateDeviceConnection", navigator.xr.test is undefined
      

  • ERROR [expected TIMEOUT] /_mozilla/webxr/sessionavailable.https.html
  • CRASH [expected OK] /_webgl/conformance/ogles/GL/sqrt/sqrt_001_to_006.html
  • CRASH [expected OK] /_webgl/conformance/ogles/GL/struct/struct_017_to_024.html
  • CRASH [expected OK] /_webgl/conformance/renderbuffers/renderbuffer-initialization.html
  • CRASH [expected ERROR] /_webgl/conformance/textures/canvas/tex-2d-rgba-rgba-unsigned_byte.html
  • CRASH [expected OK] /_webgl/conformance/textures/image_bitmap_from_blob/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html
  • CRASH [expected ERROR] /_webgl/conformance2/textures/image/tex-3d-r16f-red-float.html
  • CRASH [expected OK] /_webgl/conformance2/wasm/readpixels-2gb-in-4gb-wasm-memory.html
  • CRASH [expected OK] /cookiestore/idlharness.https.any.worker.html
  • OK /custom-elements/form-associated/ElementInternals-setFormValue.html (#29174)
    • PASS [expected FAIL] subtest: Single value - Non-empty name exists
  • OK /html/browsers/history/the-history-interface/traverse_the_history_4.html (#21383)
    • PASS [expected FAIL] subtest: Multiple history traversals, last would be aborted
  • OK /html/browsers/history/the-history-interface/traverse_the_history_5.html (#21383)
    • PASS [expected FAIL] subtest: Multiple history traversals, last would be aborted
  • CRASH [expected OK] /html/browsers/windows/embedded-opener-remove-frame.html (#23867)
  • CRASH [expected OK] /html/dom/elements/global-attributes/dir-bdi-script.html
  • OK [expected TIMEOUT] /html/interaction/focus/the-autofocus-attribute/autofocus-dialog.html (#29087)
    • FAIL [expected TIMEOUT] subtest: &lt;dialog&gt;-contained autofocus element gets focused when the dialog is shown

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

  • OK [expected TIMEOUT] /html/semantics/embedded-content/media-elements/src_object_blob.html (#40340)
    • PASS [expected TIMEOUT] subtest: HTMLMediaElement.srcObject blob
  • OK /html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-reload-location-reload.html (#32595)
    • FAIL [expected PASS] subtest: Reloading iframe loading='lazy' before it is loaded: location.reload

      uncaught exception: Error: assert_equals: expected "http://web-platform.test:8000/html/semantics/embedded-content/the-iframe-element/support/blank.htm?src" but got "about:blank"
      

  • OK /html/semantics/forms/form-submission-0/multipart-formdata.window.html (#28725)
    • FAIL [expected PASS] subtest: multipart/form-data: Basic test (formdata event)

      assert_equals: expected "\r\nContent-Disposition: form-data; name=\"basic\"\r\n\r\ntest\r\n--\r\n" but got ""
      

    • PASS [expected FAIL] subtest: multipart/form-data: 0x00 in name (normal form)
    • PASS [expected FAIL] subtest: multipart/form-data: 0x00 in value (normal form)
    • PASS [expected FAIL] subtest: multipart/form-data: \n in value (normal form)
  • OK /html/semantics/forms/form-submission-0/text-plain.window.html (#28687)
    • FAIL [expected PASS] subtest: text/plain: Basic File test (normal form)

      assert_equals: expected "basic=file-test.txt\r\n" but got ""
      

    • PASS [expected FAIL] subtest: text/plain: Basic File test (formdata event)
    • PASS [expected FAIL] subtest: text/plain: backslash in name (formdata event)
  • OK [expected CRASH] /html/semantics/forms/the-fieldset-element/disabled-003.html (#31730, #39631)
  • OK [expected ERROR] /html/user-activation/no-activation-thru-escape-key.html (#40343)
  • CRASH [expected OK] /pointerevents/compat/pointerevent_touch-action_two-finger_interaction.html (#40418)
  • OK /preload/preload-xhr.html (#39092)
    • FAIL [expected PASS] subtest: Make an XHR request immediately after creating link rel=preload.

      assert_equals: resources/dummy.xml?token=280e433a-b8d2-4942-8c4f-b5fd03fe63b4 expected 1 but got 0
      

  • TIMEOUT /resource-timing/test_resource_timing.html (#25720)
    • PASS [expected FAIL] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (img)
  • CRASH [expected OK] /trusted-types/Element-setAttribute-setAttributeNS-sinks.tentative.html
  • TIMEOUT /trusted-types/trusted-types-navigation.html?06-10 (#37920)
    • TIMEOUT [expected FAIL] subtest: Navigate a frame via anchor with javascript:-urls in report-only mode.

      Test timed out
      

    • NOTRUN [expected TIMEOUT] subtest: Navigate a frame via anchor with javascript:-urls w/ default policy in report-only mode.
  • TIMEOUT /trusted-types/trusted-types-navigation.html?31-35 (#38034)
    • TIMEOUT [expected PASS] subtest: Navigate a frame via form-submission with javascript:-urls in report-only mode.

      Test timed out
      

    • NOTRUN [expected TIMEOUT] subtest: Navigate a frame via form-submission with javascript:-urls w/ default policy in report-only mode.
  • CRASH [expected ERROR] /wasm/webapi/empty-body.any.serviceworker.html
  • CRASH [expected OK] /webaudio/the-audio-api/processing-model/feedback-delay-time.html
  • OK /webxr/xrSession_features_deviceSupport.https.html (#24357)
    • FAIL [expected PASS] subtest: Immersive XRSession requests with no supported device should reject

      assert_unreached: Should have rejected: undefined Reached unreachable code
      

  • ERROR [expected OK] /workers/baseurl/alpha/import-in-moduleworker.html (#21315)
  • ERROR [expected OK] /workers/baseurl/alpha/sharedworker-in-worker.html (#21315)
Stable unexpected results that are known to be intermittent (29)
  • OK /IndexedDB/idbobjectstore_getAll.any.html (#39276)
    • PASS [expected FAIL] subtest: Get all values with transaction.commit()
  • OK /IndexedDB/idbobjectstore_getAll.any.worker.html (#39400)
    • PASS [expected FAIL] subtest: Get all values with transaction.commit()
  • 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
      

  • TIMEOUT /content-security-policy/inheritance/location-reload.html (#38983)
    • PASS [expected FAIL] subtest: location.reload() of empty iframe.
  • OK /css/css-fonts/generic-family-keywords-001.html (#37467)
    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted generic(khmer-mul)

      assert_equals: quoted generic(khmer-mul) matches  @font-face rule expected 50 but got 30
      

    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted generic(nastaliq)

      assert_equals: quoted generic(nastaliq) matches  @font-face rule expected 50 but got 30
      

  • OK /css/css-fonts/generic-family-keywords-003.html (#38994)
    • 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)
  • OK /fetch/fetch-later/permissions-policy/deferred-fetch-allowed-by-permissions-policy.https.window.html (#40478)
    • FAIL [expected PASS] subtest: Permissions policy header: "deferred-fetch=*" allows fetchLater() in the top-level document.

      assert_equals: Number of sent beacons does not match expected count: expected 1 but got 0
      

  • OK /fetch/metadata/generated/css-font-face.https.sub.tentative.html (#32732)
    • PASS [expected FAIL] subtest: sec-fetch-dest
    • FAIL [expected PASS] subtest: sec-fetch-storage-access - Cross-site

      promise_test: Unhandled rejection with value: object "Error: Failed to query for recorded headers."
      

  • OK [expected CRASH] /fetch/metadata/generated/element-iframe.https.sub.html (#40341)
  • OK /fetch/metadata/generated/element-img-environment-change.https.sub.html (#30111)
    • PASS [expected FAIL] subtest: sec-fetch-site - Cross-site, no attributes
    • FAIL [expected PASS] subtest: sec-fetch-site - Same-Origin -&gt; Same-Site -&gt; Same-Origin redirect, no attributes

      promise_test: Unhandled rejection with value: object "Error: Failed to query for recorded headers."
      

    • PASS [expected FAIL] subtest: sec-fetch-site - Cross-Site -&gt; Same Origin, no attributes
    • FAIL [expected PASS] subtest: sec-fetch-site - Cross-Site -&gt; Same-Site, no attributes

      promise_test: Unhandled rejection with value: object "Error: Failed to query for recorded headers."
      

    • PASS [expected FAIL] subtest: sec-fetch-mode - no attributes
    • FAIL [expected PASS] subtest: sec-fetch-mode - attributes: crossorigin

      promise_test: Unhandled rejection with value: object "Error: Failed to query for recorded headers."
      

    • PASS [expected FAIL] subtest: sec-fetch-mode - attributes: crossorigin=anonymous
    • FAIL [expected PASS] subtest: sec-fetch-mode - attributes: crossorigin=use-credentials

      promise_test: Unhandled rejection with value: object "Error: Failed to query for recorded headers."
      

    • PASS [expected FAIL] subtest: sec-fetch-dest - no attributes
  • OK /fetch/metadata/generated/element-img-environment-change.sub.html (#30111)
    • PASS [expected FAIL] subtest: sec-fetch-site - Not sent to non-trustworthy same-site destination, no attributes
    • PASS [expected FAIL] subtest: sec-fetch-mode - Not sent to non-trustworthy same-origin destination, no attributes
    • PASS [expected FAIL] subtest: sec-fetch-mode - Not sent to non-trustworthy cross-site destination, no attributes
    • PASS [expected FAIL] subtest: sec-fetch-user - Not sent to non-trustworthy cross-site destination, no attributes
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination, no attributes
  • ERROR /fetch/metadata/generated/serviceworker.https.sub.html (#36247)
    • FAIL [expected PASS] subtest: sec-fetch-site - Same origin, no options - registration

      promise_test: Unhandled rejection with value: object "Error: Failed to query for recorded headers."
      

  • OK [expected ERROR] /focus/focus-event-after-switching-iframes.sub.html (#40368)
  • OK /html/browsers/browsing-the-web/navigating-across-documents/005.html (#27062)
    • FAIL [expected PASS] subtest: Link with onclick navigation and href navigation

      assert_equals: expected "href" but got "click"
      

  • OK /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-cross-origin.sub.window.html (#29056)
    • FAIL [expected PASS] subtest: Cross-origin navigation started from unload handler must be ignored

      promise_test: Unhandled rejection with value: object "SecurityError: The operation is insecure."
      

  • OK /html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click.html (#28697)
    • PASS [expected FAIL] subtest: aElement.click() before the load event must NOT replace
  • OK /html/browsers/history/the-history-interface/traverse_the_history_2.html (#21383)
    • PASS [expected FAIL] subtest: Multiple history traversals, last would be aborted
  • OK /html/browsers/history/the-history-interface/traverse_the_history_3.html (#21383)
    • PASS [expected FAIL] subtest: Multiple history traversals, last would be aborted
  • ERROR [expected OK] /html/infrastructure/common-dom-interfaces/collections/domstringlist.html (#40665)
  • TIMEOUT /html/interaction/focus/the-autofocus-attribute/supported-elements.html (#24145)
    • FAIL [expected TIMEOUT] subtest: Element with tabindex should support autofocus

      assert_equals: expected "SPAN" but got "BODY"
      

    • PASS [expected NOTRUN] subtest: Non-HTMLElement should not support autofocus
    • TIMEOUT [expected NOTRUN] subtest: Host element with delegatesFocus should support autofocus

      Test timed out
      

  • 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 [expected ERROR] /html/semantics/embedded-content/media-elements/event_timeupdate_noautoplay.html (#25046)
  • OK [expected ERROR] /html/semantics/forms/the-input-element/click-user-gesture.html (#40512)
  • OK /navigation-timing/test-navigation-type-reload.html (#33334)
    • PASS [expected FAIL] subtest: Reload domComplete &gt; Original domComplete
    • PASS [expected FAIL] subtest: Reload domContentLoadedEventEnd &gt; Original domContentLoadedEventEnd
    • PASS [expected FAIL] subtest: Reload loadEventEnd &gt; Original loadEventEnd
    • PASS [expected FAIL] subtest: Reload loadEventStart &gt; Original loadEventStart
  • OK /preload/preload-error.sub.html (#37177)
    • PASS [expected FAIL] subtest: 404 (fetch): main
  • OK /preload/preload-invalid-resources.html (#39091)
    • PASS [expected FAIL] subtest: Preloading an invalid image (invalid data) should preload and not re-fetch
  • TIMEOUT [expected OK] /trusted-types/trusted-types-navigation.html?26-30 (#38807)
    • PASS [expected FAIL] subtest: Navigate a window via form-submission with javascript:-urls in report-only mode.
    • PASS [expected FAIL] subtest: Navigate a frame via form-submission with javascript:-urls in enforcing mode.
    • TIMEOUT [expected PASS] subtest: Navigate a frame via form-submission with javascript:-urls w/ default policy in enforcing mode.

      Test timed out
      

  • OK [expected ERROR] /webxr/render_state_update.https.html (#27535)

@github-actions
Copy link

✨ Try run (#19472012056) succeeded.

Copy link
Member

@mukilan mukilan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Nov 18, 2025
@mukilan mukilan added this pull request to the merge queue Nov 18, 2025
@servo-highfive servo-highfive added the S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. label Nov 18, 2025
bound_context_id: &mut self.bound_context_id,
// Take `webxr_bridge` from the `WebGLThread` in order to avoid a double mutable borrow.
let Some(mut webxr_bridge) = self.webxr_bridge.take() else {
return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can add a warn statement here to detect similar issues in the future where the bridge is not restored correctly but we continue to receive commands.

Merged via the queue into servo:main with commit f08c6ab Nov 18, 2025
73 checks passed
@servo-highfive servo-highfive removed the S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. label Nov 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants