mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
script: When using WebRender, keep the DOM-side scroll positions for
elements with `overflow: scroll` up to date, and take them into account when doing hit testing. Closes #11648.
This commit is contained in:
parent
ce88b8ed30
commit
041cfe6d0a
14 changed files with 259 additions and 47 deletions
|
@ -43,6 +43,7 @@ use dom::bindings::utils::WindowProxyHandler;
|
|||
use encoding::types::EncodingRef;
|
||||
use euclid::length::Length as EuclidLength;
|
||||
use euclid::matrix2d::Matrix2D;
|
||||
use euclid::point::Point2D;
|
||||
use euclid::rect::Rect;
|
||||
use euclid::size::Size2D;
|
||||
use html5ever::tree_builder::QuirksMode;
|
||||
|
@ -279,6 +280,7 @@ no_jsmanaged_fields!(usize, u8, u16, u32, u64);
|
|||
no_jsmanaged_fields!(isize, i8, i16, i32, i64);
|
||||
no_jsmanaged_fields!(Sender<T>);
|
||||
no_jsmanaged_fields!(Receiver<T>);
|
||||
no_jsmanaged_fields!(Point2D<T>);
|
||||
no_jsmanaged_fields!(Rect<T>);
|
||||
no_jsmanaged_fields!(Size2D<T>);
|
||||
no_jsmanaged_fields!(Arc<T>);
|
||||
|
|
|
@ -298,6 +298,10 @@ impl Node {
|
|||
self.owner_doc().content_and_heritage_changed(self, NodeDamage::OtherNodeDamage);
|
||||
child.owner_doc().content_and_heritage_changed(child, NodeDamage::OtherNodeDamage);
|
||||
}
|
||||
|
||||
pub fn to_untrusted_node_address(&self) -> UntrustedNodeAddress {
|
||||
UntrustedNodeAddress(self.reflector().get_jsobject().get() as *const c_void)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct QuerySelectorIterator {
|
||||
|
@ -622,7 +626,7 @@ 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())
|
||||
window.scroll_offset_query(self)
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-childnode-before
|
||||
|
|
|
@ -84,14 +84,22 @@ partial interface Element {
|
|||
DOMRectList getClientRects();
|
||||
DOMRect getBoundingClientRect();
|
||||
|
||||
[Func="::script_can_initiate_scroll"]
|
||||
void scroll(optional ScrollToOptions options);
|
||||
[Func="::script_can_initiate_scroll"]
|
||||
void scroll(unrestricted double x, unrestricted double y);
|
||||
|
||||
[Func="::script_can_initiate_scroll"]
|
||||
void scrollTo(optional ScrollToOptions options);
|
||||
[Func="::script_can_initiate_scroll"]
|
||||
void scrollTo(unrestricted double x, unrestricted double y);
|
||||
[Func="::script_can_initiate_scroll"]
|
||||
void scrollBy(optional ScrollToOptions options);
|
||||
[Func="::script_can_initiate_scroll"]
|
||||
void scrollBy(unrestricted double x, unrestricted double y);
|
||||
[Func="::script_can_initiate_scroll"]
|
||||
attribute unrestricted double scrollTop;
|
||||
[Func="::script_can_initiate_scroll"]
|
||||
attribute unrestricted double scrollLeft;
|
||||
readonly attribute long scrollWidth;
|
||||
readonly attribute long scrollHeight;
|
||||
|
|
|
@ -136,11 +136,17 @@ partial interface Window {
|
|||
readonly attribute long pageXOffset;
|
||||
readonly attribute long scrollY;
|
||||
readonly attribute long pageYOffset;
|
||||
[Func="::script_can_initiate_scroll"]
|
||||
void scroll(optional ScrollToOptions options);
|
||||
[Func="::script_can_initiate_scroll"]
|
||||
void scroll(unrestricted double x, unrestricted double y);
|
||||
[Func="::script_can_initiate_scroll"]
|
||||
void scrollTo(optional ScrollToOptions options);
|
||||
[Func="::script_can_initiate_scroll"]
|
||||
void scrollTo(unrestricted double x, unrestricted double y);
|
||||
[Func="::script_can_initiate_scroll"]
|
||||
void scrollBy(optional ScrollToOptions options);
|
||||
[Func="::script_can_initiate_scroll"]
|
||||
void scrollBy(unrestricted double x, unrestricted double y);
|
||||
|
||||
// client
|
||||
|
|
|
@ -12,6 +12,7 @@ use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
|
|||
use dom::bindings::codegen::Bindings::EventHandlerBinding::OnBeforeUnloadEventHandlerNonNull;
|
||||
use dom::bindings::codegen::Bindings::EventHandlerBinding::OnErrorEventHandlerNonNull;
|
||||
use dom::bindings::codegen::Bindings::FunctionBinding::Function;
|
||||
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
|
||||
use dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions};
|
||||
use dom::bindings::codegen::Bindings::WindowBinding::{self, FrameRequestCallback, WindowMethods};
|
||||
use dom::bindings::error::{Error, ErrorResult, Fallible, report_pending_exception};
|
||||
|
@ -68,7 +69,7 @@ use script_traits::{ScriptMsg as ConstellationMsg, TimerEventRequest, TimerSourc
|
|||
use std::ascii::AsciiExt;
|
||||
use std::borrow::ToOwned;
|
||||
use std::cell::Cell;
|
||||
use std::collections::HashSet;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::default::Default;
|
||||
use std::ffi::CString;
|
||||
use std::io::{Write, stderr, stdout};
|
||||
|
@ -264,6 +265,9 @@ pub struct Window {
|
|||
|
||||
error_reporter: CSSErrorReporter,
|
||||
|
||||
/// A list of scroll offsets for each scrollable element.
|
||||
scroll_offsets: DOMRefCell<HashMap<UntrustedNodeAddress, Point2D<f32>>>,
|
||||
|
||||
#[ignore_heap_size_of = "Defined in ipc-channel"]
|
||||
panic_chan: IpcSender<PanicMsg>,
|
||||
}
|
||||
|
@ -354,6 +358,13 @@ impl Window {
|
|||
pub fn css_error_reporter(&self) -> Box<ParseErrorReporter + Send> {
|
||||
self.error_reporter.clone()
|
||||
}
|
||||
|
||||
/// Sets a new list of scroll offsets.
|
||||
///
|
||||
/// This is called when layout gives us new ones and WebRender is in use.
|
||||
pub fn set_scroll_offsets(&self, offsets: HashMap<UntrustedNodeAddress, Point2D<f32>>) {
|
||||
*self.scroll_offsets.borrow_mut() = offsets
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
||||
|
@ -1243,7 +1254,28 @@ impl Window {
|
|||
self.layout_rpc.node_overflow().0.unwrap()
|
||||
}
|
||||
|
||||
pub fn scroll_offset_query(&self, node: TrustedNodeAddress) -> Point2D<f32> {
|
||||
pub fn scroll_offset_query(&self, node: &Node) -> Point2D<f32> {
|
||||
// WebRender always keeps the scroll offsets up to date and stored here in the window. So,
|
||||
// if WR is in use, all we need to do is to check our list of scroll offsets and return the
|
||||
// result.
|
||||
if opts::get().use_webrender {
|
||||
let mut node = Root::from_ref(node);
|
||||
loop {
|
||||
if let Some(scroll_offset) = self.scroll_offsets
|
||||
.borrow()
|
||||
.get(&node.to_untrusted_node_address()) {
|
||||
return *scroll_offset
|
||||
}
|
||||
node = match node.GetParentNode() {
|
||||
Some(node) => node,
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
let offset = self.current_viewport.get().origin;
|
||||
return Point2D::new(offset.x.to_f32_px(), offset.y.to_f32_px())
|
||||
}
|
||||
|
||||
let node = node.to_trusted_node_address();
|
||||
if !self.reflow(ReflowGoal::ForScriptQuery,
|
||||
ReflowQueryType::NodeLayerIdQuery(node),
|
||||
ReflowReason::Query) {
|
||||
|
@ -1642,6 +1674,7 @@ impl Window {
|
|||
webdriver_script_chan: DOMRefCell::new(None),
|
||||
ignore_further_async_events: Arc::new(AtomicBool::new(false)),
|
||||
error_reporter: error_reporter,
|
||||
scroll_offsets: DOMRefCell::new(HashMap::new()),
|
||||
panic_chan: panic_chan,
|
||||
};
|
||||
|
||||
|
|
|
@ -112,8 +112,9 @@ mod unpremultiplytable;
|
|||
mod webdriver_handlers;
|
||||
|
||||
use dom::bindings::codegen::RegisterBindings;
|
||||
use js::jsapi::SetDOMProxyInformation;
|
||||
use js::jsapi::{Handle, JSContext, JSObject, SetDOMProxyInformation};
|
||||
use std::ptr;
|
||||
use util::opts;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[allow(unsafe_code)]
|
||||
|
@ -168,3 +169,14 @@ pub fn init() {
|
|||
|
||||
perform_platform_specific_initialization();
|
||||
}
|
||||
|
||||
/// FIXME(pcwalton): Currently WebRender cannot handle DOM-initiated scrolls. Remove this when it
|
||||
/// can. See PR #11680 for details.
|
||||
///
|
||||
/// This function is only marked `unsafe` because the `[Func=foo]` WebIDL attribute requires it. It
|
||||
/// shouldn't actually do anything unsafe.
|
||||
#[allow(unsafe_code)]
|
||||
pub unsafe fn script_can_initiate_scroll(_: *mut JSContext, _: Handle<*mut JSObject>) -> bool {
|
||||
!opts::get().use_webrender
|
||||
}
|
||||
|
||||
|
|
|
@ -85,11 +85,12 @@ use script_traits::{CompositorEvent, ConstellationControlMsg, EventResult};
|
|||
use script_traits::{InitialScriptState, MouseButton, MouseEventType, MozBrowserEvent};
|
||||
use script_traits::{NewLayoutInfo, ScriptMsg as ConstellationMsg};
|
||||
use script_traits::{ScriptThreadFactory, TimerEvent, TimerEventRequest, TimerSource};
|
||||
use script_traits::{TouchEventType, TouchId};
|
||||
use script_traits::{TouchEventType, TouchId, UntrustedNodeAddress};
|
||||
use std::borrow::ToOwned;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::option::Option;
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
use std::result::Result;
|
||||
use std::sync::atomic::{Ordering, AtomicBool};
|
||||
|
@ -728,9 +729,9 @@ impl ScriptThread {
|
|||
self.handle_viewport(id, rect);
|
||||
})
|
||||
}
|
||||
FromConstellation(ConstellationControlMsg::SetScrollState(id, scroll_offset)) => {
|
||||
FromConstellation(ConstellationControlMsg::SetScrollState(id, scroll_state)) => {
|
||||
self.profile_event(ScriptThreadEventCategory::SetScrollState, || {
|
||||
self.handle_set_scroll_state(id, &scroll_offset);
|
||||
self.handle_set_scroll_state(id, &scroll_state);
|
||||
})
|
||||
}
|
||||
FromConstellation(ConstellationControlMsg::TickAllAnimations(
|
||||
|
@ -1110,17 +1111,31 @@ impl ScriptThread {
|
|||
panic!("Page rect message sent to nonexistent pipeline");
|
||||
}
|
||||
|
||||
fn handle_set_scroll_state(&self, id: PipelineId, scroll_state: &Point2D<f32>) {
|
||||
let context = self.browsing_context.get();
|
||||
if let Some(context) = context {
|
||||
if let Some(inner_context) = context.find(id) {
|
||||
let window = inner_context.active_window();
|
||||
window.update_viewport_for_scroll(-scroll_state.x, -scroll_state.y);
|
||||
return
|
||||
fn handle_set_scroll_state(&self,
|
||||
id: PipelineId,
|
||||
scroll_states: &[(UntrustedNodeAddress, Point2D<f32>)]) {
|
||||
let window = match self.browsing_context.get() {
|
||||
Some(context) => {
|
||||
match context.find(id) {
|
||||
Some(inner_context) => inner_context.active_window(),
|
||||
None => {
|
||||
panic!("Set scroll state message sent to nonexistent pipeline: {:?}", id)
|
||||
}
|
||||
}
|
||||
}
|
||||
None => panic!("Set scroll state message sent to nonexistent pipeline: {:?}", id),
|
||||
};
|
||||
|
||||
let mut scroll_offsets = HashMap::new();
|
||||
for &(node_address, ref scroll_offset) in scroll_states {
|
||||
if node_address == UntrustedNodeAddress(ptr::null()) {
|
||||
window.update_viewport_for_scroll(-scroll_offset.x, -scroll_offset.y);
|
||||
} else {
|
||||
scroll_offsets.insert(node_address,
|
||||
Point2D::new(-scroll_offset.x, -scroll_offset.y));
|
||||
}
|
||||
}
|
||||
|
||||
panic!("Set scroll state message message sent to nonexistent pipeline: {:?}", id);
|
||||
window.set_scroll_offsets(scroll_offsets)
|
||||
}
|
||||
|
||||
fn handle_new_layout(&self, new_layout_info: NewLayoutInfo) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue