Auto merge of #9968 - izgzhen:scroll, r=asajeffrey

Implement scroll, scrollLeft, scrollTop and friends, addressing issue #9650

This is a work in progress to solve https://github.com/servo/servo/issues/9650. Thanks a lot for helping the review.

- [x] scroll
- [x] scrollTo
- [x] scrollBy
- [x] scrollTop (setter and getter)
- [x] scrollLeft (setter and getter)

The setters will be implemented in another PR after this is merged.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/9968)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-04-20 18:40:26 +05:30
commit 8d988f20c1
15 changed files with 569 additions and 12 deletions

View file

@ -714,6 +714,18 @@ impl<Window: WindowMethods> IOCompositor<Window> {
let _ = sender.send(());
}
(Msg::GetScrollOffset(pipeline_id, layer_id, sender), ShutdownState::NotShuttingDown) => {
match self.find_layer_with_pipeline_and_layer_id(pipeline_id, layer_id) {
Some(ref layer) => {
let typed = layer.extra_data.borrow().scroll_offset;
let _ = sender.send(Point2D::new(typed.x.get(), typed.y.get()));
},
None => {
warn!("Can't find requested layer in handling Msg::GetScrollOffset");
},
}
}
// When we are shutting_down, we need to avoid performing operations
// such as Paint that may crash because we have begun tearing down
// the rest of our resources.

View file

@ -98,6 +98,10 @@ pub fn run_script_listener_thread(compositor_proxy: Box<CompositorProxy + 'stati
compositor_proxy.send(Msg::TouchEventProcessed(result))
}
ScriptToCompositorMsg::GetScrollOffset(pid, lid, send) => {
compositor_proxy.send(Msg::GetScrollOffset(pid, lid, send));
}
ScriptToCompositorMsg::Exited => break,
}
}
@ -231,6 +235,8 @@ pub enum Msg {
MoveTo(Point2D<i32>),
/// Resize the window to size
ResizeTo(Size2D<u32>),
/// Get scroll offset of a layer
GetScrollOffset(PipelineId, LayerId, IpcSender<Point2D<f32>>),
/// A pipeline was shut down.
// This message acts as a synchronization point between the constellation,
// when it shuts down a pipeline, to the compositor; when the compositor
@ -272,6 +278,7 @@ impl Debug for Msg {
Msg::MoveTo(..) => write!(f, "MoveTo"),
Msg::ResizeTo(..) => write!(f, "ResizeTo"),
Msg::PipelineExited(..) => write!(f, "PipelineExited"),
Msg::GetScrollOffset(..) => write!(f, "GetScrollOffset"),
}
}
}

View file

