Skip to content

layout: Add placeholders for text carets after finishing a line#44370

Merged
mrobinson merged 1 commit into
servo:mainfrom
mrobinson:better-handling-of-empty-lines-in-textarea
Apr 22, 2026
Merged

layout: Add placeholders for text carets after finishing a line#44370
mrobinson merged 1 commit into
servo:mainfrom
mrobinson:better-handling-of-empty-lines-in-textarea

Conversation

@mrobinson

@mrobinson mrobinson commented Apr 20, 2026

Copy link
Copy Markdown
Member

Instead of readily adding a strut for text carets when processing a line
break, wait until a line is finished. This allows placing a strut on the
final line. In addition to fixing #41338 this will also make it possible
to handle line breaks as a separate kind of text run item so that we can
preserve shaping results between layouts.

In addition, some changes are made in script to ensure that these placeholders
are never placed for non-textual input elements.

Testing: This change adds a Servo-specific WPT test. Caret rendering isn't specified.
Fixes: #41338

@mrobinson mrobinson requested a review from Loirooriol as a code owner April 20, 2026 09:48
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Apr 20, 2026
@mrobinson mrobinson added the T-linux-wpt Do a try run of the WPT label Apr 20, 2026
@github-actions github-actions Bot removed the T-linux-wpt Do a try run of the WPT label Apr 20, 2026
@github-actions

Copy link
Copy Markdown

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

@mrobinson mrobinson marked this pull request as draft April 20, 2026 10:09
@github-actions

Copy link
Copy Markdown

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

