mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-01-30 18:31:08 +00:00
servo: Merge #11680 - 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 (from pcwalton:wr-overflow-scroll-hit-testing); r=jdm
Closes #11648. r? @jdm cc @paulrouget Source-Repo: https://github.com/servo/servo Source-Revision: 2d9338085561e7908c80ec7b2dd6d30125489aac
This commit is contained in:
parent
331b90272e
commit
2120477f7d
@ -487,14 +487,15 @@ impl DisplayList {
|
||||
/// Places all nodes containing the point of interest into `result`, topmost first. Respects
|
||||
/// the `pointer-events` CSS property If `topmost_only` is true, stops after placing one node
|
||||
/// into the list. `result` must be empty upon entry to this function.
|
||||
pub fn hit_test(&self, point: Point2D<Au>) -> Vec<DisplayItemMetadata> {
|
||||
pub fn hit_test(&self, point: &Point2D<Au>, scroll_offsets: &ScrollOffsetMap)
|
||||
-> Vec<DisplayItemMetadata> {
|
||||
let mut traversal = DisplayListTraversal {
|
||||
display_list: self,
|
||||
current_item_index: 0,
|
||||
last_item_index: self.list.len() - 1,
|
||||
};
|
||||
let mut result = Vec::new();
|
||||
self.root_stacking_context.hit_test(&mut traversal, point, &mut result);
|
||||
self.root_stacking_context.hit_test(&mut traversal, point, scroll_offsets, &mut result);
|
||||
result.reverse();
|
||||
result
|
||||
}
|
||||
@ -610,24 +611,38 @@ impl StackingContext {
|
||||
|
||||
pub fn hit_test<'a>(&self,
|
||||
traversal: &mut DisplayListTraversal<'a>,
|
||||
point: Point2D<Au>,
|
||||
point: &Point2D<Au>,
|
||||
scroll_offsets: &ScrollOffsetMap,
|
||||
result: &mut Vec<DisplayItemMetadata>) {
|
||||
// Convert the point into stacking context local space
|
||||
let point = if self.context_type == StackingContextType::Real {
|
||||
let point = point - self.bounds.origin;
|
||||
// Convert the point into stacking context local transform space.
|
||||
let mut point = if self.context_type == StackingContextType::Real {
|
||||
let point = *point - self.bounds.origin;
|
||||
let inv_transform = self.transform.invert();
|
||||
let frac_point = inv_transform.transform_point(&Point2D::new(point.x.to_f32_px(),
|
||||
point.y.to_f32_px()));
|
||||
Point2D::new(Au::from_f32_px(frac_point.x), Au::from_f32_px(frac_point.y))
|
||||
} else {
|
||||
point
|
||||
*point
|
||||
};
|
||||
|
||||
// Adjust the point to account for the scroll offset if necessary. This can only happen
|
||||
// when WebRender is in use.
|
||||
//
|
||||
// We don't perform this adjustment on the root stacking context because the DOM-side code
|
||||
// has already translated the point for us (e.g. in `Document::handle_mouse_move_event()`)
|
||||
// by now.
|
||||
if self.id != StackingContextId::root() {
|
||||
if let Some(scroll_offset) = scroll_offsets.get(&self.id) {
|
||||
point.x -= Au::from_f32_px(scroll_offset.x);
|
||||
point.y -= Au::from_f32_px(scroll_offset.y);
|
||||
}
|
||||
}
|
||||
|
||||
for child in self.children.iter() {
|
||||
while let Some(item) = traversal.advance(self) {
|
||||
item.hit_test(point, result);
|
||||
}
|
||||
child.hit_test(traversal, point, result);
|
||||
child.hit_test(traversal, &point, scroll_offsets, result);
|
||||
}
|
||||
|
||||
while let Some(item) = traversal.advance(self) {
|
||||
@ -1415,3 +1430,7 @@ impl WebRenderImageInfo {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The type of the scroll offset list. This is only populated if WebRender is in use.
|
||||
pub type ScrollOffsetMap = HashMap<StackingContextId, Point2D<f32>>;
|
||||
|
||||
|
@ -26,6 +26,20 @@ use euclid::Matrix4D;
|
||||
use euclid::rect::Rect;
|
||||
use msg::constellation_msg::PipelineId;
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};
|
||||
|
||||
/// The next ID that will be used for a special stacking context.
|
||||
///
|
||||
/// A special stacking context is a stacking context that is one of (a) the outer stacking context
|
||||
/// of an element with `overflow: scroll`; (b) generated content; (c) both (a) and (b).
|
||||
static NEXT_SPECIAL_STACKING_CONTEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||
|
||||
/// If none of the bits outside this mask are set, the stacking context is a special stacking
|
||||
/// context.
|
||||
///
|
||||
/// Note that we assume that the top 16 bits of the address space are unused on the platform.
|
||||
const SPECIAL_STACKING_CONTEXT_ID_MASK: usize = 0xffff;
|
||||
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum LayerKind {
|
||||
@ -159,15 +173,33 @@ pub struct StackingContextId(
|
||||
);
|
||||
|
||||
impl StackingContextId {
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub fn new(id: usize) -> StackingContextId {
|
||||
StackingContextId::new_of_type(id, FragmentType::FragmentBody)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
/// Returns a new stacking context ID for a special stacking context.
|
||||
fn next_special_id() -> usize {
|
||||
// We shift this left by 2 to make room for the fragment type ID.
|
||||
((NEXT_SPECIAL_STACKING_CONTEXT_ID.fetch_add(1, Ordering::SeqCst) + 1) << 2) &
|
||||
SPECIAL_STACKING_CONTEXT_ID_MASK
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new_of_type(id: usize, fragment_type: FragmentType) -> StackingContextId {
|
||||
debug_assert_eq!(id & fragment_type as usize, 0);
|
||||
StackingContextId(id | fragment_type as usize)
|
||||
debug_assert_eq!(id & (fragment_type as usize), 0);
|
||||
if fragment_type == FragmentType::FragmentBody {
|
||||
StackingContextId(id)
|
||||
} else {
|
||||
StackingContextId(StackingContextId::next_special_id() | (fragment_type as usize))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an ID for the stacking context that forms the outer stacking context of an element
|
||||
/// with `overflow: scroll`.
|
||||
#[inline(always)]
|
||||
pub fn new_outer(fragment_type: FragmentType) -> StackingContextId {
|
||||
StackingContextId(StackingContextId::next_special_id() | (fragment_type as usize))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -179,9 +211,28 @@ impl StackingContextId {
|
||||
pub fn id(&self) -> usize {
|
||||
self.0 & !3
|
||||
}
|
||||
|
||||
/// Returns the stacking context ID for the outer document/layout root.
|
||||
#[inline]
|
||||
pub fn root() -> StackingContextId {
|
||||
StackingContextId(0)
|
||||
}
|
||||
|
||||
/// Returns true if this is a special stacking context.
|
||||
///
|
||||
/// A special stacking context is a stacking context that is one of (a) the outer stacking
|
||||
/// context of an element with `overflow: scroll`; (b) generated content; (c) both (a) and (b).
|
||||
#[inline]
|
||||
pub fn is_special(&self) -> bool {
|
||||
(self.0 & !SPECIAL_STACKING_CONTEXT_ID_MASK) == 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// The type of fragment that a stacking context represents.
|
||||
///
|
||||
/// This can only ever grow to maximum 4 entries. That's because we cram the value of this enum
|
||||
/// into the lower 2 bits of the `StackingContextId`, which otherwise contains a 32-bit-aligned
|
||||
/// heap address.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, Deserialize, Serialize, HeapSizeOf)]
|
||||
pub enum FragmentType {
|
||||
/// A StackingContext for the fragment body itself.
|
||||
|
@ -1686,13 +1686,18 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
|
||||
return parent_id;
|
||||
}
|
||||
|
||||
let stacking_context_id =
|
||||
let has_scrolling_overflow = self.has_scrolling_overflow();
|
||||
let stacking_context_id = if has_scrolling_overflow {
|
||||
StackingContextId::new_outer(self.fragment.fragment_type())
|
||||
} else {
|
||||
StackingContextId::new_of_type(self.fragment.node.id() as usize,
|
||||
self.fragment.fragment_type());
|
||||
self.fragment.fragment_type())
|
||||
};
|
||||
self.base.stacking_context_id = stacking_context_id;
|
||||
|
||||
let inner_stacking_context_id = if self.has_scrolling_overflow() {
|
||||
StackingContextId::new_of_type(self.base.flow_id(), self.fragment.fragment_type())
|
||||
let inner_stacking_context_id = if has_scrolling_overflow {
|
||||
StackingContextId::new_of_type(self.fragment.node.id() as usize,
|
||||
self.fragment.fragment_type())
|
||||
} else {
|
||||
stacking_context_id
|
||||
};
|
||||
|
@ -21,13 +21,13 @@ use euclid::size::Size2D;
|
||||
use flow::{self, Flow, ImmutableFlowUtils, MutableOwnedFlowUtils};
|
||||
use flow_ref::{self, FlowRef};
|
||||
use fnv::FnvHasher;
|
||||
use gfx::display_list::{ClippingRegion, DisplayItemMetadata, DisplayList, LayerInfo};
|
||||
use gfx::display_list::{OpaqueNode, StackingContext, StackingContextType, WebRenderImageInfo};
|
||||
use gfx::display_list::{ClippingRegion, DisplayItemMetadata, DisplayList, LayerInfo, OpaqueNode};
|
||||
use gfx::display_list::{ScrollOffsetMap, StackingContext, StackingContextType, WebRenderImageInfo};
|
||||
use gfx::font;
|
||||
use gfx::font_cache_thread::FontCacheThread;
|
||||
use gfx::font_context;
|
||||
use gfx::paint_thread::LayoutToPaintMsg;
|
||||
use gfx_traits::{color, Epoch, LayerId, ScrollPolicy, StackingContextId};
|
||||
use gfx_traits::{color, Epoch, FragmentType, LayerId, ScrollPolicy, StackingContextId};
|
||||
use heapsize::HeapSizeOf;
|
||||
use incremental::LayoutDamageComputation;
|
||||
use incremental::{REPAINT, STORE_OVERFLOW, REFLOW_OUT_OF_FLOW, REFLOW, REFLOW_ENTIRE_DOCUMENT};
|
||||
@ -51,8 +51,8 @@ use script::layout_interface::OpaqueStyleAndLayoutData;
|
||||
use script::layout_interface::{LayoutRPC, OffsetParentResponse, NodeOverflowResponse, MarginStyleResponse};
|
||||
use script::layout_interface::{Msg, NewLayoutThreadInfo, Reflow, ReflowQueryType, ScriptReflow};
|
||||
use script::reporter::CSSErrorReporter;
|
||||
use script_traits::StackingContextScrollState;
|
||||
use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg};
|
||||
use script_traits::{StackingContextScrollState, UntrustedNodeAddress};
|
||||
use sequential;
|
||||
use serde_json;
|
||||
use std::borrow::ToOwned;
|
||||
@ -133,6 +133,9 @@ pub struct LayoutThreadData {
|
||||
|
||||
/// A queued response for the offset parent/rect of a node.
|
||||
pub margin_style_response: MarginStyleResponse,
|
||||
|
||||
/// Scroll offsets of stacking contexts. This will only be populated if WebRender is in use.
|
||||
pub stacking_context_scroll_offsets: ScrollOffsetMap,
|
||||
}
|
||||
|
||||
/// Information needed by the layout thread.
|
||||
@ -472,6 +475,7 @@ impl LayoutThread {
|
||||
resolved_style_response: None,
|
||||
offset_parent_response: OffsetParentResponse::empty(),
|
||||
margin_style_response: MarginStyleResponse::empty(),
|
||||
stacking_context_scroll_offsets: HashMap::new(),
|
||||
})),
|
||||
error_reporter: CSSErrorReporter {
|
||||
pipelineid: id,
|
||||
@ -1172,7 +1176,9 @@ impl LayoutThread {
|
||||
let point = Point2D::new(Au::from_f32_px(point.x), Au::from_f32_px(point.y));
|
||||
let result = match rw_data.display_list {
|
||||
None => panic!("Tried to hit test with no display list"),
|
||||
Some(ref dl) => dl.hit_test(point),
|
||||
Some(ref display_list) => {
|
||||
display_list.hit_test(&point, &rw_data.stacking_context_scroll_offsets)
|
||||
}
|
||||
};
|
||||
rw_data.hit_test_response = if result.len() > 0 {
|
||||
(Some(result[0]), update_cursor)
|
||||
@ -1273,14 +1279,26 @@ impl LayoutThread {
|
||||
fn set_stacking_context_scroll_states<'a, 'b>(
|
||||
&mut self,
|
||||
new_scroll_states: Vec<StackingContextScrollState>,
|
||||
_: &mut RwData<'a, 'b>) {
|
||||
possibly_locked_rw_data: &mut RwData<'a, 'b>) {
|
||||
let mut rw_data = possibly_locked_rw_data.lock();
|
||||
let mut script_scroll_states = vec![];
|
||||
let mut layout_scroll_states = HashMap::new();
|
||||
for new_scroll_state in &new_scroll_states {
|
||||
if self.root_flow.is_some() && new_scroll_state.stacking_context_id.id() == 0 {
|
||||
let _ = self.script_chan.send(ConstellationControlMsg::SetScrollState(
|
||||
self.id,
|
||||
new_scroll_state.scroll_offset));
|
||||
let offset = new_scroll_state.scroll_offset;
|
||||
layout_scroll_states.insert(new_scroll_state.stacking_context_id, offset);
|
||||
|
||||
if new_scroll_state.stacking_context_id == StackingContextId::root() {
|
||||
script_scroll_states.push((UntrustedNodeAddress::from_id(0), offset))
|
||||
} else if !new_scroll_state.stacking_context_id.is_special() &&
|
||||
new_scroll_state.stacking_context_id.fragment_type() ==
|
||||
FragmentType::FragmentBody {
|
||||
let id = new_scroll_state.stacking_context_id.id();
|
||||
script_scroll_states.push((UntrustedNodeAddress::from_id(id), offset))
|
||||
}
|
||||
}
|
||||
let _ = self.script_chan
|
||||
.send(ConstellationControlMsg::SetScrollState(self.id, script_scroll_states));
|
||||
rw_data.stacking_context_scroll_offsets = layout_scroll_states
|
||||
}
|
||||
|
||||
fn tick_all_animations<'a, 'b>(&mut self, possibly_locked_rw_data: &mut RwData<'a, 'b>) {
|
||||
|
@ -91,7 +91,9 @@ impl LayoutRPC for LayoutRPCImpl {
|
||||
let rw_data = rw_data.lock().unwrap();
|
||||
let result = match rw_data.display_list {
|
||||
None => panic!("Tried to hit test without a DisplayList"),
|
||||
Some(ref display_list) => display_list.hit_test(point),
|
||||
Some(ref display_list) => {
|
||||
display_list.hit_test(&point, &rw_data.stacking_context_scroll_offsets)
|
||||
}
|
||||
};
|
||||
|
||||
result
|
||||
|
@ -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) {
|
||||
|
@ -40,6 +40,7 @@ use euclid::rect::Rect;
|
||||
use gfx_traits::Epoch;
|
||||
use gfx_traits::LayerId;
|
||||
use gfx_traits::StackingContextId;
|
||||
use heapsize::HeapSizeOf;
|
||||
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
||||
use libc::c_void;
|
||||
use msg::constellation_msg::{FrameId, FrameType, Key, KeyModifiers, KeyState, LoadData};
|
||||
@ -52,6 +53,7 @@ use net_traits::bluetooth_thread::BluetoothMethodMsg;
|
||||
use net_traits::image_cache_thread::ImageCacheThread;
|
||||
use net_traits::response::HttpsState;
|
||||
use profile_traits::mem;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::mpsc::{Sender, Receiver};
|
||||
use url::Url;
|
||||
@ -61,11 +63,39 @@ pub use script_msg::{LayoutMsg, ScriptMsg, EventResult};
|
||||
|
||||
/// The address of a node. Layout sends these back. They must be validated via
|
||||
/// `from_untrusted_node_address` before they can be used, because we do not trust layout.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct UntrustedNodeAddress(pub *const c_void);
|
||||
|
||||
impl HeapSizeOf for UntrustedNodeAddress {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe impl Send for UntrustedNodeAddress {}
|
||||
|
||||
impl Serialize for UntrustedNodeAddress {
|
||||
fn serialize<S: Serializer>(&self, s: &mut S) -> Result<(), S::Error> {
|
||||
(self.0 as usize).serialize(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deserialize for UntrustedNodeAddress {
|
||||
fn deserialize<D: Deserializer>(d: &mut D) -> Result<UntrustedNodeAddress, D::Error> {
|
||||
let value: usize = try!(Deserialize::deserialize(d));
|
||||
Ok(UntrustedNodeAddress::from_id(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl UntrustedNodeAddress {
|
||||
/// Creates an `UntrustedNodeAddress` from the given pointer address value.
|
||||
#[inline]
|
||||
pub fn from_id(id: usize) -> UntrustedNodeAddress {
|
||||
UntrustedNodeAddress(id as *const c_void)
|
||||
}
|
||||
}
|
||||
|
||||
/// Messages sent to the layout thread from the constellation and/or compositor.
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub enum LayoutControlMsg {
|
||||
@ -125,8 +155,8 @@ pub enum ConstellationControlMsg {
|
||||
SendEvent(PipelineId, CompositorEvent),
|
||||
/// Notifies script of the viewport.
|
||||
Viewport(PipelineId, Rect<f32>),
|
||||
/// Notifies script of a new scroll offset.
|
||||
SetScrollState(PipelineId, Point2D<f32>),
|
||||
/// Notifies script of a new set of scroll offsets.
|
||||
SetScrollState(PipelineId, Vec<(UntrustedNodeAddress, Point2D<f32>)>),
|
||||
/// Requests that the script thread immediately send the constellation the title of a pipeline.
|
||||
GetTitle(PipelineId),
|
||||
/// Notifies script thread to suspend all its timers
|
||||
|
7
servo/tests/html/hit_test_overflow_scroll.html
Normal file
7
servo/tests/html/hit_test_overflow_scroll.html
Normal file
@ -0,0 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html style="overflow: scroll">
|
||||
<body style="overflow: hidden; position: relative;">
|
||||
<p>Scroll down...</p>
|
||||
<p style="padding-top: 1200px"><a href="http://example.com">Mouse over me!</a></p>
|
||||
</body>
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user