Skip to content

Update details to support ::details-content pseudo element.#42107

Merged
mrobinson merged 1 commit intoservo:mainfrom
lukewarlow:details-content
Jan 28, 2026
Merged

Update details to support ::details-content pseudo element.#42107
mrobinson merged 1 commit intoservo:mainfrom
lukewarlow:details-content

Conversation

@lukewarlow
Copy link
Copy Markdown
Member

@lukewarlow lukewarlow commented Jan 23, 2026

Update details to support ::details-content pseudo element.

This is currently not element-backed but will support the basic styling.

This replaces the existing ::-servo-details-content styling.

Testing: WPTs and manually using https://demo.lukewarlow.dev/css-forms.html

image

Stylo PR: servo/stylo#292

@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Jan 23, 2026
.unwrap();
details_content
.upcast::<Node>()
.set_implemented_pseudo_element(PseudoElement::DetailsContent);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Was ::-servo-details-content even working? I don't see where it was assigned to this node.

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.

No I don't think it was, because when I got the new one wired up the rendering was wonky with the original styles.

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.

It seems to have been created in 2016 so probably got partially refactored out at some point.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I definitely didn't wire it up when I implemented the current version of <details> :D

@simonwuelker simonwuelker self-requested a review January 23, 2026 13:45
@lukewarlow lukewarlow force-pushed the details-content branch 2 times, most recently from 62741be to 5aef944 Compare January 23, 2026 13:53
@Loirooriol Loirooriol added the T-linux-wpt Do a try run of the WPT label Jan 23, 2026
@github-actions github-actions bot removed the T-linux-wpt Do a try run of the WPT label Jan 23, 2026
@github-actions
Copy link
Copy Markdown

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

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

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

@github-actions
Copy link
Copy Markdown

