Skip to content

Indexeddb: connection lifecycle#42082

Merged
gterzian merged 15 commits intoservo:mainfrom
gterzian:indexeddb_track_connections_2
Feb 2, 2026
Merged

Indexeddb: connection lifecycle#42082
gterzian merged 15 commits intoservo:mainfrom
gterzian:indexeddb_track_connections_2

Conversation

@gterzian
Copy link
Copy Markdown
Member

@gterzian gterzian commented Jan 22, 2026

Implement the full connection lifecycle, from opening and potentially upgrading a database, waiting on existing connections to close, firing the versionchange and blocked events, and updating pending open requests when connections close.

Testing: WPT
Fixes: Part of #40983

@gterzian gterzian force-pushed the indexeddb_track_connections_2 branch from 9cd2c18 to 9a7f9ca Compare January 23, 2026 13:41
@gterzian gterzian added the T-linux-wpt Do a try run of the WPT label Jan 23, 2026
@github-actions github-actions bot removed the T-linux-wpt Do a try run of the WPT label Jan 23, 2026
@github-actions
Copy link
Copy Markdown

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

@gterzian gterzian force-pushed the indexeddb_track_connections_2 branch from a12892a to 561159a Compare January 23, 2026 16:25
@gterzian gterzian added the T-linux-wpt Do a try run of the WPT label Jan 23, 2026
@github-actions github-actions bot removed the T-linux-wpt Do a try run of the WPT label Jan 23, 2026
@github-actions
Copy link
Copy Markdown

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

@github-actions
Copy link
Copy Markdown

⚠️ Try run (#21292559435) cancelled.

@github-actions
Copy link
Copy Markdown

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

Flaky unexpected result (40)
  • CRASH [expected OK] /IndexedDB/idbfactory-open-error-properties.any.html
  • CRASH [expected OK] /IndexedDB/idbfactory-open-error-properties.any.worker.html
  • CRASH [expected OK] /IndexedDB/idbfactory-open-opaque-origin.html
  • CRASH [expected OK] /IndexedDB/idbobjectstore-rename-abort.any.html
  • CRASH [expected OK] /IndexedDB/idbobjectstore-rename-abort.any.worker.html
  • CRASH [expected PASS] /_mozilla/shadow-dom/move-element-with-ua-shadow-tree-crash.html (#39473)
  • OK /beacon/beacon-basic.https.window.html (#41723)
    • FAIL [expected PASS] subtest: Payload size restriction should be accumulated: type = string

      assert_false: expected false got true
      

  • FAIL [expected PASS] /css/css-backgrounds/background-size-042.html
  • OK /css/css-cascade/layer-font-face-override.html (#35935)
    • FAIL [expected PASS] subtest: @font-face override update with appended sheet 1

      assert_equals: expected "80px" but got "38.3166666666667px"
      

    • FAIL [expected PASS] subtest: @font-face override update with appended sheet 2

      assert_equals: expected "80px" but got "38.3166666666667px"
      

  • OK /css/css-fonts/generic-family-keywords-001.html (#37467)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(fangsong)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(kai)
  • OK /css/css-fonts/generic-family-keywords-002.html (#40929)
    • FAIL [expected PASS] subtest: font-family: -webkit-serif treated as <font-family>, not <generic-name>

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-sans-serif treated as <font-family>, not <generic-name>

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-cursive treated as <font-family>, not <generic-name>

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-fantasy treated as <font-family>, not <generic-name>

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-monospace treated as <font-family>, not <generic-name>

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-system-ui treated as <font-family>, not <generic-name>

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-math treated as <font-family>, not <generic-name>

      assert_equals: expected 30 but got 50
      

    • PASS [expected FAIL] subtest: font-family: -webkit-generic(fangsong) treated as <font-family>, not <generic-name>
    • PASS [expected FAIL] subtest: font-family: -webkit-generic(kai) treated as <font-family>, not <generic-name>
    • PASS [expected FAIL] subtest: font-family: -webkit-generic(khmer-mul) treated as <font-family>, not <generic-name>
    • And 12 more unexpected results...
  • OK /css/css-fonts/variations/font-weight-matching.html (#38577)
    • FAIL [expected PASS] subtest: Test @font-face matching for weight 250

      assert_approx_equals: @font-face should be mapped to CSSTest Weights 800. expected 90 +/- 2 but got 180
      

    • FAIL [expected PASS] subtest: Test @font-face matching for weight 399

      assert_approx_equals: @font-face should be mapped to CSSTest Weights 800. expected 90 +/- 2 but got 180
      

    • FAIL [expected PASS] subtest: Test @font-face matching for weight 470

      assert_approx_equals: @font-face should be mapped to CSSTest Weights 300. expected 90 +/- 2 but got 180
      

    • FAIL [expected PASS] subtest: Test @font-face matching for weight 500

      assert_approx_equals: @font-face should be mapped to CSSTest Weights 300. expected 90 +/- 2 but got 180
      

  • CRASH [expected PASS] /css/css-overflow/scroll-marker-in-display-none-column-crash.html
  • OK /custom-elements/form-associated/ElementInternals-setFormValue.html (#29174)
    • PASS [expected FAIL] subtest: setFormValue with an empty FormData should submit nothing
  • TIMEOUT [expected OK] /fetch/api/redirect/redirect-keepalive.https.any.html (#32153)
    • TIMEOUT [expected PASS] subtest: [keepalive][iframe][load] mixed content redirect; setting up

      Test timed out
      

  • ERROR [expected OK] /fetch/metadata/window-open.https.sub.html (#40339)
  • OK [expected ERROR] /focus/focus-event-after-switching-iframes.sub.html (#40368)
  • CRASH [expected OK] /html/anonymous-iframe/embedding.tentative.https.window.html?7-7
  • CRASH [expected OK] /html/browsers/history/the-history-interface/012.html
  • OK /html/browsers/history/the-history-interface/traverse_the_history_3.html (#21383)
    • PASS [expected FAIL] subtest: Multiple history traversals, last would be aborted
  • 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/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 [expected TIMEOUT] /html/semantics/embedded-content/media-elements/src_object_blob.html (#40340)
    • PASS [expected TIMEOUT] subtest: HTMLMediaElement.srcObject blob
  • 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
      

  • CRASH [expected OK] /html/semantics/forms/form-submission-0/form-submit-iframe-then-location-navigate.html (#29634)
  • OK /html/semantics/forms/form-submission-0/jsurl-form-submit.tentative.html (#36489)
    • PASS [expected FAIL] subtest: Verifies that form submissions scheduled inside javascript: urls take precedence over the javascript: url's return value.
  • 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 ""
      

    • FAIL [expected PASS] subtest: multipart/form-data: Basic File test (normal form)

      assert_equals: expected "\r\nContent-Disposition: form-data; name=\"basic\"; filename=\"file-test.txt\"\r\nContent-Type: text/plain\r\n\r\n\r\n--\r\n" but got ""
      

    • PASS [expected FAIL] subtest: multipart/form-data: 0x00 in name (formdata event)
    • PASS [expected FAIL] subtest: multipart/form-data: 0x00 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)
  • OK /html/semantics/forms/form-submission-0/urlencoded2.window.html (#28687)
    • FAIL [expected PASS] subtest: application/x-www-form-urlencoded: Basic test (formdata event)

      assert_equals: expected "basic=test" but got ""
      

    • FAIL [expected PASS] subtest: application/x-www-form-urlencoded: Basic File test (normal form)

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

    • PASS [expected FAIL] subtest: application/x-www-form-urlencoded: Basic File test (formdata event)
  • FAIL [expected PASS] /png/apng/fcTL-dispose-in-region-previous.html (#41410)
  • OK /preload/prefetch-document.html (#37210)
    • PASS [expected FAIL] subtest: different-site document prefetch with 'as=document' should not be consumed
  • CRASH [expected OK] /resource-timing/render-blocking-status-link.html (#41664)
  • 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
  • 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.
  • CRASH [expected TIMEOUT] /uievents/mouse/cancel-mousedown-in-subframe.html
  • OK /wasm/webapi/abort.any.html (#39966)
    • FAIL [expected PASS] subtest: instantiateStreaming() asynchronously racing with abort should succeed or reject with AbortError

      assert_equals: expected "AbortError" but got "CompileError"
      

  • CRASH [expected TIMEOUT] /wasm/webapi/empty-body.any.worker.html
  • CRASH [expected OK] /workers/WorkerNavigator_onLine.htm
  • ERROR [expected OK] /workers/baseurl/alpha/sharedworker-in-worker.html (#21315)
Stable unexpected results that are known to be intermittent (28)
  • TIMEOUT /FileAPI/url/url-in-tags-revoke.window.html (#19978)
    • PASS [expected TIMEOUT] subtest: Fetching a blob URL immediately before revoking it works in <script> tags.
  • OK /IndexedDB/idbcursor-continuePrimaryKey-exceptions.any.html (#39277)
    • FAIL [expected PASS] subtest: IDBCursor continuePrimaryKey() on object store cursor

      assert_throws_dom: continuePrimaryKey() should throw if source is not an index function "function() {
              cursor.continuePrimaryKey(2, 2);
            }" threw object "TypeError: cursor.continuePrimaryKey is not a function" that is not a DOMException InvalidAccessError: property "code" is equal to undefined, expected 15
      

  • OK /IndexedDB/idbcursor-continuePrimaryKey-exceptions.any.worker.html (#39277)
    • FAIL [expected PASS] subtest: IDBCursor continuePrimaryKey() on object store cursor

      assert_throws_dom: continuePrimaryKey() should throw if source is not an index function "function() {
              cursor.continuePrimaryKey(2, 2);
            }" threw object "TypeError: cursor.continuePrimaryKey is not a function" that is not a DOMException InvalidAccessError: property "code" is equal to undefined, expected 15
      

  • 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()
  • CRASH [expected OK] /IndexedDB/idbrequest-onupgradeneeded.any.html (#38895)
  • CRASH [expected OK] /IndexedDB/idbrequest-onupgradeneeded.any.worker.html (#38971)
  • OK /IndexedDB/key-conversion-exceptions.any.html (#39305)
    • FAIL [expected PASS] subtest: IDBCursor continue() method with throwing/invalid keys

      assert_throws_exactly: key conversion with throwing getter should rethrow function "() => {
            receiver[method](key);
          }" threw object "TypeError: receiver[method] is not a function" but we expected it to throw object "getter: throwing from getter"
      

    • FAIL [expected PASS] subtest: IDBCursor update() method with throwing/invalid keys

      assert_throws_exactly: throwing getter should rethrow during clone function "() => {
            cursor.update(value);
          }" threw object "TypeError: cursor.update is not a function" but we expected it to throw object "getter: throwing from getter"
      

  • OK /IndexedDB/key-conversion-exceptions.any.worker.html (#39284)
    • FAIL [expected PASS] subtest: IDBCursor continue() method with throwing/invalid keys

      assert_throws_exactly: key conversion with throwing getter should rethrow function "() => {
            receiver[method](key);
          }" threw object "TypeError: receiver[method] is not a function" but we expected it to throw object "getter: throwing from getter"
      

    • FAIL [expected PASS] subtest: IDBCursor update() method with throwing/invalid keys

      assert_throws_exactly: throwing getter should rethrow during clone function "() => {
            cursor.update(value);
          }" threw object "TypeError: cursor.update is not a function" but we expected it to throw object "getter: throwing from getter"
      

  • OK /_mozilla/css/offset_properties_inline.html (#40543)
    • PASS [expected FAIL] subtest: offsetTop
    • PASS [expected FAIL] subtest: offsetLeft
  • 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-003.html (#38994)
    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted serif (drawing text in a canvas)

      assert_equals: quoted serif matches  @font-face rule expected 40 but got 125
      

    • 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 cursive (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 math (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(kai) (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(nastaliq) (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)
    • And 2 more unexpected results...
  • OK /fetch/metadata/generated/css-font-face.https.sub.tentative.html (#32732)
    • FAIL [expected PASS] subtest: sec-fetch-user

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

  • OK /fetch/metadata/generated/css-font-face.sub.tentative.html (#34624)
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Not sent to non-trustworthy same-site destination
  • OK /html/browsers/browsing-the-web/navigating-across-documents/005.html (#27062)
    • PASS [expected FAIL] subtest: Link with onclick navigation and href navigation
  • 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"
      

  • OK /html/browsers/windows/embedded-opener-remove-frame.html (#23867)
    • PASS [expected FAIL] subtest: opener of discarded auxiliary browsing context
  • CRASH [expected OK] /html/infrastructure/common-dom-interfaces/collections/domstringlist.html (#40665)
  • 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 <div autofocus=""></div> but got Element node <body><div autofocus=""></div></body>
      

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

      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
      

  • ERROR [expected TIMEOUT] /html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener_base.html (#40347)
  • OK [expected ERROR] /html/user-activation/no-activation-thru-escape-key.html (#40343)
  • OK /html/webappapis/user-prompts/print-during-unload.html (#35944)
    • FAIL [expected PASS] subtest: print() during unload

      assert_array_equals: expected property 1 to be "destination" but got "error: window.print is not a function" (expected array ["start", "destination"] got ["start", "error: window.print is not a function"])
      

  • TIMEOUT /resource-timing/test_resource_timing.html (#25720)
    • FAIL [expected PASS] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (img)

      assert_equals: expected 22.389999999999993 but got 22.39
      

  • TIMEOUT /resource-timing/test_resource_timing.https.html (#25216)
    • PASS [expected FAIL] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (img)
    • PASS [expected FAIL] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (xmlhttprequest)
  • ERROR [expected OK] /webxr/render_state_update.https.html (#27535)
Stable unexpected results (25)
  • CRASH [expected OK] /IndexedDB/abort-in-initial-upgradeneeded.any.html
  • CRASH [expected OK] /IndexedDB/abort-in-initial-upgradeneeded.any.worker.html
  • TIMEOUT [expected OK] /IndexedDB/back-forward-cache-open-connection.window.html
    • TIMEOUT [expected PASS] subtest: Testing BFCache support for page with open IndexedDB connection, and eviction behavior when receiving versionchange event.

      Test timed out
      

  • CRASH [expected OK] /IndexedDB/blob-delete-objectstore-db.any.html
  • CRASH [expected OK] /IndexedDB/blob-delete-objectstore-db.any.worker.html
  • CRASH [expected OK] /IndexedDB/get-databases.any.html
  • CRASH [expected OK] /IndexedDB/get-databases.any.worker.html
  • CRASH [expected OK] /IndexedDB/idb-explicit-commit.any.html
  • CRASH [expected OK] /IndexedDB/idb-explicit-commit.any.worker.html
  • CRASH [expected OK] /IndexedDB/idb-partitioned-persistence.sub.html
  • CRASH [expected TIMEOUT] /IndexedDB/idbdatabase-createObjectStore-exception-order.any.html
  • CRASH [expected TIMEOUT] /IndexedDB/idbdatabase-createObjectStore-exception-order.any.worker.html
  • CRASH [expected TIMEOUT] /IndexedDB/idbdatabase-deleteObjectStore-exception-order.any.html
  • CRASH [expected TIMEOUT] /IndexedDB/idbdatabase-deleteObjectStore-exception-order.any.worker.html
  • CRASH [expected OK] /IndexedDB/idbdatabase_close.any.html
  • CRASH [expected OK] /IndexedDB/idbdatabase_close.any.worker.html
  • CRASH [expected OK] /IndexedDB/idbfactory_open.any.html
  • CRASH [expected OK] /IndexedDB/idbfactory_open.any.worker.html
  • CRASH [expected OK] /IndexedDB/name-scopes.any.html
  • CRASH [expected OK] /IndexedDB/name-scopes.any.worker.html
  • CRASH [expected TIMEOUT] /IndexedDB/open-request-queue.any.html
  • CRASH [expected TIMEOUT] /IndexedDB/open-request-queue.any.worker.html
  • CRASH [expected TIMEOUT] /IndexedDB/transaction-lifetime.any.html
  • CRASH [expected TIMEOUT] /IndexedDB/transaction-lifetime.any.worker.html
  • TIMEOUT [expected OK] /IndexedDB/worker-termination-aborts-upgrade.window.html
    • TIMEOUT [expected PASS] subtest: Worker Termination Aborts a Pending Upgrade

      Test timed out
      

@github-actions
Copy link
Copy Markdown

⚠️ Try run (#21293293663) failed!

@gterzian gterzian force-pushed the indexeddb_track_connections_2 branch from addcafb to f862459 Compare January 26, 2026 02:24
@gterzian gterzian added the T-linux-wpt Do a try run of the WPT label Jan 26, 2026
@github-actions github-actions bot removed the T-linux-wpt Do a try run of the WPT label Jan 26, 2026
@github-actions
Copy link
Copy Markdown

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

@github-actions
Copy link
Copy Markdown

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

Flaky unexpected result (26)
  • OK /IndexedDB/idbfactory-open-error-properties.any.html
    • PASS [expected FAIL] subtest: Properties of error event from failed open()
  • OK /IndexedDB/idbfactory-open-error-properties.any.worker.html
    • PASS [expected FAIL] subtest: Properties of error event from failed open()
  • OK /css/css-fonts/generic-family-keywords-001.html (#37467)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(khmer-mul)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(nastaliq)
  • OK /custom-elements/form-associated/ElementInternals-setFormValue.html (#29174)
    • PASS [expected FAIL] subtest: Single value - empty name exists
    • PASS [expected FAIL] subtest: Single value - Non-empty name exists
  • OK /html/browsers/browsing-the-web/navigating-across-documents/005.html (#27062)
    • PASS [expected FAIL] subtest: Link with onclick navigation and href navigation
  • 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"
      

  • OK /html/browsers/history/the-history-interface/traverse_the_history_4.html (#21383)
    • PASS [expected FAIL] subtest: Multiple history traversals, last would be aborted
  • CRASH [expected OK] /html/browsers/the-window-object/window-open-noopener.html?_self
  • 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
      

  • OK /html/semantics/forms/form-submission-0/jsurl-form-submit.tentative.html (#36489)
    • PASS [expected FAIL] subtest: Verifies that form submissions scheduled inside javascript: urls take precedence over the javascript: url's return value.
  • OK /html/semantics/forms/form-submission-0/multipart-formdata.window.html (#28725)
    • FAIL [expected PASS] subtest: multipart/form-data: Basic File test (normal form)

      assert_equals: expected "\r\nContent-Disposition: form-data; name=\"basic\"; filename=\"file-test.txt\"\r\nContent-Type: text/plain\r\n\r\n\r\n--\r\n" but got ""
      

    • PASS [expected FAIL] subtest: multipart/form-data: 0x00 in value (formdata event)
    • PASS [expected FAIL] subtest: multipart/form-data: single quote in filename (normal form)
  • OK /html/semantics/forms/form-submission-0/text-plain.window.html (#28687)
    • FAIL [expected PASS] subtest: text/plain: Basic test (formdata event)

      assert_equals: expected "basic=test\r\n" but got ""
      

    • 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: \r\n in name (normal form)
    • PASS [expected FAIL] subtest: text/plain: backslash in filename (normal form)
  • OK /html/semantics/forms/form-submission-0/urlencoded2.window.html (#28687)
    • FAIL [expected PASS] subtest: application/x-www-form-urlencoded: Basic test (formdata event)

      assert_equals: expected "basic=test" but got ""
      

    • FAIL [expected PASS] subtest: application/x-www-form-urlencoded: Basic File test (normal form)

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

  • OK /html/webappapis/dynamic-markup-insertion/document-write/module-dynamic-import.html
    • FAIL [expected PASS] subtest: document.write in an imported module

      assert_true: onload must be called expected true got false
      

  • CRASH [expected OK] /html/webappapis/scripting/processing-model-2/runtime-error-cross-origin.html
  • OK /mixed-content/tentative/autoupgrades/video-upgrade.https.sub.html (#41135)
    • FAIL [expected PASS] subtest: Video of other host autoupgraded

      assert_equals: Length. Other host expected 1 but got Infinity
      

  • OK /navigation-timing/test-navigation-type-reload.html (#33334)
    • PASS [expected FAIL] subtest: Reload domComplete > Original domComplete
    • PASS [expected FAIL] subtest: Reload domContentLoadedEventEnd > Original domContentLoadedEventEnd
    • PASS [expected FAIL] subtest: Reload domContentLoadedEventStart > Original domContentLoadedEventStart
    • PASS [expected FAIL] subtest: Reload domInteractive > Original domInteractive
    • PASS [expected FAIL] subtest: Reload fetchStart > Original fetchStart
    • PASS [expected FAIL] subtest: Reload loadEventEnd > Original loadEventEnd
    • PASS [expected FAIL] subtest: Reload loadEventStart > Original loadEventStart
  • FAIL [expected PASS] /png/apng/fcTL-blend-source-solid.html (#41560)
  • PASS [expected FAIL] /png/apng/fcTL-dispose-none.html (#41817)
  • OK /resource-timing/buffer-full-add-then-clear.html (#40819)
    • PASS [expected FAIL] subtest: Test that if the buffer is cleared after entries were added to the secondary buffer, those entries make it into the primary one
  • CRASH [expected OK] /trusted-types/HTMLElement-generic.html
  • OK [expected TIMEOUT] /trusted-types/trusted-types-navigation.html?06-10 (#37920)
    • PASS [expected TIMEOUT] subtest: Navigate a frame via anchor with javascript:-urls w/ default policy in report-only mode.
    • FAIL [expected NOTRUN] subtest: Navigate a window via anchor with javascript:-urls w/ a default policy throwing an exception in enforcing mode.

      promise_test: Unhandled rejection with value: "Unexpected message received: \"No securitypolicyviolation reported!\""
      

    • FAIL [expected NOTRUN] subtest: Navigate a window via anchor with javascript:-urls w/ a default policy throwing an exception in report-only mode.

      promise_test: Unhandled rejection with value: "Unexpected message received: \"No securitypolicyviolation reported!\""
      

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

      promise_test: Unhandled rejection with value: "Unexpected message received: \"No securitypolicyviolation reported!\""
      

    • FAIL [expected NOTRUN] subtest: Navigate a window via form-submission with javascript:-urls w/ a default policy throwing an exception in report-only mode.

      promise_test: Unhandled rejection with value: "Unexpected message received: \"No securitypolicyviolation reported!\""
      

    • FAIL [expected NOTRUN] subtest: Navigate a window via form-submission with javascript:-urls w/ a default policy making the URL invalid in enforcing mode.

      promise_test: Unhandled rejection with value: "Unexpected message received: \"No securitypolicyviolation reported!\""
      

  • CRASH [expected OK] /webaudio/the-audio-api/the-delaynode-interface/maxdelay-rounding.html
  • 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 /xhr/xhr-timeout-longtask.any.worker.html
    • FAIL [expected PASS] subtest: Long tasks should not trigger load timeout

      assert_unreached: [object ProgressEvent] Reached unreachable code
      

Stable unexpected results that are known to be intermittent (32)
  • OK /IndexedDB/idbcursor-continuePrimaryKey-exceptions.any.html (#39277)
    • FAIL [expected PASS] subtest: IDBCursor continuePrimaryKey() on object store cursor

      assert_throws_dom: continuePrimaryKey() should throw if source is not an index function "function() {
              cursor.continuePrimaryKey(2, 2);
            }" threw object "TypeError: cursor.continuePrimaryKey is not a function" that is not a DOMException InvalidAccessError: property "code" is equal to undefined, expected 15
      

  • OK /IndexedDB/idbcursor-continuePrimaryKey-exceptions.any.worker.html (#39277)
    • FAIL [expected PASS] subtest: IDBCursor continuePrimaryKey() on object store cursor

      assert_throws_dom: continuePrimaryKey() should throw if source is not an index function "function() {
              cursor.continuePrimaryKey(2, 2);
            }" threw object "TypeError: cursor.continuePrimaryKey is not a function" that is not a DOMException InvalidAccessError: property "code" is equal to undefined, expected 15
      

  • 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()
  • OK /IndexedDB/idbrequest-onupgradeneeded.any.html (#38895)
    • PASS [expected FAIL] subtest: transaction oncomplete ordering relative to open request onsuccess
  • OK /IndexedDB/idbrequest-onupgradeneeded.any.worker.html (#38971)
    • PASS [expected FAIL] subtest: transaction oncomplete ordering relative to open request onsuccess
  • OK /IndexedDB/key-conversion-exceptions.any.html (#39305)
    • FAIL [expected PASS] subtest: IDBCursor continue() method with throwing/invalid keys

      assert_throws_exactly: key conversion with throwing getter should rethrow function "() => {
            receiver[method](key);
          }" threw object "TypeError: receiver[method] is not a function" but we expected it to throw object "getter: throwing from getter"
      

    • FAIL [expected PASS] subtest: IDBCursor update() method with throwing/invalid keys

      assert_throws_exactly: throwing getter should rethrow during clone function "() => {
            cursor.update(value);
          }" threw object "TypeError: cursor.update is not a function" but we expected it to throw object "getter: throwing from getter"
      

  • OK /IndexedDB/key-conversion-exceptions.any.worker.html (#39284)
    • FAIL [expected PASS] subtest: IDBCursor continue() method with throwing/invalid keys

      assert_throws_exactly: key conversion with throwing getter should rethrow function "() => {
            receiver[method](key);
          }" threw object "TypeError: receiver[method] is not a function" but we expected it to throw object "getter: throwing from getter"
      

    • FAIL [expected PASS] subtest: IDBCursor update() method with throwing/invalid keys

      assert_throws_exactly: throwing getter should rethrow during clone function "() => {
            cursor.update(value);
          }" threw object "TypeError: cursor.update is not a function" but we expected it to throw object "getter: throwing from getter"
      

  • 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
      

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

      assert_false: expected false got true
      

  • TIMEOUT /content-security-policy/inheritance/location-reload.html (#38983)
    • PASS [expected FAIL] subtest: location.reload() of empty iframe.
  • OK /css/css-cascade/layer-cssom-order-reverse.html (#36094)
    • PASS [expected FAIL] subtest: Delete layer invalidates @font-face
  • OK /css/css-cascade/layer-font-face-override.html (#35935)
    • FAIL [expected PASS] subtest: @font-face override update with appended sheet 1

      assert_equals: expected "80px" but got "41.45px"
      

    • FAIL [expected PASS] subtest: @font-face override update with appended sheet 2

      assert_equals: expected "80px" but got "41.45px"
      

  • OK /css/css-fonts/generic-family-keywords-003.html (#38994)
    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted serif (drawing text in a canvas)

      assert_equals: quoted serif matches  @font-face rule expected 125 but got 40
      

    • 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 /css/css-fonts/variations/font-weight-matching.html (#38577)
    • FAIL [expected PASS] subtest: Test @font-face matching for weight 250

      assert_approx_equals: @font-face should be mapped to CSSTest Weights 800. expected 90 +/- 2 but got 180
      

    • FAIL [expected PASS] subtest: Test @font-face matching for weight 399

      assert_approx_equals: @font-face should be mapped to CSSTest Weights 800. expected 90 +/- 2 but got 180
      

    • FAIL [expected PASS] subtest: Test @font-face matching for weight 470

      assert_approx_equals: @font-face should be mapped to CSSTest Weights 300. expected 90 +/- 2 but got 180
      

    • FAIL [expected PASS] subtest: Test @font-face matching for weight 500

      assert_approx_equals: @font-face should be mapped to CSSTest Weights 300. expected 90 +/- 2 but got 180
      

  • 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-user

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

  • OK /fetch/metadata/generated/css-font-face.sub.tentative.html (#34624)
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination
  • ERROR /fetch/metadata/generated/serviceworker.https.sub.html (#36247)
    • PASS [expected FAIL] subtest: sec-fetch-site - Same origin, no options - registration
  • OK /html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-window-open.html (#28691)
    • PASS [expected FAIL] subtest: load event does not fire on window.open('about:blank')
  • 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 <div autofocus=""></div> but got Element node <body><div autofocus=""></div></body>
      

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

      Test timed out
      

  • 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
      

  • TIMEOUT /html/semantics/embedded-content/media-elements/autoplay-allowed-by-feature-policy.https.sub.html (#41404)
    • PASS [expected TIMEOUT] subtest: Feature-Policy header: autoplay * allows same-origin iframes.
  • TIMEOUT /html/semantics/embedded-content/media-elements/autoplay-default-feature-policy.https.sub.html (#41193)
    • PASS [expected TIMEOUT] subtest: Default "autoplay" feature policy ["self"] allows same-origin iframes.
  • TIMEOUT /html/semantics/embedded-content/media-elements/autoplay-disabled-by-feature-policy.https.sub.html (#41221)
    • FAIL [expected TIMEOUT] subtest: Feature-Policy header: autoplay "none" disallows same-origin iframes.

      assert_false: autoplay expected false got true
      

  • OK [expected TIMEOUT] /html/semantics/embedded-content/media-elements/src_object_blob.html (#40340)
    • PASS [expected TIMEOUT] subtest: HTMLMediaElement.srcObject blob
  • ERROR [expected TIMEOUT] /html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener_base.html (#40347)
  • FAIL [expected PASS] /png/apng/acTL-plays-one.html (#41218)
  • OK /preload/prefetch-document.html (#37210)
    • PASS [expected FAIL] subtest: different-site document prefetch with 'as=document' should not be consumed
  • TIMEOUT /resource-timing/test_resource_timing.html (#25720)
    • FAIL [expected PASS] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (img)

      assert_equals: expected 10.549999999999999 but got 10.55
      

  • ERROR [expected OK] /webxr/render_state_update.https.html (#27535)
Stable unexpected results (14)
  • TIMEOUT [expected OK] /IndexedDB/back-forward-cache-open-connection.window.html
    • TIMEOUT [expected PASS] subtest: Testing BFCache support for page with open IndexedDB connection, and eviction behavior when receiving versionchange event.

      Test timed out
      

  • OK /IndexedDB/get-databases.any.html
    • PASS [expected FAIL] subtest: Enumerate multiple databases.
  • OK /IndexedDB/get-databases.any.worker.html
    • PASS [expected FAIL] subtest: Enumerate multiple databases.
  • TIMEOUT [expected OK] /IndexedDB/idbdatabase_close.any.html
    • TIMEOUT [expected FAIL] subtest: Unblock the version change transaction created by an open database request

      Test timed out
      

  • TIMEOUT [expected OK] /IndexedDB/idbdatabase_close.any.worker.html
    • TIMEOUT [expected FAIL] subtest: Unblock the version change transaction created by an open database request

      Test timed out
      

  • OK /IndexedDB/idbfactory_deleteDatabase.any.html
    • PASS [expected FAIL] subtest: deleteDatabase() request should have no source, and deleting a non-existent database should succeed with oldVersion of 0.
    • PASS [expected FAIL] subtest: The deleteDatabase() request's success event is an IDBVersionChangeEvent.
  • OK /IndexedDB/idbfactory_deleteDatabase.any.worker.html
    • PASS [expected FAIL] subtest: deleteDatabase() request should have no source, and deleting a non-existent database should succeed with oldVersion of 0.
    • PASS [expected FAIL] subtest: The deleteDatabase() request's success event is an IDBVersionChangeEvent.
  • TIMEOUT [expected OK] /IndexedDB/idbfactory_open.any.html
    • TIMEOUT [expected PASS] subtest: Calling open() with version argument 9007199254740991 should not throw.

      Test timed out
      

    • TIMEOUT [expected PASS] subtest: Calling open() with version argument undefined should not throw.

      Test timed out
      

  • TIMEOUT [expected OK] /IndexedDB/idbfactory_open.any.worker.html
    • TIMEOUT [expected PASS] subtest: Calling open() with version argument 9007199254740991 should not throw.

      Test timed out
      

    • TIMEOUT [expected PASS] subtest: Calling open() with version argument undefined should not throw.

      Test timed out
      

  • TIMEOUT [expected OK] /IndexedDB/name-scopes.any.html
    • TIMEOUT [expected FAIL] subtest: Unique index keys

      Test timed out
      

  • TIMEOUT [expected OK] /IndexedDB/name-scopes.any.worker.html
    • TIMEOUT [expected FAIL] subtest: Unique index keys

      Test timed out
      

  • TIMEOUT /IndexedDB/transaction-lifetime.any.html
    • PASS [expected TIMEOUT] subtest: No Blocked event
    • TIMEOUT [expected NOTRUN] subtest: Blocked event

      Test timed out
      

  • TIMEOUT /IndexedDB/transaction-lifetime.any.worker.html
    • PASS [expected TIMEOUT] subtest: No Blocked event
    • TIMEOUT [expected NOTRUN] subtest: Blocked event

      Test timed out
      

  • TIMEOUT [expected OK] /IndexedDB/worker-termination-aborts-upgrade.window.html
    • TIMEOUT [expected PASS] subtest: Worker Termination Aborts a Pending Upgrade

      Test timed out
      

@github-actions
Copy link
Copy Markdown

⚠️ Try run (#21344975552) failed!

@gterzian gterzian force-pushed the indexeddb_track_connections_2 branch 4 times, most recently from 9de5f92 to 3e77d3f Compare January 26, 2026 15:06
@gterzian gterzian added the T-linux-wpt Do a try run of the WPT label Jan 26, 2026
@github-actions github-actions bot removed the T-linux-wpt Do a try run of the WPT label Jan 26, 2026
@github-actions
Copy link
Copy Markdown

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

@gterzian gterzian force-pushed the indexeddb_track_connections_2 branch from 3e77d3f to 4afb239 Compare January 26, 2026 15:17
@gterzian gterzian added the T-linux-wpt Do a try run of the WPT label Jan 26, 2026
@github-actions
Copy link
Copy Markdown

⚠️ Try run (#21362608695) cancelled.

@github-actions github-actions bot removed the T-linux-wpt Do a try run of the WPT label Jan 26, 2026
@github-actions
Copy link
Copy Markdown

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

@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Jan 27, 2026
@gterzian gterzian force-pushed the indexeddb_track_connections_2 branch from 328656c to 9637be4 Compare January 27, 2026 15:23
@gterzian gterzian force-pushed the indexeddb_track_connections_2 branch from 9637be4 to 406f207 Compare January 27, 2026 15:34
Signed-off-by: gterzian <[email protected]>
@gterzian gterzian force-pushed the indexeddb_track_connections_2 branch from 406f207 to 054a907 Compare January 27, 2026 15:42
Signed-off-by: gterzian <[email protected]>
Copy link
Copy Markdown
Member

@Taym95 Taym95 left a comment

Choose a reason for hiding this comment

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

small remark: maybe we should not add a connection id to pending_ unless we have a proven path that will later remove, If delivery fails, we resolve immediately by removing the connection or aborting the request.

// named versionchange at entry with db’s version and version.
if conn
.sender
.send(ConnectionMsg::VersionChange {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

will backend stall forever if it can’t deliver VersionChange to script ?

Copy link
Copy Markdown
Member Author

@gterzian gterzian Jan 29, 2026

Choose a reason for hiding this comment

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

Yes that's a good point, but instead of handling it here I think we should have a separate workflow for aborting/closing everything when a global unloads; I've added an item to #40983

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Right, I think I asked about this some time ago already.

Essentially, once we detect that the channel (in the multi-process case) is no longer sendable, everything needs to be torn down, and doing that correctly is usually non-trivial. So I agree this is best handled separately, once the main architecture is in place.

/// We store the open request, which contains the connection.
/// TODO: remove when we are sure they are not needed anymore.
connections:
DomRefCell<HashMapTracedValues<DBName, HashMapTracedValues<Uuid, Dom<IDBOpenDBRequest>>>>,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

question: why not keep a registry keyed by (db_name, connection_id) that stores the IDBDatabase not the request, HashMap<(DBName, Uuid), Dom<IDBDatabase>> and use it for VersionChange delivery then let requests be removable after success/error without risking upgrade coordination ?

Copy link
Copy Markdown
Member Author

@gterzian gterzian Jan 29, 2026

Choose a reason for hiding this comment

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

We could have some sort of struct with requests and connections; now we just use the request for that which does mean it is kept alive for longer because at some point we only need the connection. Not completely sure what it should look like however. Something to look into later? (deleting a db still needs to be integrated with the various events firing also)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Right, no need to keep requests alive for so long, but for now, it's ok I think (at the moment, the architecture is more important than cleanup/optimizations, worth a TODO though).

@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Jan 28, 2026
@janvarga
Copy link
Copy Markdown
Member

Let me take a quick look ...

Copy link
Copy Markdown
Member

@janvarga janvarga left a comment

Choose a reason for hiding this comment

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

Only NITs so far, but still looking at components/storage/indexeddb/mod.rs ...


#[no_trace]
#[ignore_malloc_size_of = "Uuid"]
id: Uuid,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

NIT, a bit heavy weight as on the open request IIRC


if receiver.recv().is_err() {
warn!("Database close failed in idb thread");
};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes, this used to be synchronous, but I don’t think that’s what the spec intends, nor how other implementations behave.

NIT: The comments above probably need to be adjusted/removed (Step 3 can be removed I think)

BTW, The forced flag, that's more about the case when the backed would ask clients to do a forced close, for example when backed wants to clear data for entire origin.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed the docs.

/// We store the open request, which contains the connection.
/// TODO: remove when we are sure they are not needed anymore.
connections:
DomRefCell<HashMapTracedValues<DBName, HashMapTracedValues<Uuid, Dom<IDBOpenDBRequest>>>>,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Right, no need to keep requests alive for so long, but for now, it's ok I think (at the moment, the architecture is more important than cleanup/optimizations, worth a TODO though).

/// Messaging used in the context of connection lifecycle management.
pub enum ConnectionMsg {
/// Error if a DB is opened for a version
/// lower than the current db version.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

NIT: Fits one line (here and below)

Copy link
Copy Markdown
Member

@janvarga janvarga left a comment

Choose a reason for hiding this comment

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

I didn’t find anything major. Overall this looks pretty good, and it seems you understood the spec very well. Thanks!

pending_upgrade: Option<VersionUpgrade>,

/// This request is pending on these connections to close.
pending_close: HashSet<Uuid>,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is more about future development, but in theory, instead of storing IDs everywhere, we could keep references to the connections themselves (e.g. Rc or similar).

Is this something we’re intentionally avoiding, or could it be a direction we consider later on?
(I use such refs in some of my WIP PRs)

Copy link
Copy Markdown
Member Author

@gterzian gterzian Jan 30, 2026

Choose a reason for hiding this comment

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

Yes we could use references; I've had a quick look and the algo just use the id so far, for example by sending a message with the id, so keeping a ref to the connection would only result in using the connection to get the id.

version: u64,

/// <https://w3c.github.io/IndexedDB/#connection-close-pending-flag>
close_pending: bool,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

NIT: Now when I see it here, I think the flag on IDBDatabase could be renamed from closing to close_pending.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

yes defenitely, but it's pre-existing this PR; I've added an item to #40983


/// <https://w3c.github.io/IndexedDB/#connection>
connections: HashMap<IndexedDBDescription, HashSet<Connection>>,
connections: HashMap<IndexedDBDescription, HashMap<Uuid, Connection>>,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ok, so this fixes the issue from previous PR.

}
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yeah, this part is quite complicated and it looks it's correctly implemented.

// named versionchange at entry with db’s version and version.
if conn
.sender
.send(ConnectionMsg::VersionChange {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Right, I think I asked about this some time ago already.

Essentially, once we detect that the channel (in the multi-process case) is no longer sendable, everything needs to be torn down, and doing that correctly is usually non-trivial. So I agree this is best handled separately, once the main architecture is in place.

// Step 2: If the forced flag is true,
// then for each transaction created using connection
// run abort a transaction with transaction and newly created "AbortError" DOMException.
// Step 3: Wait for all transactions created using connection to complete.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Right, waiting for all transactions to finish is next major thing which needs to be done (before continuing with requested upgrade).

let global = self.global();
let this = Trusted::new(self);
global.task_manager().database_access_task_source().queue(
task!(send_versionchange_notification: move || {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Wasn't this being done correctly previously, by being a task instead of synchronous?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

In the current code the method is called from a task already.

}

/// <https://w3c.github.io/IndexedDB/#closing-connection>
fn close_database(&mut self, origin: ImmutableOrigin, id: Uuid, name: String) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think it should be possible to mark the connection's close_pending flag for step 1 right? IIRC it's in the connection struct

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes I actually realize the flag is not used here, what I'm doing now is just removing the connection from the map. Maybe we should remove the flag from the connection struct, but then again perhaps I am removing the connection from the map too soon and this will become obvious as part of transaction lifecycle. cc @Taym95

I've added an item to #40983

global: &GlobalScope,
name: String,
version: u64,
upgraded: bool,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Since you're passing in false for upgraded in all cases but one, does it really make sense to keep it here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We need a way to pass either false or true in the final connection message handling.

Signed-off-by: gterzian <[email protected]>
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Jan 30, 2026
Copy link
Copy Markdown
Member Author

@gterzian gterzian left a comment

Choose a reason for hiding this comment

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

Thank you all for the reviews, I think I have addressed all items.

let global = self.global();
let this = Trusted::new(self);
global.task_manager().database_access_task_source().queue(
task!(send_versionchange_notification: move || {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

In the current code the method is called from a task already.


if receiver.recv().is_err() {
warn!("Database close failed in idb thread");
};
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed the docs.

global: &GlobalScope,
name: String,
version: u64,
upgraded: bool,
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We need a way to pass either false or true in the final connection message handling.

pending_upgrade: Option<VersionUpgrade>,

/// This request is pending on these connections to close.
pending_close: HashSet<Uuid>,
Copy link
Copy Markdown
Member Author

@gterzian gterzian Jan 30, 2026

Choose a reason for hiding this comment

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

Yes we could use references; I've had a quick look and the algo just use the id so far, for example by sending a message with the id, so keeping a ref to the connection would only result in using the connection to get the id.

version: u64,

/// <https://w3c.github.io/IndexedDB/#connection-close-pending-flag>
close_pending: bool,
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

yes defenitely, but it's pre-existing this PR; I've added an item to #40983

}

/// <https://w3c.github.io/IndexedDB/#closing-connection>
fn close_database(&mut self, origin: ImmutableOrigin, id: Uuid, name: String) {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes I actually realize the flag is not used here, what I'm doing now is just removing the connection from the map. Maybe we should remove the flag from the connection struct, but then again perhaps I am removing the connection from the map too soon and this will become obvious as part of transaction lifecycle. cc @Taym95

I've added an item to #40983

@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Jan 30, 2026
@gterzian gterzian added this pull request to the merge queue Feb 2, 2026
@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 Feb 2, 2026
Merged via the queue into servo:main with commit 91ee834 Feb 2, 2026
29 checks passed
@gterzian gterzian deleted the indexeddb_track_connections_2 branch February 2, 2026 06:58
@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 Feb 2, 2026
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.

5 participants