aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom
diff options
context:
space:
mode:
authorGregory Terzian <gterzian@users.noreply.github.com>2018-04-27 13:35:53 +0800
committerGregory Terzian <gterzian@users.noreply.github.com>2018-05-05 19:14:43 +0800
commit29d1cf6270b56af65971f27af4aa47c549eef788 (patch)
treebe7ec3a78ac287d662a7f3426f75edd4cef68cba /components/script/dom
parent427eaed535dfdeaf735a40e3cb82ad7077aa86c5 (diff)
downloadservo-29d1cf6270b56af65971f27af4aa47c549eef788.tar.gz
servo-29d1cf6270b56af65971f27af4aa47c549eef788.zip
implement "has event listener", plug into (before)unload
Diffstat (limited to 'components/script/dom')
-rw-r--r--components/script/dom/document.rs19
-rw-r--r--components/script/dom/event.rs62
-rw-r--r--components/script/dom/eventtarget.rs15
3 files changed, 60 insertions, 36 deletions
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index 4fa33056253..af58c8e53b7 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -1646,15 +1646,15 @@ impl Document {
EventCancelable::Cancelable);
let event = beforeunload_event.upcast::<Event>();
event.set_trusted(true);
- self.window.upcast::<EventTarget>().dispatch_event_with_target(
+ let event_target = self.window.upcast::<EventTarget>();
+ let has_listeners = event.has_listeners_for(&event_target, &atom!("beforeunload"));
+ event_target.dispatch_event_with_target(
document.root().upcast(),
&event,
);
// TODO: Step 6, decrease the event loop's termination nesting level by 1.
// Step 7
- if event.get_cancel_state() != EventDefault::Allowed {
- self.salvageable.set(false);
- }
+ self.salvageable.set(!has_listeners);
let mut can_unload = true;
// TODO: Step 8 send a message to embedder to prompt user.
// Step 9
@@ -1698,7 +1698,6 @@ impl Document {
);
// TODO Step 6, document visibility steps.
}
- let mut event_handled = false;
// Step 7
if !self.fired_unload.get() {
let event = Event::new(
@@ -1708,16 +1707,18 @@ impl Document {
EventCancelable::Cancelable,
);
event.set_trusted(true);
- let _ = self.window.upcast::<EventTarget>().dispatch_event_with_target(
+ let event_target = self.window.upcast::<EventTarget>();
+ let has_listeners = event.has_listeners_for(&event_target, &atom!("unload"));
+ let _ = event_target.dispatch_event_with_target(
document.root().upcast(),
&event,
);
self.fired_unload.set(true);
- event_handled = event.get_cancel_state() != EventDefault::Allowed;
+ // Step 9
+ self.salvageable.set(!has_listeners);
}
// TODO: Step 8, decrease the event loop's termination nesting level by 1.
- // Step 9
- self.salvageable.set(!event_handled);
+
// Step 13
if !recursive_flag {
for iframe in self.iter_iframes() {
diff --git a/components/script/dom/event.rs b/components/script/dom/event.rs
index aab86758e23..71b456e24cc 100644
--- a/components/script/dom/event.rs
+++ b/components/script/dom/event.rs
@@ -11,7 +11,7 @@ use dom::bindings::error::Fallible;
use dom::bindings::inheritance::Castable;
use dom::bindings::refcounted::Trusted;
use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
-use dom::bindings::root::{Dom, DomRoot, MutNullableDom, RootedReference};
+use dom::bindings::root::{DomRoot, MutNullableDom, RootedReference};
use dom::bindings::str::DOMString;
use dom::document::Document;
use dom::eventtarget::{CompiledEventListener, EventTarget, ListenerPhase};
@@ -103,6 +103,36 @@ impl Event {
self.cancelable.set(cancelable);
}
+ // Determine if there are any listeners for a given target and type.
+ // See https://github.com/whatwg/dom/issues/453
+ pub fn has_listeners_for(&self, target: &EventTarget, type_: &Atom) -> bool {
+ // TODO: take 'removed' into account? Not implemented in Servo yet.
+ // https://dom.spec.whatwg.org/#event-listener-removed
+ let mut event_path = self.construct_event_path(&target);
+ event_path.push(DomRoot::from_ref(target));
+ event_path.iter().any(|target| target.has_listeners_for(type_))
+ }
+
+ // https://dom.spec.whatwg.org/#event-path
+ fn construct_event_path(&self, target: &EventTarget) -> Vec<DomRoot<EventTarget>> {
+ let mut event_path = vec![];
+ // The "invoke" algorithm is only used on `target` separately,
+ // so we don't put it in the path.
+ if let Some(target_node) = target.downcast::<Node>() {
+ for ancestor in target_node.ancestors() {
+ event_path.push(DomRoot::from_ref(ancestor.upcast::<EventTarget>()));
+ }
+ let top_most_ancestor_or_target =
+ event_path.last().cloned().unwrap_or(DomRoot::from_ref(target));
+ if let Some(document) = DomRoot::downcast::<Document>(top_most_ancestor_or_target) {
+ if self.type_() != atom!("load") && document.browsing_context().is_some() {
+ event_path.push(DomRoot::from_ref(document.window().upcast()));
+ }
+ }
+ }
+ event_path
+ }
+
// https://dom.spec.whatwg.org/#concept-event-dispatch
pub fn dispatch(&self,
target: &EventTarget,
@@ -130,24 +160,9 @@ impl Event {
return self.status();
}
- // Step 3. The "invoke" algorithm is only used on `target` separately,
- // so we don't put it in the path.
- rooted_vec!(let mut event_path);
-
- // Step 4.
- if let Some(target_node) = target.downcast::<Node>() {
- for ancestor in target_node.ancestors() {
- event_path.push(Dom::from_ref(ancestor.upcast::<EventTarget>()));
- }
- let top_most_ancestor_or_target =
- DomRoot::from_ref(event_path.r().last().cloned().unwrap_or(target));
- if let Some(document) = DomRoot::downcast::<Document>(top_most_ancestor_or_target) {
- if self.type_() != atom!("load") && document.browsing_context().is_some() {
- event_path.push(Dom::from_ref(document.window().upcast()));
- }
- }
- }
-
+ // Step 3-4.
+ let path = self.construct_event_path(&target);
+ rooted_vec!(let event_path <- path.into_iter());
// Steps 5-9. In a separate function to short-circuit various things easily.
dispatch_to_listeners(self, target, event_path.r());
@@ -482,14 +497,7 @@ fn invoke(window: Option<&Window>,
event.current_target.set(Some(object));
// Step 5.
- if inner_invoke(window, object, event, &listeners) {
- // <https://html.spec.whatwg.org/multipage/#unload-a-document>
- // Step 9.
- // Required to establish the 'salvageable' state of document as part of unloading.
- if event.canceled.get() != EventDefault::Prevented {
- event.mark_as_handled();
- }
- }
+ inner_invoke(window, object, event, &listeners);
// TODO: step 6.
}
diff --git a/components/script/dom/eventtarget.rs b/components/script/dom/eventtarget.rs
index df15ff1b2bc..3d028a00cdb 100644
--- a/components/script/dom/eventtarget.rs
+++ b/components/script/dom/eventtarget.rs
@@ -277,6 +277,12 @@ impl EventListeners {
}
}).collect()
}
+
+ fn has_listeners(&self) -> bool {
+ // TODO: add, and take into account, a 'removed' field?
+ // https://dom.spec.whatwg.org/#event-listener-removed
+ self.0.len() > 0
+ }
}
#[dom_struct]
@@ -303,6 +309,15 @@ impl EventTarget {
Ok(EventTarget::new(global))
}
+ pub fn has_listeners_for(&self,
+ type_: &Atom)
+ -> bool {
+ match self.handlers.borrow().get(type_) {
+ Some(listeners) => listeners.has_listeners(),
+ None => false
+ }
+ }
+
pub fn get_listeners_for(&self,
type_: &Atom,
specific_phase: Option<ListenerPhase>)