layout: Disregard 'content' property for <input> elements by SimonSapin · Pull Request #44762 · servo/servo · GitHub
Skip to content

layout: Disregard 'content' property for <input> elements#44762

Merged
TimvdLippe merged 1 commit into
servo:mainfrom
SimonSapin:widgets-disregard-content-property
May 6, 2026
Merged

layout: Disregard 'content' property for <input> elements#44762
TimvdLippe merged 1 commit into
servo:mainfrom
SimonSapin:widgets-disregard-content-property

Conversation

@SimonSapin

Copy link
Copy Markdown
Member

On elements (as opposed to pseudo-elements), CSS content: url(something) turns the element into a replaced element like <img> and ignores the DOM child nodes

Some elements like <input> are "widgets" that can have native appearance: https://drafts.csswg.org/css-ui/#widget

When both apply to the same element like in the added test case, the native appearance should take priority and the content property should be disregarded: https://drafts.csswg.org/css-ui/#disregard

Previously Servo would do something half-way, generating boxes for the native appearance (including an absolutely-positioned pseudo-element for the slider of <input type=range>) but not generate fragments and later expect to find fragments, causing a panic.

Testing: adding the issue reproduction as a WPT crashtest
Fixes: #44540

@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label May 6, 2026
@SimonSapin SimonSapin added the T-linux-wpt Do a try run of the WPT label May 6, 2026
@github-actions github-actions Bot removed the T-linux-wpt Do a try run of the WPT label May 6, 2026
@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown

@servo-wpt-sync

Copy link
Copy Markdown
Collaborator

