Remove GC hazard when compiling inline event listeners. (#33965)

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
Josh Matthews 2024-10-28 12:27:42 -04:00 committed by GitHub
parent a01d66df53
commit 1d6ede7b48
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::default::Default; use std::default::Default;
use std::ffi::CString; use std::ffi::CString;
@ -100,50 +101,49 @@ enum InlineEventListener {
Null, Null,
} }
impl InlineEventListener { /// Get a compiled representation of this event handler, compiling it from its
/// Get a compiled representation of this event handler, compiling it from its /// raw source if necessary.
/// raw source if necessary. /// <https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler>
/// <https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler> fn get_compiled_handler(
fn get_compiled_handler( inline_listener: &RefCell<InlineEventListener>,
&mut self, owner: &EventTarget,
owner: &EventTarget, ty: &Atom,
ty: &Atom, can_gc: CanGc,
can_gc: CanGc, ) -> Option<CommonEventHandler> {
) -> Option<CommonEventHandler> { let listener = mem::replace(
match mem::replace(self, InlineEventListener::Null) { &mut *inline_listener.borrow_mut(),
InlineEventListener::Null => None, InlineEventListener::Null,
InlineEventListener::Uncompiled(handler) => { );
let result = owner.get_compiled_event_handler(handler, ty, can_gc); let compiled = match listener {
if let Some(ref compiled) = result { InlineEventListener::Null => None,
*self = InlineEventListener::Compiled(compiled.clone()); InlineEventListener::Uncompiled(handler) => {
} owner.get_compiled_event_handler(handler, ty, can_gc)
result },
}, InlineEventListener::Compiled(handler) => Some(handler),
InlineEventListener::Compiled(handler) => { };
*self = InlineEventListener::Compiled(handler.clone()); if let Some(ref compiled) = compiled {
Some(handler) *inline_listener.borrow_mut() = InlineEventListener::Compiled(compiled.clone());
},
}
} }
compiled
} }
#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)] #[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
enum EventListenerType { enum EventListenerType {
Additive(#[ignore_malloc_size_of = "Rc"] Rc<EventListener>), Additive(#[ignore_malloc_size_of = "Rc"] Rc<EventListener>),
Inline(InlineEventListener), Inline(RefCell<InlineEventListener>),
} }
impl EventListenerType { impl EventListenerType {
fn get_compiled_listener( fn get_compiled_listener(
&mut self, &self,
owner: &EventTarget, owner: &EventTarget,
ty: &Atom, ty: &Atom,
can_gc: CanGc, can_gc: CanGc,
) -> Option<CompiledEventListener> { ) -> Option<CompiledEventListener> {
match *self { match *self {
EventListenerType::Inline(ref mut inline) => inline EventListenerType::Inline(ref inline) => {
.get_compiled_handler(owner, ty, can_gc) get_compiled_handler(inline, owner, ty, can_gc).map(CompiledEventListener::Handler)
.map(CompiledEventListener::Handler), },
EventListenerType::Additive(ref listener) => { EventListenerType::Additive(ref listener) => {
Some(CompiledEventListener::Listener(listener.clone())) Some(CompiledEventListener::Listener(listener.clone()))
}, },
@ -308,15 +308,15 @@ impl DerefMut for EventListeners {
impl EventListeners { impl EventListeners {
// https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler // https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler
fn get_inline_listener( fn get_inline_listener(
&mut self, &self,
owner: &EventTarget, owner: &EventTarget,
ty: &Atom, ty: &Atom,
can_gc: CanGc, can_gc: CanGc,
) -> Option<CommonEventHandler> { ) -> Option<CommonEventHandler> {
for entry in &mut self.0 { for entry in &self.0 {
if let EventListenerType::Inline(ref mut inline) = entry.listener { if let EventListenerType::Inline(ref inline) = entry.listener {
// Step 1.1-1.8 and Step 2 // Step 1.1-1.8 and Step 2
return inline.get_compiled_handler(owner, ty, can_gc); return get_compiled_handler(inline, owner, ty, can_gc);
} }
} }
@ -326,14 +326,14 @@ impl EventListeners {
// https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler // https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler
fn get_listeners( fn get_listeners(
&mut self, &self,
phase: Option<ListenerPhase>, phase: Option<ListenerPhase>,
owner: &EventTarget, owner: &EventTarget,
ty: &Atom, ty: &Atom,
can_gc: CanGc, can_gc: CanGc,
) -> Vec<CompiledEventListener> { ) -> Vec<CompiledEventListener> {
self.0 self.0
.iter_mut() .iter()
.filter_map(|entry| { .filter_map(|entry| {
if phase.is_none() || Some(entry.phase) == phase { if phase.is_none() || Some(entry.phase) == phase {
// Step 1.1-1.8, 2 // Step 1.1-1.8, 2
@ -395,8 +395,8 @@ impl EventTarget {
can_gc: CanGc, can_gc: CanGc,
) -> Vec<CompiledEventListener> { ) -> Vec<CompiledEventListener> {
self.handlers self.handlers
.borrow_mut() .borrow()
.get_mut(type_) .get(type_)
.map_or(vec![], |listeners| { .map_or(vec![], |listeners| {
listeners.get_listeners(specific_phase, self, type_, can_gc) listeners.get_listeners(specific_phase, self, type_, can_gc)
}) })
@ -427,7 +427,7 @@ impl EventTarget {
// Replace if there's something to replace with, // Replace if there's something to replace with,
// but remove entirely if there isn't. // but remove entirely if there isn't.
Some(listener) => { Some(listener) => {
entries[idx].listener = EventListenerType::Inline(listener); entries[idx].listener = EventListenerType::Inline(listener.into());
}, },
None => { None => {
entries.remove(idx); entries.remove(idx);
@ -437,7 +437,7 @@ impl EventTarget {
if let Some(listener) = listener { if let Some(listener) = listener {
entries.push(EventListenerEntry { entries.push(EventListenerEntry {
phase: ListenerPhase::Bubbling, phase: ListenerPhase::Bubbling,
listener: EventListenerType::Inline(listener), listener: EventListenerType::Inline(listener.into()),
once: false, once: false,
}); });
} }
@ -455,9 +455,9 @@ impl EventTarget {
} }
fn get_inline_event_listener(&self, ty: &Atom, can_gc: CanGc) -> Option<CommonEventHandler> { fn get_inline_event_listener(&self, ty: &Atom, can_gc: CanGc) -> Option<CommonEventHandler> {
let mut handlers = self.handlers.borrow_mut(); let handlers = self.handlers.borrow();
handlers handlers
.get_mut(ty) .get(ty)
.and_then(|entry| entry.get_inline_listener(self, ty, can_gc)) .and_then(|entry| entry.get_inline_listener(self, ty, can_gc))
} }