Skip to content

script: Let HTMLCanvasElement manage the ImageKey for canvases#40375

Merged
mukilan merged 4 commits intoservo:mainfrom
mrobinson:canvas-images
Nov 4, 2025
Merged

script: Let HTMLCanvasElement manage the ImageKey for canvases#40375
mukilan merged 4 commits intoservo:mainfrom
mrobinson:canvas-images

Conversation

@mrobinson
Copy link
Copy Markdown
Member

@mrobinson mrobinson commented Nov 3, 2025

This change makes it so that the HTMLCanvasElement is responsible for
managing the ImageKey for associated RenderingContexts. Only
canvases display their contents into WebRender directly, so this makes
it so that keys are not generated for OffscreenCanvas.

The main goal here is that ImageKeys are always associated with a
particular WebView, which isn't possible in the various canvas
backends yet. This is important because each WebView may soon have a
different WebRender instance entirely with its own set of ImageKeys.

This also allows for clearing ImageKeys when canvases are disconnected
from the DOM in a future change. One tricky thing here is placeholder
canvases, which are meant to be driven from workers.

It seems that the implementation isn't correct for these at the moment
as they need to be updated to the specification. Instead, what is
happening is that any existing context / image is completely lost when
converting to an OffscreenCanvas.

Testing: This shouldn't change observable behavior, so is covered by
existing tests.
Fixes: This is part of #40261.

@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Nov 3, 2025
@mrobinson mrobinson force-pushed the canvas-images branch 2 times, most recently from c0bb55a to 4277436 Compare November 3, 2025 17:49
@mrobinson mrobinson added the T-linux-wpt Do a try run of the WPT label Nov 3, 2025
@github-actions github-actions bot removed the T-linux-wpt Do a try run of the WPT label Nov 3, 2025
@github-actions
Copy link
Copy Markdown

github-actions bot commented Nov 3, 2025

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

@github-actions
Copy link
Copy Markdown

github-actions bot commented Nov 3, 2025

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

