Skip to content

Commit

Permalink
Event dispatch rewritten to resemble spec more often, activate on cli…
Browse files Browse the repository at this point in the history
…cks better
  • Loading branch information
pshaughn committed Feb 12, 2020
1 parent ed9b584 commit 01aba1f
Show file tree
Hide file tree
Showing 29 changed files with 463 additions and 553 deletions.
7 changes: 7 additions & 0 deletions components/atoms/static_atoms.txt
Expand Up @@ -2,6 +2,9 @@ DOMContentLoaded
abort
activate
addtrack
animationend
animationiteration
animationstart
beforeunload
button
canplay
Expand Down Expand Up @@ -132,5 +135,9 @@ visibilitychange
volumechange
waiting
webglcontextcreationerror
webkitAnimationEnd
webkitAnimationIteration
webkitAnimationStart
webkitTransitionEnd
week
width
94 changes: 11 additions & 83 deletions components/script/dom/activation.rs
Expand Up @@ -2,17 +2,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::str::DOMString;
use crate::dom::element::Element;
use crate::dom::event::{Event, EventBubbles, EventCancelable};
use crate::dom::event::Event;
use crate::dom::eventtarget::EventTarget;
use crate::dom::mouseevent::MouseEvent;
use crate::dom::htmlinputelement::InputActivationState;
use crate::dom::node::window_from_node;
use crate::dom::window::ReflowReason;
use script_layout_interface::message::ReflowGoal;
use script_traits::MouseButton;