@ -45,10 +45,11 @@ use profile_traits::mem::{self, Report, ReportKind, ReportsChan};
use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType};
use profile_traits::time::{self, TimerMetadata, profile};
use query::{LayoutRPCImpl, process_content_box_request, process_content_boxes_request};
use query::{process_node_geometry_request, process_node_scroll_area_request, process_offset_parent_query};
use query::{process_resolved_style_request, process_margin_style_query};
use query::{process_node_geometry_request, process_node_layer_id_request, process_node_scroll_area_request};
use query::{process_node_overflow_request, process_resolved_style_request, process_margin_style_query};
use query::{process_offset_parent_query};
use script::dom::node::OpaqueStyleAndLayoutData;
use script::layout_interface::{LayoutRPC, OffsetParentResponse, MarginStyleResponse};
use script::layout_interface::{LayoutRPC, OffsetParentResponse, NodeOverflowResponse, MarginStyleResponse};
use script::layout_interface::{Msg, NewLayoutThreadInfo, Reflow, ReflowQueryType};
use script::layout_interface::{ScriptLayoutChan, ScriptReflow};
use script::reporter::CSSErrorReporter;
@ -117,9 +118,14 @@ pub struct LayoutThreadData {
/// A queued response for the client {top, left, width, height} of a node in pixels.
pub client_rect_response: Rect<i32>,
pub layer_id_response: Option<LayerId>,
/// A queued response for the node at a given point
pub hit_test_response: (Option<DisplayItemMetadata>, bool),
/// 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>,
@ -463,8 +469,10 @@ impl LayoutThread {
content_box_response: Rect::zero(),
content_boxes_response: Vec::new(),
client_rect_response: Rect::zero(),
layer_id_response: None,
hit_test_response: (None, false),
scroll_area_response: Rect::zero(),
overflow_response: NodeOverflowResponse(None),
resolved_style_response: None,
offset_parent_response: OffsetParentResponse::empty(),
margin_style_response: MarginStyleResponse::empty(),
@ -1015,9 +1023,15 @@ impl LayoutThread {
ReflowQueryType::NodeGeometryQuery(_) => {
rw_data.client_rect_response = Rect::zero();
},
ReflowQueryType::NodeLayerIdQuery(_) => {
rw_data.layer_id_response = None;
},
ReflowQueryType::NodeScrollGeometryQuery(_) => {
rw_data.scroll_area_response = Rect::zero();
},
ReflowQueryType::NodeOverflowQuery(_) => {
rw_data.overflow_response = NodeOverflowResponse(None);
},
ReflowQueryType::ResolvedStyleQuery(_, _, _) => {
rw_data.resolved_style_response = None;
},
@ -1177,6 +1191,14 @@ impl LayoutThread {
let node = unsafe { ServoLayoutNode::new(&node) };
rw_data.scroll_area_response = process_node_scroll_area_request(node, &mut root_flow);
},
ReflowQueryType::NodeOverflowQuery(node) => {
let node = unsafe { ServoLayoutNode::new(&node) };
rw_data.overflow_response = process_node_overflow_request(node);
},
ReflowQueryType::NodeLayerIdQuery(node) => {
let node = unsafe { ServoLayoutNode::new(&node) };
rw_data.layer_id_response = Some(process_node_layer_id_request(node));
},
ReflowQueryType::ResolvedStyleQuery(node, ref pseudo, ref property) => {
let node = unsafe { ServoLayoutNode::new(&node) };
rw_data.resolved_style_response =

View file

@ -13,11 +13,12 @@ use flow;
use flow_ref::FlowRef;
use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo};
use gfx::display_list::OpaqueNode;
use gfx_traits::{LayerId};
use layout_thread::LayoutThreadData;
use msg::constellation_msg::ConstellationChan;
use opaque_node::OpaqueNodeMethods;
use script::layout_interface::{ContentBoxResponse, ContentBoxesResponse, NodeGeometryResponse};
use script::layout_interface::{HitTestResponse, LayoutRPC, OffsetParentResponse};
use script::layout_interface::{ContentBoxResponse, NodeOverflowResponse, ContentBoxesResponse, NodeGeometryResponse};
use script::layout_interface::{HitTestResponse, LayoutRPC, OffsetParentResponse, NodeLayerIdResponse};
use script::layout_interface::{ResolvedStyleResponse, ScriptLayoutChan, MarginStyleResponse};
use script_traits::LayoutMsg as ConstellationMsg;
use script_traits::UntrustedNodeAddress;
@ -112,12 +113,23 @@ impl LayoutRPC for LayoutRPCImpl {
}
}
fn node_overflow(&self) -> NodeOverflowResponse {
NodeOverflowResponse(self.0.lock().unwrap().overflow_response.0)
}
fn node_scroll_area(&self) -> NodeGeometryResponse {
NodeGeometryResponse {
client_rect: self.0.lock().unwrap().scroll_area_response
}
}
fn node_layer_id(&self) -> NodeLayerIdResponse {
NodeLayerIdResponse {
layer_id: self.0.lock().unwrap().layer_id_response
.expect("layer_id is not correctly fetched, see PR #9968")
}
}
/// Retrieves the resolved value for a CSS style property.
fn resolved_style(&self) -> ResolvedStyleResponse {
let &LayoutRPCImpl(ref rw_data) = self;
@ -425,6 +437,7 @@ impl FragmentBorderBoxIterator for UnioningFragmentScrollAreaIterator {
let bottom_padding = (border_box.size.height - bottom_border - top_border).to_px();
let top_padding = top_border.to_px();
let left_padding = left_border.to_px();
match self.level {
Some(start_level) if level <= start_level => { self.is_child = false; }
Some(_) => {
@ -517,6 +530,11 @@ pub fn process_node_geometry_request<N: LayoutNode>(requested_node: N, layout_ro
iterator.client_rect
}
pub fn process_node_layer_id_request<N: LayoutNode>(requested_node: N) -> LayerId {
let layout_node = requested_node.to_threadsafe();
layout_node.layer_id()
}
pub fn process_node_scroll_area_request< N: LayoutNode>(requested_node: N, layout_root: &mut FlowRef)
-> Rect<i32> {
let mut iterator = UnioningFragmentScrollAreaIterator::new(requested_node.opaque());
@ -691,6 +709,14 @@ pub fn process_offset_parent_query<N: LayoutNode>(requested_node: N, layout_root
}
}
pub fn process_node_overflow_request<N: LayoutNode>(requested_node: N) -> NodeOverflowResponse {
let layout_node = requested_node.to_threadsafe();
let style = &*layout_node.style();
let style_box = style.get_box();
NodeOverflowResponse(Some((Point2D::new(style_box.overflow_x, style_box.overflow_y.0))))
}
pub fn process_margin_style_query<N: LayoutNode>(requested_node: N)
-> MarginStyleResponse {
let layout_node = requested_node.to_threadsafe();

View file

@ -34,6 +34,7 @@ use core::nonzero::NonZero;
use data::{LayoutDataFlags, PrivateLayoutData};
use gfx::display_list::OpaqueNode;
use gfx::text::glyph::CharIndex;
use gfx_traits::{LayerId, LayerType};
use incremental::RestyleDamage;
use msg::constellation_msg::PipelineId;
use opaque_node::OpaqueNodeMethods;
@ -851,6 +852,21 @@ pub trait ThreadSafeLayoutNode : Clone + Copy + Sized + PartialEq {
fn iframe_pipeline_id(&self) -> PipelineId;
fn get_colspan(&self) -> u32;
fn layer_id(&self) -> LayerId {
let layer_type = match self.get_pseudo_element_type() {
PseudoElementType::Normal => LayerType::FragmentBody,
PseudoElementType::Before(_) => LayerType::BeforePseudoContent,
PseudoElementType::After(_) => LayerType::AfterPseudoContent,
PseudoElementType::DetailsSummary(_) => LayerType::FragmentBody,
PseudoElementType::DetailsContent(_) => LayerType::FragmentBody,
};
LayerId::new_of_type(layer_type, self.opaque().id() as usize)
}
fn layer_id_for_overflow_scroll(&self) -> LayerId {
LayerId::new_of_type(LayerType::OverflowScroll, self.opaque().id() as usize)
}
}
// This trait is only public so that it can be implemented by the gecko wrapper.

View file

@ -19,6 +19,8 @@ use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
use dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions};
use dom::bindings::codegen::UnionTypes::NodeOrString;
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::global::GlobalRef;
@ -39,6 +41,7 @@ use dom::htmlanchorelement::HTMLAnchorElement;
use dom::htmlbodyelement::{HTMLBodyElement, HTMLBodyElementLayoutHelpers};
use dom::htmlbuttonelement::HTMLButtonElement;
use dom::htmlcollection::HTMLCollection;
use dom::htmlelement::HTMLElement;
use dom::htmlfieldsetelement::HTMLFieldSetElement;
use dom::htmlfontelement::{HTMLFontElement, HTMLFontElementLayoutHelpers};
use dom::htmlhrelement::{HTMLHRElement, HTMLHRLayoutHelpers};
@ -86,7 +89,7 @@ use string_cache::{Atom, Namespace, QualName};
use style::element_state::*;
use style::error_reporting::ParseErrorReporter;
use style::properties::DeclaredValue;
use style::properties::longhands::{self, background_image, border_spacing, font_family, font_size};
use style::properties::longhands::{self, background_image, border_spacing, font_family, overflow_x, font_size};
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, parse_style_attribute};
use style::selector_impl::{NonTSPseudoClass, ServoSelectorImpl};
use style::values::CSSFloat;
@ -164,6 +167,35 @@ impl Element {
document,
ElementBinding::Wrap)
}
// https://drafts.csswg.org/cssom-view/#css-layout-box
// Elements that have a computed value of the display property
// that is table-column or table-column-group
// FIXME: Currently, it is assumed to be true always
fn has_css_layout_box(&self) -> bool {
true
}
// https://drafts.csswg.org/cssom-view/#potentially-scrollable
fn potentially_scrollable(&self) -> bool {
self.has_css_layout_box() &&
!self.overflow_x_is_visible() &&
!self.overflow_y_is_visible()
}
// used value of overflow-x is "visible"
fn overflow_x_is_visible(&self) -> bool {
let window = window_from_node(self);
let overflow_pair = window.overflow_query(self.upcast::<Node>().to_trusted_node_address());
overflow_pair.x == overflow_x::computed_value::T::visible
}
// used value of overflow-y is "visible"
fn overflow_y_is_visible(&self) -> bool {
let window = window_from_node(self);
let overflow_pair = window.overflow_query(self.upcast::<Node>().to_trusted_node_address());
overflow_pair.y != overflow_x::computed_value::T::visible
}
}
#[allow(unsafe_code)]
@ -1196,6 +1228,49 @@ impl Element {
_ => Err(Error::Syntax)
}
}
// https://drafts.csswg.org/cssom-view/#dom-element-scroll
pub fn scroll(&self, x_: f64, y_: f64, behavior: ScrollBehavior) {
// Step 1.2 or 2.3
let x = if x_.is_finite() { x_ } else { 0.0f64 };
let y = if y_.is_finite() { y_ } else { 0.0f64 };
let node = self.upcast::<Node>();
// Step 3
let doc = node.owner_doc();
// Step 4
if !doc.is_fully_active() {
return;
}
// Step 5
let win = doc.DefaultView();
// Step 7
if *self.root_element() == *self {
if doc.quirks_mode() != Quirks {
win.scroll(x, y, behavior);
}
return;
}
// Step 9
if doc.GetBody().r() == self.downcast::<HTMLElement>() &&
doc.quirks_mode() == Quirks &&
!self.potentially_scrollable() {
win.scroll(x, y, behavior);
return;
}
// Step 10 (TODO)
// Step 11
win.scroll_node(node.to_trusted_node_address(), x, y, behavior);
}
}
impl ElementMethods for Element {
@ -1452,6 +1527,220 @@ impl ElementMethods for Element {
rect.size.height.to_f64_px())
}
// https://drafts.csswg.org/cssom-view/#dom-element-scroll
fn Scroll(&self, options: &ScrollToOptions) {
// Step 1
let left = options.left.unwrap_or(self.ScrollLeft());
let top = options.top.unwrap_or(self.ScrollTop());
self.scroll(left, top, options.parent.behavior);
}
// https://drafts.csswg.org/cssom-view/#dom-element-scroll
fn Scroll_(&self, x: f64, y: f64) {
self.scroll(x, y, ScrollBehavior::Auto);
}
// https://drafts.csswg.org/cssom-view/#dom-element-scrollto
fn ScrollTo(&self, options: &ScrollToOptions) {
self.Scroll(options);
}
// https://drafts.csswg.org/cssom-view/#dom-element-scrollto
fn ScrollTo_(&self, x: f64, y: f64) {
self.Scroll_(x, y);
}
// https://drafts.csswg.org/cssom-view/#dom-element-scrollby
fn ScrollBy(&self, options: &ScrollToOptions) {
// Step 2
let delta_left = options.left.unwrap_or(0.0f64);
let delta_top = options.top.unwrap_or(0.0f64);
let left = self.ScrollLeft();
let top = self.ScrollTop();
self.scroll(left + delta_left, top + delta_top,
options.parent.behavior);
}
// https://drafts.csswg.org/cssom-view/#dom-element-scrollby
fn ScrollBy_(&self, x: f64, y: f64) {
let left = self.ScrollLeft();
let top = self.ScrollTop();
self.scroll(left + x, top + y, ScrollBehavior::Auto);
}
// https://drafts.csswg.org/cssom-view/#dom-element-scrolltop
fn ScrollTop(&self) -> f64 {
let node = self.upcast::<Node>();
// Step 1
let doc = node.owner_doc();
// Step 2
if !doc.is_fully_active() {
return 0.0;
}
// Step 3
let win = doc.DefaultView();
// Step 5
if *self.root_element() == *self {
if doc.quirks_mode() == Quirks {
return 0.0;
}
// Step 6
return win.ScrollY() as f64;
}
// Step 7
if doc.GetBody().r() == self.downcast::<HTMLElement>() &&
doc.quirks_mode() == Quirks &&
!self.potentially_scrollable() {
return win.ScrollY() as f64;
}
// Step 8
if !self.has_css_layout_box() {
return 0.0;
}
// Step 9
let point = node.scroll_offset();
return point.y.abs() as f64;
}
// https://drafts.csswg.org/cssom-view/#dom-element-scrolltop
fn SetScrollTop(&self, y_: f64) {
let behavior = ScrollBehavior::Auto;
// Step 1, 2
let y = if y_.is_finite() { y_ } else { 0.0f64 };
let node = self.upcast::<Node>();
// Step 3
let doc = node.owner_doc();
// Step 4
if !doc.is_fully_active() {
return;
}
// Step 5
let win = doc.DefaultView();
// Step 7
if *self.root_element() == *self {
if doc.quirks_mode() != Quirks {
win.scroll(win.ScrollX() as f64, y, behavior);
}
return;
}
// Step 9
if doc.GetBody().r() == self.downcast::<HTMLElement>() &&
doc.quirks_mode() == Quirks &&
!self.potentially_scrollable() {
win.scroll(win.ScrollX() as f64, y, behavior);
return;
}
// Step 10 (TODO)
// Step 11
win.scroll_node(node.to_trusted_node_address(), self.ScrollLeft(), y, behavior);
}
// https://drafts.csswg.org/cssom-view/#dom-element-scrolltop
fn ScrollLeft(&self) -> f64 {
let node = self.upcast::<Node>();
// Step 1
let doc = node.owner_doc();
// Step 2
if !doc.is_fully_active() {
return 0.0;
}
// Step 3
let win = doc.DefaultView();
// Step 5
if *self.root_element() == *self {
if doc.quirks_mode() != Quirks {
// Step 6
return win.ScrollX() as f64;
}
return 0.0;
}
// Step 7
if doc.GetBody().r() == self.downcast::<HTMLElement>() &&
doc.quirks_mode() == Quirks &&
!self.potentially_scrollable() {
return win.ScrollX() as f64;
}
// Step 8
if !self.has_css_layout_box() {
return 0.0;
}
// Step 9
let point = node.scroll_offset();
return point.x.abs() as f64;
}
// https://drafts.csswg.org/cssom-view/#dom-element-scrollleft
fn SetScrollLeft(&self, x_: f64) {
let behavior = ScrollBehavior::Auto;
// Step 1, 2
let x = if x_.is_finite() { x_ } else { 0.0f64 };
let node = self.upcast::<Node>();
// Step 3
let doc = node.owner_doc();
// Step 4
if !doc.is_fully_active() {
return;
}
// Step 5
let win = doc.DefaultView();
// Step 7
if *self.root_element() == *self {
if doc.quirks_mode() == Quirks {
return;
}
win.scroll(x, win.ScrollY() as f64, behavior);
return;
}
// Step 9
if doc.GetBody().r() == self.downcast::<HTMLElement>() &&
doc.quirks_mode() == Quirks &&
!self.potentially_scrollable() {
win.scroll(x, win.ScrollY() as f64, behavior);
return;
}
// Step 10 (TODO)
// Step 11
win.scroll_node(node.to_trusted_node_address(), x, self.ScrollTop(), behavior);
}
// https://drafts.csswg.org/cssom-view/#dom-element-scrollwidth
fn ScrollWidth(&self) -> i32 {
self.upcast::<Node>().scroll_area().size.width

View file

@ -614,6 +614,12 @@ impl Node {
}
}
pub fn scroll_offset(&self) -> Point2D<f32> {
let document = self.owner_doc();
let window = document.window();
window.scroll_offset_query(self.to_trusted_node_address())
}
// https://dom.spec.whatwg.org/#dom-childnode-before
pub fn before(&self, nodes: Vec<NodeOrString>) -> ErrorResult {
// Step 1.

View file

@ -81,6 +81,15 @@ partial interface Element {
DOMRectList getClientRects();
DOMRect getBoundingClientRect();
void scroll(optional ScrollToOptions options);
void scroll(unrestricted double x, unrestricted double y);
void scrollTo(optional ScrollToOptions options);
void scrollTo(unrestricted double x, unrestricted double y);
void scrollBy(optional ScrollToOptions options);
void scrollBy(unrestricted double x, unrestricted double y);
attribute unrestricted double scrollTop;
attribute unrestricted double scrollLeft;
readonly attribute long scrollWidth;
readonly attribute long scrollHeight;

View file

@ -74,6 +74,7 @@ use std::sync::{Arc, Mutex};
use string_cache::Atom;
use style::context::ReflowGoal;
use style::error_reporting::ParseErrorReporter;
use style::properties::longhands::{overflow_x};
use style::selector_impl::PseudoElement;
use task_source::TaskSource;
use task_source::dom_manipulation::{DOMManipulationTaskSource, DOMManipulationTask};
@ -911,11 +912,13 @@ impl Window {
//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),
LayerId::null(), 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, layer_id: LayerId,
behavior: ScrollBehavior, element: Option<&Element>) {
//TODO Step 1
let point = Point2D::new(x, y);
let smooth = match behavior {
@ -934,7 +937,7 @@ impl Window {
self.current_viewport.set(Rect::new(Point2D::new(Au::from_f32_px(x), Au::from_f32_px(y)), size));
self.compositor.send(ScriptToCompositorMsg::ScrollFragmentPoint(
self.pipeline(), LayerId::null(), point, smooth)).unwrap()
self.pipeline(), layer_id, point, smooth)).unwrap()
}
pub fn client_window(&self) -> (Size2D<u32>, Point2D<i32>) {
@ -1122,6 +1125,40 @@ impl Window {
self.layout_rpc.node_scroll_area().client_rect
}
pub fn overflow_query(&self, node: TrustedNodeAddress) -> Point2D<overflow_x::computed_value::T> {
self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::NodeOverflowQuery(node),
ReflowReason::Query);
self.layout_rpc.node_overflow().0.unwrap()
}
pub fn scroll_offset_query(&self, node: TrustedNodeAddress) -> Point2D<f32> {
self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::NodeLayerIdQuery(node),
ReflowReason::Query);
let layer_id = self.layout_rpc.node_layer_id().layer_id;
let pipeline_id = self.id;
let (send, recv) = ipc::channel::<Point2D<f32>>().unwrap();
self.compositor.send(ScriptToCompositorMsg::GetScrollOffset(pipeline_id, layer_id, send)).unwrap();
recv.recv().unwrap_or(Point2D::zero())
}
// https://drafts.csswg.org/cssom-view/#dom-element-scroll
pub fn scroll_node(&self, node: TrustedNodeAddress,
x_: f64, y_: f64, behavior: ScrollBehavior) {
self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::NodeLayerIdQuery(node),
ReflowReason::Query);
let layer_id = self.layout_rpc.node_layer_id().layer_id;
// Step 12
self.perform_a_scroll(x_.to_f32().unwrap_or(0.0f32), y_.to_f32().unwrap_or(0.0f32),
layer_id, behavior, None);
}
pub fn resolved_style_query(&self,
element: TrustedNodeAddress,
pseudo: Option<PseudoElement>,
@ -1476,6 +1513,8 @@ fn debug_reflow_events(id: PipelineId, goal: &ReflowGoal, query_type: &ReflowQue
ReflowQueryType::ContentBoxesQuery(_n) => "\tContentBoxesQuery",
ReflowQueryType::HitTestQuery(_n, _o) => "\tHitTestQuery",
ReflowQueryType::NodeGeometryQuery(_n) => "\tNodeGeometryQuery",
ReflowQueryType::NodeLayerIdQuery(_n) => "\tNodeLayerIdQuery",
ReflowQueryType::NodeOverflowQuery(_n) => "\tNodeOverFlowQuery",
ReflowQueryType::NodeScrollGeometryQuery(_n) => "\tNodeScrollGeometryQuery",
ReflowQueryType::ResolvedStyleQuery(_, _, _) => "\tResolvedStyleQuery",
ReflowQueryType::OffsetParentQuery(_n) => "\tOffsetParentQuery",

View file

@ -23,7 +23,7 @@ use std::sync::Arc;
use std::sync::mpsc::{Receiver, Sender, channel};
use string_cache::Atom;
use style::context::ReflowGoal;
use style::properties::longhands::{margin_top, margin_right, margin_bottom, margin_left};
use style::properties::longhands::{margin_top, margin_right, margin_bottom, margin_left, overflow_x};
use style::selector_impl::PseudoElement;
use style::servo::Stylesheet;
use url::Url;
@ -104,8 +104,12 @@ pub trait LayoutRPC {
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 layer id of this node. Used by APIs such as `scrollTop`
fn node_layer_id(&self) -> NodeLayerIdResponse;
/// Requests the node containing the point of interest
fn hit_test(&self) -> HitTestResponse;
/// Query layout for the resolved value of a given CSS property
@ -136,6 +140,8 @@ impl MarginStyleResponse {
}
}
pub struct NodeOverflowResponse(pub Option<Point2D<overflow_x::computed_value::T>>);
pub struct ContentBoxResponse(pub Rect<Au>);
pub struct ContentBoxesResponse(pub Vec<Rect<Au>>);
pub struct HitTestResponse {
@ -144,6 +150,11 @@ pub struct HitTestResponse {
pub struct NodeGeometryResponse {
pub client_rect: Rect<i32>,
}
pub struct NodeLayerIdResponse {
pub layer_id: LayerId,
}
pub struct ResolvedStyleResponse(pub Option<String>);
#[derive(Clone)]
@ -167,8 +178,10 @@ pub enum ReflowQueryType {
NoQuery,
ContentBoxQuery(TrustedNodeAddress),
ContentBoxesQuery(TrustedNodeAddress),
NodeOverflowQuery(TrustedNodeAddress),
HitTestQuery(Point2D<f32>, bool),
NodeGeometryQuery(TrustedNodeAddress),
NodeLayerIdQuery(TrustedNodeAddress),
NodeScrollGeometryQuery(TrustedNodeAddress),
ResolvedStyleQuery(TrustedNodeAddress, Option<PseudoElement>, Atom),
OffsetParentQuery(TrustedNodeAddress),

View file

@ -376,6 +376,8 @@ pub enum ScriptToCompositorMsg {
ResizeTo(Size2D<u32>),
/// Script has handled a touch event, and either prevented or allowed default actions.
TouchEventProcessed(EventResult),
/// Get Scroll Offset
GetScrollOffset(PipelineId, LayerId, IpcSender<Point2D<f32>>),
/// Requests that the compositor shut down.
Exit,
/// Allow the compositor to free script-specific resources.

View file

@ -17,8 +17,14 @@ nav {
top: 16px;
left: 16px;
}
div {
width: 400px;
}
</style>
<nav></nav><section>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc ultricies tortor eu augue eleifend malesuada. Duis id condimentum urna. Duis vulputate urna a dignissim sodales. Aenean et magna id dui rutrum suscipit. Etiam metus mauris, congue ac suscipit dapibus, mattis non neque. Donec porttitor eros sed mauris tristique, non condimentum augue feugiat. Suspendisse iaculis faucibus nunc at porttitor. Integer convallis enim in feugiat molestie. Ut eget tincidunt mi, vel malesuada lectus. Quisque fermentum neque a sapien interdum consectetur. Nam tincidunt leo sit amet tortor ornare, sit amet ultrices ante semper. Fusce malesuada mi vitae venenatis sagittis. Duis eget urna quam.
<nav></nav><section id="section">
<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc ultricies tortor eu augue eleifend malesuada. Duis id condimentum urna. Duis vulputate urna a dignissim sodales. Aenean et magna id dui rutrum suscipit. Etiam metus mauris, congue ac suscipit dapibus, mattis non neque. Donec porttitor eros sed mauris tristique, non condimentum augue feugiat. Suspendisse iaculis faucibus nunc at porttitor. Integer convallis enim in feugiat molestie. Ut eget tincidunt mi, vel malesuada lectus. Quisque fermentum neque a sapien interdum consectetur. Nam tincidunt leo sit amet tortor ornare, sit amet ultrices ante semper. Fusce malesuada mi vitae venenatis sagittis. Duis eget urna quam.
Sed lacinia aliquam tortor quis elementum. Cras vitae mauris erat. Vestibulum posuere justo et dolor condimentum feugiat. Sed at magna nunc. Suspendisse est nunc, ultrices sed enim lobortis, vulputate rutrum mauris. Fusce ultrices eget erat blandit porta. Sed eros nulla, tristique eget porta a, viverra vel velit. Praesent sit amet odio eleifend, tempor arcu ut, elementum tellus. Suspendisse lorem tortor, sodales eget nulla a, rhoncus lobortis magna. Phasellus purus ante, rhoncus a ipsum nec, condimentum lacinia purus. Cras lobortis posuere nisi, vitae dapibus ante feugiat et. Quisque ornare nisi quis erat congue viverra. Vestibulum a nunc odio.
@ -27,4 +33,17 @@ Sed id venenatis tortor. Curabitur sit amet mauris eget mi semper rutrum vel et
Sed sed ante aliquam, rutrum nisl quis, fermentum tellus. Proin ac leo molestie, euismod mauris sed, consequat nunc. Vivamus ut leo a nunc pharetra accumsan a non lorem. Aliquam iaculis mattis augue, in eleifend est accumsan vel. Pellentesque efficitur pulvinar leo vel ornare. Pellentesque non fermentum enim, ut efficitur elit. Duis risus quam, congue vel nulla a, blandit egestas erat. Suspendisse at sodales dolor. Vivamus auctor, lorem et ultrices venenatis, erat ex mollis nisi, quis maximus libero quam a libero.
Curabitur elit lacus, bibendum non tempus a, bibendum sit amet ante. Mauris eget nibh quis leo rhoncus consequat. Integer iaculis sed sapien eu pellentesque. In aliquet elementum lorem, ut consequat elit ultrices id. Phasellus vestibulum ex ex, ac sagittis tortor convallis et. Curabitur placerat id lectus at aliquam. Morbi sed nisl sem. Nam sit amet arcu maximus, volutpat nisl ac, dignissim neque. Etiam nec efficitur libero. Quisque tristique pulvinar est, eget dictum ex vehicula non. Nam dignissim non felis a iaculis. Nullam vel dolor vitae libero aliquet congue. Donec mi eros, semper non lectus at, commodo ullamcorper ligula. Donec commodo, sem vel lacinia porttitor, elit orci maximus felis, eget eleifend est velit id lorem.
</div>
<script>
var section = document.getElementById("section");
window.setTimeout(function () {
section.scroll(0, 1000);
}, 2000)
window.setInterval(function () {
console.log("scrollTop: " + section.scrollTop);
console.log("scrollLeft: " + section.scrollLeft);
}, 1000);
</script>

View file

@ -13779,6 +13779,10 @@
"path": "cssom-view/elementFromPoint.html",
"url": "/cssom-view/elementFromPoint.html"
},
{
"path": "cssom-view/elementScroll.html",
"url": "/cssom-view/elementScroll.html"
},
{
"path": "cssom-view/elementsFromPoint.html",
"url": "/cssom-view/elementsFromPoint.html"

View file

@ -0,0 +1,4 @@
[elementScroll.html]
type: testharness
[Element scroll maximum test]
expected: FAIL

View file

@ -0,0 +1,89 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>cssom-view - elementScroll</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style>
section {
width: 300px;
height: 300px;
/*position: absolute;*/
top: 16px;
left: 16px;
border: inset gray 3px;
overflow: scroll;
background: white;
}
div {
width: 400px;
}
</style>
<section id="section">
<div>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc ultricies tortor eu augue eleifend malesuada. Duis id condimentum urna. Duis vulputate urna a dignissim sodales. Aenean et magna id dui rutrum suscipit. Etiam metus mauris, congue ac suscipit dapibus, mattis non neque. Donec porttitor eros sed mauris tristique, non condimentum augue feugiat. Suspendisse iaculis faucibus nunc at porttitor. Integer convallis enim in feugiat molestie. Ut eget tincidunt mi, vel malesuada lectus. Quisque fermentum neque a sapien interdum consectetur. Nam tincidunt leo sit amet tortor ornare, sit amet ultrices ante semper. Fusce malesuada mi vitae venenatis sagittis. Duis eget urna quam.
Sed lacinia aliquam tortor quis elementum. Cras vitae mauris erat. Vestibulum posuere justo et dolor condimentum feugiat. Sed at magna nunc. Suspendisse est nunc, ultrices sed enim lobortis, vulputate rutrum mauris. Fusce ultrices eget erat blandit porta. Sed eros nulla, tristique eget porta a, viverra vel velit. Praesent sit amet odio eleifend, tempor arcu ut, elementum tellus. Suspendisse lorem tortor, sodales eget nulla a, rhoncus lobortis magna. Phasellus purus ante, rhoncus a ipsum nec, condimentum lacinia purus. Cras lobortis posuere nisi, vitae dapibus ante feugiat et. Quisque ornare nisi quis erat congue viverra. Vestibulum a nunc odio.
Sed id venenatis tortor. Curabitur sit amet mauris eget mi semper rutrum vel et odio. Phasellus eu sapien in sem ultricies pretium eu sit amet magna. Nulla finibus nec lorem ac semper. Nulla eleifend eros id fringilla pellentesque. Proin eleifend, sem vel lobortis viverra, massa augue viverra felis, quis ultricies sapien ipsum at magna. Duis rutrum tempus lobortis. Aliquam quis nulla eget velit viverra pretium. Maecenas venenatis nec nisl at pulvinar. Duis in sodales lectus, ac porta augue.
Sed sed ante aliquam, rutrum nisl quis, fermentum tellus. Proin ac leo molestie, euismod mauris sed, consequat nunc. Vivamus ut leo a nunc pharetra accumsan a non lorem. Aliquam iaculis mattis augue, in eleifend est accumsan vel. Pellentesque efficitur pulvinar leo vel ornare. Pellentesque non fermentum enim, ut efficitur elit. Duis risus quam, congue vel nulla a, blandit egestas erat. Suspendisse at sodales dolor. Vivamus auctor, lorem et ultrices venenatis, erat ex mollis nisi, quis maximus libero quam a libero.
Curabitur elit lacus, bibendum non tempus a, bibendum sit amet ante. Mauris eget nibh quis leo rhoncus consequat. Integer iaculis sed sapien eu pellentesque. In aliquet elementum lorem, ut consequat elit ultrices id. Phasellus vestibulum ex ex, ac sagittis tortor convallis et. Curabitur placerat id lectus at aliquam. Morbi sed nisl sem. Nam sit amet arcu maximus, volutpat nisl ac, dignissim neque. Etiam nec efficitur libero. Quisque tristique pulvinar est, eget dictum ex vehicula non. Nam dignissim non felis a iaculis. Nullam vel dolor vitae libero aliquet congue. Donec mi eros, semper non lectus at, commodo ullamcorper ligula. Donec commodo, sem vel lacinia porttitor, elit orci maximus felis, eget eleifend est velit id lorem.
</div>
</section>
<script>
setup({explicit_done:true});
window.onload = function () {
var section = document.getElementById("section");
test(function () {
assert_equals(section.scrollTop, 0, "initial scrollTop should be 0");
assert_equals(section.scrollLeft, 0, "initial scrollLeft should be 0");
section.scrollTop = 30;
section.scrollLeft = 40;
assert_equals(section.scrollTop, 30, "changed scrollTop should be 40");
assert_equals(section.scrollLeft, 40, "changed scrollLeft should be 40");
}, "Element scrollTop/Left getter/setter test");
test(function () {
section.scroll(50, 60);
assert_equals(section.scrollLeft, 50, "changed scrollLeft should be 60");
assert_equals(section.scrollTop, 60, "changed scrollTop should be 50");
}, "Element scroll test");
test(function () {
section.scrollTo(80, 70);
assert_equals(section.scrollLeft, 80, "changed scrollLeft should be 70");
assert_equals(section.scrollTop, 70, "changed scrollTop should be 80");
}, "Element scrollTo test");
test(function () {
var left = section.scrollLeft;
var top = section.scrollTop;
section.scrollBy(10, 20);
assert_equals(section.scrollLeft, left + 10, "increment of scrollLeft should be 10")
assert_equals(section.scrollTop, top + 20, "increment of scrollTop should be 20")
})
test(function () {
section.scrollTop = 1000;
section.scrollLeft = 1000;
assert_equals(section.scrollTop, 636, "changed scrollTop should be 636");
assert_equals(section.scrollLeft, 100, "changed scrollLeft should be 100");
}, "Element scroll maximum test");
done();
};
</script>