mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-01-22 09:12:13 -05:00
LibWeb: Disallow Editing API calls on non-HTML documents
This is not directly mentioned in the Editing API spec, but all major browsers do this and there is a WPT for this behavior.
This commit is contained in:
parent
2cee6aeba3
commit
a0b0e91d4f
Notes:
github-actions[bot]
2025-01-21 18:09:45 +00:00
Author: https://github.com/tcl3 Commit: https://github.com/LadybirdBrowser/ladybird/commit/a0b0e91d4fb Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3332 Reviewed-by: https://github.com/gmta ✅
8 changed files with 89 additions and 31 deletions
|
@ -581,12 +581,12 @@ public:
|
|||
void set_previous_document_unload_timing(DocumentUnloadTimingInfo const& previous_document_unload_timing) { m_previous_document_unload_timing = previous_document_unload_timing; }
|
||||
|
||||
// https://w3c.github.io/editing/docs/execCommand/
|
||||
bool exec_command(FlyString const& command, bool show_ui, String const& value);
|
||||
bool query_command_enabled(FlyString const& command);
|
||||
bool query_command_indeterm(FlyString const& command);
|
||||
bool query_command_state(FlyString const& command);
|
||||
bool query_command_supported(FlyString const& command);
|
||||
String query_command_value(FlyString const& command);
|
||||
WebIDL::ExceptionOr<bool> exec_command(FlyString const& command, bool show_ui, String const& value);
|
||||
WebIDL::ExceptionOr<bool> query_command_enabled(FlyString const& command);
|
||||
WebIDL::ExceptionOr<bool> query_command_indeterm(FlyString const& command);
|
||||
WebIDL::ExceptionOr<bool> query_command_state(FlyString const& command);
|
||||
WebIDL::ExceptionOr<bool> query_command_supported(FlyString const& command);
|
||||
WebIDL::ExceptionOr<String> query_command_value(FlyString const& command);
|
||||
|
||||
// https://w3c.github.io/selection-api/#dfn-has-scheduled-selectionchange-event
|
||||
bool has_scheduled_selectionchange_event() const { return m_has_scheduled_selectionchange_event; }
|
||||
|
|
|
@ -52,7 +52,7 @@ bool command_back_color_action(DOM::Document& document, String const& value)
|
|||
bool command_bold_action(DOM::Document& document, String const&)
|
||||
{
|
||||
// If queryCommandState("bold") returns true, set the selection's value to "normal".
|
||||
if (document.query_command_state(CommandNames::bold)) {
|
||||
if (MUST(document.query_command_state(CommandNames::bold))) {
|
||||
set_the_selections_value(document, CommandNames::bold, "normal"_string);
|
||||
}
|
||||
|
||||
|
@ -1864,7 +1864,7 @@ bool command_insert_unordered_list_state(DOM::Document const& document)
|
|||
bool command_italic_action(DOM::Document& document, String const&)
|
||||
{
|
||||
// If queryCommandState("italic") returns true, set the selection's value to "normal".
|
||||
if (document.query_command_state(CommandNames::italic)) {
|
||||
if (MUST(document.query_command_state(CommandNames::italic))) {
|
||||
set_the_selections_value(document, CommandNames::italic, "normal"_string);
|
||||
}
|
||||
|
||||
|
@ -2256,7 +2256,7 @@ bool command_select_all_action(DOM::Document& document, String const&)
|
|||
bool command_strikethrough_action(DOM::Document& document, String const&)
|
||||
{
|
||||
// If queryCommandState("strikethrough") returns true, set the selection's value to null.
|
||||
if (document.query_command_state(CommandNames::strikethrough)) {
|
||||
if (MUST(document.query_command_state(CommandNames::strikethrough))) {
|
||||
set_the_selections_value(document, CommandNames::strikethrough, {});
|
||||
}
|
||||
|
||||
|
@ -2291,7 +2291,7 @@ bool command_style_with_css_state(DOM::Document const& document)
|
|||
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);
|
||||
auto state = MUST(document.query_command_state(CommandNames::subscript));
|
||||
|
||||
// 2. Set the selection's value to null.
|
||||
set_the_selections_value(document, CommandNames::subscript, {});
|
||||
|
@ -2344,7 +2344,7 @@ bool command_subscript_indeterminate(DOM::Document const& document)
|
|||
bool command_superscript_action(DOM::Document& document, String const&)
|
||||
{
|
||||
// 1. Call queryCommandState("superscript"), and let state be the result.
|
||||
auto state = document.query_command_state(CommandNames::superscript);
|
||||
auto state = MUST(document.query_command_state(CommandNames::superscript));
|
||||
|
||||
// 2. Set the selection's value to null.
|
||||
set_the_selections_value(document, CommandNames::superscript, {});
|
||||
|
@ -2397,7 +2397,7 @@ bool command_superscript_indeterminate(DOM::Document const& document)
|
|||
bool command_underline_action(DOM::Document& document, String const&)
|
||||
{
|
||||
// If queryCommandState("underline") returns true, set the selection's value to null.
|
||||
if (document.query_command_state(CommandNames::underline)) {
|
||||
if (MUST(document.query_command_state(CommandNames::underline))) {
|
||||
set_the_selections_value(document, CommandNames::underline, {});
|
||||
}
|
||||
|
||||
|
|
|
@ -14,15 +14,19 @@
|
|||
namespace Web::DOM {
|
||||
|
||||
// https://w3c.github.io/editing/docs/execCommand/#execcommand()
|
||||
bool Document::exec_command(FlyString const& command, [[maybe_unused]] bool show_ui, String const& value)
|
||||
WebIDL::ExceptionOr<bool> Document::exec_command(FlyString const& command, [[maybe_unused]] bool show_ui, String const& value)
|
||||
{
|
||||
// AD-HOC: This is not directly mentioned in the spec, but all major browsers limit editing API calls to HTML documents
|
||||
if (!is_html_document())
|
||||
return WebIDL::InvalidStateError::create(realm(), "execCommand is only supported on HTML documents"_string);
|
||||
|
||||
// 1. If only one argument was provided, let show UI be false.
|
||||
// 2. If only one or two arguments were provided, let value be the empty string.
|
||||
// NOTE: these steps are dealt by the default values for both show_ui and value
|
||||
|
||||
// 3. If command is not supported or not enabled, return false.
|
||||
// NOTE: query_command_enabled() also checks if command is supported
|
||||
if (!query_command_enabled(command))
|
||||
if (!MUST(query_command_enabled(command)))
|
||||
return false;
|
||||
|
||||
// 4. If command is not in the Miscellaneous commands section:
|
||||
|
@ -54,7 +58,7 @@ bool Document::exec_command(FlyString const& command, [[maybe_unused]] bool show
|
|||
//
|
||||
// We have to check again whether the command is enabled, because the beforeinput handler
|
||||
// might have done something annoying like getSelection().removeAllRanges().
|
||||
if (!query_command_enabled(command))
|
||||
if (!MUST(query_command_enabled(command)))
|
||||
return false;
|
||||
|
||||
// FIXME: 5. Let affected editing host be the editing host that is an inclusive ancestor of the
|
||||
|
@ -99,10 +103,14 @@ bool Document::exec_command(FlyString const& command, [[maybe_unused]] bool show
|
|||
}
|
||||
|
||||
// https://w3c.github.io/editing/docs/execCommand/#querycommandenabled()
|
||||
bool Document::query_command_enabled(FlyString const& command)
|
||||
WebIDL::ExceptionOr<bool> Document::query_command_enabled(FlyString const& command)
|
||||
{
|
||||
// AD-HOC: This is not directly mentioned in the spec, but all major browsers limit editing API calls to HTML documents
|
||||
if (!is_html_document())
|
||||
return WebIDL::InvalidStateError::create(realm(), "queryCommandEnabled is only supported on HTML documents"_string);
|
||||
|
||||
// 2. Return true if command is both supported and enabled, false otherwise.
|
||||
if (!query_command_supported(command))
|
||||
if (!MUST(query_command_supported(command)))
|
||||
return false;
|
||||
|
||||
// https://w3c.github.io/editing/docs/execCommand/#enabled
|
||||
|
@ -119,7 +127,7 @@ bool Document::query_command_enabled(FlyString const& command)
|
|||
|
||||
// AD-HOC: selectAll requires a selection object to exist.
|
||||
if (command == Editing::CommandNames::selectAll)
|
||||
return get_selection();
|
||||
return get_selection() != nullptr;
|
||||
|
||||
// The other commands defined here are enabled if the active range is not null,
|
||||
auto active_range = Editing::active_range(*this);
|
||||
|
@ -179,8 +187,12 @@ bool Document::query_command_enabled(FlyString const& command)
|
|||
}
|
||||
|
||||
// https://w3c.github.io/editing/docs/execCommand/#querycommandindeterm()
|
||||
bool Document::query_command_indeterm(FlyString const& command)
|
||||
WebIDL::ExceptionOr<bool> Document::query_command_indeterm(FlyString const& command)
|
||||
{
|
||||
// AD-HOC: This is not directly mentioned in the spec, but all major browsers limit editing API calls to HTML documents
|
||||
if (!is_html_document())
|
||||
return WebIDL::InvalidStateError::create(realm(), "queryCommandIndeterm is only supported on HTML documents"_string);
|
||||
|
||||
// 1. If command is not supported or has no indeterminacy, return false.
|
||||
auto optional_command = Editing::find_command_definition(command);
|
||||
if (!optional_command.has_value())
|
||||
|
@ -251,8 +263,12 @@ bool Document::query_command_indeterm(FlyString const& command)
|
|||
}
|
||||
|
||||
// https://w3c.github.io/editing/docs/execCommand/#querycommandstate()
|
||||
bool Document::query_command_state(FlyString const& command)
|
||||
WebIDL::ExceptionOr<bool> Document::query_command_state(FlyString const& command)
|
||||
{
|
||||
// AD-HOC: This is not directly mentioned in the spec, but all major browsers limit editing API calls to HTML documents
|
||||
if (!is_html_document())
|
||||
return WebIDL::InvalidStateError::create(realm(), "queryCommandState is only supported on HTML documents"_string);
|
||||
|
||||
// 1. If command is not supported or has no state, return false.
|
||||
auto optional_command = Editing::find_command_definition(command);
|
||||
if (!optional_command.has_value())
|
||||
|
@ -293,8 +309,12 @@ bool Document::query_command_state(FlyString const& command)
|
|||
}
|
||||
|
||||
// https://w3c.github.io/editing/docs/execCommand/#querycommandsupported()
|
||||
bool Document::query_command_supported(FlyString const& command)
|
||||
WebIDL::ExceptionOr<bool> Document::query_command_supported(FlyString const& command)
|
||||
{
|
||||
// AD-HOC: This is not directly mentioned in the spec, but all major browsers limit editing API calls to HTML documents
|
||||
if (!is_html_document())
|
||||
return WebIDL::InvalidStateError::create(realm(), "queryCommandSupported is only supported on HTML documents"_string);
|
||||
|
||||
// When the queryCommandSupported(command) method on the Document interface is invoked, the
|
||||
// user agent must return true if command is supported and available within the current script
|
||||
// on the current site, and false otherwise.
|
||||
|
@ -302,16 +322,20 @@ bool Document::query_command_supported(FlyString const& command)
|
|||
}
|
||||
|
||||
// https://w3c.github.io/editing/docs/execCommand/#querycommandvalue()
|
||||
String Document::query_command_value(FlyString const& command)
|
||||
WebIDL::ExceptionOr<String> Document::query_command_value(FlyString const& command)
|
||||
{
|
||||
// AD-HOC: This is not directly mentioned in the spec, but all major browsers limit editing API calls to HTML documents
|
||||
if (!is_html_document())
|
||||
return WebIDL::InvalidStateError::create(realm(), "queryCommandValue is only supported on HTML documents"_string);
|
||||
|
||||
// 1. If command is not supported or has no value, return the empty string.
|
||||
auto optional_command = Editing::find_command_definition(command);
|
||||
if (!optional_command.has_value())
|
||||
return {};
|
||||
return String {};
|
||||
auto const& command_definition = optional_command.release_value();
|
||||
auto value_override = command_value_override(command);
|
||||
if (!command_definition.value && !value_override.has_value())
|
||||
return {};
|
||||
return String {};
|
||||
|
||||
// 2. If command is "fontSize" and its value override is set, convert the value override to an
|
||||
// integer number of pixels and return the legacy font size for the result.
|
||||
|
|
|
@ -3323,7 +3323,7 @@ Vector<RecordedOverride> record_current_states_and_values(DOM::Document const& d
|
|||
// 6. For each command in the list "fontName", "foreColor", "hiliteColor", in order: add (command, command's value)
|
||||
// to overrides.
|
||||
for (auto const& command : { CommandNames::fontName, CommandNames::foreColor, CommandNames::hiliteColor })
|
||||
overrides.empend(command, node->document().query_command_value(command));
|
||||
overrides.empend(command, MUST(node->document().query_command_value(command)));
|
||||
|
||||
// 7. Add ("fontSize", node's effective command value for "fontSize") to overrides.
|
||||
effective_value = effective_command_value(node, CommandNames::fontSize);
|
||||
|
@ -3506,7 +3506,7 @@ void restore_states_and_values(DOM::Document& document, Vector<RecordedOverride>
|
|||
for (auto override : overrides) {
|
||||
// 1. If override is a boolean, and queryCommandState(command) returns something different from override,
|
||||
// take the action for command, with value equal to the empty string.
|
||||
if (override.value.has<bool>() && document.query_command_state(override.command) != override.value.get<bool>()) {
|
||||
if (override.value.has<bool>() && MUST(document.query_command_state(override.command)) != override.value.get<bool>()) {
|
||||
take_the_action_for_command(document, override.command, {});
|
||||
}
|
||||
|
||||
|
@ -3514,7 +3514,7 @@ void restore_states_and_values(DOM::Document& document, Vector<RecordedOverride>
|
|||
// queryCommandValue(command) returns something not equivalent to override, take the action for command,
|
||||
// with value equal to override.
|
||||
else if (override.value.has<String>() && !override.command.is_one_of(CommandNames::createLink, CommandNames::fontSize)
|
||||
&& document.query_command_value(override.command) != override.value.get<String>()) {
|
||||
&& MUST(document.query_command_value(override.command)) != override.value.get<String>()) {
|
||||
take_the_action_for_command(document, override.command, override.value.get<String>());
|
||||
}
|
||||
|
||||
|
@ -3761,7 +3761,7 @@ void set_the_selections_value(DOM::Document& document, FlyString const& command,
|
|||
}
|
||||
|
||||
// 5. Otherwise, if command is "createLink" or it has a value specified, set the value override to new value.
|
||||
else if (command == CommandNames::createLink || !document.query_command_value(CommandNames::createLink).is_empty()) {
|
||||
else if (command == CommandNames::createLink || !MUST(document.query_command_value(CommandNames::createLink)).is_empty()) {
|
||||
document.set_command_value_override(command, new_value.value());
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,6 @@ No range.
|
|||
DIV 0 - DIV 0
|
||||
BODY 0 - BODY 5
|
||||
true
|
||||
false
|
||||
queryCommandEnabled threw exception of type: InvalidStateError
|
||||
false
|
||||
Did not crash!
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 1 tests
|
||||
|
||||
1 Pass
|
||||
Pass editing APIs on an XML document should be disabled
|
|
@ -41,8 +41,12 @@
|
|||
document.implementation.createHTMLDocument(),
|
||||
];
|
||||
for (const doc of documents) {
|
||||
println(doc.queryCommandEnabled('selectAll'));
|
||||
doc.execCommand('selectAll');
|
||||
try {
|
||||
println(doc.queryCommandEnabled('selectAll'));
|
||||
doc.execCommand('selectAll');
|
||||
} catch (e) {
|
||||
println(`queryCommandEnabled threw exception of type: ${e.name}`);
|
||||
}
|
||||
}
|
||||
println('Did not crash!');
|
||||
});
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>Non-HTML document tests</title>
|
||||
<script src=../../resources/testharness.js></script>
|
||||
<script src=../../resources/testharnessreport.js></script>
|
||||
<script>
|
||||
|
||||
test(function() {
|
||||
let xmldoc =
|
||||
document.implementation.createDocument("http://www.w3.org/1999/xlink",
|
||||
"html", null);
|
||||
for (let f of [
|
||||
() => xmldoc.execCommand("bold"),
|
||||
() => xmldoc.queryCommandEnabled("bold"),
|
||||
() => xmldoc.queryCommandIndeterm("bold"),
|
||||
() => xmldoc.queryCommandState("bold"),
|
||||
() => xmldoc.queryCommandSupported("bold"),
|
||||
() => xmldoc.queryCommandValue("bold"),
|
||||
]) {
|
||||
assert_throws_dom("InvalidStateError", f);
|
||||
}
|
||||
}, "editing APIs on an XML document should be disabled");
|
||||
|
||||
</script>
|
Loading…
Reference in a new issue