servo: Merge #14367 - Reimplement scrolling to fragments (from mrobinson:scroll-fragment-point); r=pcwalton
authorMartin Robinson <mrobinson@igalia.com>
Tue, 06 Dec 2016 14:42:00 -0800
changeset 340285 d98492d1d7ed52e96c4da357476c7a39bc17ff4a
parent 340284 7d8be1b5c36a92e162454f3435ecf96d22f32634
child 340286 9d983fda55f42e837de30c159c61524c2e2853db
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspcwalton
servo: Merge #14367 - Reimplement scrolling to fragments (from mrobinson:scroll-fragment-point); r=pcwalton <!-- Please describe your changes on the following line: --> --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix #13736, #10753 (github issue number if applicable). <!-- Either: --> - [x] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> This reimplemntation of the feature uses ScrollRootIds to scroll particular scrollable areas of the page. Fixes #13736. Fixes #10753. Source-Repo: https://github.com/servo/servo Source-Revision: a0619688a638b322f17d2034e0309502af1a5e52
servo/components/compositing/compositor.rs
servo/components/compositing/compositor_thread.rs
servo/components/constellation/constellation.rs
servo/components/layout/flow.rs
servo/components/layout/query.rs
servo/components/layout_thread/lib.rs
servo/components/script/dom/document.rs
servo/components/script/dom/window.rs
servo/components/script_layout_interface/message.rs
servo/components/script_layout_interface/rpc.rs
servo/components/script_layout_interface/wrapper_traits.rs
servo/components/script_traits/script_msg.rs
--- a/servo/components/compositing/compositor.rs
+++ b/servo/components/compositing/compositor.rs
@@ -34,17 +34,17 @@ use std::sync::mpsc::Sender;
 use style_traits::{PagePx, ViewportPx};
 use style_traits::viewport::ViewportConstraints;
 use time::{precise_time_ns, precise_time_s};
 use touch::{TouchHandler, TouchAction};
 use util::geometry::ScreenPx;
 use util::opts;
 use util::prefs::PREFS;
 use webrender;
-use webrender_traits::{self, ScrollEventPhase};
+use webrender_traits::{self, ScrollEventPhase, ServoScrollRootId};
 use windowing::{self, MouseWindowEvent, WindowEvent, WindowMethods, WindowNavigateMsg};
 
 #[derive(Debug, PartialEq)]
 enum UnableToComposite {
     WindowUnprepared,
     NotReadyToPaintImage(NotReadyToPaint),
 }
 
@@ -488,19 +488,19 @@ impl<Window: WindowMethods> IOCompositor
 
             (Msg::SetFrameTree(frame_tree, response_chan),
              ShutdownState::NotShuttingDown) => {
                 self.set_frame_tree(&frame_tree, response_chan);
                 self.send_viewport_rects();
                 self.title_for_main_frame();
             }
 
-            (Msg::ScrollFragmentPoint(pipeline_id, point, _),
+            (Msg::ScrollFragmentPoint(pipeline_id, scroll_root_id, point, _),
              ShutdownState::NotShuttingDown) => {
-                self.scroll_fragment_to_point(pipeline_id, point);
+                self.scroll_fragment_to_point(pipeline_id, scroll_root_id, point);
             }
 
             (Msg::MoveTo(point),
              ShutdownState::NotShuttingDown) => {
                 self.window.set_position(point);
             }
 
             (Msg::ResizeTo(size),
@@ -756,19 +756,23 @@ impl<Window: WindowMethods> IOCompositor
         }
 
         let timestamp = precise_time_ns();
         self.delayed_composition_timer.schedule_composite(timestamp);
         self.composition_request = CompositionRequest::DelayedComposite(timestamp);
     }
 
     fn scroll_fragment_to_point(&mut self,
-                                _pipeline_id: PipelineId,
-                                _point: Point2D<f32>) {
-        println!("TODO: Support scroll_fragment_to_point again");
+                                pipeline_id: PipelineId,
+                                scroll_root_id: ScrollRootId,
+                                point: Point2D<f32>) {
+        self.webrender_api.scroll_layers_with_scroll_root_id(
+            point,
+            pipeline_id.to_webrender(),
+            ServoScrollRootId(scroll_root_id.0));
     }
 
     fn handle_window_message(&mut self, event: WindowEvent) {
         match event {
             WindowEvent::Idle => {}
 
             WindowEvent::Refresh => {
                 self.composite();
--- a/servo/components/compositing/compositor_thread.rs
+++ b/servo/components/compositing/compositor_thread.rs
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Communication with the compositor thread.
 
 use SendableFrameTree;
 use compositor::CompositingReason;
 use euclid::point::Point2D;
 use euclid::size::Size2D;
+use gfx_traits::ScrollRootId;
 use ipc_channel::ipc::IpcSender;
 use msg::constellation_msg::{Key, KeyModifiers, KeyState, PipelineId};
 use net_traits::image::base::Image;
 use profile_traits::mem;
 use profile_traits::time;
 use script_traits::{AnimationState, ConstellationMsg, EventResult};
 use servo_url::ServoUrl;
 use std::fmt::{Debug, Error, Formatter};
@@ -67,17 +68,17 @@ pub enum Msg {
     Exit,
 
     /// Informs the compositor that the constellation has completed shutdown.
     /// Required because the constellation can have pending calls to make
     /// (e.g. SetFrameTree) at the time that we send it an ExitMsg.
     ShutdownComplete,
 
     /// Scroll a page in a window
-    ScrollFragmentPoint(PipelineId, Point2D<f32>, bool),
+    ScrollFragmentPoint(PipelineId, ScrollRootId, Point2D<f32>, bool),
     /// Alerts the compositor that the current page has changed its title.
     ChangePageTitle(PipelineId, Option<String>),
     /// Alerts the compositor that the current page has changed its URL.
     ChangePageUrl(PipelineId, ServoUrl),
     /// 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.
     SetFrameTree(SendableFrameTree, IpcSender<()>),
--- a/servo/components/constellation/constellation.rs
+++ b/servo/components/constellation/constellation.rs
@@ -1033,18 +1033,19 @@ impl<Message, LTF, STF> Constellation<Me
                 debug!("constellation got SetDocumentState message");
                 self.document_states.insert(pipeline_id, state);
             }
             FromScriptMsg::Alert(pipeline_id, message, sender) => {
                 debug!("constellation got Alert message");
                 self.handle_alert(pipeline_id, message, sender);
             }
 
-            FromScriptMsg::ScrollFragmentPoint(pipeline_id, point, smooth) => {
+            FromScriptMsg::ScrollFragmentPoint(pipeline_id, scroll_root_id, point, smooth) => {
                 self.compositor_proxy.send(ToCompositorMsg::ScrollFragmentPoint(pipeline_id,
+                                                                                scroll_root_id,
                                                                                 point,
                                                                                 smooth));
             }
 
             FromScriptMsg::GetClientWindow(send) => {
                 self.compositor_proxy.send(ToCompositorMsg::GetClientWindow(send));
             }
 
--- a/servo/components/layout/flow.rs
+++ b/servo/components/layout/flow.rs
@@ -401,17 +401,20 @@ pub trait Flow: fmt::Debug + Sync + Send
 
     /// Attempts to perform incremental fixup of this flow by replacing its fragment's style with
     /// the new style. This can only succeed if the flow has exactly one fragment.
     fn repair_style(&mut self, new_style: &Arc<ServoComputedValues>);
 
     /// Print any extra children (such as fragments) contained in this Flow
     /// for debugging purposes. Any items inserted into the tree will become
     /// children of this flow.
-    fn print_extra_flow_children(&self, _: &mut PrintTree) {
+    fn print_extra_flow_children(&self, _: &mut PrintTree) { }
+
+    fn scroll_root_id(&self) -> ScrollRootId {
+        base(self).scroll_root_id
     }
 }
 
 // Base access
 
 #[inline(always)]
 #[allow(unsafe_code)]
 pub fn base<T: ?Sized + Flow>(this: &T) -> &BaseFlow {
--- a/servo/components/layout/query.rs
+++ b/servo/components/layout/query.rs
@@ -7,23 +7,24 @@
 use app_units::Au;
 use construct::ConstructionResult;
 use euclid::point::Point2D;
 use euclid::rect::Rect;
 use euclid::size::Size2D;
 use flow::{self, Flow};
 use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo};
 use gfx::display_list::{DisplayItemMetadata, DisplayList, OpaqueNode, ScrollOffsetMap};
+use gfx_traits::ScrollRootId;
 use ipc_channel::ipc::IpcSender;
 use opaque_node::OpaqueNodeMethods;
 use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse};
 use script_layout_interface::rpc::{HitTestResponse, LayoutRPC};
 use script_layout_interface::rpc::{MarginStyleResponse, NodeGeometryResponse};
 use script_layout_interface::rpc::{NodeOverflowResponse, OffsetParentResponse};
-use script_layout_interface::rpc::ResolvedStyleResponse;
+use script_layout_interface::rpc::{NodeScrollRootIdResponse, ResolvedStyleResponse};
 use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
 use script_traits::LayoutMsg as ConstellationMsg;
 use script_traits::UntrustedNodeAddress;
 use sequential;
 use servo_atoms::Atom;
 use std::cmp::{min, max};
 use std::ops::Deref;
 use std::sync::{Arc, Mutex};
@@ -59,16 +60,19 @@ pub struct LayoutThreadData {
     pub content_boxes_response: Vec<Rect<Au>>,
 
     /// A queued response for the client {top, left, width, height} of a node in pixels.
     pub client_rect_response: Rect<i32>,
 
     /// A queued response for the node at a given point
     pub hit_test_response: (Option<DisplayItemMetadata>, bool),
 
+    /// A queued response for the scroll root id for a given node.
+    pub scroll_root_id_response: Option<ScrollRootId>,
+
     /// A pair of overflow property in x and y
     pub overflow_response: NodeOverflowResponse,
 
     /// A queued response for the scroll {top, left, width, height} of a node in pixels.
     pub scroll_area_response: Rect<i32>,
 
     /// A queued response for the resolved style property of an element.
     pub resolved_style_response: Option<String>,
@@ -173,16 +177,22 @@ impl LayoutRPC for LayoutRPCImpl {
     }
 
     fn node_scroll_area(&self) -> NodeGeometryResponse {
         NodeGeometryResponse {
             client_rect: self.0.lock().unwrap().scroll_area_response
         }
     }
 
+    fn node_scroll_root_id(&self) -> NodeScrollRootIdResponse {
+        NodeScrollRootIdResponse(self.0.lock()
+                                        .unwrap().scroll_root_id_response
+                                        .expect("scroll_root_id is not correctly fetched"))
+    }
+
     /// Retrieves the resolved value for a CSS style property.
     fn resolved_style(&self) -> ResolvedStyleResponse {
         let &LayoutRPCImpl(ref rw_data) = self;
         let rw_data = rw_data.lock().unwrap();
         ResolvedStyleResponse(rw_data.resolved_style_response.clone())
     }
 
     fn offset_parent(&self) -> OffsetParentResponse {
@@ -573,16 +583,21 @@ impl FragmentBorderBoxIterator for Paren
 
 pub fn process_node_geometry_request<N: LayoutNode>(requested_node: N, layout_root: &mut Flow)
         -> Rect<i32> {
     let mut iterator = FragmentLocatingFragmentIterator::new(requested_node.opaque());
     sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
     iterator.client_rect
 }
 
+pub fn process_node_scroll_root_id_request<N: LayoutNode>(requested_node: N) -> ScrollRootId {
+    let layout_node = requested_node.to_threadsafe();
+    layout_node.scroll_root_id()
+}
+
 pub fn process_node_scroll_area_request< N: LayoutNode>(requested_node: N, layout_root: &mut Flow)
         -> Rect<i32> {
     let mut iterator = UnioningFragmentScrollAreaIterator::new(requested_node.opaque());
     sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
     match iterator.overflow_direction {
         OverflowDirection::RightAndDown => {
             let right = max(iterator.union_rect.size.width, iterator.origin_rect.size.width);
             let bottom = max(iterator.union_rect.size.height, iterator.origin_rect.size.height);
--- a/servo/components/layout_thread/lib.rs
+++ b/servo/components/layout_thread/lib.rs
@@ -67,17 +67,17 @@ use layout::display_list_builder::ToGfxC
 use layout::flow::{self, Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils};
 use layout::flow_ref::FlowRef;
 use layout::incremental::{LayoutDamageComputation, REFLOW_ENTIRE_DOCUMENT};
 use layout::layout_debug;
 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};
 use layout::query::{process_node_geometry_request, process_node_scroll_area_request};
-use layout::query::process_offset_parent_query;
+use layout::query::{process_node_scroll_root_id_request, process_offset_parent_query};
 use layout::sequential;
 use layout::traversal::{ComputeAbsolutePositions, RecalcStyleAndConstructFlows};
 use layout::webrender_helpers::WebRenderDisplayListConverter;
 use layout::wrapper::LayoutNodeLayoutData;
 use layout::wrapper::drop_style_and_layout_data;
 use layout_traits::LayoutThreadFactory;
 use msg::constellation_msg::{FrameId, PipelineId};
 use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCacheThread};
@@ -455,16 +455,17 @@ impl LayoutThread {
                 LayoutThreadData {
                     constellation_chan: constellation_chan,
                     display_list: None,
                     stylist: stylist,
                     content_box_response: Rect::zero(),
                     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),
                     resolved_style_response: None,
                     offset_parent_response: OffsetParentResponse::empty(),
                     margin_style_response: MarginStyleResponse::empty(),
                     stacking_context_scroll_offsets: HashMap::new(),
                 })),
             error_reporter: CSSErrorReporter {
@@ -998,16 +999,19 @@ impl LayoutThread {
                         rw_data.client_rect_response = Rect::zero();
                     },
                     ReflowQueryType::NodeScrollGeometryQuery(_) => {
                         rw_data.scroll_area_response = Rect::zero();
                     },
                     ReflowQueryType::NodeOverflowQuery(_) => {
                         rw_data.overflow_response = NodeOverflowResponse(None);
                     },
+                    ReflowQueryType::NodeScrollRootIdQuery(_) => {
+                        rw_data.scroll_root_id_response = None;
+                    },
                     ReflowQueryType::ResolvedStyleQuery(_, _, _) => {
                         rw_data.resolved_style_response = None;
                     },
                     ReflowQueryType::OffsetParentQuery(_) => {
                         rw_data.offset_parent_response = OffsetParentResponse::empty();
                     },
                     ReflowQueryType::MarginStyleQuery(_) => {
                         rw_data.margin_style_response = MarginStyleResponse::empty();
@@ -1227,16 +1231,20 @@ impl LayoutThread {
             ReflowQueryType::NodeScrollGeometryQuery(node) => {
                 let node = unsafe { ServoLayoutNode::new(&node) };
                 rw_data.scroll_area_response = process_node_scroll_area_request(node, root_flow);
             },
             ReflowQueryType::NodeOverflowQuery(node) => {
                 let node = unsafe { ServoLayoutNode::new(&node) };
                 rw_data.overflow_response = process_node_overflow_request(node);
             },
+            ReflowQueryType::NodeScrollRootIdQuery(node) => {
+                let node = unsafe { ServoLayoutNode::new(&node) };
+                rw_data.scroll_root_id_response = Some(process_node_scroll_root_id_request(node));
+            },
             ReflowQueryType::ResolvedStyleQuery(node, ref pseudo, ref property) => {
                 let node = unsafe { ServoLayoutNode::new(&node) };
                 let layout_context = LayoutContext::new(&shared_layout_context);
                 rw_data.resolved_style_response =
                     process_resolved_style_request(node,
                                                    &layout_context,
                                                    pseudo,
                                                    property,
@@ -1551,19 +1559,19 @@ fn get_ua_stylesheets() -> Result<UserAg
 
 /// Returns true if the given reflow query type needs a full, up-to-date display list to be present
 /// or false if it only needs stacking-relative positions.
 fn reflow_query_type_needs_display_list(query_type: &ReflowQueryType) -> bool {
     match *query_type {
         ReflowQueryType::HitTestQuery(..) => true,
         ReflowQueryType::ContentBoxQuery(_) | ReflowQueryType::ContentBoxesQuery(_) |
         ReflowQueryType::NodeGeometryQuery(_) | ReflowQueryType::NodeScrollGeometryQuery(_) |
-        ReflowQueryType::NodeOverflowQuery(_) | ReflowQueryType::ResolvedStyleQuery(..) |
-        ReflowQueryType::OffsetParentQuery(_) | ReflowQueryType::MarginStyleQuery(_) |
-        ReflowQueryType::NoQuery => false,
+        ReflowQueryType::NodeOverflowQuery(_) | ReflowQueryType::NodeScrollRootIdQuery(_) |
+        ReflowQueryType::ResolvedStyleQuery(..) | ReflowQueryType::OffsetParentQuery(_) |
+        ReflowQueryType::MarginStyleQuery(_) | ReflowQueryType::NoQuery => false,
     }
 }
 
 lazy_static! {
     static ref UA_STYLESHEETS: UserAgentStylesheets = {
         match get_ua_stylesheets() {
             Ok(stylesheets) => stylesheets,
             Err(filename) => {
--- a/servo/components/script/dom/document.rs
+++ b/servo/components/script/dom/document.rs
@@ -82,16 +82,17 @@ use dom::touchevent::TouchEvent;
 use dom::touchlist::TouchList;
 use dom::treewalker::TreeWalker;
 use dom::uievent::UIEvent;
 use dom::webglcontextevent::WebGLContextEvent;
 use dom::window::{ReflowReason, Window};
 use encoding::EncodingRef;
 use encoding::all::UTF_8;
 use euclid::point::Point2D;
+use gfx_traits::ScrollRootId;
 use html5ever::tree_builder::{LimitedQuirks, NoQuirks, Quirks, QuirksMode};
 use html5ever_atoms::{LocalName, QualName};
 use ipc_channel::ipc::{self, IpcSender};
 use js::jsapi::{JSContext, JSObject, JSRuntime};
 use js::jsapi::JS_GetRuntime;
 use msg::constellation_msg::{ALT, CONTROL, SHIFT, SUPER};
 use msg::constellation_msg::{FrameId, Key, KeyModifiers, KeyState};
 use net_traits::{FetchResponseMsg, IpcSend, ReferrerPolicy};
@@ -614,17 +615,20 @@ impl Document {
             // which may differ under the influence of writing mode.
             Some((0.0, 0.0))
         } else {
             None
         });
 
         if let Some((x, y)) = point {
             // Step 3
-            self.window.perform_a_scroll(x, y, ScrollBehavior::Instant,
+            self.window.perform_a_scroll(x,
+                                         y,
+                                         ScrollRootId::root(),
+                                         ScrollBehavior::Instant,
                                          target.r());
         }
     }
 
     fn get_anchor_by_name(&self, name: &str) -> Option<Root<Element>> {
         let check_anchor = |node: &HTMLAnchorElement| {
             let elem = node.upcast::<Element>();
             elem.get_attribute(&ns!(), &local_name!("name"))
--- a/servo/components/script/dom/window.rs
+++ b/servo/components/script/dom/window.rs
@@ -43,16 +43,17 @@ use dom::navigator::Navigator;
 use dom::node::{Node, from_untrusted_node_address, window_from_node};
 use dom::performance::Performance;
 use dom::promise::Promise;
 use dom::screen::Screen;
 use dom::storage::Storage;
 use dom::testrunner::TestRunner;
 use euclid::{Point2D, Rect, Size2D};
 use fetch;
+use gfx_traits::ScrollRootId;
 use ipc_channel::ipc::{self, IpcSender};
 use js::jsapi::{HandleObject, HandleValue, JSAutoCompartment, JSContext};
 use js::jsapi::{JS_GC, JS_GetRuntime, SetWindowProxy};
 use js::jsval::UndefinedValue;
 use js::rust::Runtime;
 use msg::constellation_msg::{FrameType, PipelineId};
 use net_traits::{ResourceThreads, ReferrerPolicy};
 use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread};
@@ -62,17 +63,18 @@ use open;
 use origin::Origin;
 use profile_traits::mem;
 use profile_traits::time::ProfilerChan;
 use rustc_serialize::base64::{FromBase64, STANDARD, ToBase64};
 use script_layout_interface::TrustedNodeAddress;
 use script_layout_interface::message::{Msg, Reflow, ReflowQueryType, ScriptReflow};
 use script_layout_interface::reporter::CSSErrorReporter;
 use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC};
-use script_layout_interface::rpc::{MarginStyleResponse, ResolvedStyleResponse};
+use script_layout_interface::rpc::{MarginStyleResponse, NodeScrollRootIdResponse};
+use script_layout_interface::rpc::ResolvedStyleResponse;
 use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, ScriptThreadEventCategory};
 use script_thread::{MainThreadScriptChan, MainThreadScriptMsg, Runnable, RunnableWrapper};
 use script_thread::SendableMainThreadScriptChan;
 use script_traits::{ConstellationControlMsg, LoadData, MozBrowserEvent, UntrustedNodeAddress};
 use script_traits::{DocumentState, TimerEvent, TimerEventId};
 use script_traits::{ScriptMsg as ConstellationMsg, TimerEventRequest, WindowSizeData, WindowSizeType};
 use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult};
 use servo_atoms::Atom;
@@ -962,23 +964,30 @@ impl Window {
         //TODO handling ongoing smooth scrolling
         if x == self.ScrollX() as f64 && y == self.ScrollY() as f64 {
             return;
         }
 
         //TODO Step 11
         //let document = self.Document();
         // Step 12
-        self.perform_a_scroll(x.to_f32().unwrap_or(0.0f32), y.to_f32().unwrap_or(0.0f32),
-                              behavior, None);
+        self.perform_a_scroll(x.to_f32().unwrap_or(0.0f32),
+                              y.to_f32().unwrap_or(0.0f32),
+                              ScrollRootId::root(),
+                              behavior,
+                              None);
     }
 
     /// https://drafts.csswg.org/cssom-view/#perform-a-scroll
-    pub fn perform_a_scroll(&self, x: f32, y: f32,
-                            behavior: ScrollBehavior, element: Option<&Element>) {
+    pub fn perform_a_scroll(&self,
+                            x: f32,
+                            y: f32,
+                            scroll_root_id: ScrollRootId,
+                            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
                 })
@@ -987,17 +996,17 @@ impl Window {
             ScrollBehavior::Smooth => true
         };
 
         // TODO (farodin91): Raise an event to stop the current_viewport
         self.update_viewport_for_scroll(x, y);
 
         let global_scope = self.upcast::<GlobalScope>();
         let message = ConstellationMsg::ScrollFragmentPoint(
-            global_scope.pipeline_id(), point, smooth);
+            global_scope.pipeline_id(), scroll_root_id, point, smooth);
         global_scope.constellation_chan().send(message).unwrap();
     }
 
     pub fn update_viewport_for_scroll(&self, x: f32, y: f32) {
         let size = self.current_viewport.get().size;
         let new_viewport = Rect::new(Point2D::new(Au::from_f32_px(x), Au::from_f32_px(y)), size);
         self.current_viewport.set(new_viewport)
     }
@@ -1267,21 +1276,34 @@ impl Window {
                 None => break,
             }
         }
         let offset = self.current_viewport.get().origin;
         Point2D::new(offset.x.to_f32_px(), offset.y.to_f32_px())
     }
 
     // https://drafts.csswg.org/cssom-view/#dom-element-scroll
-    pub fn scroll_node(&self, _node: TrustedNodeAddress,
-                       x_: f64, y_: f64, behavior: ScrollBehavior) {
+    pub fn scroll_node(&self,
+                       node: TrustedNodeAddress,
+                       x_: f64,
+                       y_: f64,
+                       behavior: ScrollBehavior) {
+        if !self.reflow(ReflowGoal::ForScriptQuery,
+                        ReflowQueryType::NodeScrollRootIdQuery(node),
+                        ReflowReason::Query) {
+            return;
+        }
+        let NodeScrollRootIdResponse(scroll_root_id) = self.layout_rpc.node_scroll_root_id();
+
         // Step 12
-        self.perform_a_scroll(x_.to_f32().unwrap_or(0.0f32), y_.to_f32().unwrap_or(0.0f32),
-                              behavior, None);
+        self.perform_a_scroll(x_.to_f32().unwrap_or(0.0f32),
+                              y_.to_f32().unwrap_or(0.0f32),
+                              scroll_root_id,
+                              behavior,
+                              None);
     }
 
     pub fn resolved_style_query(&self,
                             element: TrustedNodeAddress,
                             pseudo: Option<PseudoElement>,
                             property: &Atom) -> Option<DOMString> {
         if !self.reflow(ReflowGoal::ForScriptQuery,
                         ReflowQueryType::ResolvedStyleQuery(element, pseudo, property.clone()),
@@ -1643,16 +1665,17 @@ fn debug_reflow_events(id: PipelineId, g
     debug_msg.push_str(match *query_type {
         ReflowQueryType::NoQuery => "\tNoQuery",
         ReflowQueryType::ContentBoxQuery(_n) => "\tContentBoxQuery",
         ReflowQueryType::ContentBoxesQuery(_n) => "\tContentBoxesQuery",
         ReflowQueryType::HitTestQuery(..) => "\tHitTestQuery",
         ReflowQueryType::NodeGeometryQuery(_n) => "\tNodeGeometryQuery",
         ReflowQueryType::NodeOverflowQuery(_n) => "\tNodeOverFlowQuery",
         ReflowQueryType::NodeScrollGeometryQuery(_n) => "\tNodeScrollGeometryQuery",
+        ReflowQueryType::NodeScrollRootIdQuery(_n) => "\tNodeScrollRootIdQuery",
         ReflowQueryType::ResolvedStyleQuery(_, _, _) => "\tResolvedStyleQuery",
         ReflowQueryType::OffsetParentQuery(_n) => "\tOffsetParentQuery",
         ReflowQueryType::MarginStyleQuery(_n) => "\tMarginStyleQuery",
     });
 
     debug_msg.push_str(match *reason {
         ReflowReason::CachedPageNeededReflow => "\tCachedPageNeededReflow",
         ReflowReason::RefreshTick => "\tRefreshTick",
--- a/servo/components/script_layout_interface/message.rs
+++ b/servo/components/script_layout_interface/message.rs
@@ -89,16 +89,17 @@ pub enum Msg {
 /// Any query to perform with this reflow.
 #[derive(Debug, PartialEq)]
 pub enum ReflowQueryType {
     NoQuery,
     ContentBoxQuery(TrustedNodeAddress),
     ContentBoxesQuery(TrustedNodeAddress),
     NodeOverflowQuery(TrustedNodeAddress),
     HitTestQuery(Point2D<f32>, Point2D<f32>, bool),
+    NodeScrollRootIdQuery(TrustedNodeAddress),
     NodeGeometryQuery(TrustedNodeAddress),
     NodeScrollGeometryQuery(TrustedNodeAddress),
     ResolvedStyleQuery(TrustedNodeAddress, Option<PseudoElement>, Atom),
     OffsetParentQuery(TrustedNodeAddress),
     MarginStyleQuery(TrustedNodeAddress),
 }
 
 /// Information needed for a reflow.
--- a/servo/components/script_layout_interface/rpc.rs
+++ b/servo/components/script_layout_interface/rpc.rs
@@ -1,15 +1,16 @@
 /* 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 app_units::Au;
 use euclid::point::Point2D;
 use euclid::rect::Rect;
+use gfx_traits::ScrollRootId;
 use script_traits::UntrustedNodeAddress;
 use style::properties::longhands::{margin_top, margin_right, margin_bottom, margin_left, overflow_x};
 
 /// Synchronous messages that script can send to layout.
 ///
 /// In general, you should use messages to talk to Layout. Use the RPC interface
 /// if and only if the work is
 ///
@@ -22,16 +23,18 @@ pub trait LayoutRPC {
     /// Requests the dimensions of all the content boxes, as in the `getClientRects()` call.
     fn content_boxes(&self) -> ContentBoxesResponse;
     /// Requests the geometry of this node. Used by APIs such as `clientTop`.
     fn node_geometry(&self) -> NodeGeometryResponse;
     /// Requests the overflow-x and overflow-y of this node. Used by `scrollTop` etc.
     fn node_overflow(&self) -> NodeOverflowResponse;
     /// Requests the scroll geometry of this node. Used by APIs such as `scrollTop`.
     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;
 
@@ -43,16 +46,18 @@ pub struct ContentBoxResponse(pub Rect<A
 pub struct ContentBoxesResponse(pub Vec<Rect<Au>>);
 
 pub struct NodeGeometryResponse {
     pub client_rect: Rect<i32>,
 }
 
 pub struct NodeOverflowResponse(pub Option<Point2D<overflow_x::computed_value::T>>);
 
+pub struct NodeScrollRootIdResponse(pub ScrollRootId);
+
 pub struct HitTestResponse {
     pub node_address: Option<UntrustedNodeAddress>,
 }
 
 pub struct ResolvedStyleResponse(pub Option<String>);
 
 #[derive(Clone)]
 pub struct OffsetParentResponse {
--- a/servo/components/script_layout_interface/wrapper_traits.rs
+++ b/servo/components/script_layout_interface/wrapper_traits.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #![allow(unsafe_code)]
 
 use HTMLCanvasData;
 use LayoutNodeType;
 use OpaqueStyleAndLayoutData;
 use SVGSVGData;
-use gfx_traits::ByteIndex;
+use gfx_traits::{ByteIndex, FragmentType, ScrollRootId};
 use html5ever_atoms::{Namespace, LocalName};
 use msg::constellation_msg::PipelineId;
 use range::Range;
 use servo_url::ServoUrl;
 use std::fmt::Debug;
 use std::sync::Arc;
 use style::atomic_refcell::AtomicRefCell;
 use style::computed_values::display;
@@ -259,16 +259,30 @@ pub trait ThreadSafeLayoutNode: Clone + 
 
     fn svg_data(&self) -> Option<SVGSVGData>;
 
     /// If this node is an iframe element, returns its pipeline ID. If this node is
     /// not an iframe element, fails.
     fn iframe_pipeline_id(&self) -> PipelineId;
 
     fn get_colspan(&self) -> u32;
+
+    fn fragment_type(&self) -> FragmentType {
+        match self.get_pseudo_element_type() {
+            PseudoElementType::Normal => FragmentType::FragmentBody,
+            PseudoElementType::Before(_) => FragmentType::BeforePseudoContent,
+            PseudoElementType::After(_) => FragmentType::AfterPseudoContent,
+            PseudoElementType::DetailsSummary(_) => FragmentType::FragmentBody,
+            PseudoElementType::DetailsContent(_) => FragmentType::FragmentBody,
+        }
+    }
+
+    fn scroll_root_id(&self) -> ScrollRootId {
+        ScrollRootId::new_of_type(self.opaque().id() as usize, self.fragment_type())
+    }
 }
 
 // This trait is only public so that it can be implemented by the gecko wrapper.
 // It can be used to violate thread-safety, so don't use it elsewhere in layout!
 #[allow(unsafe_code)]
 pub trait DangerousThreadSafeLayoutNode: ThreadSafeLayoutNode {
     unsafe fn dangerous_first_child(&self) -> Option<Self>;
     unsafe fn dangerous_next_sibling(&self) -> Option<Self>;
--- a/servo/components/script_traits/script_msg.rs
+++ b/servo/components/script_traits/script_msg.rs
@@ -11,16 +11,17 @@ use LayoutControlMsg;
 use LoadData;
 use MozBrowserEvent;
 use WorkerGlobalScopeInit;
 use WorkerScriptLoadOrigin;
 use canvas_traits::CanvasMsg;
 use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
 use euclid::point::Point2D;
 use euclid::size::Size2D;
+use gfx_traits::ScrollRootId;
 use ipc_channel::ipc::IpcSender;
 use msg::constellation_msg::{FrameId, PipelineId, TraversalDirection};
 use msg::constellation_msg::{Key, KeyModifiers, KeyState};
 use net_traits::CoreResourceMsg;
 use net_traits::storage_thread::StorageType;
 use offscreen_gl_context::{GLContextAttributes, GLLimits};
 use servo_url::ServoUrl;
 use style_traits::cursor::Cursor;
@@ -116,17 +117,17 @@ pub enum ScriptMsg {
     ActivateDocument(PipelineId),
     /// Set the document state for a pipeline (used by screenshot / reftests)
     SetDocumentState(PipelineId, DocumentState),
     /// Update the pipeline Url, which can change after redirections.
     SetFinalUrl(PipelineId, ServoUrl),
     /// Check if an alert dialog box should be presented
     Alert(PipelineId, String, IpcSender<bool>),
     /// Scroll a page in a window
-    ScrollFragmentPoint(PipelineId, Point2D<f32>, bool),
+    ScrollFragmentPoint(PipelineId, ScrollRootId, Point2D<f32>, bool),
     /// Set title of current page
     /// https://html.spec.whatwg.org/multipage/#document.title
     SetTitle(PipelineId, Option<String>),
     /// Send a key event
     SendKeyEvent(Option<char>, Key, KeyState, KeyModifiers),
     /// Get Window Informations size and position
     GetClientWindow(IpcSender<(Size2D<u32>, Point2D<i32>)>),
     /// Move the window to a point