diff options
author | Glenn Watson <gw@intuitionlibrary.com> | 2015-07-27 15:20:27 +1000 |
---|---|---|
committer | Glenn Watson <gw@intuitionlibrary.com> | 2015-08-03 11:55:38 +1000 |
commit | 9e5687e3e72f2c79bf17bcfb33ea33f0659377ac (patch) | |
tree | 747b19a3de028cebc1dca77f6edd1a38fba1cf33 | |
parent | 1809748dc12ec63e3179b66109c91983f744c235 (diff) | |
download | servo-9e5687e3e72f2c79bf17bcfb33ea33f0659377ac.tar.gz servo-9e5687e3e72f2c79bf17bcfb33ea33f0659377ac.zip |
Implement offsetParent/Top/Left/Width/Height.
-rw-r--r-- | components/layout/block.rs | 2 | ||||
-rw-r--r-- | components/layout/flow.rs | 3 | ||||
-rw-r--r-- | components/layout/fragment.rs | 2 | ||||
-rw-r--r-- | components/layout/inline.rs | 2 | ||||
-rw-r--r-- | components/layout/layout_task.rs | 133 | ||||
-rw-r--r-- | components/layout/list_item.rs | 4 | ||||
-rw-r--r-- | components/layout/multicol.rs | 3 | ||||
-rw-r--r-- | components/layout/sequential.rs | 10 | ||||
-rw-r--r-- | components/layout/table.rs | 3 | ||||
-rw-r--r-- | components/layout/table_caption.rs | 3 | ||||
-rw-r--r-- | components/layout/table_cell.rs | 3 | ||||
-rw-r--r-- | components/layout/table_colgroup.rs | 1 | ||||
-rw-r--r-- | components/layout/table_row.rs | 3 | ||||
-rw-r--r-- | components/layout/table_rowgroup.rs | 3 | ||||
-rw-r--r-- | components/layout/table_wrapper.rs | 3 | ||||
-rw-r--r-- | components/script/dom/htmlelement.rs | 59 | ||||
-rw-r--r-- | components/script/dom/webidls/HTMLElement.webidl | 11 | ||||
-rw-r--r-- | components/script/dom/window.rs | 27 | ||||
-rw-r--r-- | components/script/layout_interface.rs | 17 | ||||
-rw-r--r-- | tests/wpt/mozilla/meta/MANIFEST.json | 6 | ||||
-rw-r--r-- | tests/wpt/mozilla/tests/mozilla/element_parentOffset.html | 51 |
21 files changed, 325 insertions, 24 deletions
diff --git a/components/layout/block.rs b/components/layout/block.rs index c7304865ecb..599c0737bbc 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -1968,12 +1968,14 @@ impl Flow for BlockFlow { fn iterate_through_fragment_border_boxes(&self, iterator: &mut FragmentBorderBoxIterator, + level: i32, stacking_context_position: &Point2D<Au>) { if !iterator.should_process(&self.fragment) { return } iterator.process(&self.fragment, + level, &self.fragment .stacking_relative_border_box(&self.base.stacking_relative_position, &self.base diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 64ee4d2e0d2..ad36062ddc0 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -281,8 +281,11 @@ pub trait Flow: fmt::Debug + Sync { fn compute_overflow(&self) -> Rect<Au>; /// Iterates through border boxes of all of this flow's fragments. + /// Level provides a zero based index indicating the current + /// depth of the flow tree during fragment iteration. fn iterate_through_fragment_border_boxes(&self, iterator: &mut FragmentBorderBoxIterator, + level: i32, stacking_context_position: &Point2D<Au>); /// Mutably iterates through fragments in this flow. diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 7285adca0e3..16e7bbeeeca 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -2157,7 +2157,7 @@ bitflags! { /// A top-down fragment border box iteration handler. pub trait FragmentBorderBoxIterator { /// The operation to perform. - fn process(&mut self, fragment: &Fragment, overflow: &Rect<Au>); + fn process(&mut self, fragment: &Fragment, level: i32, overflow: &Rect<Au>); /// Returns true if this fragment must be processed in-order. If this returns false, /// we skip the operation for this fragment, but continue processing siblings. diff --git a/components/layout/inline.rs b/components/layout/inline.rs index abf23e488dd..b36320cc3f9 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -1623,6 +1623,7 @@ impl Flow for InlineFlow { fn iterate_through_fragment_border_boxes(&self, iterator: &mut FragmentBorderBoxIterator, + level: i32, stacking_context_position: &Point2D<Au>) { // FIXME(#2795): Get the real container size. for fragment in self.fragments.fragments.iter() { @@ -1636,6 +1637,7 @@ impl Flow for InlineFlow { let relative_containing_block_mode = self.base.absolute_position_info.relative_containing_block_mode; iterator.process(fragment, + level, &fragment.stacking_relative_border_box(stacking_relative_position, relative_containing_block_size, relative_containing_block_mode, diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index c7eab0fdb50..e9a644105b2 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -15,7 +15,7 @@ use data::LayoutDataWrapper; use display_list_builder::ToGfxColor; use flow::{self, Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils}; use flow_ref::FlowRef; -use fragment::{Fragment, FragmentBorderBoxIterator}; +use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo}; use incremental::{LayoutDamageComputation, REFLOW, REFLOW_ENTIRE_DOCUMENT, REPAINT}; use layout_debug; use opaque_node::OpaqueNodeMethods; @@ -53,7 +53,7 @@ use net_traits::image_cache_task::{ImageCacheTask, ImageCacheResult, ImageCacheC use script::dom::bindings::js::LayoutJS; use script::dom::node::{LayoutData, Node}; use script::layout_interface::{Animation, ContentBoxResponse, ContentBoxesResponse, NodeGeometryResponse}; -use script::layout_interface::{HitTestResponse, LayoutChan, LayoutRPC, MouseOverResponse}; +use script::layout_interface::{HitTestResponse, LayoutChan, LayoutRPC, MouseOverResponse, OffsetParentResponse}; use script::layout_interface::{NewLayoutTaskInfo, Msg, Reflow, ReflowGoal, ReflowQueryType}; use script::layout_interface::{ResolvedStyleResponse, ScriptLayoutChan, ScriptReflow, TrustedNodeAddress}; use script_traits::{ConstellationControlMsg, LayoutControlMsg, OpaqueScriptLayoutChannel}; @@ -69,7 +69,7 @@ use std::ops::{Deref, DerefMut}; use std::sync::mpsc::{channel, Sender, Receiver, Select}; use std::sync::{Arc, Mutex, MutexGuard}; use string_cache::Atom; -use style::computed_values::{filter, mix_blend_mode}; +use style::computed_values::{self, filter, mix_blend_mode}; use style::media_queries::{MediaType, MediaQueryList, Device}; use style::properties::style_structs; use style::properties::longhands::{display, position}; @@ -137,6 +137,9 @@ pub struct LayoutTaskData { /// A queued response for the resolved style property of an element. pub resolved_style_response: Option<String>, + /// A queued response for the offset parent/rect of a node. + pub offset_parent_response: OffsetParentResponse, + /// The list of currently-running animations. pub running_animations: Arc<HashMap<OpaqueNode,Vec<Animation>>>, @@ -382,6 +385,7 @@ impl LayoutTask { client_rect_response: Rect::zero(), resolved_style_response: None, running_animations: Arc::new(HashMap::new()), + offset_parent_response: OffsetParentResponse::empty(), visible_rects: Arc::new(HashMap::with_hash_state(Default::default())), new_animations_receiver: new_animations_receiver, new_animations_sender: new_animations_sender, @@ -999,6 +1003,29 @@ impl LayoutTask { }; } + fn process_offset_parent_query<'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 = ParentOffsetBorderBoxIterator::new(requested_node); + sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator); + let parent_info_index = iterator.parent_nodes.iter().rposition(|info| info.is_some()); + match parent_info_index { + Some(parent_info_index) => { + let parent = iterator.parent_nodes[parent_info_index].as_ref().unwrap(); + let origin = iterator.node_border_box.origin - parent.border_box.origin; + let size = iterator.node_border_box.size; + rw_data.offset_parent_response = OffsetParentResponse { + node_address: Some(parent.node_address.to_untrusted_node_address()), + rect: Rect::new(origin, size), + }; + } + None => { + rw_data.offset_parent_response = OffsetParentResponse::empty(); + } + } + } fn compute_abs_pos_and_build_display_list<'a>(&'a self, data: &Reflow, @@ -1198,6 +1225,8 @@ impl LayoutTask { self.process_node_geometry_request(node, &mut root_flow, &mut rw_data), ReflowQueryType::ResolvedStyleQuery(node, ref pseudo, ref property) => self.process_resolved_style_request(node, pseudo, property, &mut root_flow, &mut rw_data), + ReflowQueryType::OffsetParentQuery(node) => + self.process_offset_parent_query(node, &mut root_flow, &mut rw_data), ReflowQueryType::NoQuery => {} } @@ -1516,6 +1545,12 @@ impl LayoutRPC for LayoutRPCImpl { Ok(MouseOverResponse(response_list)) } } + + fn offset_parent(&self) -> OffsetParentResponse { + let &LayoutRPCImpl(ref rw_data) = self; + let rw_data = rw_data.lock().unwrap(); + rw_data.offset_parent_response.clone() + } } struct UnioningFragmentBorderBoxIterator { @@ -1533,7 +1568,7 @@ impl UnioningFragmentBorderBoxIterator { } impl FragmentBorderBoxIterator for UnioningFragmentBorderBoxIterator { - fn process(&mut self, _: &Fragment, border_box: &Rect<Au>) { + fn process(&mut self, _: &Fragment, _: i32, border_box: &Rect<Au>) { self.rect = match self.rect { Some(rect) => { Some(rect.union(border_box)) @@ -1564,7 +1599,7 @@ impl CollectingFragmentBorderBoxIterator { } impl FragmentBorderBoxIterator for CollectingFragmentBorderBoxIterator { - fn process(&mut self, _: &Fragment, border_box: &Rect<Au>) { + fn process(&mut self, _: &Fragment, _: i32, border_box: &Rect<Au>) { self.rects.push(*border_box); } @@ -1587,8 +1622,33 @@ impl FragmentLocatingFragmentIterator { } } +struct ParentBorderBoxInfo { + node_address: OpaqueNode, + border_box: Rect<Au>, +} + +struct ParentOffsetBorderBoxIterator { + node_address: OpaqueNode, + last_level: i32, + has_found_node: bool, + node_border_box: Rect<Au>, + parent_nodes: Vec<Option<ParentBorderBoxInfo>>, +} + +impl ParentOffsetBorderBoxIterator { + fn new(node_address: OpaqueNode) -> ParentOffsetBorderBoxIterator { + ParentOffsetBorderBoxIterator { + node_address: node_address, + last_level: -1, + has_found_node: false, + node_border_box: Rect::zero(), + parent_nodes: Vec::new(), + } + } +} + impl FragmentBorderBoxIterator for FragmentLocatingFragmentIterator { - fn process(&mut self, fragment: &Fragment, border_box: &Rect<Au>) { + fn process(&mut self, fragment: &Fragment, _: i32, border_box: &Rect<Au>) { let style_structs::Border { border_top_width: top_width, border_right_width: right_width, @@ -1649,7 +1709,7 @@ impl PositionRetrievingFragmentBorderBoxIterator { } impl FragmentBorderBoxIterator for PositionRetrievingFragmentBorderBoxIterator { - fn process(&mut self, _: &Fragment, border_box: &Rect<Au>) { + fn process(&mut self, _: &Fragment, _: i32, border_box: &Rect<Au>) { self.result = Some(match self.property { PositionProperty::Left => self.position.x, @@ -1691,7 +1751,7 @@ impl MarginRetrievingFragmentBorderBoxIterator { } impl FragmentBorderBoxIterator for MarginRetrievingFragmentBorderBoxIterator { - fn process(&mut self, fragment: &Fragment, _: &Rect<Au>) { + fn process(&mut self, fragment: &Fragment, _: i32, _: &Rect<Au>) { let rect = match self.margin_padding { MarginPadding::Margin => &fragment.margin, MarginPadding::Padding => &fragment.border_padding @@ -1709,6 +1769,63 @@ impl FragmentBorderBoxIterator for MarginRetrievingFragmentBorderBoxIterator { } } +// https://drafts.csswg.org/cssom-view/#extensions-to-the-htmlelement-interface +impl FragmentBorderBoxIterator for ParentOffsetBorderBoxIterator { + fn process(&mut self, fragment: &Fragment, level: i32, border_box: &Rect<Au>) { + if fragment.node == self.node_address { + // Found the fragment in the flow tree that matches the + // DOM node being looked for. + self.has_found_node = true; + self.node_border_box = *border_box; + + // offsetParent returns null if the node is fixed. + if fragment.style.get_box().position == computed_values::position::T::fixed { + self.parent_nodes.clear(); + } + } else if level > self.last_level { + // TODO(gw): Is there a less fragile way of checking whether this + // fragment is the body element, rather than just checking that + // the parent nodes stack contains the root node only? + let is_body_element = self.parent_nodes.len() == 1; + + let is_valid_parent = match (is_body_element, + fragment.style.get_box().position, + &fragment.specific) { + // Spec says it's valid if any of these are true: + // 1) Is the body element + // 2) Is static position *and* is a table or table cell + // 3) Is not static position + (true, _, _) | + (false, computed_values::position::T::static_, &SpecificFragmentInfo::Table) | + (false, computed_values::position::T::static_, &SpecificFragmentInfo::TableCell) | + (false, computed_values::position::T::absolute, _) | + (false, computed_values::position::T::relative, _) | + (false, computed_values::position::T::fixed, _) => true, + + // Otherwise, it's not a valid parent + (false, computed_values::position::T::static_, _) => false, + }; + + let parent_info = if is_valid_parent { + Some(ParentBorderBoxInfo { + border_box: *border_box, + node_address: fragment.node, + }) + } else { + None + }; + + self.parent_nodes.push(parent_info); + } else if level < self.last_level { + self.parent_nodes.pop(); + } + } + + fn should_process(&mut self, _: &Fragment) -> bool { + !self.has_found_node + } +} + // 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/layout/list_item.rs b/components/layout/list_item.rs index a1a108848ac..27b0d4589b9 100644 --- a/components/layout/list_item.rs +++ b/components/layout/list_item.rs @@ -158,13 +158,15 @@ impl Flow for ListItemFlow { fn iterate_through_fragment_border_boxes(&self, iterator: &mut FragmentBorderBoxIterator, + level: i32, stacking_context_position: &Point2D<Au>) { - self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position); + self.block_flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position); if let Some(ref marker) = self.marker { if iterator.should_process(marker) { iterator.process( marker, + level, &marker.stacking_relative_border_box(&self.block_flow .base .stacking_relative_position, diff --git a/components/layout/multicol.rs b/components/layout/multicol.rs index 20cc7d5c1c5..3e9ff257c9c 100644 --- a/components/layout/multicol.rs +++ b/components/layout/multicol.rs @@ -98,8 +98,9 @@ impl Flow for MulticolFlow { fn iterate_through_fragment_border_boxes(&self, iterator: &mut FragmentBorderBoxIterator, + level: i32, stacking_context_position: &Point2D<Au>) { - self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) + self.block_flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position) } fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) { diff --git a/components/layout/sequential.rs b/components/layout/sequential.rs index b4c8270e804..6f806f69bf8 100644 --- a/components/layout/sequential.rs +++ b/components/layout/sequential.rs @@ -122,22 +122,24 @@ pub fn build_display_list_for_subtree(root: &mut FlowRef, pub fn iterate_through_flow_tree_fragment_border_boxes(root: &mut FlowRef, iterator: &mut FragmentBorderBoxIterator) { fn doit(flow: &mut Flow, + level: i32, iterator: &mut FragmentBorderBoxIterator, stacking_context_position: &Point2D<Au>) { - flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position); + flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position); for kid in flow::mut_base(flow).child_iter() { let stacking_context_position = if kid.is_block_flow() && kid.as_block().fragment.establishes_stacking_context() { - *stacking_context_position + flow::base(kid).stacking_relative_position + let margin = Point2D::new(kid.as_block().fragment.margin.inline_start, Au(0)); + *stacking_context_position + flow::base(kid).stacking_relative_position + margin } else { *stacking_context_position }; // FIXME(#2795): Get the real container size. - doit(kid, iterator, &stacking_context_position); + doit(kid, level+1, iterator, &stacking_context_position); } } - doit(&mut **root, iterator, &ZERO_POINT); + doit(&mut **root, 0, iterator, &ZERO_POINT); } diff --git a/components/layout/table.rs b/components/layout/table.rs index 633454097e2..197dc89db17 100644 --- a/components/layout/table.rs +++ b/components/layout/table.rs @@ -547,8 +547,9 @@ impl Flow for TableFlow { fn iterate_through_fragment_border_boxes(&self, iterator: &mut FragmentBorderBoxIterator, + level: i32, stacking_context_position: &Point2D<Au>) { - self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) + self.block_flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position) } fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) { diff --git a/components/layout/table_caption.rs b/components/layout/table_caption.rs index c840ab866ce..003f03c92c2 100644 --- a/components/layout/table_caption.rs +++ b/components/layout/table_caption.rs @@ -95,8 +95,9 @@ impl Flow for TableCaptionFlow { fn iterate_through_fragment_border_boxes(&self, iterator: &mut FragmentBorderBoxIterator, + level: i32, stacking_context_position: &Point2D<Au>) { - self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) + self.block_flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position) } fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) { diff --git a/components/layout/table_cell.rs b/components/layout/table_cell.rs index 66f9ec72259..90cc27854cc 100644 --- a/components/layout/table_cell.rs +++ b/components/layout/table_cell.rs @@ -209,8 +209,9 @@ impl Flow for TableCellFlow { fn iterate_through_fragment_border_boxes(&self, iterator: &mut FragmentBorderBoxIterator, + level: i32, stacking_context_position: &Point2D<Au>) { - self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) + self.block_flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position) } fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) { diff --git a/components/layout/table_colgroup.rs b/components/layout/table_colgroup.rs index d58c361da4d..3af39fe0347 100644 --- a/components/layout/table_colgroup.rs +++ b/components/layout/table_colgroup.rs @@ -107,6 +107,7 @@ impl Flow for TableColGroupFlow { fn iterate_through_fragment_border_boxes(&self, _: &mut FragmentBorderBoxIterator, + _: i32, _: &Point2D<Au>) {} fn mutate_fragments(&mut self, _: &mut FnMut(&mut Fragment)) {} diff --git a/components/layout/table_row.rs b/components/layout/table_row.rs index 5048644b755..e1cde61b361 100644 --- a/components/layout/table_row.rs +++ b/components/layout/table_row.rs @@ -444,8 +444,9 @@ impl Flow for TableRowFlow { fn iterate_through_fragment_border_boxes(&self, iterator: &mut FragmentBorderBoxIterator, + level: i32, stacking_context_position: &Point2D<Au>) { - self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) + self.block_flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position) } fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) { diff --git a/components/layout/table_rowgroup.rs b/components/layout/table_rowgroup.rs index 51c700ae480..f633d241218 100644 --- a/components/layout/table_rowgroup.rs +++ b/components/layout/table_rowgroup.rs @@ -234,8 +234,9 @@ impl Flow for TableRowGroupFlow { fn iterate_through_fragment_border_boxes(&self, iterator: &mut FragmentBorderBoxIterator, + level: i32, stacking_context_position: &Point2D<Au>) { - self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) + self.block_flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position) } fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) { diff --git a/components/layout/table_wrapper.rs b/components/layout/table_wrapper.rs index 56934ea875a..ecf9596f746 100644 --- a/components/layout/table_wrapper.rs +++ b/components/layout/table_wrapper.rs @@ -431,8 +431,9 @@ impl Flow for TableWrapperFlow { fn iterate_through_fragment_border_boxes(&self, iterator: &mut FragmentBorderBoxIterator, + level: i32, stacking_context_position: &Point2D<Au>) { - self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) + self.block_flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position) } fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) { diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs index 513a9d39775..376bec890cc 100644 --- a/components/script/dom/htmlelement.rs +++ b/components/script/dom/htmlelement.rs @@ -12,7 +12,7 @@ use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementM use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLFrameSetElementDerived}; use dom::bindings::codegen::InheritTypes::{EventTargetCast, HTMLInputElementCast, NodeCast}; -use dom::bindings::codegen::InheritTypes::{HTMLElementDerived, HTMLBodyElementDerived}; +use dom::bindings::codegen::InheritTypes::{HTMLElementDerived, HTMLBodyElementDerived, HTMLHtmlElementDerived}; use dom::bindings::js::{JS, MutNullableHeap, Root}; use dom::bindings::error::ErrorResult; use dom::bindings::error::Error::Syntax; @@ -216,6 +216,63 @@ impl<'a> HTMLElementMethods for &'a HTMLElement { // If `request_focus` is not called, focus will be set to None. document.r().commit_focus_transaction(FocusType::Element); } + + // https://drafts.csswg.org/cssom-view/#extensions-to-the-htmlelement-interface + fn GetOffsetParent(self) -> Option<Root<Element>> { + if self.is_htmlbodyelement() || self.is_htmlhtmlelement() { + return None; + } + + let node = NodeCast::from_ref(self); + let window = window_from_node(self); + let (element, _) = window.offset_parent_query(node.to_trusted_node_address()); + + element + } + + // https://drafts.csswg.org/cssom-view/#extensions-to-the-htmlelement-interface + fn OffsetTop(self) -> i32 { + if self.is_htmlbodyelement() { + return 0; + } + + let node = NodeCast::from_ref(self); + let window = window_from_node(self); + let (_, rect) = window.offset_parent_query(node.to_trusted_node_address()); + + rect.origin.y.to_nearest_px() + } + + // https://drafts.csswg.org/cssom-view/#extensions-to-the-htmlelement-interface + fn OffsetLeft(self) -> i32 { + if self.is_htmlbodyelement() { + return 0; + } + + let node = NodeCast::from_ref(self); + let window = window_from_node(self); + let (_, rect) = window.offset_parent_query(node.to_trusted_node_address()); + + rect.origin.x.to_nearest_px() + } + + // https://drafts.csswg.org/cssom-view/#extensions-to-the-htmlelement-interface + fn OffsetWidth(self) -> i32 { + let node = NodeCast::from_ref(self); + let window = window_from_node(self); + let (_, rect) = window.offset_parent_query(node.to_trusted_node_address()); + + rect.size.width.to_nearest_px() + } + + // https://drafts.csswg.org/cssom-view/#extensions-to-the-htmlelement-interface + fn OffsetHeight(self) -> i32 { + let node = NodeCast::from_ref(self); + let window = window_from_node(self); + let (_, rect) = window.offset_parent_query(node.to_trusted_node_address()); + + rect.size.height.to_nearest_px() + } } // https://html.spec.whatwg.org/#attr-data-* diff --git a/components/script/dom/webidls/HTMLElement.webidl b/components/script/dom/webidls/HTMLElement.webidl index f4c0376b618..90b11d85bff 100644 --- a/components/script/dom/webidls/HTMLElement.webidl +++ b/components/script/dom/webidls/HTMLElement.webidl @@ -45,5 +45,16 @@ interface HTMLElement : Element { //readonly attribute boolean? commandDisabled; //readonly attribute boolean? commandChecked; }; + +// http://dev.w3.org/csswg/cssom-view/#extensions-to-the-htmlelement-interface +partial interface HTMLElement { + // CSSOM things are not [Pure] because they can flush + readonly attribute Element? offsetParent; + readonly attribute long offsetTop; + readonly attribute long offsetLeft; + readonly attribute long offsetWidth; + readonly attribute long offsetHeight; +}; + HTMLElement implements GlobalEventHandlers; HTMLElement implements ElementCSSInlineStyle; diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index d7a9e9f243a..1320ca354b9 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -8,7 +8,7 @@ use dom::bindings::codegen::Bindings::EventHandlerBinding::{OnErrorEventHandlerN use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::FunctionBinding::Function; use dom::bindings::codegen::Bindings::WindowBinding::{self, WindowMethods, FrameRequestCallback}; -use dom::bindings::codegen::InheritTypes::{NodeCast, EventTargetCast}; +use dom::bindings::codegen::InheritTypes::{NodeCast, ElementCast, EventTargetCast}; use dom::bindings::global::global_object_for_js_object; use dom::bindings::error::{report_pending_exception, Fallible}; use dom::bindings::error::Error::InvalidCharacter; @@ -27,7 +27,7 @@ use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId}; use dom::htmlelement::HTMLElement; use dom::location::Location; use dom::navigator::Navigator; -use dom::node::{window_from_node, TrustedNodeAddress, NodeHelpers}; +use dom::node::{window_from_node, TrustedNodeAddress, NodeHelpers, from_untrusted_node_address}; use dom::performance::Performance; use dom::screen::Screen; use dom::storage::Storage; @@ -578,6 +578,7 @@ pub trait WindowHelpers { fn client_rect_query(self, node_geometry_request: TrustedNodeAddress) -> Rect<i32>; fn resolved_style_query(self, element: TrustedNodeAddress, pseudo: Option<PseudoElement>, property: &Atom) -> Option<String>; + fn offset_parent_query(self, node: TrustedNodeAddress) -> (Option<Root<Element>>, Rect<Au>); fn handle_reflow_complete_msg(self, reflow_id: u32); fn set_fragment_name(self, fragment: Option<String>); fn steal_fragment_name(self) -> Option<String>; @@ -827,6 +828,27 @@ impl<'a> WindowHelpers for &'a Window { resolved } + fn offset_parent_query(self, node: TrustedNodeAddress) -> (Option<Root<Element>>, Rect<Au>) { + self.reflow(ReflowGoal::ForScriptQuery, + ReflowQueryType::OffsetParentQuery(node), + ReflowReason::Query); + let response = self.layout_rpc.offset_parent(); + let js_runtime = self.js_runtime.borrow(); + let js_runtime = js_runtime.as_ref().unwrap(); + let element = match response.node_address { + Some(parent_node_address) => { + let node = from_untrusted_node_address(js_runtime.rt(), + parent_node_address); + let element = ElementCast::to_ref(node.r()).unwrap(); + Some(Root::from_ref(element)) + } + None => { + None + } + }; + (element, response.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 { @@ -1135,6 +1157,7 @@ fn debug_reflow_events(goal: &ReflowGoal, query_type: &ReflowQueryType, reason: ReflowQueryType::ContentBoxesQuery(_n) => "\tContentBoxesQuery", ReflowQueryType::NodeGeometryQuery(_n) => "\tNodeGeometryQuery", ReflowQueryType::ResolvedStyleQuery(_, _, _) => "\tResolvedStyleQuery", + ReflowQueryType::OffsetParentQuery(_n) => "\tOffsetParentQuery", }); debug_msg.push_str(match *reason { diff --git a/components/script/layout_interface.rs b/components/script/layout_interface.rs index 8fcb298880c..a6fb9ad8491 100644 --- a/components/script/layout_interface.rs +++ b/components/script/layout_interface.rs @@ -104,6 +104,7 @@ pub trait LayoutRPC { fn mouse_over(&self, node: TrustedNodeAddress, point: Point2D<f32>) -> Result<MouseOverResponse, ()>; /// Query layout for the resolved value of a given CSS property fn resolved_style(&self) -> ResolvedStyleResponse; + fn offset_parent(&self) -> OffsetParentResponse; } @@ -116,6 +117,21 @@ pub struct HitTestResponse(pub UntrustedNodeAddress); pub struct MouseOverResponse(pub Vec<UntrustedNodeAddress>); pub struct ResolvedStyleResponse(pub Option<String>); +#[derive(Clone)] +pub struct OffsetParentResponse { + pub node_address: Option<UntrustedNodeAddress>, + pub rect: Rect<Au>, +} + +impl OffsetParentResponse { + pub fn empty() -> OffsetParentResponse { + OffsetParentResponse { + node_address: None, + rect: Rect::zero(), + } + } +} + /// Why we're doing reflow. #[derive(PartialEq, Copy, Clone, Debug)] pub enum ReflowGoal { @@ -133,6 +149,7 @@ pub enum ReflowQueryType { ContentBoxesQuery(TrustedNodeAddress), NodeGeometryQuery(TrustedNodeAddress), ResolvedStyleQuery(TrustedNodeAddress, Option<PseudoElement>, Atom), + OffsetParentQuery(TrustedNodeAddress), } /// Information needed for a reflow. diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 18329c0c5f3..8dda0aba215 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -473,6 +473,12 @@ "url": "/_mozilla/mozilla/element_matches_empty.html" } ], + "mozilla/element_parentOffset.html": [ + { + "path": "mozilla/element_parentOffset.html", + "url": "/_mozilla/mozilla/element_parentOffset.html" + } + ], "mozilla/empty_clientrect.html": [ { "path": "mozilla/empty_clientrect.html", diff --git a/tests/wpt/mozilla/tests/mozilla/element_parentOffset.html b/tests/wpt/mozilla/tests/mozilla/element_parentOffset.html new file mode 100644 index 00000000000..d2b390c05cb --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/element_parentOffset.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html> + <head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <style type="text/css"> + #outer { + position: absolute; + margin-left: 50px; + width: 400px; + height: 400px; + background-color: red; + } + #inner { + margin-left: 100px; + width: 200px; + height: 200px; + background-color: green; + } + #div { + margin: 25px; + position: absolute; + width: 80px; + height: 130px; + background-color: blue; + } + </style> + </head> + <body> + <div id="outer"> + <div id="inner"> + <div id="div"></div> + </div> + </div> + + <script> + test(function() { + var div = document.getElementById("div"); + var outer = document.getElementById("outer"); + + var parent = div.offsetParent; + assert_equals(parent, outer); + + assert_equals(div.offsetLeft, 125); + assert_equals(div.offsetTop, 25); + assert_equals(div.offsetWidth, 80); + assert_equals(div.offsetHeight, 130); + }); + </script> + </body> +</html> |