servo: Merge #8785 - Add slow path for hit testing of iframe behind positioned content layer (from mbrubeck:fixed-hit-test); r=pcwalton
authorMatt Brubeck <mbrubeck@limpet.net>
Fri, 04 Dec 2015 02:24:52 +0500
changeset 384347 3a594787e82bdf251ec65e3877222c9fc2305aae
parent 384346 a97a4ba06241d98551eadc74dc5517df8613295e
child 384348 4da2d30e3debd6543e8831943f5f2a0a1d471db3
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)
reviewerspcwalton
servo: Merge #8785 - Add slow path for hit testing of iframe behind positioned content layer (from mbrubeck:fixed-hit-test); r=pcwalton Fixes browser.html blocker #8759. r? @pcwalton This adds a slow path for cases where the compositor's layer-based hit testing is incorrect. If the script task discovers that a mouse event should have been dispatched to an iframe, it bounces the event back to the constellation to be forwarded to the correct pipeline. This isn't terribly slow (on the slow path, it adds one extra round-trip message between script and constellation), but if we want to optimize this better we could instead replace the compositor's layer hit testing with display list hit testing in the paint task. This would be a more complicated change that I think we should save for a follow-up. This only fixes mouse input for now. A basically-identical change will be needed for touch-screen input, whether we stick with this approach or switch to the paint task. Source-Repo: https://github.com/servo/servo Source-Revision: bc62b5aadb62267582fbd65daa28438ce6c6ac9c
servo/components/compositing/compositor.rs
servo/components/compositing/compositor_layer.rs
servo/components/compositing/constellation.rs
servo/components/compositing/windowing.rs
servo/components/msg/constellation_msg.rs
servo/components/script/dom/document.rs
servo/components/script/dom/htmliframeelement.rs
servo/components/script/script_task.rs
servo/components/script_traits/lib.rs
servo/ports/cef/browser_host.rs
servo/ports/glutin/window.rs
servo/ports/gonk/Cargo.lock
servo/ports/gonk/Cargo.toml
servo/ports/gonk/src/input.rs
servo/ports/gonk/src/main.rs
--- a/servo/components/compositing/compositor.rs
+++ b/servo/components/compositing/compositor.rs
@@ -25,23 +25,23 @@ use layers::platform::surface::NativeDis
 use layers::rendergl;
 use layers::rendergl::RenderContext;
 use layers::scene::Scene;
 use layout_traits::LayoutControlChan;
 use msg::compositor_msg::{Epoch, EventResult, FrameTreeId, LayerId, LayerKind};
 use msg::compositor_msg::{LayerProperties, ScrollPolicy};
 use msg::constellation_msg::CompositorMsg as ConstellationMsg;
 use msg::constellation_msg::{AnimationState, Image, PixelFormat};
-use msg::constellation_msg::{Key, KeyModifiers, KeyState, LoadData};
+use msg::constellation_msg::{Key, KeyModifiers, KeyState, LoadData, MouseButton};
 use msg::constellation_msg::{NavigationDirection, PipelineId, WindowSizeData};
 use pipeline::CompositionPipeline;
 use profile_traits::mem::{self, ReportKind, Reporter, ReporterRequest};
 use profile_traits::time::{self, ProfilerCategory, profile};
 use script_traits::CompositorEvent::{MouseMoveEvent, TouchEvent};
-use script_traits::{ConstellationControlMsg, LayoutControlMsg, MouseButton};
+use script_traits::{ConstellationControlMsg, LayoutControlMsg};
 use script_traits::{TouchEventType, TouchId};
 use scrolling::ScrollingTimerProxy;
 use std::collections::hash_map::Entry::{Occupied, Vacant};
 use std::collections::{HashMap, HashSet};
 use std::fs::File;
 use std::mem as std_mem;
 use std::rc::Rc;
 use std::slice::bytes::copy_memory;
--- a/servo/components/compositing/compositor_layer.rs
+++ b/servo/components/compositing/compositor_layer.rs
@@ -7,19 +7,19 @@ use compositor::IOCompositor;
 use euclid::length::Length;
 use euclid::point::{Point2D, TypedPoint2D};
 use euclid::rect::Rect;
 use euclid::size::TypedSize2D;
 use layers::color::Color;
 use layers::geometry::LayerPixel;
 use layers::layers::{Layer, LayerBufferSet};
 use msg::compositor_msg::{Epoch, LayerId, LayerProperties, ScrollPolicy};
-use msg::constellation_msg::PipelineId;
+use msg::constellation_msg::{MouseEventType, PipelineId};
 use script_traits::CompositorEvent;
-use script_traits::CompositorEvent::{ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent};
+use script_traits::CompositorEvent::{MouseButtonEvent, MouseMoveEvent};
 use script_traits::ConstellationControlMsg;
 use std::rc::Rc;
 use windowing::{MouseWindowEvent, WindowMethods};
 
 #[derive(Debug)]
 pub struct CompositorData {
     /// This layer's pipeline id. The compositor can associate this id with an
     /// actual CompositionPipeline.
@@ -367,21 +367,21 @@ impl CompositorLayer for Layer<Composito
     fn send_mouse_event<Window>(&self,
                                 compositor: &IOCompositor<Window>,
                                 event: MouseWindowEvent,
                                 cursor: TypedPoint2D<LayerPixel, f32>)
                                 where Window: WindowMethods {
         let event_point = cursor.to_untyped();
         let message = match event {
             MouseWindowEvent::Click(button, _) =>
-                ClickEvent(button, event_point),
+                MouseButtonEvent(MouseEventType::Click, button, event_point),
             MouseWindowEvent::MouseDown(button, _) =>
-                MouseDownEvent(button, event_point),
+                MouseButtonEvent(MouseEventType::MouseDown, button, event_point),
             MouseWindowEvent::MouseUp(button, _) =>
-                MouseUpEvent(button, event_point),
+                MouseButtonEvent(MouseEventType::MouseUp, button, event_point),
         };
         self.send_event(compositor, message);
     }
 
     fn send_mouse_move_event<Window>(&self,
                                      compositor: &IOCompositor<Window>,
                                      cursor: TypedPoint2D<LayerPixel, f32>)
                                      where Window: WindowMethods {
--- a/servo/components/compositing/constellation.rs
+++ b/servo/components/compositing/constellation.rs
@@ -608,16 +608,29 @@ impl<LTF: LayoutTaskFactory, STF: Script
                 self.handle_mozbrowser_event_msg(pipeline_id,
                                                  subpage_id,
                                                  event);
             }
             Request::Script(FromScriptMsg::Focus(pipeline_id)) => {
                 debug!("constellation got focus message");
                 self.handle_focus_msg(pipeline_id);
             }
+            Request::Script(FromScriptMsg::ForwardMouseButtonEvent(
+                    pipeline_id, event_type, button, point)) => {
+                if let Some(pipeline) = self.pipelines.get(&pipeline_id) {
+                    pipeline.script_chan.send(ConstellationControlMsg::SendEvent(pipeline_id,
+                        CompositorEvent::MouseButtonEvent(event_type, button, point)));
+                }
+            }
+            Request::Script(FromScriptMsg::ForwardMouseMoveEvent(pipeline_id, point)) => {
+                if let Some(pipeline) = self.pipelines.get(&pipeline_id) {
+                    pipeline.script_chan.send(ConstellationControlMsg::SendEvent(pipeline_id,
+                        CompositorEvent::MouseMoveEvent(Some(point))));
+                }
+            }
             Request::Script(FromScriptMsg::GetClipboardContents(sender)) => {
                 let result = self.clipboard_ctx.as_ref().map_or(
                     "".to_owned(),
                     |ctx| ctx.get_contents().unwrap_or_else(|e| {
                         debug!("Error getting clipboard contents ({}), defaulting to empty string", e);
                         "".to_owned()
                     })
                 );
--- a/servo/components/compositing/windowing.rs
+++ b/servo/components/compositing/windowing.rs
@@ -6,33 +6,32 @@
 
 use compositor_task::{CompositorProxy, CompositorReceiver};
 use euclid::point::TypedPoint2D;
 use euclid::scale_factor::ScaleFactor;
 use euclid::size::TypedSize2D;
 use euclid::{Point2D, Size2D};
 use layers::geometry::DevicePixel;
 use layers::platform::surface::NativeDisplay;
-use msg::constellation_msg::{Key, KeyModifiers, KeyState};
+use msg::constellation_msg::{Key, KeyModifiers, KeyState, MouseButton};
 use net_traits::net_error_list::NetError;
-use script_traits::{MouseButton, TouchEventType, TouchId};
+use script_traits::{TouchEventType, TouchId};
 use std::fmt::{Debug, Error, Formatter};
 use std::rc::Rc;
 use url::Url;
 use util::cursor::Cursor;
 use util::geometry::ScreenPx;
 
 #[derive(Clone)]
 pub enum MouseWindowEvent {
     Click(MouseButton, TypedPoint2D<DevicePixel, f32>),
     MouseDown(MouseButton, TypedPoint2D<DevicePixel, f32>),
     MouseUp(MouseButton, TypedPoint2D<DevicePixel, f32>),
 }
 
-
 #[derive(Clone)]
 pub enum WindowNavigateMsg {
     Forward,
     Back,
 }
 
 /// Events that the windowing system sends to Servo.
 #[derive(Clone)]
--- a/servo/components/msg/constellation_msg.rs
+++ b/servo/components/msg/constellation_msg.rs
@@ -2,16 +2,17 @@
  * 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/. */
 
 //! The high-level interface from script to constellation. Using this abstract interface helps
 //! reduce coupling between these two components.
 
 use canvas_traits::CanvasMsg;
 use compositor_msg::Epoch;
+use euclid::point::Point2D;
 use euclid::scale_factor::ScaleFactor;
 use euclid::size::{Size2D, TypedSize2D};
 use hyper::header::Headers;
 use hyper::method::Method;
 use ipc_channel::ipc::{self, IpcReceiver, IpcSender, IpcSharedMemory};
 use layers::geometry::DevicePixel;
 use offscreen_gl_context::GLContextAttributes;
 use serde::{Deserialize, Serialize};
@@ -278,16 +279,20 @@ pub enum ScriptMsg {
                          IpcSender<Result<(IpcSender<CanvasMsg>, usize), String>>),
     /// Dispatched after the DOM load event has fired on a document
     /// Causes a `load` event to be dispatched to any enclosing frame context element
     /// for the given pipeline.
     DOMLoad(PipelineId),
     Failure(Failure),
     /// Notifies the constellation that this frame has received focus.
     Focus(PipelineId),
+    /// Re-send a mouse button event that was sent to the parent window.
+    ForwardMouseButtonEvent(PipelineId, MouseEventType, MouseButton, Point2D<f32>),
+    /// Re-send a mouse move event that was sent to the parent window.
+    ForwardMouseMoveEvent(PipelineId, Point2D<f32>),
     /// Requests that the constellation retrieve the current contents of the clipboard
     GetClipboardContents(IpcSender<String>),
     /// <head> tag finished parsing
     HeadParsed,
     LoadComplete(PipelineId),
     LoadUrl(PipelineId, LoadData),
     /// Dispatch a mozbrowser event to a given iframe. Only available in experimental mode.
     MozBrowserEvent(PipelineId, SubpageId, MozBrowserEvent),
@@ -302,16 +307,34 @@ pub enum ScriptMsg {
     /// Requests that the constellation set the contents of the clipboard
     SetClipboardContents(String),
     /// Requests that the constellation inform the compositor of the a cursor change.
     SetCursor(Cursor),
     /// Notifies the constellation that the viewport has been constrained in some manner
     ViewportConstrained(PipelineId, ViewportConstraints),
 }
 
+#[derive(Deserialize, HeapSizeOf, Serialize)]
+pub enum MouseEventType {
+    Click,
+    MouseDown,
+    MouseUp,
+}
+
+/// The mouse button involved in the event.
+#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
+pub enum MouseButton {
+    /// The left mouse button.
+    Left,
+    /// The middle mouse button.
+    Middle,
+    /// The right mouse button.
+    Right,
+}
+
 /// Messages from the paint task to the constellation.
 #[derive(Deserialize, Serialize)]
 pub enum PaintMsg {
     Ready(PipelineId),
     Failure(Failure),
 }
 
 #[derive(Clone, Eq, PartialEq, Deserialize, Serialize, Debug)]
--- a/servo/components/script/dom/document.rs
+++ b/servo/components/script/dom/document.rs
@@ -1,21 +1,24 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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 document_loader::{DocumentLoader, LoadType};
 use dom::attr::{Attr, AttrValue};
 use dom::bindings::cell::DOMRefCell;
+use dom::bindings::codegen::Bindings::DOMRectBinding::DOMRectMethods;
 use dom::bindings::codegen::Bindings::DocumentBinding;
 use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyState};
+use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
 use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
 use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
 use dom::bindings::codegen::Bindings::EventHandlerBinding::OnErrorEventHandlerNonNull;
 use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods;
+use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
 use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
 use dom::bindings::codegen::Bindings::NodeFilterBinding::NodeFilter;
 use dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceMethods;
 use dom::bindings::codegen::Bindings::TouchBinding::TouchMethods;
 use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
 use dom::bindings::codegen::UnionTypes::NodeOrString;
 use dom::bindings::error::{Error, ErrorResult, Fallible};
 use dom::bindings::global::GlobalRef;
@@ -77,23 +80,24 @@ use ipc_channel::ipc::{self, IpcSender};
 use js::jsapi::{JSContext, JSObject, JSRuntime};
 use layout_interface::{HitTestResponse, MouseOverResponse};
 use layout_interface::{LayoutChan, Msg};
 use layout_interface::{ReflowGoal, ReflowQueryType};
 use msg::compositor_msg::ScriptToCompositorMsg;
 use msg::constellation_msg::ScriptMsg as ConstellationMsg;
 use msg::constellation_msg::{ALT, CONTROL, SHIFT, SUPER};
 use msg::constellation_msg::{AnimationState, PipelineId};
-use msg::constellation_msg::{ConstellationChan, FocusType, Key, KeyModifiers, KeyState, MozBrowserEvent, SubpageId};
+use msg::constellation_msg::{ConstellationChan, FocusType, Key, KeyModifiers, KeyState};
+use msg::constellation_msg::{MouseButton, MouseEventType, MozBrowserEvent, SubpageId};
 use net_traits::ControlMsg::{GetCookiesForUrl, SetCookiesForUrl};
 use net_traits::CookieSource::NonHTTP;
 use net_traits::{AsyncResponseTarget, PendingAsyncLoad};
 use num::ToPrimitive;
 use script_task::{MainThreadScriptMsg, Runnable};
-use script_traits::{MouseButton, TouchEventType, TouchId, UntrustedNodeAddress};
+use script_traits::{TouchEventType, TouchId, UntrustedNodeAddress};
 use std::ascii::AsciiExt;
 use std::borrow::ToOwned;
 use std::boxed::FnBox;
 use std::cell::{Cell, Ref, RefMut};
 use std::collections::HashMap;
 use std::collections::hash_map::Entry::{Occupied, Vacant};
 use std::default::Default;
 use std::iter::FromIterator;
@@ -598,17 +602,17 @@ impl Document {
         let root = self.upcast::<Node>();
         for node in root.traverse_preorder() {
             node.dirty(NodeDamage::OtherNodeDamage)
         }
     }
 
     pub fn handle_mouse_event(&self,
                               js_runtime: *mut JSRuntime,
-                              _button: MouseButton,
+                              button: MouseButton,
                               client_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, client_point);
@@ -629,16 +633,31 @@ impl Document {
                 let parent = node.GetParentNode();
                 match parent.and_then(Root::downcast::<Element>) {
                     Some(parent) => parent,
                     None => return,
                 }
             },
         };
 