⚠️ Try run (#21288678797) cancelled.

@github-actions
Copy link
Copy Markdown

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

Flaky unexpected result (41)
  • OK /FileAPI/file/send-file-form-punctuation.html
    • PASS [expected FAIL] subtest: Upload file-for-upload-in-form-LEFT-CURLY-BRACKET-[{].txt (ASCII) in UTF-8 form
  • OK /FileAPI/file/send-file-form-utf-8.html
    • FAIL [expected PASS] subtest: Upload file-for-upload-in-form-.txt (x-user-defined) in UTF-8 form

      assert_greater_than: file-for-upload-in-form-.txt: multipart form data must have at least 3 lines: "" expected a number greater than 2 but got 0
      

    • FAIL [expected PASS] subtest: Upload file-for-upload-in-form-☺😂.txt (windows-1252) in UTF-8 form

      assert_greater_than: file-for-upload-in-form-☺😂.txt: multipart form data must have at least 3 lines: "" expected a number greater than 2 but got 0
      

  • OK /IndexedDB/idbfactory-open-error-properties.any.html
    • PASS [expected FAIL] subtest: Properties of error event from failed open()
  • OK /IndexedDB/idbfactory-open-error-properties.any.worker.html
    • PASS [expected FAIL] subtest: Properties of error event from failed open()
  • CRASH [expected PASS] /_mozilla/shadow-dom/move-element-with-ua-shadow-tree-crash.html (#39473)
  • CRASH [expected OK] /_webgl/conformance/canvas/drawingbuffer-static-canvas-test.html
  • CRASH [expected OK] /_webgl/conformance/context/context-hidden-alpha.html
  • FAIL [expected PASS] /css/css-backgrounds/background-size-042.html
  • 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 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 generic(khmer-mul) (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted ui-monospace (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted ui-rounded (drawing text in a canvas)
  • OK /custom-elements/form-associated/ElementInternals-setFormValue.html (#29174)
    • PASS [expected FAIL] subtest: Single value - Non-empty name exists
    • PASS [expected FAIL] subtest: Newline normalization - \n in value (formdata)
  • 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 /html/browsers/browsing-the-web/navigating-across-documents/005.html (#27062)
    • PASS [expected FAIL] subtest: Link with onclick navigation and href navigation
  • CRASH [expected OK] /html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html
  • OK /html/browsers/history/the-history-interface/traverse_the_history_3.html (#21383)
    • PASS [expected FAIL] subtest: Multiple history traversals, last would be aborted
  • OK /html/browsers/windows/browsing-context-names/duplicate-name-order.html (#34623)
    • PASS [expected FAIL] subtest: Duplicate name lookup order
  • OK [expected TIMEOUT] /html/semantics/embedded-content/media-elements/src_object_blob.html (#40340)
    • PASS [expected TIMEOUT] subtest: HTMLMediaElement.srcObject blob
  • OK /html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-nav-location-replace-set-src.html (#32697)
    • PASS [expected FAIL] subtest: Navigating iframe loading='lazy' and then setting src: location.replace
  • 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"
      

  • 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/multipart-formdata.window.html (#28725)
    • FAIL [expected PASS] subtest: multipart/form-data: Basic test (formdata event)

      assert_equals: expected "\r\nContent-Disposition: form-data; name=\"basic\"\r\n\r\ntest\r\n--\r\n" but got ""
      

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

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

    • PASS [expected FAIL] subtest: text/plain: 0x00 in name (formdata event)
    • PASS [expected FAIL] subtest: text/plain: double quote in filename (normal form)
  • OK /html/semantics/forms/form-submission-0/urlencoded2.window.html (#28687)
    • FAIL [expected PASS] subtest: application/x-www-form-urlencoded: Basic File test (normal form)

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

    • PASS [expected FAIL] subtest: application/x-www-form-urlencoded: Basic File test (formdata event)
    • PASS [expected FAIL] subtest: application/x-www-form-urlencoded: 0x00 in value (formdata event)
  • ERROR [expected TIMEOUT] /html/semantics/forms/form-submission-target/rel-base-target.html (#40379)
  • CRASH [expected TIMEOUT] /html/semantics/forms/the-select-element/customizable-select/select-appearance-picker-select-border.html
  • OK /html/semantics/scripting-1/the-script-element/execution-timing/077.html (#22139)
    • FAIL [expected PASS] subtest: adding several types of scripts through the DOM and removing some of them confuses scheduler

      assert_array_equals: expected property 1 to be "Script #1 ran" but got "Script #3 ran" (expected array ["Script #2 ran", "Script #1 ran", "Script #3 ran", "Script #4 ran"] got ["Script #2 ran", "Script #3 ran", "Script #4 ran", "Script #1 ran"])
      

  • CRASH [expected OK] /html/webappapis/scripting/reporterror.any.worker.html
  • CRASH [expected OK] /html/webappapis/timers/type-long-settimeout.any.html
  • OK /mixed-content/tentative/autoupgrades/audio-upgrade.https.sub.html (#41697)
    • FAIL [expected PASS] subtest: Audio autoupgraded

      assert_equals: Length expected 1 but got Infinity
      

    • FAIL [expected PASS] subtest: Audio of other host autoupgraded

      assert_equals: Length of other host audio is correct expected 1 but got Infinity
      

  • CRASH [expected OK] /resource-timing/render-blocking-status-link.html (#41664)
  • CRASH [expected OK] /resource-timing/tentative/initiator-url/set-timeout.html
  • OK /touch-events/single-tap-when-touchend-listener-use-sync-xhr.html (#41175)
    • PASS [expected FAIL] subtest: Click event should be fired when touchend opens synchronous XHR
  • CRASH [expected OK] /trusted-types/Element-setAttribute-setAttributeNS-sinks.tentative.html
  • 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!\""
      

  • CRASH [expected OK] /wasm/webapi/esm-integration/wasm-to-wasm-link-error.tentative.html
  • OK /webdriver/tests/classic/delete_all_cookies/delete.py
    • FAIL [expected PASS] subtest: test_null_response_value

      AssertionError: no such window (404): No such window
      

  • OK /webdriver/tests/classic/dismiss_alert/dismiss.py (#39098)
    • FAIL [expected PASS] subtest: test_dismiss_in_popup_window

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

  • OK /webdriver/tests/classic/element_clear/disabled.py
    • FAIL [expected PASS] subtest: test_button[button]

      webdriver.error.NoSuchWindowException: no such window (404): No such window
      

  • OK /webdriver/tests/classic/element_click/bubbling.py
    • FAIL [expected PASS] subtest: test_click_event_bubbles_to_parents

      webdriver.error.NoSuchWindowException: no such window (404): No such window
      

  • OK /webdriver/tests/classic/execute_script/node.py
    • FAIL [expected PASS] subtest: test_detached_shadow_root[top_context]

      webdriver.error.NoSuchWindowException: no such window (404): No such window
      

  • OK /webdriver/tests/classic/get_computed_label/get.py
    • ERROR [expected FAIL] subtest: test_no_browsing_context

      setup error: webdriver.error.NoSuchElementException: no such element (404)
      

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

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

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

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

  • OK /IndexedDB/idbobjectstore_getAll.any.html (#39276)
    • PASS [expected FAIL] subtest: Get all values with transaction.commit()
  • OK /IndexedDB/idbobjectstore_getAll.any.worker.html (#39400)
    • PASS [expected FAIL] subtest: Get all values with transaction.commit()
  • OK /IndexedDB/idbrequest-onupgradeneeded.any.html (#38895)
    • PASS [expected FAIL] subtest: transaction oncomplete ordering relative to open request onsuccess
  • OK /IndexedDB/idbrequest-onupgradeneeded.any.worker.html (#38971)
    • PASS [expected FAIL] subtest: transaction oncomplete ordering relative to open request onsuccess
  • OK /IndexedDB/key-conversion-exceptions.any.html (#39305)
    • FAIL [expected PASS] subtest: IDBCursor continue() method with throwing/invalid keys

      assert_throws_exactly: key conversion with throwing getter should rethrow function "() =&gt; {
            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 "() =&gt; {
            cursor.update(value);
          }" threw object "TypeError: cursor.update is not a function" but we expected it to throw object "getter: throwing from getter"
      

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

      assert_throws_exactly: key conversion with throwing getter should rethrow function "() =&gt; {
            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 "() =&gt; {
            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
      

  • 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 6 more unexpected results...
  • 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 /css/css-cascade/layer-cssom-order-reverse.html (#36094)
    • PASS [expected FAIL] subtest: Delete layer invalidates @font-face
  • OK /css/css-fonts/generic-family-keywords-001.html (#37467)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(nastaliq)
  • OK /fetch/metadata/generated/css-font-face.https.sub.tentative.html (#32732)
    • FAIL [expected PASS] subtest: sec-fetch-user

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

  • OK /fetch/metadata/generated/css-font-face.sub.tentative.html (#34624)
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Not sent to non-trustworthy same-site destination
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination
  • CRASH [expected TIMEOUT] /html/browsers/history/the-location-interface/location_replace_session_history.html (#41896)
  • 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
  • ERROR [expected TIMEOUT] /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 domContentLoadedEventEnd &gt; Original domContentLoadedEventEnd
  • FAIL [expected PASS] /png/apng/acTL-plays-one.html (#41218)
  • TIMEOUT /resource-timing/test_resource_timing.html (#25720)
    • FAIL [expected PASS] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (img)

      assert_equals: expected 7.080000000000005 but got 7.08
      

  • TIMEOUT /resource-timing/test_resource_timing.https.html (#25216)
    • PASS [expected FAIL] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (img)
    • PASS [expected FAIL] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (xmlhttprequest)
  • TIMEOUT /trusted-types/trusted-types-navigation.html?31-35 (#38034)
    • TIMEOUT [expected PASS] subtest: Navigate a frame via form-submission with javascript:-urls in report-only mode.

      Test timed out
      

    • NOTRUN [expected TIMEOUT] subtest: Navigate a frame via form-submission with javascript:-urls w/ default policy in report-only mode.
Stable unexpected results (1)
  • OK /css/css-shadow-parts/pseudo-elements-after-part.html
    • PASS [expected FAIL] subtest: "::part(mypart)::details-content" should be a valid selector

@github-actions
Copy link
Copy Markdown

⚠️ Try run (#21290625117) failed!

@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Jan 23, 2026
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Jan 25, 2026
@jdm jdm added this pull request to the merge queue Jan 26, 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 Jan 26, 2026
@atouchet
Copy link
Copy Markdown
Contributor

Shouldn't this wait for the stylo PR to be merged?

@jdm jdm removed this pull request from the merge queue due to a manual request Jan 26, 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 Jan 26, 2026
@jdm
Copy link
Copy Markdown
Member

jdm commented Jan 26, 2026

Yep, jumped the gun there. Thanks for catching that!

@mrobinson
Copy link
Copy Markdown
Member

The Servo PR should land shortly.

This is currently not element-backed but will support the basic styling.

Signed-off-by: Luke Warlow <[email protected]>
@lukewarlow
Copy link
Copy Markdown
Member Author

Have updated this to use the new stylo revision and then squashed.

@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Jan 28, 2026
@mrobinson mrobinson added this pull request to the merge queue Jan 28, 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 Jan 28, 2026
Merged via the queue into servo:main with commit aad7424 Jan 28, 2026
29 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 Jan 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants