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:
Patrick Walton 2016-06-11 11:01:36 -05:00
parent 331b90272e
commit 2120477f7d
14 changed files with 259 additions and 47 deletions

View File

@ -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>>;

View File

@ -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.

View File

@ -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
};

View File

@ -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>) {

View File

@ -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

View File

@ -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>);

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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,
};

View File

@ -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
}

View File

@ -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) {

View File

@ -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

View 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>