+        // If the target is an iframe, forward the event to the child document.
+        if let Some(iframe) = el.downcast::<HTMLIFrameElement>() {
+            if let Some(pipeline_id) = iframe.pipeline_id() {
+                let rect = iframe.upcast::<Element>().GetBoundingClientRect();
+                let child_origin = Point2D::new(rect.X() as f32, rect.Y() as f32);
+                let child_point = client_point - child_origin;
+
+                let event = ConstellationMsg::ForwardMouseButtonEvent(pipeline_id,
+                                                                      mouse_event_type,
+                                                                      button, child_point);
+                self.window.constellation_chan().0.send(event).unwrap();
+            }
+            return;
+        }
+
         let node = el.upcast::<Node>();
         debug!("{} on {:?}", mouse_event_type_string, node.debug_str());
         // Prevent click event if form control element is disabled.
         if let MouseEventType::Click = mouse_event_type {
             if el.click_event_filter_by_disabled_state() {
                 return;
             }
 
@@ -761,21 +780,33 @@ impl Document {
                 }
             }
         }
 
         // Send mousemove event to topmost target
         if mouse_over_addresses.len() > 0 {
             let top_most_node = node::from_untrusted_node_address(js_runtime,
                                                                   mouse_over_addresses[0]);
+            let client_point = client_point.unwrap(); // Must succeed because hit test succeeded.
+
+            // If the target is an iframe, forward the event to the child document.
+            if let Some(iframe) = top_most_node.downcast::<HTMLIFrameElement>() {
+                if let Some(pipeline_id) = iframe.pipeline_id() {
+                    let rect = iframe.upcast::<Element>().GetBoundingClientRect();
+                    let child_origin = Point2D::new(rect.X() as f32, rect.Y() as f32);
+                    let child_point = client_point - child_origin;
+
+                    let event = ConstellationMsg::ForwardMouseMoveEvent(pipeline_id, child_point);
+                    self.window.constellation_chan().0.send(event).unwrap();
+                }
+                return;
+            }
 
             let target = top_most_node.upcast();
-            if let Some(client_point) = client_point {
-                self.fire_mouse_event(client_point, target, "mousemove".to_owned());
-            }
+            self.fire_mouse_event(client_point, target, "mousemove".to_owned());
         }
 
         // Store the current mouse over targets for next frame
         prev_mouse_over_targets.clear();
         prev_mouse_over_targets.append(&mut *mouse_over_targets);
 
         self.window.reflow(ReflowGoal::ForDisplay,
                            ReflowQueryType::NoQuery,
@@ -1357,24 +1388,16 @@ impl Document {
         self.dom_content_loaded_event_end.get()
     }
 
     pub fn get_dom_complete(&self) -> u64 {
         self.dom_complete.get()
     }
 }
 
