servo: Merge #5725 - added dispatching for mousedown and mouseup events, fixes #5705 (from pgonda:dispatch-mousedown-mouseup); r=jdm
authorPeter <ptrgonda@gmail.com>
Thu, 30 Apr 2015 12:22:03 -0500
changeset 382935 5455270ea085a0f4dc8924b201cdbe2562a2e549
parent 382934 13b2f2c347c2991b770a783920ecd5b84be71b22
child 382936 ac1a2d43bd8ea4eb5ea4103f2a039ec003519cf0
push id7198
push userjlorenzo@mozilla.com
push dateTue, 18 Apr 2017 12:07:49 +0000
treeherdermozilla-beta@d57aa49c3948 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm
servo: Merge #5725 - added dispatching for mousedown and mouseup events, fixes #5705 (from pgonda:dispatch-mousedown-mouseup); r=jdm Source-Repo: https://github.com/servo/servo Source-Revision: 88ed4e58e16e1ccf2957e1838175b5195c5d7950
servo/components/script/dom/document.rs
servo/components/script/script_task.rs
servo/ports/glutin/window.rs
servo/tests/html/test_mouse_down_mouse_up_click.html
--- a/servo/components/script/dom/document.rs
+++ b/servo/components/script/dom/document.rs
@@ -215,25 +215,26 @@ pub trait DocumentHelpers<'a> {
     fn get_focused_element(self) -> Option<Temporary<Element>>;
     fn is_scripting_enabled(self) -> bool;
     fn begin_focus_transaction(self);
     fn request_focus(self, elem: JSRef<Element>);
     fn commit_focus_transaction(self, focus_type: FocusType);
     fn title_changed(self);
     fn send_title_to_compositor(self);
     fn dirty_all_nodes(self);
