From b5d51dd2636935471447fc741ffbb95c62e37f94 Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Sat, 30 Sep 2017 15:50:47 +0200 Subject: [PATCH] Switch to using WebRender hit testing This trades quite a bit of complicated code in Servo for few more messages and a significant performance improvement. In particular, WebRender can search the entire display list at once instead of ping-ponging down the pipeline tree. This allows us to send mouse events to the correct pipeline immediately. --- Cargo.lock | 2 + components/compositing/Cargo.toml | 1 + components/compositing/compositor.rs | 180 +++++++------- components/compositing/compositor_thread.rs | 4 +- components/compositing/lib.rs | 1 + components/constellation/constellation.rs | 38 +-- components/gfx/display_list/mod.rs | 127 ---------- components/layout/query.rs | 31 +-- components/layout/webrender_helpers.rs | 72 +++--- components/layout_thread/Cargo.toml | 1 + components/layout_thread/lib.rs | 71 +++--- components/msg/constellation_msg.rs | 11 + components/script/dom/document.rs | 227 ++++++------------ components/script/dom/window.rs | 38 +-- components/script/script_thread.rs | 73 ++++-- components/script_layout_interface/message.rs | 26 +- components/script_layout_interface/rpc.rs | 8 +- components/script_traits/lib.rs | 13 +- components/script_traits/script_msg.rs | 3 - components/style_traits/cursor.rs | 9 + 20 files changed, 381 insertions(+), 555 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 889ea6b16168..65fa20485d0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -475,6 +475,7 @@ dependencies = [ "gleam 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", "net_traits 0.0.1", @@ -1567,6 +1568,7 @@ dependencies = [ "layout 0.0.1", "layout_traits 0.0.1", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "metrics 0.0.1", "msg 0.0.1", diff --git a/components/compositing/Cargo.toml b/components/compositing/Cargo.toml index 40ec91054486..56cdd8c1e4a3 100644 --- a/components/compositing/Cargo.toml +++ b/components/compositing/Cargo.toml @@ -15,6 +15,7 @@ gfx_traits = {path = "../gfx_traits"} gleam = "0.4" image = "0.16" ipc-channel = "0.9" +libc = "0.2" log = "0.3.5" msg = {path = "../msg"} net_traits = {path = "../net_traits"} diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index a652346c7943..92c8aff47194 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -6,20 +6,20 @@ use CompositionPipeline; use SendableFrameTree; use compositor_thread::{CompositorProxy, CompositorReceiver}; use compositor_thread::{InitialCompositorState, Msg, RenderListener}; -use euclid::{Point2D, TypedPoint2D, TypedVector2D, ScaleFactor}; +use euclid::{TypedPoint2D, TypedVector2D, ScaleFactor}; use gfx_traits::Epoch; use gleam::gl; use image::{DynamicImage, ImageFormat, RgbImage}; use ipc_channel::ipc::{self, IpcSharedMemory}; +use libc::c_void; use msg::constellation_msg::{PipelineId, PipelineIndex, PipelineNamespaceId}; use net_traits::image::base::{Image, PixelFormat}; use nonzero::NonZero; use profile_traits::time::{self, ProfilerCategory, profile}; -use script_traits::{AnimationState, AnimationTickType, ConstellationControlMsg}; -use script_traits::{ConstellationMsg, LayoutControlMsg, MouseButton}; -use script_traits::{MouseEventType, ScrollState}; -use script_traits::{TouchpadPressurePhase, TouchEventType, TouchId, WindowSizeData, WindowSizeType}; -use script_traits::CompositorEvent::{self, MouseMoveEvent, MouseButtonEvent, TouchEvent, TouchpadPressureEvent}; +use script_traits::{AnimationState, AnimationTickType, ConstellationMsg, LayoutControlMsg}; +use script_traits::{MouseButton, MouseEventType, ScrollState, TouchEventType, TouchId}; +use script_traits::{TouchpadPressurePhase, UntrustedNodeAddress, WindowSizeData, WindowSizeType}; +use script_traits::CompositorEvent::{MouseMoveEvent, MouseButtonEvent, TouchEvent, TouchpadPressureEvent}; use servo_config::opts; use servo_config::prefs::PREFS; use servo_geometry::DeviceIndependentPixel; @@ -29,12 +29,13 @@ use std::rc::Rc; use std::sync::mpsc::Sender; use std::time::{Duration, Instant}; use style_traits::{CSSPixel, DevicePixel, PinchZoomFactor}; +use style_traits::cursor::Cursor; use style_traits::viewport::ViewportConstraints; use time::{precise_time_ns, precise_time_s}; use touch::{TouchHandler, TouchAction}; use webrender; -use webrender_api::{self, ClipId, DeviceUintRect, DeviceUintSize, LayoutPoint, LayoutVector2D}; -use webrender_api::{ScrollEventPhase, ScrollLocation, ScrollClamping}; +use webrender_api::{self, DeviceUintRect, DeviceUintSize, HitTestFlags, HitTestResult}; +use webrender_api::{LayoutVector2D, ScrollEventPhase, ScrollLocation}; use windowing::{self, MouseWindowEvent, WebRenderDebugOption, WindowMethods}; #[derive(Debug, PartialEq)] @@ -464,11 +465,6 @@ impl IOCompositor { self.send_viewport_rects(); } - (Msg::ScrollFragmentPoint(scroll_root_id, point, _), - ShutdownState::NotShuttingDown) => { - self.scroll_fragment_to_point(scroll_root_id, point); - } - (Msg::Recomposite(reason), ShutdownState::NotShuttingDown) => { self.composition_request = CompositionRequest::CompositeNow(reason) } @@ -656,13 +652,6 @@ impl IOCompositor { } } - fn scroll_fragment_to_point(&mut self, id: ClipId, point: Point2D) { - self.webrender_api.scroll_node_with_id(self.webrender_document, - LayoutPoint::from_untyped(&point), - id, - ScrollClamping::ToContentBounds); - } - pub fn on_resize_window_event(&mut self, new_size: DeviceUintSize) { debug!("compositor resizing to {:?}", new_size.to_untyped()); @@ -707,32 +696,47 @@ impl IOCompositor { MouseWindowEvent::MouseUp(_, p) => p, }; - let root_pipeline_id = match self.get_root_pipeline_id() { - Some(root_pipeline_id) => root_pipeline_id, + let results = self.hit_test_at_point(point); + let result = match results.items.first() { + Some(result) => result, None => return, }; - if let Some(pipeline) = self.pipeline(root_pipeline_id) { - let dppx = self.page_zoom * self.hidpi_factor(); - let translated_point = (point / dppx).to_untyped(); - let event_to_send = match mouse_window_event { - MouseWindowEvent::Click(button, _) => { - MouseButtonEvent(MouseEventType::Click, button, translated_point) - } - MouseWindowEvent::MouseDown(button, _) => { - MouseButtonEvent(MouseEventType::MouseDown, button, translated_point) - } - MouseWindowEvent::MouseUp(button, _) => { - MouseButtonEvent(MouseEventType::MouseUp, button, translated_point) - } - }; - let msg = ConstellationControlMsg::SendEvent(root_pipeline_id, event_to_send); - if let Err(e) = pipeline.script_chan.send(msg) { - warn!("Sending control event to script failed ({}).", e); + let point = result.point_in_viewport.to_untyped(); + let node_address = Some(UntrustedNodeAddress(result.tag.0 as *const c_void)); + let event_to_send = match mouse_window_event { + MouseWindowEvent::Click(button, _) => { + MouseButtonEvent(MouseEventType::Click, button, point, node_address) } + MouseWindowEvent::MouseDown(button, _) => { + MouseButtonEvent(MouseEventType::MouseDown, button, point, node_address) + } + MouseWindowEvent::MouseUp(button, _) => { + MouseButtonEvent(MouseEventType::MouseUp, button, point, node_address) + } + }; + + let pipeline_id = PipelineId::from_webrender(result.pipeline); + let msg = ConstellationMsg::ForwardEvent(pipeline_id, event_to_send); + if let Err(e) = self.constellation_chan.send(msg) { + warn!("Sending event to constellation failed ({}).", e); } } + fn hit_test_at_point(&self, point: TypedPoint2D) -> HitTestResult { + let dppx = self.page_zoom * self.hidpi_factor(); + let scaled_point = (point / dppx).to_untyped(); + + let world_cursor = webrender_api::WorldPoint::from_untyped(&scaled_point); + self.webrender_api.hit_test( + self.webrender_document, + None, + world_cursor, + HitTestFlags::empty() + ) + + } + pub fn on_mouse_window_move_event_class(&mut self, cursor: TypedPoint2D) { if opts::get().convert_mouse_to_touch { self.on_touch_move(TouchId(0), cursor); @@ -751,26 +755,43 @@ impl IOCompositor { return; } - let dppx = self.page_zoom * self.hidpi_factor(); - let event_to_send = MouseMoveEvent(Some((cursor / dppx).to_untyped())); - let msg = ConstellationControlMsg::SendEvent(root_pipeline_id, event_to_send); - if let Some(pipeline) = self.pipeline(root_pipeline_id) { - if let Err(e) = pipeline.script_chan.send(msg) { - warn!("Sending mouse control event to script failed ({}).", e); + let results = self.hit_test_at_point(cursor); + if let Some(item) = results.items.first() { + let node_address = Some(UntrustedNodeAddress(item.tag.0 as *const c_void)); + let event = MouseMoveEvent(Some(item.point_in_viewport.to_untyped()), node_address); + let pipeline_id = PipelineId::from_webrender(item.pipeline); + let msg = ConstellationMsg::ForwardEvent(pipeline_id, event); + if let Err(e) = self.constellation_chan.send(msg) { + warn!("Sending event to constellation failed ({}).", e); + } + + if let Some(cursor) = Cursor::from_u8(item.tag.1).ok() { + let msg = ConstellationMsg::SetCursor(cursor); + if let Err(e) = self.constellation_chan.send(msg) { + warn!("Sending event to constellation failed ({}).", e); + } } } } - fn send_event_to_root_pipeline(&self, event: CompositorEvent) { - let root_pipeline_id = match self.get_root_pipeline_id() { - Some(root_pipeline_id) => root_pipeline_id, - None => return, - }; - - if let Some(pipeline) = self.pipeline(root_pipeline_id) { - let msg = ConstellationControlMsg::SendEvent(root_pipeline_id, event); - if let Err(e) = pipeline.script_chan.send(msg) { - warn!("Sending control event to script failed ({}).", e); + fn send_touch_event( + &self, + event_type: TouchEventType, + identifier: TouchId, + point: TypedPoint2D) + { + let results = self.hit_test_at_point(point); + if let Some(item) = results.items.first() { + let event = TouchEvent( + event_type, + identifier, + item.point_in_viewport.to_untyped(), + Some(UntrustedNodeAddress(item.tag.0 as *const c_void)), + ); + let pipeline_id = PipelineId::from_webrender(item.pipeline); + let msg = ConstellationMsg::ForwardEvent(pipeline_id, event); + if let Err(e) = self.constellation_chan.send(msg) { + warn!("Sending event to constellation failed ({}).", e); } } } @@ -789,11 +810,7 @@ impl IOCompositor { fn on_touch_down(&mut self, identifier: TouchId, point: TypedPoint2D) { self.touch_handler.on_touch_down(identifier, point); - let dppx = self.page_zoom * self.hidpi_factor(); - let translated_point = (point / dppx).to_untyped(); - self.send_event_to_root_pipeline(TouchEvent(TouchEventType::Down, - identifier, - translated_point)); + self.send_touch_event(TouchEventType::Down, identifier, point); } fn on_touch_move(&mut self, identifier: TouchId, point: TypedPoint2D) { @@ -821,22 +838,15 @@ impl IOCompositor { }); } TouchAction::DispatchEvent => { - let dppx = self.page_zoom * self.hidpi_factor(); - let translated_point = (point / dppx).to_untyped(); - self.send_event_to_root_pipeline(TouchEvent(TouchEventType::Move, - identifier, - translated_point)); + self.send_touch_event(TouchEventType::Move, identifier, point); } _ => {} } } fn on_touch_up(&mut self, identifier: TouchId, point: TypedPoint2D) { - let dppx = self.page_zoom * self.hidpi_factor(); - let translated_point = (point / dppx).to_untyped(); - self.send_event_to_root_pipeline(TouchEvent(TouchEventType::Up, - identifier, - translated_point)); + self.send_touch_event(TouchEventType::Up, identifier, point); + if let TouchAction::Click = self.touch_handler.on_touch_up(identifier, point) { self.simulate_mouse_click(point); } @@ -845,23 +855,31 @@ impl IOCompositor { fn on_touch_cancel(&mut self, identifier: TouchId, point: TypedPoint2D) { // Send the event to script. self.touch_handler.on_touch_cancel(identifier, point); - let dppx = self.page_zoom * self.hidpi_factor(); - let translated_point = (point / dppx).to_untyped(); - self.send_event_to_root_pipeline(TouchEvent(TouchEventType::Cancel, - identifier, - translated_point)); + self.send_touch_event(TouchEventType::Cancel, identifier, point); } pub fn on_touchpad_pressure_event(&self, point: TypedPoint2D, pressure: f32, phase: TouchpadPressurePhase) { - if let Some(true) = PREFS.get("dom.forcetouch.enabled").as_boolean() { - let dppx = self.page_zoom * self.hidpi_factor(); - let translated_point = (point / dppx).to_untyped(); - self.send_event_to_root_pipeline(TouchpadPressureEvent(translated_point, - pressure, - phase)); + match PREFS.get("dom.forcetouch.enabled").as_boolean() { + Some(true) => {}, + _ => return, + } + + let results = self.hit_test_at_point(point); + if let Some(item) = results.items.first() { + let event = TouchpadPressureEvent( + item.point_in_viewport.to_untyped(), + pressure, + phase, + Some(UntrustedNodeAddress(item.tag.0 as *const c_void)), + ); + let pipeline_id = PipelineId::from_webrender(item.pipeline); + let msg = ConstellationMsg::ForwardEvent(pipeline_id, event); + if let Err(e) = self.constellation_chan.send(msg) { + warn!("Sending event to constellation failed ({}).", e); + } } } diff --git a/components/compositing/compositor_thread.rs b/components/compositing/compositor_thread.rs index 03a7b46c7c0f..2fc1373da563 100644 --- a/components/compositing/compositor_thread.rs +++ b/components/compositing/compositor_thread.rs @@ -158,8 +158,6 @@ pub enum Msg { /// (e.g. SetFrameTree) at the time that we send it an ExitMsg. ShutdownComplete, - /// Scroll a page in a window - ScrollFragmentPoint(webrender_api::ClipId, Point2D, bool), /// Alerts the compositor that the given pipeline has changed whether it is running animations. ChangeRunningAnimationsState(PipelineId, AnimationState), /// Replaces the current frame tree, typically called during main frame navigation. @@ -195,6 +193,7 @@ pub enum Msg { PendingPaintMetric(PipelineId, Epoch), /// The load of a page has completed LoadComplete(TopLevelBrowsingContextId), + } impl Debug for Msg { @@ -202,7 +201,6 @@ impl Debug for Msg { match *self { Msg::Exit => write!(f, "Exit"), Msg::ShutdownComplete => write!(f, "ShutdownComplete"), - Msg::ScrollFragmentPoint(..) => write!(f, "ScrollFragmentPoint"), Msg::ChangeRunningAnimationsState(..) => write!(f, "ChangeRunningAnimationsState"), Msg::SetFrameTree(..) => write!(f, "SetFrameTree"), Msg::Recomposite(..) => write!(f, "Recomposite"), diff --git a/components/compositing/lib.rs b/components/compositing/lib.rs index b2abdcbf94d3..82702dfb7340 100644 --- a/components/compositing/lib.rs +++ b/components/compositing/lib.rs @@ -9,6 +9,7 @@ extern crate gfx_traits; extern crate gleam; extern crate image; extern crate ipc_channel; +extern crate libc; #[macro_use] extern crate log; extern crate msg; diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 3ad7a2e7edf4..3262308c3efb 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -1082,6 +1082,12 @@ impl Constellation debug!("constellation got {:?} WebVR events", events.len()); self.handle_webvr_events(pipeline_ids, events); } + FromCompositorMsg::ForwardEvent(destination_pipeline_id, event) => { + self.forward_event(destination_pipeline_id, event); + } + FromCompositorMsg::SetCursor(cursor) => { + self.handle_set_cursor_msg(cursor) + } } } @@ -1175,15 +1181,8 @@ impl Constellation debug!("constellation got focus message"); self.handle_focus_msg(source_pipeline_id); } - FromScriptMsg::ForwardEvent(dest_id, event) => { - let msg = ConstellationControlMsg::SendEvent(dest_id, event); - let result = match self.pipelines.get(&dest_id) { - None => { debug!("Pipeline {:?} got event after closure.", dest_id); return; } - Some(pipeline) => pipeline.event_loop.send(msg), - }; - if let Err(e) = result { - self.handle_send_error(dest_id, e); - } + FromScriptMsg::ForwardEvent(destination_pipeline_id, event) => { + self.forward_event(destination_pipeline_id, event); } FromScriptMsg::GetClipboardContents(sender) => { let contents = match self.clipboard_ctx { @@ -1250,13 +1249,6 @@ impl Constellation debug!("constellation got Alert message"); self.handle_alert(source_top_ctx_id, message, sender); } - - FromScriptMsg::ScrollFragmentPoint(scroll_root_id, point, smooth) => { - self.compositor_proxy.send(ToCompositorMsg::ScrollFragmentPoint(scroll_root_id, - point, - smooth)); - } - FromScriptMsg::GetClientWindow(send) => { self.embedder_proxy.send(EmbedderMsg::GetClientWindow(source_top_ctx_id, send)); } @@ -1592,6 +1584,20 @@ impl Constellation } } + fn forward_event(&mut self, destination_pipeline_id: PipelineId, event: CompositorEvent) { + let msg = ConstellationControlMsg::SendEvent(destination_pipeline_id, event); + let result = match self.pipelines.get(&destination_pipeline_id) { + None => { + debug!("Pipeline {:?} got event after closure.", destination_pipeline_id); + return; + } + Some(pipeline) => pipeline.event_loop.send(msg), + }; + if let Err(e) = result { + self.handle_send_error(destination_pipeline_id, e); + } + } + fn handle_new_top_level_browsing_context(&mut self, url: ServoUrl, reply: IpcSender) { let window_size = self.window_size.initial_viewport; let pipeline_id = PipelineId::new(); diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index 341024b63eed..ceb7016f9f29 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -206,79 +206,6 @@ impl DisplayList { } } - // Return all nodes containing the point of interest, bottommost first, and - // respecting the `pointer-events` CSS property. - pub fn hit_test(&self, - point: &Point2D, - scroll_offsets: &ScrollOffsetMap) - -> Vec { - let mut result = Vec::new(); - let mut traversal = DisplayListTraversal::new(self); - self.hit_test_contents(&mut traversal, - point, - &mut ScrollOffsetLookup::new(&mut HashMap::new(), scroll_offsets), - &mut result); - result - } - - fn hit_test_contents<'a>(&self, - traversal: &mut DisplayListTraversal<'a>, - point: &Point2D, - offset_lookup: &mut ScrollOffsetLookup, - result: &mut Vec) { - while let Some(item) = traversal.next() { - match item { - &DisplayItem::PushStackingContext(ref context_item) => { - self.hit_test_stacking_context(&context_item.stacking_context, - item.scroll_node_id(), - traversal, - point, - offset_lookup, - result); - } - &DisplayItem::PopStackingContext(_) => return, - &DisplayItem::DefineClipScrollNode(ref item) => { - offset_lookup.add_clip_scroll_node(&item.node); - } - _ => { - if let Some(meta) = item.hit_test(*point, offset_lookup) { - result.push(meta); - } - } - } - } - } - - fn hit_test_stacking_context<'a>(&self, - stacking_context: &StackingContext, - clip_id: ClipId, - traversal: &mut DisplayListTraversal<'a>, - point: &Point2D, - offset_lookup: &mut ScrollOffsetLookup, - result: &mut Vec) { - debug_assert!(stacking_context.context_type == StackingContextType::Real); - - let mut point = *point - stacking_context.bounds.origin.to_vector(); - if stacking_context.scroll_policy == ScrollPolicy::Fixed { - let old_offset = offset_lookup.calculated_total_offsets.get(&clip_id).cloned(); - offset_lookup.calculated_total_offsets.insert(clip_id, Vector2D::zero()); - - self.hit_test_contents(traversal, &point, offset_lookup, result); - - match old_offset { - Some(offset) => offset_lookup.calculated_total_offsets.insert(clip_id, offset), - None => offset_lookup.calculated_total_offsets.remove(&clip_id), - }; - } else if let Some(transform) = stacking_context.transform { - if let Some(ref mut sublookup) = - offset_lookup.new_for_reference_frame(clip_id, &transform, &mut point) { - self.hit_test_contents(traversal, &point, sublookup, result); - } - } else { - self.hit_test_contents(traversal, &point, offset_lookup, result); - } - } - pub fn print(&self) { let mut print_tree = PrintTree::new("Display List".to_owned()); self.print_with_tree(&mut print_tree); @@ -1283,60 +1210,6 @@ impl DisplayItem { } println!("{}+ {:?}", indent, self); } - - fn hit_test(&self, - point: Point2D, - offset_lookup: &mut ScrollOffsetLookup) - -> Option { - // TODO(pcwalton): Use a precise algorithm here. This will allow us to properly hit - // test elements with `border-radius`, for example. - let base_item = self.base(); - - let scroll_offset = offset_lookup.full_offset_for_clip_scroll_node(&self.scroll_node_id()); - let point = Point2D::new(point.x - Au::from_f32_px(scroll_offset.x), - point.y - Au::from_f32_px(scroll_offset.y)); - - if !base_item.local_clip.clip_rect().contains(&point.to_pointf()) { - // Clipped out. - return None; - } - if !self.bounds().contains(&point) { - // Can't possibly hit. - return None; - } - if base_item.metadata.pointing.is_none() { - // `pointer-events` is `none`. Ignore this item. - return None; - } - - match *self { - DisplayItem::Border(ref border) => { - // If the point is inside the border, it didn't hit the border! - let interior_rect = - Rect::new( - Point2D::new(border.base.bounds.origin.x + - border.border_widths.left, - border.base.bounds.origin.y + - border.border_widths.top), - Size2D::new(border.base.bounds.size.width - - (border.border_widths.left + - border.border_widths.right), - border.base.bounds.size.height - - (border.border_widths.top + - border.border_widths.bottom))); - if interior_rect.contains(&point) { - return None; - } - } - DisplayItem::BoxShadow(_) => { - // Box shadows can never be hit. - return None; - } - _ => {} - } - - Some(base_item.metadata) - } } impl fmt::Debug for DisplayItem { diff --git a/components/layout/query.rs b/components/layout/query.rs index c781412a4b6d..53655df667f9 100644 --- a/components/layout/query.rs +++ b/components/layout/query.rs @@ -10,16 +10,15 @@ use context::LayoutContext; use euclid::{Point2D, Vector2D, Rect, Size2D}; use flow::{self, Flow}; use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo}; -use gfx::display_list::{DisplayItemMetadata, DisplayList, OpaqueNode, ScrollOffsetMap}; +use gfx::display_list::{DisplayList, OpaqueNode, ScrollOffsetMap}; use inline::LAST_FRAGMENT_OF_ELEMENT; use ipc_channel::ipc::IpcSender; use msg::constellation_msg::PipelineId; use opaque_node::OpaqueNodeMethods; -use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse}; -use script_layout_interface::rpc::{HitTestResponse, LayoutRPC}; +use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC}; use script_layout_interface::rpc::{MarginStyleResponse, NodeGeometryResponse}; -use script_layout_interface::rpc::{NodeOverflowResponse, OffsetParentResponse}; -use script_layout_interface::rpc::{NodeScrollRootIdResponse, ResolvedStyleResponse, TextIndexResponse}; +use script_layout_interface::rpc::{NodeOverflowResponse, NodeScrollRootIdResponse}; +use script_layout_interface::rpc::{OffsetParentResponse, ResolvedStyleResponse, TextIndexResponse}; use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode}; use script_traits::LayoutMsg as ConstellationMsg; use script_traits::UntrustedNodeAddress; @@ -35,7 +34,6 @@ use style::properties::{style_structs, PropertyId, PropertyDeclarationId, Longha use style::properties::longhands::{display, position}; use style::selector_parser::PseudoElement; use style_traits::ToCss; -use style_traits::cursor::Cursor; use webrender_api::ClipId; use wrapper::LayoutNodeLayoutData; @@ -58,9 +56,6 @@ pub struct LayoutThreadData { /// A queued response for the client {top, left, width, height} of a node in pixels. pub client_rect_response: Rect, - /// A queued response for the node at a given point - pub hit_test_response: (Option, bool), - /// A queued response for the scroll root id for a given node. pub scroll_root_id_response: Option, @@ -119,24 +114,6 @@ impl LayoutRPC for LayoutRPCImpl { ContentBoxesResponse(rw_data.content_boxes_response.clone()) } - /// Requests the node containing the point of interest. - fn hit_test(&self) -> HitTestResponse { - let &LayoutRPCImpl(ref rw_data) = self; - let rw_data = rw_data.lock().unwrap(); - let &(ref result, update_cursor) = &rw_data.hit_test_response; - if update_cursor { - // Compute the new cursor. - let cursor = match *result { - None => Cursor::Default, - Some(dim) => dim.pointing.unwrap(), - }; - rw_data.constellation_chan.send(ConstellationMsg::SetCursor(cursor)).unwrap(); - } - HitTestResponse { - node_address: result.map(|dim| dim.node.to_untrusted_node_address()), - } - } - fn nodes_from_point_response(&self) -> Vec { let &LayoutRPCImpl(ref rw_data) = self; let rw_data = rw_data.lock().unwrap(); diff --git a/components/layout/webrender_helpers.rs b/components/layout/webrender_helpers.rs index fbd5d423ddf4..4477294fced7 100644 --- a/components/layout/webrender_helpers.rs +++ b/components/layout/webrender_helpers.rs @@ -19,23 +19,12 @@ use style::values::generics::effects::Filter as GenericFilter; use webrender_api::{self, ClipAndScrollInfo, ComplexClipRegion, DisplayListBuilder}; use webrender_api::{ExtendMode, LayoutTransform}; -fn prim_info(local_rect: Rect, - local_clip: Option) -> webrender_api::LayoutPrimitiveInfo { - match local_clip { - Some(local_clip) => { - webrender_api::LayoutPrimitiveInfo::with_clip(local_rect.to_rectf(), local_clip) - } - None => { - webrender_api::LayoutPrimitiveInfo::new(local_rect.to_rectf()) - } - } -} - pub trait WebRenderDisplayListConverter { fn convert_to_webrender(&self, pipeline_id: PipelineId) -> DisplayListBuilder; } trait WebRenderDisplayItemConverter { + fn prim_info(&self) -> webrender_api::LayoutPrimitiveInfo; fn convert_to_webrender(&self, builder: &mut DisplayListBuilder, current_clip_and_scroll_info: &mut ClipAndScrollInfo); @@ -249,6 +238,20 @@ impl WebRenderDisplayListConverter for DisplayList { } impl WebRenderDisplayItemConverter for DisplayItem { + fn prim_info(&self) -> webrender_api::LayoutPrimitiveInfo { + let tag = match self.base().metadata.pointing { + Some(cursor) => Some((self.base().metadata.node.0 as u64, cursor as u8)), + None => None, + }; + webrender_api::LayoutPrimitiveInfo { + rect: self.base().bounds.to_rectf(), + local_clip: self.base().local_clip, + // TODO(gw): Make use of the WR backface visibility functionality. + is_backface_visible: true, + tag: tag, + } + } + fn convert_to_webrender(&self, builder: &mut DisplayListBuilder, current_clip_and_scroll_info: &mut ClipAndScrollInfo) { @@ -261,11 +264,7 @@ impl WebRenderDisplayItemConverter for DisplayItem { match *self { DisplayItem::SolidColor(ref item) => { - let color = item.color; - if color.a > 0.0 { - builder.push_rect(&prim_info(item.base.bounds, Some(item.base.local_clip)), - color); - } + builder.push_rect(&self.prim_info(), item.color); } DisplayItem::Text(ref item) => { let mut origin = item.baseline_origin.clone(); @@ -294,7 +293,7 @@ impl WebRenderDisplayItemConverter for DisplayItem { } if glyphs.len() > 0 { - builder.push_text(&prim_info(item.base.bounds, Some(item.base.local_clip)), + builder.push_text(&self.prim_info(), &glyphs, item.text_run.font_key, item.text_color, @@ -305,7 +304,7 @@ impl WebRenderDisplayItemConverter for DisplayItem { if let Some(id) = item.webrender_image.key { if item.stretch_size.width > Au(0) && item.stretch_size.height > Au(0) { - builder.push_image(&prim_info(item.base.bounds, Some(item.base.local_clip)), + builder.push_image(&self.prim_info(), item.stretch_size.to_sizef(), item.tile_spacing.to_sizef(), item.image_rendering.to_image_rendering(), @@ -314,7 +313,6 @@ impl WebRenderDisplayItemConverter for DisplayItem { } } DisplayItem::Border(ref item) => { - let rect = item.base.bounds; let widths = item.border_widths.to_border_widths(); let details = match item.details { @@ -395,7 +393,7 @@ impl WebRenderDisplayItemConverter for DisplayItem { } }; - builder.push_border(&prim_info(rect, Some(item.base.local_clip)), widths, details); + builder.push_border(&self.prim_info(), widths, details); } DisplayItem::Gradient(ref item) => { let rect = item.base.bounds; @@ -410,7 +408,7 @@ impl WebRenderDisplayItemConverter for DisplayItem { end_point, item.gradient.stops.clone(), extend_mode); - builder.push_gradient(&prim_info(rect, Some(item.base.local_clip)), + builder.push_gradient(&self.prim_info(), gradient, rect.size.to_sizef(), webrender_api::LayoutSize::zero()); @@ -428,14 +426,14 @@ impl WebRenderDisplayItemConverter for DisplayItem { radius, item.gradient.stops.clone(), extend_mode); - builder.push_radial_gradient(&prim_info(rect, Some(item.base.local_clip)), + builder.push_radial_gradient(&self.prim_info(), gradient, rect.size.to_sizef(), webrender_api::LayoutSize::zero()); } DisplayItem::Line(ref item) => { let box_bounds = item.base.bounds.to_rectf(); - builder.push_line(&prim_info(item.base.bounds, Some(item.base.local_clip)), + builder.push_line(&self.prim_info(), box_bounds.origin.y + box_bounds.size.height, box_bounds.origin.x, box_bounds.origin.x + box_bounds.size.width, @@ -445,9 +443,8 @@ impl WebRenderDisplayItemConverter for DisplayItem { item.style); } DisplayItem::BoxShadow(ref item) => { - let rect = item.base.bounds; let box_bounds = item.box_bounds.to_rectf(); - builder.push_box_shadow(&prim_info(rect, Some(item.base.local_clip)), + builder.push_box_shadow(&self.prim_info(), box_bounds, item.offset.to_vectorf(), item.color, @@ -457,8 +454,7 @@ impl WebRenderDisplayItemConverter for DisplayItem { item.clip_mode.to_clip_mode()); } DisplayItem::PushTextShadow(ref item) => { - let rect = item.base.bounds; - builder.push_shadow(&prim_info(rect, Some(item.base.local_clip)), + builder.push_shadow(&self.prim_info(), webrender_api::Shadow { blur_radius: item.blur_radius.to_f32_px(), offset: item.offset.to_vectorf(), @@ -469,9 +465,7 @@ impl WebRenderDisplayItemConverter for DisplayItem { builder.pop_all_shadows(); } DisplayItem::Iframe(ref item) => { - let rect = item.base.bounds; - let pipeline_id = item.iframe.to_webrender(); - builder.push_iframe(&prim_info(rect, Some(item.base.local_clip)), pipeline_id); + builder.push_iframe(&self.prim_info(), item.iframe.to_webrender()); } DisplayItem::PushStackingContext(ref item) => { let stacking_context = &item.stacking_context; @@ -484,13 +478,15 @@ impl WebRenderDisplayItemConverter for DisplayItem { LayoutTransform::from_untyped(&perspective) }); - builder.push_stacking_context(&prim_info(stacking_context.bounds, None), - stacking_context.scroll_policy, - transform, - stacking_context.transform_style, - perspective, - stacking_context.mix_blend_mode, - stacking_context.filters.to_filter_ops()); + builder.push_stacking_context( + &webrender_api::LayoutPrimitiveInfo::new(stacking_context.bounds.to_rectf()), + stacking_context.scroll_policy, + transform, + stacking_context.transform_style, + perspective, + stacking_context.mix_blend_mode, + stacking_context.filters.to_filter_ops() + ); } DisplayItem::PopStackingContext(_) => builder.pop_stacking_context(), DisplayItem::DefineClipScrollNode(ref item) => { diff --git a/components/layout_thread/Cargo.toml b/components/layout_thread/Cargo.toml index 4106e86e47ce..6108c9c2447f 100644 --- a/components/layout_thread/Cargo.toml +++ b/components/layout_thread/Cargo.toml @@ -25,6 +25,7 @@ ipc-channel = "0.9" layout = {path = "../layout"} layout_traits = {path = "../layout_traits"} lazy_static = "0.2" +libc = "0.2" log = "0.3.5" metrics = {path = "../metrics"} msg = {path = "../msg"} diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 528e06c6424d..ab3e620c16e4 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -22,6 +22,7 @@ extern crate layout; extern crate layout_traits; #[macro_use] extern crate lazy_static; +extern crate libc; #[macro_use] extern crate log; extern crate metrics; @@ -73,7 +74,6 @@ use layout::flow::{self, Flow, ImmutableFlowUtils, MutableOwnedFlowUtils}; use layout::flow_ref::FlowRef; use layout::incremental::{LayoutDamageComputation, REFLOW_ENTIRE_DOCUMENT, RelayoutMode}; use layout::layout_debug; -use layout::opaque_node::OpaqueNodeMethods; use layout::parallel; use layout::query::{LayoutRPCImpl, LayoutThreadData, process_content_box_request, process_content_boxes_request}; use layout::query::{process_margin_style_query, process_node_overflow_request, process_resolved_style_request}; @@ -84,6 +84,7 @@ use layout::traversal::{ComputeStackingRelativePositions, PreorderFlowTraversal, use layout::webrender_helpers::WebRenderDisplayListConverter; use layout::wrapper::LayoutNodeLayoutData; use layout_traits::LayoutThreadFactory; +use libc::c_void; use metrics::{PaintTimeMetrics, ProfilerMetadataFactory}; use msg::constellation_msg::PipelineId; use msg::constellation_msg::TopLevelBrowsingContextId; @@ -92,8 +93,8 @@ use parking_lot::RwLock; use profile_traits::mem::{self, Report, ReportKind, ReportsChan}; use profile_traits::time::{self, TimerMetadata, profile}; use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType}; -use script_layout_interface::message::{Msg, NewLayoutThreadInfo, Reflow, ReflowGoal}; -use script_layout_interface::message::{ScriptReflow, ReflowComplete}; +use script_layout_interface::message::{Msg, NewLayoutThreadInfo, NodesFromPointQueryType, Reflow}; +use script_layout_interface::message::{ReflowComplete, ReflowGoal, ScriptReflow}; use script_layout_interface::rpc::{LayoutRPC, MarginStyleResponse, NodeOverflowResponse, OffsetParentResponse}; use script_layout_interface::rpc::TextIndexResponse; use script_layout_interface::wrapper_traits::LayoutNode; @@ -120,10 +121,8 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::mpsc::{Receiver, Sender, channel}; use std::thread; use style::animation::Animation; -use style::context::{QuirksMode, SharedStyleContext}; -use style::context::{StyleSystemOptions, ThreadLocalStyleContextCreationInfo}; -use style::context::RegisteredSpeculativePainter; -use style::context::RegisteredSpeculativePainters; +use style::context::{QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters}; +use style::context::{SharedStyleContext, StyleSystemOptions, ThreadLocalStyleContextCreationInfo}; use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TElement, TNode}; use style::driver; use style::error_reporting::{NullReporter, RustLogReporter}; @@ -519,7 +518,6 @@ impl LayoutThread { content_box_response: None, content_boxes_response: Vec::new(), client_rect_response: Rect::zero(), - hit_test_response: (None, false), scroll_root_id_response: None, scroll_area_response: Rect::zero(), overflow_response: NodeOverflowResponse(None), @@ -705,6 +703,14 @@ impl LayoutThread { Msg::UpdateScrollStateFromScript(state) => { let mut rw_data = possibly_locked_rw_data.lock(); rw_data.scroll_offsets.insert(state.scroll_root_id, state.scroll_offset); + + let point = Point2D::new(-state.scroll_offset.x, -state.scroll_offset.y); + self.webrender_api.scroll_node_with_id( + self.webrender_document, + webrender_api::LayoutPoint::from_untyped(&point), + state.scroll_root_id, + webrender_api::ScrollClamping::ToContentBounds + ); } Msg::ReapStyleAndLayoutData(dead_data) => { unsafe { @@ -1078,10 +1084,7 @@ impl LayoutThread { ReflowGoal::ContentBoxesQuery(_) => { rw_data.content_boxes_response = Vec::new(); }, - ReflowGoal::HitTestQuery(..) => { - rw_data.hit_test_response = (None, false); - }, - ReflowGoal::NodesFromPoint(..) => { + ReflowGoal::NodesFromPointQuery(..) => { rw_data.nodes_from_point_response = Vec::new(); }, ReflowGoal::NodeGeometryQuery(_) => { @@ -1355,15 +1358,6 @@ impl LayoutThread { let node = unsafe { ServoLayoutNode::new(&node) }; rw_data.content_boxes_response = process_content_boxes_request(node, root_flow); }, - ReflowGoal::HitTestQuery(client_point, update_cursor) => { - let point = Point2D::new(Au::from_f32_px(client_point.x), - Au::from_f32_px(client_point.y)); - let result = rw_data.display_list - .as_ref() - .expect("Tried to hit test with no display list") - .hit_test(&point, &rw_data.scroll_offsets); - rw_data.hit_test_response = (result.last().cloned(), update_cursor); - }, ReflowGoal::TextIndexQuery(node, mouse_x, mouse_y) => { let node = unsafe { ServoLayoutNode::new(&node) }; let opaque_node = node.opaque(); @@ -1411,25 +1405,28 @@ impl LayoutThread { let node = unsafe { ServoLayoutNode::new(&node) }; rw_data.margin_style_response = process_margin_style_query(node); }, - ReflowGoal::NodesFromPoint(client_point) => { - let client_point = Point2D::new(Au::from_f32_px(client_point.x), - Au::from_f32_px(client_point.y)); - let nodes_from_point_list = { - let result = match rw_data.display_list { - None => panic!("Tried to hit test without a DisplayList"), - Some(ref display_list) => { - display_list.hit_test(&client_point, &rw_data.scroll_offsets) - } - }; - - result + ReflowGoal::NodesFromPointQuery(client_point, ref reflow_goal) => { + let mut flags = match reflow_goal { + &NodesFromPointQueryType::Topmost => webrender_api::HitTestFlags::empty(), + &NodesFromPointQueryType::All => webrender_api::HitTestFlags::FIND_ALL, }; - rw_data.nodes_from_point_response = nodes_from_point_list.iter() - .rev() - .map(|metadata| metadata.node.to_untrusted_node_address()) + // The point we get is not relative to the entire WebRender scene, but to this + // particular pipeline, so we need to tell WebRender about that. + flags.insert(webrender_api::HitTestFlags::POINT_RELATIVE_TO_PIPELINE_VIEWPORT); + + let client_point = webrender_api::WorldPoint::from_untyped(&client_point); + let results = self.webrender_api.hit_test( + self.webrender_document, + Some(self.id.to_webrender()), + client_point, + flags + ); + + rw_data.nodes_from_point_response = results.items.iter() + .map(|item| UntrustedNodeAddress(item.tag.0 as *const c_void)) .collect() - } + }, ReflowGoal::Full | ReflowGoal::TickAnimations => {} } diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs index 40c1827b262a..9eb1a9cf1168 100644 --- a/components/msg/constellation_msg.rs +++ b/components/msg/constellation_msg.rs @@ -246,6 +246,17 @@ impl PipelineId { webrender_api::PipelineId(namespace_id, index.get()) } + #[allow(unsafe_code)] + pub fn from_webrender(pipeline: webrender_api::PipelineId) -> PipelineId { + let webrender_api::PipelineId(namespace_id, index) = pipeline; + unsafe { + PipelineId { + namespace_id: PipelineNamespaceId(namespace_id), + index: PipelineIndex(NonZero::new_unchecked(index)), + } + } + } + pub fn root_scroll_node(&self) -> webrender_api::ClipId { webrender_api::ClipId::root_scroll_node(self.to_webrender()) } diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index f4e734ceec54..e87a8fd130e0 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -10,10 +10,8 @@ use dom::attr::Attr; use dom::beforeunloadevent::BeforeUnloadEvent; use dom::bindings::callback::ExceptionHandling; 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, ElementCreationOptions}; -use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods; use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementBinding::HTMLIFrameElementMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::codegen::Bindings::NodeFilterBinding::NodeFilter; @@ -94,7 +92,7 @@ use dom::windowproxy::WindowProxy; use dom_struct::dom_struct; use encoding::EncodingRef; use encoding::all::UTF_8; -use euclid::{Point2D, Vector2D}; +use euclid::Point2D; use html5ever::{LocalName, Namespace, QualName}; use hyper::header::{Header, SetCookie}; use hyper_serde::Serde; @@ -110,14 +108,12 @@ use net_traits::pub_domains::is_pub_domain; use net_traits::request::RequestInit; use net_traits::response::HttpsState; use num_traits::ToPrimitive; -use script_layout_interface::message::{Msg, ReflowGoal}; +use script_layout_interface::message::{Msg, NodesFromPointQueryType, ReflowGoal}; use script_runtime::{CommonScriptMsg, ScriptThreadEventCategory}; use script_thread::{MainThreadScriptMsg, ScriptThread}; -use script_traits::{AnimationState, CompositorEvent, DocumentActivity}; -use script_traits::{MouseButton, MouseEventType, MozBrowserEvent}; -use script_traits::{MsDuration, ScriptMsg, TouchpadPressurePhase}; -use script_traits::{TouchEventType, TouchId}; -use script_traits::UntrustedNodeAddress; +use script_traits::{AnimationState, DocumentActivity, MouseButton, MouseEventType}; +use script_traits::{MozBrowserEvent, MsDuration, ScriptMsg, TouchEventType, TouchId}; +use script_traits::{TouchpadPressurePhase, UntrustedNodeAddress}; use servo_arc::Arc; use servo_atoms::Atom; use servo_config::prefs::PREFS; @@ -834,11 +830,14 @@ impl Document { } #[allow(unsafe_code)] - pub fn handle_mouse_event(&self, - js_runtime: *mut JSRuntime, - button: MouseButton, - client_point: Point2D, - mouse_event_type: MouseEventType) { + pub fn handle_mouse_event( + &self, + js_runtime: *mut JSRuntime, + _button: MouseButton, + client_point: Point2D, + mouse_event_type: MouseEventType, + node_address: Option + ) { let mouse_event_type_string = match mouse_event_type { MouseEventType::Click => "click".to_owned(), MouseEventType::MouseUp => "mouseup".to_owned(), @@ -846,41 +845,17 @@ impl Document { }; debug!("{}: at {:?}", mouse_event_type_string, client_point); - let node = match self.window.hit_test_query(client_point, false) { - Some(node_address) => { - debug!("node address is {:?}", node_address); - unsafe { - node::from_untrusted_node_address(js_runtime, node_address) - } - }, + let el = node_address.and_then(|address| { + let node = unsafe { node::from_untrusted_node_address(js_runtime, address) }; + node.inclusive_ancestors() + .filter_map(DomRoot::downcast::) + .next() + }); + let el = match el { + Some(el) => el, None => return, }; - let el = match node.downcast::() { - Some(el) => DomRoot::from_ref(el), - None => { - let parent = node.GetParentNode(); - match parent.and_then(DomRoot::downcast::) { - Some(parent) => parent, - None => return, - } - }, - }; - - // If the target is an iframe, forward the event to the child document. - if let Some(iframe) = el.downcast::() { - if let Some(pipeline_id) = iframe.pipeline_id() { - let rect = iframe.upcast::().GetBoundingClientRect(); - let child_origin = Vector2D::new(rect.X() as f32, rect.Y() as f32); - let child_point = client_point - child_origin; - - let event = CompositorEvent::MouseButtonEvent(mouse_event_type, button, child_point); - let event = ScriptMsg::ForwardEvent(pipeline_id, event); - self.send_to_constellation(event); - } - return; - } - let node = el.upcast::(); debug!("{} on {:?}", mouse_event_type_string, node.debug_str()); // Prevent click event if form control element is disabled. @@ -997,45 +972,24 @@ impl Document { } #[allow(unsafe_code)] - pub fn handle_touchpad_pressure_event(&self, - js_runtime: *mut JSRuntime, - client_point: Point2D, - pressure: f32, - phase_now: TouchpadPressurePhase) { - let node = match self.window.hit_test_query(client_point, false) { - Some(node_address) => unsafe { - node::from_untrusted_node_address(js_runtime, node_address) - }, - None => return - }; - - let el = match node.downcast::() { - Some(el) => DomRoot::from_ref(el), - None => { - let parent = node.GetParentNode(); - match parent.and_then(DomRoot::downcast::) { - Some(parent) => parent, - None => return - } - }, + pub fn handle_touchpad_pressure_event( + &self, + js_runtime: *mut JSRuntime, + pressure: f32, + phase_now: TouchpadPressurePhase, + node_address: Option + ) { + let el = node_address.and_then(|address| { + let node = unsafe { node::from_untrusted_node_address(js_runtime, address) }; + node.inclusive_ancestors() + .filter_map(DomRoot::downcast::) + .next() + }); + let el = match el { + Some(el) => el, + None => return, }; - // If the target is an iframe, forward the event to the child document. - if let Some(iframe) = el.downcast::() { - if let Some(pipeline_id) = iframe.pipeline_id() { - let rect = iframe.upcast::().GetBoundingClientRect(); - let child_origin = Vector2D::new(rect.X() as f32, rect.Y() as f32); - let child_point = client_point - child_origin; - - let event = CompositorEvent::TouchpadPressureEvent(child_point, - pressure, - phase_now); - let event = ScriptMsg::ForwardEvent(pipeline_id, event); - self.send_to_constellation(event); - } - return; - } - let phase_before = self.touchpad_pressure_phase.get(); self.touchpad_pressure_phase.set(phase_now); @@ -1101,10 +1055,13 @@ impl Document { } #[allow(unsafe_code)] - pub fn handle_mouse_move_event(&self, - js_runtime: *mut JSRuntime, - client_point: Option>, - prev_mouse_over_target: &MutNullableDom) { + pub fn handle_mouse_move_event( + &self, + js_runtime: *mut JSRuntime, + client_point: Option>, + prev_mouse_over_target: &MutNullableDom, + node_address: Option + ) { let client_point = match client_point { None => { // If there's no point, there's no target under the mouse @@ -1115,31 +1072,21 @@ impl Document { Some(client_point) => client_point, }; - let maybe_new_target = self.window.hit_test_query(client_point, true).and_then(|address| { + let maybe_new_target = node_address.and_then(|address| { let node = unsafe { node::from_untrusted_node_address(js_runtime, address) }; node.inclusive_ancestors() .filter_map(DomRoot::downcast::) .next() }); - // Send mousemove event to topmost target, and forward it if it's an iframe - if let Some(ref new_target) = maybe_new_target { - // If the target is an iframe, forward the event to the child document. - if let Some(iframe) = new_target.downcast::() { - if let Some(pipeline_id) = iframe.pipeline_id() { - let rect = iframe.upcast::().GetBoundingClientRect(); - let child_origin = Vector2D::new(rect.X() as f32, rect.Y() as f32); - let child_point = client_point - child_origin; - - let event = CompositorEvent::MouseMoveEvent(Some(child_point)); - let event = ScriptMsg::ForwardEvent(pipeline_id, event); - self.send_to_constellation(event); - } - return; - } + // Send mousemove event to topmost target, unless it's an iframe, in which case the + // compositor should have also sent an event to the inner document. + let new_target = match maybe_new_target { + Some(ref target) => target, + None => return, + }; - self.fire_mouse_event(client_point, new_target.upcast(), "mousemove".to_owned()); - } + self.fire_mouse_event(client_point, new_target.upcast(), "mousemove".to_owned()); // Nothing more to do here, mousemove is sent, // and the element under the mouse hasn't changed. @@ -1197,12 +1144,14 @@ impl Document { } #[allow(unsafe_code)] - pub fn handle_touch_event(&self, - js_runtime: *mut JSRuntime, - event_type: TouchEventType, - touch_id: TouchId, - point: Point2D) - -> TouchEventResult { + pub fn handle_touch_event( + &self, + js_runtime: *mut JSRuntime, + event_type: TouchEventType, + touch_id: TouchId, + point: Point2D, + node_address: Option + ) -> TouchEventResult { let TouchId(identifier) = touch_id; let event_name = match event_type { @@ -1212,37 +1161,17 @@ impl Document { TouchEventType::Cancel => "touchcancel", }; - let node = match self.window.hit_test_query(point, false) { - Some(node_address) => unsafe { - node::from_untrusted_node_address(js_runtime, node_address) - }, - None => return TouchEventResult::Processed(false), - }; - let el = match node.downcast::() { - Some(el) => DomRoot::from_ref(el), - None => { - let parent = node.GetParentNode(); - match parent.and_then(DomRoot::downcast::) { - Some(parent) => parent, - None => return TouchEventResult::Processed(false), - } - }, + let el = node_address.and_then(|address| { + let node = unsafe { node::from_untrusted_node_address(js_runtime, address) }; + node.inclusive_ancestors() + .filter_map(DomRoot::downcast::) + .next() + }); + let el = match el { + Some(el) => el, + None => return TouchEventResult::Forwarded, }; - // If the target is an iframe, forward the event to the child document. - if let Some(iframe) = el.downcast::() { - if let Some(pipeline_id) = iframe.pipeline_id() { - let rect = iframe.upcast::().GetBoundingClientRect(); - let child_origin = Vector2D::new(rect.X() as f32, rect.Y() as f32); - let child_point = point - child_origin; - - let event = CompositorEvent::TouchEvent(event_type, touch_id, child_point); - let event = ScriptMsg::ForwardEvent(pipeline_id, event); - self.send_to_constellation(event); - } - return TouchEventResult::Forwarded; - } - let target = DomRoot::upcast::(el); let window = &*self.window; @@ -2005,8 +1934,12 @@ impl Document { !self.has_browsing_context || !url_has_network_scheme(&self.url()) } - pub fn nodes_from_point(&self, client_point: &Point2D) -> Vec { - if !self.window.reflow(ReflowGoal::NodesFromPoint(*client_point), ReflowReason::Query) { + pub fn nodes_from_point(&self, + client_point: &Point2D, + reflow_goal: NodesFromPointQueryType) + -> Vec { + if !self.window.reflow(ReflowGoal::NodesFromPointQuery(*client_point, reflow_goal), + ReflowReason::Query) { return vec!(); }; @@ -3662,12 +3595,11 @@ impl DocumentMethods for Document { return None; } - match self.window.hit_test_query(*point, false) { - Some(untrusted_node_address) => { + match self.nodes_from_point(point, NodesFromPointQueryType::Topmost).first() { + Some(address) => { let js_runtime = unsafe { JS_GetRuntime(window.get_cx()) }; - let node = unsafe { - node::from_untrusted_node_address(js_runtime, untrusted_node_address) + node::from_untrusted_node_address(js_runtime, *address) }; let parent_node = node.GetParentNode().unwrap(); let element_ref = node.downcast::().unwrap_or_else(|| { @@ -3701,7 +3633,8 @@ impl DocumentMethods for Document { let js_runtime = unsafe { JS_GetRuntime(window.get_cx()) }; // Step 1 and Step 3 - let mut elements: Vec> = self.nodes_from_point(point).iter() + let nodes = self.nodes_from_point(point, NodesFromPointQueryType::All); + let mut elements: Vec> = nodes.iter() .flat_map(|&untrusted_node_address| { let node = unsafe { node::from_untrusted_node_address(js_runtime, untrusted_node_address) diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 1f8e32820642..899cdac67927 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -1149,28 +1149,15 @@ impl Window { x: f32, y: f32, scroll_root_id: ClipId, - behavior: ScrollBehavior, - element: Option<&Element>) { - //TODO Step 1 - let point = Point2D::new(x, y); - let smooth = match behavior { - ScrollBehavior::Auto => { - element.map_or(false, |_element| { - // TODO check computed scroll-behaviour CSS property - true - }) - } - ScrollBehavior::Instant => false, - ScrollBehavior::Smooth => true - }; - + _behavior: ScrollBehavior, + _element: Option<&Element>) { + // TODO Step 1 + // TODO(mrobinson, #18709): Add smooth scrolling support to WebRender so that we can + // properly process ScrollBehavior here. self.layout_chan.send(Msg::UpdateScrollStateFromScript(ScrollState { scroll_root_id: scroll_root_id, scroll_offset: Vector2D::new(-x, -y), })).unwrap(); - - let message = ScriptMsg::ScrollFragmentPoint(scroll_root_id, point, smooth); - self.send_to_constellation(message); } pub fn update_viewport_for_scroll(&self, x: f32, y: f32) { @@ -1406,18 +1393,6 @@ impl Window { self.layout_rpc.node_geometry().client_rect } - pub fn hit_test_query(&self, - client_point: Point2D, - update_cursor: bool) - -> Option { - if !self.reflow(ReflowGoal::HitTestQuery(client_point, update_cursor), - ReflowReason::Query) { - return None - } - - self.layout_rpc.hit_test().node_address - } - pub fn scroll_area_query(&self, node: TrustedNodeAddress) -> Rect { if !self.reflow(ReflowGoal::NodeScrollGeometryQuery(node), ReflowReason::Query) { return Rect::zero(); @@ -1910,8 +1885,7 @@ fn debug_reflow_events(id: PipelineId, reflow_goal: &ReflowGoal, reason: &Reflow ReflowGoal::Full => "\tFull", ReflowGoal::ContentBoxQuery(_n) => "\tContentBoxQuery", ReflowGoal::ContentBoxesQuery(_n) => "\tContentBoxesQuery", - ReflowGoal::HitTestQuery(..) => "\tHitTestQuery", - ReflowGoal::NodesFromPoint(..) => "\tNodesFromPoint", + ReflowGoal::NodesFromPointQuery(..) => "\tNodesFromPointQuery", ReflowGoal::NodeGeometryQuery(_n) => "\tNodeGeometryQuery", ReflowGoal::NodeOverflowQuery(_n) => "\tNodeOverFlowQuery", ReflowGoal::NodeScrollGeometryQuery(_n) => "\tNodeScrollGeometryQuery", diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 6c722cec865a..c93fc56e50cd 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -1010,7 +1010,7 @@ impl ScriptThread { } FromConstellation(ConstellationControlMsg::SendEvent( _, - MouseMoveEvent(_))) => { + MouseMoveEvent(..))) => { match mouse_move_event_index { None => { mouse_move_event_index = Some(sequential.len()); @@ -2187,11 +2187,11 @@ impl ScriptThread { self.handle_resize_event(pipeline_id, new_size, size_type); } - MouseButtonEvent(event_type, button, point) => { - self.handle_mouse_event(pipeline_id, event_type, button, point); + MouseButtonEvent(event_type, button, point, node_address) => { + self.handle_mouse_event(pipeline_id, event_type, button, point, node_address); } - MouseMoveEvent(point) => { + MouseMoveEvent(point, node_address) => { let document = match { self.documents.borrow().find_document(pipeline_id) } { Some(document) => document, None => return warn!("Message sent to closed pipeline {}.", pipeline_id), @@ -2201,7 +2201,8 @@ impl ScriptThread { let prev_mouse_over_target = self.topmost_mouse_over_target.get(); document.handle_mouse_move_event(self.js_runtime.rt(), point, - &self.topmost_mouse_over_target); + &self.topmost_mouse_over_target, + node_address); // Short-circuit if nothing changed if self.topmost_mouse_over_target.get() == prev_mouse_over_target { @@ -2244,8 +2245,14 @@ impl ScriptThread { } } } - TouchEvent(event_type, identifier, point) => { - let touch_result = self.handle_touch_event(pipeline_id, event_type, identifier, point); + TouchEvent(event_type, identifier, point, node_address) => { + let touch_result = self.handle_touch_event( + pipeline_id, + event_type, + identifier, + point, + node_address + ); match (event_type, touch_result) { (TouchEventType::Down, TouchEventResult::Processed(handled)) => { let result = if handled { @@ -2263,12 +2270,17 @@ impl ScriptThread { } } - TouchpadPressureEvent(point, pressure, phase) => { + TouchpadPressureEvent(_point, pressure, phase, node_address) => { let doc = match { self.documents.borrow().find_document(pipeline_id) } { Some(doc) => doc, None => return warn!("Message sent to closed pipeline {}.", pipeline_id), }; - doc.handle_touchpad_pressure_event(self.js_runtime.rt(), point, pressure, phase); + doc.handle_touchpad_pressure_event( + self.js_runtime.rt(), + pressure, + phase, + node_address + ); } KeyEvent(ch, key, state, modifiers) => { @@ -2281,24 +2293,35 @@ impl ScriptThread { } } - fn handle_mouse_event(&self, - pipeline_id: PipelineId, - mouse_event_type: MouseEventType, - button: MouseButton, - point: Point2D) { + fn handle_mouse_event( + &self, + pipeline_id: PipelineId, + mouse_event_type: MouseEventType, + button: MouseButton, + point: Point2D, + node_address: Option + ) { let document = match { self.documents.borrow().find_document(pipeline_id) } { Some(document) => document, None => return warn!("Message sent to closed pipeline {}.", pipeline_id), }; - document.handle_mouse_event(self.js_runtime.rt(), button, point, mouse_event_type); + document.handle_mouse_event( + self.js_runtime.rt(), + button, + point, + mouse_event_type, + node_address + ); } - fn handle_touch_event(&self, - pipeline_id: PipelineId, - event_type: TouchEventType, - identifier: TouchId, - point: Point2D) - -> TouchEventResult { + fn handle_touch_event( + &self, + pipeline_id: PipelineId, + event_type: TouchEventType, + identifier: TouchId, + point: Point2D, + node_address: Option + ) -> TouchEventResult { let document = match { self.documents.borrow().find_document(pipeline_id) } { Some(document) => document, None => { @@ -2306,7 +2329,13 @@ impl ScriptThread { return TouchEventResult::Processed(true); }, }; - document.handle_touch_event(self.js_runtime.rt(), event_type, identifier, point) + document.handle_touch_event( + self.js_runtime.rt(), + event_type, + identifier, + point, + node_address + ) } /// https://html.spec.whatwg.org/multipage/#navigating-across-documents diff --git a/components/script_layout_interface/message.rs b/components/script_layout_interface/message.rs index a026b349ab87..6f8009ecdd88 100644 --- a/components/script_layout_interface/message.rs +++ b/components/script_layout_interface/message.rs @@ -100,6 +100,11 @@ pub enum Msg { SetNavigationStart(f64), } +#[derive(Debug, PartialEq)] +pub enum NodesFromPointQueryType { + All, + Topmost, +} /// Any query to perform with this reflow. #[derive(Debug, PartialEq)] @@ -109,7 +114,6 @@ pub enum ReflowGoal { ContentBoxQuery(TrustedNodeAddress), ContentBoxesQuery(TrustedNodeAddress), NodeOverflowQuery(TrustedNodeAddress), - HitTestQuery(Point2D, bool), NodeScrollRootIdQuery(TrustedNodeAddress), NodeGeometryQuery(TrustedNodeAddress), NodeScrollGeometryQuery(TrustedNodeAddress), @@ -117,7 +121,7 @@ pub enum ReflowGoal { OffsetParentQuery(TrustedNodeAddress), MarginStyleQuery(TrustedNodeAddress), TextIndexQuery(TrustedNodeAddress, i32, i32), - NodesFromPoint(Point2D), + NodesFromPointQuery(Point2D, NodesFromPointQueryType), } impl ReflowGoal { @@ -125,9 +129,8 @@ impl ReflowGoal { /// be present or false if it only needs stacking-relative positions. pub fn needs_display_list(&self) -> bool { match *self { - ReflowGoal::NodesFromPoint(..) | ReflowGoal::HitTestQuery(..) | - ReflowGoal::TextIndexQuery(..) | ReflowGoal::TickAnimations | - ReflowGoal::Full => true, + ReflowGoal::NodesFromPointQuery(..) | ReflowGoal::TextIndexQuery(..) | + ReflowGoal::TickAnimations | ReflowGoal::Full => true, ReflowGoal::ContentBoxQuery(_) | ReflowGoal::ContentBoxesQuery(_) | ReflowGoal::NodeGeometryQuery(_) | ReflowGoal::NodeScrollGeometryQuery(_) | ReflowGoal::NodeOverflowQuery(_) | ReflowGoal::NodeScrollRootIdQuery(_) | @@ -141,12 +144,13 @@ impl ReflowGoal { pub fn needs_display(&self) -> bool { match *self { ReflowGoal::MarginStyleQuery(_) | ReflowGoal::TextIndexQuery(..) | - ReflowGoal::HitTestQuery(..) | ReflowGoal::ContentBoxQuery(_) | - ReflowGoal::ContentBoxesQuery(_) | ReflowGoal::NodeGeometryQuery(_) | - ReflowGoal::NodeScrollGeometryQuery(_) | ReflowGoal::NodeOverflowQuery(_) | - ReflowGoal::NodeScrollRootIdQuery(_) | ReflowGoal::ResolvedStyleQuery(..) | - ReflowGoal::OffsetParentQuery(_) | ReflowGoal::NodesFromPoint(..) => false, - ReflowGoal::Full | ReflowGoal::TickAnimations => true, + ReflowGoal::ContentBoxQuery(_) | ReflowGoal::ContentBoxesQuery(_) | + ReflowGoal::NodeGeometryQuery(_) | ReflowGoal::NodeScrollGeometryQuery(_) | + ReflowGoal::NodeOverflowQuery(_) | ReflowGoal::NodeScrollRootIdQuery(_) | + ReflowGoal::ResolvedStyleQuery(..) | + ReflowGoal::OffsetParentQuery(_) => false, + ReflowGoal::NodesFromPointQuery(..) | ReflowGoal::Full | + ReflowGoal::TickAnimations => true, } } } diff --git a/components/script_layout_interface/rpc.rs b/components/script_layout_interface/rpc.rs index ab3509e76042..5702c8cce9d5 100644 --- a/components/script_layout_interface/rpc.rs +++ b/components/script_layout_interface/rpc.rs @@ -29,17 +29,15 @@ pub trait LayoutRPC { fn node_scroll_area(&self) -> NodeGeometryResponse; /// Requests the scroll root id of this node. Used by APIs such as `scrollTop` fn node_scroll_root_id(&self) -> NodeScrollRootIdResponse; - /// Requests the node containing the point of interest - fn hit_test(&self) -> HitTestResponse; /// Query layout for the resolved value of a given CSS property fn resolved_style(&self) -> ResolvedStyleResponse; fn offset_parent(&self) -> OffsetParentResponse; /// Query layout for the resolve values of the margin properties for an element. fn margin_style(&self) -> MarginStyleResponse; + fn text_index(&self) -> TextIndexResponse; /// Requests the list of nodes from the given point. fn nodes_from_point_response(&self) -> Vec; - fn text_index(&self) -> TextIndexResponse; } pub struct ContentBoxResponse(pub Option>); @@ -54,10 +52,6 @@ pub struct NodeOverflowResponse(pub Option, -} - pub struct ResolvedStyleResponse(pub String); #[derive(Clone)] diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index e8081c9eeafa..12028374a346 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -68,6 +68,7 @@ use std::sync::Arc; use std::sync::mpsc::{Receiver, Sender, RecvTimeoutError}; use style_traits::CSSPixel; use style_traits::SpeculativePainter; +use style_traits::cursor::Cursor; use webdriver_msg::{LoadStatus, WebDriverScriptCommand}; use webrender_api::{ClipId, DevicePixel, DocumentId, ImageKey}; use webvr_traits::{WebVREvent, WebVRMsg}; @@ -434,13 +435,13 @@ pub enum CompositorEvent { /// The window was resized. ResizeEvent(WindowSizeData, WindowSizeType), /// A mouse button state changed. - MouseButtonEvent(MouseEventType, MouseButton, Point2D), + MouseButtonEvent(MouseEventType, MouseButton, Point2D, Option), /// The mouse was moved over a point (or was moved out of the recognizable region). - MouseMoveEvent(Option>), + MouseMoveEvent(Option>, Option), /// A touch event was generated with a touch ID and location. - TouchEvent(TouchEventType, TouchId, Point2D), + TouchEvent(TouchEventType, TouchId, Point2D, Option), /// Touchpad pressure event - TouchpadPressureEvent(Point2D, f32, TouchpadPressurePhase), + TouchpadPressureEvent(Point2D, f32, TouchpadPressurePhase, Option), /// A key was pressed. KeyEvent(Option, Key, KeyState, KeyModifiers), } @@ -798,6 +799,10 @@ pub enum ConstellationMsg { CloseBrowser(TopLevelBrowsingContextId), /// Make browser visible. SelectBrowser(TopLevelBrowsingContextId), + /// Forward an event to the script task of the given pipeline. + ForwardEvent(PipelineId, CompositorEvent), + /// Requesting a change to the onscreen cursor. + SetCursor(Cursor), } /// Resources required by workerglobalscopes diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs index 6ad93d922d46..c338e8f1e987 100644 --- a/components/script_traits/script_msg.rs +++ b/components/script_traits/script_msg.rs @@ -27,7 +27,6 @@ use servo_url::ServoUrl; use style_traits::CSSPixel; use style_traits::cursor::Cursor; use style_traits::viewport::ViewportConstraints; -use webrender_api::ClipId; /// Messages from the layout to the constellation. #[derive(Deserialize, Serialize)] @@ -134,8 +133,6 @@ pub enum ScriptMsg { SetFinalUrl(ServoUrl), /// Check if an alert dialog box should be presented Alert(String, IpcSender), - /// Scroll a page in a window - ScrollFragmentPoint(ClipId, Point2D, bool), /// Set title of current page /// https://html.spec.whatwg.org/multipage/#document.title SetTitle(Option), diff --git a/components/style_traits/cursor.rs b/components/style_traits/cursor.rs index 9f28924601b2..ebd7e13c42c4 100644 --- a/components/style_traits/cursor.rs +++ b/components/style_traits/cursor.rs @@ -35,6 +35,15 @@ macro_rules! define_cursor { _ => Err(()) } } + + /// From the C u8 value, get the corresponding Cursor enum. + pub fn from_u8(value: u8) -> Result { + match value { + $( $c_value => Ok(Cursor::$c_variant), )+ + $( #[cfg(feature = "gecko")] $g_value => Ok(Cursor::$g_variant), )+ + _ => Err(()) + } + } } impl ToCss for Cursor {