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
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue