Skip to content

script: Use an implemented pseudo-element to fortype=color ::color-swatch#37427

Merged
Loirooriol merged 5 commits intoservo:mainfrom
stevennovaryo:internal-pseudo-element
Jul 9, 2025
Merged

script: Use an implemented pseudo-element to fortype=color ::color-swatch#37427
Loirooriol merged 5 commits intoservo:mainfrom
stevennovaryo:internal-pseudo-element

Conversation

@stevennovaryo
Copy link
Copy Markdown
Contributor

@stevennovaryo stevennovaryo commented Jun 13, 2025

Implement internal pseudo element, which would be resolved as a "Implemented Pseudo Element" within style computation. This is an concrete element that would has a primary style after the style computation, but could match and style resolved like an pseudo element. Therefore, it would have a different behavior compared to how does pseudos that ServoLayoutNode had. Where they would not have a concrete element behind it. Note that, due to the nature of these pseudo elements residing inside a UA widget, these pseudo elements would therefore not be accessible in JavaScript by default.

This kind of element is required in order to implement the form control pseudo element like ::placeholder, ::color-swatch, ::field-text, etc.

See this docs for more details of the implementation.

Then, the implemented pseudo element is utilized to implement style matching for input type=text.

Servo's side of: servo/stylo#212

Testing: No WPT regression.

Copy link
Copy Markdown
Contributor

@xiaochengh xiaochengh left a comment

Choose a reason for hiding this comment

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

Very good!

Could you also link the Stylo-side PR in the PR description?

@stevennovaryo stevennovaryo changed the title Implement Internal Pseudo Element Implement Internal Pseudo Element For type=color Input Jun 26, 2025
@stevennovaryo stevennovaryo changed the title Implement Internal Pseudo Element For type=color Input Implement Internal Pseudo Element For Input type=color Jun 26, 2025
@stevennovaryo stevennovaryo force-pushed the internal-pseudo-element branch from fffc35e to ab012a5 Compare June 27, 2025 08:22
@stevennovaryo stevennovaryo added the T-linux-wpt Do a try run of the WPT label Jun 27, 2025
@github-actions github-actions bot removed the T-linux-wpt Do a try run of the WPT label Jun 27, 2025
@github-actions
Copy link
Copy Markdown

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

@github-actions
Copy link
Copy Markdown

