servo: Merge #10034 - Implement Document#elementsFromPoint (from rilut:implement-elementsfrompoint); r=emilio
authorRizky Luthfianto <mrluthfianto@gmail.com>
Mon, 04 Apr 2016 12:19:09 +0500
changeset 338403 cd1371a3f59874967b2d27bdc2d233e2690daebe
parent 338402 7d44c01c4fa2510762cef864d3cd5909d136108c
child 338404 db1cadac173c0704bb13db5e6599806c3785676a
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
servo: Merge #10034 - Implement Document#elementsFromPoint (from rilut:implement-elementsfrompoint); r=emilio Fixes #9859. I'm trying to implement Document#elementsFromPoint, which I need to reuse the `get_nodes_under_mouse` and `mouse_over` function which have been removed a days ago in #9715. So I added it back while I'm not sure if my implementation is correct. Any advice will be greatly appreciated. Source-Repo: https://github.com/servo/servo Source-Revision: 241518a7d2c26da421d0273f101550215576c5a7
servo/components/layout/query.rs
servo/components/script/dom/document.rs
servo/components/script/dom/webidls/Document.webidl
servo/components/script/layout_interface.rs
--- a/servo/components/layout/query.rs
+++ b/servo/components/layout/query.rs
@@ -15,16 +15,17 @@ use fragment::{Fragment, FragmentBorderB
 use gfx::display_list::OpaqueNode;
 use layout_thread::LayoutThreadData;
 use msg::constellation_msg::ConstellationChan;
 use opaque_node::OpaqueNodeMethods;
 use script::layout_interface::{ContentBoxResponse, ContentBoxesResponse, NodeGeometryResponse};
 use script::layout_interface::{HitTestResponse, LayoutRPC, OffsetParentResponse};
 use script::layout_interface::{ResolvedStyleResponse, ScriptLayoutChan, MarginStyleResponse};
 use script_traits::LayoutMsg as ConstellationMsg;
+use script_traits::UntrustedNodeAddress;
 use sequential;
 use std::cmp::{min, max};
 use std::ops::Deref;
 use std::sync::{Arc, Mutex};
 use string_cache::Atom;
 use style::computed_values;
 use style::logical_geometry::{WritingMode, BlockFlowDirection, InlineBaseDirection};
 use style::properties::ComputedValues;
@@ -80,16 +81,34 @@ impl LayoutRPC for LayoutRPCImpl {
             let ConstellationChan(ref constellation_chan) = rw_data.constellation_chan;
             constellation_chan.send(ConstellationMsg::SetCursor(cursor)).unwrap();
         }
         HitTestResponse {
             node_address: result.map(|dim| dim.node.to_untrusted_node_address()),
         }
     }
 
+    fn nodes_from_point(&self, point: Point2D<f32>) -> Vec<UntrustedNodeAddress> {
+        let point = Point2D::new(Au::from_f32_px(point.x), Au::from_f32_px(point.y));
+        let nodes_from_point_list = {
+            let &LayoutRPCImpl(ref rw_data) = self;
+            let rw_data = rw_data.lock().unwrap();
+            let result = match rw_data.display_list {
+                None => panic!("Tried to hit test without a DisplayList"),
+                Some(ref display_list) => display_list.hit_test(point),
+            };
+
+            result
+        };
+
+        nodes_from_point_list.iter()
+           .map(|metadata| metadata.node.to_untrusted_node_address())
+           .collect()
+    }
+
     fn node_geometry(&self) -> NodeGeometryResponse {
         let &LayoutRPCImpl(ref rw_data) = self;
         let rw_data = rw_data.lock().unwrap();
         NodeGeometryResponse {
             client_rect: rw_data.client_rect_response
         }
     }
 
