diff options
author | Gregory Terzian <gterzian@users.noreply.github.com> | 2018-04-27 13:35:53 +0800 |
---|---|---|
committer | Gregory Terzian <gterzian@users.noreply.github.com> | 2018-05-05 19:14:43 +0800 |
commit | 29d1cf6270b56af65971f27af4aa47c549eef788 (patch) | |
tree | be7ec3a78ac287d662a7f3426f75edd4cef68cba /components/script/dom | |
parent | 427eaed535dfdeaf735a40e3cb82ad7077aa86c5 (diff) | |
download | servo-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.rs | 19 | ||||
-rw-r--r-- | components/script/dom/event.rs | 62 | ||||
-rw-r--r-- | components/script/dom/eventtarget.rs | 15 |
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>) |