-    fn handle_click_event(self, js_runtime: *mut JSRuntime,
-                          button: MouseButton, point: Point2D<f32>);
     fn dispatch_key_event(self, key: Key, state: KeyState,
         modifiers: KeyModifiers, compositor: &mut Box<ScriptListener+'static>);
     fn node_from_nodes_and_strings(self, nodes: Vec<NodeOrString>)
                                    -> Fallible<Temporary<Node>>;
     fn get_body_attribute(self, local_name: &Atom) -> DOMString;
     fn set_body_attribute(self, local_name: &Atom, value: DOMString);
 
+    fn handle_mouse_event(self, js_runtime: *mut JSRuntime,
+                          button: MouseButton, point: Point2D<f32>,
+                          mouse_event_type: MouseEventType);
     /// Handles a mouse-move event coming from the compositor.
     fn handle_mouse_move_event(self,
                                js_runtime: *mut JSRuntime,
                                point: Point2D<f32>,
                                prev_mouse_over_targets: &mut RootedVec<JS<Node>>);
 
     fn set_current_script(self, script: Option<JSRef<HTMLScriptElement>>);
     fn trigger_mozbrowser_event(self, event: MozBrowserEvent);
@@ -506,19 +507,25 @@ impl<'a> DocumentHelpers<'a> for JSRef<'
 
     fn dirty_all_nodes(self) {
         let root: JSRef<Node> = NodeCast::from_ref(self);
         for node in root.traverse_preorder() {
             node.root().r().dirty(NodeDamage::OtherNodeDamage)
         }
     }
 
-    fn handle_click_event(self, js_runtime: *mut JSRuntime,
-                          _button: MouseButton, point: Point2D<f32>) {
-        debug!("ClickEvent: clicked at {:?}", point);
+    fn handle_mouse_event(self, js_runtime: *mut JSRuntime,
+                          _button: MouseButton, point: Point2D<f32>,
+                          mouse_event_type: MouseEventType) {
+        let mouse_event_type_string = match mouse_event_type {
+            MouseEventType::Click => "click".to_owned(),
+            MouseEventType::MouseUp => "mouseup".to_owned(),
+            MouseEventType::MouseDown => "mousedown".to_owned(),
+        };
+        debug!("{}: at {:?}", mouse_event_type_string, point);
         let node = match self.hit_test(&point) {
             Some(node_address) => {
                 debug!("node address is {:?}", node_address.0);
                 node::from_untrusted_node_address(js_runtime, node_address)
             },
             None => return,
         }.root();
 
@@ -529,47 +536,57 @@ impl<'a> DocumentHelpers<'a> for JSRef<'
                 match parent.and_then(ElementCast::to_temporary) {
                     Some(parent) => parent,
                     None => return,
                 }
             },
         }.root();
 
         let node: JSRef<Node> = NodeCast::from_ref(el.r());
-        debug!("clicked on {:?}", node.debug_str());
+        debug!("{} on {:?}", mouse_event_type_string, node.debug_str());
         // Prevent click event if form control element is disabled.
-        if node.click_event_filter_by_disabled_state() {
-            return;
+        if let  MouseEventType::Click = mouse_event_type {
+            if node.click_event_filter_by_disabled_state() {
+                return;
+            }
+
+            self.begin_focus_transaction();
         }
 
-        self.begin_focus_transaction();
-
         let window = self.window.root();
 
         // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-click
         let x = point.x as i32;
         let y = point.y as i32;
         let event = MouseEvent::new(window.r(),
-                                    "click".to_owned(),
+                                    mouse_event_type_string,
                                     EventBubbles::Bubbles,
                                     EventCancelable::Cancelable,
                                     Some(window.r()),
                                     0i32,
                                     x, y, x, y,
                                     false, false, false, false,
                                     0i16,
                                     None).root();
         let event: JSRef<Event> = EventCast::from_ref(event.r());
 
         // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#trusted-events
         event.set_trusted(true);
         // https://html.spec.whatwg.org/multipage/#run-authentic-click-activation-steps
-        el.r().authentic_click_activation(event);
+        match mouse_event_type {
+            MouseEventType::Click => el.r().authentic_click_activation(event),
+            _ =>  {
+                let target: JSRef<EventTarget> = EventTargetCast::from_ref(node);
+                event.fire(target);
+            },
+        }
 
-        self.commit_focus_transaction(FocusType::Element);
+        if let MouseEventType::Click = mouse_event_type {
+            self.commit_focus_transaction(FocusType::Element);
+        }
         window.r().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, ReflowReason::MouseEvent);
     }
 
     fn handle_mouse_move_event(self,
                                js_runtime: *mut JSRuntime,
                                point: Point2D<f32>,
                                prev_mouse_over_targets: &mut RootedVec<JS<Node>>) {
         // Build a list of elements that are currently under the mouse.
@@ -773,16 +790,22 @@ impl<'a> DocumentHelpers<'a> for JSRef<'
                                                               subpage_id,
                                                               event);
                 chan.send(event).unwrap();
             }
         }
     }
 }
 
+pub enum MouseEventType {
+    Click,
+    MouseDown,
+    MouseUp,
+}
+
 #[derive(PartialEq)]
 pub enum DocumentSource {
     FromParser,
     NotFromParser,
 }
 
 pub trait LayoutDocumentHelpers {
     #[allow(unsafe_code)]
--- a/servo/components/script/script_task.rs
+++ b/servo/components/script/script_task.rs
@@ -26,17 +26,17 @@ use dom::bindings::conversions::FromJSVa
 use dom::bindings::conversions::StringificationBehavior;
 use dom::bindings::js::{JS, JSRef, OptionalRootable, RootCollection};
 use dom::bindings::js::{RootCollectionPtr, Rootable, RootedReference};
 use dom::bindings::js::Temporary;
 use dom::bindings::refcounted::{LiveDOMReferences, Trusted, TrustedReference};
 use dom::bindings::structuredclone::StructuredCloneData;
 use dom::bindings::trace::{JSTraceable, trace_collections, RootedVec};
 use dom::bindings::utils::{wrap_for_same_compartment, pre_wrap};
-use dom::document::{Document, IsHTMLDocument, DocumentHelpers, DocumentProgressHandler, DocumentProgressTask, DocumentSource};
+use dom::document::{Document, IsHTMLDocument, DocumentHelpers, DocumentProgressHandler, DocumentProgressTask, DocumentSource, MouseEventType};
 use dom::element::{Element, AttributeHandlers};
 use dom::event::{Event, EventHelpers, EventBubbles, EventCancelable};
 use dom::htmliframeelement::{HTMLIFrameElement, HTMLIFrameElementHelpers};
 use dom::uievent::UIEvent;
 use dom::eventtarget::EventTarget;
 use dom::node::{Node, NodeHelpers, NodeDamage, window_from_node};
 use dom::window::{Window, WindowHelpers, ScriptHelpers, ReflowReason};
 use dom::worker::TrustedWorkerAddress;
@@ -46,17 +46,17 @@ use layout_interface;
 use page::{Page, IterablePage, Frame};
 use timers::TimerId;
 use devtools;
 use webdriver_handlers;
 
 use devtools_traits::{DevtoolsControlChan, DevtoolsControlPort, DevtoolsPageInfo};
 use devtools_traits::{DevtoolsControlMsg, DevtoolScriptControlMsg};
 use devtools_traits::{TimelineMarker, TimelineMarkerType, TracingMetadata};
-use script_traits::CompositorEvent;
+use script_traits::{CompositorEvent, MouseButton};
 use script_traits::CompositorEvent::{ResizeEvent, ClickEvent};
 use script_traits::CompositorEvent::{MouseDownEvent, MouseUpEvent};
 use script_traits::CompositorEvent::{MouseMoveEvent, KeyEvent};
 use script_traits::{NewLayoutInfo, OpaqueScriptLayoutChannel};
 use script_traits::{ConstellationControlMsg, ScriptControlChan};
 use script_traits::ScriptTaskFactory;
 use webdriver_traits::WebDriverScriptCommand;
 use msg::compositor_msg::ReadyState::{FinishedLoading, Loading, PerformingLayout};
@@ -1225,27 +1225,27 @@ impl ScriptTask {
                 if self.need_emit_timeline_marker(TimelineMarkerType::DOMEvent) {
                    _marker = AutoDOMEventMarker::new(self);
                 }
 
                 self.handle_resize_event(pipeline_id, new_size);
             }
 
             ClickEvent(button, point) => {
-                let _marker;
-                if self.need_emit_timeline_marker(TimelineMarkerType::DOMEvent) {
-                    _marker = AutoDOMEventMarker::new(self);
-                }
-                let page = get_page(&self.root_page(), pipeline_id);
-                let document = page.document().root();
-                document.r().handle_click_event(self.js_runtime.rt(), button, point);
+                self.handle_mouse_event(pipeline_id, MouseEventType::Click, button, point);
             }
 
-            MouseDownEvent(..) => {}
-            MouseUpEvent(..) => {}
+            MouseDownEvent(button, point) => {
+                self.handle_mouse_event(pipeline_id, MouseEventType::MouseDown, button, point);
+            }
+
+            MouseUpEvent(button, point) => {
+                self.handle_mouse_event(pipeline_id, MouseEventType::MouseUp, button, point);
+            }
+
             MouseMoveEvent(point) => {
                 let _marker;
                 if self.need_emit_timeline_marker(TimelineMarkerType::DOMEvent) {
                     _marker = AutoDOMEventMarker::new(self);
                 }
                 let page = get_page(&self.root_page(), pipeline_id);
                 let document = page.document().root();
                 let mut mouse_over_targets = RootedVec::new();
@@ -1263,16 +1263,26 @@ impl ScriptTask {
                 let page = get_page(&self.root_page(), pipeline_id);
                 let document = page.document().root();
                 document.r().dispatch_key_event(
                     key, state, modifiers, &mut *self.compositor.borrow_mut());
             }
         }
     }
 