--- a/servo/components/script/dom/document.rs
+++ b/servo/components/script/dom/document.rs
@@ -89,16 +89,17 @@ use msg::constellation_msg::{ALT, CONTRO
 use msg::constellation_msg::{ConstellationChan, Key, KeyModifiers, KeyState};
 use msg::constellation_msg::{PipelineId, SubpageId};
 use net_traits::ControlMsg::{GetCookiesForUrl, SetCookiesForUrl};
 use net_traits::CookieSource::NonHTTP;
 use net_traits::response::HttpsState;
 use net_traits::{AsyncResponseTarget, PendingAsyncLoad};
 use num::ToPrimitive;
 use script_thread::{MainThreadScriptChan, MainThreadScriptMsg, Runnable, ScriptChan};
+use script_traits::UntrustedNodeAddress;
 use script_traits::{AnimationState, MouseButton, MouseEventType, MozBrowserEvent};
 use script_traits::{ScriptMsg as ConstellationMsg, ScriptToCompositorMsg};
 use script_traits::{TouchEventType, TouchId};
 use std::ascii::AsciiExt;
 use std::borrow::ToOwned;
 use std::boxed::FnBox;
 use std::cell::{Cell, Ref, RefMut};
 use std::collections::hash_map::Entry::{Occupied, Vacant};
@@ -1475,16 +1476,22 @@ impl Document {
             match &*url.scheme {
                 "ftp" | "http" | "https" => true,
                 _ => false,
             }
         }
 
         self.browsing_context.is_none() || !url_has_network_scheme(&self.url)
     }
+
+    pub fn nodes_from_point(&self, page_point: &Point2D<f32>) -> Vec<UntrustedNodeAddress> {
+        assert!(self.GetDocumentElement().is_some());
+
+        self.window.layout().nodes_from_point(*page_point)
+    }
 }
 
 #[derive(PartialEq, HeapSizeOf)]
 pub enum DocumentSource {
     FromParser,
     NotFromParser,
 }
 
@@ -2596,16 +2603,50 @@ impl DocumentMethods for Document {
                     parent_node.downcast::<Element>().unwrap()
                 });
 
                 Some(Root::from_ref(element_ref))
             },
             None => self.GetDocumentElement()
         }
     }
+
+    #[allow(unsafe_code)]
+    // https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint
+    fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<Root<Element>> {
+        let x = *x as f32;
+        let y = *y as f32;
+        let point = &Point2D { x: x, y: y };
+        let window = window_from_node(self);
+        let viewport = window.window_size().unwrap().visible_viewport;
+
+        // Step 2
+        if x < 0.0 || y < 0.0 || x > viewport.width.get() || y > viewport.height.get() {
+            return vec!();
+        }
+
+        let js_runtime = unsafe { JS_GetRuntime(window.get_cx()) };
+
+        // Step 1 and Step 3
+        let mut elements: Vec<Root<Element>> = self.nodes_from_point(point).iter()
+            .flat_map(|&untrusted_node_address| {
+                let node = node::from_untrusted_node_address(js_runtime, untrusted_node_address);
+                Root::downcast::<Element>(node)
+        }).collect();
+
+        // Step 4
+        if let Some(root_element) = self.GetDocumentElement() {
+            if elements.last() != Some(&root_element) {
+                elements.push(root_element);
+            }
+        }
+
+        // Step 5
+        elements
+    }
 }
 
 fn is_scheme_host_port_tuple(url: &Url) -> bool {
     url.host().is_some() && url.port_or_default().is_some()
 }
 
 fn update_with_current_time(marker: &Cell<u64>) {
     if marker.get() == Default::default() {
@@ -2667,8 +2708,9 @@ pub enum FocusType {
     Parent,     // Focusing a parent element (an iframe)
 }
 
 /// Focus events
 pub enum FocusEventType {
     Focus,      // Element gained focus. Doesn't bubble.
     Blur,       // Element lost focus. Doesn't bubble.
 }
+
--- a/servo/components/script/dom/webidls/Document.webidl
+++ b/servo/components/script/dom/webidls/Document.webidl
@@ -179,14 +179,15 @@ partial interface Document {
                         double screenY);
 
       TouchList createTouchList(Touch... touches);
 };
 
 // https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint
 partial interface Document {
   Element? elementFromPoint(double x, double y);
+  sequence<Element> elementsFromPoint(double x, double y);
 };
 
 // https://drafts.csswg.org/cssom/#extensions-to-the-document-interface
 partial interface Document {
   [SameObject] readonly attribute StyleSheetList styleSheets;
 };
--- a/servo/components/script/layout_interface.rs
+++ b/servo/components/script/layout_interface.rs
@@ -108,16 +108,18 @@ pub trait LayoutRPC {
     fn node_scroll_area(&self) -> NodeGeometryResponse;
     /// 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 nodes_from_point(&self, point: Point2D<f32>) -> Vec<UntrustedNodeAddress>;
 }
 
 #[derive(Clone)]
 pub struct MarginStyleResponse {
     pub top: margin_top::computed_value::T,
     pub right: margin_right::computed_value::T,
     pub bottom: margin_bottom::computed_value::T,
     pub left: margin_left::computed_value::T,