mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
Auto merge of #6784 - glennw:offset-ext, r=pcwalton
Implement offsetParent/Top/Left/Width/Height. <!-- Reviewable:start --> [<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/6784) <!-- Reviewable:end -->
This commit is contained in:
commit
d66c59a152
25 changed files with 334 additions and 29 deletions
|
@ -2011,12 +2011,14 @@ impl Flow for BlockFlow {
|
||||||
|
|
||||||
fn iterate_through_fragment_border_boxes(&self,
|
fn iterate_through_fragment_border_boxes(&self,
|
||||||
iterator: &mut FragmentBorderBoxIterator,
|
iterator: &mut FragmentBorderBoxIterator,
|
||||||
|
level: i32,
|
||||||
stacking_context_position: &Point2D<Au>) {
|
stacking_context_position: &Point2D<Au>) {
|
||||||
if !iterator.should_process(&self.fragment) {
|
if !iterator.should_process(&self.fragment) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator.process(&self.fragment,
|
iterator.process(&self.fragment,
|
||||||
|
level,
|
||||||
&self.fragment
|
&self.fragment
|
||||||
.stacking_relative_border_box(&self.base.stacking_relative_position,
|
.stacking_relative_border_box(&self.base.stacking_relative_position,
|
||||||
&self.base
|
&self.base
|
||||||
|
|
|
@ -281,8 +281,11 @@ pub trait Flow: fmt::Debug + Sync {
|
||||||
fn compute_overflow(&self) -> Rect<Au>;
|
fn compute_overflow(&self) -> Rect<Au>;
|
||||||
|
|
||||||
/// Iterates through border boxes of all of this flow's fragments.
|
/// 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,
|
fn iterate_through_fragment_border_boxes(&self,
|
||||||
iterator: &mut FragmentBorderBoxIterator,
|
iterator: &mut FragmentBorderBoxIterator,
|
||||||
|
level: i32,
|
||||||
stacking_context_position: &Point2D<Au>);
|
stacking_context_position: &Point2D<Au>);
|
||||||
|
|
||||||
/// Mutably iterates through fragments in this flow.
|
/// Mutably iterates through fragments in this flow.
|
||||||
|
|
|
@ -2164,7 +2164,7 @@ bitflags! {
|
||||||
/// A top-down fragment border box iteration handler.
|
/// A top-down fragment border box iteration handler.
|
||||||
pub trait FragmentBorderBoxIterator {
|
pub trait FragmentBorderBoxIterator {
|
||||||
/// The operation to perform.
|
/// 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,
|
/// 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.
|
/// we skip the operation for this fragment, but continue processing siblings.
|
||||||
|
|
|
@ -1623,6 +1623,7 @@ impl Flow for InlineFlow {
|
||||||
|
|
||||||
fn iterate_through_fragment_border_boxes(&self,
|
fn iterate_through_fragment_border_boxes(&self,
|
||||||
iterator: &mut FragmentBorderBoxIterator,
|
iterator: &mut FragmentBorderBoxIterator,
|
||||||
|
level: i32,
|
||||||
stacking_context_position: &Point2D<Au>) {
|
stacking_context_position: &Point2D<Au>) {
|
||||||
// FIXME(#2795): Get the real container size.
|
// FIXME(#2795): Get the real container size.
|
||||||
for fragment in self.fragments.fragments.iter() {
|
for fragment in self.fragments.fragments.iter() {
|
||||||
|
@ -1636,6 +1637,7 @@ impl Flow for InlineFlow {
|
||||||
let relative_containing_block_mode =
|
let relative_containing_block_mode =
|
||||||
self.base.absolute_position_info.relative_containing_block_mode;
|
self.base.absolute_position_info.relative_containing_block_mode;
|
||||||
iterator.process(fragment,
|
iterator.process(fragment,
|
||||||
|
level,
|
||||||
&fragment.stacking_relative_border_box(stacking_relative_position,
|
&fragment.stacking_relative_border_box(stacking_relative_position,
|
||||||
relative_containing_block_size,
|
relative_containing_block_size,
|
||||||
relative_containing_block_mode,
|
relative_containing_block_mode,
|
||||||
|
|
|
@ -15,7 +15,7 @@ use data::LayoutDataWrapper;
|
||||||
use display_list_builder::ToGfxColor;
|
use display_list_builder::ToGfxColor;
|
||||||
use flow::{self, Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils};
|
use flow::{self, Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils};
|
||||||
use flow_ref::FlowRef;
|
use flow_ref::FlowRef;
|
||||||
use fragment::{Fragment, FragmentBorderBoxIterator};
|
use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo};
|
||||||
use incremental::{LayoutDamageComputation, REFLOW, REFLOW_ENTIRE_DOCUMENT, REPAINT};
|
use incremental::{LayoutDamageComputation, REFLOW, REFLOW_ENTIRE_DOCUMENT, REPAINT};
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use opaque_node::OpaqueNodeMethods;
|
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::bindings::js::LayoutJS;
|
||||||
use script::dom::node::{LayoutData, Node};
|
use script::dom::node::{LayoutData, Node};
|
||||||
use script::layout_interface::{Animation, ContentBoxResponse, ContentBoxesResponse, NodeGeometryResponse};
|
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::{NewLayoutTaskInfo, Msg, Reflow, ReflowGoal, ReflowQueryType};
|
||||||
use script::layout_interface::{ResolvedStyleResponse, ScriptLayoutChan, ScriptReflow, TrustedNodeAddress};
|
use script::layout_interface::{ResolvedStyleResponse, ScriptLayoutChan, ScriptReflow, TrustedNodeAddress};
|
||||||
use script_traits::{ConstellationControlMsg, LayoutControlMsg, OpaqueScriptLayoutChannel};
|
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::mpsc::{channel, Sender, Receiver, Select};
|
||||||
use std::sync::{Arc, Mutex, MutexGuard};
|
use std::sync::{Arc, Mutex, MutexGuard};
|
||||||
use string_cache::Atom;
|
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::media_queries::{MediaType, MediaQueryList, Device};
|
||||||
use style::properties::style_structs;
|
use style::properties::style_structs;
|
||||||
use style::properties::longhands::{display, position};
|
use style::properties::longhands::{display, position};
|
||||||
|
@ -137,6 +137,9 @@ pub struct LayoutTaskData {
|
||||||
/// A queued response for the resolved style property of an element.
|
/// A queued response for the resolved style property of an element.
|
||||||
pub resolved_style_response: Option<String>,
|
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.
|
/// The list of currently-running animations.
|
||||||
pub running_animations: Arc<HashMap<OpaqueNode,Vec<Animation>>>,
|
pub running_animations: Arc<HashMap<OpaqueNode,Vec<Animation>>>,
|
||||||
|
|
||||||
|
@ -382,6 +385,7 @@ impl LayoutTask {
|
||||||
client_rect_response: Rect::zero(),
|
client_rect_response: Rect::zero(),
|
||||||
resolved_style_response: None,
|
resolved_style_response: None,
|
||||||
running_animations: Arc::new(HashMap::new()),
|
running_animations: Arc::new(HashMap::new()),
|
||||||
|
offset_parent_response: OffsetParentResponse::empty(),
|
||||||
visible_rects: Arc::new(HashMap::with_hash_state(Default::default())),
|
visible_rects: Arc::new(HashMap::with_hash_state(Default::default())),
|
||||||
new_animations_receiver: new_animations_receiver,
|
new_animations_receiver: new_animations_receiver,
|
||||||
new_animations_sender: new_animations_sender,
|
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,
|
fn compute_abs_pos_and_build_display_list<'a>(&'a self,
|
||||||
data: &Reflow,
|
data: &Reflow,
|
||||||
|
@ -1198,6 +1225,8 @@ impl LayoutTask {
|
||||||
self.process_node_geometry_request(node, &mut root_flow, &mut rw_data),
|
self.process_node_geometry_request(node, &mut root_flow, &mut rw_data),
|
||||||
ReflowQueryType::ResolvedStyleQuery(node, ref pseudo, ref property) =>
|
ReflowQueryType::ResolvedStyleQuery(node, ref pseudo, ref property) =>
|
||||||
self.process_resolved_style_request(node, pseudo, property, &mut root_flow, &mut rw_data),
|
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 => {}
|
ReflowQueryType::NoQuery => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1516,6 +1545,12 @@ impl LayoutRPC for LayoutRPCImpl {
|
||||||
Ok(MouseOverResponse(response_list))
|
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 {
|
struct UnioningFragmentBorderBoxIterator {
|
||||||
|
@ -1533,7 +1568,7 @@ impl UnioningFragmentBorderBoxIterator {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FragmentBorderBoxIterator for 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 {
|
self.rect = match self.rect {
|
||||||
Some(rect) => {
|
Some(rect) => {
|
||||||
Some(rect.union(border_box))
|
Some(rect.union(border_box))
|
||||||
|
@ -1564,7 +1599,7 @@ impl CollectingFragmentBorderBoxIterator {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FragmentBorderBoxIterator for 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);
|
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 {
|
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 {
|
let style_structs::Border {
|
||||||
border_top_width: top_width,
|
border_top_width: top_width,
|
||||||
border_right_width: right_width,
|
border_right_width: right_width,
|
||||||
|
@ -1649,7 +1709,7 @@ impl PositionRetrievingFragmentBorderBoxIterator {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FragmentBorderBoxIterator for 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 =
|
self.result =
|
||||||
Some(match self.property {
|
Some(match self.property {
|
||||||
PositionProperty::Left => self.position.x,
|
PositionProperty::Left => self.position.x,
|
||||||
|
@ -1691,7 +1751,7 @@ impl MarginRetrievingFragmentBorderBoxIterator {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FragmentBorderBoxIterator for 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 {
|
let rect = match self.margin_padding {
|
||||||
MarginPadding::Margin => &fragment.margin,
|
MarginPadding::Margin => &fragment.margin,
|
||||||
MarginPadding::Padding => &fragment.border_padding
|
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
|
// The default computed value for background-color is transparent (see
|
||||||
// http://dev.w3.org/csswg/css-backgrounds/#background-color). However, we
|
// http://dev.w3.org/csswg/css-backgrounds/#background-color). However, we
|
||||||
// need to propagate the background color from the root HTML/Body
|
// need to propagate the background color from the root HTML/Body
|
||||||
|
|
|
@ -158,13 +158,15 @@ impl Flow for ListItemFlow {
|
||||||
|
|
||||||
fn iterate_through_fragment_border_boxes(&self,
|
fn iterate_through_fragment_border_boxes(&self,
|
||||||
iterator: &mut FragmentBorderBoxIterator,
|
iterator: &mut FragmentBorderBoxIterator,
|
||||||
|
level: i32,
|
||||||
stacking_context_position: &Point2D<Au>) {
|
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 let Some(ref marker) = self.marker {
|
||||||
if iterator.should_process(marker) {
|
if iterator.should_process(marker) {
|
||||||
iterator.process(
|
iterator.process(
|
||||||
marker,
|
marker,
|
||||||
|
level,
|
||||||
&marker.stacking_relative_border_box(&self.block_flow
|
&marker.stacking_relative_border_box(&self.block_flow
|
||||||
.base
|
.base
|
||||||
.stacking_relative_position,
|
.stacking_relative_position,
|
||||||
|
|
|
@ -98,8 +98,9 @@ impl Flow for MulticolFlow {
|
||||||
|
|
||||||
fn iterate_through_fragment_border_boxes(&self,
|
fn iterate_through_fragment_border_boxes(&self,
|
||||||
iterator: &mut FragmentBorderBoxIterator,
|
iterator: &mut FragmentBorderBoxIterator,
|
||||||
|
level: i32,
|
||||||
stacking_context_position: &Point2D<Au>) {
|
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)) {
|
fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) {
|
||||||
|
|
|
@ -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,
|
pub fn iterate_through_flow_tree_fragment_border_boxes(root: &mut FlowRef,
|
||||||
iterator: &mut FragmentBorderBoxIterator) {
|
iterator: &mut FragmentBorderBoxIterator) {
|
||||||
fn doit(flow: &mut Flow,
|
fn doit(flow: &mut Flow,
|
||||||
|
level: i32,
|
||||||
iterator: &mut FragmentBorderBoxIterator,
|
iterator: &mut FragmentBorderBoxIterator,
|
||||||
stacking_context_position: &Point2D<Au>) {
|
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() {
|
for kid in flow::mut_base(flow).child_iter() {
|
||||||
let stacking_context_position =
|
let stacking_context_position =
|
||||||
if kid.is_block_flow() && kid.as_block().fragment.establishes_stacking_context() {
|
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 {
|
} else {
|
||||||
*stacking_context_position
|
*stacking_context_position
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME(#2795): Get the real container size.
|
// 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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -547,8 +547,9 @@ impl Flow for TableFlow {
|
||||||
|
|
||||||
fn iterate_through_fragment_border_boxes(&self,
|
fn iterate_through_fragment_border_boxes(&self,
|
||||||
iterator: &mut FragmentBorderBoxIterator,
|
iterator: &mut FragmentBorderBoxIterator,
|
||||||
|
level: i32,
|
||||||
stacking_context_position: &Point2D<Au>) {
|
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)) {
|
fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) {
|
||||||
|
|
|
@ -95,8 +95,9 @@ impl Flow for TableCaptionFlow {
|
||||||
|
|
||||||
fn iterate_through_fragment_border_boxes(&self,
|
fn iterate_through_fragment_border_boxes(&self,
|
||||||
iterator: &mut FragmentBorderBoxIterator,
|
iterator: &mut FragmentBorderBoxIterator,
|
||||||
|
level: i32,
|
||||||
stacking_context_position: &Point2D<Au>) {
|
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)) {
|
fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) {
|
||||||
|
|
|
@ -209,8 +209,9 @@ impl Flow for TableCellFlow {
|
||||||
|
|
||||||
fn iterate_through_fragment_border_boxes(&self,
|
fn iterate_through_fragment_border_boxes(&self,
|
||||||
iterator: &mut FragmentBorderBoxIterator,
|
iterator: &mut FragmentBorderBoxIterator,
|
||||||
|
level: i32,
|
||||||
stacking_context_position: &Point2D<Au>) {
|
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)) {
|
fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) {
|
||||||
|
|
|
@ -107,6 +107,7 @@ impl Flow for TableColGroupFlow {
|
||||||
|
|
||||||
fn iterate_through_fragment_border_boxes(&self,
|
fn iterate_through_fragment_border_boxes(&self,
|
||||||
_: &mut FragmentBorderBoxIterator,
|
_: &mut FragmentBorderBoxIterator,
|
||||||
|
_: i32,
|
||||||
_: &Point2D<Au>) {}
|
_: &Point2D<Au>) {}
|
||||||
|
|
||||||
fn mutate_fragments(&mut self, _: &mut FnMut(&mut Fragment)) {}
|
fn mutate_fragments(&mut self, _: &mut FnMut(&mut Fragment)) {}
|
||||||
|
|
|
@ -444,8 +444,9 @@ impl Flow for TableRowFlow {
|
||||||
|
|
||||||
fn iterate_through_fragment_border_boxes(&self,
|
fn iterate_through_fragment_border_boxes(&self,
|
||||||
iterator: &mut FragmentBorderBoxIterator,
|
iterator: &mut FragmentBorderBoxIterator,
|
||||||
|
level: i32,
|
||||||
stacking_context_position: &Point2D<Au>) {
|
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)) {
|
fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) {
|
||||||
|
|
|
@ -234,8 +234,9 @@ impl Flow for TableRowGroupFlow {
|
||||||
|
|
||||||
fn iterate_through_fragment_border_boxes(&self,
|
fn iterate_through_fragment_border_boxes(&self,
|
||||||
iterator: &mut FragmentBorderBoxIterator,
|
iterator: &mut FragmentBorderBoxIterator,
|
||||||
|
level: i32,
|
||||||
stacking_context_position: &Point2D<Au>) {
|
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)) {
|
fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) {
|
||||||
|
|
|
@ -431,8 +431,9 @@ impl Flow for TableWrapperFlow {
|
||||||
|
|
||||||
fn iterate_through_fragment_border_boxes(&self,
|
fn iterate_through_fragment_border_boxes(&self,
|
||||||
iterator: &mut FragmentBorderBoxIterator,
|
iterator: &mut FragmentBorderBoxIterator,
|
||||||
|
level: i32,
|
||||||
stacking_context_position: &Point2D<Au>) {
|
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)) {
|
fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) {
|
||||||
|
|
|
@ -12,7 +12,7 @@ use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementM
|
||||||
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||||
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLFrameSetElementDerived};
|
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLFrameSetElementDerived};
|
||||||
use dom::bindings::codegen::InheritTypes::{EventTargetCast, HTMLInputElementCast, NodeCast};
|
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::js::{JS, MutNullableHeap, Root};
|
||||||
use dom::bindings::error::ErrorResult;
|
use dom::bindings::error::ErrorResult;
|
||||||
use dom::bindings::error::Error::Syntax;
|
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.
|
// If `request_focus` is not called, focus will be set to None.
|
||||||
document.r().commit_focus_transaction(FocusType::Element);
|
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-*
|
// https://html.spec.whatwg.org/#attr-data-*
|
||||||
|
|
|
@ -45,5 +45,16 @@ interface HTMLElement : Element {
|
||||||
//readonly attribute boolean? commandDisabled;
|
//readonly attribute boolean? commandDisabled;
|
||||||
//readonly attribute boolean? commandChecked;
|
//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 GlobalEventHandlers;
|
||||||
HTMLElement implements ElementCSSInlineStyle;
|
HTMLElement implements ElementCSSInlineStyle;
|
||||||
|
|
|
@ -8,7 +8,7 @@ use dom::bindings::codegen::Bindings::EventHandlerBinding::{OnErrorEventHandlerN
|
||||||
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
|
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
|
||||||
use dom::bindings::codegen::Bindings::FunctionBinding::Function;
|
use dom::bindings::codegen::Bindings::FunctionBinding::Function;
|
||||||
use dom::bindings::codegen::Bindings::WindowBinding::{self, WindowMethods, FrameRequestCallback};
|
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::global::global_object_for_js_object;
|
||||||
use dom::bindings::error::{report_pending_exception, Fallible};
|
use dom::bindings::error::{report_pending_exception, Fallible};
|
||||||
use dom::bindings::error::Error::InvalidCharacter;
|
use dom::bindings::error::Error::InvalidCharacter;
|
||||||
|
@ -27,7 +27,7 @@ use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId};
|
||||||
use dom::htmlelement::HTMLElement;
|
use dom::htmlelement::HTMLElement;
|
||||||
use dom::location::Location;
|
use dom::location::Location;
|
||||||
use dom::navigator::Navigator;
|
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::performance::Performance;
|
||||||
use dom::screen::Screen;
|
use dom::screen::Screen;
|
||||||
use dom::storage::Storage;
|
use dom::storage::Storage;
|
||||||
|
@ -582,6 +582,7 @@ pub trait WindowHelpers {
|
||||||
fn client_rect_query(self, node_geometry_request: TrustedNodeAddress) -> Rect<i32>;
|
fn client_rect_query(self, node_geometry_request: TrustedNodeAddress) -> Rect<i32>;
|
||||||
fn resolved_style_query(self, element: TrustedNodeAddress,
|
fn resolved_style_query(self, element: TrustedNodeAddress,
|
||||||
pseudo: Option<PseudoElement>, property: &Atom) -> Option<String>;
|
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 handle_reflow_complete_msg(self, reflow_id: u32);
|
||||||
fn set_fragment_name(self, fragment: Option<String>);
|
fn set_fragment_name(self, fragment: Option<String>);
|
||||||
fn steal_fragment_name(self) -> Option<String>;
|
fn steal_fragment_name(self) -> Option<String>;
|
||||||
|
@ -831,6 +832,27 @@ impl<'a> WindowHelpers for &'a Window {
|
||||||
resolved
|
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());
|
||||||
|
element.map(Root::from_ref)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(element, response.rect)
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_reflow_complete_msg(self, reflow_id: u32) {
|
fn handle_reflow_complete_msg(self, reflow_id: u32) {
|
||||||
let last_reflow_id = self.last_reflow_id.get();
|
let last_reflow_id = self.last_reflow_id.get();
|
||||||
if last_reflow_id == reflow_id {
|
if last_reflow_id == reflow_id {
|
||||||
|
@ -1139,6 +1161,7 @@ fn debug_reflow_events(goal: &ReflowGoal, query_type: &ReflowQueryType, reason:
|
||||||
ReflowQueryType::ContentBoxesQuery(_n) => "\tContentBoxesQuery",
|
ReflowQueryType::ContentBoxesQuery(_n) => "\tContentBoxesQuery",
|
||||||
ReflowQueryType::NodeGeometryQuery(_n) => "\tNodeGeometryQuery",
|
ReflowQueryType::NodeGeometryQuery(_n) => "\tNodeGeometryQuery",
|
||||||
ReflowQueryType::ResolvedStyleQuery(_, _, _) => "\tResolvedStyleQuery",
|
ReflowQueryType::ResolvedStyleQuery(_, _, _) => "\tResolvedStyleQuery",
|
||||||
|
ReflowQueryType::OffsetParentQuery(_n) => "\tOffsetParentQuery",
|
||||||
});
|
});
|
||||||
|
|
||||||
debug_msg.push_str(match *reason {
|
debug_msg.push_str(match *reason {
|
||||||
|
|
|
@ -104,6 +104,7 @@ pub trait LayoutRPC {
|
||||||
fn mouse_over(&self, node: TrustedNodeAddress, point: Point2D<f32>) -> Result<MouseOverResponse, ()>;
|
fn mouse_over(&self, node: TrustedNodeAddress, point: Point2D<f32>) -> Result<MouseOverResponse, ()>;
|
||||||
/// Query layout for the resolved value of a given CSS property
|
/// Query layout for the resolved value of a given CSS property
|
||||||
fn resolved_style(&self) -> ResolvedStyleResponse;
|
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 MouseOverResponse(pub Vec<UntrustedNodeAddress>);
|
||||||
pub struct ResolvedStyleResponse(pub Option<String>);
|
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.
|
/// Why we're doing reflow.
|
||||||
#[derive(PartialEq, Copy, Clone, Debug)]
|
#[derive(PartialEq, Copy, Clone, Debug)]
|
||||||
pub enum ReflowGoal {
|
pub enum ReflowGoal {
|
||||||
|
@ -133,6 +149,7 @@ pub enum ReflowQueryType {
|
||||||
ContentBoxesQuery(TrustedNodeAddress),
|
ContentBoxesQuery(TrustedNodeAddress),
|
||||||
NodeGeometryQuery(TrustedNodeAddress),
|
NodeGeometryQuery(TrustedNodeAddress),
|
||||||
ResolvedStyleQuery(TrustedNodeAddress, Option<PseudoElement>, Atom),
|
ResolvedStyleQuery(TrustedNodeAddress, Option<PseudoElement>, Atom),
|
||||||
|
OffsetParentQuery(TrustedNodeAddress),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information needed for a reflow.
|
/// Information needed for a reflow.
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
[first-letter-dynamic-003a.htm]
|
||||||
|
type: reftest
|
||||||
|
expected: FAIL
|
|
@ -0,0 +1,3 @@
|
||||||
|
[first-letter-dynamic-003b.htm]
|
||||||
|
type: reftest
|
||||||
|
expected: FAIL
|
|
@ -0,0 +1,3 @@
|
||||||
|
[run-in-basic-017.htm]
|
||||||
|
type: reftest
|
||||||
|
expected: FAIL
|
|
@ -1,5 +0,0 @@
|
||||||
[input-type-button.html]
|
|
||||||
type: testharness
|
|
||||||
[label value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -473,6 +473,12 @@
|
||||||
"url": "/_mozilla/mozilla/element_matches_empty.html"
|
"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": [
|
"mozilla/empty_clientrect.html": [
|
||||||
{
|
{
|
||||||
"path": "mozilla/empty_clientrect.html",
|
"path": "mozilla/empty_clientrect.html",
|
||||||
|
|
51
tests/wpt/mozilla/tests/mozilla/element_parentOffset.html
Normal file
51
tests/wpt/mozilla/tests/mozilla/element_parentOffset.html
Normal file
|
@ -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>
|
Loading…
Add table
Add a link
Reference in a new issue