script: Record state for nodes prior to executing commands (#43177)

There is a chicken and egg problem here: to be able to restore the state
of nodes, we need the implementation of several other commands. However,
those commands then need some of the algorithms that are implemented for
storing the state.

Therefore, this PR has no behavioral changes, but includes preparations
to be able to first implement the functionality of the relevant
commands.

To that end, several changes were necessary:
1. Remove BaseCommand and instead use CommandName enum to define
behavior. The majority of the action part of the commands still exists
in separate files for readability (turn off whitespace changes in the
diff to make things readable)
2. Change the usages of a DOMString to the new CommandName enum in the
methods of execCommand. Both to make the enum work, but also as an
improvement to avoid operating on strings.
3. The {value,state}_override values in document need to be a map, not a
single boolean. I didn't understand these when I first implemented it,
but now it's clearer to me what their structure is.

In the end, the relevant recording and restoring state is implemented,
to the extent that it is possible. It does some light-weight
interaction, but most of the code doesn't do anything yet since the
relevant commands aren't implemented.

Part of #25005 

Testing: no behavioral changes, only preparations for future work

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
This commit is contained in:
Tim van der Lippe
2026-03-15 13:57:19 +01:00
committed by GitHub
parent 0d764f0d20
commit 15b74e04b6
8 changed files with 739 additions and 439 deletions

View File

@@ -4,37 +4,95 @@
use crate::dom::bindings::str::DOMString;
use crate::dom::document::Document;
use crate::dom::execcommand::commands::defaultparagraphseparator::execute_default_paragraph_separator_command;
use crate::dom::execcommand::commands::delete::execute_delete_command;
use crate::dom::execcommand::commands::stylewithcss::execute_style_with_css_command;
use crate::dom::selection::Selection;
pub(crate) trait BaseCommand {
/// <https://w3c.github.io/editing/docs/execCommand/#indeterminate>
fn is_indeterminate(&self) -> bool {
false
}
/// <https://w3c.github.io/editing/docs/execCommand/#state>
fn current_state(&self, _document: &Document) -> Option<bool> {
None
}
/// <https://w3c.github.io/editing/docs/execCommand/#value>
fn current_value(&self, _document: &Document) -> Option<DOMString> {
None
}
/// <https://w3c.github.io/editing/docs/execCommand/#action>
fn execute(
&self,
cx: &mut js::context::JSContext,
document: &Document,
selection: &Selection,
value: DOMString,
) -> bool;
}
#[derive(Default, Clone, Copy, MallocSizeOf)]
pub(crate) enum DefaultSingleLineContainerName {
#[default]
Div,
Paragraph,
}
impl From<DefaultSingleLineContainerName> for DOMString {
fn from(default_single_line_container_name: DefaultSingleLineContainerName) -> Self {
match default_single_line_container_name {
DefaultSingleLineContainerName::Div => DOMString::from("div"),
DefaultSingleLineContainerName::Paragraph => DOMString::from("p"),
}
}
}
#[derive(Clone, Copy, Eq, Hash, MallocSizeOf, PartialEq)]
#[expect(unused)] // TODO(25005): implement all commands
pub(crate) enum CommandName {
BackColor,
Bold,
CreateLink,
DefaultParagraphSeparator,
Delete,
FontSize,
HiliteColor,
Italic,
Redo,
SelectAll,
Strikethrough,
StyleWithCss,
Subscript,
Superscript,
Underline,
Undo,
Unlink,
Usecss,
}
impl CommandName {
/// <https://w3c.github.io/editing/docs/execCommand/#indeterminate>
pub(crate) fn is_indeterminate(&self) -> bool {
false
}
/// <https://w3c.github.io/editing/docs/execCommand/#state>
pub(crate) fn current_state(&self, document: &Document) -> Option<bool> {
Some(match self {
CommandName::StyleWithCss => {
// https://w3c.github.io/editing/docs/execCommand/#the-stylewithcss-command
// > True if the CSS styling flag is true, otherwise false.
document.css_styling_flag()
},
_ => return None,
})
}
/// <https://w3c.github.io/editing/docs/execCommand/#value>
pub(crate) fn current_value(&self, document: &Document) -> Option<DOMString> {
Some(match self {
CommandName::DefaultParagraphSeparator => {
// https://w3c.github.io/editing/docs/execCommand/#the-defaultparagraphseparator-command
// > Return the context object's default single-line container name.
document.default_single_line_container_name().into()
},
_ => return None,
})
}
/// <https://w3c.github.io/editing/docs/execCommand/#action>
pub(crate) fn execute(
&self,
cx: &mut js::context::JSContext,
document: &Document,
selection: &Selection,
value: DOMString,
) -> bool {
match self {
CommandName::DefaultParagraphSeparator => {
execute_default_paragraph_separator_command(document, value)
},
CommandName::Delete => execute_delete_command(cx, document, selection),
CommandName::StyleWithCss => execute_style_with_css_command(document, value),
_ => false,
}
}
}