diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index 59e85385b04..8148374bdd8 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -977,6 +977,11 @@ impl ClippingRegion { }).collect(), } } + + #[inline] + pub fn is_max(&self) -> bool { + self.main == max_rect() && self.complex.is_empty() + } } impl fmt::Debug for ClippingRegion { diff --git a/components/layout/block.rs b/components/layout/block.rs index 1c1ff0287da..4df73666a27 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -47,8 +47,8 @@ use gfx::display_list::{ClippingRegion, StackingContext}; use gfx_traits::LayerId; use gfx_traits::print_tree::PrintTree; use layout_debug; -use model::{self, IntrinsicISizes, MarginCollapseInfo}; -use model::{CollapsibleMargins, MaybeAuto, specified, specified_or_none}; +use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo, MaybeAuto}; +use model::{specified, specified_or_none}; use rustc_serialize::{Encodable, Encoder}; use script_layout_interface::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW}; use script_layout_interface::restyle_damage::REPOSITION; @@ -57,7 +57,7 @@ use std::cmp::{max, min}; use std::fmt; use std::sync::Arc; use style::computed_values::{border_collapse, box_sizing, display, float, overflow_x, overflow_y}; -use style::computed_values::{position, text_align, transform, transform_style}; +use style::computed_values::{position, text_align, transform_style}; use style::context::{SharedStyleContext, StyleContext}; use style::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode}; use style::properties::ServoComputedValues; @@ -2070,12 +2070,9 @@ impl Flow for BlockFlow { self.base.position.size.to_physical(self.base.writing_mode); // Compute the origin and clipping rectangle for children. - // - // `clip` is in the child coordinate system. - let mut clip; - let origin_for_children; + let relative_offset = relative_offset.to_physical(self.base.writing_mode); let is_stacking_context = self.fragment.establishes_stacking_context(); - if is_stacking_context { + let origin_for_children = if is_stacking_context { // We establish a stacking context, so the position of our children is vertically // correct, but has to be adjusted to accommodate horizontal margins. (Note the // calculation involving `position` below and recall that inline-direction flow @@ -2083,13 +2080,10 @@ impl Flow for BlockFlow { // // FIXME(pcwalton): Is this vertical-writing-direction-safe? let margin = self.fragment.margin.to_physical(self.base.writing_mode); - origin_for_children = Point2D::new(-margin.left, Au(0)); - clip = self.base.clip.translate(&-self.base.stacking_relative_position); + Point2D::new(-margin.left, Au(0)) } else { - let relative_offset = relative_offset.to_physical(self.base.writing_mode); - origin_for_children = self.base.stacking_relative_position + relative_offset; - clip = self.base.clip.clone(); - } + self.base.stacking_relative_position + relative_offset + }; let stacking_relative_position_of_display_port_for_children = if is_stacking_context || self.is_root() { @@ -2120,9 +2114,17 @@ impl Flow for BlockFlow { .early_absolute_position_info .relative_containing_block_mode, CoordinateSystem::Own); - self.fragment.adjust_clipping_region_for_children( - &mut clip, - &stacking_relative_border_box); + + // Our parent set our `clip` field to the clipping region in its coordinate system. Change + // it to our coordinate system. + self.switch_coordinate_system_if_necessary(); + self.fragment.adjust_clip_for_style(&mut self.base.clip, &stacking_relative_border_box); + + // Compute the clipping region for children, taking our `overflow` properties and so forth + // into account. + let mut clip_for_children = self.base.clip.clone(); + self.fragment.adjust_clipping_region_for_children(&mut clip_for_children, + &stacking_relative_border_box); // Process children. for kid in self.base.child_iter_mut() { @@ -2166,50 +2168,18 @@ impl Flow for BlockFlow { flow::mut_base(kid).late_absolute_position_info = late_absolute_position_info_for_children; - let clip = if kid.is_block_like() { - let mut clip = clip.clone(); - let kid = kid.as_block(); - // TODO(notriddle): To properly support transformations, we either need - // non-rectangular clipping regions in display lists, or clipping - // regions in terms of the parent coordinate system instead of the - // child coordinate system. - // - // This is a workaround for a common idiom of transform: translate(). - if let Some(ref operations) = kid.fragment.style().get_effects().transform.0 { - for operation in operations { - match *operation { - transform::ComputedOperation::Translate(tx, ty, _) => { - // N.B. When the clipping value comes from us, it - // shouldn't be transformed. - let tx = if let overflow_x::T::hidden = kid.fragment.style().get_box() - .overflow_x { - Au(0) - } else { - model::specified(tx, kid.base.block_container_inline_size) - }; - let ty = if let overflow_x::T::hidden = kid.fragment.style().get_box() - .overflow_y.0 { - Au(0) - } else { - model::specified( - ty, - kid.base.block_container_explicit_block_size.unwrap_or(Au(0)) - ) - }; - let off = Point2D::new(tx, ty); - clip = clip.translate(&-off); - } - _ => {} - }; - } - } - clip - } else { - clip.clone() - }; - flow::mut_base(kid).clip = clip; flow::mut_base(kid).stacking_relative_position_of_display_port = stacking_relative_position_of_display_port_for_children; + + // This clipping region is in our coordinate system. The child will fix it up to be in + // its own coordinate system by itself if necessary. + // + // Rationale: If the child is absolutely positioned, it hasn't been positioned at this + // point (as absolutely-positioned flows position themselves in + // `compute_absolute_position()`). Therefore, we don't always know what the child's + // coordinate system is here. So we store the clipping region in our coordinate system + // for now; the child will move it later if needed. + flow::mut_base(kid).clip = clip_for_children.clone() } self.base.restyle_damage.remove(REPOSITION) diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 5fba5159bbc..33d44a82278 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -15,7 +15,7 @@ use azure::azure_hl::Color; use block::{BlockFlow, BlockStackingContextType}; use canvas_traits::{CanvasData, CanvasMsg, FromLayoutMsg}; use context::SharedLayoutContext; -use euclid::{Matrix4D, Point2D, Point3D, Radians, Rect, SideOffsets2D, Size2D}; +use euclid::{Matrix4D, Point2D, Radians, Rect, SideOffsets2D, Size2D}; use flex::FlexFlow; use flow::{BaseFlow, Flow, IS_ABSOLUTELY_POSITIONED}; use flow_ref; @@ -242,7 +242,7 @@ pub trait FragmentDisplayListBuilding { clip: &ClippingRegion, stacking_relative_display_port: &Rect); - /// Adjusts the clipping region for descendants of this fragment as appropriate. + /// Adjusts the clipping region for all descendants of this fragment as appropriate. fn adjust_clipping_region_for_children(&self, current_clip: &mut ClippingRegion, stacking_relative_border_box: &Rect); @@ -293,6 +293,9 @@ pub trait FragmentDisplayListBuilding { scroll_policy: ScrollPolicy, mode: StackingContextCreationMode) -> StackingContext; + + /// Returns the 4D matrix representing this fragment's transform. + fn transform_matrix(&self, stacking_relative_border_box: &Rect) -> Matrix4D; } fn handle_overlapping_radii(size: &Size2D, radii: &BorderRadii) -> BorderRadii { @@ -1107,12 +1110,9 @@ impl FragmentDisplayListBuilding for Fragment { } } - // Calculate the clip rect. If there's nothing to render at all, don't even construct - // display list items. - let mut clip = (*clip).clone(); - self.adjust_clip_for_style(&mut clip, &stacking_relative_border_box); + // Check the clip rect. If there's nothing to render at all, don't even construct display + // list items. let empty_rect = !clip.might_intersect_rect(&stacking_relative_border_box); - if self.is_primary_fragment() && !empty_rect { // Add shadows, background, borders, and outlines, if applicable. if let Some(ref inline_context) = self.inline_context { @@ -1122,13 +1122,13 @@ impl FragmentDisplayListBuilding for Fragment { &*node.style, display_list_section, &stacking_relative_border_box, - &clip); + clip); self.build_display_list_for_box_shadow_if_applicable( state, &*node.style, display_list_section, &stacking_relative_border_box, - &clip); + clip); let mut style = node.style.clone(); properties::modify_border_style_for_inline_sides( @@ -1141,13 +1141,13 @@ impl FragmentDisplayListBuilding for Fragment { border_painting_mode, &stacking_relative_border_box, display_list_section, - &clip); + clip); self.build_display_list_for_outline_if_applicable( state, &*node.style, &stacking_relative_border_box, - &clip); + clip); } } @@ -1156,22 +1156,22 @@ impl FragmentDisplayListBuilding for Fragment { &*self.style, display_list_section, &stacking_relative_border_box, - &clip); + clip); self.build_display_list_for_box_shadow_if_applicable(state, &*self.style, display_list_section, &stacking_relative_border_box, - &clip); + clip); self.build_display_list_for_borders_if_applicable(state, &*self.style, border_painting_mode, &stacking_relative_border_box, display_list_section, - &clip); + clip); self.build_display_list_for_outline_if_applicable(state, &*self.style, &stacking_relative_border_box, - &clip); + clip); } } @@ -1181,11 +1181,11 @@ impl FragmentDisplayListBuilding for Fragment { self.build_display_items_for_selection_if_necessary(state, &stacking_relative_border_box, display_list_section, - &clip); + clip); } if empty_rect { - return; + return } debug!("Fragment::build_display_list: intersected. Adding display item..."); @@ -1193,12 +1193,10 @@ impl FragmentDisplayListBuilding for Fragment { // Create special per-fragment-type display items. self.build_fragment_type_specific_display_items(state, &stacking_relative_border_box, - &clip); + clip); if opts::get().show_debug_fragment_borders { - self.build_debug_borders_around_fragment(state, - &stacking_relative_border_box, - &clip); + self.build_debug_borders_around_fragment(state, &stacking_relative_border_box, clip) } } @@ -1417,56 +1415,7 @@ impl FragmentDisplayListBuilding for Fragment { } }; - let mut transform = Matrix4D::identity(); - if let Some(ref operations) = self.style().get_effects().transform.0 { - let transform_origin = self.style().get_effects().transform_origin; - let transform_origin = - Point3D::new(model::specified(transform_origin.horizontal, - border_box.size.width).to_f32_px(), - model::specified(transform_origin.vertical, - border_box.size.height).to_f32_px(), - transform_origin.depth.to_f32_px()); - - let pre_transform = Matrix4D::create_translation(transform_origin.x, - transform_origin.y, - transform_origin.z); - let post_transform = Matrix4D::create_translation(-transform_origin.x, - -transform_origin.y, - -transform_origin.z); - - for operation in operations { - let matrix = match *operation { - transform::ComputedOperation::Rotate(ax, ay, az, theta) => { - let theta = 2.0f32 * f32::consts::PI - theta.radians(); - Matrix4D::create_rotation(ax, ay, az, Radians::new(theta)) - } - transform::ComputedOperation::Perspective(d) => { - create_perspective_matrix(d) - } - transform::ComputedOperation::Scale(sx, sy, sz) => { - Matrix4D::create_scale(sx, sy, sz) - } - transform::ComputedOperation::Translate(tx, ty, tz) => { - let tx = model::specified(tx, border_box.size.width).to_f32_px(); - let ty = model::specified(ty, border_box.size.height).to_f32_px(); - let tz = tz.to_f32_px(); - Matrix4D::create_translation(tx, ty, tz) - } - transform::ComputedOperation::Matrix(m) => { - m.to_gfx_matrix() - } - transform::ComputedOperation::Skew(theta_x, theta_y) => { - Matrix4D::create_skew(Radians::new(theta_x.radians()), - Radians::new(theta_y.radians())) - } - }; - - transform = transform.pre_mul(&matrix); - } - - transform = pre_transform.pre_mul(&transform).pre_mul(&post_transform); - } - + let transform = self.transform_matrix(&border_box); let perspective = match self.style().get_effects().perspective { LengthOrNone::Length(d) => { let perspective_origin = self.style().get_effects().perspective_origin; @@ -1545,13 +1494,9 @@ impl FragmentDisplayListBuilding for Fragment { return } - // Account for style-specified `clip`. - self.adjust_clip_for_style(current_clip, stacking_relative_border_box); - let overflow_x = self.style.get_box().overflow_x; let overflow_y = self.style.get_box().overflow_y.0; - - if let (overflow_x::T::visible, overflow_x::T::visible) = (overflow_x, overflow_y) { + if overflow_x == overflow_x::T::visible && overflow_y == overflow_x::T::visible { return } @@ -1562,7 +1507,8 @@ impl FragmentDisplayListBuilding for Fragment { stacking_relative_border_box } overflow_clip_box::T::content_box => { - overflow_clip_rect_owner = self.stacking_relative_content_box(stacking_relative_border_box); + overflow_clip_rect_owner = + self.stacking_relative_content_box(stacking_relative_border_box); &overflow_clip_rect_owner } }; @@ -1570,8 +1516,8 @@ impl FragmentDisplayListBuilding for Fragment { // Clip according to the values of `overflow-x` and `overflow-y`. // // FIXME(pcwalton): This may be more complex than it needs to be, since it seems to be - // impossible with the computed value rules as they are to have `overflow-x: visible` with - // `overflow-y: ` or vice versa! + // impossible with the computed value rules as they are to have `overflow-x: visible` + // with `overflow-y: ` or vice versa! if let overflow_x::T::hidden = self.style.get_box().overflow_x { let mut bounds = current_clip.bounding_rect(); let max_x = cmp::min(bounds.max_x(), overflow_clip_rect.max_x()); @@ -1590,7 +1536,8 @@ impl FragmentDisplayListBuilding for Fragment { let border_radii = build_border_radius(stacking_relative_border_box, self.style.get_border()); if !border_radii.is_square() { - current_clip.intersect_with_rounded_rect(stacking_relative_border_box, &border_radii) + current_clip.intersect_with_rounded_rect(stacking_relative_border_box, + &border_radii) } } @@ -1731,6 +1678,64 @@ impl FragmentDisplayListBuilding for Fragment { clip_mode: BoxShadowClipMode::None, })); } + + fn transform_matrix(&self, stacking_relative_border_box: &Rect) -> Matrix4D { + let mut transform = Matrix4D::identity(); + let operations = match self.style.get_effects().transform.0 { + None => return transform, + Some(ref operations) => operations, + }; + + let transform_origin = &self.style.get_effects().transform_origin; + let transform_origin_x = model::specified(transform_origin.horizontal, + stacking_relative_border_box.size + .width).to_f32_px(); + let transform_origin_y = model::specified(transform_origin.vertical, + stacking_relative_border_box.size + .height).to_f32_px(); + let transform_origin_z = transform_origin.depth.to_f32_px(); + + let pre_transform = Matrix4D::create_translation(transform_origin_x, + transform_origin_y, + transform_origin_z); + let post_transform = Matrix4D::create_translation(-transform_origin_x, + -transform_origin_y, + -transform_origin_z); + + for operation in operations { + let matrix = match *operation { + transform::ComputedOperation::Rotate(ax, ay, az, theta) => { + let theta = 2.0f32 * f32::consts::PI - theta.radians(); + Matrix4D::create_rotation(ax, ay, az, Radians::new(theta)) + } + transform::ComputedOperation::Perspective(d) => { + create_perspective_matrix(d) + } + transform::ComputedOperation::Scale(sx, sy, sz) => { + Matrix4D::create_scale(sx, sy, sz) + } + transform::ComputedOperation::Translate(tx, ty, tz) => { + let tx = + model::specified(tx, stacking_relative_border_box.size.width).to_f32_px(); + let ty = + model::specified(ty, stacking_relative_border_box.size.height).to_f32_px(); + let tz = tz.to_f32_px(); + Matrix4D::create_translation(tx, ty, tz) + } + transform::ComputedOperation::Matrix(m) => { + m.to_gfx_matrix() + } + transform::ComputedOperation::Skew(theta_x, theta_y) => { + Matrix4D::create_skew(Radians::new(theta_x.radians()), + Radians::new(theta_y.radians())) + } + }; + + transform = transform.pre_mul(&matrix); + } + + pre_transform.pre_mul(&transform).pre_mul(&post_transform) + } } pub trait BlockFlowDisplayListBuilding { @@ -1738,6 +1743,16 @@ pub trait BlockFlowDisplayListBuilding { fn build_display_list_for_block(&mut self, state: &mut DisplayListBuildState, border_painting_mode: BorderPaintingMode); + + /// Changes this block's clipping region from its parent's coordinate system to its own + /// coordinate system if necessary (i.e. if this block is a stacking context). + /// + /// The clipping region is initially in each block's parent's coordinate system because the + /// parent of each block does not have enough information to determine what the child's + /// coordinate system is on its own. Specifically, if the child is absolutely positioned, the + /// parent does not know where the child's absolute position is at the time it assigns clipping + /// regions, because flows compute their own absolute positions. + fn switch_coordinate_system_if_necessary(&mut self); } impl BlockFlowDisplayListBuilding for BlockFlow { @@ -1848,14 +1863,6 @@ impl BlockFlowDisplayListBuilding for BlockFlow { }; // Add the box that starts the block context. - let clip_owner; - let clip = if establishes_stacking_context { - clip_owner = self.base.clip.translate(&-self.base.stacking_relative_position); - &clip_owner - } else { - &self.base.clip - }; - self.fragment .build_display_list(state, &self.base.stacking_relative_position, @@ -1867,11 +1874,61 @@ impl BlockFlowDisplayListBuilding for BlockFlow { .relative_containing_block_mode, border_painting_mode, background_border_section, - clip, + &self.base.clip, &self.base.stacking_relative_position_of_display_port); self.base.build_display_items_for_debugging_tint(state, self.fragment.node); } + + fn switch_coordinate_system_if_necessary(&mut self) { + // Avoid overflows! + if self.base.clip.is_max() { + return + } + + if !self.fragment.establishes_stacking_context() { + return + } + + let stacking_relative_border_box = + self.fragment.stacking_relative_border_box(&self.base.stacking_relative_position, + &self.base + .early_absolute_position_info + .relative_containing_block_size, + self.base + .early_absolute_position_info + .relative_containing_block_mode, + CoordinateSystem::Parent); + self.base.clip = self.base.clip.translate(&-stacking_relative_border_box.origin); + + // Account for `transform`, if applicable. + if self.fragment.style.get_effects().transform.0.is_none() { + return + } + let transform = match self.fragment + .transform_matrix(&stacking_relative_border_box) + .inverse() { + Some(transform) => transform, + None => { + // Singular matrix. Ignore it. + return + } + }; + + // FIXME(pcwalton): This is inaccurate: not all transforms are 2D, and not all clips are + // axis-aligned. + let bounding_rect = self.base.clip.bounding_rect(); + let bounding_rect = Rect::new(Point2D::new(bounding_rect.origin.x.to_f32_px(), + bounding_rect.origin.y.to_f32_px()), + Size2D::new(bounding_rect.size.width.to_f32_px(), + bounding_rect.size.height.to_f32_px())); + let clip_rect = transform.to_2d().transform_rect(&bounding_rect); + let clip_rect = Rect::new(Point2D::new(Au::from_f32_px(clip_rect.origin.x), + Au::from_f32_px(clip_rect.origin.y)), + Size2D::new(Au::from_f32_px(clip_rect.size.width), + Au::from_f32_px(clip_rect.size.height))); + self.base.clip = ClippingRegion::from_rect(&clip_rect) + } } pub trait InlineFlowDisplayListBuilding { @@ -2117,3 +2174,4 @@ pub enum StackingContextCreationMode { PseudoPositioned, PseudoFloat, } + diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 28694686ef0..8e68250a80d 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -935,7 +935,9 @@ pub struct BaseFlow { /// assignment. pub late_absolute_position_info: LateAbsolutePositionInfo, - /// The clipping region for this flow and its descendants, in layer coordinates. + /// The clipping region for this flow and its descendants, in the coordinate system of the + /// nearest ancestor stacking context. If this flow itself represents a stacking context, then + /// this is in the flow's own coordinate system. pub clip: ClippingRegion, /// The stacking-relative position of the display port. diff --git a/components/layout/inline.rs b/components/layout/inline.rs index c06e3631719..18bb1ad54a3 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -7,8 +7,7 @@ use app_units::Au; use block::AbsoluteAssignBSizesTraversal; use context::{LayoutContext, SharedLayoutContext}; -use display_list_builder::{FragmentDisplayListBuilding, InlineFlowDisplayListBuilding}; -use display_list_builder::DisplayListBuildState; +use display_list_builder::{DisplayListBuildState, InlineFlowDisplayListBuilding}; use euclid::{Point2D, Size2D}; use floats::{FloatKind, Floats, PlacementInfo}; use flow::{self, BaseFlow, Flow, FlowClass, ForceNonfloatedFlag, IS_ABSOLUTELY_POSITIONED}; @@ -1547,15 +1546,11 @@ impl Flow for InlineFlow { CoordinateSystem::Parent); let stacking_relative_content_box = fragment.stacking_relative_content_box(&stacking_relative_border_box); - let mut clip = self.base.clip.clone(); - fragment.adjust_clipping_region_for_children(&mut clip, - &stacking_relative_border_box); + let is_positioned = fragment.is_positioned(); match fragment.specific { SpecificFragmentInfo::InlineBlock(ref mut info) => { let flow = flow_ref::deref_mut(&mut info.flow_ref); - flow::mut_base(flow).clip = clip; - let block_flow = flow.as_mut_block(); block_flow.base.late_absolute_position_info = self.base.late_absolute_position_info; @@ -1573,10 +1568,13 @@ impl Flow for InlineFlow { stacking_relative_content_box.origin; block_flow.base.stacking_relative_position_of_display_port = self.base.stacking_relative_position_of_display_port; + + // Write the clip in our coordinate system into the child flow. (The kid will + // fix it up to be in its own coordinate system if necessary.) + block_flow.base.clip = self.base.clip.clone() } SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut info) => { let flow = flow_ref::deref_mut(&mut info.flow_ref); - flow::mut_base(flow).clip = clip; let block_flow = flow.as_mut_block(); block_flow.base.late_absolute_position_info = self.base.late_absolute_position_info; @@ -1585,11 +1583,12 @@ impl Flow for InlineFlow { stacking_relative_border_box.origin; block_flow.base.stacking_relative_position_of_display_port = self.base.stacking_relative_position_of_display_port; + + // As above, this is in our coordinate system for now. + block_flow.base.clip = self.base.clip.clone() } SpecificFragmentInfo::InlineAbsolute(ref mut info) => { let flow = flow_ref::deref_mut(&mut info.flow_ref); - flow::mut_base(flow).clip = clip; - let block_flow = flow.as_mut_block(); block_flow.base.late_absolute_position_info = self.base.late_absolute_position_info; @@ -1605,6 +1604,9 @@ impl Flow for InlineFlow { stacking_relative_border_box.origin; block_flow.base.stacking_relative_position_of_display_port = self.base.stacking_relative_position_of_display_port; + + // As above, this is in our coordinate system for now. + block_flow.base.clip = self.base.clip.clone() } _ => {} } diff --git a/tests/wpt/metadata-css/css-transforms-1_dev/html/transform-transformable-inline-block.htm.ini b/tests/wpt/metadata-css/css-transforms-1_dev/html/transform-transformable-inline-block.htm.ini deleted file mode 100644 index 405256c2282..00000000000 --- a/tests/wpt/metadata-css/css-transforms-1_dev/html/transform-transformable-inline-block.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[transform-transformable-inline-block.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index de970ab9432..084394e7ef9 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -4668,6 +4668,18 @@ "url": "/_mozilla/css/quotes_simple_a.html" } ], + "css/relative_position_clip_a.html": [ + { + "path": "css/relative_position_clip_a.html", + "references": [ + [ + "/_mozilla/css/relative_position_clip_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/relative_position_clip_a.html" + } + ], "css/relative_position_vertical_percentage_a.html": [ { "path": "css/relative_position_vertical_percentage_a.html", @@ -18486,6 +18498,18 @@ "url": "/_mozilla/css/quotes_simple_a.html" } ], + "css/relative_position_clip_a.html": [ + { + "path": "css/relative_position_clip_a.html", + "references": [ + [ + "/_mozilla/css/relative_position_clip_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/relative_position_clip_a.html" + } + ], "css/relative_position_vertical_percentage_a.html": [ { "path": "css/relative_position_vertical_percentage_a.html", diff --git a/tests/wpt/mozilla/tests/css/relative_position_clip_a.html b/tests/wpt/mozilla/tests/css/relative_position_clip_a.html new file mode 100644 index 00000000000..801239c93ae --- /dev/null +++ b/tests/wpt/mozilla/tests/css/relative_position_clip_a.html @@ -0,0 +1,19 @@ + + + + +
X + diff --git a/tests/wpt/mozilla/tests/css/relative_position_clip_ref.html b/tests/wpt/mozilla/tests/css/relative_position_clip_ref.html new file mode 100644 index 00000000000..376c854fe81 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/relative_position_clip_ref.html @@ -0,0 +1,12 @@ + + + +
X +