8000 canvas: Move font selection and text shaping to `script` by mrobinson · Pull Request #38979 · servo/servo · GitHub
[go: up one dir, main page]

Skip to content

Conversation

mrobinson
Copy link
Member

Instead of doing font selection and text shaping in canvas, move this
to script. This allows canvas to use the shared Document
FontContext, which has access to web fonts. In addition, ensure that
there is a font style accessible for OffscreenCanvas in workers.

Testing: This causes a number of WPT tests to start to pass as web fonts are
supported on canvas again. In addition, some start to fail as they expose other
issues:

  • The lack of support for the Context2D.fontStretch property
  • Issues with zerosize gradient interpolation.
  • Differences between quoted and unquoted font family names. This seems like
    a timing issue with the way we are handling web fonts. The test seems to be
    expecting Local fonts to be available immediately (without waiting for them
    to load). This isn't how Servo works ATM. Seems like an issue with the test.

@mrobinson mrobinson added the T-linux-wpt Do a try run of the WPT label Aug 27, 2025
@github-actions github-actions bot removed the T-linux-wpt Do a try run of the WPT label Aug 27, 2025
Copy link

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

Copy link

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

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

  • CRASH [expected TIMEOUT] /IndexedDB/crashtests/create-index.any.html
  • 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 /css/css-cascade/layer-font-face-override.html (#35935)
    • PASS [expected FAIL] subtest: @font-face override update with appended sheet 1
    • PASS [expected FAIL] subtest: @font-face override update with appended sheet 2
  • FAIL [expected PASS] /css/css-color/opacity-overlapping-letters.html
  • PASS [expected FAIL] /css/css-grid/grid-items/grid-auto-margin-and-replaced-item-001.html (#37162)
  • OK /html/browsers/browsing-the-web/navigating-across-documents/009.html (#24456)
    • PASS [expected FAIL] subtest: Link with onclick form submit to javascript url with document.write and href navigation
  • OK /html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-window-open.html (#28691)
    • FAIL [expected PASS] subtest: load event does not fire on window.open('about:blank')

      assert_unreached: load should not be fired Reached unreachable code
      

  • CRASH [expected OK] /html/browsers/the-window-object/open-close/open-features-non-integer-left.html
  • TIMEOUT [expected OK] /html/infrastructure/urls/base-url/document-base-url-window-initiator-is-not-opener.https.window.html (#30970)
  • TIMEOUT [expected OK] /html/interaction/focus/the-autofocus-attribute/autofocus-dialog.html (#29087)
    • TIMEOUT [expected FAIL] subtest: <dialog>-contained autofocus element gets focused when the dialog is shown

      Test timed out
      

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

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

  • OK [expected CRASH] /html/semantics/embedded-content/the-iframe-element/if 8000 rame_sandbox_popups_escaping-2.html (#22667)
    • FAIL [expected TIMEOUT] subtest: Check that popups from a sandboxed iframe escape the sandbox if allow-popups-to-escape-sandbox is used

      assert_equals: It came from a sandboxed iframe expected "null" but got "http://web-platform.test:8000"
      

  • CRASH [expected OK] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html (#24066)
  • OK /html/semantics/forms/form-submission-0/urlencoded2.window.html (#28687)
    • PASS [expected FAIL] subtest: application/x-www-form-urlencoded: Basic test (normal form)
  • OK /intersection-observer/callback-cross-realm-report-exception.html (#38829)
    • FAIL [expected PASS] subtest: IntersectionObserver reports the exception from its callback in the callback's global object

      assert_array_equals: lengths differ, expected array ["frame1"] length 1, got [] length 0
      

  • TIMEOUT /preload/preload-resource-match.https.html (#38088)
    • TIMEOUT [expected FAIL] subtest: Loading script (use-credentials) with link (no-cors) should discard the preloaded response

      Test timed out
      

    • NOTRUN [expected TIMEOUT] subtest: Loading script (use-credentials) with link (anonymous) should discard the preloaded response
  • FAIL [expected PASS] /shadow-dom/shadow-style-invalidation-vw-units.html (#38468)
  • OK [expected TIMEOUT] /webmessaging/without-ports/018.html (#24485)
    • PASS [expected TIMEOUT] subtest: origin of the script that invoked the method, javascript:
Stable unexpected results that are known to be intermittent (24)
  • FAIL [expected PASS] /_mozilla/css/stacked_layers.html (#15988)
  • FAIL [expected PASS] /_mozilla/gfx-rs-gecko/descriptor-ranges.html (#23258)
  • 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 /_webgl/conformance/textures/misc/texture-upload-size.html (#21770)
    • FAIL [expected PASS] subtest: WebGL test #45

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

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

      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 #49

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

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

      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 #85
    • PASS [expected FAIL] subtest: WebGL test #87
    • PASS [expected FAIL] subtest: WebGL test #89
    • PASS [expected FAIL] subtest: WebGL test #91
  • OK /css/compositing/canvas-composite-modes.html (#37283)
    • PASS [expected FAIL] subtest: globalCompositeOperation source-out
    • PASS [expected FAIL] subtest: globalCompositeOperation destination-out
    • FAIL [expected PASS] subtest: globalCompositeOperation source-atop

      assert_approx_equals: green: g=47 a=47 source-atop g=47 a=47 expected 46.11702127659574 +/- 6.212765957446809 but got 53
      

    • FAIL [expected PASS] subtest: globalCompositeOperation destination-atop

      assert_approx_equals: green: g=47 a=47 destination-atop g=47 a=47 expected 46.11702127659574 +/- 6.212765957446809 but got 53
      

  • PASS [expected FAIL] /css/css-fonts/downloadable-font-scoped-to-document.html (#38691)
  • TIMEOUT [expected FAIL] /dom/xslt/large-cdata.html (#38029)
  • OK /fetch/metadata/generated/css-font-face.https.sub.tentative.html (#32732)
    • PASS [expected FAIL] subtest: sec-fetch-user
  • OK /html/browsers/browsing-the-web/navigating-across-documents/008.html (#24456)
    • PASS [expected FAIL] subtest: Link with onclick form submit to javascript url and href navigation
  • OK /html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-navigate-immediately.html (#29048)
    • FAIL [expected PASS] subtest: Navigating to a different document with location.href

      assert_equals: expected "http://web-platform.test:8000/common/blank.html?1" but got "about:blank"
      

    • PASS [expected FAIL] subtest: Navigating to a different document with link click
    • FAIL [expected PASS] subtest: Navigating to a different document with form submission

      assert_equals: expected "http://web-platform.test:8000/common/blank.html?1=" but got "about:blank"
      

  • OK /html/browsers/browsing-the-web/navigating-across-documents/refresh/same-document-refresh.html (#34597)
    • PASS [expected FAIL] subtest: Same-Document Referrer from Refresh
  • OK /html/browsers/windows/embedded-opener-remove-frame.html (#23867)
    • PASS [expected FAIL] subtest: opener of discarded auxiliary browsing context
  • PASS [expected FAIL] /html/canvas/element/manual/drawing-text-to-the-canvas/canvas.2d.disconnected-font-size-math.html (#30063)
  • OK [expected TIMEOUT] /html/interaction/focus/the-autofocus-attribute/document-with-fragment-empty.html (#28259)
    • FAIL [expected TIMEOUT] subtest: Autofocus elements in top-level browsing context's documents with empty fragments should work.

      assert_not_equals: got disallowed value Element node <body></body>
      

  • TIMEOUT /html/interaction/focus/the-autofocus-attribute/supported-elements.html (#24145)
    • TIMEOUT [expected FAIL] subtest: Element with tabindex should support autofocus

      Test timed out
      

    • NOTRUN [expected PASS] subtest: Non-HTMLElement should not support autofocus
  • OK [expected TIMEOUT] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-1.html (#22647)
    • FAIL [expected TIMEOUT] subtest: Check that popups from a sandboxed iframe escape the sandbox if allow-popups-to-escape-sandbox is used

      assert_equals: It came from a sandboxed iframe expected "null" but got "http://web-platform.test:8000"
      

  • OK [expected TIMEOUT] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-3.html (#24066)
  • OK [expected CRASH] /html/semantics/forms/the-fieldset-element/disabled-003.html (#31730)
  • OK /html/semantics/scripting-1/the-script-element/execution-timing/077.html (#22139)
    • PASS [expected FAIL] subtest: adding several types of scripts through the DOM and removing some of them confuses scheduler
  • OK /preload/preload-error.sub.html (#37177)
    • FAIL [expected PASS] subtest: 404 (style): main

      assert_greater_than: http://web-platform.test:8000/preload/resources/dummy.css?pipe=status%28404%29&label=style should be loaded expected a number greater than 0 but got 0
      

    • PASS [expected FAIL] subtest: CORS (style): main
    • FAIL [expected PASS] subtest: success (script): main

      assert_greater_than: http://web-platform.test:8000/preload/resources/dummy.js?label=script should be loaded expected a number greater than 0 but got 0
      

    • PASS [expected FAIL] subtest: 404 (script): main
    • FAIL [expected PASS] subtest: CORS (script): main

      assert_greater_than: http://not-web-platform.test:8000/preload/resources/dummy.js?pipe=header%28Access-Control-Allow-Origin%2C*%29&label=script should be loaded expected a number greater than 0 but got 0
      

    • FAIL [expected PASS] subtest: 404 (xhr): main

      assert_greater_than: http://web-platform.test:8000/preload/resources/dummy.xml?pipe=status%28404%29&label=xhr should be loaded expected a number greater than 0 but got 0
      

    • FAIL [expected PASS] subtest: CORS (xhr): main

      assert_greater_than: http://not-web-platform.test:8000/preload/resources/dummy.xml?pipe=header%28Access-Control-Allow-Origin%2C*%29&label=xhr should be loaded expected a number greater than 0 but got 0
      

    • PASS [expected FAIL] subtest: Decode-error (style): main
    • PASS [expected FAIL] subtest: Decode-error (script): main
  • TIMEOUT /resource-timing/test_resource_timing.html (#25720)
    • PASS [expected FAIL] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (img)
  • TIMEOUT [expected CRASH] /trusted-types/trusted-types-navigation.html?06-10 (#37920)
    • TIMEOUT [expected FAIL] subtest: Navigate a frame via anchor with javascript:-urls w/ default policy in report-only mode.

      Test timed out
      

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

      promise_test: Unhandled rejection with value: "Unexpected message received: {\"type\":\"DOMContentLoaded\",\"uri\":\"http://web-platform.test:8000/trusted-types/support/navigation-support.html?form-submission=1&defaultpolicy=replace&frame=1&navigationattempted=1&continue=1\"}"
      

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

Stable unexpected results (3)
  • OK /css/css-fonts/generic-family-keywords-003.html
    • 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
      

    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted sans-serif (drawing text in a canvas)

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

    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted cursive (drawing text in a canvas)

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

    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted fantasy (drawing text in a canvas)

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

    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted monospace (drawing text in a canvas)

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

    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted system-ui (drawing text in a canvas)

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

    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted generic(fangsong) (drawing text in a canvas)

      assert_equals: quoted generic(fangsong) matches  @font-face rule expected 125 but got 40
      

    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted generic(kai) (drawing text in a canvas)

      assert_equals: quoted generic(kai) matches  @font-face rule expected 125 but got 40
      

    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted generic(khmer-mul) (drawing text in a canvas)

      8000
      assert_equals: quoted generic(khmer-mul) matches  @font-face rule expected 125 but got 40
      

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

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

  • OK /html/canvas/element/text/2d.text.measure.width.basic.html
    • PASS [expected FAIL] subtest: The width of character is same as font used
  • OK /html/canvas/offscreen/text/2d.text.measure.width.nullCharacter.html
    • FAIL [expected PASS] subtest: Null character does not take up space

      assert_equals: ctx.measureText('\u0000').width === 0 (got 10[number], expected 0[number]) expected 0 but got 10
      

Copy link

⚠️ Try run (#17276595993) failed.

Copy link
Contributor
@nicoburns nicoburns left a comment

Choose a reason for hiding this comment

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

This makes a lot of sense, and seems like it makes the code quite a bit simpler :)

@sagudev
Copy link
Member 8000
sagudev commented Aug 28, 2025

For a follow up we should extraact text_preparation_algorithm method, that would be used by fill_text, measure_text and stroke_text to better match the spec and avoid duplication.

Instead of doing font selection and text shaping in `canvas`, move this
to `script`. This allows canvas to use the shared `Document`
`FontContext`, which has access to web fonts. In addition, ensure that
there is a font style accessible for `OffscreenCanvas` in workers.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
@mrobinson mrobinson force-pushed the font-context-canvas branch from 1a8879b to 1706189 Compare August 28, 2025 06:48
@mrobinson mrobinson enabled auto-merge August 28, 2025 06:48
@mrobinson mrobinson added this pull request to the merge queue Aug 28, 2025
github-merge-queue bot pushed a commit that referenced this pull request Aug 28, 2025
Instead of doing font selection and text shaping in `canvas`, move this
to `script`. This allows canvas to use the shared `Document`
`FontContext`, which has access to web fonts. In addition, ensure that
there is a font style accessible for `OffscreenCanvas` in workers.

Testing: This causes a number of WPT tests to start to pass as web fonts
are
supported on canvas again. In addition, some start to fail as they
expose other
issues:
 - The lack of support for the `Context2D.fontStretch` property
 - Issues with zerosize gradient interpolation.
- Differences between quoted and unquoted font family names. This seems
like
a timing issue with the way we are handling web fonts. The test seems to
be
expecting Local fonts to be available immediately (without waiting for
them
to load). This isn't how Servo works ATM. Seems like an issue with the
test.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Aug 28, 2025
@mrobinson mrobinson added this pull request to the merge queue Aug 28, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Aug 28, 2025
@mrobinson mrobinson added this pull request to the merge queue Aug 28, 2025
Merged via the queue into servo:main with commit cb64def Aug 28, 2025
22 checks passed
@mrobinson mrobinson deleted the font-context-canvas branch August 28, 2025 11:13
mrobinson added a commit that referenced this pull request Aug 28, 2025
…bgpu)

{"fail_fast": false, "matrix": [{"name": "Linux (Production, WPT)", "workflow": "linux", "wpt": true, "profile": "production", "unit_tests": false, "build_libservo": false, "bencher": false, "build_args": "", "wpt_args": "_webgpu", "number_of_wpt_chunks": 20}]}
github-merge-queue bot pushed a commit that referenced this pull request Aug 29, 2025
This was (presumably accidentally) added in #38979, cc @mrobinson

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
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.

3 participants
0