-#[derive(HeapSizeOf)]
-pub enum MouseEventType {
-    Click,
-    MouseDown,
-    MouseUp,
-}
-
-
 #[derive(PartialEq, HeapSizeOf)]
 pub enum DocumentSource {
     FromParser,
     NotFromParser,
 }
 
 #[allow(unsafe_code)]
 pub trait LayoutDocumentHelpers {
--- a/servo/components/script/dom/htmliframeelement.rs
+++ b/servo/components/script/dom/htmliframeelement.rs
@@ -181,16 +181,21 @@ impl HTMLIFrameElement {
     }
 
     #[inline]
     pub fn containing_page_pipeline_id(&self) -> Option<PipelineId> {
         self.containing_page_pipeline_id.get()
     }
 
     #[inline]
+    pub fn pipeline_id(&self) -> Option<PipelineId> {
+        self.pipeline_id.get()
+    }
+
+    #[inline]
     pub fn subpage_id(&self) -> Option<SubpageId> {
         self.subpage_id.get()
     }
 
     pub fn pipeline(&self) -> Option<PipelineId> {
         self.pipeline_id.get()
     }
 
--- a/servo/components/script/script_task.rs
+++ b/servo/components/script/script_task.rs
@@ -26,18 +26,17 @@ use dom::bindings::codegen::Bindings::Do
 use dom::bindings::conversions::{FromJSValConvertible, StringificationBehavior};
 use dom::bindings::global::GlobalRef;
 use dom::bindings::inheritance::Castable;
 use dom::bindings::js::{JS, RootCollection, trace_roots};
 use dom::bindings::js::{Root, RootCollectionPtr, RootedReference};
 use dom::bindings::refcounted::{LiveDOMReferences, Trusted, TrustedReference, trace_refcounted_objects};
 use dom::bindings::trace::{JSTraceable, RootedVec, trace_traceables};
 use dom::bindings::utils::{DOM_CALLBACKS, WRAP_CALLBACKS};
-use dom::document::{Document, DocumentProgressHandler, IsHTMLDocument};
-use dom::document::{DocumentSource, MouseEventType};
+use dom::document::{Document, DocumentProgressHandler, DocumentSource, IsHTMLDocument};
 use dom::element::Element;
 use dom::event::{Event, EventBubbles, EventCancelable};
 use dom::htmlanchorelement::HTMLAnchorElement;
 use dom::node::{Node, NodeDamage, window_from_node};
 use dom::servohtmlparser::{ParserContext, ServoHTMLParser};
 use dom::uievent::UIEvent;
 use dom::window::{ReflowReason, ScriptHelpers, Window};
 use dom::worker::TrustedWorkerAddress;
@@ -60,34 +59,32 @@ use js::jsval::UndefinedValue;
 use js::rust::Runtime;
 use layout_interface::{ReflowQueryType};
 use layout_interface::{self, LayoutChan, NewLayoutTaskInfo, ReflowGoal, ScriptLayoutChan};
 use libc;
 use mem::heap_size_of_self_and_children;
 use msg::compositor_msg::{EventResult, LayerId, ScriptToCompositorMsg};
 use msg::constellation_msg::ScriptMsg as ConstellationMsg;
 use msg::constellation_msg::{ConstellationChan, FocusType, LoadData};
-use msg::constellation_msg::{MozBrowserEvent, PipelineId};
+use msg::constellation_msg::{MouseButton, MouseEventType, MozBrowserEvent, PipelineId};
 use msg::constellation_msg::{PipelineNamespace};
 use msg::constellation_msg::{SubpageId, WindowSizeData, WorkerId};
 use msg::webdriver_msg::WebDriverScriptCommand;
 use net_traits::LoadData as NetLoadData;
 use net_traits::image_cache_task::{ImageCacheChan, ImageCacheResult, ImageCacheTask};
 use net_traits::storage_task::StorageTask;
 use net_traits::{AsyncResponseTarget, ControlMsg, LoadConsumer, Metadata, ResourceTask};
 use network_listener::NetworkListener;
 use page::{Frame, IterablePage, Page};
 use parse::html::{ParseContext, parse_html};
 use profile_traits::mem::{self, OpaqueSender, Report, ReportKind, ReportsChan};
 use profile_traits::time::{self, ProfilerCategory, profile};
-use script_traits::CompositorEvent::{ClickEvent, ResizeEvent};
-use script_traits::CompositorEvent::{KeyEvent, MouseMoveEvent};
-use script_traits::CompositorEvent::{MouseDownEvent, MouseUpEvent, TouchEvent};
-use script_traits::{CompositorEvent, ConstellationControlMsg};
-use script_traits::{InitialScriptState, MouseButton, NewLayoutInfo};
+use script_traits::CompositorEvent::{KeyEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent};
+use script_traits::CompositorEvent::{TouchEvent};
+use script_traits::{CompositorEvent, ConstellationControlMsg, InitialScriptState, NewLayoutInfo};
 use script_traits::{OpaqueScriptLayoutChannel, ScriptState, ScriptTaskFactory};
 use script_traits::{TimerEvent, TimerEventRequest, TimerSource};
 use script_traits::{TouchEventType, TouchId};
 use std::any::Any;
 use std::borrow::ToOwned;
 use std::cell::{Cell, RefCell};
 use std::collections::HashSet;
 use std::io::{Write, stdout};
@@ -1781,26 +1778,18 @@ impl ScriptTask {
     /// TODO: Actually perform DOM event dispatch.
     fn handle_event(&self, pipeline_id: PipelineId, event: CompositorEvent) {
 
         match event {
             ResizeEvent(new_size) => {
                 self.handle_resize_event(pipeline_id, new_size);
             }
 
-            ClickEvent(button, point) => {
-                self.handle_mouse_event(pipeline_id, MouseEventType::Click, button, point);
-            }
-
-            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);
+            MouseButtonEvent(event_type, button, point) => {
+                self.handle_mouse_event(pipeline_id, event_type, button, point);
             }
 
             MouseMoveEvent(point) => {
                 let page = get_page(&self.root_page(), pipeline_id);
                 let document = page.document();
 
                 let mut prev_mouse_over_targets: RootedVec<JS<Element>> = RootedVec::new();
                 for target in &*self.mouse_over_targets.borrow_mut() {
--- a/servo/components/script_traits/lib.rs
+++ b/servo/components/script_traits/lib.rs
@@ -29,16 +29,17 @@ use euclid::length::Length;
 use euclid::point::Point2D;
 use euclid::rect::Rect;
 use ipc_channel::ipc::{IpcReceiver, IpcSender};
 use libc::c_void;
 use msg::compositor_msg::{Epoch, LayerId, ScriptToCompositorMsg};
 use msg::constellation_msg::ScriptMsg as ConstellationMsg;
 use msg::constellation_msg::{ConstellationChan, Failure, PipelineId, WindowSizeData};
 use msg::constellation_msg::{Key, KeyModifiers, KeyState, LoadData, SubpageId};
+use msg::constellation_msg::{MouseButton, MouseEventType};
 use msg::constellation_msg::{MozBrowserEvent, PipelineNamespaceId};
 use msg::webdriver_msg::WebDriverScriptCommand;
 use net_traits::ResourceTask;
 use net_traits::image_cache_task::ImageCacheTask;
 use net_traits::storage_task::StorageTask;
 use profile_traits::mem;
 use std::any::Any;
 use util::ipc::OptionalOpaqueIpcSender;
@@ -141,27 +142,16 @@ pub enum ConstellationControlMsg {
     DispatchFrameLoadEvent {
         /// The pipeline that has been marked as loaded.
         target: PipelineId,
         /// The pipeline that contains a frame loading the target pipeline.
         parent: PipelineId
     },
 }
 
-/// The mouse button involved in the event.
-#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
-pub enum MouseButton {
-    /// The left mouse button.
-    Left,
-    /// The middle mouse button.
-    Middle,
-    /// The right mouse button.
-    Right,
-}
-
 /// The type of input represented by a multi-touch event.
 #[derive(Clone, Copy, Debug, Deserialize, Serialize)]
 pub enum TouchEventType {
     /// A new touch point came in contact with the screen.
     Down,
     /// An existing touch point changed location.
     Move,
     /// A touch point was removed from the screen.
@@ -176,22 +166,18 @@ pub enum TouchEventType {
 #[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)]
 pub struct TouchId(pub i32);
 
 /// Events from the compositor that the script task needs to know about
 #[derive(Deserialize, Serialize)]
 pub enum CompositorEvent {
     /// The window was resized.
     ResizeEvent(WindowSizeData),
-    /// A point was clicked.
-    ClickEvent(MouseButton, Point2D<f32>),
-    /// A mouse button was pressed on a point.
-    MouseDownEvent(MouseButton, Point2D<f32>),
-    /// A mouse button was released on a point.
-    MouseUpEvent(MouseButton, Point2D<f32>),
+    /// A mouse button state changed.
+    MouseButtonEvent(MouseEventType, MouseButton, Point2D<f32>),
     /// The mouse was moved over a point (or was moved out of the recognizable region).
     MouseMoveEvent(Option<Point2D<f32>>),
     /// A touch event was generated with a touch ID and location.
     TouchEvent(TouchEventType, TouchId, Point2D<f32>),
     /// A key was pressed.
     KeyEvent(Key, KeyState, KeyModifiers),
 }
 
--- a/servo/ports/cef/browser_host.rs
+++ b/servo/ports/cef/browser_host.rs
@@ -9,18 +9,17 @@ use types::cef_event_flags_t::{EVENTFLAG
 use types::cef_key_event_type_t::{KEYEVENT_CHAR, KEYEVENT_KEYDOWN, KEYEVENT_KEYUP, KEYEVENT_RAWKEYDOWN};
 use types::{cef_mouse_button_type_t, cef_mouse_event, cef_rect_t, cef_key_event, cef_window_handle_t};
 use wrappers::CefWrap;
 
 use compositing::windowing::{WindowEvent, MouseWindowEvent};
 use euclid::point::Point2D;
 use euclid::size::Size2D;
 use libc::{c_double, c_int};
-use msg::constellation_msg::{self, KeyModifiers, KeyState};
-use script_traits::MouseButton;
+use msg::constellation_msg::{self, KeyModifiers, KeyState, MouseButton};
 use std::cell::{Cell, RefCell};
 
 pub struct ServoCefBrowserHost {
     /// A reference to the browser.
     pub browser: RefCell<Option<CefBrowser>>,
     /// A reference to the client.
     pub client: CefClient,
     /// flag for return value of prepare_for_composite
--- a/servo/ports/glutin/window.rs
+++ b/servo/ports/glutin/window.rs
@@ -248,17 +248,17 @@ impl Window {
         let mouse_pos = self.mouse_pos.get();
         let event = WindowEvent::Scroll(Point2D::typed(dx as f32, dy as f32),
                                         Point2D::typed(mouse_pos.x as i32, mouse_pos.y as i32));
         self.event_queue.borrow_mut().push(event);
     }
 
     /// Helper function to handle a click
     fn handle_mouse(&self, button: glutin::MouseButton, action: glutin::ElementState, x: i32, y: i32) {
-        use script_traits::MouseButton;
+        use msg::constellation_msg::MouseButton;
 
         // FIXME(tkuehn): max pixel dist should be based on pixel density
         let max_pixel_dist = 10f64;
         let event = match action {
             ElementState::Pressed => {
                 self.mouse_down_point.set(Point2D::new(x, y));
                 self.mouse_down_button.set(Some(button));
                 MouseWindowEvent::MouseDown(MouseButton::Left, Point2D::typed(x as f32, y as f32))
--- a/servo/ports/gonk/Cargo.lock
+++ b/servo/ports/gonk/Cargo.lock
@@ -12,17 +12,16 @@ dependencies = [
  "gleam 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "layers 0.2.0 (git+https://github.com/servo/rust-layers)",
  "layout 0.0.1",
  "libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "msg 0.0.1",
  "net_traits 0.0.1",
  "profile 0.0.1",
  "script 0.0.1",
- "script_traits 0.0.1",
  "servo 0.0.1",
  "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
 ]
 
 [[package]]
 name = "advapi32-sys"
--- a/servo/ports/gonk/Cargo.toml
+++ b/servo/ports/gonk/Cargo.toml
@@ -13,19 +13,16 @@ git = "https://github.com/servo/rust-lay
 features = ["plugins"]
 
 [dependencies.msg]
 path = "../../components/msg"
 
 [dependencies.script]
 path = "../../components/script"
 
-[dependencies.script_traits]
-path = "../../components/script_traits"
-
 [dependencies.net_traits]
 path = "../../components/net_traits"
 
 [dependencies.gfx]
 path = "../../components/gfx"
 
 [dependencies.layout]
 path = "../../components/layout"
--- a/servo/ports/gonk/src/input.rs
+++ b/servo/ports/gonk/src/input.rs
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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 compositing::windowing::{WindowEvent, MouseWindowEvent};
 use errno::errno;
 use euclid::point::Point2D;
 use libc::{c_int, c_long, time_t};
-use script_traits::MouseButton;
+use msg::constellation_msg::MouseButton;
 use std::fs::File;
 use std::io::Read;
 use std::mem::{size_of, transmute, zeroed};
 use std::os::unix::io::AsRawFd;
 use std::path::Path;
 use std::sync::mpsc::Sender;
 use std::thread;
 
--- a/servo/ports/gonk/src/main.rs
+++ b/servo/ports/gonk/src/main.rs
@@ -28,17 +28,16 @@ extern crate egl;
 extern crate env_logger;
 extern crate errno;
 extern crate euclid;
 extern crate gleam;
 extern crate layers;
 extern crate libc;
 extern crate msg;
 extern crate net_traits;
-extern crate script_traits;
 extern crate servo;
 extern crate time;
 extern crate url;
 extern crate util;
 
 #[link(name = "stlport")]
 extern {}