Auto merge of #22100 - Eijebong:once, r=jdm

Implement AddEventListenerOptions: once

Fixes #13242

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/22100)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2018-12-22 11:35:44 -05:00 committed by GitHub
commit cc0e7fde21
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 27 additions and 15 deletions

View file

@ -525,7 +525,10 @@ fn inner_invoke(
// Step 2.2. // Step 2.2.
found = true; 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. // Step 2.6.
let marker = TimelineMarker::start("DOMEvent".to_owned()); 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. /// A listener in a collection of event listeners.
struct EventListenerEntry { struct EventListenerEntry {
phase: ListenerPhase, phase: ListenerPhase,
listener: EventListenerType, 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)] #[derive(JSTraceable, MallocSizeOf)]
@ -401,12 +408,22 @@ impl EventTarget {
entries.push(EventListenerEntry { entries.push(EventListenerEntry {
phase: ListenerPhase::Bubbling, phase: ListenerPhase::Bubbling,
listener: EventListenerType::Inline(listener), 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> { fn get_inline_event_listener(&self, ty: &Atom) -> Option<CommonEventHandler> {
let mut handlers = self.handlers.borrow_mut(); let mut handlers = self.handlers.borrow_mut();
handlers handlers
@ -662,6 +679,7 @@ impl EventTarget {
let new_entry = EventListenerEntry { let new_entry = EventListenerEntry {
phase: phase, phase: phase,
listener: EventListenerType::Additive(listener), listener: EventListenerType::Additive(listener),
once: options.once,
}; };
if !entry.contains(&new_entry) { if !entry.contains(&new_entry) {
entry.push(new_entry); entry.push(new_entry);
@ -690,6 +708,7 @@ impl EventTarget {
let old_entry = EventListenerEntry { let old_entry = EventListenerEntry {
phase: phase, phase: phase,
listener: EventListenerType::Additive(listener.clone()), listener: EventListenerType::Additive(listener.clone()),
once: false,
}; };
if let Some(position) = entry.iter().position(|e| *e == old_entry) { if let Some(position) = entry.iter().position(|e| *e == old_entry) {
entry.remove(position); entry.remove(position);
@ -744,6 +763,7 @@ impl From<AddEventListenerOptionsOrBoolean> for AddEventListenerOptions {
AddEventListenerOptionsOrBoolean::AddEventListenerOptions(options) => options, AddEventListenerOptionsOrBoolean::AddEventListenerOptions(options) => options,
AddEventListenerOptionsOrBoolean::Boolean(capture) => Self { AddEventListenerOptionsOrBoolean::Boolean(capture) => Self {
parent: EventListenerOptions { capture }, parent: EventListenerOptions { capture },
once: false,
}, },
} }
} }

View file

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

View file

@ -29,5 +29,5 @@ dictionary EventListenerOptions {
dictionary AddEventListenerOptions : EventListenerOptions { dictionary AddEventListenerOptions : EventListenerOptions {
// boolean passive = false; // 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