11//! Per-subtree state of apps
22
3+ use std:: borrow:: Cow ;
34use std:: cell:: RefCell ;
45use std:: collections:: HashSet ;
56use std:: hash:: { Hash , Hasher } ;
67use std:: rc:: { Rc , Weak } ;
78use std:: sync:: atomic:: { AtomicBool , AtomicU32 , Ordering } ;
89
9- use gloo:: events:: { EventListener , EventListenerOptions , EventListenerPhase } ;
10- use wasm_bindgen:: prelude:: wasm_bindgen;
11- use wasm_bindgen:: JsCast ;
12- use web_sys:: { Element , Event , EventTarget as HtmlEventTarget , ShadowRoot } ;
10+ use wasm_bindgen:: prelude:: { wasm_bindgen, Closure } ;
11+ use wasm_bindgen:: { JsCast , UnwrapThrowExt } ;
12+ use web_sys:: {
13+ AddEventListenerOptions , Element , Event , EventTarget as HtmlEventTarget , ShadowRoot ,
14+ } ;
1315
1416use super :: { test_log, Registry } ;
1517use crate :: virtual_dom:: { Listener , ListenerKind } ;
@@ -113,6 +115,70 @@ impl From<&dyn Listener> for EventDescriptor {
113115 }
114116}
115117
118+ // FIXME: this is a reproduction of gloo's EventListener to work around #2989
119+ // change back to gloo's implementation once it has been decided how to fix this upstream
120+ // The important part is that we use `Fn` instead of `FnMut` below!
121+ type EventClosure = Closure < dyn Fn ( & Event ) > ;
122+ #[ derive( Debug ) ]
123+ #[ must_use = "event listener will never be called after being dropped" ]
124+ struct EventListener {
125+ target : HtmlEventTarget ,
126+ event_type : Cow < ' static , str > ,
127+ callback : Option < EventClosure > ,
128+ }
129+
130+ impl Drop for EventListener {
131+ #[ inline]
132+ fn drop ( & mut self ) {
133+ if let Some ( ref callback) = self . callback {
134+ self . target
135+ . remove_event_listener_with_callback_and_bool (
136+ & self . event_type ,
137+ callback. as_ref ( ) . unchecked_ref ( ) ,
138+ true , // Always capture
139+ )
140+ . unwrap_throw ( ) ;
141+ }
142+ }
143+ }
144+
145+ impl EventListener {
146+ fn new (
147+ target : & HtmlEventTarget ,
148+ desc : & EventDescriptor ,
149+ callback : impl ' static + Fn ( & Event ) ,
150+ ) -> Self {
151+ let event_type = desc. kind . type_name ( ) ;
152+
153+ let callback = Closure :: wrap ( Box :: new ( callback) as Box < dyn Fn ( & Event ) > ) ;
154+ // defaults: { once: false }
155+ let mut options = AddEventListenerOptions :: new ( ) ;
156+ options. capture ( true ) . passive ( desc. passive ) ;
157+
158+ target
159+ . add_event_listener_with_callback_and_add_event_listener_options (
160+ & event_type,
161+ callback. as_ref ( ) . unchecked_ref ( ) ,
162+ & options,
163+ )
164+ . unwrap_throw ( ) ;
165+
166+ EventListener {
167+ target : target. clone ( ) ,
168+ event_type,
169+ callback : Some ( callback) ,
170+ }
171+ }
172+
173+ #[ cfg( not( test) ) ]
174+ fn forget ( mut self ) {
175+ if let Some ( callback) = self . callback . take ( ) {
176+ // Should always match, but no need to introduce a panic path here
177+ callback. forget ( ) ;
178+ }
179+ }
180+ }
181+
116182/// Ensures event handler registration.
117183// Separate struct to DRY, while avoiding partial struct mutability.
118184#[ derive( Debug ) ]
@@ -135,15 +201,8 @@ impl HostHandlers {
135201 }
136202 }
137203
138- fn add_listener ( & mut self , desc : & EventDescriptor , callback : impl ' static + FnMut ( & Event ) ) {
139- let cl = {
140- let desc = desc. clone ( ) ;
141- let options = EventListenerOptions {
142- phase : EventListenerPhase :: Capture ,
143- passive : desc. passive ,
144- } ;
145- EventListener :: new_with_options ( & self . host , desc. kind . type_name ( ) , options, callback)
146- } ;
204+ fn add_listener ( & mut self , desc : & EventDescriptor , callback : impl ' static + Fn ( & Event ) ) {
205+ let cl = EventListener :: new ( & self . host , desc, callback) ;
147206
148207 // Never drop the closure as this event handler is static
149208 #[ cfg( not( test) ) ]
0 commit comments