diff options
Diffstat (limited to 'components/compositing/compositor.rs')
-rw-r--r-- | components/compositing/compositor.rs | 89 |
1 files changed, 85 insertions, 4 deletions
diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index b453547365f..b1ac1dfd3c9 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -17,6 +17,7 @@ use canvas::canvas_paint_thread::ImageUpdate; use crossbeam_channel::Sender; use embedder_traits::Cursor; use euclid::{Point2D, Rect, Scale, Vector2D}; +use fnv::{FnvHashMap, FnvHashSet}; use gfx_traits::{Epoch, FontData}; #[cfg(feature = "gl")] use image::{DynamicImage, ImageFormat}; @@ -31,7 +32,7 @@ use net_traits::image_cache::CorsStatus; #[cfg(feature = "gl")] use pixels::PixelFormat; use profile_traits::time::{self as profile_time, profile, ProfilerCategory}; -use script_traits::compositor::HitTestInfo; +use script_traits::compositor::{HitTestInfo, ScrollTree}; use script_traits::CompositorEvent::{MouseButtonEvent, MouseMoveEvent, TouchEvent, WheelEvent}; use script_traits::{ AnimationState, AnimationTickType, CompositorHitTestResult, LayoutControlMsg, MouseButton, @@ -49,9 +50,9 @@ use style_traits::viewport::ViewportConstraints; use style_traits::{CSSPixel, DevicePixel, PinchZoomFactor}; use time::{now, precise_time_ns, precise_time_s}; use webrender_api::units::{ - DeviceIntPoint, DeviceIntSize, DevicePoint, LayoutVector2D, WorldPoint, + DeviceIntPoint, DeviceIntSize, DevicePoint, LayoutPoint, LayoutVector2D, WorldPoint, }; -use webrender_api::{self, HitTestFlags, ScrollLocation}; +use webrender_api::{self, ExternalScrollId, HitTestFlags, ScrollClamping, ScrollLocation}; use webrender_surfman::WebrenderSurfman; #[derive(Debug, PartialEq)] @@ -269,6 +270,10 @@ struct PipelineDetails { /// Hit test items for this pipeline. This is used to map WebRender hit test /// information to the full information necessary for Servo. hit_test_items: Vec<HitTestInfo>, + + /// The compositor-side [ScrollTree]. This is used to allow finding and scrolling + /// nodes in the compositor before forwarding new offsets to WebRender. + scroll_tree: ScrollTree, } impl PipelineDetails { @@ -279,6 +284,30 @@ impl PipelineDetails { animation_callbacks_running: false, visible: true, hit_test_items: Vec::new(), + scroll_tree: ScrollTree::default(), + } + } + + fn install_new_scroll_tree(&mut self, new_scroll_tree: ScrollTree) { + let old_scroll_offsets: FnvHashMap<ExternalScrollId, LayoutVector2D> = self + .scroll_tree + .nodes + .drain(..) + .filter_map(|node| match (node.external_id(), node.offset()) { + (Some(external_id), Some(offset)) => Some((external_id, offset)), + _ => None, + }) + .collect(); + + self.scroll_tree = new_scroll_tree; + for node in self.scroll_tree.nodes.iter_mut() { + match node.external_id() { + Some(external_id) => match old_scroll_offsets.get(&external_id) { + Some(new_offset) => node.set_offset(*new_offset), + None => continue, + }, + _ => continue, + }; } } } @@ -647,6 +676,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> { let details = self.pipeline_details(PipelineId::from_webrender(pipeline)); details.hit_test_items = compositor_display_list_info.hit_test_info; + details.install_new_scroll_tree(compositor_display_list_info.scroll_tree); let mut txn = webrender_api::Transaction::new(); txn.set_display_list( @@ -850,10 +880,38 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> { .send_transaction(self.webrender_document, txn); self.create_pipeline_details_for_frame_tree(&frame_tree); + self.reset_scroll_tree_for_unattached_pipelines(&frame_tree); self.frame_tree_id.next(); } + fn reset_scroll_tree_for_unattached_pipelines(&mut self, frame_tree: &SendableFrameTree) { + // TODO(mrobinson): Eventually this can selectively preserve the scroll trees + // state for some unattached pipelines in order to preserve scroll position when + // navigating backward and forward. + fn collect_pipelines( + pipelines: &mut FnvHashSet<PipelineId>, + frame_tree: &SendableFrameTree, + ) { + pipelines.insert(frame_tree.pipeline.id); + for kid in &frame_tree.children { + collect_pipelines(pipelines, kid); + } + } + + let mut attached_pipelines: FnvHashSet<PipelineId> = FnvHashSet::default(); + collect_pipelines(&mut attached_pipelines, frame_tree); + + self.pipeline_details + .iter_mut() + .filter(|(id, _)| !attached_pipelines.contains(id)) + .for_each(|(_, details)| { + details.scroll_tree.nodes.iter_mut().for_each(|node| { + node.set_offset(LayoutVector2D::zero()); + }) + }) + } + fn create_pipeline_details_for_frame_tree(&mut self, frame_tree: &SendableFrameTree) { self.pipeline_details(frame_tree.pipeline.id).pipeline = Some(frame_tree.pipeline.clone()); @@ -1005,6 +1063,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> { point_relative_to_item: item.point_relative_to_item.to_untyped(), node: UntrustedNodeAddress(info.node as *const c_void), cursor: info.cursor, + scroll_tree_node: info.scroll_tree_node, }) }) .collect() @@ -1220,7 +1279,29 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> { let cursor = (combined_event.cursor.to_f32() / self.scale).to_untyped(); let cursor = WorldPoint::from_untyped(cursor); let mut txn = webrender_api::Transaction::new(); - txn.scroll(scroll_location, cursor); + + let result = match self.hit_test_at_point(cursor) { + Some(result) => result, + None => return, + }; + + if let Some(details) = self.pipeline_details.get_mut(&result.pipeline_id) { + match details + .scroll_tree + .scroll_node_or_ancestor(&result.scroll_tree_node, scroll_location) + { + Some((external_id, offset)) => { + let scroll_origin = LayoutPoint::new(-offset.x, -offset.y); + txn.scroll_node_with_id( + scroll_origin, + external_id, + ScrollClamping::NoClamping, + ); + }, + None => {}, + } + } + if combined_event.magnification != 1.0 { let old_zoom = self.pinch_zoom_level(); self.set_pinch_zoom_level(old_zoom * combined_event.magnification); |