Flaky unexpected result (24)
  • OK /IndexedDB/transaction-create_in_versionchange.any.html
    • FAIL [expected PASS] subtest: Attempt to create new transactions inside a versionchange transaction

      assert_array_equals: events expected property 4 to be "open_rq.success: [object IDBDatabase]" but got "complete_count.success: 2" (expected array ["versionchange_add.success: 1", "versionchange_count.success: 0", "versionchange_add2.success: 2", "versionchange_txn.complete", "open_rq.success: [object IDBDatabase]", "complete_count.success: 2", "complete2_get.success: 1"] got ["versionchange_add.success: 1", "versionchange_count.success: 0", "versionchange_add2.success: 2", "versionchange_txn.complete", "complete_count.success: 2", "open_rq.success: [object IDBDatabase]", "complete2_get.success: 1"])
      

  • ERROR [expected OK] /_mozilla/mozilla/input-value.html
  • CRASH [expected OK] /content-security-policy/meta/sandbox-iframe.html (#43478)
  • FAIL [expected PASS] /css/css-ui/appearance-menulist-button-001.html
  • CRASH [expected OK] /fetch/api/credentials/cookies.any.worker.html
  • ERROR /fetch/metadata/generated/serviceworker.https.sub.html (#36247)
    • PASS [expected FAIL] subtest: sec-fetch-site - Same origin, no options - registration
  • OK /fetch/metadata/window-open.https.sub.html (#40339)
    • FAIL [expected PASS] subtest: Same-site window, forced, reloaded

      The operation is insecure.
      

  • CRASH [expected OK] /html/browsers/browsing-the-web/history-traversal/event-order/before-load-hash.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
  • TIMEOUT /html/semantics/embedded-content/media-elements/preserves-pitch.html (#40352)
    • PASS [expected TIMEOUT] subtest: Speed-ups should not change the pitch when preservesPitch=true
    • PASS [expected NOTRUN] subtest: Slow-downs should not change the pitch when preservesPitch=true
    • TIMEOUT [expected NOTRUN] subtest: Speed-ups should change the pitch when preservesPitch=false

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

  • FAIL [expected PASS] /png/apng/fcTL-dispose-before-region-background.html
  • TIMEOUT [expected OK] /pointerevents/compat/pointerevent_touch-action_two-finger_interaction.html
    • 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/test_resource_timing.https.html (#25216)
    • PASS [expected FAIL] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (iframe)
  • 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
  • OK [expected TIMEOUT] /trusted-types/trusted-types-navigation.html?06-10 (#37920)
    • 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 [expected TIMEOUT] /trusted-types/trusted-types-navigation.html?31-35 (#38034)
    • PASS [expected TIMEOUT] subtest: Navigate a frame via form-submission with javascript:-urls w/ default policy in report-only mode.
    • FAIL [expected NOTRUN] subtest: Navigate a window via form-submission with javascript:-urls w/ a default policy throwing an exception in enforcing mode.

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

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

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

    • FAIL [expected NOTRUN] subtest: Navigate a window via form-submission with javascript:-urls w/ a default policy making the URL invalid in enforcing mode.

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

  • OK /webdriver/tests/classic/add_cookie/user_prompts.py
    • FAIL [expected PASS] subtest: test_accept[alert-None]

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

  • OK /webdriver/tests/classic/element_clear/user_prompts.py
    • FAIL [expected PASS] subtest: test_accept[alert-None]

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

  • OK /webdriver/tests/classic/find_elements_from_shadow_root/user_prompts.py
    • FAIL [expected PASS] subtest: test_accept[alert-None]

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

  • TIMEOUT [expected OK] /websockets/Close-readyState-Closed.any.html?wss
Stable unexpected results that are known to be intermittent (21)
  • TIMEOUT /FileAPI/url/url-in-tags-revoke.window.html (#19978)
    • TIMEOUT [expected PASS] subtest: Fetching a blob URL immediately before revoking it works in <script> tags.

      Test timed out
      

  • TIMEOUT [expected OK] /_mozilla/mozilla/window-resize-event.html (#36741)
    • TIMEOUT [expected PASS] subtest: Popup onresize event fires after resizeTo

      Test timed out
      

  • OK [expected ERROR] /_webgl/conformance2/textures/misc/origin-clean-conformance-offscreencanvas.html (#29589)
    • PASS [expected NOTRUN] subtest: Overall test
  • ERROR /_webgl/conformance2/textures/misc/tex-3d-size-limit.html (#44280)
    • PASS [expected FAIL] subtest: WebGL test #36
    • PASS [expected FAIL] subtest: WebGL test #37
    • PASS [expected FAIL] subtest: WebGL test #39
    • PASS [expected FAIL] subtest: WebGL test #40
    • PASS [expected FAIL] subtest: WebGL test #41
    • PASS [expected FAIL] subtest: WebGL test #44
    • FAIL [expected PASS] subtest: WebGL test #77

      assert_true: successfullyParsed should be true (of type boolean). Was undefined (of type undefined). expected true got false
      

  • OK /css/css-cascade/layer-font-face-override.html (#35935)
    • PASS [expected FAIL] subtest: @font-face override update with appended sheet 1
    • PASS [expected FAIL] subtest: @font-face override update with appended sheet 2
  • 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 generic(nastaliq) (drawing text in a canvas)
    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted ui-serif (drawing text in a canvas)

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

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

      assert_equals: unquoted ui-sans-serif does not match @font-face rule expected 40 but got 125
      

  • TIMEOUT /fetch/metadata/generated/css-images.https.sub.tentative.html (#42229)
    • FAIL [expected PASS] subtest: content sec-fetch-site - Same-Origin -> Same Origin

      assert_unreached: Reached unreachable code
      

  • TIMEOUT [expected ERROR] /html/browsers/browsing-the-web/history-traversal/pageswap/pageswap-initial-navigation.html (#40387)
  • 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.html (#44028)
  • 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 TIMEOUT] /html/browsers/history/the-location-interface/location_replace_session_history.html (#41896)
  • OK /html/browsers/windows/embedded-opener-remove-frame.html (#23867)
    • FAIL [expected PASS] subtest: opener of discarded auxiliary browsing context

      assert_object_equals: property "get" expected function "function opener() {
          [native code]
      }" got function "function opener() {
          [native code]
      }"
      

  • TIMEOUT /html/interaction/focus/the-autofocus-attribute/supported-elements.html (#24145)
    • TIMEOUT [expected PASS] subtest: Non-HTMLElement should not support autofocus

      Test timed out
      

  • OK /html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-1.html (#39694)
    • FAIL [expected PASS] subtest: Meta refresh is blocked by the allow-scripts sandbox flag at its creation time, not when refresh comes due

      uncaught exception: Error: assert_unreached: The iframe from which the meta came from must not refresh Reached unreachable code
      

  • OK /html/semantics/scripting-1/the-script-element/module/dynamic-import/blob-url.any.html (#33948)
    • 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"
      

  • OK /mixed-content/tentative/autoupgrades/mixed-content-cors.https.sub.html (#41123)
    • PASS [expected FAIL] subtest: Cross-Origin video should get upgraded even if CORS is set
  • OK /resource-timing/test_resource_timing.html (#25720)
    • PASS [expected FAIL] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (xmlhttprequest)
  • TIMEOUT [expected OK] /trusted-types/trusted-types-navigation.html?26-30 (#38807)
    • TIMEOUT [expected PASS] subtest: Navigate a window via form-submission with javascript:-urls in report-only mode.

      Test timed out
      

    • NOTRUN [expected PASS] subtest: Navigate a window via form-submission with javascript:-urls w/ default policy in report-only mode.
    • NOTRUN [expected PASS] subtest: Navigate a frame via form-submission with javascript:-urls in enforcing mode.
    • NOTRUN [expected PASS] subtest: Navigate a frame via form-submission with javascript:-urls w/ default policy in enforcing mode.
  • OK /trusted-types/trusted-types-reporting.html (#43737)
    • PASS [expected FAIL] subtest: Trusted Type violation report: creating a forbidden-but-not-reported policy.
    • PASS [expected FAIL] subtest: Trusted Type violation report: sample for SVGScriptElement href assignment by setAttribute
  • TIMEOUT [expected OK] /webstorage/localstorage-about-blank-3P-iframe-opens-3P-window.partitioned.html (#29053)
    • TIMEOUT [expected FAIL] subtest: StorageKey: test 3P about:blank window opened from a 3P iframe

      Test timed out
      

Stable unexpected results (42)
  • FAIL [expected PASS] /_mozilla/appearance/input-range.html
  • FAIL [expected PASS] /_mozilla/css/quirks-mode-br-line-height.html
  • PASS [expected FAIL] /contenteditable/synthetic-height.html
  • FAIL [expected PASS] /css/CSS2/box-display/block-in-inline-007.xht
  • FAIL [expected PASS] /css/CSS2/css1/c548-ln-ht-000.xht
  • FAIL [expected PASS] /css/CSS2/floats-clear/floats-031.xht
  • FAIL [expected PASS] /css/CSS2/floats-clear/floats-clear-multicol-001.html
  • FAIL [expected PASS] /css/CSS2/floats-clear/floats-clear-multicol-balancing-001.html
  • FAIL [expected PASS] /css/CSS2/generated-content/content-173.xht
  • FAIL [expected PASS] /css/CSS2/positioning/abspos-block-level-001.html
  • FAIL [expected PASS] /css/CSS2/text/text-indent-on-blank-line-rtl-left-align.html
  • FAIL [expected PASS] /css/CSS2/text/white-space-008.xht
  • FAIL [expected PASS] /css/CSS2/text/white-space-pre-001.xht
  • FAIL [expected PASS] /css/CSS2/text/white-space-pre-002.xht
  • FAIL [expected PASS] /css/CSS2/visudet/inline-block-baseline-016.html
  • FAIL [expected PASS] /css/css-flexbox/abspos/flex-abspos-staticpos-fallback-justify-content-001.html
  • FAIL [expected PASS] /css/css-flexbox/flexitem-stretch-range.html
  • OK /css/css-grid/alignment/grid-align-baseline.html
    • FAIL [expected PASS] subtest: Additional Check 3

      assert_equals: baseline4 and baseline5 should be top aligned with each other; expected 0 but got 34
      

  • PASS [expected FAIL] /css/css-sizing/range-percent-intrinsic-size-2.html
  • PASS [expected FAIL] /css/css-sizing/range-percent-intrinsic-size-2a.html
  • FAIL [expected PASS] /css/css-text/line-breaking/line-breaking-replaced-004.html
  • FAIL [expected PASS] /css/css-text/line-breaking/line-breaking-replaced-005.html
  • FAIL [expected PASS] /css/css-text/overflow-wrap/overflow-wrap-anywhere-004.html
  • FAIL [expected PASS] /css/css-text/overflow-wrap/overflow-wrap-break-word-004.html
  • FAIL [expected PASS] /css/css-text/overflow-wrap/overflow-wrap-break-word-006.html
  • FAIL [expected PASS] /css/css-text/text-indent/text-indent-length-001.html
  • FAIL [expected PASS] /css/css-text/text-indent/text-indent-length-002.html
  • FAIL [expected PASS] /css/css-text/white-space/break-spaces-newline-011.html
  • FAIL [expected PASS] /css/css-text/white-space/break-spaces-newline-015.html
  • FAIL [expected PASS] /css/css-text/white-space/break-spaces-newline-016.html
  • FAIL [expected PASS] /css/css-text/white-space/break-spaces-tab-003.html
  • FAIL [expected PASS] /css/css-text/white-space/break-spaces-tab-004.html
  • FAIL [expected PASS] /css/css-text/white-space/pre-wrap-010.html
  • FAIL [expected PASS] /css/css-text/white-space/pre-wrap-016.html
  • FAIL [expected PASS] /css/css-text/white-space/pre-wrap-float-001.html
  • FAIL [expected PASS] /css/css-text/white-space/pre-wrap-leading-spaces-010.html
  • PASS [expected FAIL] /css/css-text/white-space/white-space-collapse-preserve-breaks-001.xht
  • FAIL [expected PASS] /css/css-text/word-break/word-break-min-content-003.html
  • OK /editing/run/delete-to-make-editing-host-empty.html
    • PASS [expected FAIL] subtest: "<div contenteditable></div>" and "<div contenteditable><br></div>" should have almost same height
  • OK /html/rendering/widgets/baseline-alignment-and-overflow.tentative.html
    • FAIL [expected PASS] subtest: <input type="range" style="overflow: visible; appearance: auto;">

      assert_equals: <span>.offsetTop expected 71 but got 20
      

    • FAIL [expected PASS] subtest: <input type="range" style="overflow: visible; appearance: none;">

      assert_equals: <span>.offsetTop expected 71 but got 20
      

  • ERROR [expected TIMEOUT] /pointerevents/pointerevent_lostpointercapture_for_disconnected_shadow_host.html
  • FAIL [expected PASS] /quirks/line-height-preserved-segment-break.html

@github-actions

Copy link
Copy Markdown

⚠️ Try run (#24659858511) failed!

@mrobinson mrobinson force-pushed the better-handling-of-empty-lines-in-textarea branch from fc7b48d to 2d89293 Compare April 20, 2026 11:36
@mrobinson mrobinson changed the title layout: Add struts for text carets after finishing a line layout: Add placeholders for text carets after finishing a line Apr 20, 2026
@mrobinson mrobinson added the T-linux-wpt Do a try run of the WPT label Apr 20, 2026
@github-actions github-actions Bot removed the T-linux-wpt Do a try run of the WPT label Apr 20, 2026
@github-actions

Copy link
Copy Markdown

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

@github-actions

Copy link
Copy Markdown

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

Flaky unexpected result (39)
  • TIMEOUT /FileAPI/url/url-in-tags-revoke.window.html (#19978)
    • TIMEOUT [expected PASS] subtest: Fetching a blob URL immediately before revoking it works in <script> tags.

      Test timed out
      

  • OK /IndexedDB/idbdatabase_deleteObjectStore.any.html (#43823)
    • PASS [expected FAIL] subtest: Deleted object store's name should be removed from database's list. Attempting to use a deleted IDBObjectStore should throw an InvalidStateError
  • 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
      

  • CRASH [expected OK] /content-security-policy/meta/sandbox-iframe.html (#43478)
  • FAIL [expected PASS] /css/css-backgrounds/border-image-repeat-space-9.html
  • OK /css/css-cascade/layer-cssom-order-reverse.html (#36094)
    • FAIL [expected PASS] subtest: Delete layer invalidates @font-face

      assert_equals: expected "220px" but got "133px"
      

  • OK /css/css-cascade/layer-font-face-override.html (#35935)
    • PASS [expected FAIL] subtest: @font-face override update with appended sheet 1
    • PASS [expected FAIL] subtest: @font-face override update with appended sheet 2
  • FAIL [expected PASS] /css/css-ui/appearance-button-001.html
  • CRASH [expected OK] /fetch/api/cors/cors-redirect.any.html
  • CRASH [expected ERROR] /fetch/api/redirect/redirect-method.any.sharedworker.html
  • TIMEOUT [expected ERROR] /html/browsers/browsing-the-web/history-traversal/pageswap/pageswap-initial-navigation.html (#40387)
  • OK /html/browsers/browsing-the-web/navigating-across-documents/005.html (#27062)
    • PASS [expected FAIL] subtest: Link with onclick navigation and href navigation
  • 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-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
  • CRASH [expected OK] /html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-popup.html
  • OK [expected TIMEOUT] /html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit.html (#44028)
  • CRASH [expected TIMEOUT] /html/browsers/history/the-history-interface/002.html (#12580)
  • OK /html/browsers/windows/browsing-context-names/duplicate-name-order.html (#34623)
    • PASS [expected FAIL] subtest: Duplicate name lookup order
  • OK /html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-1.html (#39694)
    • FAIL [expected PASS] subtest: Meta refresh is blocked by the allow-scripts sandbox flag at its creation time, not when refresh comes due

      uncaught exception: Error: assert_unreached: The iframe from which the meta came from must not refresh Reached unreachable code
      

  • 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 /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"])
      

  • OK /html/semantics/scripting-1/the-script-element/module/choice-of-error-1.html (#44058)
    • PASS [expected FAIL] subtest: Parse errors in different files should be reported depending on different roots
  • 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: 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
      

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

      assert_true: onload must be called expected true got false
      

  • FAIL [expected PASS] /png/apng/fcTL-dispose-in-region-previous.html (#41410)
  • TIMEOUT [expected OK] /pointerevents/compat/pointerevent_touch-action_two-finger_interaction.html
    • 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'
  • ERROR [expected OK] /resource-timing/cors-preflight.any.html (#28694)
  • OK [expected CRASH] /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 (img)
  • 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?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 [expected TIMEOUT] /trusted-types/trusted-types-navigation.html?06-10 (#37920)
    • 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!\""
      

  • TIMEOUT [expected OK] /trusted-types/trusted-types-navigation.html?26-30 (#38807)
    • TIMEOUT [expected PASS] subtest: Navigate a window via form-submission with javascript:-urls in report-only mode.

      Test timed out
      

    • NOTRUN [expected PASS] subtest: Navigate a window via form-submission with javascript:-urls w/ default policy in report-only mode.
    • NOTRUN [expected PASS] subtest: Navigate a frame via form-submission with javascript:-urls in enforcing mode.
    • NOTRUN [expected PASS] subtest: Navigate a frame via form-submission with javascript:-urls w/ default policy in enforcing mode.
  • 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.
  • OK /visual-viewport/resize-event-order.html (#41981)
    • PASS [expected FAIL] subtest: Popup: DOMWindow resize fired before VisualViewport.
  • TIMEOUT [expected OK] /webstorage/localstorage-about-blank-3P-iframe-opens-3P-window.partitioned.html (#29053)
    • TIMEOUT [expected FAIL] subtest: StorageKey: test 3P about:blank window opened from a 3P iframe

      Test timed out
      

Stable unexpected results that are known to be intermittent (17)
  • TIMEOUT [expected OK] /_mozilla/mozilla/window-resize-event.html (#36741)
    • TIMEOUT [expected PASS] subtest: Popup onresize event fires after resizeTo

      Test timed out
      

  • OK [expected ERROR] /_webgl/conformance2/textures/misc/origin-clean-conformance-offscreencanvas.html (#29589)
    • PASS [expected NOTRUN] subtest: Overall test
  • ERROR /_webgl/conformance2/textures/misc/tex-3d-size-limit.html (#44280)
    • PASS [expected FAIL] subtest: WebGL test #36
    • PASS [expected FAIL] subtest: WebGL test #37
    • PASS [expected FAIL] subtest: WebGL test #39
    • PASS [expected FAIL] subtest: WebGL test #40
    • PASS [expected FAIL] subtest: WebGL test #41
    • PASS [expected FAIL] subtest: WebGL test #44
    • FAIL [expected PASS] subtest: WebGL test #77

      assert_true: successfullyParsed should be true (of type boolean). Was undefined (of type undefined). expected true got false
      

  • OK /css/css-fonts/generic-family-keywords-001.html (#37467)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(kai)
  • 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 math (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(kai) (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(nastaliq) (drawing text in a canvas)
  • OK /dom/nodes/moveBefore/iframe-document-preserve.window.html (#43152)
    • FAIL [expected PASS] subtest: moveBefore(): cross-origin iframe is preserved: remove new parent

      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

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

  • OK [expected ERROR] /fetch/fetch-later/quota/same-origin-iframe/sandboxed-iframe.https.window.html (#41704)
  • OK /fetch/metadata/generated/css-font-face.https.sub.tentative.html (#32732)
    • PASS [expected FAIL] subtest: sec-fetch-mode
    • PASS [expected FAIL] subtest: sec-fetch-dest
  • OK /fetch/metadata/generated/css-font-face.sub.tentative.html (#34624)
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination
  • TIMEOUT /fetch/metadata/generated/css-images.https.sub.tentative.html (#42229)
    • FAIL [expected PASS] subtest: content sec-fetch-site - Same-Origin -> Same Origin

      assert_unreached: Reached unreachable code
      

  • 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])
      

  • OK /html/browsers/history/the-history-interface/traverse_the_history_5.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])
      

  • TIMEOUT /html/interaction/focus/the-autofocus-attribute/supported-elements.html (#24145)
    • TIMEOUT [expected NOTRUN] subtest: Host element with delegatesFocus should support autofocus

      Test timed out
      

  • TIMEOUT /html/semantics/embedded-content/media-elements/preserves-pitch.html (#40352)
    • PASS [expected TIMEOUT] subtest: Speed-ups should not change the pitch when preservesPitch=true
    • PASS [expected NOTRUN] subtest: Slow-downs should not change the pitch when preservesPitch=true
    • TIMEOUT [expected NOTRUN] subtest: Speed-ups should change the pitch when preservesPitch=false

      Test timed out
      

  • OK /mixed-content/tentative/autoupgrades/mixed-content-cors.https.sub.html (#41123)
    • PASS [expected FAIL] subtest: Cross-Origin video should get upgraded even if CORS is set
  • 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
  • OK /trusted-types/trusted-types-reporting.html (#43737)
    • PASS [expected FAIL] subtest: Trusted Type violation report: creating a forbidden-but-not-reported policy.
    • PASS [expected FAIL] subtest: Trusted Type violation report: sample for custom element assignment

@github-actions

Copy link
Copy Markdown

✨ Try run (#24664354334) succeeded.

@mrobinson mrobinson marked this pull request as ready for review April 20, 2026 12:34
@mrobinson mrobinson requested a review from gterzian as a code owner April 20, 2026 12:35
@mrobinson mrobinson force-pushed the better-handling-of-empty-lines-in-textarea branch from 2d89293 to d730cd9 Compare April 21, 2026 10:49

@SimonSapin SimonSapin 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.

A few questions but looks good overall

Comment on lines +2002 to +2003
self.is_textual_or_password
.set(self.input_type().is_textual_or_password());

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.

This is in the attribute_mutated method of the VirtualMethods trait. Is that called when initially creating an element from HTML parsing?

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.

Yes, I believe it is called when the attribute is processed by the parser. There is one more case, which is when there is no type attribute specified at all. In that case this method will not be called.

Comment on lines +800 to +802
/// The primary font used for this container, if one exists. This is the font that is
/// used when not falling back.
default_font: Option<FontRef>,

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.

What does “not falling back” mean here?

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.

That means using a fallback font. I write this comment to make this a bit clearer.

Comment thread components/layout/flow/inline/mod.rs Outdated
Comment on lines +1636 to +1646
let Some(offsets) =
self.ifc
.shared_selection
.clone()
.map(|shared_selection| TextRunOffsets {
shared_selection,
character_range: line_start_offset..line_start_offset + 1,
})
else {
return;
};

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.

Small nit, feel free to ignore

Since we have let Some(…) = unwrapping the Option anyway, calling map is slightly more verbose than necessary:

Suggested change
let Some(offsets) =
self.ifc
.shared_selection
.clone()
.map(|shared_selection| TextRunOffsets {
shared_selection,
character_range: line_start_offset..line_start_offset + 1,
})
else {
return;
};
let Some(shared_selection) = self.ifc.shared_selection.clone() else {
return;
};
let offsets = TextRunOffsets {
shared_selection,
character_range: line_start_offset..line_start_offset + 1,
};

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.

Oh, good point. I'll make this change.

@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Apr 21, 2026
Instead of readily adding a strut for text carets when processing a line
break, wait until a line is finished. This allows placing a strut on the
final line. In addition to fixing servo#41338 this will also make it possible
to handle line breaks as a separate kind of text run item so that we can
preserve shaping results between layouts.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
@mrobinson mrobinson force-pushed the better-handling-of-empty-lines-in-textarea branch from d730cd9 to 7cf5c2c Compare April 22, 2026 06:43
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Apr 22, 2026
@mrobinson mrobinson enabled auto-merge April 22, 2026 06:43
@mrobinson mrobinson added this pull request to the merge queue Apr 22, 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 Apr 22, 2026
Merged via the queue into servo:main with commit 09ef444 Apr 22, 2026
33 checks passed
@mrobinson mrobinson deleted the better-handling-of-empty-lines-in-textarea branch April 22, 2026 07:25
@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 Apr 22, 2026
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.

Carets do not render on empty lines in <textarea>

3 participants