Skip to content

Commit

Permalink
Adding in Cancel and AutoCancel
Browse files Browse the repository at this point in the history
  • Loading branch information
Pauan committed Feb 23, 2018
1 parent 1b1f3ef commit f4a9989
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 39 deletions.
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
28 changes: 14 additions & 14 deletions src/webapi/event_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
}
}
}
Expand All @@ -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
Expand Down
15 changes: 8 additions & 7 deletions src/webapi/mutation_observer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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`.
Expand Down Expand Up @@ -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 {
Expand All @@ -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)
Expand Down
15 changes: 8 additions & 7 deletions src/webapi/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
};
}
}
}

Expand Down Expand Up @@ -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
Expand Down
59 changes: 59 additions & 0 deletions src/webcore/cancel.rs
Original file line number Diff line number Diff line change
@@ -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!(),
}
}
}
1 change: 1 addition & 0 deletions src/webcore/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
19 changes: 10 additions & 9 deletions src/webcore/promise.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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).
Expand Down
3 changes: 2 additions & 1 deletion src/webcore/promise_future.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};


Expand All @@ -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< (), () > {
Expand Down

0 comments on commit f4a9989

Please sign in to comment.