From f4a9989976973082f26cc2eaed606586bd22026c Mon Sep 17 00:00:00 2001 From: Pauan Date: Thu, 22 Feb 2018 15:08:52 -1000 Subject: [PATCH] Adding in Cancel and AutoCancel --- src/lib.rs | 2 +- src/webapi/event_target.rs | 28 ++++++++-------- src/webapi/mutation_observer.rs | 15 +++++---- src/webapi/window.rs | 15 +++++---- src/webcore/cancel.rs | 59 +++++++++++++++++++++++++++++++++ src/webcore/mod.rs | 1 + src/webcore/promise.rs | 19 ++++++----- src/webcore/promise_future.rs | 3 +- 8 files changed, 103 insertions(+), 39 deletions(-) create mode 100644 src/webcore/cancel.rs diff --git a/src/lib.rs b/src/lib.rs index 5b234796..3f916d05 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -207,7 +207,7 @@ pub mod web { }; pub use webapi::cross_origin_setting::CrossOriginSetting; pub use webapi::date::Date; - pub use webapi::event_target::{IEventTarget, EventTarget, EventListenerHandle}; + pub use webapi::event_target::{IEventTarget, EventTarget, EventListener}; pub use webapi::window::RequestAnimationFrameHandle; pub use webapi::node::{INode, Node, CloneKind}; pub use webapi::element::{IElement, Element}; diff --git a/src/webapi/event_target.rs b/src/webapi/event_target.rs index 3dd20aa1..5d2da66c 100644 --- a/src/webapi/event_target.rs +++ b/src/webapi/event_target.rs @@ -4,34 +4,33 @@ use webcore::value::Reference; use webcore::try_from::TryInto; use webcore::reference_type::ReferenceType; use webapi::event::{ConcreteEvent, IEvent}; +use webcore::cancel::{Cancel, AutoCancel}; use private::TODO; /// A handle to a particular event listener. -pub struct EventListenerHandle { +pub struct EventListener { event_type: &'static str, reference: Reference, listener_reference: Reference } -impl fmt::Debug for EventListenerHandle { +impl fmt::Debug for EventListener { fn fmt( &self, formatter: &mut fmt::Formatter ) -> fmt::Result { - write!( formatter, "EventListenerHandle {{ event_type: {}, reference: {:?} }}", self.event_type, self.reference ) + write!( formatter, "EventListener {{ event_type: {}, reference: {:?} }}", self.event_type, self.reference ) } } -impl EventListenerHandle { - /// Removes the handler from the [IEventTarget](trait.IEventTarget.html) on +impl Cancel for EventListener { + /// Removes the listener from the [IEventTarget](trait.IEventTarget.html) on /// which it was previously registered. /// /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener) // https://dom.spec.whatwg.org/#ref-for-dom-eventtarget-removeeventlistener%E2%91%A0 - pub fn remove( self ) { + fn cancel( &mut self ) { js! { @(no_return) - var self = @{self.reference}; - var event_type = @{self.event_type}; - var listener = @{self.listener_reference}; + var listener = @{&self.listener_reference}; + @{&self.reference}.removeEventListener( @{self.event_type}, listener ); listener.drop(); - self.removeEventListener( event_type, listener ); } } } @@ -42,26 +41,27 @@ impl EventListenerHandle { /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget) // https://dom.spec.whatwg.org/#eventtarget pub trait IEventTarget: ReferenceType { - /// Adds given event handler to the list the list of event listeners for + /// Adds given event handler to the list of event listeners for /// the specified `EventTarget` on which it's called. /// /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener) // https://dom.spec.whatwg.org/#ref-for-dom-eventtarget-addeventlistener%E2%91%A0 - fn add_event_listener< T, F >( &self, listener: F ) -> EventListenerHandle + fn add_event_listener< T, F >( &self, listener: F ) -> AutoCancel< EventListener > where T: ConcreteEvent, F: FnMut( T ) + 'static { let reference = self.as_ref(); + let listener_reference = js! { var listener = @{listener}; @{reference}.addEventListener( @{T::EVENT_TYPE}, listener ); return listener; }.try_into().unwrap(); - EventListenerHandle { + AutoCancel::new( EventListener { event_type: T::EVENT_TYPE, reference: reference.clone(), listener_reference: listener_reference - } + } ) } /// Dispatches an `Event` at this `EventTarget`, invoking the affected event listeners in the diff --git a/src/webapi/mutation_observer.rs b/src/webapi/mutation_observer.rs index 71af3335..002bf44b 100644 --- a/src/webapi/mutation_observer.rs +++ b/src/webapi/mutation_observer.rs @@ -3,6 +3,7 @@ use webcore::value::{Reference, Value, ConversionError}; use webapi::node_list::NodeList; use webcore::try_from::{TryFrom, TryInto}; use webapi::node::{INode, Node}; +use webcore::cancel::{Cancel, AutoCancel}; use private::TODO; /// Provides a way to receive notifications about changes to the DOM. @@ -62,17 +63,17 @@ impl MutationObserver { /// /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver#Constructor) // https://dom.spec.whatwg.org/#ref-for-dom-mutationobserver-mutationobserver - pub fn new< F >( callback: F ) -> MutationObserverHandle + pub fn new< F >( callback: F ) -> AutoCancel< MutationObserverHandle > where F: FnMut( Vec< MutationRecord >, Self ) + 'static { let callback_reference: Reference = js! ( return @{callback}; ).try_into().unwrap(); - MutationObserverHandle { + AutoCancel::new( MutationObserverHandle { callback_reference: callback_reference.clone(), mutation_observer: js! ( return new MutationObserver( @{callback_reference} ); ).try_into().unwrap(), - } + } ) } /// Starts observing changes to the `target`. @@ -159,7 +160,7 @@ impl MutationObserver { /// This is created by the [`MutationObserver::new`](struct.MutationObserver.html#method.new) method, and /// it can use the same methods as [`MutationObserver`](struct.MutationObserver.html). /// -/// When the `MutationObserverHandle` is dropped, the [`disconnect`](#method.disconnect) +/// When the `MutationObserverHandle` is cancelled, the [`disconnect`](#method.disconnect) /// method will automatically be called. #[ derive( Debug ) ] pub struct MutationObserverHandle { @@ -171,14 +172,14 @@ impl std::ops::Deref for MutationObserverHandle { type Target = MutationObserver; #[inline] - fn deref(&self) -> &Self::Target { + fn deref( &self ) -> &Self::Target { &self.mutation_observer } } -impl Drop for MutationObserverHandle { +impl Cancel for MutationObserverHandle { #[inline] - fn drop( &mut self ) { + fn cancel( &mut self ) { self.disconnect(); js! { @(no_return) diff --git a/src/webapi/window.rs b/src/webapi/window.rs index 1aa9fef5..a2facf5a 100644 --- a/src/webapi/window.rs +++ b/src/webapi/window.rs @@ -7,21 +7,22 @@ use webapi::location::Location; use webapi::history::History; use webcore::once::Once; use webcore::value::Value; +use webcore::cancel::{Cancel, AutoCancel}; /// A handle to a pending animation frame request. #[derive(Debug)] pub struct RequestAnimationFrameHandle(Value); -impl RequestAnimationFrameHandle { +impl Cancel for RequestAnimationFrameHandle { /// Cancels an animation frame request. /// /// [(Javascript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Window/cancelAnimationFrame) - pub fn cancel(self) { - js!{ - var val = @{self.0}; + fn cancel( &mut self ) { + js! { @(no_return) + var val = @{&self.0}; val.window.cancelAnimationFrame(val.request); val.callback.drop(); - }; + } } } @@ -123,13 +124,13 @@ impl Window { /// /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) // https://html.spec.whatwg.org/#the-window-object:dom-window-requestanimationframe - pub fn request_animation_frame< F: FnOnce(f64) + 'static>( &self, callback: F) -> RequestAnimationFrameHandle { + pub fn request_animation_frame< F: FnOnce(f64) + 'static>( &self, callback: F) -> AutoCancel< RequestAnimationFrameHandle > { let values: Value = js!{ var callback = @{Once(callback)}; var request = @{self}.requestAnimationFrame(callback); return { request: request, callback: callback, window: @{self} }; }; - RequestAnimationFrameHandle(values) + AutoCancel::new( RequestAnimationFrameHandle(values) ) } /// Returns the global [History](struct.History.html) object, which provides methods to diff --git a/src/webcore/cancel.rs b/src/webcore/cancel.rs new file mode 100644 index 00000000..691562ac --- /dev/null +++ b/src/webcore/cancel.rs @@ -0,0 +1,59 @@ +use std::ops::{Deref, DerefMut}; + + +pub trait Cancel { + fn cancel( &mut self ); +} + + +#[must_use = " + The AutoCancel will be automatically cancelled when it is dropped. + You probably don't want this to happen. + How to fix this: either use the AutoCancel, or use .leak() which will cause it to not be cancelled (this will leak memory!) +"] +#[derive(Debug)] +pub struct AutoCancel< A: Cancel >( Option< A > ); + +impl< A: Cancel > AutoCancel< A > { + #[inline] + pub fn new( canceler: A ) -> Self { + AutoCancel( Some( canceler ) ) + } + + #[inline] + pub fn leak( mut self ) -> A { + self.0.take().unwrap() + } +} + +impl< A: Cancel > Drop for AutoCancel< A > { + #[inline] + fn drop( &mut self ) { + match self.0 { + Some( ref mut canceler ) => canceler.cancel(), + None => {}, + } + } +} + +impl< A: Cancel > Deref for AutoCancel< A > { + type Target = A; + + #[inline] + fn deref( &self ) -> &Self::Target { + match self.0 { + Some( ref canceler ) => canceler, + None => unreachable!(), + } + } +} + +impl< A: Cancel > DerefMut for AutoCancel< A > { + #[inline] + fn deref_mut( &mut self ) -> &mut Self::Target { + match self.0 { + Some( ref mut canceler ) => canceler, + None => unreachable!(), + } + } +} diff --git a/src/webcore/mod.rs b/src/webcore/mod.rs index 7088fb94..8b26b51e 100644 --- a/src/webcore/mod.rs +++ b/src/webcore/mod.rs @@ -17,6 +17,7 @@ pub mod once; pub mod instance_of; pub mod reference_type; pub mod promise; +pub mod cancel; #[cfg(feature = "futures")] pub mod promise_future; diff --git a/src/webcore/promise.rs b/src/webcore/promise.rs index b539a4d7..65593a57 100644 --- a/src/webcore/promise.rs +++ b/src/webcore/promise.rs @@ -2,6 +2,7 @@ use std; use webcore::once::Once; use webcore::value::{Value, Reference}; use webcore::try_from::{TryInto, TryFrom}; +use webcore::cancel::{Cancel, AutoCancel}; #[cfg(feature = "futures")] use futures::unsync::oneshot::channel; @@ -19,8 +20,8 @@ pub struct DoneHandle { done: Value, } -impl Drop for DoneHandle { - fn drop( &mut self ) { +impl Cancel for DoneHandle { + fn cancel( &mut self ) { js! { @(no_return) @{&self.done}[0] = true; @{&self.callback}.drop(); @@ -100,13 +101,13 @@ impl Promise { /// If the `Promise` never succeeds / fails then the `callback` will never be called. /// /// This method returns a [`DoneHandle`](struct.DoneHandle.html). When the [`DoneHandle`](struct.DoneHandle.html) - /// is dropped it will drop the `callback` and the `callback` will never be called. This is useful if you are + /// is cancelled it will drop the `callback` and the `callback` will never be called. This is useful if you are /// no longer interested in the `Promise`'s result. /// - /// But if you *are* interested in the `Promise`'s result, then you need to make sure to keep the handle alive - /// until after the callback is called. + /// But if you *are* interested in the `Promise`'s result, then you either need to make sure to keep the handle + /// alive until after the callback is called, or you need to use the `leak` method. /// - /// Dropping the [`DoneHandle`](struct.DoneHandle.html) does ***not*** cancel the `Promise`, because promises + /// Cancelling the [`DoneHandle`](struct.DoneHandle.html) does ***not*** cancel the `Promise`, because promises /// do not support cancellation. /// /// # Examples @@ -122,7 +123,7 @@ impl Promise { /// /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then) // https://www.ecma-international.org/ecma-262/6.0/#sec-performpromisethen - pub fn done< A, B, F >( &self, callback: F ) -> DoneHandle + pub fn done< A, B, F >( &self, callback: F ) -> AutoCancel< DoneHandle > where A: TryFrom< Value >, B: TryFrom< Value >, // TODO these Debug constraints are only needed because of unwrap @@ -165,10 +166,10 @@ impl Promise { return done; ); - DoneHandle { + AutoCancel::new( DoneHandle { callback, done, - } + } ) } /// This method should rarely be needed, instead use [`value.try_into()`](unstable/trait.TryInto.html) to convert directly from a [`Value`](enum.Value.html) into a [`PromiseFuture`](struct.PromiseFuture.html). diff --git a/src/webcore/promise_future.rs b/src/webcore/promise_future.rs index 6f11a5c5..2e3f747f 100644 --- a/src/webcore/promise_future.rs +++ b/src/webcore/promise_future.rs @@ -5,6 +5,7 @@ use webapi::error; use futures::{Future, Poll, Async}; use futures::unsync::oneshot::Receiver; use webcore::promise_executor::spawn; +use webcore::cancel::AutoCancel; use super::promise::{Promise, DoneHandle}; @@ -21,7 +22,7 @@ use super::promise::{Promise, DoneHandle}; /// ``` pub struct PromiseFuture< Value, Error = error::Error > { pub(crate) future: Receiver< Result< Value, Error > >, - pub(crate) _done_handle: DoneHandle, + pub(crate) _done_handle: AutoCancel< DoneHandle >, } impl PromiseFuture< (), () > {