Skip to content

fonts: Synthesize variations from font-style for macos & freetype systems - #46918

Merged
simonwuelker merged 5 commits into
servo:mainfrom
simonwuelker:font-variations-from-font-style
Aug 19, 2026
Merged

simonwuelker merged 5 commits into
servo:mainfrom
simonwuelker:font-variations-from-font-style

Conversation

@simonwuelker

@simonwuelker simonwuelker commented Jul 31, 2026 •

Copy link
Copy Markdown
Member

Synthesizing variations from font-style requires a bit more work than for other properties because depending on which variation axes the font has we need to set either ital (to 0 or 1) or slnt (to a number of degrees). Currently we synthesize variations too early, when the font data is not yet avaiable. That's why this change moves the implementation of https://drafts.csswg.org/css-fonts-4/#apply-font-matching-variations to Font::new.

On macos and windows PlatformFont::copy_with_variations is not yet implemented, so variations are synthesized but they do nothing. I hope to fix this in future changes but its hard to approach since I don't have access to macos/windows machines.

Testing: New tests start to pass
Part of #38800

@simonwuelker
simonwuelker requested a review from nicoburns as a code owner July 31, 2026 16:20
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Jul 31, 2026
@simonwuelker
simonwuelker force-pushed the font-variations-from-font-style branch from c50f102 to 90a7008 Compare August 1, 2026 00:38
@simonwuelker

Copy link
Copy Markdown
Member Author

Getting this working on all platforms is the last real blocker to getting rid of the layout_variable_fonts_enabled pref.

@mrobinson

Copy link
Copy Markdown
Member

@simonwuelker I'm happy to take another look at this when you think it's ready!

@simonwuelker

Copy link
Copy Markdown
Member Author

@simonwuelker I'm happy to take another look at this when you think it's ready!

I'll likely need a couple days before I have time to implement the changes we talked about, will dm you once its ready (:

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
@simonwuelker
simonwuelker force-pushed the font-variations-from-font-style branch from 90a7008 to 246f1a8 Compare August 17, 2026 13:46
@simonwuelker simonwuelker added the T-linux-wpt Do a try run of the WPT label Aug 17, 2026
@github-actions github-actions Bot removed the T-linux-wpt Do a try run of the WPT label Aug 17, 2026
@simonwuelker
simonwuelker force-pushed the font-variations-from-font-style branch from 246f1a8 to 3ced6e2 Compare August 18, 2026 01:12
@simonwuelker simonwuelker added the T-linux-wpt Do a try run of the WPT label Aug 18, 2026
@github-actions github-actions Bot removed the T-linux-wpt Do a try run of the WPT label Aug 18, 2026
@github-actions

Copy link
Copy Markdown

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

@github-actions

Copy link
Copy Markdown

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

