From 9189fe06a55f698f6b1568a319e81c3d6b409659 Mon Sep 17 00:00:00 2001 From: Tim van der Lippe Date: Thu, 23 Apr 2026 17:54:57 +0200 Subject: [PATCH] script: Add initial implementation of italic command (#44432) While working on this, I realised that the `current_state` computation was wrong. Instead, the spec actually clearly defines what to do, but I hadn't found it yet. The code now correctly implements state computation and voila, it also fixes the previous underline issue that I didn't understand why it would fail. Part of #25005 Testing: WPT Signed-off-by: Tim van der Lippe --- .../script/dom/execcommand/basecommand.rs | 47 +- .../script/dom/execcommand/commands/italic.rs | 28 + .../script/dom/execcommand/commands/mod.rs | 1 + .../script/dom/execcommand/execcommands.rs | 1 + tests/wpt/meta/editing/event.html.ini | 6 - .../other/editing-div-outside-body.html.ini | 15 - ...ommand-with-text-editor.tentative.html.ini | 63 +- ...ecified-by-parent-block.tentative.html.ini | 3 - tests/wpt/meta/editing/run/italic.html.ini | 1155 +---------------- tests/wpt/meta/editing/run/multitest.html.ini | 213 --- .../meta/editing/run/strikethrough.html.ini | 20 +- tests/wpt/meta/editing/run/underline.html.ini | 18 - 12 files changed, 132 insertions(+), 1438 deletions(-) create mode 100644 components/script/dom/execcommand/commands/italic.rs diff --git a/components/script/dom/execcommand/basecommand.rs b/components/script/dom/execcommand/basecommand.rs index 72877b746a3..dfafd95d13a 100644 --- a/components/script/dom/execcommand/basecommand.rs +++ b/components/script/dom/execcommand/basecommand.rs @@ -21,6 +21,7 @@ use crate::dom::execcommand::commands::delete::execute_delete_command; use crate::dom::execcommand::commands::fontsize::{ execute_fontsize_command, font_size_loosely_equivalent, value_for_fontsize_command, }; +use crate::dom::execcommand::commands::italic::execute_italic_command; use crate::dom::execcommand::commands::strikethrough::execute_strikethrough_command; use crate::dom::execcommand::commands::stylewithcss::execute_style_with_css_command; use crate::dom::execcommand::commands::underline::execute_underline_command; @@ -230,21 +231,42 @@ impl CommandName { // > True if the CSS styling flag is true, otherwise false. document.css_styling_flag() }, - CommandName::FontSize => { - // Font size does not have a state defined for its command - false - }, _ => { - // https://w3c.github.io/editing/docs/execCommand/#state - // > The state of a command is true if it is already in effect, - // > in some sense specific to the command. + // https://w3c.github.io/editing/docs/execCommand/#inline-formatting-command-definitions + // > If a command has inline command activated values defined, its state is true if either + // > no formattable node is effectively contained in the active range, + // > and the active range's start node's effective command value is one of the given values; + // > or if there is at least one formattable node effectively contained in the active range, + // > and all of them have an effective command value equal to one of the given values. + let inline_command_activated_values = self.inline_command_activated_values(); + if inline_command_activated_values.is_empty() { + return None; + } let selection = document.GetSelection(CanGc::from_cx(cx))?; let active_range = selection.active_range()?; - active_range - .first_formattable_contained_node() - .unwrap_or_else(|| active_range.start_container()) - .effective_command_value(self) - .is_some() + let mut at_least_one_child_is_formattable = false; + let mut all_children_have_matching_command_values = true; + active_range.for_each_effectively_contained_child(|node| { + if !node.is_formattable() { + return; + } + at_least_one_child_is_formattable = true; + all_children_have_matching_command_values &= node + .effective_command_value(self) + .is_some_and(|effective_value| { + inline_command_activated_values.contains(&&*effective_value.str()) + }); + }); + if at_least_one_child_is_formattable { + all_children_have_matching_command_values + } else { + active_range + .start_container() + .effective_command_value(self) + .is_some_and(|effective_value| { + inline_command_activated_values.contains(&&*effective_value.str()) + }) + } }, }) } @@ -373,6 +395,7 @@ impl CommandName { }, CommandName::Delete => execute_delete_command(cx, document, selection), CommandName::FontSize => execute_fontsize_command(cx, document, selection, value), + CommandName::Italic => execute_italic_command(cx, document, selection), CommandName::Strikethrough => execute_strikethrough_command(cx, document, selection), CommandName::StyleWithCss => execute_style_with_css_command(document, value), CommandName::Underline => execute_underline_command(cx, document, selection), diff --git a/components/script/dom/execcommand/commands/italic.rs b/components/script/dom/execcommand/commands/italic.rs new file mode 100644 index 00000000000..c207791a994 --- /dev/null +++ b/components/script/dom/execcommand/commands/italic.rs @@ -0,0 +1,28 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use js::context::JSContext; + +use crate::dom::document::Document; +use crate::dom::execcommand::basecommand::CommandName; +use crate::dom::execcommand::execcommands::DocumentExecCommandSupport; +use crate::dom::selection::Selection; + +/// +pub(crate) fn execute_italic_command( + cx: &mut JSContext, + document: &Document, + selection: &Selection, +) -> bool { + // > If queryCommandState("italic") returns true, set the selection's value to "normal". + // > Otherwise set the selection's value to "italic". Either way, return true. + let value = if document.command_state_for_command(cx, "italic".into()) { + Some("normal".into()) + } else { + Some("italic".into()) + }; + selection.set_the_selection_value(cx, value, CommandName::Italic, document); + + true +} diff --git a/components/script/dom/execcommand/commands/mod.rs b/components/script/dom/execcommand/commands/mod.rs index 1e1f5a77526..6d42ffb6662 100644 --- a/components/script/dom/execcommand/commands/mod.rs +++ b/components/script/dom/execcommand/commands/mod.rs @@ -5,6 +5,7 @@ pub(crate) mod defaultparagraphseparator; pub(crate) mod delete; pub(crate) mod fontsize; +pub(crate) mod italic; pub(crate) mod strikethrough; pub(crate) mod stylewithcss; pub(crate) mod underline; diff --git a/components/script/dom/execcommand/execcommands.rs b/components/script/dom/execcommand/execcommands.rs index 1e13007127c..af2e03adfa9 100644 --- a/components/script/dom/execcommand/execcommands.rs +++ b/components/script/dom/execcommand/execcommands.rs @@ -120,6 +120,7 @@ impl Document { "delete" => CommandName::Delete, "defaultparagraphseparator" => CommandName::DefaultParagraphSeparator, "fontsize" => CommandName::FontSize, + "italic" => CommandName::Italic, "strikethrough" => CommandName::Strikethrough, "stylewithcss" => CommandName::StyleWithCss, "underline" => CommandName::Underline, diff --git a/tests/wpt/meta/editing/event.html.ini b/tests/wpt/meta/editing/event.html.ini index 5bf0ae86d13..8b2178055ec 100644 --- a/tests/wpt/meta/editing/event.html.ini +++ b/tests/wpt/meta/editing/event.html.ini @@ -65,12 +65,6 @@ [Command hiliteColor, value "green": input event] expected: FAIL - [Command italic, value "": input event] - expected: FAIL - - [Command italic, value "quasit": input event] - expected: FAIL - [Command removeFormat, value "": input event] expected: FAIL diff --git a/tests/wpt/meta/editing/other/editing-div-outside-body.html.ini b/tests/wpt/meta/editing/other/editing-div-outside-body.html.ini index 459cbfe7909..1aaef989864 100644 --- a/tests/wpt/meta/editing/other/editing-div-outside-body.html.ini +++ b/tests/wpt/meta/editing/other/editing-div-outside-body.html.ini @@ -14,9 +14,6 @@ [Test for execCommand("bold", false, undefined) in "
a[b\]c
"] expected: FAIL - [Test for execCommand("italic", false, undefined) in "
a[b\]c
"] - expected: FAIL - [Test for execCommand("createLink", false, "another.html") in "
a[b\]c
"] expected: FAIL @@ -43,9 +40,6 @@ [Test for execCommand("bold", false, undefined) in "
a[b\]c
"] expected: FAIL - [Test for execCommand("italic", false, undefined) in "
a[b\]c
"] - expected: FAIL - [Test for execCommand("createLink", false, "another.html") in "
a[b\]c
"] expected: FAIL @@ -72,9 +66,6 @@ [Test for execCommand("bold", false, undefined) in "
a[b\]c
"] expected: FAIL - [Test for execCommand("italic", false, undefined) in "
a[b\]c
"] - expected: FAIL - [Test for execCommand("createLink", false, "another.html") in "
a[b\]c
"] expected: FAIL @@ -101,9 +92,6 @@ [Test for execCommand("bold", false, undefined) in "
a[b\]c
"] expected: FAIL - [Test for execCommand("italic", false, undefined) in "
a[b\]c
"] - expected: FAIL - [Test for execCommand("createLink", false, "another.html") in "
a[b\]c
"] expected: FAIL @@ -130,9 +118,6 @@ [Test for execCommand("bold", false, undefined) in "
a[b\]c
"] expected: FAIL - [Test for execCommand("italic", false, undefined) in "
a[b\]c
"] - expected: FAIL - [Test for execCommand("createLink", false, "another.html") in "
a[b\]c
"] expected: FAIL diff --git a/tests/wpt/meta/editing/other/exec-command-with-text-editor.tentative.html.ini b/tests/wpt/meta/editing/other/exec-command-with-text-editor.tentative.html.ini index 61859ac4214..9ac296319a9 100644 --- a/tests/wpt/meta/editing/other/exec-command-with-text-editor.tentative.html.ini +++ b/tests/wpt/meta/editing/other/exec-command-with-text-editor.tentative.html.ini @@ -2,9 +2,6 @@ [In , execCommand("bold", false, bold), a[b\]c): The command should be supported] expected: FAIL - [In , execCommand("italic", false, null), a[b\]c): The command should be supported] - expected: FAIL - [In , execCommand("superscript", false, null), a[b\]c): The command should be supported] expected: FAIL @@ -155,9 +152,6 @@ [In in contenteditable, execCommand("bold", false, bold), a[b\]c): The command should be supported] expected: FAIL - [In in contenteditable, execCommand("italic", false, null), a[b\]c): The command should be supported] - expected: FAIL - [In in contenteditable, execCommand("superscript", false, null), a[b\]c): The command should be supported] expected: FAIL @@ -434,14 +428,26 @@ [In in contenteditable, execCommand("strikethrough", false, null), a[b\]c): input.target should be undefined] expected: FAIL + [In in contenteditable, execCommand("italic", false, null), a[b\]c): The command should not be enabled] + expected: FAIL + + [In in contenteditable, execCommand("italic", false, null), a[b\]c): execCommand() should return false] + expected: FAIL + + [In in contenteditable, execCommand("italic", false, null), a[b\]c): should not fire beforeinput event] + expected: FAIL + + [In in contenteditable, execCommand("italic", false, null), a[b\]c): input.inputType should be undefined] + expected: FAIL + + [In in contenteditable, execCommand("italic", false, null), a[b\]c): input.target should be undefined] + expected: FAIL + [exec-command-with-text-editor.tentative.html?type=text] [In , execCommand("bold", false, bold), a[b\]c): The command should be supported] expected: FAIL - [In , execCommand("italic", false, null), a[b\]c): The command should be supported] - expected: FAIL - [In , execCommand("superscript", false, null), a[b\]c): The command should be supported] expected: FAIL @@ -592,9 +598,6 @@ [In in contenteditable, execCommand("bold", false, bold), a[b\]c): The command should be supported] expected: FAIL - [In in contenteditable, execCommand("italic", false, null), a[b\]c): The command should be supported] - expected: FAIL - [In in contenteditable, execCommand("superscript", false, null), a[b\]c): The command should be supported] expected: FAIL @@ -871,14 +874,26 @@ [In in contenteditable, execCommand("strikethrough", false, null), a[b\]c): input.target should be undefined] expected: FAIL + [In in contenteditable, execCommand("italic", false, null), a[b\]c): The command should not be enabled] + expected: FAIL + + [In in contenteditable, execCommand("italic", false, null), a[b\]c): execCommand() should return false] + expected: FAIL + + [In in contenteditable, execCommand("italic", false, null), a[b\]c): should not fire beforeinput event] + expected: FAIL + + [In in contenteditable, execCommand("italic", false, null), a[b\]c): input.inputType should be undefined] + expected: FAIL + + [In in contenteditable, execCommand("italic", false, null), a[b\]c): input.target should be undefined] + expected: FAIL + [exec-command-with-text-editor.tentative.html?type=textarea] [In