mirror of
https://github.com/servo/servo.git
synced 2025-08-02 12:10:29 +01:00
implement "has event listener", plug into (before)unload
This commit is contained in:
parent
427eaed535
commit
29d1cf6270
9 changed files with 66 additions and 56 deletions
|
@ -1646,15 +1646,15 @@ impl Document {
|
||||||
EventCancelable::Cancelable);
|
EventCancelable::Cancelable);
|
||||||
let event = beforeunload_event.upcast::<Event>();
|
let event = beforeunload_event.upcast::<Event>();
|
||||||
event.set_trusted(true);
|
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(),
|
document.root().upcast(),
|
||||||
&event,
|
&event,
|
||||||
);
|
);
|
||||||
// TODO: Step 6, decrease the event loop's termination nesting level by 1.
|
// TODO: Step 6, decrease the event loop's termination nesting level by 1.
|
||||||
// Step 7
|
// Step 7
|
||||||
if event.get_cancel_state() != EventDefault::Allowed {
|
self.salvageable.set(!has_listeners);
|
||||||
self.salvageable.set(false);
|
|
||||||
}
|
|
||||||
let mut can_unload = true;
|
let mut can_unload = true;
|
||||||
// TODO: Step 8 send a message to embedder to prompt user.
|
// TODO: Step 8 send a message to embedder to prompt user.
|
||||||
// Step 9
|
// Step 9
|
||||||
|
@ -1698,7 +1698,6 @@ impl Document {
|
||||||
);
|
);
|
||||||
// TODO Step 6, document visibility steps.
|
// TODO Step 6, document visibility steps.
|
||||||
}
|
}
|
||||||
let mut event_handled = false;
|
|
||||||
// Step 7
|
// Step 7
|
||||||
if !self.fired_unload.get() {
|
if !self.fired_unload.get() {
|
||||||
let event = Event::new(
|
let event = Event::new(
|
||||||
|
@ -1708,16 +1707,18 @@ impl Document {
|
||||||
EventCancelable::Cancelable,
|
EventCancelable::Cancelable,
|
||||||
);
|
);
|
||||||
event.set_trusted(true);
|
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(),
|
document.root().upcast(),
|
||||||
&event,
|
&event,
|
||||||
);
|
);
|
||||||
self.fired_unload.set(true);
|
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.
|
// TODO: Step 8, decrease the event loop's termination nesting level by 1.
|
||||||
// Step 9
|
|
||||||
self.salvageable.set(!event_handled);
|
|
||||||
// Step 13
|
// Step 13
|
||||||
if !recursive_flag {
|
if !recursive_flag {
|
||||||
for iframe in self.iter_iframes() {
|
for iframe in self.iter_iframes() {
|
||||||
|
|
|
@ -11,7 +11,7 @@ use dom::bindings::error::Fallible;
|
||||||
use dom::bindings::inheritance::Castable;
|
use dom::bindings::inheritance::Castable;
|
||||||
use dom::bindings::refcounted::Trusted;
|
use dom::bindings::refcounted::Trusted;
|
||||||
use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
|
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::bindings::str::DOMString;
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::eventtarget::{CompiledEventListener, EventTarget, ListenerPhase};
|
use dom::eventtarget::{CompiledEventListener, EventTarget, ListenerPhase};
|
||||||
|
@ -103,6 +103,36 @@ impl Event {
|
||||||
self.cancelable.set(cancelable);
|
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
|
// https://dom.spec.whatwg.org/#concept-event-dispatch
|
||||||
pub fn dispatch(&self,
|
pub fn dispatch(&self,
|
||||||
target: &EventTarget,
|
target: &EventTarget,
|
||||||
|
@ -130,24 +160,9 @@ impl Event {
|
||||||
return self.status();
|
return self.status();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3. The "invoke" algorithm is only used on `target` separately,
|
// Step 3-4.
|
||||||
// so we don't put it in the path.
|
let path = self.construct_event_path(&target);
|
||||||
rooted_vec!(let mut event_path);
|
rooted_vec!(let event_path <- path.into_iter());
|
||||||
|
|
||||||
// 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()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Steps 5-9. In a separate function to short-circuit various things easily.
|
// Steps 5-9. In a separate function to short-circuit various things easily.
|
||||||
dispatch_to_listeners(self, target, event_path.r());
|
dispatch_to_listeners(self, target, event_path.r());
|
||||||
|
|
||||||
|
@ -482,14 +497,7 @@ fn invoke(window: Option<&Window>,
|
||||||
event.current_target.set(Some(object));
|
event.current_target.set(Some(object));
|
||||||
|
|
||||||
// Step 5.
|
// Step 5.
|
||||||
if inner_invoke(window, object, event, &listeners) {
|
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: step 6.
|
// TODO: step 6.
|
||||||
}
|
}
|
||||||
|
|
|
@ -277,6 +277,12 @@ impl EventListeners {
|
||||||
}
|
}
|
||||||
}).collect()
|
}).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]
|
#[dom_struct]
|
||||||
|
@ -303,6 +309,15 @@ impl EventTarget {
|
||||||
Ok(EventTarget::new(global))
|
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,
|
pub fn get_listeners_for(&self,
|
||||||
type_: &Atom,
|
type_: &Atom,
|
||||||
specific_phase: Option<ListenerPhase>)
|
specific_phase: Option<ListenerPhase>)
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
[001.html]
|
|
||||||
type: testharness
|
|
||||||
expected: TIMEOUT
|
|
||||||
[pageshow event from traversal]
|
|
||||||
expected: TIMEOUT
|
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
[window-name-after-cross-origin-main-frame-navigation.sub.html]
|
[window-name-after-cross-origin-main-frame-navigation.sub.html]
|
||||||
type: testharness
|
type: testharness
|
||||||
expected: TIMEOUT
|
expected: ERROR
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
[window-name-after-cross-origin-sub-frame-navigation.sub.html]
|
|
||||||
type: testharness
|
|
||||||
expected: TIMEOUT
|
|
||||||
[Test that the window name is correct]
|
|
||||||
expected: NOTRUN
|
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
[window-name-after-same-origin-main-frame-navigation.sub.html]
|
[window-name-after-same-origin-main-frame-navigation.sub.html]
|
||||||
type: testharness
|
type: testharness
|
||||||
expected: TIMEOUT
|
expected: ERROR
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
[window-name-after-same-origin-sub-frame-navigation.sub.html]
|
|
||||||
type: testharness
|
|
||||||
expected: TIMEOUT
|
|
||||||
[Test that the window name is correct]
|
|
||||||
expected: NOTRUN
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[assign_after_load.html]
|
||||||
|
type: testharness
|
||||||
|
[Assignment to location after document is completely loaded]
|
||||||
|
expected: FAIL
|
Loading…
Add table
Add a link
Reference in a new issue