mirror of
https://github.com/servo/servo.git
synced 2025-07-23 07:13:52 +01:00
Implement basic (unhooked) framework for element activation
This commit is contained in:
parent
c3fdd60adc
commit
ddfa0c7de7
2 changed files with 129 additions and 22 deletions
|
@ -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 {}
|
||||
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;
|
||||
|
||||
|
||||
/// 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
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue