Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Event listeners and dispatch #1171

Merged
merged 4 commits into from Nov 5, 2013
Merged
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Prev

Add basic event dispatch with bubbling, capturing, and propagation in…

…terruption.
  • Loading branch information
jdm committed Nov 5, 2013
commit 88f5c2b1333d69feed4198b5e13b3314f17091e5
@@ -768,7 +768,8 @@ pub enum Error {
NotFound,
HierarchyRequest,
InvalidCharacter,
NotSupported
NotSupported,
InvalidState
}

pub type Fallible<T> = Result<T, Error>;
@@ -31,6 +31,13 @@ pub struct AbstractEvent {
event: *mut Box<Event>
}

pub enum EventPhase {
Phase_None = 0,
Phase_Capturing,
Phase_At_Target,
Phase_Bubbling
}

impl AbstractEvent {
pub fn from_box(box: *mut Box<Event>) -> AbstractEvent {
AbstractEvent {
@@ -95,6 +102,14 @@ impl AbstractEvent {
assert!(self.is_mouseevent());
self.transmute_mut()
}

pub fn propagation_stopped(&self) -> bool {
self.event().stop_propagation
}

pub fn bubbles(&self) -> bool {
self.event().bubbles
}
}

impl DerivedWrapper for AbstractEvent {
@@ -138,23 +153,37 @@ pub enum EventTypeId {
pub struct Event {
type_id: EventTypeId,
reflector_: Reflector,
current_target: Option<AbstractEventTarget>,
target: Option<AbstractEventTarget>,
type_: ~str,
phase: EventPhase,
default_prevented: bool,
stop_propagation: bool,
stop_immediate: bool,
cancelable: bool,
bubbles: bool,
trusted: bool,
dispatching: bool,
initialized: bool
}

impl Event {
pub fn new_inherited(type_id: EventTypeId) -> Event {
Event {
type_id: type_id,
reflector_: Reflector::new(),
current_target: None,
target: None,
phase: Phase_None,
type_: ~"",
default_prevented: false,
cancelable: true,
bubbles: true,
trusted: false
trusted: false,
dispatching: false,
stop_propagation: false,
stop_immediate: false,
initialized: false,
}
}

@@ -173,33 +202,38 @@ impl Event {
}

pub fn EventPhase(&self) -> u16 {
0
self.phase as u16
}

pub fn Type(&self) -> DOMString {
Some(self.type_.clone())
}

pub fn GetTarget(&self) -> Option<AbstractEventTarget> {
None
self.target
}

pub fn GetCurrentTarget(&self) -> Option<AbstractEventTarget> {
None
self.current_target
}

pub fn DefaultPrevented(&self) -> bool {
self.default_prevented
}

pub fn PreventDefault(&mut self) {
self.default_prevented = true
if self.cancelable {
self.default_prevented = true
}
}

pub fn StopPropagation(&mut self) {
self.stop_propagation = true;
}

pub fn StopImmediatePropagation(&mut self) {
self.stop_immediate = true;
self.stop_propagation = true;
}

pub fn Bubbles(&self) -> bool {
@@ -221,6 +255,7 @@ impl Event {
self.type_ = null_str_as_word_null(type_);
self.cancelable = cancelable;
self.bubbles = bubbles;
self.initialized = true;
Ok(())
}

@@ -0,0 +1,111 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::callback::eReportExceptions;
use dom::eventtarget::{AbstractEventTarget, Capturing, Bubbling};
use dom::event::{AbstractEvent, Phase_At_Target, Phase_None, Phase_Bubbling, Phase_Capturing};
use dom::node::AbstractNode;
use servo_util::tree::{TreeNodeRef};

// See http://dom.spec.whatwg.org/#concept-event-dispatch for the full dispatch algorithm
pub fn dispatch_event(target: AbstractEventTarget, event: AbstractEvent) -> bool {
assert!(!event.event().dispatching);

{
let event = event.mut_event();
event.target = Some(target);
event.dispatching = true;
}

let type_ = event.event().type_.clone();
let mut chain = ~[];

//TODO: no chain if not participating in a tree
if target.is_node() {
for ancestor in AbstractNode::from_eventtarget(target).ancestors() {
chain.push(AbstractEventTarget::from_node(ancestor));
}
}

event.mut_event().phase = Phase_Capturing;

//FIXME: The "callback this value" should be currentTarget

/* capturing */
for &cur_target in chain.rev_iter() {
//XXX bad clone
let stopped = match cur_target.eventtarget().get_listeners_for(type_.clone(), Capturing) {
Some(listeners) => {
event.mut_event().current_target = Some(cur_target);
for listener in listeners.iter() {
listener.HandleEvent__(event, eReportExceptions);

if event.event().stop_immediate {
break;
}
}

event.propagation_stopped()
}
None => false
};

if stopped {
break;
}
}

/* at target */
if !event.propagation_stopped() {
{
let event = event.mut_event();
event.phase = Phase_At_Target;
event.current_target = Some(target);
}

let opt_listeners = target.eventtarget().get_listeners(type_.clone());
for listeners in opt_listeners.iter() {
for listener in listeners.iter() {
listener.HandleEvent__(event, eReportExceptions);
if event.event().stop_immediate {
break;
}
}
}
}

/* bubbling */
if event.bubbles() && !event.propagation_stopped() {
event.mut_event().phase = Phase_Bubbling;

for &cur_target in chain.iter() {
//XXX bad clone
let stopped = match cur_target.eventtarget().get_listeners_for(type_.clone(), Bubbling) {
Some(listeners) => {
event.mut_event().current_target = Some(cur_target);
for listener in listeners.iter() {
listener.HandleEvent__(event, eReportExceptions);

if event.event().stop_immediate {
break;
}
}

event.propagation_stopped()
}
None => false
};
if stopped {
break;
}
}
}

let event = event.mut_event();
event.dispatching = false;
event.phase = Phase_None;
event.current_target = None;

!event.DefaultPrevented()
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.