diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2017-10-17 17:09:25 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-10-17 17:09:25 -0500 |
commit | ca08271345f78fa881c174545f5b69a8ccb78143 (patch) | |
tree | 6e1244687e960df40313b3a24a167135654dae31 /components/script/dom/document.rs | |
parent | 0e62a5829b7c29ae2667a21a439aff1e89201bf3 (diff) | |
parent | b5d51dd2636935471447fc741ffbb95c62e37f94 (diff) | |
download | servo-ca08271345f78fa881c174545f5b69a8ccb78143.tar.gz servo-ca08271345f78fa881c174545f5b69a8ccb78143.zip |
Auto merge of #18704 - mrobinson:wr-hit-testing, r=jdm,glennw,mbrubeck
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.
<!-- Please describe your changes on the following line: -->
---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [ ] These changes fix #__ (github issue number if applicable).
<!-- Either: -->
- [ ] There are tests for these changes OR
- [x] These changes do not require tests because they should not change behavior.
<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->
<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/18704)
<!-- Reviewable:end -->
Diffstat (limited to 'components/script/dom/document.rs')
-rw-r--r-- | components/script/dom/document.rs | 227 |
1 files changed, 80 insertions, 147 deletions
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index b04b681afca..fd8bf37a897 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -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 - }; - - 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 - } - }, + 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, }; - // 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; - - let event = CompositorEvent::MouseMoveEvent(Some(child_point)); - let event = ScriptMsg::ForwardEvent(pipeline_id, event); - self.send_to_constellation(event); - } - return; - } + // 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, + }; - 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,37 +1161,17 @@ 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 = 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), - } - }, + 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, }; - // 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) |