aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/script/dom/activation.rs74
-rw-r--r--components/script/dom/element.rs81
2 files changed, 131 insertions, 24 deletions
diff --git a/components/script/dom/activation.rs b/components/script/dom/activation.rs
index cb803d67ba1..98e8d435c12 100644
--- a/components/script/dom/activation.rs
+++ b/components/script/dom/activation.rs
@@ -2,26 +2,60 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use dom::bindings::codegen::InheritTypes::HTMLInputElementCast;
-use dom::bindings::js::JSRef;
-use dom::element::HTMLInputElementTypeId;
-use dom::htmlinputelement::HTMLInputElement;
-use dom::node::{ElementNodeTypeId, Node, NodeHelpers};
-
-pub trait Activatable {}
-
-
-/// Obtain an Activatable instance for a given Node-derived object,
-/// if it is activatable
-pub fn activation_vtable_for<'a>(node: &'a JSRef<'a, Node>) -> Option<&'a Activatable + 'a> {
- match node.type_id() {
- ElementNodeTypeId(HTMLInputElementTypeId) => {
- let _element: &'a JSRef<'a, HTMLInputElement> = HTMLInputElementCast::to_borrowed_ref(node).unwrap();
- // Some(element as &'a VirtualMethods + 'a)
- None
- },
- _ => {
- None
+use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
+use dom::bindings::codegen::InheritTypes::{EventCast, EventTargetCast};
+use dom::bindings::js::{JSRef, Temporary, OptionalRootable};
+use dom::element::{Element, ActivationElementHelpers};
+use dom::event::{Event, EventHelpers};
+use dom::eventtarget::{EventTarget, EventTargetHelpers};
+use dom::mouseevent::MouseEvent;
+use dom::node::window_from_node;
+
+
+/// Trait for elements with defined activation behavior
+pub trait Activatable : Copy {
+ fn as_element(&self) -> Temporary<Element>;
+
+ // https://html.spec.whatwg.org/multipage/interaction.html#run-pre-click-activation-steps
+ fn pre_click_activation(&self);
+
+ // https://html.spec.whatwg.org/multipage/interaction.html#run-canceled-activation-steps
+ fn canceled_activation(&self);
+
+ // https://html.spec.whatwg.org/multipage/interaction.html#run-post-click-activation-steps
+ fn post_click_activation(&self);
+
+ // https://html.spec.whatwg.org/multipage/interaction.html#run-synthetic-click-activation-steps
+ fn synthetic_click_activation(&self, ctrlKey: bool, shiftKey: bool, altKey: bool, metaKey: bool) {
+ let element = self.as_element().root();
+ // Step 1
+ if element.click_in_progress() {
+ return;
}
+ // Step 2
+ element.set_click_in_progress(true);
+ // Step 3
+ self.pre_click_activation();
+
+ // Step 4
+ // https://html.spec.whatwg.org/multipage/webappapis.html#fire-a-synthetic-mouse-event
+ let win = window_from_node(*element).root();
+ let target: JSRef<EventTarget> = EventTargetCast::from_ref(*element);
+ let mouse = MouseEvent::new(*win, "click".to_string(), false, false, Some(*win), 1,
+ 0, 0, 0, 0, ctrlKey, shiftKey, altKey, metaKey,
+ 0, None).root();
+ let event: JSRef<Event> = EventCast::from_ref(*mouse);
+ event.set_trusted(true);
+ target.dispatch_event_with_target(None, event).ok();
+
+ // Step 5
+ if event.DefaultPrevented() {
+ self.canceled_activation();
+ } else {
+ self.post_click_activation();
+ }
+
+ // Step 6
+ element.set_click_in_progress(false);
}
}
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
index d3b5b1946aa..ca12d6f2afd 100644
--- a/components/script/dom/element.rs
+++ b/components/script/dom/element.rs
@@ -4,16 +4,18 @@
//! Element nodes.
+use dom::activation::Activatable;
use dom::attr::{Attr, ReplacedAttr, FirstSetAttr, AttrHelpers, AttrHelpersForLayout};
use dom::attr::{AttrValue, StringAttrValue, UIntAttrValue, AtomAttrValue};
use dom::namednodemap::NamedNodeMap;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
+use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use dom::bindings::codegen::Bindings::ElementBinding;
use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
use dom::bindings::codegen::Bindings::NamedNodeMapBinding::NamedNodeMapMethods;
-use dom::bindings::codegen::InheritTypes::{ElementDerived, HTMLInputElementDerived};
-use dom::bindings::codegen::InheritTypes::{HTMLTableCellElementDerived, NodeCast};
+use dom::bindings::codegen::InheritTypes::{ElementDerived, HTMLInputElementDerived, HTMLTableCellElementDerived};
+use dom::bindings::codegen::InheritTypes::{HTMLInputElementCast, NodeCast, EventTargetCast, ElementCast};
use dom::bindings::js::{MutNullableJS, JS, JSRef, Temporary, TemporaryPushable};
use dom::bindings::js::{OptionalSettable, OptionalRootable, Root};
use dom::bindings::utils::{Reflectable, Reflector};
@@ -24,7 +26,8 @@ use dom::domrect::DOMRect;
use dom::domrectlist::DOMRectList;
use dom::document::{Document, DocumentHelpers, LayoutDocumentHelpers};
use dom::domtokenlist::DOMTokenList;
-use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::event::Event;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId, EventTargetHelpers};
use dom::htmlcollection::HTMLCollection;
use dom::htmlinputelement::{HTMLInputElement, RawLayoutHTMLInputElementHelpers};
use dom::htmlserializer::serialize;
@@ -41,7 +44,7 @@ use servo_util::namespace;
use servo_util::str::{DOMString, LengthOrPercentageOrAuto};
use std::ascii::AsciiExt;
-use std::cell::{Ref, RefMut};
+use std::cell::{Cell, Ref, RefMut};
use std::default::Default;
use std::mem;
use string_cache::{Atom, Namespace, QualName};
@@ -57,6 +60,7 @@ pub struct Element {
style_attribute: DOMRefCell<Option<style::PropertyDeclarationBlock>>,
attr_list: MutNullableJS<NamedNodeMap>,
class_list: MutNullableJS<DOMTokenList>,
+ click_in_progress: Cell<bool>,
}
impl ElementDerived for EventTarget {
@@ -175,6 +179,7 @@ impl Element {
attr_list: Default::default(),
class_list: Default::default(),
style_attribute: DOMRefCell::new(None),
+ click_in_progress: Cell::new(false),
}
}
@@ -1183,3 +1188,71 @@ impl<'a> style::TElement<'a> for JSRef<'a, Element> {
}
}
}
+
+pub trait ActivationElementHelpers<'a> {
+ fn as_maybe_activatable(&'a self) -> Option<&'a Activatable + 'a>;
+ fn click_in_progress(self) -> bool;
+ fn set_click_in_progress(self, click: bool);
+ fn nearest_activable_element(self) -> Option<JSRef<'a, Element>>;
+ fn authentic_click_activation<'b>(self, event: JSRef<'b, Event>);
+}
+
+impl<'a> ActivationElementHelpers<'a> for JSRef<'a, Element> {
+ fn as_maybe_activatable(&'a self) -> Option<&'a Activatable + 'a> {
+ let node: JSRef<Node> = NodeCast::from_ref(*self);
+ match node.type_id() {
+ ElementNodeTypeId(HTMLInputElementTypeId) => {
+ let _element: &'a JSRef<'a, HTMLInputElement> = HTMLInputElementCast::to_borrowed_ref(self).unwrap();
+ // Some(element as &'a Activatable + 'a)
+ None
+ },
+ _ => {
+ None
+ }
+ }
+ }
+
+ fn click_in_progress(self) -> bool {
+ self.click_in_progress.get()
+ }
+
+ fn set_click_in_progress(self, click: bool) {
+ self.click_in_progress.set(click)
+ }
+
+ // https://html.spec.whatwg.org/multipage/interaction.html#nearest-activatable-element
+ fn nearest_activable_element(self) -> Option<JSRef<'a, Element>> {
+ let node: JSRef<Node> = NodeCast::from_ref(self);
+ node.ancestors()
+ .filter_map(|node| ElementCast::to_ref(node))
+ .filter(|e| e.as_maybe_activatable().is_some()).next()
+ }
+
+ // https://html.spec.whatwg.org/multipage/interaction.html#run-authentic-click-activation-steps
+ fn authentic_click_activation<'b>(self, event: JSRef<'b, Event>) {
+ let target: JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ // Step 2 (requires canvas support)
+ // Step 3
+ self.set_click_in_progress(true);
+ // Step 4
+ let e = self.nearest_activable_element();
+ // Step 5
+ e.map(|a| a.as_maybe_activatable().map(|el| el.pre_click_activation()));
+ // Step 6
+ target.dispatch_event_with_target(None, event).ok();
+ e.map(|el| {
+ if NodeCast::from_ref(el).is_in_doc() {
+ return; // XXXManishearth do we need this check?
+ }
+ el.as_maybe_activatable().map(|a| {
+ if event.DefaultPrevented() {
+ a.post_click_activation();
+ } else {
+ a.canceled_activation();
+ }
+ });
+ });
+ // Step 7
+ self.set_click_in_progress(false);
+ }
+}