🤖 Opened new upstream WPT pull request (web-platform-tests/wpt#59691) with upstreamable changes.

@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown

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

Flaky unexpected result (30)
  • OK /IndexedDB/idbtransaction-oncomplete.any.html (#42845)
    • FAIL [expected PASS] subtest: IDBTransaction - complete event

      assert_array_equals: lengths differ, expected array ["upgradeneeded", "complete", "success", "opencursor"] length 4, got ["upgradeneeded", "complete", "success"] length 3
      

  • 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 #53
    • PASS [expected FAIL] subtest: WebGL test #55
    • PASS [expected FAIL] subtest: WebGL test #57
    • PASS [expected FAIL] subtest: WebGL test #59
    • FAIL [expected PASS] subtest: WebGL test #61

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

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

      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
      

    • And 10 more unexpected results...
  • FAIL [expected PASS] /css/css-backgrounds/background-size-041.html
  • FAIL [expected PASS] /css/css-backgrounds/border-image-repeat-space-9.html
  • OK /css/css-grid/grid-definition/grid-inline-support-repeat-001.html
    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: repeat(1, auto); and grid-template-rows: repeat(1, auto);

      assert_in_array: gridTemplateColumns value "59px" not in array ["90px"]
      

    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: repeat(2, auto); and grid-template-rows: repeat(2, auto);

      assert_in_array: gridTemplateColumns value "59px 0px" not in array ["90px 0px"]
      

    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: max-content repeat(2, 25%) 1fr; and grid-template-rows: max-content repeat(2, 25%) 1fr;

      assert_in_array: gridTemplateColumns value "59px 200px 200px 341px" not in array ["90px 200px 200px 310px", "90px repeat(2, 200px) 310px"]
      

    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: repeat(2, min-content 50px); and grid-template-rows: repeat(2, min-content 50px);

      assert_in_array: gridTemplateColumns value "28px 50px 0px 50px" not in array ["40px 50px 0px 50px"]
      

    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: repeat(-1, auto); and grid-template-rows: repeat(-1, auto);

      assert_in_array: gridTemplateColumns value "59px" not in array ["90px"]
      

    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: repeat(auto, 2); and grid-template-rows: repeat(auto, 2);

      assert_in_array: gridTemplateColumns value "59px" not in array ["90px"]
      

    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: repeat 2, auto; and grid-template-rows: repeat 2, auto;

      assert_in_array: gridTemplateColumns value "59px" not in array ["90px"]
      

    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: repeat(2 auto); and grid-template-rows: repeat(2 auto);

      assert_in_array: gridTemplateColumns value "59px" not in array ["90px"]
      

    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: 100px (repeat 2, auto); and grid-template-rows: (repeat 2, auto);

      assert_in_array: gridTemplateColumns value "59px" not in array ["90px"]
      

    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: repeat(2, 50px repeat(2, 100px)); and grid-template-rows: repeat(2, 50px repeat(2, 100px));

      assert_in_array: gridTemplateColumns value "59px" not in array ["90px"]
      

    • And 1 more unexpected results...
  • OK /css/css-grid/grid-definition/grid-inline-template-columns-rows-resolved-values-001.html
    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: ; and grid-template-rows: ;

      assert_in_array: gridTemplateColumns value "78px" not in array ["110px"]
      

    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: auto auto; and grid-template-rows: ;

      assert_in_array: gridTemplateColumns value "64px 106px" not in array ["100px 110px"]
      

    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: ; and grid-template-rows: 50px;

      assert_in_array: gridTemplateColumns value "78px" not in array ["110px"]
      

    • FAIL [expected PASS] subtest: 'grid' with: grid-template-columns: ; and grid-template-rows: 50px 30px;

      assert_in_array: gridTemplateColumns value "78px" not in array ["110px"]
      

    • FAIL [expected PASS] subtest: 'gridItemsPositions' with: grid-template-columns: ; and grid-template-rows: ;

      assert_in_array: gridTemplateColumns value "78px 0px 0px 0px 64px" not in array ["110px 0px 0px 0px 100px", "110px repeat(3, 0px) 100px"]
      

    • FAIL [expected PASS] subtest: 'gridItemsPositions' with: grid-template-columns: 60px; and grid-template-rows: ;

      assert_in_array: gridTemplateColumns value "60px 0px 0px 0px 64px" not in array ["60px 0px 0px 0px 100px", "60px repeat(3, 0px) 100px"]
      

    • FAIL [expected PASS] subtest: 'gridItemsPositions' with: grid-template-columns: 60px 50px; and grid-template-rows: ;

      assert_in_array: gridTemplateColumns value "60px 50px 0px 0px 64px" not in array ["60px 50px 0px 0px 100px", "60px 50px repeat(2, 0px) 100px"]
      

    • FAIL [expected PASS] subtest: 'gridItemsPositions' with: grid-template-columns: ; and grid-template-rows: 60px;

      assert_in_array: gridTemplateColumns value "78px 0px 0px 0px 64px" not in array ["110px 0px 0px 0px 100px", "110px repeat(3, 0px) 100px"]
      

    • FAIL [expected PASS] subtest: 'gridItemsPositions' with: grid-template-columns: ; and grid-template-rows: 60px 50px;

      assert_in_array: gridTemplateColumns value "78px 0px 0px 0px 64px" not in array ["110px 0px 0px 0px 100px", "110px repeat(3, 0px) 100px"]
      

    • FAIL [expected PASS] subtest: 'gridItemsPositions' with: grid-template-columns: 60px; and grid-template-rows: 60px;

      assert_in_array: gridTemplateColumns value "60px 0px 0px 0px 64px" not in array ["60px 0px 0px 0px 100px", "60px repeat(3, 0px) 100px"]
      

    • And 21 more unexpected results...
  • FAIL [expected PASS] /css/css-ui/webkit-appearance-meter-001.html
  • 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)
  • ERROR [expected OK] /fetch/fetch-later/quota/same-origin-iframe/sandboxed-iframe.https.window.html (#41704)
  • TIMEOUT /fetch/metadata/generated/css-images.sub.tentative.html (#29047)
    • TIMEOUT [expected PASS] subtest: background-image sec-fetch-site - Not sent to non-trustworthy same-origin destination

      Test timed out
      

    • FAIL [expected PASS] subtest: content sec-fetch-site - HTTPS downgrade-upgrade

      assert_unreached: Reached unreachable code
      

  • ERROR [expected OK] /fetch/metadata/window-open.https.sub.html (#40339)
  • OK /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment.html (#20768)
    • FAIL [expected PASS] subtest: Tests that a fragment navigation in the unload handler will not block the initial navigation

      assert_equals: expected "" but got "#fragment"
      

  • OK /html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click.html (#28697)
    • PASS [expected FAIL] subtest: aElement.click() before the load event must NOT replace
  • OK /html/browsers/history/the-history-interface/traverse_the_history_4.html (#21383)
    • FAIL [expected PASS] subtest: Multiple history traversals, last would be aborted

      assert_array_equals: Pages opened during history navigation expected property 1 to be 5 but got 3 (expected array [6, 5] got [6, 3])
      

  • CRASH [expected OK] /html/browsers/history/the-location-interface/location-ancestor-origins-new-object.html
  • TIMEOUT [expected OK] /html/interaction/focus/the-autofocus-attribute/supported-elements.html (#24145)
    • TIMEOUT [expected FAIL] subtest: Host element with delegatesFocus should support autofocus

      Test timed out
      

    • NOTRUN [expected FAIL] subtest: Host element with delegatesFocus including no focusable descendants should be skipped
    • NOTRUN [expected FAIL] subtest: Area element should support autofocus
  • 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/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 3 but got 0
      

  • 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/scripting-1/the-script-element/module/dynamic-import/blob-url.any.worker-module.html (#43510)
    • FAIL [expected PASS] subtest: Revoking a blob URL immediately after calling import will not fail

      promise_test: Unhandled rejection with value: object "TypeError: Module fetching failed"
      

  • TIMEOUT [expected OK] /html/user-activation/navigation-state-reset-sameorigin.html
    • TIMEOUT [expected PASS] subtest: Post-navigation state reset.

      Test timed out
      

  • TIMEOUT [expected OK] /infrastructure/testdriver/click_nested.html (#43887)
    • NOTRUN [expected FAIL] subtest: TestDriver click method with multiple windows and nested iframe
  • TIMEOUT [expected OK] /navigation-timing/test-navigation-attributes-exist.html
  • OK /preload/prefetch-document.html (#37210)
    • FAIL [expected PASS] subtest: different-site document prefetch with 'as=document' should not be consumed

      assert_equals: expected 2 but got 1
      

  • OK /resource-timing/buffer-full-then-increased.html (#44408)
    • PASS [expected FAIL] subtest: Test that overflowing the buffer and immediately increasing its limit does not trigger the resourcetimingbufferfull event
  • OK /sanitizer-api/sanitizer-inert-document.tentative.html (#44273)
    • PASS [expected FAIL] subtest: Test whether setHTMLUnsafe loads the image.
  • OK [expected TIMEOUT] /trusted-types/trusted-types-navigation.html?06-10 (#37920)
    • PASS [expected FAIL] subtest: Navigate a frame via anchor with javascript:-urls in report-only mode.
    • PASS [expected TIMEOUT] subtest: Navigate a frame via anchor with javascript:-urls w/ default policy in report-only mode.
    • FAIL [expected NOTRUN] subtest: Navigate a window via anchor with javascript:-urls w/ a default policy throwing an exception in enforcing mode.

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

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

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

  • OK /visual-viewport/resize-event-order.html (#41981)
    • PASS [expected FAIL] subtest: Popup: DOMWindow resize fired before VisualViewport.
  • OK /webdriver/tests/classic/accept_alert/accept.py (#43194)
    • FAIL [expected PASS] subtest: test_accept_in_popup_window

      AssertionError: no such alert (404): No user prompt is currently active.
      

Stable unexpected results that are known to be intermittent (19)
  • OK /IndexedDB/transaction-create_in_versionchange.any.html (#44200)
    • PASS [expected FAIL] subtest: Attempt to create new transactions inside a versionchange transaction
  • 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 OK] /content-security-policy/meta/sandbox-iframe.html (#43478)
  • OK [expected TIMEOUT] /credential-management/credentialscontainer-frame-basics.https.html (#39430)
    • FAIL [expected TIMEOUT] subtest: navigator.credentials should be undefined in documents generated from data: URLs.

      assert_equals: expected "undefined" but got "[object CredentialsContainer]"
      

  • OK /css/css-fonts/generic-family-keywords-001.html (#37467)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(fangsong)
  • OK /css/css-fonts/generic-family-keywords-003.html (#38994)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted cursive (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted fantasy (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted monospace (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted system-ui (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted math (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(khmer-mul) (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(nastaliq) (drawing text in a canvas)
  • ERROR [expected OK] /fetch/fetch-later/quota/same-origin-iframe/accumulated-oversized-payload.https.window.html (#41705)
  • 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
  • ERROR [expected OK] /focus/focus-event-after-switching-iframes.sub.html (#40368)
  • 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 OK] /html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click.html (#44099)
    • TIMEOUT [expected FAIL] subtest: Replace before load, triggered by submitButton.click()

      Test timed out
      

  • ERROR [expected OK] /html/canvas/offscreen/text/2d.text.measure.getActualBoundingBox.tentative.html (#43710)
  • TIMEOUT [expected OK] /html/interaction/focus/the-autofocus-attribute/update-the-rendering.html (#24145)
    • TIMEOUT [expected FAIL] subtest: "Flush autofocus candidates" should be happen before a scroll event and animation frame callbacks

      Test timed out
      

  • OK /html/webappapis/user-prompts/print-during-unload.html (#35944)
    • PASS [expected FAIL] subtest: print() during unload
  • OK /navigation-timing/test-navigation-type-reload.html (#33334)
    • PASS [expected FAIL] subtest: Reload domComplete &gt; Original domComplete
    • PASS [expected FAIL] subtest: Reload domContentLoadedEventEnd &gt; Original domContentLoadedEventEnd
    • PASS [expected FAIL] subtest: Reload domContentLoadedEventStart &gt; Original domContentLoadedEventStart
    • PASS [expected FAIL] subtest: Reload loadEventEnd &gt; Original loadEventEnd
    • PASS [expected FAIL] subtest: Reload loadEventStart &gt; Original loadEventStart
  • CRASH [expected OK] /resource-timing/render-blocking-status-link.html (#41664)
  • OK /resource-timing/test_resource_timing.https.html (#25216)
    • PASS [expected FAIL] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (iframe)
    • PASS [expected FAIL] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (link)
  • TIMEOUT /trusted-types/trusted-types-navigation.html?26-30 (#38807)
    • PASS [expected TIMEOUT] subtest: Navigate a window via form-submission with javascript:-urls in report-only mode.
    • PASS [expected NOTRUN] subtest: Navigate a window via form-submission with javascript:-urls w/ default policy in report-only mode.
    • PASS [expected NOTRUN] subtest: Navigate a frame via form-submission with javascript:-urls in enforcing mode.
    • TIMEOUT [expected NOTRUN] subtest: Navigate a frame via form-submission with javascript:-urls w/ default policy in enforcing mode.

      Test timed out
      

  • OK /trusted-types/trusted-types-reporting.html (#43737)
    • FAIL [expected PASS] subtest: Trusted Type violation report: creating a forbidden-but-not-reported policy.

      assert_equals: a single violation reported expected 1 but got 0
      

    • FAIL [expected PASS] subtest: Trusted Type violation report: sample for SVGScriptElement href assignment by setAttribute

      assert_equals: a single violation reported expected 1 but got 0
      

    • FAIL [expected PASS] subtest: Trusted Type violation report: Worker constructor

      assert_equals: a single violation reported expected 1 but got 2
      

Stable unexpected results (1)
  • PASS [expected FAIL] /html/rendering/replaced-elements/images/input-image-content.html

@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown

⚠️ Try run (#25435009743) failed!

On elements (as opposed to pseudo-elements), CSS `content:
url(something)` turns the element into a replaced element like <img> and
ignores the DOM child nodes

Some elements like `<input>` are "widgets" that can have native
appearance: https://drafts.csswg.org/css-ui/#widget

When both apply to the same element like in the added test case, the
native appearance should take priority and the `content` property should
be disregarded: https://drafts.csswg.org/css-ui/#disregard

Previously Servo would do something half-way, generating boxes for the
native appearance (including an absolutely-positioned pseudo-element for
the slider of `<input type=range>`) but not generate fragments and later
expect to find fragments, causing a panic.

Fixes: servo#44540

Co-authored-by: Martin Robinson <mrobinson@igalia.com>
Signed-off-by: Simon Sapin <simon@igalia.com>
@SimonSapin SimonSapin force-pushed the widgets-disregard-content-property branch from aadfc18 to 566d52c Compare May 6, 2026 13:13
@servo-wpt-sync

Copy link
Copy Markdown
Collaborator

📝 Transplanted new upstreamable changes to existing upstream WPT pull request (web-platform-tests/wpt#59691).

Comment on lines -1 to -2

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.

The test harness now considers this test passing but it’s arguably still failing: it says “You should see a red dot” but Servo does not render any red. Both the reftest and the reference use <input type="image"> which Servo presumably does not implement correctly yet

@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label May 6, 2026
@TimvdLippe TimvdLippe added this pull request to the merge queue May 6, 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 May 6, 2026
Merged via the queue into servo:main with commit 4118d6f May 6, 2026
30 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 May 6, 2026
@SimonSapin SimonSapin deleted the widgets-disregard-content-property branch May 7, 2026 10:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

4 participants