LibWeb: Fix crashing after input into empty contenteditable
Change `EventHandler::handle_keydown()` to no longer assume the cursor position's node is always a `DOM::Text`. While this assumption holds for `HTMLInputElement` that has a shadow DOM with a text node, an empty `contenteditable` might not have any children. With this change, `handle_keydown()` creates a new text node if the cursor position's node is not a text node.
This commit is contained in:
parent
5d76fa72e9
commit
906ac71eca
Notes:
sideshowbarker
2024-07-17 00:16:31 +09:00
Author: https://github.com/kalenikaliaksandr Commit: https://github.com/SerenityOS/serenity/commit/906ac71eca Pull-request: https://github.com/SerenityOS/serenity/pull/23326 Reviewed-by: https://github.com/awesomekling
5 changed files with 47 additions and 9 deletions
|
@ -0,0 +1 @@
|
|||
hello
|
|
@ -0,0 +1,15 @@
|
|||
<script src="../include.js"></script>
|
||||
<style>
|
||||
#input {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
<div id="input" contenteditable="true"></div>
|
||||
<script>
|
||||
test(() => {
|
||||
const input = document.getElementById("input");
|
||||
internals.sendText(input, "hello");
|
||||
});
|
||||
</script>
|
|
@ -28,6 +28,7 @@ public:
|
|||
|
||||
JS::GCPtr<Node> node() { return m_node; }
|
||||
JS::GCPtr<Node const> node() const { return m_node; }
|
||||
void set_node(JS::NonnullGCPtr<Node> node) { m_node = node; }
|
||||
|
||||
unsigned offset() const { return m_offset; }
|
||||
bool offset_is_at_end_of_node() const;
|
||||
|
|
|
@ -114,6 +114,15 @@ void EditEventHandler::handle_insert(JS::NonnullGCPtr<DOM::Position> position, u
|
|||
node.set_data(MUST(builder.to_string()));
|
||||
|
||||
node.invalidate_style();
|
||||
} else {
|
||||
auto& node = *position->node();
|
||||
auto& realm = node.realm();
|
||||
StringBuilder builder;
|
||||
builder.append_code_point(code_point);
|
||||
auto text = realm.heap().allocate<DOM::Text>(realm, node.document(), MUST(builder.to_string()));
|
||||
MUST(node.append_child(*text));
|
||||
position->set_node(text);
|
||||
position->set_offset(1);
|
||||
}
|
||||
|
||||
// FIXME: When nodes are removed from the DOM, the associated layout nodes become stale and still
|
||||
|
|
|
@ -787,8 +787,6 @@ bool EventHandler::handle_keydown(KeyCode key, u32 modifiers, u32 code_point)
|
|||
return false;
|
||||
|
||||
if (m_browsing_context->cursor_position() && m_browsing_context->cursor_position()->node()->is_editable()) {
|
||||
auto& node = verify_cast<DOM::Text>(*m_browsing_context->cursor_position()->node());
|
||||
|
||||
if (key == KeyCode::Key_Backspace) {
|
||||
if (!m_browsing_context->decrement_cursor_position_offset()) {
|
||||
// FIXME: Move to the previous node and delete the last character there.
|
||||
|
@ -819,23 +817,37 @@ bool EventHandler::handle_keydown(KeyCode key, u32 modifiers, u32 code_point)
|
|||
return true;
|
||||
}
|
||||
if (key == KeyCode::Key_Home) {
|
||||
m_browsing_context->set_cursor_position(DOM::Position::create(realm, node, 0));
|
||||
auto& cursor_position_node = *m_browsing_context->cursor_position()->node();
|
||||
if (cursor_position_node.is_text())
|
||||
m_browsing_context->set_cursor_position(DOM::Position::create(realm, cursor_position_node, 0));
|
||||
return true;
|
||||
}
|
||||
if (key == KeyCode::Key_End) {
|
||||
m_browsing_context->set_cursor_position(DOM::Position::create(realm, node, (unsigned)node.data().bytes().size()));
|
||||
auto& cursor_position_node = *m_browsing_context->cursor_position()->node();
|
||||
if (cursor_position_node.is_text()) {
|
||||
auto& text_node = static_cast<DOM::Text&>(cursor_position_node);
|
||||
m_browsing_context->set_cursor_position(DOM::Position::create(realm, text_node, (unsigned)text_node.data().bytes().size()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (key == KeyCode::Key_Return) {
|
||||
if (is<HTML::HTMLInputElement>(node.editable_text_node_owner())) {
|
||||
auto& input_element = static_cast<HTML::HTMLInputElement&>(*node.editable_text_node_owner());
|
||||
|
||||
if (auto* form = input_element.form()) {
|
||||
HTML::HTMLInputElement* input_element = nullptr;
|
||||
if (auto node = m_browsing_context->cursor_position()->node()) {
|
||||
if (node->is_text()) {
|
||||
auto& text_node = static_cast<DOM::Text&>(*node);
|
||||
if (is<HTML::HTMLInputElement>(text_node.editable_text_node_owner()))
|
||||
input_element = static_cast<HTML::HTMLInputElement*>(text_node.editable_text_node_owner());
|
||||
} else if (node->is_html_input_element()) {
|
||||
input_element = static_cast<HTML::HTMLInputElement*>(node.ptr());
|
||||
}
|
||||
}
|
||||
if (input_element) {
|
||||
if (auto* form = input_element->form()) {
|
||||
form->implicitly_submit_form().release_value_but_fixme_should_propagate_errors();
|
||||
return true;
|
||||
}
|
||||
|
||||
input_element.commit_pending_changes();
|
||||
input_element->commit_pending_changes();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue