mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Switch to using WebRender hit testing
This trades quite a bit of complicated code in Servo for few more messages and a significant performance improvement. In particular, WebRender can search the entire display list at once instead of ping-ponging down the pipeline tree. This allows us to send mouse events to the correct pipeline immediately.
This commit is contained in:
parent
00e2a1c62a
commit
b5d51dd263
20 changed files with 381 additions and 555 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -475,6 +475,7 @@ dependencies = [
|
|||
"gleam 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"image 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ipc-channel 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"msg 0.0.1",
|
||||
"net_traits 0.0.1",
|
||||
|
@ -1567,6 +1568,7 @@ dependencies = [
|
|||
"layout 0.0.1",
|
||||
"layout_traits 0.0.1",
|
||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"metrics 0.0.1",
|
||||
"msg 0.0.1",
|
||||
|
|
|
@ -15,6 +15,7 @@ gfx_traits = {path = "../gfx_traits"}
|
|||
gleam = "0.4"
|
||||
image = "0.16"
|
||||
ipc-channel = "0.9"
|
||||
libc = "0.2"
|
||||
log = "0.3.5"
|
||||
msg = {path = "../msg"}
|
||||
net_traits = {path = "../net_traits"}
|
||||
|
|
|
@ -6,20 +6,20 @@ use CompositionPipeline;
|
|||
use SendableFrameTree;
|
||||
use compositor_thread::{CompositorProxy, CompositorReceiver};
|
||||
use compositor_thread::{InitialCompositorState, Msg, RenderListener};
|
||||
use euclid::{Point2D, TypedPoint2D, TypedVector2D, ScaleFactor};
|
||||
use euclid::{TypedPoint2D, TypedVector2D, ScaleFactor};
|
||||
use gfx_traits::Epoch;
|
||||
use gleam::gl;
|
||||
use image::{DynamicImage, ImageFormat, RgbImage};
|
||||
use ipc_channel::ipc::{self, IpcSharedMemory};
|
||||
use libc::c_void;
|
||||
use msg::constellation_msg::{PipelineId, PipelineIndex, PipelineNamespaceId};
|
||||
use net_traits::image::base::{Image, PixelFormat};
|
||||
use nonzero::NonZero;
|
||||
use profile_traits::time::{self, ProfilerCategory, profile};
|
||||
use script_traits::{AnimationState, AnimationTickType, ConstellationControlMsg};
|
||||
use script_traits::{ConstellationMsg, LayoutControlMsg, MouseButton};
|
||||
use script_traits::{MouseEventType, ScrollState};
|
||||
use script_traits::{TouchpadPressurePhase, TouchEventType, TouchId, WindowSizeData, WindowSizeType};
|
||||
use script_traits::CompositorEvent::{self, MouseMoveEvent, MouseButtonEvent, TouchEvent, TouchpadPressureEvent};
|
||||
use script_traits::{AnimationState, AnimationTickType, ConstellationMsg, LayoutControlMsg};
|
||||
use script_traits::{MouseButton, MouseEventType, ScrollState, TouchEventType, TouchId};
|
||||
use script_traits::{TouchpadPressurePhase, UntrustedNodeAddress, WindowSizeData, WindowSizeType};
|
||||
use script_traits::CompositorEvent::{MouseMoveEvent, MouseButtonEvent, TouchEvent, TouchpadPressureEvent};
|
||||
use servo_config::opts;
|
||||
use servo_config::prefs::PREFS;
|
||||
use servo_geometry::DeviceIndependentPixel;
|
||||
|
@ -29,12 +29,13 @@ use std::rc::Rc;
|
|||
use std::sync::mpsc::Sender;
|
||||
use std::time::{Duration, Instant};
|
||||
use style_traits::{CSSPixel, DevicePixel, PinchZoomFactor};
|
||||
use style_traits::cursor::Cursor;
|
||||
use style_traits::viewport::ViewportConstraints;
|
||||
use time::{precise_time_ns, precise_time_s};
|
||||
use touch::{TouchHandler, TouchAction};
|
||||
use webrender;
|
||||
use webrender_api::{self, ClipId, DeviceUintRect, DeviceUintSize, LayoutPoint, LayoutVector2D};
|
||||
use webrender_api::{ScrollEventPhase, ScrollLocation, ScrollClamping};
|
||||
use webrender_api::{self, DeviceUintRect, DeviceUintSize, HitTestFlags, HitTestResult};
|
||||
use webrender_api::{LayoutVector2D, ScrollEventPhase, ScrollLocation};
|
||||
use windowing::{self, MouseWindowEvent, WebRenderDebugOption, WindowMethods};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
@ -464,11 +465,6 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
self.send_viewport_rects();
|
||||
}
|
||||
|
||||
(Msg::ScrollFragmentPoint(scroll_root_id, point, _),
|
||||
ShutdownState::NotShuttingDown) => {
|
||||
self.scroll_fragment_to_point(scroll_root_id, point);
|
||||
}
|
||||
|
||||
(Msg::Recomposite(reason), ShutdownState::NotShuttingDown) => {
|
||||
self.composition_request = CompositionRequest::CompositeNow(reason)
|
||||
}
|
||||
|
@ -656,13 +652,6 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
}
|
||||
}
|
||||
|
||||
fn scroll_fragment_to_point(&mut self, id: ClipId, point: Point2D<f32>) {
|
||||
self.webrender_api.scroll_node_with_id(self.webrender_document,
|
||||
LayoutPoint::from_untyped(&point),
|
||||
id,
|
||||
ScrollClamping::ToContentBounds);
|
||||
}
|
||||
|
||||
pub fn on_resize_window_event(&mut self, new_size: DeviceUintSize) {
|
||||
debug!("compositor resizing to {:?}", new_size.to_untyped());
|
||||
|
||||
|
@ -707,32 +696,47 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
MouseWindowEvent::MouseUp(_, p) => p,
|
||||
};
|
||||
|
||||
let root_pipeline_id = match self.get_root_pipeline_id() {
|
||||
Some(root_pipeline_id) => root_pipeline_id,
|
||||
let results = self.hit_test_at_point(point);
|
||||
let result = match results.items.first() {
|
||||
Some(result) => result,
|
||||
None => return,
|
||||
};
|
||||
|
||||
if let Some(pipeline) = self.pipeline(root_pipeline_id) {
|
||||
let dppx = self.page_zoom * self.hidpi_factor();
|
||||
let translated_point = (point / dppx).to_untyped();
|
||||
let event_to_send = match mouse_window_event {
|
||||
MouseWindowEvent::Click(button, _) => {
|
||||
MouseButtonEvent(MouseEventType::Click, button, translated_point)
|
||||
}
|
||||
MouseWindowEvent::MouseDown(button, _) => {
|
||||
MouseButtonEvent(MouseEventType::MouseDown, button, translated_point)
|
||||
}
|
||||
MouseWindowEvent::MouseUp(button, _) => {
|
||||
MouseButtonEvent(MouseEventType::MouseUp, button, translated_point)
|
||||
}
|
||||
};
|
||||
let msg = ConstellationControlMsg::SendEvent(root_pipeline_id, event_to_send);
|
||||
if let Err(e) = pipeline.script_chan.send(msg) {
|
||||
warn!("Sending control event to script failed ({}).", e);
|
||||
let point = result.point_in_viewport.to_untyped();
|
||||
let node_address = Some(UntrustedNodeAddress(result.tag.0 as *const c_void));
|
||||
let event_to_send = match mouse_window_event {
|
||||
MouseWindowEvent::Click(button, _) => {
|
||||
MouseButtonEvent(MouseEventType::Click, button, point, node_address)
|
||||
}
|
||||
MouseWindowEvent::MouseDown(button, _) => {
|
||||
MouseButtonEvent(MouseEventType::MouseDown, button, point, node_address)
|
||||
}
|
||||
MouseWindowEvent::MouseUp(button, _) => {
|
||||
MouseButtonEvent(MouseEventType::MouseUp, button, point, node_address)
|
||||
}
|
||||
};
|
||||
|
||||
let pipeline_id = PipelineId::from_webrender(result.pipeline);
|
||||
let msg = ConstellationMsg::ForwardEvent(pipeline_id, event_to_send);
|
||||
if let Err(e) = self.constellation_chan.send(msg) {
|
||||
warn!("Sending event to constellation failed ({}).", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn hit_test_at_point(&self, point: TypedPoint2D<f32, DevicePixel>) -> HitTestResult {
|
||||
let dppx = self.page_zoom * self.hidpi_factor();
|
||||
let scaled_point = (point / dppx).to_untyped();
|
||||
|
||||
let world_cursor = webrender_api::WorldPoint::from_untyped(&scaled_point);
|
||||
self.webrender_api.hit_test(
|
||||
self.webrender_document,
|
||||
None,
|
||||
world_cursor,
|
||||
HitTestFlags::empty()
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
pub fn on_mouse_window_move_event_class(&mut self, cursor: TypedPoint2D<f32, DevicePixel>) {
|
||||
if opts::get().convert_mouse_to_touch {
|
||||
self.on_touch_move(TouchId(0), cursor);
|
||||
|
@ -751,26 +755,43 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
return;
|
||||
}
|
||||
|
||||
let dppx = self.page_zoom * self.hidpi_factor();
|
||||
let event_to_send = MouseMoveEvent(Some((cursor / dppx).to_untyped()));
|
||||
let msg = ConstellationControlMsg::SendEvent(root_pipeline_id, event_to_send);
|
||||
if let Some(pipeline) = self.pipeline(root_pipeline_id) {
|
||||
if let Err(e) = pipeline.script_chan.send(msg) {
|
||||
warn!("Sending mouse control event to script failed ({}).", e);
|
||||
let results = self.hit_test_at_point(cursor);
|
||||
if let Some(item) = results.items.first() {
|
||||
let node_address = Some(UntrustedNodeAddress(item.tag.0 as *const c_void));
|
||||
let event = MouseMoveEvent(Some(item.point_in_viewport.to_untyped()), node_address);
|
||||
let pipeline_id = PipelineId::from_webrender(item.pipeline);
|
||||
let msg = ConstellationMsg::ForwardEvent(pipeline_id, event);
|
||||
if let Err(e) = self.constellation_chan.send(msg) {
|
||||
warn!("Sending event to constellation failed ({}).", e);
|
||||
}
|
||||
|
||||
if let Some(cursor) = Cursor::from_u8(item.tag.1).ok() {
|
||||
let msg = ConstellationMsg::SetCursor(cursor);
|
||||
if let Err(e) = self.constellation_chan.send(msg) {
|
||||
warn!("Sending event to constellation failed ({}).", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn send_event_to_root_pipeline(&self, event: CompositorEvent) {
|
||||
let root_pipeline_id = match self.get_root_pipeline_id() {
|
||||
Some(root_pipeline_id) => root_pipeline_id,
|
||||
None => return,
|
||||
};
|
||||
|
||||
if let Some(pipeline) = self.pipeline(root_pipeline_id) {
|
||||
let msg = ConstellationControlMsg::SendEvent(root_pipeline_id, event);
|
||||
if let Err(e) = pipeline.script_chan.send(msg) {
|
||||
warn!("Sending control event to script failed ({}).", e);
|
||||
fn send_touch_event(
|
||||
&self,
|
||||
event_type: TouchEventType,
|
||||
identifier: TouchId,
|
||||
point: TypedPoint2D<f32, DevicePixel>)
|
||||
{
|
||||
let results = self.hit_test_at_point(point);
|
||||
if let Some(item) = results.items.first() {
|
||||
let event = TouchEvent(
|
||||
event_type,
|
||||
identifier,
|
||||
item.point_in_viewport.to_untyped(),
|
||||
Some(UntrustedNodeAddress(item.tag.0 as *const c_void)),
|
||||
);
|
||||
let pipeline_id = PipelineId::from_webrender(item.pipeline);
|
||||
let msg = ConstellationMsg::ForwardEvent(pipeline_id, event);
|
||||
if let Err(e) = self.constellation_chan.send(msg) {
|
||||
warn!("Sending event to constellation failed ({}).", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -789,11 +810,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
|
||||
fn on_touch_down(&mut self, identifier: TouchId, point: TypedPoint2D<f32, DevicePixel>) {
|
||||
self.touch_handler.on_touch_down(identifier, point);
|
||||
let dppx = self.page_zoom * self.hidpi_factor();
|
||||
let translated_point = (point / dppx).to_untyped();
|
||||
self.send_event_to_root_pipeline(TouchEvent(TouchEventType::Down,
|
||||
identifier,
|
||||
translated_point));
|
||||
self.send_touch_event(TouchEventType::Down, identifier, point);
|
||||
}
|
||||
|
||||
fn on_touch_move(&mut self, identifier: TouchId, point: TypedPoint2D<f32, DevicePixel>) {
|
||||
|
@ -821,22 +838,15 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
});
|
||||
}
|
||||
TouchAction::DispatchEvent => {
|
||||
let dppx = self.page_zoom * self.hidpi_factor();
|
||||
let translated_point = (point / dppx).to_untyped();
|
||||
self.send_event_to_root_pipeline(TouchEvent(TouchEventType::Move,
|
||||
identifier,
|
||||
translated_point));
|
||||
self.send_touch_event(TouchEventType::Move, identifier, point);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_touch_up(&mut self, identifier: TouchId, point: TypedPoint2D<f32, DevicePixel>) {
|
||||
let dppx = self.page_zoom * self.hidpi_factor();
|
||||
let translated_point = (point / dppx).to_untyped();
|
||||
self.send_event_to_root_pipeline(TouchEvent(TouchEventType::Up,
|
||||
identifier,
|
||||
translated_point));
|
||||
self.send_touch_event(TouchEventType::Up, identifier, point);
|
||||
|
||||
if let TouchAction::Click = self.touch_handler.on_touch_up(identifier, point) {
|
||||
self.simulate_mouse_click(point);
|
||||
}
|
||||
|
@ -845,23 +855,31 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
fn on_touch_cancel(&mut self, identifier: TouchId, point: TypedPoint2D<f32, DevicePixel>) {
|
||||
// Send the event to script.
|
||||
self.touch_handler.on_touch_cancel(identifier, point);
|
||||
let dppx = self.page_zoom * self.hidpi_factor();
|
||||
let translated_point = (point / dppx).to_untyped();
|
||||
self.send_event_to_root_pipeline(TouchEvent(TouchEventType::Cancel,
|
||||
identifier,
|
||||
translated_point));
|
||||
self.send_touch_event(TouchEventType::Cancel, identifier, point);
|
||||
}
|
||||
|
||||
pub fn on_touchpad_pressure_event(&self,
|
||||
point: TypedPoint2D<f32, DevicePixel>,
|
||||
pressure: f32,
|
||||
phase: TouchpadPressurePhase) {
|
||||
if let Some(true) = PREFS.get("dom.forcetouch.enabled").as_boolean() {
|
||||
let dppx = self.page_zoom * self.hidpi_factor();
|
||||
let translated_point = (point / dppx).to_untyped();
|
||||
self.send_event_to_root_pipeline(TouchpadPressureEvent(translated_point,
|
||||
pressure,
|
||||
phase));
|
||||
match PREFS.get("dom.forcetouch.enabled").as_boolean() {
|
||||
Some(true) => {},
|
||||
_ => return,
|
||||
}
|
||||
|
||||
let results = self.hit_test_at_point(point);
|
||||
if let Some(item) = results.items.first() {
|
||||
let event = TouchpadPressureEvent(
|
||||
item.point_in_viewport.to_untyped(),
|
||||
pressure,
|
||||
phase,
|
||||
Some(UntrustedNodeAddress(item.tag.0 as *const c_void)),
|
||||
);
|
||||
let pipeline_id = PipelineId::from_webrender(item.pipeline);
|
||||
let msg = ConstellationMsg::ForwardEvent(pipeline_id, event);
|
||||
if let Err(e) = self.constellation_chan.send(msg) {
|
||||
warn!("Sending event to constellation failed ({}).", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -158,8 +158,6 @@ pub enum Msg {
|
|||
/// (e.g. SetFrameTree) at the time that we send it an ExitMsg.
|
||||
ShutdownComplete,
|
||||
|
||||
/// Scroll a page in a window
|
||||
ScrollFragmentPoint(webrender_api::ClipId, Point2D<f32>, bool),
|
||||
/// Alerts the compositor that the given pipeline has changed whether it is running animations.
|
||||
ChangeRunningAnimationsState(PipelineId, AnimationState),
|
||||
/// Replaces the current frame tree, typically called during main frame navigation.
|
||||
|
@ -195,6 +193,7 @@ pub enum Msg {
|
|||
PendingPaintMetric(PipelineId, Epoch),
|
||||
/// The load of a page has completed
|
||||
LoadComplete(TopLevelBrowsingContextId),
|
||||
|
||||
}
|
||||
|
||||
impl Debug for Msg {
|
||||
|
@ -202,7 +201,6 @@ impl Debug for Msg {
|
|||
match *self {
|
||||
Msg::Exit => write!(f, "Exit"),
|
||||
Msg::ShutdownComplete => write!(f, "ShutdownComplete"),
|
||||
Msg::ScrollFragmentPoint(..) => write!(f, "ScrollFragmentPoint"),
|
||||
Msg::ChangeRunningAnimationsState(..) => write!(f, "ChangeRunningAnimationsState"),
|
||||
Msg::SetFrameTree(..) => write!(f, "SetFrameTree"),
|
||||
Msg::Recomposite(..) => write!(f, "Recomposite"),
|
||||
|
|
|
@ -9,6 +9,7 @@ extern crate gfx_traits;
|
|||
extern crate gleam;
|
||||
extern crate image;
|
||||
extern crate ipc_channel;
|
||||
extern crate libc;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate msg;
|
||||
|
|
|
@ -1082,6 +1082,12 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
debug!("constellation got {:?} WebVR events", events.len());
|
||||
self.handle_webvr_events(pipeline_ids, events);
|
||||
}
|
||||
FromCompositorMsg::ForwardEvent(destination_pipeline_id, event) => {
|
||||
self.forward_event(destination_pipeline_id, event);
|
||||
}
|
||||
FromCompositorMsg::SetCursor(cursor) => {
|
||||
self.handle_set_cursor_msg(cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1175,15 +1181,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
debug!("constellation got focus message");
|
||||
self.handle_focus_msg(source_pipeline_id);
|
||||
}
|
||||
FromScriptMsg::ForwardEvent(dest_id, event) => {
|
||||
let msg = ConstellationControlMsg::SendEvent(dest_id, event);
|
||||
let result = match self.pipelines.get(&dest_id) {
|
||||
None => { debug!("Pipeline {:?} got event after closure.", dest_id); return; }
|
||||
Some(pipeline) => pipeline.event_loop.send(msg),
|
||||
};
|
||||
if let Err(e) = result {
|
||||
self.handle_send_error(dest_id, e);
|
||||
}
|
||||
FromScriptMsg::ForwardEvent(destination_pipeline_id, event) => {
|
||||
self.forward_event(destination_pipeline_id, event);
|
||||
}
|
||||
FromScriptMsg::GetClipboardContents(sender) => {
|
||||
let contents = match self.clipboard_ctx {
|
||||
|
@ -1250,13 +1249,6 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
debug!("constellation got Alert message");
|
||||
self.handle_alert(source_top_ctx_id, message, sender);
|
||||
}
|
||||
|
||||
FromScriptMsg::ScrollFragmentPoint(scroll_root_id, point, smooth) => {
|
||||
self.compositor_proxy.send(ToCompositorMsg::ScrollFragmentPoint(scroll_root_id,
|
||||
point,
|
||||
smooth));
|
||||
}
|
||||
|
||||
FromScriptMsg::GetClientWindow(send) => {
|
||||
self.embedder_proxy.send(EmbedderMsg::GetClientWindow(source_top_ctx_id, send));
|
||||
}
|
||||
|
@ -1592,6 +1584,20 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
}
|
||||
}
|
||||
|
||||
fn forward_event(&mut self, destination_pipeline_id: PipelineId, event: CompositorEvent) {
|
||||
let msg = ConstellationControlMsg::SendEvent(destination_pipeline_id, event);
|
||||
let result = match self.pipelines.get(&destination_pipeline_id) {
|
||||
None => {
|
||||
debug!("Pipeline {:?} got event after closure.", destination_pipeline_id);
|
||||
return;
|
||||
}
|
||||
Some(pipeline) => pipeline.event_loop.send(msg),
|
||||
};
|
||||
if let Err(e) = result {
|
||||
self.handle_send_error(destination_pipeline_id, e);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_new_top_level_browsing_context(&mut self, url: ServoUrl, reply: IpcSender<TopLevelBrowsingContextId>) {
|
||||
let window_size = self.window_size.initial_viewport;
|
||||
let pipeline_id = PipelineId::new();
|
||||
|
|
|
@ -206,79 +206,6 @@ impl DisplayList {
|
|||
}
|
||||
}
|
||||
|
||||
// Return all nodes containing the point of interest, bottommost first, and
|
||||
// respecting the `pointer-events` CSS property.
|
||||
pub fn hit_test(&self,
|
||||
point: &Point2D<Au>,
|
||||
scroll_offsets: &ScrollOffsetMap)
|
||||
-> Vec<DisplayItemMetadata> {
|
||||
let mut result = Vec::new();
|
||||
let mut traversal = DisplayListTraversal::new(self);
|
||||
self.hit_test_contents(&mut traversal,
|
||||
point,
|
||||
&mut ScrollOffsetLookup::new(&mut HashMap::new(), scroll_offsets),
|
||||
&mut result);
|
||||
result
|
||||
}
|
||||
|
||||
fn hit_test_contents<'a>(&self,
|
||||
traversal: &mut DisplayListTraversal<'a>,
|
||||
point: &Point2D<Au>,
|
||||
offset_lookup: &mut ScrollOffsetLookup,
|
||||
result: &mut Vec<DisplayItemMetadata>) {
|
||||
while let Some(item) = traversal.next() {
|
||||
match item {
|
||||
&DisplayItem::PushStackingContext(ref context_item) => {
|
||||
self.hit_test_stacking_context(&context_item.stacking_context,
|
||||
item.scroll_node_id(),
|
||||
traversal,
|
||||
point,
|
||||
offset_lookup,
|
||||
result);
|
||||
}
|
||||
&DisplayItem::PopStackingContext(_) => return,
|
||||
&DisplayItem::DefineClipScrollNode(ref item) => {
|
||||
offset_lookup.add_clip_scroll_node(&item.node);
|
||||
}
|
||||
_ => {
|
||||
if let Some(meta) = item.hit_test(*point, offset_lookup) {
|
||||
result.push(meta);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn hit_test_stacking_context<'a>(&self,
|
||||
stacking_context: &StackingContext,
|
||||
clip_id: ClipId,
|
||||
traversal: &mut DisplayListTraversal<'a>,
|
||||
point: &Point2D<Au>,
|
||||
offset_lookup: &mut ScrollOffsetLookup,
|
||||
result: &mut Vec<DisplayItemMetadata>) {
|
||||
debug_assert!(stacking_context.context_type == StackingContextType::Real);
|
||||
|
||||
let mut point = *point - stacking_context.bounds.origin.to_vector();
|
||||
if stacking_context.scroll_policy == ScrollPolicy::Fixed {
|
||||
let old_offset = offset_lookup.calculated_total_offsets.get(&clip_id).cloned();
|
||||
offset_lookup.calculated_total_offsets.insert(clip_id, Vector2D::zero());
|
||||
|
||||
self.hit_test_contents(traversal, &point, offset_lookup, result);
|
||||
|
||||
match old_offset {
|
||||
Some(offset) => offset_lookup.calculated_total_offsets.insert(clip_id, offset),
|
||||
None => offset_lookup.calculated_total_offsets.remove(&clip_id),
|
||||
};
|
||||
} else if let Some(transform) = stacking_context.transform {
|
||||
if let Some(ref mut sublookup) =
|
||||
offset_lookup.new_for_reference_frame(clip_id, &transform, &mut point) {
|
||||
self.hit_test_contents(traversal, &point, sublookup, result);
|
||||
}
|
||||
} else {
|
||||
self.hit_test_contents(traversal, &point, offset_lookup, result);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(&self) {
|
||||
let mut print_tree = PrintTree::new("Display List".to_owned());
|
||||
self.print_with_tree(&mut print_tree);
|
||||
|
@ -1283,60 +1210,6 @@ impl DisplayItem {
|
|||
}
|
||||
println!("{}+ {:?}", indent, self);
|
||||
}
|
||||
|
||||
fn hit_test(&self,
|
||||
point: Point2D<Au>,
|
||||
offset_lookup: &mut ScrollOffsetLookup)
|
||||
-> Option<DisplayItemMetadata> {
|
||||
// TODO(pcwalton): Use a precise algorithm here. This will allow us to properly hit
|
||||
// test elements with `border-radius`, for example.
|
||||
let base_item = self.base();
|
||||
|
||||
let scroll_offset = offset_lookup.full_offset_for_clip_scroll_node(&self.scroll_node_id());
|
||||
let point = Point2D::new(point.x - Au::from_f32_px(scroll_offset.x),
|
||||
point.y - Au::from_f32_px(scroll_offset.y));
|
||||
|
||||
if !base_item.local_clip.clip_rect().contains(&point.to_pointf()) {
|
||||
// Clipped out.
|
||||
return None;
|
||||
}
|
||||
if !self.bounds().contains(&point) {
|
||||
// Can't possibly hit.
|
||||
return None;
|
||||
}
|
||||
if base_item.metadata.pointing.is_none() {
|
||||
// `pointer-events` is `none`. Ignore this item.
|
||||
return None;
|
||||
}
|
||||
|
||||
match *self {
|
||||
DisplayItem::Border(ref border) => {
|
||||
// If the point is inside the border, it didn't hit the border!
|
||||
let interior_rect =
|
||||
Rect::new(
|
||||
Point2D::new(border.base.bounds.origin.x +
|
||||
border.border_widths.left,
|
||||
border.base.bounds.origin.y +
|
||||
border.border_widths.top),
|
||||
Size2D::new(border.base.bounds.size.width -
|
||||
(border.border_widths.left +
|
||||
border.border_widths.right),
|
||||
border.base.bounds.size.height -
|
||||
(border.border_widths.top +
|
||||
border.border_widths.bottom)));
|
||||
if interior_rect.contains(&point) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
DisplayItem::BoxShadow(_) => {
|
||||
// Box shadows can never be hit.
|
||||
return None;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Some(base_item.metadata)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for DisplayItem {
|
||||
|
|
|
@ -10,16 +10,15 @@ use context::LayoutContext;
|
|||
use euclid::{Point2D, Vector2D, Rect, Size2D};
|
||||
use flow::{self, Flow};
|
||||
use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo};
|
||||
use gfx::display_list::{DisplayItemMetadata, DisplayList, OpaqueNode, ScrollOffsetMap};
|
||||
use gfx::display_list::{DisplayList, OpaqueNode, ScrollOffsetMap};
|
||||
use inline::LAST_FRAGMENT_OF_ELEMENT;
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use msg::constellation_msg::PipelineId;
|
||||
use opaque_node::OpaqueNodeMethods;
|
||||
use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse};
|
||||
use script_layout_interface::rpc::{HitTestResponse, LayoutRPC};
|
||||
use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC};
|
||||
use script_layout_interface::rpc::{MarginStyleResponse, NodeGeometryResponse};
|
||||
use script_layout_interface::rpc::{NodeOverflowResponse, OffsetParentResponse};
|
||||
use script_layout_interface::rpc::{NodeScrollRootIdResponse, ResolvedStyleResponse, TextIndexResponse};
|
||||
use script_layout_interface::rpc::{NodeOverflowResponse, NodeScrollRootIdResponse};
|
||||
use script_layout_interface::rpc::{OffsetParentResponse, ResolvedStyleResponse, TextIndexResponse};
|
||||
use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
|
||||
use script_traits::LayoutMsg as ConstellationMsg;
|
||||
use script_traits::UntrustedNodeAddress;
|
||||
|
@ -35,7 +34,6 @@ use style::properties::{style_structs, PropertyId, PropertyDeclarationId, Longha
|
|||
use style::properties::longhands::{display, position};
|
||||
use style::selector_parser::PseudoElement;
|
||||
use style_traits::ToCss;
|
||||
use style_traits::cursor::Cursor;
|
||||
use webrender_api::ClipId;
|
||||
use wrapper::LayoutNodeLayoutData;
|
||||
|
||||
|
@ -58,9 +56,6 @@ pub struct LayoutThreadData {
|
|||
/// A queued response for the client {top, left, width, height} of a node in pixels.
|
||||
pub client_rect_response: Rect<i32>,
|
||||
|
||||
/// A queued response for the node at a given point
|
||||
pub hit_test_response: (Option<DisplayItemMetadata>, bool),
|
||||
|
||||
/// A queued response for the scroll root id for a given node.
|
||||
pub scroll_root_id_response: Option<ClipId>,
|
||||
|
||||
|
@ -119,24 +114,6 @@ impl LayoutRPC for LayoutRPCImpl {
|
|||
ContentBoxesResponse(rw_data.content_boxes_response.clone())
|
||||
}
|
||||
|
||||
/// Requests the node containing the point of interest.
|
||||
fn hit_test(&self) -> HitTestResponse {
|
||||
let &LayoutRPCImpl(ref rw_data) = self;
|
||||
let rw_data = rw_data.lock().unwrap();
|
||||
let &(ref result, update_cursor) = &rw_data.hit_test_response;
|
||||
if update_cursor {
|
||||
// Compute the new cursor.
|
||||
let cursor = match *result {
|
||||
None => Cursor::Default,
|
||||
Some(dim) => dim.pointing.unwrap(),
|
||||
};
|
||||
rw_data.constellation_chan.send(ConstellationMsg::SetCursor(cursor)).unwrap();
|
||||
}
|
||||
HitTestResponse {
|
||||
node_address: result.map(|dim| dim.node.to_untrusted_node_address()),
|
||||
}
|
||||
}
|
||||
|
||||
fn nodes_from_point_response(&self) -> Vec<UntrustedNodeAddress> {
|
||||
let &LayoutRPCImpl(ref rw_data) = self;
|
||||
let rw_data = rw_data.lock().unwrap();
|
||||
|
|
|
@ -19,23 +19,12 @@ use style::values::generics::effects::Filter as GenericFilter;
|
|||
use webrender_api::{self, ClipAndScrollInfo, ComplexClipRegion, DisplayListBuilder};
|
||||
use webrender_api::{ExtendMode, LayoutTransform};
|
||||
|
||||
fn prim_info(local_rect: Rect<Au>,
|
||||
local_clip: Option<webrender_api::LocalClip>) -> webrender_api::LayoutPrimitiveInfo {
|
||||
match local_clip {
|
||||
Some(local_clip) => {
|
||||
webrender_api::LayoutPrimitiveInfo::with_clip(local_rect.to_rectf(), local_clip)
|
||||
}
|
||||
None => {
|
||||
webrender_api::LayoutPrimitiveInfo::new(local_rect.to_rectf())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WebRenderDisplayListConverter {
|
||||
fn convert_to_webrender(&self, pipeline_id: PipelineId) -> DisplayListBuilder;
|
||||
}
|
||||
|
||||
trait WebRenderDisplayItemConverter {
|
||||
fn prim_info(&self) -> webrender_api::LayoutPrimitiveInfo;
|
||||
fn convert_to_webrender(&self,
|
||||
builder: &mut DisplayListBuilder,
|
||||
current_clip_and_scroll_info: &mut ClipAndScrollInfo);
|
||||
|
@ -249,6 +238,20 @@ impl WebRenderDisplayListConverter for DisplayList {
|
|||
}
|
||||
|
||||
impl WebRenderDisplayItemConverter for DisplayItem {
|
||||
fn prim_info(&self) -> webrender_api::LayoutPrimitiveInfo {
|
||||
let tag = match self.base().metadata.pointing {
|
||||
Some(cursor) => Some((self.base().metadata.node.0 as u64, cursor as u8)),
|
||||
None => None,
|
||||
};
|
||||
webrender_api::LayoutPrimitiveInfo {
|
||||
rect: self.base().bounds.to_rectf(),
|
||||
local_clip: self.base().local_clip,
|
||||
// TODO(gw): Make use of the WR backface visibility functionality.
|
||||
is_backface_visible: true,
|
||||
tag: tag,
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_to_webrender(&self,
|
||||
builder: &mut DisplayListBuilder,
|
||||
current_clip_and_scroll_info: &mut ClipAndScrollInfo) {
|
||||
|
@ -261,11 +264,7 @@ impl WebRenderDisplayItemConverter for DisplayItem {
|
|||
|
||||
match *self {
|
||||
DisplayItem::SolidColor(ref item) => {
|
||||
let color = item.color;
|
||||
if color.a > 0.0 {
|
||||
builder.push_rect(&prim_info(item.base.bounds, Some(item.base.local_clip)),
|
||||
color);
|
||||
}
|
||||
builder.push_rect(&self.prim_info(), item.color);
|
||||
}
|
||||
DisplayItem::Text(ref item) => {
|
||||
let mut origin = item.baseline_origin.clone();
|
||||
|
@ -294,7 +293,7 @@ impl WebRenderDisplayItemConverter for DisplayItem {
|
|||
}
|
||||
|
||||
if glyphs.len() > 0 {
|
||||
builder.push_text(&prim_info(item.base.bounds, Some(item.base.local_clip)),
|
||||
builder.push_text(&self.prim_info(),
|
||||
&glyphs,
|
||||
item.text_run.font_key,
|
||||
item.text_color,
|
||||
|
@ -305,7 +304,7 @@ impl WebRenderDisplayItemConverter for DisplayItem {
|
|||
if let Some(id) = item.webrender_image.key {
|
||||
if item.stretch_size.width > Au(0) &&
|
||||
item.stretch_size.height > Au(0) {
|
||||
builder.push_image(&prim_info(item.base.bounds, Some(item.base.local_clip)),
|
||||
builder.push_image(&self.prim_info(),
|
||||
item.stretch_size.to_sizef(),
|
||||
item.tile_spacing.to_sizef(),
|
||||
item.image_rendering.to_image_rendering(),
|
||||
|
@ -314,7 +313,6 @@ impl WebRenderDisplayItemConverter for DisplayItem {
|
|||
}
|
||||
}
|
||||
DisplayItem::Border(ref item) => {
|
||||
let rect = item.base.bounds;
|
||||
let widths = item.border_widths.to_border_widths();
|
||||
|
||||
let details = match item.details {
|
||||
|
@ -395,7 +393,7 @@ impl WebRenderDisplayItemConverter for DisplayItem {
|
|||
}
|
||||
};
|
||||
|
||||
builder.push_border(&prim_info(rect, Some(item.base.local_clip)), widths, details);
|
||||
builder.push_border(&self.prim_info(), widths, details);
|
||||
}
|
||||
DisplayItem::Gradient(ref item) => {
|
||||
let rect = item.base.bounds;
|
||||
|
@ -410,7 +408,7 @@ impl WebRenderDisplayItemConverter for DisplayItem {
|
|||
end_point,
|
||||
item.gradient.stops.clone(),
|
||||
extend_mode);
|
||||
builder.push_gradient(&prim_info(rect, Some(item.base.local_clip)),
|
||||
builder.push_gradient(&self.prim_info(),
|
||||
gradient,
|
||||
rect.size.to_sizef(),
|
||||
webrender_api::LayoutSize::zero());
|
||||
|
@ -428,14 +426,14 @@ impl WebRenderDisplayItemConverter for DisplayItem {
|
|||
radius,
|
||||
item.gradient.stops.clone(),
|
||||
extend_mode);
|
||||
builder.push_radial_gradient(&prim_info(rect, Some(item.base.local_clip)),
|
||||
builder.push_radial_gradient(&self.prim_info(),
|
||||
gradient,
|
||||
rect.size.to_sizef(),
|
||||
webrender_api::LayoutSize::zero());
|
||||
}
|
||||
DisplayItem::Line(ref item) => {
|
||||
let box_bounds = item.base.bounds.to_rectf();
|
||||
builder.push_line(&prim_info(item.base.bounds, Some(item.base.local_clip)),
|
||||
builder.push_line(&self.prim_info(),
|
||||
box_bounds.origin.y + box_bounds.size.height,
|
||||
box_bounds.origin.x,
|
||||
box_bounds.origin.x + box_bounds.size.width,
|
||||
|
@ -445,9 +443,8 @@ impl WebRenderDisplayItemConverter for DisplayItem {
|
|||
item.style);
|
||||
}
|
||||
DisplayItem::BoxShadow(ref item) => {
|
||||
let rect = item.base.bounds;
|
||||
let box_bounds = item.box_bounds.to_rectf();
|
||||
builder.push_box_shadow(&prim_info(rect, Some(item.base.local_clip)),
|
||||
builder.push_box_shadow(&self.prim_info(),
|
||||
box_bounds,
|
||||
item.offset.to_vectorf(),
|
||||
item.color,
|
||||
|
@ -457,8 +454,7 @@ impl WebRenderDisplayItemConverter for DisplayItem {
|
|||
item.clip_mode.to_clip_mode());
|
||||
}
|
||||
DisplayItem::PushTextShadow(ref item) => {
|
||||
let rect = item.base.bounds;
|
||||
builder.push_shadow(&prim_info(rect, Some(item.base.local_clip)),
|
||||
builder.push_shadow(&self.prim_info(),
|
||||
webrender_api::Shadow {
|
||||
blur_radius: item.blur_radius.to_f32_px(),
|
||||
offset: item.offset.to_vectorf(),
|
||||
|
@ -469,9 +465,7 @@ impl WebRenderDisplayItemConverter for DisplayItem {
|
|||
builder.pop_all_shadows();
|
||||
}
|
||||
DisplayItem::Iframe(ref item) => {
|
||||
let rect = item.base.bounds;
|
||||
let pipeline_id = item.iframe.to_webrender();
|
||||
builder.push_iframe(&prim_info(rect, Some(item.base.local_clip)), pipeline_id);
|
||||
builder.push_iframe(&self.prim_info(), item.iframe.to_webrender());
|
||||
}
|
||||
DisplayItem::PushStackingContext(ref item) => {
|
||||
let stacking_context = &item.stacking_context;
|
||||
|
@ -484,13 +478,15 @@ impl WebRenderDisplayItemConverter for DisplayItem {
|
|||
LayoutTransform::from_untyped(&perspective)
|
||||
});
|
||||
|
||||
builder.push_stacking_context(&prim_info(stacking_context.bounds, None),
|
||||
stacking_context.scroll_policy,
|
||||
transform,
|
||||
stacking_context.transform_style,
|
||||
perspective,
|
||||
stacking_context.mix_blend_mode,
|
||||
stacking_context.filters.to_filter_ops());
|
||||
builder.push_stacking_context(
|
||||
&webrender_api::LayoutPrimitiveInfo::new(stacking_context.bounds.to_rectf()),
|
||||
stacking_context.scroll_policy,
|
||||
transform,
|
||||
stacking_context.transform_style,
|
||||
perspective,
|
||||
stacking_context.mix_blend_mode,
|
||||
stacking_context.filters.to_filter_ops()
|
||||
);
|
||||
}
|
||||
DisplayItem::PopStackingContext(_) => builder.pop_stacking_context(),
|
||||
DisplayItem::DefineClipScrollNode(ref item) => {
|
||||
|
|
|
@ -25,6 +25,7 @@ ipc-channel = "0.9"
|
|||
layout = {path = "../layout"}
|
||||
layout_traits = {path = "../layout_traits"}
|
||||
lazy_static = "0.2"
|
||||
libc = "0.2"
|
||||
log = "0.3.5"
|
||||
metrics = {path = "../metrics"}
|
||||
msg = {path = "../msg"}
|
||||
|
|
|
@ -22,6 +22,7 @@ extern crate layout;
|
|||
extern crate layout_traits;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
extern crate libc;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate metrics;
|
||||
|
@ -73,7 +74,6 @@ use layout::flow::{self, Flow, ImmutableFlowUtils, MutableOwnedFlowUtils};
|
|||
use layout::flow_ref::FlowRef;
|
||||
use layout::incremental::{LayoutDamageComputation, REFLOW_ENTIRE_DOCUMENT, RelayoutMode};
|
||||
use layout::layout_debug;
|
||||
use layout::opaque_node::OpaqueNodeMethods;
|
||||
use layout::parallel;
|
||||
use layout::query::{LayoutRPCImpl, LayoutThreadData, process_content_box_request, process_content_boxes_request};
|
||||
use layout::query::{process_margin_style_query, process_node_overflow_request, process_resolved_style_request};
|
||||
|
@ -84,6 +84,7 @@ use layout::traversal::{ComputeStackingRelativePositions, PreorderFlowTraversal,
|
|||
use layout::webrender_helpers::WebRenderDisplayListConverter;
|
||||
use layout::wrapper::LayoutNodeLayoutData;
|
||||
use layout_traits::LayoutThreadFactory;
|
||||
use libc::c_void;
|
||||
use metrics::{PaintTimeMetrics, ProfilerMetadataFactory};
|
||||
use msg::constellation_msg::PipelineId;
|
||||
use msg::constellation_msg::TopLevelBrowsingContextId;
|
||||
|
@ -92,8 +93,8 @@ use parking_lot::RwLock;
|
|||
use profile_traits::mem::{self, Report, ReportKind, ReportsChan};
|
||||
use profile_traits::time::{self, TimerMetadata, profile};
|
||||
use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType};
|
||||
use script_layout_interface::message::{Msg, NewLayoutThreadInfo, Reflow, ReflowGoal};
|
||||
use script_layout_interface::message::{ScriptReflow, ReflowComplete};
|
||||
use script_layout_interface::message::{Msg, NewLayoutThreadInfo, NodesFromPointQueryType, Reflow};
|
||||
use script_layout_interface::message::{ReflowComplete, ReflowGoal, ScriptReflow};
|
||||
use script_layout_interface::rpc::{LayoutRPC, MarginStyleResponse, NodeOverflowResponse, OffsetParentResponse};
|
||||
use script_layout_interface::rpc::TextIndexResponse;
|
||||
use script_layout_interface::wrapper_traits::LayoutNode;
|
||||
|
@ -120,10 +121,8 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
|||
use std::sync::mpsc::{Receiver, Sender, channel};
|
||||
use std::thread;
|
||||
use style::animation::Animation;
|
||||
use style::context::{QuirksMode, SharedStyleContext};
|
||||
use style::context::{StyleSystemOptions, ThreadLocalStyleContextCreationInfo};
|
||||
use style::context::RegisteredSpeculativePainter;
|
||||
use style::context::RegisteredSpeculativePainters;
|
||||
use style::context::{QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters};
|
||||
use style::context::{SharedStyleContext, StyleSystemOptions, ThreadLocalStyleContextCreationInfo};
|
||||
use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TElement, TNode};
|
||||
use style::driver;
|
||||
use style::error_reporting::{NullReporter, RustLogReporter};
|
||||
|
@ -519,7 +518,6 @@ impl LayoutThread {
|
|||
content_box_response: None,
|
||||
content_boxes_response: Vec::new(),
|
||||
client_rect_response: Rect::zero(),
|
||||
hit_test_response: (None, false),
|
||||
scroll_root_id_response: None,
|
||||
scroll_area_response: Rect::zero(),
|
||||
overflow_response: NodeOverflowResponse(None),
|
||||
|
@ -705,6 +703,14 @@ impl LayoutThread {
|
|||
Msg::UpdateScrollStateFromScript(state) => {
|
||||
let mut rw_data = possibly_locked_rw_data.lock();
|
||||
rw_data.scroll_offsets.insert(state.scroll_root_id, state.scroll_offset);
|
||||
|
||||
let point = Point2D::new(-state.scroll_offset.x, -state.scroll_offset.y);
|
||||
self.webrender_api.scroll_node_with_id(
|
||||
self.webrender_document,
|
||||
webrender_api::LayoutPoint::from_untyped(&point),
|
||||
state.scroll_root_id,
|
||||
webrender_api::ScrollClamping::ToContentBounds
|
||||
);
|
||||
}
|
||||
Msg::ReapStyleAndLayoutData(dead_data) => {
|
||||
unsafe {
|
||||
|
@ -1078,10 +1084,7 @@ impl LayoutThread {
|
|||
ReflowGoal::ContentBoxesQuery(_) => {
|
||||
rw_data.content_boxes_response = Vec::new();
|
||||
},
|
||||
ReflowGoal::HitTestQuery(..) => {
|
||||
rw_data.hit_test_response = (None, false);
|
||||
},
|
||||
ReflowGoal::NodesFromPoint(..) => {
|
||||
ReflowGoal::NodesFromPointQuery(..) => {
|
||||
rw_data.nodes_from_point_response = Vec::new();
|
||||
},
|
||||
ReflowGoal::NodeGeometryQuery(_) => {
|
||||
|
@ -1355,15 +1358,6 @@ impl LayoutThread {
|
|||
let node = unsafe { ServoLayoutNode::new(&node) };
|
||||
rw_data.content_boxes_response = process_content_boxes_request(node, root_flow);
|
||||
},
|
||||
ReflowGoal::HitTestQuery(client_point, update_cursor) => {
|
||||
let point = Point2D::new(Au::from_f32_px(client_point.x),
|
||||
Au::from_f32_px(client_point.y));
|
||||
let result = rw_data.display_list
|
||||
.as_ref()
|
||||
.expect("Tried to hit test with no display list")
|
||||
.hit_test(&point, &rw_data.scroll_offsets);
|
||||
rw_data.hit_test_response = (result.last().cloned(), update_cursor);
|
||||
},
|
||||
ReflowGoal::TextIndexQuery(node, mouse_x, mouse_y) => {
|
||||
let node = unsafe { ServoLayoutNode::new(&node) };
|
||||
let opaque_node = node.opaque();
|
||||
|
@ -1411,25 +1405,28 @@ impl LayoutThread {
|
|||
let node = unsafe { ServoLayoutNode::new(&node) };
|
||||
rw_data.margin_style_response = process_margin_style_query(node);
|
||||
},
|
||||
ReflowGoal::NodesFromPoint(client_point) => {
|
||||
let client_point = Point2D::new(Au::from_f32_px(client_point.x),
|
||||
Au::from_f32_px(client_point.y));
|
||||
let nodes_from_point_list = {
|
||||
let result = match rw_data.display_list {
|
||||
None => panic!("Tried to hit test without a DisplayList"),
|
||||
Some(ref display_list) => {
|
||||
display_list.hit_test(&client_point, &rw_data.scroll_offsets)
|
||||
}
|
||||
};
|
||||
|
||||
result
|
||||
ReflowGoal::NodesFromPointQuery(client_point, ref reflow_goal) => {
|
||||
let mut flags = match reflow_goal {
|
||||
&NodesFromPointQueryType::Topmost => webrender_api::HitTestFlags::empty(),
|
||||
&NodesFromPointQueryType::All => webrender_api::HitTestFlags::FIND_ALL,
|
||||
};
|
||||
|
||||
rw_data.nodes_from_point_response = nodes_from_point_list.iter()
|
||||
.rev()
|
||||
.map(|metadata| metadata.node.to_untrusted_node_address())
|
||||
// The point we get is not relative to the entire WebRender scene, but to this
|
||||
// particular pipeline, so we need to tell WebRender about that.
|
||||
flags.insert(webrender_api::HitTestFlags::POINT_RELATIVE_TO_PIPELINE_VIEWPORT);
|
||||
|
||||
let client_point = webrender_api::WorldPoint::from_untyped(&client_point);
|
||||
let results = self.webrender_api.hit_test(
|
||||
self.webrender_document,
|
||||
Some(self.id.to_webrender()),
|
||||
client_point,
|
||||
flags
|
||||
);
|
||||
|
||||
rw_data.nodes_from_point_response = results.items.iter()
|
||||
.map(|item| UntrustedNodeAddress(item.tag.0 as *const c_void))
|
||||
.collect()
|
||||
}
|
||||
},
|
||||
|
||||
ReflowGoal::Full | ReflowGoal::TickAnimations => {}
|
||||
}
|
||||
|
|
|
@ -246,6 +246,17 @@ impl PipelineId {
|
|||
webrender_api::PipelineId(namespace_id, index.get())
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
pub fn from_webrender(pipeline: webrender_api::PipelineId) -> PipelineId {
|
||||
let webrender_api::PipelineId(namespace_id, index) = pipeline;
|
||||
unsafe {
|
||||
PipelineId {
|
||||
namespace_id: PipelineNamespaceId(namespace_id),
|
||||
index: PipelineIndex(NonZero::new_unchecked(index)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn root_scroll_node(&self) -> webrender_api::ClipId {
|
||||
webrender_api::ClipId::root_scroll_node(self.to_webrender())
|
||||
}
|
||||
|
|
|
@ -10,10 +10,8 @@ use dom::attr::Attr;
|
|||
use dom::beforeunloadevent::BeforeUnloadEvent;
|
||||
use dom::bindings::callback::ExceptionHandling;
|
||||
use dom::bindings::cell::DomRefCell;
|
||||
use dom::bindings::codegen::Bindings::DOMRectBinding::DOMRectMethods;
|
||||
use dom::bindings::codegen::Bindings::DocumentBinding;
|
||||
use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyState, ElementCreationOptions};
|
||||
use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
|
||||
use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
|
||||
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
|
||||
use dom::bindings::codegen::Bindings::NodeFilterBinding::NodeFilter;
|
||||
|
@ -94,7 +92,7 @@ use dom::windowproxy::WindowProxy;
|
|||
use dom_struct::dom_struct;
|
||||
use encoding::EncodingRef;
|
||||
use encoding::all::UTF_8;
|
||||
use euclid::{Point2D, Vector2D};
|
||||
use euclid::Point2D;
|
||||
use html5ever::{LocalName, Namespace, QualName};
|
||||
use hyper::header::{Header, SetCookie};
|
||||
use hyper_serde::Serde;
|
||||
|
@ -110,14 +108,12 @@ use net_traits::pub_domains::is_pub_domain;
|
|||
use net_traits::request::RequestInit;
|
||||
use net_traits::response::HttpsState;
|
||||
use num_traits::ToPrimitive;
|
||||
use script_layout_interface::message::{Msg, ReflowGoal};
|
||||
use script_layout_interface::message::{Msg, NodesFromPointQueryType, ReflowGoal};
|
||||
use script_runtime::{CommonScriptMsg, ScriptThreadEventCategory};
|
||||
use script_thread::{MainThreadScriptMsg, ScriptThread};
|
||||
use script_traits::{AnimationState, CompositorEvent, DocumentActivity};
|
||||
use script_traits::{MouseButton, MouseEventType, MozBrowserEvent};
|
||||
use script_traits::{MsDuration, ScriptMsg, TouchpadPressurePhase};
|
||||
use script_traits::{TouchEventType, TouchId};
|
||||
use script_traits::UntrustedNodeAddress;
|
||||
use script_traits::{AnimationState, DocumentActivity, MouseButton, MouseEventType};
|
||||
use script_traits::{MozBrowserEvent, MsDuration, ScriptMsg, TouchEventType, TouchId};
|
||||
use script_traits::{TouchpadPressurePhase, UntrustedNodeAddress};
|
||||
use servo_arc::Arc;
|
||||
use servo_atoms::Atom;
|
||||
use servo_config::prefs::PREFS;
|
||||
|
@ -834,11 +830,14 @@ impl Document {
|
|||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
pub fn handle_mouse_event(&self,
|
||||
js_runtime: *mut JSRuntime,
|
||||
button: MouseButton,
|
||||
client_point: Point2D<f32>,
|
||||
mouse_event_type: MouseEventType) {
|
||||
pub fn handle_mouse_event(
|
||||
&self,
|
||||
js_runtime: *mut JSRuntime,
|
||||
_button: MouseButton,
|
||||
client_point: Point2D<f32>,
|
||||
mouse_event_type: MouseEventType,
|
||||
node_address: Option<UntrustedNodeAddress>
|
||||
) {
|
||||
let mouse_event_type_string = match mouse_event_type {
|
||||
MouseEventType::Click => "click".to_owned(),
|
||||
MouseEventType::MouseUp => "mouseup".to_owned(),
|
||||
|
@ -846,41 +845,17 @@ impl Document {
|
|||
};
|
||||
debug!("{}: at {:?}", mouse_event_type_string, client_point);
|
||||
|
||||
let node = match self.window.hit_test_query(client_point, false) {
|
||||
Some(node_address) => {
|
||||
debug!("node address is {:?}", node_address);
|
||||
unsafe {
|
||||
node::from_untrusted_node_address(js_runtime, node_address)
|
||||
}
|
||||
},
|
||||
let el = node_address.and_then(|address| {
|
||||
let node = unsafe { node::from_untrusted_node_address(js_runtime, address) };
|
||||
node.inclusive_ancestors()
|
||||
.filter_map(DomRoot::downcast::<Element>)
|
||||
.next()
|
||||
});
|
||||
let el = match el {
|
||||
Some(el) => el,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let el = match node.downcast::<Element>() {
|
||||
Some(el) => DomRoot::from_ref(el),
|
||||
None => {
|
||||
let parent = node.GetParentNode();
|
||||
match parent.and_then(DomRoot::downcast::<Element>) {
|
||||
Some(parent) => parent,
|
||||
None => return,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// If the target is an iframe, forward the event to the child document.
|
||||
if let Some(iframe) = el.downcast::<HTMLIFrameElement>() {
|
||||
if let Some(pipeline_id) = iframe.pipeline_id() {
|
||||
let rect = iframe.upcast::<Element>().GetBoundingClientRect();
|
||||
let child_origin = Vector2D::new(rect.X() as f32, rect.Y() as f32);
|
||||
let child_point = client_point - child_origin;
|
||||
|
||||
let event = CompositorEvent::MouseButtonEvent(mouse_event_type, button, child_point);
|
||||
let event = ScriptMsg::ForwardEvent(pipeline_id, event);
|
||||
self.send_to_constellation(event);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let node = el.upcast::<Node>();
|
||||
debug!("{} on {:?}", mouse_event_type_string, node.debug_str());
|
||||
// Prevent click event if form control element is disabled.
|
||||
|
@ -997,45 +972,24 @@ impl Document {
|
|||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
pub fn handle_touchpad_pressure_event(&self,
|
||||
js_runtime: *mut JSRuntime,
|
||||
client_point: Point2D<f32>,
|
||||
pressure: f32,
|
||||
phase_now: TouchpadPressurePhase) {
|
||||
let node = match self.window.hit_test_query(client_point, false) {
|
||||
Some(node_address) => unsafe {
|
||||
node::from_untrusted_node_address(js_runtime, node_address)
|
||||
},
|
||||
None => return
|
||||
pub fn handle_touchpad_pressure_event(
|
||||
&self,
|
||||
js_runtime: *mut JSRuntime,
|
||||
pressure: f32,
|
||||
phase_now: TouchpadPressurePhase,
|
||||
node_address: Option<UntrustedNodeAddress>
|
||||
) {
|
||||
let el = node_address.and_then(|address| {
|
||||
let node = unsafe { node::from_untrusted_node_address(js_runtime, address) };
|
||||
node.inclusive_ancestors()
|
||||
.filter_map(DomRoot::downcast::<Element>)
|
||||
.next()
|
||||
});
|
||||
let el = match el {
|
||||
Some(el) => el,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let el = match node.downcast::<Element>() {
|
||||
Some(el) => DomRoot::from_ref(el),
|
||||
None => {
|
||||
let parent = node.GetParentNode();
|
||||
match parent.and_then(DomRoot::downcast::<Element>) {
|
||||
Some(parent) => parent,
|
||||
None => return
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// If the target is an iframe, forward the event to the child document.
|
||||
if let Some(iframe) = el.downcast::<HTMLIFrameElement>() {
|
||||
if let Some(pipeline_id) = iframe.pipeline_id() {
|
||||
let rect = iframe.upcast::<Element>().GetBoundingClientRect();
|
||||
let child_origin = Vector2D::new(rect.X() as f32, rect.Y() as f32);
|
||||
let child_point = client_point - child_origin;
|
||||
|
||||
let event = CompositorEvent::TouchpadPressureEvent(child_point,
|
||||
pressure,
|
||||
phase_now);
|
||||
let event = ScriptMsg::ForwardEvent(pipeline_id, event);
|
||||
self.send_to_constellation(event);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let phase_before = self.touchpad_pressure_phase.get();
|
||||
self.touchpad_pressure_phase.set(phase_now);
|
||||
|
||||
|
@ -1101,10 +1055,13 @@ impl Document {
|
|||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
pub fn handle_mouse_move_event(&self,
|
||||
js_runtime: *mut JSRuntime,
|
||||
client_point: Option<Point2D<f32>>,
|
||||
prev_mouse_over_target: &MutNullableDom<Element>) {
|
||||
pub fn handle_mouse_move_event(
|
||||
&self,
|
||||
js_runtime: *mut JSRuntime,
|
||||
client_point: Option<Point2D<f32>>,
|
||||
prev_mouse_over_target: &MutNullableDom<Element>,
|
||||
node_address: Option<UntrustedNodeAddress>
|
||||
) {
|
||||
let client_point = match client_point {
|
||||
None => {
|
||||
// If there's no point, there's no target under the mouse
|
||||
|
@ -1115,31 +1072,21 @@ impl Document {
|
|||
Some(client_point) => client_point,
|
||||
};
|
||||
|
||||
let maybe_new_target = self.window.hit_test_query(client_point, true).and_then(|address| {
|
||||
let maybe_new_target = node_address.and_then(|address| {
|
||||
let node = unsafe { node::from_untrusted_node_address(js_runtime, address) };
|
||||
node.inclusive_ancestors()
|
||||
.filter_map(DomRoot::downcast::<Element>)
|
||||
.next()
|
||||
});
|
||||
|
||||
// Send mousemove event to topmost target, and forward it if it's an iframe
|
||||
if let Some(ref new_target) = maybe_new_target {
|
||||
// If the target is an iframe, forward the event to the child document.
|
||||
if let Some(iframe) = new_target.downcast::<HTMLIFrameElement>() {
|
||||
if let Some(pipeline_id) = iframe.pipeline_id() {
|
||||
let rect = iframe.upcast::<Element>().GetBoundingClientRect();
|
||||
let child_origin = Vector2D::new(rect.X() as f32, rect.Y() as f32);
|
||||
let child_point = client_point - child_origin;
|
||||
// Send mousemove event to topmost target, unless it's an iframe, in which case the
|
||||
// compositor should have also sent an event to the inner document.
|
||||
let new_target = match maybe_new_target {
|
||||
Some(ref target) => target,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let event = CompositorEvent::MouseMoveEvent(Some(child_point));
|
||||
let event = ScriptMsg::ForwardEvent(pipeline_id, event);
|
||||
self.send_to_constellation(event);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
self.fire_mouse_event(client_point, new_target.upcast(), "mousemove".to_owned());
|
||||
}
|
||||
self.fire_mouse_event(client_point, new_target.upcast(), "mousemove".to_owned());
|
||||
|
||||
// Nothing more to do here, mousemove is sent,
|
||||
// and the element under the mouse hasn't changed.
|
||||
|
@ -1197,12 +1144,14 @@ impl Document {
|
|||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
pub fn handle_touch_event(&self,
|
||||
js_runtime: *mut JSRuntime,
|
||||
event_type: TouchEventType,
|
||||
touch_id: TouchId,
|
||||
point: Point2D<f32>)
|
||||
-> TouchEventResult {
|
||||
pub fn handle_touch_event(
|
||||
&self,
|
||||
js_runtime: *mut JSRuntime,
|
||||
event_type: TouchEventType,
|
||||
touch_id: TouchId,
|
||||
point: Point2D<f32>,
|
||||
node_address: Option<UntrustedNodeAddress>
|
||||
) -> TouchEventResult {
|
||||
let TouchId(identifier) = touch_id;
|
||||
|
||||
let event_name = match event_type {
|
||||
|
@ -1212,36 +1161,16 @@ impl Document {
|
|||
TouchEventType::Cancel => "touchcancel",
|
||||
};
|
||||
|
||||
let node = match self.window.hit_test_query(point, false) {
|
||||
Some(node_address) => unsafe {
|
||||
node::from_untrusted_node_address(js_runtime, node_address)
|
||||
},
|
||||
None => return TouchEventResult::Processed(false),
|
||||
let el = node_address.and_then(|address| {
|
||||
let node = unsafe { node::from_untrusted_node_address(js_runtime, address) };
|
||||
node.inclusive_ancestors()
|
||||
.filter_map(DomRoot::downcast::<Element>)
|
||||
.next()
|
||||
});
|
||||
let el = match el {
|
||||
Some(el) => el,
|
||||
None => return TouchEventResult::Forwarded,
|
||||
};
|
||||
let el = match node.downcast::<Element>() {
|
||||
Some(el) => DomRoot::from_ref(el),
|
||||
None => {
|
||||
let parent = node.GetParentNode();
|
||||
match parent.and_then(DomRoot::downcast::<Element>) {
|
||||
Some(parent) => parent,
|
||||
None => return TouchEventResult::Processed(false),
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// If the target is an iframe, forward the event to the child document.
|
||||
if let Some(iframe) = el.downcast::<HTMLIFrameElement>() {
|
||||
if let Some(pipeline_id) = iframe.pipeline_id() {
|
||||
let rect = iframe.upcast::<Element>().GetBoundingClientRect();
|
||||
let child_origin = Vector2D::new(rect.X() as f32, rect.Y() as f32);
|
||||
let child_point = point - child_origin;
|
||||
|
||||
let event = CompositorEvent::TouchEvent(event_type, touch_id, child_point);
|
||||
let event = ScriptMsg::ForwardEvent(pipeline_id, event);
|
||||
self.send_to_constellation(event);
|
||||
}
|
||||
return TouchEventResult::Forwarded;
|
||||
}
|
||||
|
||||
let target = DomRoot::upcast::<EventTarget>(el);
|
||||
let window = &*self.window;
|
||||
|
@ -2005,8 +1934,12 @@ impl Document {
|
|||
!self.has_browsing_context || !url_has_network_scheme(&self.url())
|
||||
}
|
||||
|
||||
pub fn nodes_from_point(&self, client_point: &Point2D<f32>) -> Vec<UntrustedNodeAddress> {
|
||||
if !self.window.reflow(ReflowGoal::NodesFromPoint(*client_point), ReflowReason::Query) {
|
||||
pub fn nodes_from_point(&self,
|
||||
client_point: &Point2D<f32>,
|
||||
reflow_goal: NodesFromPointQueryType)
|
||||
-> Vec<UntrustedNodeAddress> {
|
||||
if !self.window.reflow(ReflowGoal::NodesFromPointQuery(*client_point, reflow_goal),
|
||||
ReflowReason::Query) {
|
||||
return vec!();
|
||||
};
|
||||
|
||||
|
@ -3662,12 +3595,11 @@ impl DocumentMethods for Document {
|
|||
return None;
|
||||
}
|
||||
|
||||
match self.window.hit_test_query(*point, false) {
|
||||
Some(untrusted_node_address) => {
|
||||
match self.nodes_from_point(point, NodesFromPointQueryType::Topmost).first() {
|
||||
Some(address) => {
|
||||
let js_runtime = unsafe { JS_GetRuntime(window.get_cx()) };
|
||||
|
||||
let node = unsafe {
|
||||
node::from_untrusted_node_address(js_runtime, untrusted_node_address)
|
||||
node::from_untrusted_node_address(js_runtime, *address)
|
||||
};
|
||||
let parent_node = node.GetParentNode().unwrap();
|
||||
let element_ref = node.downcast::<Element>().unwrap_or_else(|| {
|
||||
|
@ -3701,7 +3633,8 @@ impl DocumentMethods for Document {
|
|||
let js_runtime = unsafe { JS_GetRuntime(window.get_cx()) };
|
||||
|
||||
// Step 1 and Step 3
|
||||
let mut elements: Vec<DomRoot<Element>> = self.nodes_from_point(point).iter()
|
||||
let nodes = self.nodes_from_point(point, NodesFromPointQueryType::All);
|
||||
let mut elements: Vec<DomRoot<Element>> = nodes.iter()
|
||||
.flat_map(|&untrusted_node_address| {
|
||||
let node = unsafe {
|
||||
node::from_untrusted_node_address(js_runtime, untrusted_node_address)
|
||||
|
|
|
@ -1149,28 +1149,15 @@ impl Window {
|
|||
x: f32,
|
||||
y: f32,
|
||||
scroll_root_id: ClipId,
|
||||
behavior: ScrollBehavior,
|
||||
element: Option<&Element>) {
|
||||
//TODO Step 1
|
||||
let point = Point2D::new(x, y);
|
||||
let smooth = match behavior {
|
||||
ScrollBehavior::Auto => {
|
||||
element.map_or(false, |_element| {
|
||||
// TODO check computed scroll-behaviour CSS property
|
||||
true
|
||||
})
|
||||
}
|
||||
ScrollBehavior::Instant => false,
|
||||
ScrollBehavior::Smooth => true
|
||||
};
|
||||
|
||||
_behavior: ScrollBehavior,
|
||||
_element: Option<&Element>) {
|
||||
// TODO Step 1
|
||||
// TODO(mrobinson, #18709): Add smooth scrolling support to WebRender so that we can
|
||||
// properly process ScrollBehavior here.
|
||||
self.layout_chan.send(Msg::UpdateScrollStateFromScript(ScrollState {
|
||||
scroll_root_id: scroll_root_id,
|
||||
scroll_offset: Vector2D::new(-x, -y),
|
||||
})).unwrap();
|
||||
|
||||
let message = ScriptMsg::ScrollFragmentPoint(scroll_root_id, point, smooth);
|
||||
self.send_to_constellation(message);
|
||||
}
|
||||
|
||||
pub fn update_viewport_for_scroll(&self, x: f32, y: f32) {
|
||||
|
@ -1406,18 +1393,6 @@ impl Window {
|
|||
self.layout_rpc.node_geometry().client_rect
|
||||
}
|
||||
|
||||
pub fn hit_test_query(&self,
|
||||
client_point: Point2D<f32>,
|
||||
update_cursor: bool)
|
||||
-> Option<UntrustedNodeAddress> {
|
||||
if !self.reflow(ReflowGoal::HitTestQuery(client_point, update_cursor),
|
||||
ReflowReason::Query) {
|
||||
return None
|
||||
}
|
||||
|
||||
self.layout_rpc.hit_test().node_address
|
||||
}
|
||||
|
||||
pub fn scroll_area_query(&self, node: TrustedNodeAddress) -> Rect<i32> {
|
||||
if !self.reflow(ReflowGoal::NodeScrollGeometryQuery(node), ReflowReason::Query) {
|
||||
return Rect::zero();
|
||||
|
@ -1910,8 +1885,7 @@ fn debug_reflow_events(id: PipelineId, reflow_goal: &ReflowGoal, reason: &Reflow
|
|||
ReflowGoal::Full => "\tFull",
|
||||
ReflowGoal::ContentBoxQuery(_n) => "\tContentBoxQuery",
|
||||
ReflowGoal::ContentBoxesQuery(_n) => "\tContentBoxesQuery",
|
||||
ReflowGoal::HitTestQuery(..) => "\tHitTestQuery",
|
||||
ReflowGoal::NodesFromPoint(..) => "\tNodesFromPoint",
|
||||
ReflowGoal::NodesFromPointQuery(..) => "\tNodesFromPointQuery",
|
||||
ReflowGoal::NodeGeometryQuery(_n) => "\tNodeGeometryQuery",
|
||||
ReflowGoal::NodeOverflowQuery(_n) => "\tNodeOverFlowQuery",
|
||||
ReflowGoal::NodeScrollGeometryQuery(_n) => "\tNodeScrollGeometryQuery",
|
||||
|
|
|
@ -1010,7 +1010,7 @@ impl ScriptThread {
|
|||
}
|
||||
FromConstellation(ConstellationControlMsg::SendEvent(
|
||||
_,
|
||||
MouseMoveEvent(_))) => {
|
||||
MouseMoveEvent(..))) => {
|
||||
match mouse_move_event_index {
|
||||
None => {
|
||||
mouse_move_event_index = Some(sequential.len());
|
||||
|
@ -2187,11 +2187,11 @@ impl ScriptThread {
|
|||
self.handle_resize_event(pipeline_id, new_size, size_type);
|
||||
}
|
||||
|
||||
MouseButtonEvent(event_type, button, point) => {
|
||||
self.handle_mouse_event(pipeline_id, event_type, button, point);
|
||||
MouseButtonEvent(event_type, button, point, node_address) => {
|
||||
self.handle_mouse_event(pipeline_id, event_type, button, point, node_address);
|
||||
}
|
||||
|
||||
MouseMoveEvent(point) => {
|
||||
MouseMoveEvent(point, node_address) => {
|
||||
let document = match { self.documents.borrow().find_document(pipeline_id) } {
|
||||
Some(document) => document,
|
||||
None => return warn!("Message sent to closed pipeline {}.", pipeline_id),
|
||||
|
@ -2201,7 +2201,8 @@ impl ScriptThread {
|
|||
let prev_mouse_over_target = self.topmost_mouse_over_target.get();
|
||||
|
||||
document.handle_mouse_move_event(self.js_runtime.rt(), point,
|
||||
&self.topmost_mouse_over_target);
|
||||
&self.topmost_mouse_over_target,
|
||||
node_address);
|
||||
|
||||
// Short-circuit if nothing changed
|
||||
if self.topmost_mouse_over_target.get() == prev_mouse_over_target {
|
||||
|
@ -2244,8 +2245,14 @@ impl ScriptThread {
|
|||
}
|
||||
}
|
||||
}
|
||||
TouchEvent(event_type, identifier, point) => {
|
||||
let touch_result = self.handle_touch_event(pipeline_id, event_type, identifier, point);
|
||||
TouchEvent(event_type, identifier, point, node_address) => {
|
||||
let touch_result = self.handle_touch_event(
|
||||
pipeline_id,
|
||||
event_type,
|
||||
identifier,
|
||||
point,
|
||||
node_address
|
||||
);
|
||||
match (event_type, touch_result) {
|
||||
(TouchEventType::Down, TouchEventResult::Processed(handled)) => {
|
||||
let result = if handled {
|
||||
|
@ -2263,12 +2270,17 @@ impl ScriptThread {
|
|||
}
|
||||
}
|
||||
|
||||
TouchpadPressureEvent(point, pressure, phase) => {
|
||||
TouchpadPressureEvent(_point, pressure, phase, node_address) => {
|
||||
let doc = match { self.documents.borrow().find_document(pipeline_id) } {
|
||||
Some(doc) => doc,
|
||||
None => return warn!("Message sent to closed pipeline {}.", pipeline_id),
|
||||
};
|
||||
doc.handle_touchpad_pressure_event(self.js_runtime.rt(), point, pressure, phase);
|
||||
doc.handle_touchpad_pressure_event(
|
||||
self.js_runtime.rt(),
|
||||
pressure,
|
||||
phase,
|
||||
node_address
|
||||
);
|
||||
}
|
||||
|
||||
KeyEvent(ch, key, state, modifiers) => {
|
||||
|
@ -2281,24 +2293,35 @@ impl ScriptThread {
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_mouse_event(&self,
|
||||
pipeline_id: PipelineId,
|
||||
mouse_event_type: MouseEventType,
|
||||
button: MouseButton,
|
||||
point: Point2D<f32>) {
|
||||
fn handle_mouse_event(
|
||||
&self,
|
||||
pipeline_id: PipelineId,
|
||||
mouse_event_type: MouseEventType,
|
||||
button: MouseButton,
|
||||
point: Point2D<f32>,
|
||||
node_address: Option<UntrustedNodeAddress>
|
||||
) {
|
||||
let document = match { self.documents.borrow().find_document(pipeline_id) } {
|
||||
Some(document) => document,
|
||||
None => return warn!("Message sent to closed pipeline {}.", pipeline_id),
|
||||
};
|
||||
document.handle_mouse_event(self.js_runtime.rt(), button, point, mouse_event_type);
|
||||
document.handle_mouse_event(
|
||||
self.js_runtime.rt(),
|
||||
button,
|
||||
point,
|
||||
mouse_event_type,
|
||||
node_address
|
||||
);
|
||||
}
|
||||
|
||||
fn handle_touch_event(&self,
|
||||
pipeline_id: PipelineId,
|
||||
event_type: TouchEventType,
|
||||
identifier: TouchId,
|
||||
point: Point2D<f32>)
|
||||
-> TouchEventResult {
|
||||
fn handle_touch_event(
|
||||
&self,
|
||||
pipeline_id: PipelineId,
|
||||
event_type: TouchEventType,
|
||||
identifier: TouchId,
|
||||
point: Point2D<f32>,
|
||||
node_address: Option<UntrustedNodeAddress>
|
||||
) -> TouchEventResult {
|
||||
let document = match { self.documents.borrow().find_document(pipeline_id) } {
|
||||
Some(document) => document,
|
||||
None => {
|
||||
|
@ -2306,7 +2329,13 @@ impl ScriptThread {
|
|||
return TouchEventResult::Processed(true);
|
||||
},
|
||||
};
|
||||
document.handle_touch_event(self.js_runtime.rt(), event_type, identifier, point)
|
||||
document.handle_touch_event(
|
||||
self.js_runtime.rt(),
|
||||
event_type,
|
||||
identifier,
|
||||
point,
|
||||
node_address
|
||||
)
|
||||
}
|
||||
|
||||
/// https://html.spec.whatwg.org/multipage/#navigating-across-documents
|
||||
|
|
|
@ -100,6 +100,11 @@ pub enum Msg {
|
|||
SetNavigationStart(f64),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum NodesFromPointQueryType {
|
||||
All,
|
||||
Topmost,
|
||||
}
|
||||
|
||||
/// Any query to perform with this reflow.
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
@ -109,7 +114,6 @@ pub enum ReflowGoal {
|
|||
ContentBoxQuery(TrustedNodeAddress),
|
||||
ContentBoxesQuery(TrustedNodeAddress),
|
||||
NodeOverflowQuery(TrustedNodeAddress),
|
||||
HitTestQuery(Point2D<f32>, bool),
|
||||
NodeScrollRootIdQuery(TrustedNodeAddress),
|
||||
NodeGeometryQuery(TrustedNodeAddress),
|
||||
NodeScrollGeometryQuery(TrustedNodeAddress),
|
||||
|
@ -117,7 +121,7 @@ pub enum ReflowGoal {
|
|||
OffsetParentQuery(TrustedNodeAddress),
|
||||
MarginStyleQuery(TrustedNodeAddress),
|
||||
TextIndexQuery(TrustedNodeAddress, i32, i32),
|
||||
NodesFromPoint(Point2D<f32>),
|
||||
NodesFromPointQuery(Point2D<f32>, NodesFromPointQueryType),
|
||||
}
|
||||
|
||||
impl ReflowGoal {
|
||||
|
@ -125,9 +129,8 @@ impl ReflowGoal {
|
|||
/// be present or false if it only needs stacking-relative positions.
|
||||
pub fn needs_display_list(&self) -> bool {
|
||||
match *self {
|
||||
ReflowGoal::NodesFromPoint(..) | ReflowGoal::HitTestQuery(..) |
|
||||
ReflowGoal::TextIndexQuery(..) | ReflowGoal::TickAnimations |
|
||||
ReflowGoal::Full => true,
|
||||
ReflowGoal::NodesFromPointQuery(..) | ReflowGoal::TextIndexQuery(..) |
|
||||
ReflowGoal::TickAnimations | ReflowGoal::Full => true,
|
||||
ReflowGoal::ContentBoxQuery(_) | ReflowGoal::ContentBoxesQuery(_) |
|
||||
ReflowGoal::NodeGeometryQuery(_) | ReflowGoal::NodeScrollGeometryQuery(_) |
|
||||
ReflowGoal::NodeOverflowQuery(_) | ReflowGoal::NodeScrollRootIdQuery(_) |
|
||||
|
@ -141,12 +144,13 @@ impl ReflowGoal {
|
|||
pub fn needs_display(&self) -> bool {
|
||||
match *self {
|
||||
ReflowGoal::MarginStyleQuery(_) | ReflowGoal::TextIndexQuery(..) |
|
||||
ReflowGoal::HitTestQuery(..) | ReflowGoal::ContentBoxQuery(_) |
|
||||
ReflowGoal::ContentBoxesQuery(_) | ReflowGoal::NodeGeometryQuery(_) |
|
||||
ReflowGoal::NodeScrollGeometryQuery(_) | ReflowGoal::NodeOverflowQuery(_) |
|
||||
ReflowGoal::NodeScrollRootIdQuery(_) | ReflowGoal::ResolvedStyleQuery(..) |
|
||||
ReflowGoal::OffsetParentQuery(_) | ReflowGoal::NodesFromPoint(..) => false,
|
||||
ReflowGoal::Full | ReflowGoal::TickAnimations => true,
|
||||
ReflowGoal::ContentBoxQuery(_) | ReflowGoal::ContentBoxesQuery(_) |
|
||||
ReflowGoal::NodeGeometryQuery(_) | ReflowGoal::NodeScrollGeometryQuery(_) |
|
||||
ReflowGoal::NodeOverflowQuery(_) | ReflowGoal::NodeScrollRootIdQuery(_) |
|
||||
ReflowGoal::ResolvedStyleQuery(..) |
|
||||
ReflowGoal::OffsetParentQuery(_) => false,
|
||||
ReflowGoal::NodesFromPointQuery(..) | ReflowGoal::Full |
|
||||
ReflowGoal::TickAnimations => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,17 +29,15 @@ pub trait LayoutRPC {
|
|||
fn node_scroll_area(&self) -> NodeGeometryResponse;
|
||||
/// Requests the scroll root id of this node. Used by APIs such as `scrollTop`
|
||||
fn node_scroll_root_id(&self) -> NodeScrollRootIdResponse;
|
||||
/// Requests the node containing the point of interest
|
||||
fn hit_test(&self) -> HitTestResponse;
|
||||
/// Query layout for the resolved value of a given CSS property
|
||||
fn resolved_style(&self) -> ResolvedStyleResponse;
|
||||
fn offset_parent(&self) -> OffsetParentResponse;
|
||||
/// Query layout for the resolve values of the margin properties for an element.
|
||||
fn margin_style(&self) -> MarginStyleResponse;
|
||||
fn text_index(&self) -> TextIndexResponse;
|
||||
/// Requests the list of nodes from the given point.
|
||||
fn nodes_from_point_response(&self) -> Vec<UntrustedNodeAddress>;
|
||||
|
||||
fn text_index(&self) -> TextIndexResponse;
|
||||
}
|
||||
|
||||
pub struct ContentBoxResponse(pub Option<Rect<Au>>);
|
||||
|
@ -54,10 +52,6 @@ pub struct NodeOverflowResponse(pub Option<Point2D<overflow_x::computed_value::T
|
|||
|
||||
pub struct NodeScrollRootIdResponse(pub ClipId);
|
||||
|
||||
pub struct HitTestResponse {
|
||||
pub node_address: Option<UntrustedNodeAddress>,
|
||||
}
|
||||
|
||||
pub struct ResolvedStyleResponse(pub String);
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
|
@ -68,6 +68,7 @@ use std::sync::Arc;
|
|||
use std::sync::mpsc::{Receiver, Sender, RecvTimeoutError};
|
||||
use style_traits::CSSPixel;
|
||||
use style_traits::SpeculativePainter;
|
||||
use style_traits::cursor::Cursor;
|
||||
use webdriver_msg::{LoadStatus, WebDriverScriptCommand};
|
||||
use webrender_api::{ClipId, DevicePixel, DocumentId, ImageKey};
|
||||
use webvr_traits::{WebVREvent, WebVRMsg};
|
||||
|
@ -434,13 +435,13 @@ pub enum CompositorEvent {
|
|||
/// The window was resized.
|
||||
ResizeEvent(WindowSizeData, WindowSizeType),
|
||||
/// A mouse button state changed.
|
||||
MouseButtonEvent(MouseEventType, MouseButton, Point2D<f32>),
|
||||
MouseButtonEvent(MouseEventType, MouseButton, Point2D<f32>, Option<UntrustedNodeAddress>),
|
||||
/// The mouse was moved over a point (or was moved out of the recognizable region).
|
||||
MouseMoveEvent(Option<Point2D<f32>>),
|
||||
MouseMoveEvent(Option<Point2D<f32>>, Option<UntrustedNodeAddress>),
|
||||
/// A touch event was generated with a touch ID and location.
|
||||
TouchEvent(TouchEventType, TouchId, Point2D<f32>),
|
||||
TouchEvent(TouchEventType, TouchId, Point2D<f32>, Option<UntrustedNodeAddress>),
|
||||
/// Touchpad pressure event
|
||||
TouchpadPressureEvent(Point2D<f32>, f32, TouchpadPressurePhase),
|
||||
TouchpadPressureEvent(Point2D<f32>, f32, TouchpadPressurePhase, Option<UntrustedNodeAddress>),
|
||||
/// A key was pressed.
|
||||
KeyEvent(Option<char>, Key, KeyState, KeyModifiers),
|
||||
}
|
||||
|
@ -798,6 +799,10 @@ pub enum ConstellationMsg {
|
|||
CloseBrowser(TopLevelBrowsingContextId),
|
||||
/// Make browser visible.
|
||||
SelectBrowser(TopLevelBrowsingContextId),
|
||||
/// Forward an event to the script task of the given pipeline.
|
||||
ForwardEvent(PipelineId, CompositorEvent),
|
||||
/// Requesting a change to the onscreen cursor.
|
||||
SetCursor(Cursor),
|
||||
}
|
||||
|
||||
/// Resources required by workerglobalscopes
|
||||
|
|
|
@ -27,7 +27,6 @@ use servo_url::ServoUrl;
|
|||
use style_traits::CSSPixel;
|
||||
use style_traits::cursor::Cursor;
|
||||
use style_traits::viewport::ViewportConstraints;
|
||||
use webrender_api::ClipId;
|
||||
|
||||
/// Messages from the layout to the constellation.
|
||||
#[derive(Deserialize, Serialize)]
|
||||
|
@ -134,8 +133,6 @@ pub enum ScriptMsg {
|
|||
SetFinalUrl(ServoUrl),
|
||||
/// Check if an alert dialog box should be presented
|
||||
Alert(String, IpcSender<bool>),
|
||||
/// Scroll a page in a window
|
||||
ScrollFragmentPoint(ClipId, Point2D<f32>, bool),
|
||||
/// Set title of current page
|
||||
/// https://html.spec.whatwg.org/multipage/#document.title
|
||||
SetTitle(Option<String>),
|
||||
|
|
|
@ -35,6 +35,15 @@ macro_rules! define_cursor {
|
|||
_ => Err(())
|
||||
}
|
||||
}
|
||||
|
||||
/// From the C u8 value, get the corresponding Cursor enum.
|
||||
pub fn from_u8(value: u8) -> Result<Cursor, ()> {
|
||||
match value {
|
||||
$( $c_value => Ok(Cursor::$c_variant), )+
|
||||
$( #[cfg(feature = "gecko")] $g_value => Ok(Cursor::$g_variant), )+
|
||||
_ => Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCss for Cursor {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue