mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 04:09:13 +00:00
LibWeb: Ensure EventHandler visits its mouse selection target
We hold a raw pointer to the mouse selection target, which is a mixin- style class inherited only by JS::Cell classes. By not visiting this object, we sometime had a dangling reference to it after it had been garbage collected.
This commit is contained in:
parent
18a160e0e9
commit
d5be18617e
Notes:
github-actions[bot]
2025-02-27 09:54:17 +00:00
Author: https://github.com/trflynn89
Commit: d5be18617e
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3713
Reviewed-by: https://github.com/AtkinsSJ ✅
7 changed files with 56 additions and 2 deletions
|
@ -14,7 +14,8 @@
|
||||||
|
|
||||||
namespace Web::DOM {
|
namespace Web::DOM {
|
||||||
|
|
||||||
class EditingHostManager : public JS::Cell
|
class EditingHostManager
|
||||||
|
: public JS::Cell
|
||||||
, public InputEventsTarget {
|
, public InputEventsTarget {
|
||||||
GC_CELL(EditingHostManager, JS::Cell);
|
GC_CELL(EditingHostManager, JS::Cell);
|
||||||
GC_DECLARE_ALLOCATOR(EditingHostManager);
|
GC_DECLARE_ALLOCATOR(EditingHostManager);
|
||||||
|
@ -45,6 +46,8 @@ public:
|
||||||
private:
|
private:
|
||||||
EditingHostManager(GC::Ref<Document>);
|
EditingHostManager(GC::Ref<Document>);
|
||||||
|
|
||||||
|
virtual GC::Ref<JS::Cell> as_cell() override { return *this; }
|
||||||
|
|
||||||
GC::Ref<Document> m_document;
|
GC::Ref<Document> m_document;
|
||||||
GC::Ptr<DOM::Node> m_active_contenteditable_element;
|
GC::Ptr<DOM::Node> m_active_contenteditable_element;
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,6 +15,8 @@ class InputEventsTarget {
|
||||||
public:
|
public:
|
||||||
virtual ~InputEventsTarget() = default;
|
virtual ~InputEventsTarget() = default;
|
||||||
|
|
||||||
|
virtual GC::Ref<JS::Cell> as_cell() = 0;
|
||||||
|
|
||||||
virtual void handle_insert(String const&) = 0;
|
virtual void handle_insert(String const&) = 0;
|
||||||
virtual void handle_return_key() = 0;
|
virtual void handle_return_key() = 0;
|
||||||
|
|
||||||
|
|
|
@ -969,4 +969,9 @@ GC::Ptr<DOM::Position> FormAssociatedTextControlElement::cursor_position() const
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GC::Ref<JS::Cell> FormAssociatedTextControlElement::as_cell()
|
||||||
|
{
|
||||||
|
return form_associated_element_to_html_element();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,7 +160,8 @@ enum class SelectionSource {
|
||||||
DOM,
|
DOM,
|
||||||
};
|
};
|
||||||
|
|
||||||
class FormAssociatedTextControlElement : public FormAssociatedElement
|
class FormAssociatedTextControlElement
|
||||||
|
: public FormAssociatedElement
|
||||||
, public InputEventsTarget {
|
, public InputEventsTarget {
|
||||||
public:
|
public:
|
||||||
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-textarea/input-relevant-value
|
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-textarea/input-relevant-value
|
||||||
|
@ -231,6 +232,8 @@ protected:
|
||||||
void relevant_value_was_changed();
|
void relevant_value_was_changed();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
virtual GC::Ref<JS::Cell> as_cell() override;
|
||||||
|
|
||||||
void collapse_selection_to_offset(size_t);
|
void collapse_selection_to_offset(size_t);
|
||||||
void selection_was_changed();
|
void selection_was_changed();
|
||||||
|
|
||||||
|
|
|
@ -1375,6 +1375,9 @@ void EventHandler::visit_edges(JS::Cell::Visitor& visitor) const
|
||||||
{
|
{
|
||||||
m_drag_and_drop_event_handler->visit_edges(visitor);
|
m_drag_and_drop_event_handler->visit_edges(visitor);
|
||||||
visitor.visit(m_mouse_event_tracking_paintable);
|
visitor.visit(m_mouse_event_tracking_paintable);
|
||||||
|
|
||||||
|
if (m_mouse_selection_target)
|
||||||
|
visitor.visit(m_mouse_selection_target->as_cell());
|
||||||
}
|
}
|
||||||
|
|
||||||
Unicode::Segmenter& EventHandler::word_segmenter()
|
Unicode::Segmenter& EventHandler::word_segmenter()
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
PASS (didn't crash)
|
|
@ -0,0 +1,37 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<iframe id="iframe"></iframe>
|
||||||
|
<script src="../include.js"></script>
|
||||||
|
<script>
|
||||||
|
const runTest = () => {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
let iframe = document.getElementById("iframe");
|
||||||
|
|
||||||
|
iframe.onload = () => {
|
||||||
|
internals.movePointerTo(20, 40);
|
||||||
|
internals.mouseDown(20, 40);
|
||||||
|
internals.movePointerTo(60, 40);
|
||||||
|
|
||||||
|
iframe.onload = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
internals.movePointerTo(20, 40);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
iframe.src = "data:text/html,<p contenteditable>Text 2</p>";
|
||||||
|
};
|
||||||
|
|
||||||
|
iframe.src = "data:text/html,<p contenteditable>Text 1</p>";
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
asyncTest(async done => {
|
||||||
|
for (let i = 0; i < 10; ++i) {
|
||||||
|
await runTest();
|
||||||
|
internals.gc();
|
||||||
|
}
|
||||||
|
|
||||||
|
println("PASS (didn't crash)");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
</script>
|
Loading…
Add table
Add a link
Reference in a new issue