Flaky unexpected result (18)
  • OK /FileAPI/url/url-with-fetch.any.worker.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 occurred"
      

  • 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
      

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

      Test timed out
      

  • OK /_webgl/conformance/rendering/texture-switch-performance.html (#23384)
    • FAIL [expected PASS] subtest: WebGL test #0

      assert_true: Texture switching significantly hurt performance - achieved 120 frames in 2.013 seconds (0.67 times baseline performance) expected true got false
      

  • TIMEOUT [expected OK] /credential-management/credentialscontainer-frame-basics.https.html (#39430)
    • TIMEOUT [expected FAIL] subtest: navigator.credentials should be undefined in documents generated from data: URLs.

      Test timed out
      

  • OK /css/css-animations/event-order.tentative.html (#39000)
    • PASS [expected FAIL] subtest: Same events on pseudo-elements follow the prescribed order
  • 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-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
      

  • OK /css/css-text/i18n/unknown-lang/css-text-line-break-po-normal.html
    • FAIL [expected PASS] subtest: FE6A SMALL PERCENT SIGN may NOT appear at line start if normal

      assert_approx_equals: expected 80 +/- 1 but got 87
      

  • 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-site destination
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination
  • OK /html/browsers/browsing-the-web/navigating-across-documents/008.html (#24456)
    • FAIL [expected PASS] subtest: Link with onclick form submit to javascript url 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)
    • 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.window.html (#29049)
    • PASS [expected FAIL] subtest: Same-origin navigation started from unload handler must be ignored
  • 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
  • TIMEOUT [expected PASS] /html/browsers/origin/origin-keyed-agent-clusters/popups-crash.https.html
  • OK /html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-nav-location-replace.html (#32604)
    • FAIL [expected PASS] subtest: Navigating iframe loading='lazy' before it is loaded: location.replace

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

  • OK [expected TIMEOUT] /webmessaging/with-ports/017.html (#24486)
    • PASS [expected TIMEOUT] subtest: origin of the script that invoked the method, about:blank
  • TIMEOUT [expected OK] /webstorage/localstorage-about-blank-3P-iframe-opens-3P-window.partitioned.html (#29053)
    • TIMEOUT [expected PASS] subtest: StorageKey: test 3P about:blank window opened from a 3P iframe

      Test timed out
      

Stable unexpected results that are known to be intermittent (29)
  • 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/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"
      

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

  • FAIL [expected PASS] /_mozilla/mozilla/sslfail.html (#10760)
  • OK /_webgl/conformance2/rendering/texture-switch-performance.html (#23384)
    • FAIL [expected PASS] subtest: WebGL test #0

      assert_true: Texture switching significantly hurt performance - achieved 119 frames in 2.013 seconds (0.69 times baseline performance) expected true got false
      

  • 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 /custom-elements/form-associated/ElementInternals-setFormValue.html (#29174)
    • PASS [expected FAIL] subtest: Single value - empty name exists
  • OK /custom-elements/form-associated/form-disabled-callback.html (#38843)
    • PASS [expected FAIL] subtest: A disabled form-associated custom element should not submit an entry for it
  • OK /fetch/metadata/generated/css-font-face.https.sub.tentative.html (#32732)
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Cross-site
  • 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 /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/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
  • TIMEOUT [expected OK] /html/interaction/focus/the-autofocus-attribute/document-with-fragment-empty.html (#28259)
    • TIMEOUT [expected FAIL] subtest: Autofocus elements in top-level browsing context's documents with empty fragments should work.

      Test timed out
      

  • OK [expected TIMEOUT] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_other_frame_popup.sub.html (#39702)
    • FAIL [expected TIMEOUT] subtest: Sandboxed iframe can not navigate other frame's popup

      assert_equals: expected "cannot navigate" but got "can navigate"
      

  • OK /html/semantics/forms/form-submission-0/multipart-formdata.window.html (#28725)
    • PASS [expected FAIL] subtest: multipart/form-data: Basic test (normal form)
    • PASS [expected FAIL] subtest: multipart/form-data: 0x00 in name (formdata event)
  • OK /html/semantics/forms/form-submission-0/text-plain.window.html (#28687)
    • PASS [expected FAIL] subtest: text/plain: Basic test (normal form)
    • PASS [expected FAIL] subtest: text/plain: Basic File test (formdata event)
    • PASS [expected FAIL] subtest: text/plain: 0x00 in name (formdata event)
  • OK /html/semantics/forms/form-submission-0/urlencoded2.window.html (#28687)
    • PASS [expected FAIL] subtest: application/x-www-form-urlencoded: Basic test (normal form)
    • PASS [expected FAIL] subtest: application/x-www-form-urlencoded: Basic File test (formdata event)
    • PASS [expected FAIL] subtest: application/x-www-form-urlencoded: 0x00 in name (formdata event)
  • OK /html/semantics/forms/historical.html (#28568)
    • PASS [expected FAIL] subtest: <input name=isindex> should not be supported
  • OK [expected CRASH] /html/semantics/forms/the-fieldset-element/disabled-003.html (#31730, #39631)
  • TIMEOUT [expected OK] /html/semantics/interactive-elements/the-details-element/name-attribute.html (#40355)
  • OK /preload/preload-error.sub.html (#37177)
    • PASS [expected FAIL] subtest: success (fetch): main
    • PASS [expected FAIL] subtest: CORS (fetch): main
  • OK /service-workers/service-worker/fetch-event.https.html (#36234)
    • PASS [expected FAIL] subtest: Service Worker falls back to network in fetch event with POST form
  • OK /trusted-types/trusted-types-navigation.html?01-05 (#38975)
    • FAIL [expected PASS] subtest: Navigate a window via anchor with javascript:-urls in report-only mode.

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

  • OK /trusted-types/trusted-types-navigation.html?26-30 (#38807)
    • PASS [expected FAIL] subtest: Navigate a frame via form-submission with javascript:-urls in enforcing mode.
  • 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 OK] /websockets/unload-a-document/001.html?wss (#40072)
Stable unexpected results (3)
  • OK /_webgl/conformance/canvas/buffer-offscreen-test.html
    • FAIL [expected PASS] subtest: WebGL test #7

      assert_true: remainder of buffer should be un-cleared red
      at (0, 0) expected: 255,0,0,255 was 0,0,0,0 expected true got false
      

  • ERROR /_webgl/conformance/textures/webgl_canvas/tex-2d-luminance-luminance-unsigned_byte.html
    • PASS [expected FAIL] subtest: WebGL test #12
    • PASS [expected FAIL] subtest: WebGL test #18
    • PASS [expected FAIL] subtest: WebGL test #24
    • PASS [expected FAIL] subtest: WebGL test #42
    • PASS [expected FAIL] subtest: WebGL test #48
  • ERROR /_webgl/conformance/textures/webgl_canvas/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html
    • PASS [expected FAIL] subtest: WebGL test #12
    • PASS [expected FAIL] subtest: WebGL test #18
    • PASS [expected FAIL] subtest: WebGL test #24
    • PASS [expected FAIL] subtest: WebGL test #42
    • PASS [expected FAIL] subtest: WebGL test #48

@github-actions
Copy link
Copy Markdown

github-actions bot commented Nov 3, 2025

⚠️ Try run (#19044544341) failed.

let mut dirty_canvases = self.dirty_canvases.borrow_mut();
if dirty_canvases
.iter()
.any(|dirty_canvas| std::ptr::eq(&**dirty_canvas, canvas))
Copy link
Copy Markdown
Member

@mukilan mukilan Nov 4, 2025

Choose a reason for hiding this comment

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

If we import the DomObject trait, we can use the PartialEq of dom structs to do dirty_canvas.reflector() == canvas.reflector().

Or even better, since we create Dom::ref(canvas) in the last line of the method, we can move it up before this line and just do *dirty_canvas == canvas. Never mind, we can't have the Dom on the stack.

@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Nov 4, 2025
Copy link
Copy Markdown
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.

Looks like there is a regression in /_webgl/conformance/canvas/buffer-offscreen-test.html. I'll look into it.

@mukilan
Copy link
Copy Markdown
Member

mukilan commented Nov 4, 2025

Looks like there is a regression in /_webgl/conformance/canvas/buffer-offscreen-test.html. I'll look into it.

The issue is that the failing case checks the behaviour of offscreen canvas i.e a canvas element not connected to the document tree.

Previously, the update_the_rendering in Document filtered out these offscreen canvases so that they never get presented to the screen, which would have triggered the clearing of the buffers as preserveDrawingBuffer is false.

The new logic doesn't do this filtering in Document, so it end up presenting and clearing the offscreen canvas, causing the test to fail.

I believe we just need add the following check to WebGLRenderingContext::update_rendering:

if !self.onscreen() {
    return false;
}

This matches what we have for CanvasRenderingContext2D as well.

@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Nov 4, 2025
@mukilan mukilan added the T-linux-wpt Do a try run of the WPT label Nov 4, 2025
@github-actions github-actions bot removed the T-linux-wpt Do a try run of the WPT label Nov 4, 2025
@github-actions
Copy link
Copy Markdown

github-actions bot commented Nov 4, 2025

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

@mukilan mukilan added the T-linux-wpt Do a try run of the WPT label Nov 4, 2025
@github-actions github-actions bot removed the T-linux-wpt Do a try run of the WPT label Nov 4, 2025
@github-actions
Copy link
Copy Markdown

github-actions bot commented Nov 4, 2025

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

@mukilan
Copy link
Copy Markdown
Member

mukilan commented Nov 4, 2025

I've triggered a new try run after rebasing as the previous one failed since CI now passes --log-raw-stable-unexpected to mach.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Nov 4, 2025

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

Flaky unexpected result (42)
  • 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
      

  • OK /IndexedDB/idbobjectstore_get.any.worker.html (#38852)
    • FAIL [expected PASS] subtest: Attempts to retrieve a record that doesn't exist

      assert_unreached: unexpected open.success event Reached unreachable code
      

  • OK /_mozilla/mozilla/FileAPI/file-upload.html (#40348)
    • FAIL [expected PASS] subtest: form submission of uploaded file

      assert_equals: expected "OK" but got "\n\n\n\n\n    var fileInput = document.getElementById(\"file-input\");\n    fileInput.addEventListener(\"change\", () => {\n      parent.postMessage(\"loadedAndFilesChanged\");\n    });\n    fileInput.selectFiles([\"./tests/wpt/mozilla/tests/mozilla/FileAPI/resource/upload.txt\"]);\n\n"
      

  • ERROR [expected OK] /_mozilla/mozilla/img_load_more_than_cache.html
  • ERROR [expected TIMEOUT] /_mozilla/webxr/sessionavailable.https.html
  • CRASH [expected OK] /_webgl/conformance/canvas/drawingbuffer-static-canvas-test.html
  • CRASH [expected OK] /_webgl/conformance/glsl/bugs/sampler-array-struct-function-arg.html
  • CRASH [expected OK] /_webgl/conformance/glsl/misc/shader-with-int-return-value.frag.html
  • CRASH [expected OK] /_webgl/conformance/rendering/draw-arrays-out-of-bounds.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 14 more unexpected results...
  • CRASH [expected OK] /_webgl/conformance2/glsl3/array-assign-constructor.html
  • CRASH [expected ERROR] /_webgl/conformance2/textures/image/tex-3d-rgb32f-rgb-float.html
  • OK /cookiestore/cookieStore_getAll_set_creation_url.https.any.html
    • FAIL [expected PASS] subtest: cookieStore.set and cookieStore.getAll use the creation url

      assert_equals: expected 1 but got 2
      

  • OK /css/css-cascade/layer-cssom-order-reverse.html (#36094)
    • FAIL [expected PASS] subtest: Delete layer invalidates @font-face

      assert_equals: expected "220px" but got "133px"
      

  • OK /css/css-fonts/generic-family-keywords-001.html (#37467)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(fangsong)
  • CRASH [expected OK] /css/css-tables/bounding-box-computation-1.html
  • OK /custom-elements/form-associated/ElementInternals-setFormValue.html (#29174)
    • PASS [expected FAIL] subtest: Single value - empty name exists
    • PASS [expected FAIL] subtest: Null value should submit nothing
  • OK /fetch/fetch-later/new-window.https.window.html (#32036)
    • FAIL [expected PASS] subtest: A same-origin window[target=''][features=''] can trigger fetchLater.

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

    • FAIL [expected PASS] subtest: A same-origin window[target=''][features='popup'] can trigger fetchLater.

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

    • FAIL [expected PASS] subtest: A same-origin window[target='_blank'][features=''] can trigger fetchLater.

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

    • FAIL [expected PASS] subtest: A same-origin window[target='_blank'][features='popup'] can trigger fetchLater.

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

  • OK /fetch/fetch-later/permissions-policy/deferred-fetch-allowed-by-permissions-policy.https.window.html
    • 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.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 cross-site destination
  • 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 /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/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/the-windowproxy-exotic-object/windowproxy-prototype-setting-cross-origin.sub.html
  • TIMEOUT [expected OK] /html/interaction/focus/the-autofocus-attribute/document-with-fragment-nonexistent.html (#28259)
    • TIMEOUT [expected FAIL] subtest: Autofocus elements in top-level browsing context's documents with non-existent fragments should work.

      Test timed out
      

  • TIMEOUT [expected OK] /html/interaction/focus/the-autofocus-attribute/document-with-fragment-top.html (#28259)
    • TIMEOUT [expected FAIL] subtest: Autofocus elements in top-level browsing context's documents with "top" fragments should work.

      Test timed out
      

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

    • TIMEOUT [expected NOTRUN] subtest: Non-HTMLElement should not support autofocus

      Test timed out
      

  • OK /html/semantics/embedded-content/media-elements/seeking/seek-to-currentTime.html (#38247)
    • FAIL [expected PASS] subtest: seek to currentTime

      assert_greater_than: seekable ranges expected a number greater than 0 but got 0
      

  • TIMEOUT [expected OK] /html/semantics/embedded-content/media-elements/src_object_blob.html (#40340)
    • TIMEOUT [expected PASS] subtest: HTMLMediaElement.srcObject blob

      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.
  • CRASH [expected TIMEOUT] /html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener.html
  • TIMEOUT [expected ERROR] /html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener_base.html (#40347)
  • 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
  • PASS [expected FAIL] /png/apng/acTL-plays-one.html
  • TIMEOUT /resource-timing/test_resource_timing.https.html (#25216)
    • PASS [expected FAIL] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (img)
  • CRASH [expected OK] /trusted-types/TrustedTypePolicy-CSP-no-name.html
  • OK /trusted-types/trusted-types-navigation.html?01-05 (#38975)
    • FAIL [expected PASS] subtest: Navigate a window via anchor with javascript:-urls in report-only mode.

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

  • CRASH [expected ERROR] /wasm/webapi/invalid-code.any.sharedworker.html
  • CRASH [expected TIMEOUT] /webaudio/the-audio-api/the-audiocontext-interface/promise-methods-after-discard.html
  • CRASH [expected OK] /webmessaging/message-channels/basics.any.worker.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
      

Stable unexpected results that are known to be intermittent (30)
  • 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/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"
      

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

  • 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
      

  • CRASH [expected PASS] /_mozilla/shadow-dom/move-element-with-ua-shadow-tree-crash.html (#39473)
  • 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 /custom-elements/form-associated/form-disabled-callback.html (#38843)
    • PASS [expected FAIL] subtest: A disabled form-associated custom element should not submit an entry for it
  • FAIL [expected TIMEOUT] /dom/xslt/large-cdata.html (#38029)
  • OK [expected TIMEOUT] /fetch/api/redirect/redirect-keepalive.https.any.html (#32153)
    • PASS [expected TIMEOUT] subtest: [keepalive][iframe][load] mixed content redirect; setting up
  • OK /fetch/metadata/generated/css-font-face.https.sub.tentative.html (#32732)
    • PASS [expected FAIL] subtest: sec-fetch-user
  • TIMEOUT [expected CRASH] /fetch/metadata/window-open.https.sub.html (#40339)
  • 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/008.html (#24456)
    • FAIL [expected PASS] subtest: Link with onclick form submit to javascript url and href navigation

      assert_equals: expected "href" but got "click"
      

  • OK /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment.html (#20768)
    • PASS [expected FAIL] subtest: Tests that a fragment navigation in the unload handler will not block the initial navigation
  • 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/semantics/forms/form-submission-0/multipart-formdata.window.html (#28725)
    • PASS [expected FAIL] subtest: multipart/form-data: Basic test (normal form)
    • PASS [expected FAIL] subtest: multipart/form-data: Basic test (formdata event)
  • OK /html/semantics/forms/form-submission-0/text-plain.window.html (#28687)
    • PASS [expected FAIL] subtest: text/plain: Basic test (normal form)
    • PASS [expected FAIL] subtest: text/plain: Basic test (formdata event)
    • PASS [expected FAIL] subtest: text/plain: 0x00 in name (normal form)
    • PASS [expected FAIL] subtest: text/plain: 0x00 in value (normal form)
  • OK /html/semantics/forms/form-submission-0/urlencoded2.window.html (#28687)
    • PASS [expected FAIL] subtest: application/x-www-form-urlencoded: Basic test (normal form)
    • PASS [expected FAIL] subtest: application/x-www-form-urlencoded: Basic test (formdata event)
  • OK /html/semantics/forms/historical.html (#28568)
    • PASS [expected FAIL] subtest: <input name=isindex> should not be supported
  • OK [expected CRASH] /html/semantics/forms/the-fieldset-element/disabled-003.html (#31730, #39631)
  • TIMEOUT [expected OK] /html/semantics/interactive-elements/the-details-element/name-attribute.html (#40355)
  • OK /preload/preload-error.sub.html (#37177)
    • PASS [expected FAIL] subtest: 404 (fetch): main
    • PASS [expected FAIL] subtest: CORS (fetch): main
  • ERROR [expected OK] /resource-timing/cors-preflight.any.html (#28694)
  • OK /trusted-types/trusted-types-navigation.html?21-25 (#38997)
    • PASS [expected FAIL] subtest: Navigate a window via form-submission with javascript:-urls in enforcing mode.
  • TIMEOUT [expected OK] /trusted-types/trusted-types-navigation.html?26-30 (#38807)
    • TIMEOUT [expected PASS] subtest: Navigate a frame via form-submission with javascript:-urls w/ default policy in enforcing mode.

      Test timed out
      

  • OK /trusted-types/trusted-types-navigation.html?31-35 (#38034)
    • PASS [expected FAIL] subtest: Navigate a frame via form-submission with javascript:-urls in report-only mode.
  • TIMEOUT [expected OK] /webstorage/localstorage-about-blank-3P-iframe-opens-3P-window.partitioned.html (#29053)
    • TIMEOUT [expected PASS] subtest: StorageKey: test 3P about:blank window opened from a 3P iframe

      Test timed out
      

@github-actions
Copy link
Copy Markdown

github-actions bot commented Nov 4, 2025

✨ Try run (#19061720001) succeeded.

mrobinson and others added 2 commits November 4, 2025 15:44
This change makes it so that the `HTMLCanvasElement` is responsible for
managing the `ImageKey` for assocaited `RenderingContext`s. Only
canvases display their contents into WebRender directly, so this makes
it so that keys are not generated for `OffscreenCanvas`.

The main goal here is that `ImageKey`s are always associated with a
particular `WebView`, which isn't possible in the various canvas
backends yet. This is important because each `Webview` may soon have a
different WebRender instance entirely with its own set of `ImageKey`s.

This also allows for clearing `ImageKey`s when canvases are disconnected
from the DOM in a future change. One tricky thing here is placeholder
canvases, which are meant to be driven from workers.

It seems that the implementation isn't correct for these at the moment
as they need to be updated to the specification. Instead, what is
happening is that any existing context / image is completely lost when
converting to an `OffscreenCanvas`.

Co-authored-by: Mukilan Thiyagarajan <[email protected]>
Signed-off-by: Martin Robinson <[email protected]>
Signed-off-by: Mukilan Thiyagarajan <[email protected]>
@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Nov 4, 2025
@mukilan mukilan added this pull request to the merge queue Nov 4, 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 4, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Nov 4, 2025
@servo-highfive servo-highfive added S-tests-failed The changes caused existing tests to fail. and removed S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. labels Nov 4, 2025
@servo-highfive servo-highfive added S-awaiting-review There is new code that needs to be reviewed. and removed S-tests-failed The changes caused existing tests to fail. labels Nov 4, 2025
@mukilan mukilan added this pull request to the merge queue Nov 4, 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 4, 2025
Merged via the queue into servo:main with commit 9fa6303 Nov 4, 2025
35 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 4, 2025
@mrobinson mrobinson deleted the canvas-images branch November 18, 2025 10:05
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.

4 participants