+    fn handle_mouse_event(&self, pipeline_id: PipelineId, mouse_event_type: MouseEventType, button: MouseButton, point: Point2D<f32>) {
+        let _marker;
+        if self.need_emit_timeline_marker(TimelineMarkerType::DOMEvent) {
+            _marker = AutoDOMEventMarker::new(self);
+        }
+        let page = get_page(&self.root_page(), pipeline_id);
+        let document = page.document().root();
+        document.r().handle_mouse_event(self.js_runtime.rt(), button, point, mouse_event_type);
+    }
+
     /// https://html.spec.whatwg.org/multipage/#navigating-across-documents
     /// The entry point for content to notify that a new load has been requested
     /// for the given pipeline (specifically the "navigate" algorithm).
     fn handle_navigate(&self, pipeline_id: PipelineId, subpage_id: Option<SubpageId>, load_data: LoadData) {
         match subpage_id {
             Some(subpage_id) => {
                 let borrowed_page = self.root_page();
                 let iframe = borrowed_page.find(pipeline_id).and_then(|page| {
--- a/servo/ports/glutin/window.rs
+++ b/servo/ports/glutin/window.rs
@@ -232,31 +232,32 @@ impl Window {
         let max_pixel_dist = 10f64;
         let event = match action {
             ElementState::Pressed => {
                 self.mouse_down_point.set(Point2D(x, y));
                 self.mouse_down_button.set(Some(button));
                 MouseWindowEvent::MouseDown(MouseButton::Left, TypedPoint2D(x as f32, y as f32))
             }
             ElementState::Released => {
+                let mouse_up_event = MouseWindowEvent::MouseUp(MouseButton::Left, TypedPoint2D(x as f32, y as f32));
                 match self.mouse_down_button.get() {
-                    None => (),
+                    None => mouse_up_event,
                     Some(but) if button == but => {
                         let pixel_dist = self.mouse_down_point.get() - Point2D(x, y);
                         let pixel_dist = ((pixel_dist.x * pixel_dist.x +
                                            pixel_dist.y * pixel_dist.y) as f64).sqrt();
                         if pixel_dist < max_pixel_dist {
-                            let click_event = MouseWindowEvent::Click(
-                                MouseButton::Left, TypedPoint2D(x as f32, y as f32));
-                            self.event_queue.borrow_mut().push(WindowEvent::MouseWindowEventClass(click_event));
+                            self.event_queue.borrow_mut().push(WindowEvent::MouseWindowEventClass(mouse_up_event));
+                            MouseWindowEvent::Click(MouseButton::Left, TypedPoint2D(x as f32, y as f32))
+                        } else {
+                            mouse_up_event
                         }
-                    }
-                    Some(_) => (),
+                    },
+                    Some(_) => mouse_up_event,
                 }
-                MouseWindowEvent::MouseUp(MouseButton::Left, TypedPoint2D(x as f32, y as f32))
             }
         };
         self.event_queue.borrow_mut().push(WindowEvent::MouseWindowEventClass(event));
     }
 
     #[cfg(not(target_os="linux"))]
     fn handle_next_event(&self) -> bool {
         let event = self.window.wait_events().next().unwrap();
new file mode 100644
--- /dev/null
+++ b/servo/tests/html/test_mouse_down_mouse_up_click.html
@@ -0,0 +1,14 @@
+<body>
+<input id="clicked">
+<script>
+  document.getElementById("clicked").addEventListener("mousedown", function () {
+	  window.alert("mousedown"); },
+	  false);
+  document.getElementById("clicked").addEventListener('mouseup', function() {
+	  window.alert("mouseup"); },
+	  false);
+  document.getElementById("clicked").addEventListener("click", function() {
+	  window.alert("clicked"); },
+	  false);
+</script>
+</body>