mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
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:
commit
8d988f20c1
15 changed files with 569 additions and 12 deletions
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue