Implement AddEventListenerOptions: once

Fixes #13242
This commit is contained in:
Bastien Orivel 2018-11-03 16:23:33 +01:00
parent f3ca48206e
commit 50c832762f
5 changed files with 27 additions and 15 deletions

View file

@ -525,7 +525,10 @@ fn inner_invoke(
// Step 2.2.
found = true;
// TODO: step 2.5.
// Step 2.5.
if let CompiledEventListener::Listener(event_listener) = listener {
object.remove_listener_if_once(&event.type_(), &event_listener);
}
// Step 2.6.
let marker = TimelineMarker::start("DOMEvent".to_owned());

View file

@ -244,11 +244,18 @@ impl CompiledEventListener {
}
}
#[derive(Clone, DenyPublicFields, JSTraceable, MallocSizeOf, PartialEq)]
#[derive(Clone, DenyPublicFields, JSTraceable, MallocSizeOf)]
/// A listener in a collection of event listeners.
struct EventListenerEntry {
phase: ListenerPhase,
listener: EventListenerType,
once: bool,
}
impl std::cmp::PartialEq for EventListenerEntry {
fn eq(&self, other: &Self) -> bool {
self.phase == other.phase && self.listener == other.listener
}
}
#[derive(JSTraceable, MallocSizeOf)]
@ -401,12 +408,22 @@ impl EventTarget {
entries.push(EventListenerEntry {
phase: ListenerPhase::Bubbling,
listener: EventListenerType::Inline(listener),
once: false,
});
}
},
}
}
pub fn remove_listener_if_once(&self, ty: &Atom, listener: &Rc<EventListener>) {
let mut handlers = self.handlers.borrow_mut();
let listener = EventListenerType::Additive(listener.clone());
for entries in handlers.get_mut(ty) {
entries.drain_filter(|e| e.listener == listener && e.once);
}
}
fn get_inline_event_listener(&self, ty: &Atom) -> Option<CommonEventHandler> {
let mut handlers = self.handlers.borrow_mut();
handlers
@ -662,6 +679,7 @@ impl EventTarget {
let new_entry = EventListenerEntry {
phase: phase,
listener: EventListenerType::Additive(listener),
once: options.once,
};
if !entry.contains(&new_entry) {
entry.push(new_entry);
@ -690,6 +708,7 @@ impl EventTarget {
let old_entry = EventListenerEntry {
phase: phase,
listener: EventListenerType::Additive(listener.clone()),
once: false,
};
if let Some(position) = entry.iter().position(|e| *e == old_entry) {
entry.remove(position);
@ -744,6 +763,7 @@ impl From<AddEventListenerOptionsOrBoolean> for AddEventListenerOptions {
AddEventListenerOptionsOrBoolean::AddEventListenerOptions(options) => options,
AddEventListenerOptionsOrBoolean::Boolean(capture) => Self {
parent: EventListenerOptions { capture },
once: false,
},
}
}

View file

@ -95,6 +95,7 @@ impl MediaQueryListMethods for MediaQueryList {
listener,
AddEventListenerOptions {
parent: EventListenerOptions { capture: false },
once: false,
},
);
}

View file

@ -29,5 +29,5 @@ dictionary EventListenerOptions {
dictionary AddEventListenerOptions : EventListenerOptions {
// boolean passive = false;
// boolean once = false;
boolean once = false;
};

View file

@ -1,12 +0,0 @@
[AddEventListenerOptions-once.html]
type: testharness
bug: https://github.com/servo/servo/issues/13242
[Once listener should be invoked only once]
expected: FAIL
[Once listener should be invoked only once even if the event is nested]
expected: FAIL
[Once listener should be added / removed like normal listeners]
expected: FAIL