Flaky unexpected result (35)
  • TIMEOUT [expected OK] /_webgl/conformance/textures/misc/texture-video-transparent.html
    • NOTRUN [expected PASS] subtest: Overall test
  • CRASH [expected OK] /_webgl/conformance/uniforms/no-over-optimization-on-uniform-array-06.html
  • TIMEOUT [expected OK] /_webgl/conformance/uniforms/no-over-optimization-on-uniform-array-14.html
    • NOTRUN [expected PASS] subtest: Overall test
  • TIMEOUT [expected OK] /_webgl/conformance/uniforms/no-over-optimization-on-uniform-array-15.html
  • CRASH [expected OK] /_webgl/conformance/uniforms/no-over-optimization-on-uniform-array-16.html
  • TIMEOUT /content-security-policy/inheritance/auxiliary-blank-document.html (#44821)
    • TIMEOUT [expected PASS] subtest: For transient doc, violation event occurs in auxiliary, not opener window.

      Test timed out
      

    • NOTRUN [expected TIMEOUT] subtest: window.open() inherits CSPs on transient about:blank.
  • OK /cookies/partitioned-cookies/partitioned-cookies-samesite-attribute.https.html (#45255)
    • FAIL [expected PASS] subtest: In embedded cross-site contexts, partitioned cookies can only be set with explicit SameSite=None

      assert_equals: expected 0 but got 3
      

  • FAIL [expected PASS] /css/css-backgrounds/background-size-041.html
  • FAIL [expected PASS] /css/css-image-animation/image-animation-img-running-to-normal.html (#46567)
  • FAIL [expected PASS] /css/css-sizing/dynamic-available-size-iframe.html (#45763)
  • ERROR [expected TIMEOUT] /css/css-sizing/responsive-iframe/responsive-iframe-allow-origins.html?container=subdomain,origin=cross,allowed=wildcard,expect=400 (#47268)
  • FAIL [expected PASS] /css/filter-effects/kernel-unit-length-001.html
  • CRASH [expected OK] /dom/nodes/NodeList-static-length-getter-tampered-indexOf-3.html
  • TIMEOUT [expected OK] /fetch/content-encoding/zstd/big-zstd-body.https.any.worker.html
    • TIMEOUT [expected PASS] subtest: large zstd data should be decompressed successfully

      Test timed out
      

    • NOTRUN [expected FAIL] subtest: large zstd data should be decompressed successfully with byte stream
  • OK [expected TIMEOUT] /html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit.html (#44098)
    • FAIL [expected TIMEOUT] subtest: Replace before load, triggered by formElement.requestSubmit()

      assert_equals: expected "http://web-platform.test:8000/common/blank.html?thereplacement=" but got "http://web-platform.test:8000/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/code-injector.html?pipe=sub(none)&amp;code=%0A%20%20%20%20const%20form%20%3D%20document.createElement(%22form%22)%3B%0A%20%20%20%20form.action%20%3D%20%22%2Fcommon%2Fblank.html%22%3B%0A%0A%20%20%20%20const%20input%20%3D%20document.createElement(%22input%22)%3B%0A%20%20%20%20input.type%20%3D%20%22hidden%22%3B%0A%20%20%20%20input.name%20%3D%20%22thereplacement%22%3B%0A%20%20%20%20form.append(input)%3B%0A%0A%20%20%20%20document.currentScript.before(form)%3B%0A%20%20%20%20form.requestSubmit()%3B%0A%20%20"
      

  • TIMEOUT [expected ERROR] /html/browsers/history/the-location-interface/location_replace_session_history.html (#41896)
  • TIMEOUT /html/semantics/embedded-content/media-elements/autoplay-allowed-by-permissions-policy.https.sub.html (#46803)
    • PASS [expected TIMEOUT] subtest: Permissions-Policy header: autoplay * allows same-origin iframes.
  • TIMEOUT /html/semantics/embedded-content/media-elements/autoplay-disabled-by-permissions-policy.https.sub.html (#46012, #46804)
    • TIMEOUT [expected FAIL] subtest: Permissions-Policy header: autoplay "none" disallows same-origin iframes.

      Test timed out
      

  • OK /html/semantics/embedded-content/media-elements/media_fragment_seek.html (#24114)
    • FAIL [expected PASS] subtest: Video should seek to time specified in media fragment syntax

      assert_equals: expected 1 but got 0
      

  • TIMEOUT [expected OK] /html/semantics/embedded-content/media-elements/track/track-element/track-cues-seeking.html (#46789)
    • TIMEOUT [expected FAIL] subtest: TextTrack's activeCues are indexed and updated during video playback

      Test timed out
      

  • OK /html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-nav-form-submit.html (#32607)
    • FAIL [expected PASS] subtest: Navigating iframe loading='lazy' before it is loaded: form submit

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

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

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

  • 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 /mixed-content/tentative/autoupgrades/video-upgrade.https.sub.html (#41135)
    • FAIL [expected PASS] subtest: Video autoupgraded

      assert_equals: Length. expected 1 but got Infinity
      

  • TIMEOUT [expected OK] /pointerevents/compat/pointerevent_touch-action_two-finger_interaction.html (#44693)
    • NOTRUN [expected PASS] subtest: touch two-finger pan on 'touch-action: pan-x pan-y'
    • NOTRUN [expected FAIL] subtest: touch two-finger pan on 'touch-action: pinch-zoom'
  • OK /resource-timing/buffer-full-add-then-clear.html (#40819)
    • FAIL [expected PASS] subtest: Test that if the buffer is cleared after entries were added to the secondary buffer, those entries make it into the primary one

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

  • OK /resource-timing/buffer-full-then-increased.html (#44408)
    • FAIL [expected PASS] subtest: Test that overflowing the buffer and immediately increasing its limit does not trigger the resourcetimingbufferfull event

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

  • CRASH [expected OK] /trusted-types/script-enforcement-016.html
  • TIMEOUT [expected OK] /trusted-types/trusted-types-navigation.html?16-20 (#44835)
    • TIMEOUT [expected PASS] subtest: Navigate a frame via area with javascript:-urls w/ default policy in enforcing mode.

      Test timed out
      

    • NOTRUN [expected FAIL] subtest: Navigate a frame via area with javascript:-urls in report-only mode.
    • NOTRUN [expected PASS] subtest: Navigate a frame via area with javascript:-urls w/ default policy in report-only mode.
  • OK /webdriver/tests/classic/perform_actions/pointer_touch.py
    • PASS [expected FAIL] subtest: test_move_to_fractional_position
    • FAIL [expected PASS] subtest: test_touch_pointer_in_shadow_tree[outer-open]

      AssertionError
      

    • FAIL [expected PASS] subtest: test_touch_pointer_in_shadow_tree[outer-closed]

      AssertionError
      

    • FAIL [expected PASS] subtest: test_touch_pointer_in_shadow_tree[inner-open]

      AssertionError
      

  • CRASH [expected OK] /webstorage/storage_local_setitem_quotaexceedederr.window.html
  • TIMEOUT [expected OK] /webstorage/storage_session_setitem_quotaexceedederr.window.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 (13)
  • 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] subtest: Payload size restriction should be accumulated: type = arraybuffer

      assert_false: expected false got true
      

  • OK /css/css-fonts/variations/at-font-face-font-matching.html (#20684)
    • FAIL [expected PASS] subtest: Matching font-weight: '400' should prefer '351 398' over '501 550'

      assert_equals: Unexpected font on test element expected 487 but got 532
      

    • FAIL [expected PASS] subtest: Matching font-weight: '430' should prefer '420 440' over '450 460'

      assert_equals: Unexpected font on test element expected 487 but got 532
      

    • FAIL [expected PASS] subtest: Matching font-weight: '500' should prefer '450 460' over '400'

      assert_equals: Unexpected font on test element expected 487 but got 532
      

    • FAIL [expected PASS] subtest: Matching font-weight: '500' should prefer '350 399' over '351 398'

      assert_equals: Unexpected font on test element expected 487 but got 532
      

    • FAIL [expected PASS] subtest: Matching font-stretch: '90%' should prefer '50% 80%' over '60% 70%'

      assert_equals: Unexpected font on test element expected 487 but got 532
      

    • FAIL [expected PASS] subtest: Matching font-style: 'oblique 20deg' should prefer 'oblique 10deg' over 'oblique 0deg'

      assert_equals: Unexpected font on test element expected 487 but got 532
      

    • FAIL [expected PASS] subtest: Matching font-style: 'oblique 10deg' should prefer 'oblique -50deg -20deg' over 'oblique -40deg -30deg'

      assert_equals: Unexpected font on test element expected 487 but got 532
      

    • FAIL [expected PASS] subtest: Matching font-style: 'oblique -10deg' should prefer 'oblique 0deg 10deg' over 'oblique 40deg 50deg'

      assert_equals: Unexpected font on test element expected 487 but got 532
      

  • ERROR [expected OK] /css/css-sizing/responsive-iframe/responsive-iframe-allow-origins.html?container=subdomain,origin=same,allowed=wildcard,expect=400 (#47268)
  • OK /dom/nodes/moveBefore/iframe-document-preserve.window.html (#43152)
    • FAIL [expected PASS] subtest: moveBefore(): cross-origin iframe is preserved: remove self

      assert_equals: iframe does not fire a second load event expected 1 but got 0
      

    • FAIL [expected PASS] subtest: moveBefore(): cross-origin iframe is preserved: remove self via replaceChildren()

      assert_equals: iframe does not fire a second load event expected 1 but got 0
      

  • TIMEOUT /fetch/metadata/generated/css-images.sub.tentative.html (#29047)
    • TIMEOUT [expected PASS] subtest: background-image sec-fetch-mode - Not sent to non-trustworthy cross-site destination

      Test timed out
      

  • 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 [expected TIMEOUT] /html/infrastructure/urls/base-url/document-base-url-window-initiator-is-not-opener.https.window.html (#30970)
  • OK [expected TIMEOUT] /html/semantics/embedded-content/media-elements/src_object_blob.html (#40340)
    • PASS [expected TIMEOUT] subtest: HTMLMediaElement.srcObject blob
  • TIMEOUT /html/syntax/parsing/html5lib_write.html?file=processing-instructions (#46205)
    • TIMEOUT [expected FAIL] subtest: &lt;body&gt;&lt;?v-123&gt;

      Test timed out
      

  • TIMEOUT [expected OK] /html/syntax/parsing/html5lib_write_single.html?file=processing-instructions (#46206)
    • TIMEOUT [expected PASS] subtest: &lt;body&gt;&lt;?xml&gt;

      Test timed out
      

    • TIMEOUT [expected PASS] subtest: &lt;body&gt;&lt;?xml-stylesheet&gt;

      Test timed out
      

    • TIMEOUT [expected PASS] subtest: &lt;body&gt;&lt;?XML&gt;

      Test timed out
      

    • TIMEOUT [expected PASS] subtest: &lt;body&gt;&lt;?XML-stylesheet&gt;

      Test timed out
      

    • TIMEOUT [expected PASS] subtest: &lt;body&gt;&lt;?xML&gt;

      Test timed out
      

    • TIMEOUT [expected PASS] subtest: &lt;body&gt;&lt;?xml-STYLesheet&gt;

      Test timed out
      

    • TIMEOUT [expected PASS] subtest: &lt;body&gt;&lt;?1st-place&gt;

      Test timed out
      

  • OK /pointerevents/compat/pointerevent_touch_target_after_pointerdown_target_removed.tentative.html (#42813)
    • PASS [expected FAIL] subtest: After a pointerdown listener moves the target to different position, touch events should be fired on the pointerdown target, but pointer events should be fired on the pointerdown target parent
    • PASS [expected FAIL] subtest: After a pointerdown listener moves the target to different position, touchmove event should be fired on the pointerdown target parent
  • OK /sanitizer-api/sanitizer-inert-document.html (#45260)
    • FAIL [expected PASS] subtest: Test whether setHTML loads the image.

      promise_test: Unhandled rejection with value: "The server observed a request. It shouldn't have."
      

    • PASS [expected FAIL] subtest: Test whether setHTMLUnsafe loads the image.
  • OK [expected TIMEOUT] /trusted-types/trusted-types-navigation.html?06-10 (#37920)
    • FAIL [expected PASS] subtest: Navigate a frame via anchor with javascript:-urls in report-only mode.

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

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

@github-actions

Copy link
Copy Markdown

✨ Try run (#32087714483) succeeded.

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
@simonwuelker
simonwuelker force-pushed the font-variations-from-font-style branch from 3ced6e2 to f148104 Compare August 18, 2026 02:42
Comment thread components/fonts/font.rs Outdated
/// `self` is consumed to work around platform differences. On some platforms, changing the
/// variations requires creating an entirely new font face, whereas on others the returned
/// font is `self`.
fn copy_with_variations(self, _variations: &[FontVariation]) -> Result<Self, &'static str> {

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.

I'm not entirely happy with the name of this function, so feel free to suggest something better.

@mrobinson mrobinson left a comment

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.

Thanks! I have a few comments below. I've also added a new commit to this PR which adds support to this change for macOS. I hope to tackle Windows soon (hopefully tomorrow).

Comment thread components/fonts/font.rs Outdated
Comment thread components/fonts/font.rs Outdated
Comment thread components/fonts/font.rs Outdated
Comment thread components/fonts/font.rs Outdated
Comment thread components/fonts/font_context.rs
@servo-highfive servo-highfive added S-needs-code-changes Changes have not yet been made that were requested by a reviewer. and removed S-awaiting-review There is new code that needs to be reviewed. labels Aug 18, 2026
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
@mrobinson
mrobinson force-pushed the font-variations-from-font-style branch from 9447ed4 to 382b130 Compare August 18, 2026 17:36
@servo-highfive servo-highfive added S-awaiting-review There is new code that needs to be reviewed. and removed S-needs-code-changes Changes have not yet been made that were requested by a reviewer. labels Aug 18, 2026
@simonwuelker simonwuelker added the T-linux-wpt Do a try run of the WPT label Aug 19, 2026
@github-actions github-actions Bot removed the T-linux-wpt Do a try run of the WPT label Aug 19, 2026
@github-actions

Copy link
Copy Markdown

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

@github-actions

Copy link
Copy Markdown

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

Flaky unexpected result (45)
  • CRASH [expected OK] /IndexedDB/idbcursor-continue.any.worker.html
  • TIMEOUT [expected OK] /_webgl/conformance/textures/misc/tex-image-and-sub-image-2d-with-array-buffer-view.html
    • NOTRUN [expected PASS] subtest: Overall test
  • TIMEOUT [expected OK] /_webgl/conformance/uniforms/no-over-optimization-on-uniform-array-14.html
    • NOTRUN [expected PASS] subtest: Overall test
  • CRASH [expected OK] /_webgl/conformance/uniforms/no-over-optimization-on-uniform-array-16.html
  • TIMEOUT [expected OK] /_webgl/conformance2/sync/sync-webgl-specific.html
  • OK /_webgl/conformance2/textures/misc/origin-clean-conformance-offscreencanvas.html
    • FAIL [expected PASS] subtest: WebGL test #0

      assert_true: Image setup failed (timeout). expected true got false
      

  • OK /cookies/partitioned-cookies/partitioned-cookies-samesite-attribute.https.html (#45255)
    • FAIL [expected PASS] subtest: In embedded cross-site contexts, partitioned cookies can only be set with explicit SameSite=None

      assert_equals: expected 0 but got 3
      

  • FAIL [expected PASS] /css/css-backgrounds/background-size-042.html
  • FAIL [expected PASS] /css/css-sizing/dynamic-available-size-iframe.html (#45763)
  • ERROR [expected TIMEOUT] /css/css-sizing/responsive-iframe/responsive-iframe-allow-origins.html?container=subdomain,origin=cross,allowed=wildcard,expect=400 (#47268)
  • TIMEOUT [expected OK] /fetch/content-encoding/gzip/big-gzip-body.https.any.worker.html
    • TIMEOUT [expected PASS] subtest: large gzip data should be decompressed successfully

      Test timed out
      

    • NOTRUN [expected FAIL] subtest: large gzip data should be decompressed successfully with byte stream
  • TIMEOUT [expected OK] /fetch/content-encoding/zstd/big-zstd-body.https.any.html
  • ERROR [expected TIMEOUT] /html/browsers/browsing-the-web/history-traversal/pageswap/pageswap-initial-navigation.html (#40387)
  • OK /html/browsers/browsing-the-web/navigating-across-documents/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 [expected TIMEOUT] /html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click.html (#44099)
    • FAIL [expected TIMEOUT] subtest: Replace before load, triggered by submitButton.click()

      assert_equals: expected "http://web-platform.test:8000/common/blank.html?thereplacement=" but got "http://web-platform.test:8000/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/code-injector.html?pipe=sub(none)&amp;code=%0A%20%20%20%20const%20form%20%3D%20document.createElement(%22form%22)%3B%0A%20%20%20%20form.action%20%3D%20%22%2Fcommon%2Fblank.html%22%3B%0A%0A%20%20%20%20const%20input%20%3D%20document.createElement(%22input%22)%3B%0A%20%20%20%20input.type%20%3D%20%22hidden%22%3B%0A%20%20%20%20input.name%20%3D%20%22thereplacement%22%3B%0A%20%20%20%20form.append(input)%3B%0A%0A%20%20%20%20const%20button%20%3D%20document.createElement(%22button%22)%3B%0A%20%20%20%20button.type%20%3D%20%22submit%22%3B%0A%20%20%20%20form.append(button)%3B%0A%0A%20%20%20%20document.currentScript.before(form)%3B%0A%20%20%20%20button.click()%3B%0A%20%20"
      

  • OK [expected TIMEOUT] /html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit.html (#44028)
  • OK /html/browsers/browsing-the-web/overlapping-navigations-and-traversals/cross-document-nav-cross-document-nav.html (#29181)
    • PASS [expected FAIL] subtest: cross-document navigation then cross-document navigation
  • TIMEOUT [expected ERROR] /html/browsers/history/the-location-interface/location_replace_session_history.html (#41896)
  • OK [expected TIMEOUT] /html/infrastructure/urls/base-url/document-base-url-window-initiator-is-not-opener.https.window.html (#30970)
  • TIMEOUT /html/semantics/embedded-content/media-elements/autoplay-disabled-by-permissions-policy.https.sub.html (#46012, #46804)
    • TIMEOUT [expected FAIL] subtest: Permissions-Policy header: autoplay "none" disallows same-origin iframes.

      Test timed out
      

  • 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 [expected TIMEOUT] /html/semantics/embedded-content/the-video-element/video_timeupdate_on_seek.html
    • PASS [expected TIMEOUT] subtest: timeupdate is emitted after a seek before the data is received: webm.
  • CRASH [expected FAIL] /html/semantics/interestfor/interestfor-plain-inline-element.tentative.html
  • TIMEOUT [expected OK] /html/syntax/parsing/html5lib_write.html?file=search-element
    • TIMEOUT [expected PASS] subtest: &lt;!doctype html&gt;&lt;p&gt;foo&lt;search&gt;bar&lt;p&gt;baz

      Test timed out
      

    • TIMEOUT [expected PASS] subtest: &lt;!doctype html&gt;&lt;search&gt;&lt;p&gt;foo&lt;/search&gt;bar

      Test timed out
      

    • TIMEOUT [expected PASS] subtest: &lt;!DOCTYPE html&gt;xxx&lt;svg&gt;&lt;x&gt;&lt;g&gt;&lt;a&gt;&lt;search&gt;&lt;b&gt;

      Test timed out
      

  • TIMEOUT [expected OK] /html/user-activation/navigation-state-reset-sameorigin.html (#45151)
    • TIMEOUT [expected FAIL] subtest: Post-navigation state reset.

      Test timed out
      

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

      assert_true: onload must be called expected true got false
      

  • OK /paint-timing/with-lcp/fcp-document-opacity-image.html (#43068)
    • FAIL [expected PASS] subtest: Test that FCP after opacity change is not a larger value than LCP

      assert_equals: First contentful paint marked too early.  expected 0 but got 1
      

  • ERROR [expected TIMEOUT] /performance-timeline/not-restored-reasons/abort-block-bfcache.window.html (#42802)
  • TIMEOUT [expected OK] /pointerevents/compat/pointerevent_touch-action_two-finger_interaction.html (#44693)
    • NOTRUN [expected PASS] subtest: touch two-finger pan on 'touch-action: pan-x pan-y'
    • NOTRUN [expected FAIL] subtest: touch two-finger pan on 'touch-action: pinch-zoom'
  • OK /pointerevents/compat/pointerevent_touch_target_after_pointerdown_target_removed.tentative.html (#42813)
    • FAIL [expected PASS] subtest: After a pointerdown listener removes its target, touch events should be fired on the touchstart target even though an orphan and pointer events should be fired on the parent

      assert_equals: expected "[ { type: pointerdown, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: touchstart, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: pointermove, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: pointerup, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: touchend, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; } ]" but got "[ { type: pointerdown, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: touchstart, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: pointermove, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: pointerup, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: touchend, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: pointermove, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: pointerdown, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: pointerup, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; } ]"
      

    • FAIL [expected PASS] subtest: After a pointerdown listener removes its target, touchmove event should be fired on the pointerdown target

      assert_equals: expected "[ { type: pointerdown, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: touchstart, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: pointermove, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: touchmove, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: pointerup, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: touchend, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; } ]" but got "[ { type: pointerdown, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: touchstart, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: pointermove, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: touchmove, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: pointerup, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: touchend, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: pointermove, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: pointerdown, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: pointerup, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; } ]"
      

  • OK /pointerevents/pointerevent_pointercapture_in_frame.html?touch (#45445)
    • FAIL [expected PASS] subtest: Test touchpointer capture in same-origin frame: Pointer down at inner frame and set pointer capture to outer frame should not capture.

      assert_array_equals: Received events: innerFrame received pointerdown,innerFrame received pointermove,innerFrame received pointerup,innerFrame received lostpointercapture lengths differ, expected array ["innerFrame received pointerdown", "innerFrame received pointermove", "innerFrame received pointerup"] length 3, got ["innerFrame received pointerdown", "innerFrame received pointermove", "innerFrame received pointerup", "innerFrame received lostpointercapture"] length 4
      

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

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

  • OK /resource-timing/buffer-full-then-increased.html (#44408)
    • FAIL [expected PASS] subtest: Test that overflowing the buffer and immediately increasing its limit does not trigger the resourcetimingbufferfull event

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

  • 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/script-enforcement-016.html
  • OK [expected TIMEOUT] /trusted-types/trusted-types-navigation.html?01-05 (#38975)
    • PASS [expected TIMEOUT] subtest: Navigate a window via anchor with javascript:-urls in report-only mode.
    • PASS [expected NOTRUN] subtest: Navigate a window via anchor with javascript:-urls w/ default policy in report-only mode.
    • PASS [expected NOTRUN] subtest: Navigate a frame via anchor with javascript:-urls in enforcing mode.
  • OK /upgrade-insecure-requests/gen/sharedworker-classic-data.meta/upgrade/websocket.https.html
    • FAIL [expected PASS] subtest: Upgrade-Insecure-Requests: Expects allowed for websocket to cross-ws-downgrade origin and no-redirect redirection from https context.

      assert_equals: The resource request should be 'allowed'. expected "allowed" but got "blocked"
      

    • FAIL [expected PASS] subtest: Upgrade-Insecure-Requests: Expects allowed for websocket to same-ws-downgrade origin and no-redirect redirection from https context.

      assert_equals: The resource request should be 'allowed'. expected "allowed" but got "blocked"
      

  • OK /upgrade-insecure-requests/gen/sharedworker-module-data.meta/upgrade/xhr.https.html
    • FAIL [expected PASS] subtest: Upgrade-Insecure-Requests: Expects allowed for xhr to cross-http-downgrade origin and downgrade redirection from https context.

      assert_equals: The resource request should be 'allowed'. expected "allowed" but got "blocked"
      

    • FAIL [expected PASS] subtest: Upgrade-Insecure-Requests: Expects allowed for xhr to cross-http-downgrade origin and no-redirect redirection from https context.

      assert_equals: The resource request should be 'allowed'. expected "allowed" but got "blocked"
      

    • FAIL [expected PASS] subtest: Upgrade-Insecure-Requests: Expects allowed for xhr to cross-https origin and downgrade redirection from https context.

      assert_equals: The resource request should be 'allowed'. expected "allowed" but got "blocked"
      

    • FAIL [expected PASS] subtest: Upgrade-Insecure-Requests: Expects allowed for xhr to same-http-downgrade origin and downgrade redirection from https context.

      assert_equals: The resource request should be 'allowed'. expected "allowed" but got "blocked"
      

    • FAIL [expected PASS] subtest: Upgrade-Insecure-Requests: Expects allowed for xhr to same-http-downgrade origin and no-redirect redirection from https context.

      assert_equals: The resource request should be 'allowed'. expected "allowed" but got "blocked"
      

    • FAIL [expected PASS] subtest: Upgrade-Insecure-Requests: Expects allowed for xhr to same-https origin and downgrade redirection from https context.

      assert_equals: The resource request should be 'allowed'. expected "allowed" but got "blocked"
      

  • OK /visual-viewport/resize-event-order.html (#41981)
    • PASS [expected FAIL] subtest: Popup: DOMWindow resize fired before VisualViewport.
  • 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"
      

  • OK /webdriver/tests/classic/perform_actions/key_events.py
    • FAIL [expected PASS] subtest: test_printable_key_sends_correct_events["-Quote]

      assert [] == [{'code': 'Quote', 'key': '"', 'type': 'keydown'}, {'code': 'Quote', 'key': '"', 'type': 'keypress'}, {'code': 'Quote', 'key': '"', 'type': 'keyup'}]
      

  • OK [expected TIMEOUT] /webdriver/tests/classic/perform_actions/navigation.py (#38822)
  • OK /webdriver/tests/classic/perform_actions/pointer_pen.py
    • FAIL [expected PASS] subtest: test_pen_pointer_in_shadow_tree[outer-open]

      AssertionError
      

    • FAIL [expected PASS] subtest: test_pen_pointer_in_shadow_tree[outer-closed]

      AssertionError
      

    • FAIL [expected PASS] subtest: test_pen_pointer_in_shadow_tree[inner-open]

      AssertionError
      

    • FAIL [expected PASS] subtest: test_pen_pointer_in_shadow_tree[inner-closed]

      AssertionError
      

  • TIMEOUT [expected OK] /webstorage/storage_session_setitem_quotaexceedederr.window.html
Stable unexpected results that are known to be intermittent (18)
  • OK /beacon/beacon-basic.https.window.html (#41723)
    • PASS [expected FAIL] subtest: Payload size restriction should be accumulated: type = blob
  • OK /css/css-flexbox/text-as-flexitem-size-001.html (#47104)
    • FAIL [expected PASS] subtest: .flexbox &gt; div 1

      assert_equals: 
      &lt;div data-expected-width="70" data-expected-height="35"&gt;
              &lt;p&gt;xx xxx&lt;/p&gt;
              &lt;p&gt;xx&lt;/p&gt;
            &lt;/div&gt;
      width expected 70 but got 46
      

    • FAIL [expected PASS] subtest: .flexbox &gt; div 2

      assert_equals: 
      &lt;div data-expected-width="50" data-expected-height="45"&gt;
              &lt;p&gt;xx xxx&lt;/p&gt;
              &lt;p&gt;xx&lt;/p&gt;
            &lt;/div&gt;
      width expected 50 but got 46
      

    • FAIL [expected PASS] subtest: .flexbox &gt; div 5

      assert_equals: 
      &lt;div style="height: 30px" data-expected-width="70" data-expected-height="30"&gt;
              &lt;p&gt;xx xxx&lt;/p&gt;
              &lt;p&gt;xx&lt;/p&gt;
            &lt;/div&gt;
      width expected 70 but got 46
      

    • FAIL [expected PASS] subtest: .flexbox &gt; div 8

      assert_equals: 
      &lt;div style="min-height: 40px" data-expected-width="70" data-expected-height="40"&gt;
              &lt;p&gt;xx xxx&lt;/p&gt;
              &lt;p&gt;xx&lt;/p&gt;
            &lt;/div&gt;
      width expected 70 but got 46
      

    • FAIL [expected PASS] subtest: .flexbox &gt; div 11

      assert_equals: 
      &lt;div style="max-height: 20px" data-expected-width="70" data-expected-height="20"&gt;
              &lt;p&gt;xx xxx&lt;/p&gt;
              &lt;p&gt;xx&lt;/p&gt;
            &lt;/div&gt;
      width expected 70 but got 46
      

  • OK /css/css-fonts/variations/at-font-face-font-matching.html (#20684)
    • FAIL [expected PASS] subtest: Matching font-weight: '501' should prefer '503 520' over '500'

      assert_equals: Unexpected font on test element expected 487 but got 532
      

    • FAIL [expected PASS] subtest: Matching font-style: 'italic' should prefer 'oblique -60deg -30deg' over 'oblique -50deg -40deg'

      assert_equals: Unexpected font on test element expected 487 but got 532
      

    • FAIL [expected PASS] subtest: Matching font-style: 'oblique 0deg' should prefer 'oblique 15deg 20deg' over 'oblique 30deg 60deg'

      assert_equals: Unexpected font on test element expected 487 but got 532
      

    • FAIL [expected PASS] subtest: Matching font-style: 'oblique 0deg' should prefer 'oblique -50deg -20deg' over 'oblique -40deg -30deg'

      assert_equals: Unexpected font on test element expected 487 but got 532
      

    • FAIL [expected PASS] subtest: Matching font-style: 'oblique -20deg' should prefer 'oblique 0deg' over 'oblique 30deg 60deg'

      assert_equals: Unexpected font on test element expected 487 but got 532
      

    • FAIL [expected PASS] subtest: Matching font-style: 'oblique -21deg' should prefer 'oblique 0deg' over 'oblique 30deg 60deg'

      assert_equals: Unexpected font on test element expected 487 but got 532
      

  • ERROR [expected OK] /css/css-sizing/responsive-iframe/responsive-iframe-allow-origins.html?container=subdomain,origin=same,allowed=wildcard,expect=400 (#47268)
  • 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
      

  • OK [expected ERROR] /fetch/fetch-later/quota/same-origin-iframe/multiple-iframes.https.window.html (#35176)
  • TIMEOUT /fetch/metadata/generated/css-images.sub.tentative.html (#29047)
    • FAIL [expected PASS] subtest: content sec-fetch-site - HTTPS downgrade-upgrade

      assert_unreached: Reached unreachable code
      

  • 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 /html/semantics/embedded-content/media-elements/autoplay-default-permissions-policy.https.sub.html (#46805)
    • PASS [expected TIMEOUT] subtest: Default "autoplay" feature policy ["self"] allows same-origin iframes.
  • OK [expected TIMEOUT] /html/semantics/embedded-content/media-elements/src_object_blob.html (#40340)
    • PASS [expected TIMEOUT] subtest: HTMLMediaElement.srcObject blob
  • TIMEOUT /html/syntax/parsing/html5lib_write.html?file=processing-instructions (#46205)
    • TIMEOUT [expected FAIL] subtest: &lt;body&gt;&lt;?r2-d2 data&gt;

      Test timed out
      

    • TIMEOUT [expected FAIL] subtest: &lt;body&gt;&lt;?level-99&gt;

      Test timed out
      

  • OK [expected TIMEOUT] /infrastructure/testdriver/click_nested.html (#43887)
    • FAIL [expected NOTRUN] subtest: TestDriver click method with multiple windows and nested iframe

      can't access property "document", child.frames[2] is undefined
      

  • OK /resource-timing/test_resource_timing.https.html (#25216)
    • PASS [expected FAIL] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (xmlhttprequest)
  • OK /sanitizer-api/sanitizer-inert-document.html (#45260)
    • FAIL [expected PASS] subtest: Test whether setHTML loads the image.

      promise_test: Unhandled rejection with value: "The server observed a request. It shouldn't have."
      

    • PASS [expected FAIL] subtest: Test whether setHTMLUnsafe loads the image.
  • TIMEOUT /trusted-types/trusted-types-navigation.html?06-10 (#37920)
    • FAIL [expected PASS] subtest: Navigate a frame via anchor with javascript:-urls in report-only mode.

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

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

      Test timed out
      

  • OK /user-timing/measure_associated_with_navigation_timing.html (#47273)
    • FAIL [expected PASS] subtest: loadTime plus loadEventEnd to a mark "a" should equal to navigationStart to "a".

      assert_equals: loadTime plus loadEventEnd to a mark "a" should equal to navigationStart to "a". expected 18.93 but got 18.919999999999998
      

  • OK [expected TIMEOUT] /webstorage/localstorage-about-blank-3P-iframe-opens-3P-window.partitioned.html (#29053)
    • FAIL [expected TIMEOUT] subtest: StorageKey: test 3P about:blank window opened from a 3P iframe

      assert_true: about:blank window opened by 3P iframe does not inherit 3P iframe's StorageKey expected true got false
      

Stable unexpected results (1)
  • PASS [expected FAIL] /css/css-fonts/matching/range-descriptor-reversed.html

@github-actions

Copy link
Copy Markdown

⚠️ Try run (#32201800050) failed!

@mrobinson mrobinson left a comment

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.

Looks good. I'm happy to land Windows support in a followup.

@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Aug 19, 2026
@simonwuelker

Copy link
Copy Markdown
Member Author

Looks good. I'm happy to land Windows support in a followup.

Nice, thank you!

@simonwuelker
simonwuelker added this pull request to the merge queue Aug 19, 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 Aug 19, 2026
@simonwuelker
simonwuelker removed this pull request from the merge queue due to a manual request Aug 19, 2026
@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 Aug 19, 2026
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
@simonwuelker
simonwuelker force-pushed the font-variations-from-font-style branch from e61a998 to 886ce74 Compare August 19, 2026 10:09
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Aug 19, 2026
@simonwuelker
simonwuelker enabled auto-merge August 19, 2026 10:10
@simonwuelker simonwuelker changed the title fonts: Synthesize variations from font-style for freetype systems fonts: Synthesize variations from font-style for macos & freetype systems Aug 19, 2026
@simonwuelker
simonwuelker added this pull request to the merge queue Aug 19, 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 Aug 19, 2026
Merged via the queue into servo:main with commit 88497f8 Aug 19, 2026
37 checks passed
@simonwuelker
simonwuelker deleted the font-variations-from-font-style branch August 19, 2026 11:39
@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 Aug 19, 2026
RichardTjokroutomo pushed a commit to RichardTjokroutomo/servo that referenced this pull request Oct 6, 2026
…ystems (servo#46918)

Synthesizing variations from `font-style` requires a bit more work than
for other properties because depending on which variation axes the font
has we need to set either `ital` (to 0 or 1) or `slnt` (to a number of
degrees). Currently we synthesize variations too early, when the font
data is not yet avaiable. That's why this change moves the
implementation of
https://drafts.csswg.org/css-fonts-4/#apply-font-matching-variations to
`Font::new`.

On macos and windows `PlatformFont::copy_with_variations` is not yet
implemented, so variations are synthesized but they do nothing. I hope
to fix this in future changes but its hard to approach since I don't
have access to macos/windows machines.

Testing: New tests start to pass
Part of servo#38800

---------

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
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.

3 participants