/// Trait for elements with defined activation behavior
pub trait Activatable {
Expand All @@ -21,13 +17,17 @@ pub trait Activatable {
// Is this particular instance of the element activatable?
fn is_instance_activatable(&self) -> bool;

// https://html.spec.whatwg.org/multipage/#run-pre-click-activation-steps
fn pre_click_activation(&self);
// https://dom.spec.whatwg.org/#eventtarget-legacy-pre-activation-behavior
fn legacy_pre_activation_behavior(&self) -> Option<InputActivationState> {
None
}

// https://html.spec.whatwg.org/multipage/#run-canceled-activation-steps
fn canceled_activation(&self);
// https://dom.spec.whatwg.org/#eventtarget-legacy-canceled-activation-behavior
fn legacy_canceled_activation_behavior(&self, _state_before: Option<InputActivationState>) {}

// https://html.spec.whatwg.org/multipage/#run-post-click-activation-steps
// https://dom.spec.whatwg.org/#eventtarget-activation-behavior
// event and target are used only by HTMLAnchorElement, in the case
// where the target is an <img ismap> so the href gets coordinates appended
fn activation_behavior(&self, event: &Event, target: &EventTarget);

// https://html.spec.whatwg.org/multipage/#concept-selector-active
Expand All @@ -45,75 +45,3 @@ pub trait Activatable {
win.reflow(ReflowGoal::Full, ReflowReason::ElementStateChanged);
}
}

/// Whether an activation was initiated via the click() method
#[derive(PartialEq)]
pub enum ActivationSource {
FromClick,
NotFromClick,
}

// https://html.spec.whatwg.org/multipage/#run-synthetic-click-activation-steps
pub fn synthetic_click_activation(
element: &Element,
ctrl_key: bool,
shift_key: bool,
alt_key: bool,
meta_key: bool,
source: ActivationSource,
) {
// Step 1
if element.click_in_progress() {
return;
}
// Step 2
element.set_click_in_progress(true);
// Step 3
let activatable = element.as_maybe_activatable();
if let Some(a) = activatable {
a.pre_click_activation();
}

// Step 4
// https://html.spec.whatwg.org/multipage/#fire-a-synthetic-mouse-event
let win = window_from_node(element);
let target = element.upcast::<EventTarget>();
let mouse = MouseEvent::new(
&win,
DOMString::from("click"),
EventBubbles::DoesNotBubble,
EventCancelable::NotCancelable,
Some(&win),
1,
0,
0,
0,
0,
ctrl_key,
shift_key,
alt_key,
meta_key,
0,
MouseButton::Left as u16,
None,
None,
);
let event = mouse.upcast::<Event>();
if source == ActivationSource::FromClick {
event.set_trusted(false);
}
target.dispatch_event(event);

// Step 5
if let Some(a) = activatable {
if event.DefaultPrevented() {
a.canceled_activation();
} else {
// post click activation
a.activation_behavior(event, target);
}
}

// Step 6
element.set_click_in_progress(false);
}
40 changes: 14 additions & 26 deletions components/script/dom/document.rs
Expand Up @@ -3,7 +3,6 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::document_loader::{DocumentLoader, LoadType};
use crate::dom::activation::{synthetic_click_activation, ActivationSource};
use crate::dom::attr::Attr;
use crate::dom::beforeunloadevent::BeforeUnloadEvent;
use crate::dom::bindings::callback::ExceptionHandling;
Expand Down Expand Up @@ -557,8 +556,7 @@ impl Document {
let event = event.upcast::<Event>();
event.set_trusted(true);
// FIXME(nox): Why are errors silenced here?
let _ = window.upcast::<EventTarget>().dispatch_event_with_target(
document.upcast(),
let _ = window.dispatch_event_with_target_override(
&event,
);
}),
Expand Down Expand Up @@ -1015,7 +1013,11 @@ impl Document {
// https://html.spec.whatwg.org/multipage/#run-authentic-click-activation-steps
let activatable = el.as_maybe_activatable();
match mouse_event_type {
MouseEventType::Click => el.authentic_click_activation(event),
MouseEventType::Click => {
el.set_click_in_progress(true);
event.fire(node.upcast());
el.set_click_in_progress(false);
},
MouseEventType::MouseDown => {
if let Some(a) = activatable {
a.enter_formal_activation_state();
Expand Down Expand Up @@ -1477,16 +1479,9 @@ impl Document {
if (keyboard_event.key == Key::Enter || keyboard_event.code == Code::Space) &&
keyboard_event.state == KeyState::Up
{
let maybe_elem = target.downcast::<Element>();
if let Some(el) = maybe_elem {
synthetic_click_activation(
el,
false,
false,
false,
false,
ActivationSource::NotFromClick,
)
if let Some(elem) = target.downcast::<Element>() {
elem.upcast::<Node>()
.fire_synthetic_mouse_event_not_trusted(DOMString::from("click"));
}
}
}
Expand Down Expand Up @@ -1802,7 +1797,6 @@ impl Document {
// Step 2
self.incr_ignore_opens_during_unload_counter();
//Step 3-5.
let document = Trusted::new(self);
let beforeunload_event = BeforeUnloadEvent::new(
&self.window,
atom!("beforeunload"),
Expand All @@ -1813,7 +1807,7 @@ impl Document {
event.set_trusted(true);
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);
self.window.dispatch_event_with_target_override(&event);
// TODO: Step 6, decrease the event loop's termination nesting level by 1.
// Step 7
if has_listeners {
Expand Down Expand Up @@ -1857,7 +1851,6 @@ impl Document {
// TODO: Step 1, increase the event loop's termination nesting level by 1.
// Step 2
self.incr_ignore_opens_during_unload_counter();
let document = Trusted::new(self);
// Step 3-6
if self.page_showing.get() {
self.page_showing.set(false);
Expand All @@ -1870,10 +1863,7 @@ impl Document {
);
let event = event.upcast::<Event>();
event.set_trusted(true);
let _ = self
.window
.upcast::<EventTarget>()
.dispatch_event_with_target(document.root().upcast(), &event);
let _ = self.window.dispatch_event_with_target_override(&event);
// TODO Step 6, document visibility steps.
}
// Step 7
Expand All @@ -1887,7 +1877,7 @@ impl Document {
event.set_trusted(true);
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);
let _ = self.window.dispatch_event_with_target_override(&event);
self.fired_unload.set(true);
// Step 9
if has_listeners {
Expand Down Expand Up @@ -1983,8 +1973,7 @@ impl Document {

debug!("About to dispatch load for {:?}", document.url());
// FIXME(nox): Why are errors silenced here?
let _ = window.upcast::<EventTarget>().dispatch_event_with_target(
document.upcast(),
let _ = window.dispatch_event_with_target_override(
&event,
);

Expand Down Expand Up @@ -2028,8 +2017,7 @@ impl Document {
event.set_trusted(true);

// FIXME(nox): Why are errors silenced here?
let _ = window.upcast::<EventTarget>().dispatch_event_with_target(
document.upcast(),
let _ = window.dispatch_event_with_target_override(
&event,
);
}),
Expand Down
48 changes: 0 additions & 48 deletions components/script/dom/element.rs
Expand Up @@ -11,7 +11,6 @@ use crate::dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use crate::dom::bindings::codegen::Bindings::ElementBinding;
use crate::dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function;
use crate::dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods;
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
Expand Down Expand Up @@ -39,7 +38,6 @@ use crate::dom::document::{determine_policy_for_token, Document, LayoutDocumentH
use crate::dom::documentfragment::DocumentFragment;
use crate::dom::domrect::DOMRect;
use crate::dom::domtokenlist::DOMTokenList;
use crate::dom::event::Event;
use crate::dom::eventtarget::EventTarget;
use crate::dom::htmlanchorelement::HTMLAnchorElement;
use crate::dom::htmlbodyelement::{HTMLBodyElement, HTMLBodyElementLayoutHelpers};
Expand Down Expand Up @@ -3222,52 +3220,6 @@ impl Element {
}
}

/// Please call this method *only* for real click events
///
/// <https://html.spec.whatwg.org/multipage/#run-authentic-click-activation-steps>
///
/// Use an element's synthetic click activation (or handle_event) for any script-triggered clicks.
/// If the spec says otherwise, check with Manishearth first
pub fn authentic_click_activation(&self, event: &Event) {
// Not explicitly part of the spec, however this helps enforce the invariants
// required to save state between pre-activation and post-activation
// since we cannot nest authentic clicks (unlike synthetic click activation, where
// the script can generate more click events from the handler)
assert!(!self.click_in_progress());

let target = self.upcast();
// Step 2 (requires canvas support)
// Step 3
self.set_click_in_progress(true);
// Step 4
let e = self.nearest_activable_element();
match e {
Some(ref el) => match el.as_maybe_activatable() {
Some(elem) => {
// Step 5-6
elem.pre_click_activation();
event.fire(target);
if !event.DefaultPrevented() {
// post click activation
elem.activation_behavior(event, target);
} else {
elem.canceled_activation();
}
},
// Step 6
None => {
event.fire(target);
},
},
// Step 6
None => {
event.fire(target);
},
}
// Step 7
self.set_click_in_progress(false);
}

// https://html.spec.whatwg.org/multipage/#language
pub fn get_lang(&self) -> String {
self.upcast::<Node>()
Expand Down

0 comments on commit 01aba1f

Please sign in to comment.