From 162ecd0aac1ce8cd924b4f7853f7bfe6ca015348 Mon Sep 17 00:00:00 2001 From: Till Schneidereit Date: Sun, 19 Jul 2015 15:04:50 +0200 Subject: [PATCH] Implement client{Top, Left, Height, Width} element properties --- components/layout/layout_task.rs | 59 +++++++++- components/script/dom/element.rs | 20 ++++ components/script/dom/node.rs | 5 + components/script/dom/webidls/Element.webidl | 5 + components/script/dom/window.rs | 9 ++ components/script/layout_interface.rs | 6 + tests/wpt/mozilla/meta/MANIFEST.json | 6 + .../mozilla/client-top-left-height-width.html | 105 ++++++++++++++++++ 8 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 tests/wpt/mozilla/tests/mozilla/client-top-left-height-width.html diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index 860762aecca7..dac4b40e6c37 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -52,7 +52,7 @@ use net_traits::{load_bytes_iter, PendingAsyncLoad}; use net_traits::image_cache_task::{ImageCacheTask, ImageCacheResult, ImageCacheChan}; use script::dom::bindings::js::LayoutJS; use script::dom::node::{LayoutData, Node}; -use script::layout_interface::{Animation, ContentBoxResponse, ContentBoxesResponse}; +use script::layout_interface::{Animation, ContentBoxResponse, ContentBoxesResponse, NodeGeometryResponse}; use script::layout_interface::{HitTestResponse, LayoutChan, LayoutRPC, MouseOverResponse}; use script::layout_interface::{NewLayoutTaskInfo, Msg, Reflow, ReflowGoal, ReflowQueryType}; use script::layout_interface::{ScriptLayoutChan, ScriptReflow, TrustedNodeAddress}; @@ -126,6 +126,9 @@ pub struct LayoutTaskData { /// A queued response for the content boxes of a node. pub content_boxes_response: Vec>, + /// A queued response for the client {top, left, width, height} of a node. + pub client_rect_response: Rect, + /// The list of currently-running animations. pub running_animations: Vec, @@ -358,6 +361,7 @@ impl LayoutTask { generation: 0, content_box_response: Rect::zero(), content_boxes_response: Vec::new(), + client_rect_response: Rect::zero(), running_animations: Vec::new(), visible_rects: Arc::new(HashMap::with_hash_state(Default::default())), new_animations_receiver: new_animations_receiver, @@ -843,6 +847,16 @@ impl LayoutTask { rw_data.content_boxes_response = iterator.rects; } + fn process_node_geometry_request<'a>(&'a self, + requested_node: TrustedNodeAddress, + layout_root: &mut FlowRef, + rw_data: &mut RWGuard<'a>) { + let requested_node: OpaqueNode = OpaqueNodeMethods::from_script_node(requested_node); + let mut iterator = FragmentLocatingFragmentIterator::new(requested_node); + sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator); + rw_data.client_rect_response = iterator.client_rect; + } + fn compute_abs_pos_and_build_display_list<'a>(&'a self, data: &Reflow, layout_root: &mut FlowRef, @@ -1039,6 +1053,9 @@ impl LayoutTask { ReflowQueryType::ContentBoxesQuery(node) => { self.process_content_boxes_request(node, &mut root_flow, &mut rw_data) } + ReflowQueryType::NodeGeometryQuery(node) => { + self.process_node_geometry_request(node, &mut root_flow, &mut rw_data) + } ReflowQueryType::NoQuery => {} } @@ -1278,6 +1295,14 @@ impl LayoutRPC for LayoutRPCImpl { ContentBoxesResponse(rw_data.content_boxes_response.clone()) } + 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 + } + } + /// Requests the node containing the point of interest. fn hit_test(&self, _: TrustedNodeAddress, point: Point2D) -> Result { let point = Point2D::new(Au::from_f32_px(point.x), Au::from_f32_px(point.y)); @@ -1395,6 +1420,38 @@ impl FragmentBorderBoxIterator for CollectingFragmentBorderBoxIterator { } } +struct FragmentLocatingFragmentIterator { + node_address: OpaqueNode, + client_rect: Rect, +} + +impl FragmentLocatingFragmentIterator { + fn new(node_address: OpaqueNode) -> FragmentLocatingFragmentIterator { + FragmentLocatingFragmentIterator { + node_address: node_address, + client_rect: Rect::zero() + } + } +} + +impl FragmentBorderBoxIterator for FragmentLocatingFragmentIterator { + fn process(&mut self, fragment: &Fragment, border_box: &Rect) { + let border_style_struct = fragment.style.get_border(); + let top_width = border_style_struct.border_top_width; + let right_width = border_style_struct.border_right_width; + let bottom_width = border_style_struct.border_bottom_width; + let left_width = border_style_struct.border_left_width; + self.client_rect.origin.y = top_width.to_px(); + self.client_rect.origin.x = left_width.to_px(); + self.client_rect.size.width = (border_box.size.width - left_width - right_width).to_px(); + self.client_rect.size.height = (border_box.size.height - top_width - bottom_width).to_px(); + } + + fn should_process(&mut self, fragment: &Fragment) -> bool { + fragment.node == self.node_address + } +} + // The default computed value for background-color is transparent (see // http://dev.w3.org/csswg/css-backgrounds/#background-color). However, we // need to propagate the background color from the root HTML/Body diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index ff9864c88b8f..e2e260a2bb8e 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -1296,6 +1296,26 @@ impl<'a> ElementMethods for &'a Element { rect.origin.x + rect.size.width) } + fn ClientTop(self) -> i32 { + let node = NodeCast::from_ref(self); + node.get_client_rect().origin.y + } + + fn ClientLeft(self) -> i32 { + let node = NodeCast::from_ref(self); + node.get_client_rect().origin.x + } + + fn ClientWidth(self) -> i32 { + let node = NodeCast::from_ref(self); + node.get_client_rect().size.width + } + + fn ClientHeight(self) -> i32 { + let node = NodeCast::from_ref(self); + node.get_client_rect().size.height + } + // https://dvcs.w3.org/hg/innerhtml/raw-file/tip/index.html#widl-Element-innerHTML fn GetInnerHTML(self) -> Fallible { //XXX TODO: XML case diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index d7432c570b0b..790b2b3b249e 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -504,6 +504,7 @@ pub trait NodeHelpers { fn get_bounding_content_box(self) -> Rect; fn get_content_boxes(self) -> Vec>; + fn get_client_rect(self) -> Rect; fn before(self, nodes: Vec) -> ErrorResult; fn after(self, nodes: Vec) -> ErrorResult; @@ -806,6 +807,10 @@ impl<'a> NodeHelpers for &'a Node { window_from_node(self).r().content_boxes_query(self.to_trusted_node_address()) } + fn get_client_rect(self) -> Rect { + window_from_node(self).r().client_rect_query(self.to_trusted_node_address()) + } + // https://dom.spec.whatwg.org/#dom-childnode-before fn before(self, nodes: Vec) -> ErrorResult { match self.parent_node.get() { diff --git a/components/script/dom/webidls/Element.webidl b/components/script/dom/webidls/Element.webidl index cbaf599d25eb..4dce68f6ba4f 100644 --- a/components/script/dom/webidls/Element.webidl +++ b/components/script/dom/webidls/Element.webidl @@ -59,6 +59,11 @@ interface Element : Node { partial interface Element { DOMRectList getClientRects(); DOMRect getBoundingClientRect(); + + readonly attribute long clientTop; + readonly attribute long clientLeft; + readonly attribute long clientWidth; + readonly attribute long clientHeight; }; // https://domparsing.spec.whatwg.org/#extensions-to-the-element-interface diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 845f7ea75215..c4ed0ea60720 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -534,6 +534,7 @@ pub trait WindowHelpers { fn layout(&self) -> &LayoutRPC; fn content_box_query(self, content_box_request: TrustedNodeAddress) -> Rect; fn content_boxes_query(self, content_boxes_request: TrustedNodeAddress) -> Vec>; + fn client_rect_query(self, node_geometry_request: TrustedNodeAddress) -> Rect; fn handle_reflow_complete_msg(self, reflow_id: u32); fn handle_resize_inactive_msg(self, new_size: WindowSizeData); fn set_fragment_name(self, fragment: Option); @@ -764,6 +765,13 @@ impl<'a> WindowHelpers for &'a Window { rects } + fn client_rect_query(self, node_geometry_request: TrustedNodeAddress) -> Rect { + self.reflow(ReflowGoal::ForScriptQuery, + ReflowQueryType::NodeGeometryQuery(node_geometry_request), + ReflowReason::Query); + self.layout_rpc.node_geometry().client_rect + } + fn handle_reflow_complete_msg(self, reflow_id: u32) { let last_reflow_id = self.last_reflow_id.get(); if last_reflow_id == reflow_id { @@ -1072,6 +1080,7 @@ fn debug_reflow_events(goal: &ReflowGoal, query_type: &ReflowQueryType, reason: ReflowQueryType::NoQuery => "\tNoQuery", ReflowQueryType::ContentBoxQuery(_n) => "\tContentBoxQuery", ReflowQueryType::ContentBoxesQuery(_n) => "\tContentBoxesQuery", + ReflowQueryType::NodeGeometryQuery(_n) => "\tNodeGeometryQuery", }); debug_msg.push_str(match *reason { diff --git a/components/script/layout_interface.rs b/components/script/layout_interface.rs index 3c26f0622148..60270a80c4b0 100644 --- a/components/script/layout_interface.rs +++ b/components/script/layout_interface.rs @@ -95,6 +95,8 @@ pub trait LayoutRPC { fn content_box(&self) -> ContentBoxResponse; /// Requests the dimensions of all the content boxes, as in the `getClientRects()` call. fn content_boxes(&self) -> ContentBoxesResponse; + /// Requests the clientTop. + fn node_geometry(&self) -> NodeGeometryResponse; /// Requests the node containing the point of interest fn hit_test(&self, node: TrustedNodeAddress, point: Point2D) -> Result; fn mouse_over(&self, node: TrustedNodeAddress, point: Point2D) -> Result; @@ -102,6 +104,9 @@ pub trait LayoutRPC { pub struct ContentBoxResponse(pub Rect); pub struct ContentBoxesResponse(pub Vec>); +pub struct NodeGeometryResponse { + pub client_rect: Rect, +} pub struct HitTestResponse(pub UntrustedNodeAddress); pub struct MouseOverResponse(pub Vec); @@ -120,6 +125,7 @@ pub enum ReflowQueryType { NoQuery, ContentBoxQuery(TrustedNodeAddress), ContentBoxesQuery(TrustedNodeAddress), + NodeGeometryQuery(TrustedNodeAddress), } /// Information needed for a reflow. diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 02ec1797a79e..39596d2982cd 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -275,6 +275,12 @@ "url": "/_mozilla/mozilla/child_reparenting.html" } ], + "mozilla/client-top-left-height-width.html": [ + { + "path": "mozilla/client-top-left-height-width.html", + "url": "/_mozilla/mozilla/client-top-left-height-width.html" + } + ], "mozilla/collections.html": [ { "path": "mozilla/collections.html", diff --git a/tests/wpt/mozilla/tests/mozilla/client-top-left-height-width.html b/tests/wpt/mozilla/tests/mozilla/client-top-left-height-width.html new file mode 100644 index 000000000000..6a43354a1b97 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/client-top-left-height-width.html @@ -0,0 +1,105 @@ + + + + + + + +
my div
+
my div
+
my div
+
+
+
+ X +
+
+
+
rotated
+
inline-block
+ + +