⚠️ Try run (#15921748309) failed.

@stevennovaryo stevennovaryo force-pushed the internal-pseudo-element branch from ab012a5 to 7598425 Compare June 27, 2025 08:30
@stevennovaryo stevennovaryo added the T-linux-wpt Do a try run of the WPT label Jun 27, 2025
@github-actions github-actions bot removed the T-linux-wpt Do a try run of the WPT label Jun 27, 2025
@github-actions
Copy link
Copy Markdown

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

@github-actions
Copy link
Copy Markdown

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

Flaky unexpected result (16)
  • 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-align/blocks/align-content-block-003.html (#37360)
    • FAIL [expected PASS] subtest: .test 1: start

      assert_equals: 
      <div class="test" style="align-content: start" title="start">
          <div class="in-flow" data-offset-y="35"></div>
          <div class="in-flow">
            <span class="label">START</span>
            <span class="abspos" data-offset-y="20">ABS</span>
            <span class="relpos" data-offset-y="20">REL</span>
            <div class="overflow">OVERFLOW</div>
          </div>
        </div>
      offsetTop expected 20 but got 19
      

    • FAIL [expected PASS] subtest: .test 2: center

      assert_equals: 
      <div class="test" style="align-content: center" title="center">
          <div class="in-flow" data-offset-y="35"></div>
          <div class="in-flow">
            <span class="label">CENTER</span>
            <span class="abspos" data-offset-y="20">ABS</span>
            <span class="relpos" data-offset-y="20">REL</span>
            <div class="overflow">OVERFLOW</div>
          </div>
        </div>
      offsetTop expected 20 but got 19
      

    • FAIL [expected PASS] subtest: .test 3: end

      assert_equals: 
      <div class="test" style="align-content: end" title="end">
          <div class="in-flow" data-offset-y="35"></div>
          <div class="in-flow">
            <span class="label">END</span>
            <span class="abspos" data-offset-y="20">ABS</span>
            <span class="relpos" data-offset-y="20">REL</span>
            <div class="overflow">OVERFLOW</div>
          </div>
        </div>
      offsetTop expected 20 but got 19
      

    • FAIL [expected PASS] subtest: .test 4: baseline

      assert_equals: 
      <div class="test" style="align-content: baseline" title="baseline">
          <div class="in-flow" data-offset-y="35"></div>
          <div class="in-flow">
            <span class="label">BASELINE</span>
            <span class="abspos" data-offset-y="20">ABS</span>
            <span class="relpos" data-offset-y="20">REL</span>
            <div class="overflow">OVERFLOW</div>
          </div>
        </div>
      offsetTop expected 20 but got 19
      

    • FAIL [expected PASS] subtest: .test 5: last baseline

      assert_equals: 
      <div class="test" style="align-content: last baseline" title="last baseline">
          <div class="in-flow" data-offset-y="35"></div>
          <div class="in-flow">
            <span class="label">LAST BASELINE</span>
            <span class="abspos" data-offset-y="20">ABS</span>
            <span class="relpos" data-offset-y="20">REL</span>
            <div class="overflow">OVERFLOW</div>
          </div>
        </div>
      offsetTop expected 20 but got 19
      

    • FAIL [expected PASS] subtest: .test 6: flex-start

      assert_equals: 
      <div class="test" style="align-content: flex-start" title="flex-start">
          <div class="in-flow" data-offset-y="35"></div>
          <div class="in-flow">
            <span class="label">FLEX-START</span>
            <span class="abspos" data-offset-y="20">ABS</span>
            <span class="relpos" data-offset-y="20">REL</span>
            <div class="overflow">OVERFLOW</div>
          </div>
        </div>
      offsetTop expected 20 but got 19
      

    • FAIL [expected PASS] subtest: .test 7: flex-end

      assert_equals: 
      <div class="test" style="align-content: flex-end" title="flex-end">
          <div class="in-flow" data-offset-y="35"></div>
          <div class="in-flow">
            <span class="label">FLEX-END</span>
            <span class="abspos" data-offset-y="20">ABS</span>
            <span class="relpos" data-offset-y="20">REL</span>
            <div class="overflow">OVERFLOW</div>
          </div>
        </div>
      offsetTop expected 20 but got 19
      

    • FAIL [expected PASS] subtest: .test 8: unsafe start

      assert_equals: 
      <div class="test" style="align-content: unsafe start" title="unsafe start">
          <div class="in-flow" data-offset-y="35"></div>
          <div class="in-flow">
            <span class="label">UNSAFE START</span>
            <span class="abspos" data-offset-y="20">ABS</span>
            <span class="relpos" data-offset-y="20">REL</span>
            <div class="overflow">OVERFLOW</div>
          </div>
        </div>
      offsetTop expected 20 but got 19
      

    • FAIL [expected PASS] subtest: .test 11: safe start

      assert_equals: 
      <div class="test" style="align-content: safe start" title="safe start">
          <div class="in-flow" data-offset-y="35"></div>
          <div class="in-flow">
            <span class="label">SAFE START</span>
            <span class="abspos" data-offset-y="20">ABS</span>
            <span class="relpos" data-offset-y="20">REL</span>
            <div class="overflow">OVERFLOW</div>
          </div>
        </div>
      offsetTop expected 20 but got 19
      

    • FAIL [expected PASS] subtest: .test 12: safe center

      assert_equals: 
      <div class="test" style="align-content: safe center" title="safe center">
          <div class="in-flow" data-offset-y="35"></div>
          <div class="in-flow">
            <span class="label">SAFE CENTER</span>
            <span class="abspos" data-offset-y="20">ABS</span>
            <span class="relpos" data-offset-y="20">REL</span>
            <div class="overflow">OVERFLOW</div>
          </div>
        </div>
      offsetTop expected 20 but got 19
      

    • And 5 more unexpected results...
  • OK /css/css-fonts/generic-family-keywords-001.html (#37467)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(fangsong)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(khmer-mul)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(nastaliq)
  • OK [expected ERROR] /fetch/fetch-later/quota/same-origin-iframe/small-payload.tentative.https.window.html (#35210)
  • OK /fetch/metadata/generated/css-font-face.https.sub.tentative.html (#32732)
    • PASS [expected FAIL] subtest: sec-fetch-site - Cross-Site -> Same Origin
    • PASS [expected FAIL] subtest: sec-fetch-user
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Same site
  • OK /fetch/metadata/generated/css-font-face.sub.tentative.html (#34624)
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination
  • OK /html/browsers/browsing-the-web/navigating-across-documents/empty-iframe-load-event.html (#29066)
    • FAIL [expected PASS] subtest: Check execution order from nested timeout

      assert_equals: Expected nested setTimeout to run second expected true but got false
      

    • FAIL [expected PASS] subtest: Check execution order on load handler

      assert_equals: Expected onload to run first expected false but got true
      

  • 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/interaction/focus/the-autofocus-attribute/update-the-rendering.html (#24145)
    • FAIL [expected TIMEOUT] subtest: "Flush autofocus candidates" should be happen before a scroll event and animation frame callbacks

      assert_array_equals: animationFrame lengths differ, expected array ["autofocus", "scroll", "animationFrame"] length 3, got ["animationFrame"] length 1
      

  • CRASH [expected TIMEOUT] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-1.html (#22647)
  • CRASH [expected TIMEOUT] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-3.html (#24057)
  • CRASH [expected TIMEOUT] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html (#24066)
  • OK /html/semantics/forms/form-submission-0/multipart-formdata.window.html (#28725)
    • PASS [expected FAIL] subtest: multipart/form-data: backslash in name (formdata event)
  • OK /html/semantics/scripting-1/the-script-element/module/dynamic-import/blob-url.any.worker.html (#33909)
    • FAIL [expected PASS] subtest: Revoking a blob URL immediately after calling import will not fail

      promise_test: Unhandled rejection with value: object "TypeError: Dynamic import failed"
      

  • OK [expected TIMEOUT] /webmessaging/with-ports/017.html (#24486)
    • PASS [expected TIMEOUT] subtest: origin of the script that invoked the method, about:blank
  • OK [expected ERROR] /workers/constructors/Worker/Worker-constructor.html (#22991)
Stable unexpected results that are known to be intermittent (16)
  • TIMEOUT /FileAPI/url/url-in-tags-revoke.window.html (#19978)
    • PASS [expected TIMEOUT] subtest: Fetching a blob URL immediately before revoking it works in <script> tags.
  • FAIL [expected PASS] /_mozilla/mozilla/sslfail.html (#10760)
  • FAIL [expected PASS] /css/css-grid/grid-items/grid-auto-margin-and-replaced-item-001.html (#37162)
  • FAIL [expected PASS] /css/css-sizing/dynamic-available-size-iframe.html (#31559)
  • OK /html/browsers/browsing-the-web/navigating-across-documents/009.html (#24456)
    • FAIL [expected PASS] subtest: Link with onclick form submit to javascript url with document.write and href navigation

      assert_array_equals: expected property 1 to be "href" but got "click" (expected array ["write", "href"] got ["write", "click"])
      

  • OK /html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-nosrc.html (#34819)
    • FAIL [expected PASS] subtest: form submission

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

  • OK /html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-iframe-contentWindow.html (#28681)
    • PASS [expected FAIL] subtest: load & pageshow events do not fire on contentWindow of <iframe> element created with src=''
    • PASS [expected FAIL] subtest: load & pageshow events do not fire on contentWindow of <iframe> element created with src='about:blank'
  • OK /html/browsers/history/the-history-interface/traverse_the_history_4.html (#21383)
    • PASS [expected FAIL] subtest: Multiple history traversals, last would be aborted
  • TIMEOUT /html/interaction/focus/the-autofocus-attribute/supported-elements.html (#24145)
    • FAIL [expected TIMEOUT] subtest: Element with tabindex should support autofocus

      assert_equals: expected "SPAN" but got "BODY"
      

    • PASS [expected NOTRUN] subtest: Non-HTMLElement should not support autofocus
    • TIMEOUT [expected NOTRUN] subtest: Host element with delegatesFocus should support autofocus

      Test timed out
      

  • TIMEOUT /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-2.html (#22154)
    • NOTRUN [expected FAIL] subtest: Check that popups from a sandboxed iframe do not escape the sandbox
  • OK /html/semantics/forms/form-submission-0/urlencoded2.window.html (#28687)
    • PASS [expected FAIL] subtest: application/x-www-form-urlencoded: double quote in value (formdata event)
  • OK /navigation-timing/test-navigation-type-reload.html (#33334)
    • PASS [expected FAIL] subtest: Reload domContentLoadedEventEnd > Original domContentLoadedEventEnd
    • PASS [expected FAIL] subtest: Reload domContentLoadedEventStart > Original domContentLoadedEventStart
    • PASS [expected FAIL] subtest: Reload domInteractive > Original domInteractive
    • PASS [expected FAIL] subtest: Reload fetchStart > Original fetchStart
  • OK /preload/preload-error.sub.html (#37177)
    • PASS [expected FAIL] subtest: CORS (style): main
    • PASS [expected FAIL] subtest: success (script): main
    • FAIL [expected PASS] subtest: CORS (script): main

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

    • PASS [expected FAIL] subtest: 404 (xhr): main
    • PASS [expected FAIL] subtest: CORS (xhr): main
    • PASS [expected FAIL] subtest: Decode-error (style): main
    • FAIL [expected PASS] subtest: MIME-error (script): main

      assert_greater_than: http://web-platform.test:8000/preload/resources/dummy.css?pipe=header%28Content-Type%2Ctext%2Fnotjavascript%29&label=script should be loaded expected a number greater than 0 but got 0
      

  • OK /resize-observer/eventloop.html (#33599)
    • PASS [expected FAIL] subtest: test0: multiple notifications inside same event loop
  • OK /webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html (#22849)
    • FAIL [expected PASS] subtest: X Stitched sine-wave buffers at sample rate 43800 does not equal [0,0.06264832615852356,0.12505052983760834,0.18696144223213196,0.24813786149024963,0.308339387178421,0.36732959747314453,0.4248766601085663,0.480754554271698,0.5347436666488647,0.5866320133209229,0.6362156271934509,0.6832997798919678,0.7276994585990906,0.7692402601242065,0.8077589869499207...] with an element-wise tolerance of {"absoluteThreshold":0.0038986,"relativeThreshold":0}. Index Actual Expected AbsError RelError Test threshold [14650] 2.5596454060988547e-39 8.6956524848937988e-1 8.6956524848937988e-1 1.0000000000000000e+0 3.8985999999999999e-3 [14651] 3.0547976493835449e-1 8.9879405498504639e-1 5.9331429004669189e-1 6.6012262403823208e-1 3.8985999999999999e-3 Max AbsError of 8.6956524848937988e-1 at index of 14650. Max RelError of 1.0000000000000000e+0 at index of 14650.

      assert_true: expected true got false
      

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

      Test timed out
      

@github-actions
Copy link
Copy Markdown

✨ Try run (#15921880207) succeeded.

@stevennovaryo stevennovaryo requested a review from Loirooriol July 4, 2025 09:48
Signed-off-by: stevennovaryo <[email protected]>
@mrobinson mrobinson changed the title Implement Internal Pseudo Element For Input type=color script: Use an implemented pseudo-element to fortype=color ::color-swatch Jul 9, 2025
Signed-off-by: stevennovaryo <[email protected]>
Signed-off-by: stevennovaryo <[email protected]>
Signed-off-by: stevennovaryo <[email protected]>
@Loirooriol Loirooriol force-pushed the internal-pseudo-element branch from 896402a to 523edea Compare July 9, 2025 15:00
@Loirooriol Loirooriol enabled auto-merge July 9, 2025 15:01
@Loirooriol Loirooriol added this pull request to the merge queue Jul 9, 2025
Merged via the queue into servo:main with commit 378c464 Jul 9, 2025
21 checks passed
github-merge-queue bot pushed a commit that referenced this pull request Jul 23, 2025
)

Depends on #37427.

In addition to the changes introduced by
#37065, there are several performance
improvements and nits as follows:
- Use the internal pseudo element for style matching, this will reduce
the performance regression by ~66%.
- Manual construction of the `Text` node inside a text container. This
is followed by the modification of the inner `Text` node instead of
using `SetTextContent` which is more expensive.
- Use `implemented_pseudo_element` instead of
`text_control_inner_editor` `NodeFlag` to handle the special cases that
these elements should follow, specifically the:
  - focus delegation workaround;
  - selections; and
  - line height resolving.
- More documentation.

Servo's side of: servo/stylo#217

Testing: No new unexpected WPT failure, except for the one introduced by
#37065.
Fixes: #36307 #37205

---------

Signed-off-by: stevennovaryo <[email protected]>
github-merge-queue bot pushed a commit that referenced this pull request Jul 25, 2025
Depend on: 
- #37427
- #37483

Utilize input `type=text` for the display of all textual input. In
which, consist of
https://html.spec.whatwg.org/#the-input-element-as-a-text-entry-widget
and
https://html.spec.whatwg.org/#the-input-element-as-domain-specific-widgets
inputs.

For `password`, `url`, `tel`, and, `email` input, the appearance of
input container is exactly the same as the `text` input. Other types of
textual input simply extends `text` input by adding extra components
inside the container.

Testing: Servo textual input appearance WPT.

---------

Signed-off-by: stevennovaryo <[email protected]>
Signed-off-by: Jo Steven Novaryo <[email protected]>
minghuaw pushed a commit to minghuaw/servo that referenced this pull request Aug 1, 2025
Depend on: 
- servo#37427
- servo#37483

Utilize input `type=text` for the display of all textual input. In
which, consist of
https://html.spec.whatwg.org/#the-input-element-as-a-text-entry-widget
and
https://html.spec.whatwg.org/#the-input-element-as-domain-specific-widgets
inputs.

For `password`, `url`, `tel`, and, `email` input, the appearance of
input container is exactly the same as the `text` input. Other types of
textual input simply extends `text` input by adding extra components
inside the container.

Testing: Servo textual input appearance WPT.

---------

Signed-off-by: stevennovaryo <[email protected]>
Signed-off-by: Jo Steven Novaryo <[email protected]>
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.

4 participants