Skip to content

Commit

Permalink
LibWeb: Add the "subscript" editing command
Browse files Browse the repository at this point in the history
  • Loading branch information
gmta committed Jan 8, 2025
1 parent 93a0289 commit 35b18a3
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 0 deletions.
60 changes: 60 additions & 0 deletions Libraries/LibWeb/Editing/Commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1328,6 +1328,59 @@ bool command_style_with_css_state(DOM::Document const& document)
return document.css_styling_flag();
}

// https://w3c.github.io/editing/docs/execCommand/#the-subscript-command
bool command_subscript_action(DOM::Document& document, String const&)
{
// 1. Call queryCommandState("subscript"), and let state be the result.
auto state = document.query_command_state(CommandNames::subscript);

// 2. Set the selection's value to null.
set_the_selections_value(document, CommandNames::subscript, {});

// 3. If state is false, set the selection's value to "subscript".
if (!state)
set_the_selections_value(document, CommandNames::subscript, "subscript"_string);

// 4. Return true.
return true;
}

// https://w3c.github.io/editing/docs/execCommand/#the-subscript-command
bool command_subscript_indeterminate(DOM::Document const& document)
{
// True if either among formattable nodes that are effectively contained in the active range, there is at least one
// with effective command value "subscript" and at least one with some other effective command value;
bool has_subscript_value = false;
bool has_other_value = false;
bool has_mixed_value = false;
for_each_node_effectively_contained_in_range(active_range(document), [&](GC::Ref<DOM::Node> descendant) {
if (!is_formattable_node(descendant))
return TraversalDecision::Continue;

auto node_value = effective_command_value(descendant, CommandNames::subscript);
if (!node_value.has_value())
return TraversalDecision::Continue;

if (node_value.value() == "subscript"sv) {
has_subscript_value = true;
} else {
has_other_value = true;
if (!has_mixed_value && node_value.value() == "mixed"sv)
has_mixed_value = true;
}
if (has_subscript_value && has_other_value)
return TraversalDecision::Break;

return TraversalDecision::Continue;
});
if (has_subscript_value && has_other_value)
return true;

// or if there is some formattable node effectively contained in the active range with effective command value
// "mixed". Otherwise false.
return has_mixed_value;
}

static Array const commands {
// https://w3c.github.io/editing/docs/execCommand/#the-backcolor-command
CommandDefinition {
Expand Down Expand Up @@ -1426,6 +1479,13 @@ static Array const commands {
.action = command_style_with_css_action,
.state = command_style_with_css_state,
},
// https://w3c.github.io/editing/docs/execCommand/#the-subscript-command
CommandDefinition {
.command = CommandNames::subscript,
.action = command_subscript_action,
.indeterminate = command_subscript_indeterminate,
.inline_activated_values = { "subscript"sv },
},
};

Optional<CommandDefinition const&> find_command_definition(FlyString const& command)
Expand Down
2 changes: 2 additions & 0 deletions Libraries/LibWeb/Editing/Commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,7 @@ bool command_remove_format_action(DOM::Document&, String const&);
bool command_strikethrough_action(DOM::Document&, String const&);
bool command_style_with_css_action(DOM::Document&, String const&);
bool command_style_with_css_state(DOM::Document const&);
bool command_subscript_action(DOM::Document&, String const&);
bool command_subscript_indeterminate(DOM::Document const&);

}
4 changes: 4 additions & 0 deletions Tests/LibWeb/Text/expected/Editing/execCommand-subscript.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Div contents: "foobar" state: false selection: #document 0 #document 0
Div contents: "foo<sub>bar</sub>" state: true selection: #text 0 #text 3
Div contents: "<sub>foobar</sub>" state: true selection: #text 0 #text 3
Div contents: "<sub>foo</sub>bar" state: false selection: #text 0 #text 3
34 changes: 34 additions & 0 deletions Tests/LibWeb/Text/input/Editing/execCommand-subscript.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<script src="../include.js"></script>
<div contenteditable="true">foobar</div>
<script>
test(() => {
const range = document.createRange();
getSelection().addRange(range);

const divElm = document.querySelector('div');
const printableSelection = () => {
let activeRange = getSelection().getRangeAt(0);
return `${activeRange.startContainer.nodeName} ${activeRange.startOffset} ${activeRange.endContainer.nodeName} ${activeRange.endOffset}`;
};
const report = () => println(`Div contents: "${divElm.innerHTML}" state: ${document.queryCommandState('subscript')} selection: ${printableSelection()}`);
report();

// Add subscript to 'bar'
range.setStart(divElm.childNodes[0], 3);
range.setEnd(divElm.childNodes[0], 6);
document.execCommand('subscript');
report();

// Add subscript to 'foo'
range.setStart(divElm.childNodes[0], 0);
range.setEnd(divElm.childNodes[0], 3);
document.execCommand('subscript');
report();

// Desubscriptify 'bar'
range.setStart(divElm.childNodes[0].childNodes[1], 0);
range.setEnd(divElm.childNodes[0].childNodes[1], 3);
document.execCommand('subscript');
report();
});
</script>

0 comments on commit 35b18a3

Please sign in to comment.