Skip to content

script: Implement check for supported and enabled commands#42634

Merged
TimvdLippe merged 1 commit intoservo:mainfrom
TimvdLippe:exec-command-support
Feb 17, 2026
Merged

script: Implement check for supported and enabled commands#42634
TimvdLippe merged 1 commit intoservo:mainfrom
TimvdLippe:exec-command-support

Conversation

@TimvdLippe
Copy link
Copy Markdown
Contributor

The first step of execCommand commands is to figure out
if they are supported and enabled. Therefore, implement
these two pieces with only 1 command: delete.

The implementation of delete is currently mostly dummy,
to have at least something going. But the main part of
this change is to setup the infrastructure to figure out
when commands are supported and enabled.

For the first part, its simply the list of commands we
currently have implemented, which is only delete.

For the second part, we need to consider the active range
of the current selection and do various checks, as well as
check the presence of contenteditable.

Part of #25005

@TimvdLippe TimvdLippe added the T-linux-wpt Do a try run of the WPT label Feb 15, 2026
@github-actions github-actions Bot removed the T-linux-wpt Do a try run of the WPT label Feb 15, 2026
@github-actions
Copy link
Copy Markdown

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

@github-actions
Copy link
Copy Markdown

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

Flaky unexpected result (33)
  • 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 [expected ERROR] /IndexedDB/cursor-overloads.any.worker.html (#42324)
  • CRASH [expected PASS] /_mozilla/shadow-dom/move-element-with-ua-shadow-tree-crash.html (#39473)
  • OK [expected TIMEOUT] /_webgl/conformance/textures/misc/tex-video-using-tex-unit-non-zero.html (#39735)
    • PASS [expected NOTRUN] subtest: Overall test
    • PASS [expected FAIL] subtest: WebGL test #0
  • CRASH [expected OK] /_webgl/conformance2/wasm/readpixels-2gb-in-4gb-wasm-memory.html
  • OK /beacon/beacon-basic.https.window.html (#41723)
    • PASS [expected FAIL] subtest: Payload size restriction should be accumulated: type = string
  • OK /css/CSS2/linebox/vertical-align-top-bottom-001.html (#41015)
    • FAIL [expected PASS] subtest: text-top+

      assert_approx_equals: expected "44" +/- 0 but got 41
      

    • FAIL [expected PASS] subtest: text-top+top

      assert_approx_equals: expected "5" +/- 0 but got 3
      

    • FAIL [expected PASS] subtest: text-top+text-top

      assert_approx_equals: expected "5" +/- 0 but got 3
      

    • FAIL [expected PASS] subtest: text-top+text-bottom

      assert_approx_equals: expected "40" +/- 0 but got 37
      

    • FAIL [expected PASS] subtest: text-bottom+

      assert_approx_equals: expected "44" +/- 0 but got 45
      

    • FAIL [expected PASS] subtest: text-bottom+top

      assert_approx_equals: expected "5" +/- 0 but got 7
      

    • FAIL [expected PASS] subtest: text-bottom+text-top

      assert_approx_equals: expected "5" +/- 0 but got 7
      

    • FAIL [expected PASS] subtest: bottom+

      assert_approx_equals: expected "49" +/- 0 but got 48
      

    • FAIL [expected PASS] subtest: bottom+text-top

      assert_approx_equals: expected "45" +/- 0 but got 43
      

    • FAIL [expected PASS] subtest: bottom+text-bottom

      assert_approx_equals: expected "45" +/- 0 but got 44
      

  • FAIL [expected PASS] /css/css-backgrounds/border-image-repeat-space-9.html
  • OK /css/css-fonts/generic-family-keywords-001.html (#37467)
    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted generic(kai)

      assert_equals: quoted generic(kai) matches  @font-face rule expected 50 but got 30
      

    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted generic(khmer-mul)

      assert_equals: quoted generic(khmer-mul) matches  @font-face rule expected 50 but got 30
      

  • OK /css/css-fonts/generic-family-keywords-002.html (#40929)
    • FAIL [expected PASS] subtest: font-family: -webkit-serif treated as <font-family>, not <generic-name>

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-sans-serif treated as <font-family>, not <generic-name>

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-cursive treated as <font-family>, not <generic-name>

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-fantasy treated as <font-family>, not <generic-name>

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-monospace treated as <font-family>, not <generic-name>

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-system-ui treated as <font-family>, not <generic-name>

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-math treated as <font-family>, not <generic-name>

      assert_equals: expected 30 but got 50
      

    • PASS [expected FAIL] subtest: font-family: -webkit-generic(fangsong) treated as <font-family>, not <generic-name>
    • PASS [expected FAIL] subtest: font-family: -webkit-generic(kai) treated as <font-family>, not <generic-name>
    • PASS [expected FAIL] subtest: font-family: -webkit-generic(khmer-mul) treated as <font-family>, not <generic-name>
    • And 12 more unexpected results...
  • FAIL [expected PASS] /css/css-ui/compute-kind-widget-generated/grouped-kind-of-widget-fallback-border-block-start-style-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)
  • OK /fetch/metadata/generated/css-font-face.https.sub.tentative.html (#32732)
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Cross-site
  • 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-site destination

      Test timed out
      

  • OK /fetch/metadata/window-open.https.sub.html (#40339)
    • FAIL [expected PASS] subtest: Same-site window, forced, reloaded

      The operation is insecure.
      

  • 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)
    • PASS [expected FAIL] subtest: Tests that a fragment navigation in the unload handler will not block the initial navigation
  • OK /html/browsers/browsing-the-web/navigating-across-documents/refresh/same-document-refresh.html (#34597)
    • FAIL [expected PASS] subtest: Same-Document Referrer from Refresh

      assert_equals: original page loads expected "http://web-platform.test:8000/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refresh-with-section.sub.html?url=%23section" but got "http://web-platform.test:8000/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refresh-with-section.sub.html?url=%23section#section"
      

  • OK /html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click.html (#28697)
    • FAIL [expected PASS] subtest: aElement.click() before the load event must NOT replace

      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)&code=%0A%20%20%20%20const%20a%20%3D%20document.createElement(%22a%22)%3B%0A%20%20%20%20a.href%20%3D%20%22%2Fcommon%2Fblank.html%3Fthereplacement%22%3B%0A%20%20%20%20document.currentScript.before(a)%3B%0A%20%20%20%20a.click()%3B%0A%20%20"
      

  • 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/semantics/embedded-content/media-elements/autoplay-disabled-by-feature-policy.https.sub.html (#41221)
    • FAIL [expected TIMEOUT] subtest: Feature-Policy header: autoplay "none" disallows same-origin iframes.

      assert_false: autoplay expected false got true
      

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

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

  • TIMEOUT [expected OK] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_other_frame_popup.sub.html (#39702)
    • TIMEOUT [expected FAIL] subtest: Sandboxed iframe can not navigate other frame's popup

      Test timed out
      

  • OK /html/semantics/forms/form-submission-0/jsurl-form-submit.tentative.html (#36489)
    • PASS [expected FAIL] subtest: Verifies that form submissions scheduled inside javascript: urls take precedence over the javascript: url's return value.
  • TIMEOUT [expected OK] /html/user-activation/navigation-state-reset-sameorigin.html
    • TIMEOUT [expected PASS] subtest: Post-navigation state reset.

      Test timed out
      

  • CRASH [expected OK] /intersection-observer/svg-intersection-with-fractional-bounds.html
  • OK /navigation-timing/test-navigation-type-reload.html (#33334)
    • FAIL [expected PASS] subtest: Reload domContentLoadedEventEnd > Original domContentLoadedEventEnd

      assert_true: Reload domContentLoadedEventEnd > Original domContentLoadedEventEnd expected true got false
      

  • 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/test_resource_timing.https.html (#25216)
    • PASS [expected FAIL] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (link)
  • 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?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.
  • 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
      

Stable unexpected results that are known to be intermittent (15)
  • 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 #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
      

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

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

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

      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 #69
    • PASS [expected FAIL] subtest: WebGL test #71
    • And 2 more unexpected results...
  • OK /css/css-cascade/layer-font-face-override.html (#35935)
    • FAIL [expected PASS] subtest: @font-face override update with appended sheet 2

      assert_equals: expected "80px" but got "38.3166666666667px"
      

  • 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(kai) (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)
  • OK /fetch/metadata/generated/css-font-face.sub.tentative.html (#34624)
    • FAIL [expected PASS] subtest: sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination

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

  • ERROR /fetch/metadata/generated/serviceworker.https.sub.html (#36247)
    • FAIL [expected PASS] subtest: sec-fetch-site - Same origin, no options - registration

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

  • 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/history/the-history-interface/traverse_the_history_3.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 3 but got 2 (expected array [6, 3] got [6, 2])
      

  • OK [expected TIMEOUT] /html/infrastructure/urls/base-url/document-base-url-window-initiator-is-not-opener.https.window.html (#30970)
  • OK [expected TIMEOUT] /html/interaction/focus/the-autofocus-attribute/supported-elements.html (#24145)
    • FAIL [expected NOTRUN] subtest: Host element with delegatesFocus should support autofocus

      assert_equals: expected Element node <div autofocus=""></div> but got Element node <body></body>
      

    • FAIL [expected NOTRUN] subtest: Host element with delegatesFocus including no focusable descendants should be skipped

      assert_equals: expected Element node <input autofocus=""></input> but got Element node <body><div autofocus=""></div><input autofocus=""></body>
      

    • FAIL [expected NOTRUN] subtest: Area element should support autofocus

      promise_test: Unhandled rejection with value: object "TypeError: can't access property "appendChild", w.document.querySelector(...) is null"
      

  • OK /html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-1.html (#39694)
    • PASS [expected FAIL] subtest: Meta refresh is blocked by the allow-scripts sandbox flag at its creation time, not when refresh comes due
  • OK /html/semantics/embedded-content/media-elements/media_fragment_seek.html (#24114)
    • PASS [expected FAIL] subtest: Video should seek to time specified in media fragment syntax
  • OK /resource-timing/test_resource_timing.html (#25720)
    • FAIL [expected PASS] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (xmlhttprequest)

      assert_equals: expected 6.290000000000003 but got 6.28
      

  • OK /visual-viewport/resize-event-order.html (#41981)
    • PASS [expected FAIL] subtest: Popup: DOMWindow resize fired before VisualViewport.
Stable unexpected results (31)
  • OK /editing/other/editing-around-select-element.tentative.html?delete
    • FAIL [expected PASS] subtest: execCommand(delete, false, "") in <div contenteditable><p>ab[c</p><select><option>d]ef</option></select></div>: shouldn't modify in <option>

      assert_in_array: value "<div contenteditable><p>ab</p><select><option>ef</option></select></div>" not in array ["<div contenteditable><p>abc</p><select><option>def</option></select></div>", "<div contenteditable><p>ab</p><select><option>def</option></select></div>", "<div contenteditable><p>ab<select><option>def</option></select></p></div>"]
      

    • FAIL [expected PASS] subtest: execCommand(delete, false, "") in <div contenteditable><select><option>ab[c</option></select><p>d]ef</p></div>: shouldn't modify in <option>

      assert_in_array: value "<div contenteditable><select><option>ab</option></select><p>ef</p></div>" not in array ["<div contenteditable><select><option>abc</option></select><p>def</p></div>", "<div contenteditable><select><option>abc</option></select><p>ef</p></div>", "<div contenteditable><select><option>abc</option></select>ef</div>"]
      

    • FAIL [expected PASS] subtest: execCommand(delete, false, "") in <div contenteditable><p>abc</p><select><option>{def}</option></select><p>ghi</p></div>: shouldn't modify in <option>

      assert_equals: expected "<div contenteditable><p>abc</p><select><option>def</option></select><p>ghi</p></div>" but got "<div contenteditable><p>abc</p><select><option></option></select><p>ghi</p></div>"
      

    • FAIL [expected PASS] subtest: execCommand(delete, false, "") in <div contenteditable><p>abc</p><select><option>{def</option><option>ghi}</option></select><p>jkl</p></div>: shouldn't join <option>s

      assert_equals: expected "<div contenteditable><p>abc</p><select><option>def</option><option>ghi</option></select><p>jkl</p></div>" but got "<div contenteditable><p>abc</p><select><option></option><option></option></select><p>jkl</p></div>"
      

    • FAIL [expected PASS] subtest: execCommand(delete, false, "") in <div contenteditable><p>abc</p><select>{<option>def</option>}<option>ghi</option></select><p>jkl</p></div>: shouldn't delete <option>

      assert_equals: expected "<div contenteditable><p>abc</p><select><option>def</option><option>ghi</option></select><p>jkl</p></div>" but got "<div contenteditable><p>abc</p><select><option>ghi</option></select><p>jkl</p></div>"
      

    • FAIL [expected PASS] subtest: execCommand(delete, false, "") in <div contenteditable><p>abc</p><select><option>def</option>{<option>ghi</option>}</select><p>jkl</p></div>: shouldn't delete <option>

      assert_equals: expected "<div contenteditable><p>abc</p><select><option>def</option><option>ghi</option></select><p>jkl</p></div>" but got "<div contenteditable><p>abc</p><select><option>def</option></select><p>jkl</p></div>"
      

    • FAIL [expected PASS] subtest: execCommand(delete, false, "") in <div contenteditable><p>abc</p><select>{<option>def</option><option>ghi</option>}</select><p>jkl</p></div>: shouldn't delete <option>s nor <select>

      assert_equals: expected "<div contenteditable><p>abc</p><select><option>def</option><option>ghi</option></select><p>jkl</p></div>" but got "<div contenteditable><p>abc</p><select></select><p>jkl</p></div>"
      

    • FAIL [expected PASS] subtest: execCommand(delete, false, "") in <div contenteditable><p>abc</p><select><optgroup>{<option>def</option><option>ghi</option>}</optgroup></select><p>jkl</p></div>: shouldn't delete <option>, <optgroup> nor <select>

      assert_equals: expected "<div contenteditable><p>abc</p><select><optgroup><option>def</option><option>ghi</option></optgroup></select><p>jkl</p></div>" but got "<div contenteditable><p>abc</p><select><optgroup></optgroup></select><p>jkl</p></div>"
      

    • PASS [expected FAIL] subtest: execCommand(delete, false, "") in <div contenteditable><p>abc</p>{<select><option>def</option><option>ghi</option></select>}<p>jkl</p></div>: <select> element itself should be removable
    • PASS [expected FAIL] subtest: execCommand(delete, false, "") in <div contenteditable><p>abc</p>{<select><optgroup><option>def</option><option>ghi</option></optgroup></select>}<p>jkl</p></div>: <select> element itself should be removable
    • And 20 more unexpected results...
  • OK /editing/other/exec-command-never-throw-exceptions.tentative.html
    • PASS [expected FAIL] subtest: Testing queryCommandEnabled with unknown command
  • OK /editing/other/legacy-edit-command.html?command=contentReadOnly&param=false
    • PASS [expected FAIL] subtest: queryCommandEnabled("contentReadOnly")
  • OK /editing/other/legacy-edit-command.html?command=contentReadOnly&param=true
    • PASS [expected FAIL] subtest: queryCommandEnabled("contentReadOnly")
  • OK /editing/other/legacy-edit-command.html?command=decreaseFontSize
    • PASS [expected FAIL] subtest: queryCommandEnabled("decreaseFontSize")
  • OK /editing/other/legacy-edit-command.html?command=getHTML
    • PASS [expected FAIL] subtest: queryCommandEnabled("getHTML")
  • OK /editing/other/legacy-edit-command.html?command=heading&param=h1
    • PASS [expected FAIL] subtest: queryCommandEnabled("heading")
  • OK /editing/other/legacy-edit-command.html?command=heading&param=h2
    • PASS [expected FAIL] subtest: queryCommandEnabled("heading")
  • OK /editing/other/legacy-edit-command.html?command=heading&param=h3
    • PASS [expected FAIL] subtest: queryCommandEnabled("heading")
  • OK /editing/other/legacy-edit-command.html?command=heading&param=h4
    • PASS [expected FAIL] subtest: queryCommandEnabled("heading")
  • OK /editing/other/legacy-edit-command.html?command=heading&param=h5
    • PASS [expected FAIL] subtest: queryCommandEnabled("heading")
  • OK /editing/other/legacy-edit-command.html?command=heading&param=h6
    • PASS [expected FAIL] subtest: queryCommandEnabled("heading")
  • OK /editing/other/legacy-edit-command.html?command=increaseFontSize
    • PASS [expected FAIL] subtest: queryCommandEnabled("increaseFontSize")
  • OK /editing/other/legacy-edit-command.html?command=insertBrOrReturn&param=false
    • PASS [expected FAIL] subtest: queryCommandEnabled("insertBrOrReturn")
  • OK /editing/other/legacy-edit-command.html?command=insertBrOrReturn&param=true
    • PASS [expected FAIL] subtest: queryCommandEnabled("insertBrOrReturn")
  • OK /editing/other/legacy-edit-command.html?command=readonly&param=false
    • PASS [expected FAIL] subtest: queryCommandEnabled("readonly")
  • OK /editing/other/legacy-edit-command.html?command=readonly&param=true
    • PASS [expected FAIL] subtest: queryCommandEnabled("readonly")
  • OK /editing/plaintext-only/styling-commands.html?styleWithCSS=false
    • PASS [expected FAIL] subtest: execCommand("backColor", false, "red") when a[b]c: the command should be disabled
    • PASS [expected FAIL] subtest: execCommand("bold", false, undefined) when a[b]c: the command should be disabled
    • PASS [expected FAIL] subtest: execCommand("createLink", false, "http://example.com") when a[b]c: the command should be disabled
    • PASS [expected FAIL] subtest: execCommand("decreaseFontSize", false, undefined) when a[b]c: the command should be disabled
    • PASS [expected FAIL] subtest: execCommand("fontName", false, "Arial") when a[b]c: the command should be disabled
    • PASS [expected FAIL] subtest: execCommand("fontSize", false, "7") when a[b]c: the command should be disabled
    • PASS [expected FAIL] subtest: execCommand("formatBlock", false, "blockquote") when a[b]c: the command should be disabled
    • PASS [expected FAIL] subtest: execCommand("formatBlock", false, "div") when a[b]c: the command should be disabled
    • PASS [expected FAIL] subtest: execCommand("formatBlock", false, "p") when a[b]c: the command should be disabled
    • PASS [expected FAIL] subtest: execCommand("formatBlock", false, "pre") when a[b]c: the command should be disabled
    • And 20 more unexpected results...
  • OK /editing/plaintext-only/styling-commands.html?styleWithCSS=true
    • PASS [expected FAIL] subtest: execCommand("backColor", false, "red") when a[b]c: the command should be disabled
    • PASS [expected FAIL] subtest: execCommand("bold", false, undefined) when a[b]c: the command should be disabled
    • PASS [expected FAIL] subtest: execCommand("createLink", false, "http://example.com") when a[b]c: the command should be disabled
    • PASS [expected FAIL] subtest: execCommand("decreaseFontSize", false, undefined) when a[b]c: the command should be disabled
    • PASS [expected FAIL] subtest: execCommand("fontName", false, "Arial") when a[b]c: the command should be disabled
    • PASS [expected FAIL] subtest: execCommand("fontSize", false, "7") when a[b]c: the command should be disabled
    • PASS [expected FAIL] subtest: execCommand("formatBlock", false, "blockquote") when a[b]c: the command should be disabled
    • PASS [expected FAIL] subtest: execCommand("formatBlock", false, "div") when a[b]c: the command should be disabled
    • PASS [expected FAIL] subtest: execCommand("formatBlock", false, "p") when a[b]c: the command should be disabled
    • PASS [expected FAIL] subtest: execCommand("formatBlock", false, "pre") when a[b]c: the command should be disabled
    • And 20 more unexpected results...
  • OK /editing/run/multitest.html?1-1000
    • PASS [expected FAIL] subtest: [["bold",""],["delete",""]] "foo[]bar": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["bold",""],["delete",""],["inserttext","a"]] "foo[]bar": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["italic",""],["delete",""]] "foo[]bar": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["italic",""],["delete",""],["inserttext","a"]] "foo[]bar": execCommand("delete", false, "") return value
  • OK /editing/run/multitest.html?1001-2000
    • PASS [expected FAIL] subtest: [["strikethrough",""],["delete",""]] "foo[]bar": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["strikethrough",""],["delete",""],["inserttext","a"]] "foo[]bar": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["subscript",""],["delete",""]] "foo[]bar": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["subscript",""],["delete",""],["inserttext","a"]] "foo[]bar": execCommand("delete", false, "") return value
  • OK /editing/run/multitest.html?2001-3000
    • PASS [expected FAIL] subtest: [["superscript",""],["delete",""]] "foo[]bar": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["superscript",""],["delete",""],["inserttext","a"]] "foo[]bar": execCommand("delete", false, "") return value
  • OK /editing/run/multitest.html?3001-4000
    • PASS [expected FAIL] subtest: [["underline",""],["delete",""]] "foo[]bar": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["underline",""],["delete",""],["inserttext","a"]] "foo[]bar": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["backcolor","#00FFFF"],["delete",""]] "foo[]bar": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["backcolor","#00FFFF"],["delete",""],["inserttext","a"]] "foo[]bar": execCommand("delete", false, "") return value
  • OK /editing/run/multitest.html?4001-5000
    • PASS [expected FAIL] subtest: [["createlink","http://www.google.com/"],["delete",""]] "foo[]bar": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["createlink","http://www.google.com/"],["delete",""],["inserttext","a"]] "foo[]bar": execCommand("delete", false, "") return value
  • OK /editing/run/multitest.html?5001-6000
    • PASS [expected FAIL] subtest: [["fontname","sans-serif"],["delete",""]] "foo[]bar": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["fontname","sans-serif"],["delete",""],["inserttext","a"]] "foo[]bar": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["fontsize","4"],["delete",""]] "foo[]bar": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["fontsize","4"],["delete",""],["inserttext","a"]] "foo[]bar": execCommand("delete", false, "") return value
  • OK /editing/run/multitest.html?6001-7000
    • PASS [expected FAIL] subtest: [["forecolor","#0000FF"],["delete",""]] "foo[]bar": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["forecolor","#0000FF"],["delete",""],["inserttext","a"]] "foo[]bar": execCommand("delete", false, "") return value
  • OK /editing/run/multitest.html?7001-8000
    • PASS [expected FAIL] subtest: [["hilitecolor","#00FFFF"],["delete",""]] "foo[]bar": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["hilitecolor","#00FFFF"],["delete",""],["inserttext","a"]] "foo[]bar": execCommand("delete", false, "") return value
  • OK /editing/run/multitest.html?8001-9000
    • PASS [expected FAIL] subtest: [["delete",""],["inserttext","a"]] "foo<b>[bar]</b>baz": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["delete",""],["inserttext","a"]] "foo<i>[bar]</i>baz": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["delete",""],["inserttext","a"]] "foo<s>[bar]</s>baz": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["delete",""],["inserttext","a"]] "foo<sub>[bar]</sub>baz": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["delete",""],["inserttext","a"]] "foo<sup>[bar]</sup>baz": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["delete",""],["inserttext","a"]] "foo<u>[bar]</u>baz": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["delete",""],["inserttext","a"]] "foo<a href=http://www.google.com>[bar]</a>baz": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["delete",""],["inserttext","a"]] "foo<font face=sans-serif>[bar]</font>baz": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["delete",""],["inserttext","a"]] "foo<font size=4>[bar]</font>baz": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["delete",""],["inserttext","a"]] "foo<font color=#0000FF>[bar]</font>baz": execCommand("delete", false, "") return value
    • And 44 more unexpected results...
  • OK /editing/run/multitest.html?9001-last
    • PASS [expected FAIL] subtest: [["delete",""],["inserttext","a"]] "foo<span style=background-color:#00FFFF>[bar</span>baz]": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["delete",""],["inserttext","a"]] "foo<a href=http://www.google.com><font color=blue>[bar</font></a>baz]": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["delete",""],["inserttext","a"]] "foo<font color=blue><a href=http://www.google.com>[bar</a></font>baz]": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["delete",""],["inserttext","a"]] "foo<a href=http://www.google.com><font color=brown>[bar</font></a>baz]": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["delete",""],["inserttext","a"]] "foo<font color=brown><a href=http://www.google.com>[bar</a></font>baz]": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["delete",""],["inserttext","a"]] "foo<a href=http://www.google.com><font color=black>[bar</font></a>baz]": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["delete",""],["inserttext","a"]] "foo<a href=http://www.google.com><u>[bar</u></a>baz]": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["delete",""],["inserttext","a"]] "foo<u><a href=http://www.google.com>[bar</a></u>baz]": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["delete",""],["inserttext","a"]] "foo<sub><font size=2>[bar</font></sub>baz]": execCommand("delete", false, "") return value
    • PASS [expected FAIL] subtest: [["delete",""],["inserttext","a"]] "foo<font size=2><sub>[bar</sub></font>baz]": execCommand("delete", false, "") return value
    • And 57 more unexpected results...
  • OK /html/dom/idlharness.https.html?include=(Document|Window)
    • PASS [expected FAIL] subtest: Document interface: operation queryCommandEnabled(DOMString)
    • PASS [expected FAIL] subtest: Document interface: iframe.contentDocument must inherit property "queryCommandEnabled(DOMString)" with the proper type
    • PASS [expected FAIL] subtest: Document interface: calling queryCommandEnabled(DOMString) on iframe.contentDocument with too few arguments must throw TypeError
    • PASS [expected FAIL] subtest: Document interface: new Document() must inherit property "queryCommandEnabled(DOMString)" with the proper type
    • PASS [expected FAIL] subtest: Document interface: calling queryCommandEnabled(DOMString) on new Document() with too few arguments must throw TypeError
    • PASS [expected FAIL] subtest: Document interface: documentWithHandlers must inherit property "queryCommandEnabled(DOMString)" with the proper type
    • PASS [expected FAIL] subtest: Document interface: calling queryCommandEnabled(DOMString) on documentWithHandlers with too few arguments must throw TypeError
  • OK /html/editing/editing-0/contenteditable/contentEditable-slotted-inherit.html
    • PASS [expected FAIL] subtest: Slotted child of contenteditable host should be editable - slot direct child of shadow root
    • PASS [expected FAIL] subtest: Slotted child of contenteditable host should be editable - slot wrapped in shadow tree ancestor

@github-actions
Copy link
Copy Markdown

⚠️ Try run (#22036341222) failed!

The first step of `execCommand` commands is to figure out
if they are supported and enabled. Therefore, implement
these two pieces with only 1 command: delete.

The implementation of `delete` is currently mostly dummy,
to have at least something going. But the main part of
this change is to setup the infrastructure to figure out
when commands are supported and enabled.

For the first part, its simply the list of commands we
currently have implemented, which is only delete.

For the second part, we need to consider the active range
of the current selection and do various checks, as well as
check the presence of `contenteditable`.

Part of servo#25005

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
@TimvdLippe TimvdLippe marked this pull request as ready for review February 16, 2026 19:43
@TimvdLippe TimvdLippe requested a review from gterzian as a code owner February 16, 2026 19:43
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Feb 16, 2026
Copy link
Copy Markdown
Member

@simonwuelker simonwuelker left a comment

Choose a reason for hiding this comment

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

Personally I'd prefer a Command enum that we can match in fn execute and friends over Box<dyn BaseCommand>. In my experience the latter is hard to debug because its annoying to identify the command given only dyn BaseCommand. But I'm not working on this code, so I'll leave that up to you.

Thanks for working on this API!

@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Feb 17, 2026
@TimvdLippe
Copy link
Copy Markdown
Contributor Author

I did think about an enum and am not yet sure what's gonna work best. The spec is quite vague at times and all over the place, so I am trying my best to put some structure to it. Once I know more details, I will reconsider implementing it as enum for the mentioned reasons.

@TimvdLippe TimvdLippe added this pull request to the merge queue Feb 17, 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 Feb 17, 2026
Merged via the queue into servo:main with commit 92e2d00 Feb 17, 2026
33 checks passed
@TimvdLippe TimvdLippe deleted the exec-command-support branch February 17, 2026 14:14
@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 Feb 17, 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.

3 participants