aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/gfx/display_list/mod.rs4
-rw-r--r--components/layout/block.rs26
-rw-r--r--components/layout/display_list_builder.rs151
-rw-r--r--components/layout/fragment.rs5
-rw-r--r--components/layout/query.rs3
-rw-r--r--components/layout/sequential.rs2
-rw-r--r--components/layout/table.rs5
-rw-r--r--components/layout/table_caption.rs5
-rw-r--r--components/layout/table_cell.rs5
-rw-r--r--components/layout/table_row.rs5
-rw-r--r--components/layout/table_rowgroup.rs5
-rw-r--r--components/layout/table_wrapper.rs5
-rw-r--r--components/layout/webrender_helpers.rs6
-rw-r--r--components/style/properties/longhand/box.mako.rs3
-rw-r--r--tests/wpt/mozilla/meta/MANIFEST.json690
-rw-r--r--tests/wpt/mozilla/meta/css/css-position-3/position-sticky-get-bounding-client-rect.html.ini17
-rw-r--r--tests/wpt/mozilla/meta/css/css-position-3/position-sticky-grid.html.ini7
-rw-r--r--tests/wpt/mozilla/meta/css/css-position-3/position-sticky-inline.html.ini7
-rw-r--r--tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-bottom.html.ini8
-rw-r--r--tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-inline.html.ini7
-rw-r--r--tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-left.html.ini7
-rw-r--r--tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-right.html.ini7
-rw-r--r--tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-table.html.ini7
-rw-r--r--tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-top.html.ini7
-rw-r--r--tests/wpt/mozilla/meta/css/css-position-3/position-sticky-offset-top-left.html.ini10
-rw-r--r--tests/wpt/mozilla/meta/css/css-position-3/position-sticky-overflow-padding.html.ini7
-rw-r--r--tests/wpt/mozilla/meta/css/css-position-3/position-sticky-parsing.html.ini6
-rw-r--r--tests/wpt/mozilla/meta/css/css-position-3/position-sticky-writing-modes.html.ini7
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-bottom-ref.html69
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-bottom.html104
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-flexbox-ref.html63
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-flexbox.html80
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-get-bounding-client-rect.html99
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-grid-ref.html76
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-grid.html93
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-inflow-position-ref.html42
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-inflow-position.html46
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-inline-ref.html68
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-inline.html104
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-input-box-gets-focused-after-scroll.html30
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-left-ref.html68
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-left.html103
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-margins-ref.html63
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-margins.html92
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-bottom-ref.html76
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-bottom.html135
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-inline-ref.html73
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-inline.html114
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-left-ref.html76
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-left.html141
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-right-ref.html76
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-right.html149
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-table-ref.html66
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-table.html131
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-top-ref.html83
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-top.html128
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-offset-top-left.html78
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-overflow-padding-ref.html69
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-overflow-padding.html106
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-parsing.html73
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-right-ref.html68
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-right.html102
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-stacking-context-ref.html17
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-stacking-context.html38
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tfoot-bottom-ref.html62
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tfoot-bottom.html121
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-bottom-ref.html62
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-bottom.html127
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-left-ref.html62
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-left.html115
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-right-ref.html62
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-right.html115
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-top-ref.html62
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-top.html127
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-thead-top-ref.html62
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-thead-top.html121
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tr-bottom-ref.html62
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tr-bottom.html118
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tr-top-ref.html62
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tr-top.html119
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-top-ref.html68
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-top.html104
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-writing-modes-ref.html57
-rw-r--r--tests/wpt/mozilla/tests/css/css-position-3/position-sticky-writing-modes.html68
84 files changed, 5729 insertions, 50 deletions
diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs
index 718c44487ed..99a99a47b81 100644
--- a/components/gfx/display_list/mod.rs
+++ b/components/gfx/display_list/mod.rs
@@ -34,7 +34,8 @@ use style_traits::cursor::Cursor;
use text::TextRun;
use text::glyph::ByteIndex;
use webrender_api::{self, ClipAndScrollInfo, ClipId, ColorF, GradientStop, LocalClip};
-use webrender_api::{MixBlendMode, ScrollPolicy, ScrollSensitivity, TransformStyle};
+use webrender_api::{MixBlendMode, ScrollPolicy, ScrollSensitivity, StickyFrameInfo};
+use webrender_api::TransformStyle;
pub use style::dom::OpaqueNode;
@@ -559,6 +560,7 @@ impl fmt::Debug for StackingContext {
#[derive(Clone, Debug, Deserialize, HeapSizeOf, Serialize)]
pub enum ScrollRootType {
ScrollFrame(ScrollSensitivity),
+ StickyFrame(StickyFrameInfo),
Clip,
}
diff --git a/components/layout/block.rs b/components/layout/block.rs
index b22519ee71a..b0bb0c9b671 100644
--- a/components/layout/block.rs
+++ b/components/layout/block.rs
@@ -29,9 +29,9 @@
use app_units::{Au, MAX_AU};
use context::LayoutContext;
-use display_list_builder::{BorderPaintingMode, DisplayListBuildState};
-use display_list_builder::BlockFlowDisplayListBuilding;
-use euclid::{Point2D, Size2D, Rect};
+use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode};
+use display_list_builder::{DisplayListBuildState, EstablishContainingBlock};
+use euclid::{Point2D, Rect, SideOffsets2D, Size2D};
use floats::{ClearType, FloatKind, Floats, PlacementInfo};
use flow::{self, BaseFlow, EarlyAbsolutePositionInfo, Flow, FlowClass, ForceNonfloatedFlag};
use flow::{BLOCK_POSITION_IS_STATIC, CLEARS_LEFT, CLEARS_RIGHT};
@@ -54,7 +54,7 @@ use std::sync::Arc;
use style::computed_values::{border_collapse, box_sizing, display, float, overflow_x};
use style::computed_values::{position, text_align};
use style::context::SharedStyleContext;
-use style::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode};
+use style::logical_geometry::{LogicalMargin, LogicalPoint, LogicalRect, LogicalSize, WritingMode};
use style::properties::ComputedValues;
use style::servo::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW};
use style::values::computed::{LengthOrPercentageOrNone, LengthOrPercentage};
@@ -643,7 +643,7 @@ impl BlockFlow {
&mut self.fragment
}
- pub fn stacking_relative_position(&self, coor: CoordinateSystem) -> Rect<Au> {
+ pub fn stacking_relative_border_box(&self, coor: CoordinateSystem) -> Rect<Au> {
return self.fragment.stacking_relative_border_box(
&self.base.stacking_relative_position,
&self.base.early_absolute_position_info.relative_containing_block_size,
@@ -1787,6 +1787,20 @@ impl BlockFlow {
self.flags.contains(HAS_SCROLLING_OVERFLOW)
}
+ // Return offset from original position because of `position: sticky`.
+ pub fn sticky_position(&self) -> SideOffsets2D<MaybeAuto> {
+ let containing_block_size = &self.base.early_absolute_position_info
+ .relative_containing_block_size;
+ let writing_mode = self.base.early_absolute_position_info.relative_containing_block_mode;
+ let offsets = self.fragment.style().logical_position();
+ let as_margins = LogicalMargin::new(writing_mode,
+ MaybeAuto::from_style(offsets.block_start, containing_block_size.inline),
+ MaybeAuto::from_style(offsets.inline_end, containing_block_size.inline),
+ MaybeAuto::from_style(offsets.block_end, containing_block_size.inline),
+ MaybeAuto::from_style(offsets.inline_start, containing_block_size.inline));
+ as_margins.to_physical(writing_mode)
+ }
+
}
impl Flow for BlockFlow {
@@ -2134,7 +2148,7 @@ impl Flow for BlockFlow {
}
fn collect_stacking_contexts(&mut self, state: &mut DisplayListBuildState) {
- self.collect_stacking_contexts_for_block(state);
+ self.collect_stacking_contexts_for_block(state, EstablishContainingBlock::Yes);
}
fn build_display_list(&mut self, state: &mut DisplayListBuildState) {
diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs
index e7f4474e8f6..6387c72ce3d 100644
--- a/components/layout/display_list_builder.rs
+++ b/components/layout/display_list_builder.rs
@@ -14,7 +14,8 @@ use app_units::{AU_PER_PX, Au};
use block::{BlockFlow, BlockStackingContextType};
use canvas_traits::canvas::{CanvasMsg, FromLayoutMsg};
use context::LayoutContext;
-use euclid::{Transform3D, Point2D, Vector2D, Rect, SideOffsets2D, Size2D, TypedSize2D};
+use euclid::{Point2D, Rect, SideOffsets2D, Size2D, Transform3D, TypedSize2D};
+use euclid::Vector2D;
use flex::FlexFlow;
use flow::{BaseFlow, Flow, IS_ABSOLUTELY_POSITIONED};
use flow_ref::FlowRef;
@@ -72,7 +73,8 @@ use style_traits::ToCss;
use style_traits::cursor::Cursor;
use table_cell::CollapsedBordersForCell;
use webrender_api::{ClipAndScrollInfo, ClipId, ColorF, ComplexClipRegion, GradientStop, LineStyle};
-use webrender_api::{LocalClip, RepeatMode, ScrollPolicy, ScrollSensitivity};
+use webrender_api::{LocalClip, RepeatMode, ScrollPolicy, ScrollSensitivity, StickyFrameInfo};
+use webrender_api::StickySideConstraint;
use webrender_helpers::{ToBorderRadius, ToMixBlendMode, ToRectF, ToTransformStyle};
trait ResolvePercentage {
@@ -101,11 +103,11 @@ fn convert_repeat_mode(from: RepeatKeyword) -> RepeatMode {
}
}
-fn establishes_containing_block_for_absolute(positioning: position::T) -> bool {
- match positioning {
- position::T::absolute | position::T::relative | position::T::fixed => true,
- _ => false,
- }
+fn establishes_containing_block_for_absolute(can_establish_containing_block: EstablishContainingBlock,
+ positioning: position::T)
+ -> bool {
+ can_establish_containing_block == EstablishContainingBlock::Yes &&
+ position::T::static_ != positioning
}
trait RgbColor {
@@ -192,6 +194,9 @@ pub struct DisplayListBuildState<'a> {
/// A stack of clips used to cull display list entries that are outside the
/// rendered region, but only collected at containing block boundaries.
pub containing_block_clip_stack: Vec<Rect<Au>>,
+
+ /// The flow parent's content box, used to calculate sticky constraints.
+ parent_stacking_relative_content_box: Rect<Au>,
}
impl<'a> DisplayListBuildState<'a> {
@@ -211,6 +216,7 @@ impl<'a> DisplayListBuildState<'a> {
iframe_sizes: Vec::new(),
clip_stack: Vec::new(),
containing_block_clip_stack: Vec::new(),
+ parent_stacking_relative_content_box: Rect::zero(),
}
}
@@ -2254,8 +2260,16 @@ impl FragmentDisplayListBuilding for Fragment {
}
}
+#[derive(Clone, Copy, PartialEq)]
+pub enum EstablishContainingBlock {
+ Yes,
+ No,
+}
+
pub trait BlockFlowDisplayListBuilding {
- fn collect_stacking_contexts_for_block(&mut self, state: &mut DisplayListBuildState);
+ fn collect_stacking_contexts_for_block(&mut self,
+ state: &mut DisplayListBuildState,
+ can_establish_containing_block: EstablishContainingBlock);
fn transform_clip_to_coordinate_space(&mut self,
state: &mut DisplayListBuildState,
@@ -2263,8 +2277,12 @@ pub trait BlockFlowDisplayListBuilding {
fn setup_clipping_for_block(&mut self,
state: &mut DisplayListBuildState,
preserved_state: &mut PreservedDisplayListState,
- stacking_context_type: BlockStackingContextType)
+ stacking_context_type: BlockStackingContextType,
+ can_establish_containing_block: EstablishContainingBlock)
-> ClipAndScrollInfo;
+ fn setup_scroll_root_for_position(&mut self,
+ state: &mut DisplayListBuildState,
+ border_box: &Rect<Au>);
fn setup_scroll_root_for_overflow(&mut self,
state: &mut DisplayListBuildState,
border_box: &Rect<Au>);
@@ -2297,6 +2315,7 @@ pub struct PreservedDisplayListState {
containing_block_clip_and_scroll_info: ClipAndScrollInfo,
clips_pushed: usize,
containing_block_clips_pushed: usize,
+ stacking_relative_content_box: Rect<Au>,
}
impl PreservedDisplayListState {
@@ -2308,6 +2327,7 @@ impl PreservedDisplayListState {
containing_block_clip_and_scroll_info: state.containing_block_clip_and_scroll_info,
clips_pushed: 0,
containing_block_clips_pushed: 0,
+ stacking_relative_content_box: state.parent_stacking_relative_content_box,
}
}
@@ -2322,6 +2342,7 @@ impl PreservedDisplayListState {
state.current_real_stacking_context_id = self.real_stacking_context_id;
state.current_clip_and_scroll_info = self.clip_and_scroll_info;
state.containing_block_clip_and_scroll_info = self.containing_block_clip_and_scroll_info;
+ state.parent_stacking_relative_content_box = self.stacking_relative_content_box;
let truncate_length = state.clip_stack.len() - self.clips_pushed;
state.clip_stack.truncate(truncate_length);
@@ -2359,7 +2380,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
if state.clip_stack.is_empty() {
return;
}
- let border_box = self.stacking_relative_position(CoordinateSystem::Parent);
+ let border_box = self.stacking_relative_border_box(CoordinateSystem::Parent);
let transform = match self.fragment.transform_matrix(&border_box) {
Some(transform) => transform,
None => return,
@@ -2412,7 +2433,9 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
}
}
- fn collect_stacking_contexts_for_block(&mut self, state: &mut DisplayListBuildState) {
+ fn collect_stacking_contexts_for_block(&mut self,
+ state: &mut DisplayListBuildState,
+ can_establish_containing_block: EstablishContainingBlock) {
let mut preserved_state = PreservedDisplayListState::new(state);
let block_stacking_context_type = self.block_stacking_context_type();
@@ -2432,8 +2455,13 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
// stored in state.current_clip_and_scroll_info. If we create a stacking context,
// we don't want it to be contained by its own scroll root.
let containing_clip_and_scroll_info =
- self.setup_clipping_for_block(state, &mut preserved_state, block_stacking_context_type);
- if establishes_containing_block_for_absolute(self.positioning()) {
+ self.setup_clipping_for_block(state,
+ &mut preserved_state,
+ block_stacking_context_type,
+ can_establish_containing_block);
+
+ if establishes_containing_block_for_absolute(can_establish_containing_block,
+ self.positioning()) {
state.containing_block_clip_and_scroll_info = state.current_clip_and_scroll_info;
}
@@ -2459,7 +2487,8 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
fn setup_clipping_for_block(&mut self,
state: &mut DisplayListBuildState,
preserved_state: &mut PreservedDisplayListState,
- stacking_context_type: BlockStackingContextType)
+ stacking_context_type: BlockStackingContextType,
+ can_establish_containing_block: EstablishContainingBlock)
-> ClipAndScrollInfo {
// If this block is absolutely positioned, we should be clipped and positioned by
// the scroll root of our nearest ancestor that establishes a containing block.
@@ -2477,26 +2506,33 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
};
self.base.clip_and_scroll_info = Some(containing_clip_and_scroll_info);
- let coordinate_system = if self.fragment.establishes_stacking_context() {
- CoordinateSystem::Own
+ let stacking_relative_border_box = if self.fragment.establishes_stacking_context() {
+ self.stacking_relative_border_box(CoordinateSystem::Own)
} else {
- CoordinateSystem::Parent
+ self.stacking_relative_border_box(CoordinateSystem::Parent)
};
- 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,
- coordinate_system);
-
if stacking_context_type == BlockStackingContextType::StackingContext {
self.transform_clip_to_coordinate_space(state, preserved_state);
}
+ self.setup_scroll_root_for_position(state, &stacking_relative_border_box);
self.setup_scroll_root_for_overflow(state, &stacking_relative_border_box);
self.setup_scroll_root_for_css_clip(state, preserved_state, &stacking_relative_border_box);
self.base.clip = state.clip_stack.last().cloned().unwrap_or_else(max_rect);
+ // We keep track of our position so that any stickily positioned elements can
+ // properly determine the extent of their movement relative to scrolling containers.
+ if can_establish_containing_block == EstablishContainingBlock::Yes {
+ let border_box = if self.fragment.establishes_stacking_context() {
+ stacking_relative_border_box
+ } else {
+ self.stacking_relative_border_box(CoordinateSystem::Own)
+ };
+ state.parent_stacking_relative_content_box =
+ self.fragment.stacking_relative_content_box(&border_box)
+ }
+
match self.positioning() {
position::T::absolute | position::T::relative | position::T::fixed =>
state.containing_block_clip_and_scroll_info = state.current_clip_and_scroll_info,
@@ -2506,6 +2542,72 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
containing_clip_and_scroll_info
}
+ fn setup_scroll_root_for_position(&mut self,
+ state: &mut DisplayListBuildState,
+ border_box: &Rect<Au>) {
+ if self.positioning() != position::T::sticky {
+ return;
+ }
+
+ let sticky_position = self.sticky_position();
+ if sticky_position.left == MaybeAuto::Auto && sticky_position.right == MaybeAuto::Auto &&
+ sticky_position.top == MaybeAuto::Auto && sticky_position.bottom == MaybeAuto::Auto {
+ return;
+ }
+
+ // Since position: sticky elements always establish a stacking context, we will
+ // have previously calculated our border box in our own coordinate system. In
+ // order to properly calculate max offsets we need to compare our size and
+ // position in our parent's coordinate system.
+ let border_box_in_parent = self.stacking_relative_border_box(CoordinateSystem::Parent);
+ let margins = self.fragment.margin.to_physical(
+ self.base.early_absolute_position_info.relative_containing_block_mode);
+
+ // Position:sticky elements are always restricted based on the size and position of
+ // their containing block, which for sticky items is like relative and statically
+ // positioned items: just the parent block.
+ let constraint_rect = state.parent_stacking_relative_content_box;
+
+ let to_max_offset = |constraint_edge: Au, moving_edge: Au| -> f32 {
+ (constraint_edge - moving_edge).to_f32_px()
+ };
+
+ let to_sticky_info = |margin: MaybeAuto, max_offset: f32| -> Option<StickySideConstraint> {
+ match margin {
+ MaybeAuto::Auto => None,
+ MaybeAuto::Specified(value) =>
+ Some(StickySideConstraint { margin: value.to_f32_px(), max_offset }),
+ }
+ };
+
+ let sticky_frame_info = StickyFrameInfo::new(
+ to_sticky_info(sticky_position.top,
+ to_max_offset(constraint_rect.max_y(), border_box_in_parent.max_y())),
+ to_sticky_info(sticky_position.right,
+ to_max_offset(constraint_rect.min_x(), border_box_in_parent.min_x() - margins.left)),
+ to_sticky_info(sticky_position.bottom,
+ to_max_offset(constraint_rect.min_y(), border_box_in_parent.min_y() - margins.top)),
+ to_sticky_info(sticky_position.left,
+ to_max_offset(constraint_rect.max_x(), border_box_in_parent.max_x())));
+
+ let new_scroll_root_id = ClipId::new(self.fragment.unique_id(IdType::OverflowClip),
+ state.layout_context.id.to_webrender());
+ let parent_id = self.clip_and_scroll_info(state.layout_context.id).scroll_node_id;
+ state.add_scroll_root(
+ ScrollRoot {
+ id: new_scroll_root_id,
+ parent_id: parent_id,
+ clip: ClippingRegion::from_rect(border_box),
+ content_rect: Rect::zero(),
+ root_type: ScrollRootType::StickyFrame(sticky_frame_info),
+ },
+ );
+
+ let new_clip_and_scroll_info = ClipAndScrollInfo::simple(new_scroll_root_id);
+ self.base.clip_and_scroll_info = Some(new_clip_and_scroll_info);
+ state.current_clip_and_scroll_info = new_clip_and_scroll_info;
+ }
+
fn setup_scroll_root_for_overflow(&mut self,
state: &mut DisplayListBuildState,
border_box: &Rect<Au>) {
@@ -2737,7 +2839,8 @@ impl InlineFlowDisplayListBuilding for InlineFlow {
for fragment in self.fragments.fragments.iter_mut() {
let previous_cb_clip_scroll_info = state.containing_block_clip_and_scroll_info;
- if establishes_containing_block_for_absolute(fragment.style.get_box().position) {
+ if establishes_containing_block_for_absolute(EstablishContainingBlock::Yes,
+ fragment.style.get_box().position) {
state.containing_block_clip_and_scroll_info = state.current_clip_and_scroll_info;
}
diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs
index 4e0763612ad..8cd37f314ed 100644
--- a/components/layout/fragment.rs
+++ b/components/layout/fragment.rs
@@ -2521,8 +2521,9 @@ impl Fragment {
return true
}
- // Fixed position blocks always create stacking contexts.
- if self.style.get_box().position == position::T::fixed {
+ // Fixed position and sticky position always create stacking contexts.
+ if self.style().get_box().position == position::T::fixed ||
+ self.style().get_box().position == position::T::sticky {
return true
}
diff --git a/components/layout/query.rs b/components/layout/query.rs
index 5803a247e19..f3040376f60 100644
--- a/components/layout/query.rs
+++ b/components/layout/query.rs
@@ -598,6 +598,7 @@ impl FragmentBorderBoxIterator for ParentOffsetBorderBoxIterator {
(true, _, _) |
(false, computed_values::position::T::static_, &SpecificFragmentInfo::Table) |
(false, computed_values::position::T::static_, &SpecificFragmentInfo::TableCell) |
+ (false, computed_values::position::T::sticky, _) |
(false, computed_values::position::T::absolute, _) |
(false, computed_values::position::T::relative, _) |
(false, computed_values::position::T::fixed, _) => true,
@@ -766,7 +767,7 @@ where
let positioned = match style.get_box().position {
position::computed_value::T::relative |
- /*position::computed_value::T::sticky |*/
+ position::computed_value::T::sticky |
position::computed_value::T::fixed |
position::computed_value::T::absolute => true,
_ => false
diff --git a/components/layout/sequential.rs b/components/layout/sequential.rs
index 3c95b75fbb6..1121ace2d89 100644
--- a/components/layout/sequential.rs
+++ b/components/layout/sequential.rs
@@ -95,7 +95,7 @@ pub fn iterate_through_flow_tree_fragment_border_boxes(root: &mut Flow, iterator
flow::base(kid).stacking_relative_position +
stacking_context_position.to_vector();
let relative_position = kid.as_block()
- .stacking_relative_position(CoordinateSystem::Own);
+ .stacking_relative_border_box(CoordinateSystem::Own);
if let Some(matrix) = kid.as_block()
.fragment
.transform_matrix(&relative_position) {
diff --git a/components/layout/table.rs b/components/layout/table.rs
index b56ac7c4d09..974a497bb94 100644
--- a/components/layout/table.rs
+++ b/components/layout/table.rs
@@ -10,7 +10,8 @@ use app_units::Au;
use block::{BlockFlow, CandidateBSizeIterator, ISizeAndMarginsComputer};
use block::{ISizeConstraintInput, ISizeConstraintSolution};
use context::LayoutContext;
-use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode, DisplayListBuildState};
+use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode};
+use display_list_builder::{DisplayListBuildState, EstablishContainingBlock};
use euclid::Point2D;
use flow;
use flow::{BaseFlow, EarlyAbsolutePositionInfo, Flow, FlowClass, ImmutableFlowUtils, OpaqueFlow};
@@ -503,7 +504,7 @@ impl Flow for TableFlow {
}
fn collect_stacking_contexts(&mut self, state: &mut DisplayListBuildState) {
- self.block_flow.collect_stacking_contexts(state);
+ self.block_flow.collect_stacking_contexts_for_block(state, EstablishContainingBlock::Yes);
}
fn repair_style(&mut self, new_style: &::ServoArc<ComputedValues>) {
diff --git a/components/layout/table_caption.rs b/components/layout/table_caption.rs
index 8e5fa302028..39e20d95bbf 100644
--- a/components/layout/table_caption.rs
+++ b/components/layout/table_caption.rs
@@ -9,7 +9,8 @@
use app_units::Au;
use block::BlockFlow;
use context::LayoutContext;
-use display_list_builder::DisplayListBuildState;
+use display_list_builder::{BlockFlowDisplayListBuilding, DisplayListBuildState};
+use display_list_builder::EstablishContainingBlock;
use euclid::Point2D;
use flow::{Flow, FlowClass, OpaqueFlow};
use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
@@ -80,7 +81,7 @@ impl Flow for TableCaptionFlow {
}
fn collect_stacking_contexts(&mut self, state: &mut DisplayListBuildState) {
- self.block_flow.collect_stacking_contexts(state);
+ self.block_flow.collect_stacking_contexts_for_block(state, EstablishContainingBlock::No);
}
fn repair_style(&mut self, new_style: &::ServoArc<ComputedValues>) {
diff --git a/components/layout/table_cell.rs b/components/layout/table_cell.rs
index 370937bc73d..389232345f6 100644
--- a/components/layout/table_cell.rs
+++ b/components/layout/table_cell.rs
@@ -9,7 +9,8 @@
use app_units::Au;
use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayCollapseFlag};
use context::LayoutContext;
-use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode, DisplayListBuildState};
+use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode};
+use display_list_builder::{DisplayListBuildState, EstablishContainingBlock};
use euclid::{Point2D, Rect, SideOffsets2D, Size2D};
use flow::{self, Flow, FlowClass, IS_ABSOLUTELY_POSITIONED, OpaqueFlow};
use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
@@ -260,7 +261,7 @@ impl Flow for TableCellFlow {
}
fn collect_stacking_contexts(&mut self, state: &mut DisplayListBuildState) {
- self.block_flow.collect_stacking_contexts(state);
+ self.block_flow.collect_stacking_contexts_for_block(state, EstablishContainingBlock::No);
}
fn repair_style(&mut self, new_style: &::ServoArc<ComputedValues>) {
diff --git a/components/layout/table_row.rs b/components/layout/table_row.rs
index 3e6365d8c77..070e7da9229 100644
--- a/components/layout/table_row.rs
+++ b/components/layout/table_row.rs
@@ -9,7 +9,8 @@
use app_units::Au;
use block::{BlockFlow, ISizeAndMarginsComputer};
use context::LayoutContext;
-use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode, DisplayListBuildState};
+use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode};
+use display_list_builder::{DisplayListBuildState, EstablishContainingBlock};
use euclid::Point2D;
use flow::{self, EarlyAbsolutePositionInfo, Flow, FlowClass, ImmutableFlowUtils, OpaqueFlow};
use flow_list::MutFlowListIterator;
@@ -478,7 +479,7 @@ impl Flow for TableRowFlow {
}
fn collect_stacking_contexts(&mut self, state: &mut DisplayListBuildState) {
- self.block_flow.collect_stacking_contexts(state);
+ self.block_flow.collect_stacking_contexts_for_block(state, EstablishContainingBlock::No);
}
fn repair_style(&mut self, new_style: &::ServoArc<ComputedValues>) {
diff --git a/components/layout/table_rowgroup.rs b/components/layout/table_rowgroup.rs
index 760a20a72fc..c3cb764686b 100644
--- a/components/layout/table_rowgroup.rs
+++ b/components/layout/table_rowgroup.rs
@@ -9,7 +9,8 @@
use app_units::Au;
use block::{BlockFlow, ISizeAndMarginsComputer};
use context::LayoutContext;
-use display_list_builder::DisplayListBuildState;
+use display_list_builder::{BlockFlowDisplayListBuilding, DisplayListBuildState};
+use display_list_builder::EstablishContainingBlock;
use euclid::Point2D;
use flow::{Flow, FlowClass, OpaqueFlow};
use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
@@ -183,7 +184,7 @@ impl Flow for TableRowGroupFlow {
}
fn collect_stacking_contexts(&mut self, state: &mut DisplayListBuildState) {
- self.block_flow.collect_stacking_contexts(state);
+ self.block_flow.collect_stacking_contexts_for_block(state, EstablishContainingBlock::No);
}
fn repair_style(&mut self, new_style: &::ServoArc<ComputedValues>) {
diff --git a/components/layout/table_wrapper.rs b/components/layout/table_wrapper.rs
index 531176d52d3..5619a487ea7 100644
--- a/components/layout/table_wrapper.rs
+++ b/components/layout/table_wrapper.rs
@@ -17,7 +17,8 @@ use app_units::Au;
use block::{AbsoluteNonReplaced, BlockFlow, FloatNonReplaced, ISizeAndMarginsComputer, ISizeConstraintInput};
use block::{ISizeConstraintSolution, MarginsMayCollapseFlag};
use context::LayoutContext;
-use display_list_builder::DisplayListBuildState;
+use display_list_builder::{BlockFlowDisplayListBuilding, DisplayListBuildState};
+use display_list_builder::EstablishContainingBlock;
use euclid::Point2D;
use floats::FloatKind;
use flow::{Flow, FlowClass, ImmutableFlowUtils, INLINE_POSITION_IS_STATIC, OpaqueFlow};
@@ -463,7 +464,7 @@ impl Flow for TableWrapperFlow {
}
fn collect_stacking_contexts(&mut self, state: &mut DisplayListBuildState) {
- self.block_flow.collect_stacking_contexts(state);
+ self.block_flow.collect_stacking_contexts_for_block(state, EstablishContainingBlock::No);
}
fn repair_style(&mut self, new_style: &::ServoArc<ComputedValues>) {
diff --git a/components/layout/webrender_helpers.rs b/components/layout/webrender_helpers.rs
index 6e5a774e283..6370043542f 100644
--- a/components/layout/webrender_helpers.rs
+++ b/components/layout/webrender_helpers.rs
@@ -493,10 +493,11 @@ impl WebRenderDisplayItemConverter for DisplayItem {
builder.push_clip_id(item.scroll_root.parent_id);
let our_id = item.scroll_root.id;
+ let item_rect = item.scroll_root.clip.main.to_rectf();
let webrender_id = match item.scroll_root.root_type {
ScrollRootType::Clip => {
builder.define_clip(Some(our_id),
- item.scroll_root.clip.main.to_rectf(),
+ item_rect,
item.scroll_root.clip.get_complex_clips(),
None)
}
@@ -508,6 +509,9 @@ impl WebRenderDisplayItemConverter for DisplayItem {
None,
scroll_sensitivity)
}
+ ScrollRootType::StickyFrame(sticky_frame_info) => {
+ builder.define_sticky_frame(Some(our_id), item_rect, sticky_frame_info)
+ }
};
debug_assert!(our_id == webrender_id);
diff --git a/components/style/properties/longhand/box.mako.rs b/components/style/properties/longhand/box.mako.rs
index 659d8c69307..65112ec87ae 100644
--- a/components/style/properties/longhand/box.mako.rs
+++ b/components/style/properties/longhand/box.mako.rs
@@ -204,8 +204,7 @@ ${helpers.single_keyword("-moz-top-layer", "none top",
products="gecko", animation_value_type="none", internal=True,
spec="Internal (not web-exposed)")}
-${helpers.single_keyword("position", "static absolute relative fixed",
- extra_gecko_values="sticky",
+${helpers.single_keyword("position", "static absolute relative fixed sticky",
animation_value_type="discrete",
flags="CREATES_STACKING_CONTEXT ABSPOS_CB",
spec="https://drafts.csswg.org/css-position/#position-property")}
diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json
index 683409fb6cc..41b40df9639 100644
--- a/tests/wpt/mozilla/meta/MANIFEST.json
+++ b/tests/wpt/mozilla/meta/MANIFEST.json
@@ -1403,6 +1403,318 @@
{}
]
],
+ "css/css-position-3/position-sticky-bottom.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-bottom.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-bottom-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-flexbox.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-flexbox.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-flexbox-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-grid.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-grid.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-grid-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-inflow-position.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-inflow-position.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-inflow-position-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-inline.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-inline.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-inline-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-left.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-left.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-left-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-margins.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-margins.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-margins-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-nested-bottom.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-nested-bottom.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-nested-bottom-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-nested-inline.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-nested-inline.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-nested-inline-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-nested-left.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-nested-left.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-nested-left-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-nested-right.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-nested-right.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-nested-right-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-nested-table.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-nested-table.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-nested-table-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-nested-top.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-nested-top.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-nested-top-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-overflow-padding.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-overflow-padding.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-overflow-padding-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-right.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-right.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-right-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-stacking-context.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-stacking-context.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-stacking-context-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-table-tfoot-bottom.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-table-tfoot-bottom.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-table-tfoot-bottom-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-table-th-bottom.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-table-th-bottom.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-table-th-bottom-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-table-th-left.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-table-th-left.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-table-th-left-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-table-th-right.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-table-th-right.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-table-th-right-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-table-th-top.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-table-th-top.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-table-th-top-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-table-thead-top.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-table-thead-top.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-table-thead-top-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-table-tr-bottom.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-table-tr-bottom.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-table-tr-bottom-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-table-tr-top.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-table-tr-top.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-table-tr-top-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-top.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-top.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-top-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-writing-modes.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-writing-modes.html",
+ [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-writing-modes-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
"css/data_img_a.html": [
[
"/_mozilla/css/data_img_a.html",
@@ -8101,6 +8413,136 @@
{}
]
],
+ "css/css-position-3/position-sticky-bottom-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-flexbox-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-grid-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-inflow-position-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-inline-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-left-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-margins-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-nested-bottom-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-nested-inline-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-nested-left-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-nested-right-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-nested-table-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-nested-top-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-overflow-padding-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-right-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-stacking-context-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-table-tfoot-bottom-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-table-th-bottom-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-table-th-left-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-table-th-right-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-table-th-top-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-table-thead-top-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-table-tr-bottom-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-table-tr-top-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-top-ref.html": [
+ [
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-writing-modes-ref.html": [
+ [
+ {}
+ ]
+ ],
"css/css/ahem.css": [
[
{}
@@ -13518,6 +13960,30 @@
{}
]
],
+ "css/css-position-3/position-sticky-get-bounding-client-rect.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-get-bounding-client-rect.html",
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-input-box-gets-focused-after-scroll.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-input-box-gets-focused-after-scroll.html",
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-offset-top-left.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-offset-top-left.html",
+ {}
+ ]
+ ],
+ "css/css-position-3/position-sticky-parsing.html": [
+ [
+ "/_mozilla/css/css-position-3/position-sticky-parsing.html",
+ {}
+ ]
+ ],
"css/empty-keyframes.html": [
[
"/_mozilla/css/empty-keyframes.html",
@@ -22967,6 +23433,230 @@
"768744b9db67d625e4ba3ae1809a967648440503",
"support"
],
+ "css/css-position-3/position-sticky-bottom-ref.html": [
+ "726d6e927d84669e9355701ccd948349d377e6fd",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-bottom.html": [
+ "2a908e60a635dbf765987c0f93d0f33c8ea85de6",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-flexbox-ref.html": [
+ "f8dedb4a637ea3f4bf79eb621f52a8c4622f8c75",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-flexbox.html": [
+ "efb055a7efb5ee3aabd28e369d0bdc9ae98bd33d",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-get-bounding-client-rect.html": [
+ "5b9a1a29084f46228749c1b2b1a664be3ce02c43",
+ "testharness"
+ ],
+ "css/css-position-3/position-sticky-grid-ref.html": [
+ "9748c25d3db9b5ec2753ff53ceb0b82db9453cdc",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-grid.html": [
+ "a06a40f39b4a748c111dc01281261c5451204f95",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-inflow-position-ref.html": [
+ "bcce2ded8073a7b5b3477bcf90157cb0e77c2b40",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-inflow-position.html": [
+ "c8e2bcdddf9e8ee93f9306d88b96c3bf1f1bfaf6",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-inline-ref.html": [
+ "9458cab53d2065e4893d127ee0097bbd53c6b898",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-inline.html": [
+ "9fe0b3407310fbe1ee8b1614db0801bdf93b38be",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-input-box-gets-focused-after-scroll.html": [
+ "6580451dddfd6f8865925326c170f630f343fbcd",
+ "testharness"
+ ],
+ "css/css-position-3/position-sticky-left-ref.html": [
+ "9de7a8ba6019395d729b32e514cc3bd9fee25d2b",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-left.html": [
+ "5151bca08dff652ea728cb8bccbb6b7c6d364dd8",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-margins-ref.html": [
+ "0cdb788c913f47a121114ac5b8e6a140bb08c1ff",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-margins.html": [
+ "72fb6ae7d97bf2448ebd68ccf110edd6bae2c92f",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-nested-bottom-ref.html": [
+ "59a8e46358a8a5bf8638a2d1982c63becef5bc77",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-nested-bottom.html": [
+ "3604a921be04927dd19b805b7c9abaed6d0e7c72",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-nested-inline-ref.html": [
+ "8fb9378e91a20b71ef886b9aac0147b25d00a9a3",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-nested-inline.html": [
+ "50be9f2fb6ab9295081f6f13705be4853e48fdde",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-nested-left-ref.html": [
+ "52804c5589c3035818cd653c1801a70645a9fe99",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-nested-left.html": [
+ "c32881097147e285b6ee66e6239af4808d780c83",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-nested-right-ref.html": [
+ "5703ad6457deca332232e510dc479c39b7020d24",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-nested-right.html": [
+ "39683624316599779b0efcb347010b92694e02a6",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-nested-table-ref.html": [
+ "7b8956bc720e2e25e7ff0bc5889812c26837ab58",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-nested-table.html": [
+ "87a80629bcfcace28d4f13bce99325d55d317574",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-nested-top-ref.html": [
+ "66ea8b8c72023089d52e6ebdf5bfff5d56259bfc",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-nested-top.html": [
+ "88e35164b6ede3adf9727989cf59ff9956bdbae7",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-offset-top-left.html": [
+ "a25b64d016644c272ea92b6129a59eefb21d2fa0",
+ "testharness"
+ ],
+ "css/css-position-3/position-sticky-overflow-padding-ref.html": [
+ "b3d81934cc90e70dff6bc5cd7789594a8fcd7ecf",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-overflow-padding.html": [
+ "588502dc7eb4a7f88f78dd1b2cdc857861c89f77",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-parsing.html": [
+ "224bc984bc6eb4a55931461cf7e51f7b04d219f4",
+ "testharness"
+ ],
+ "css/css-position-3/position-sticky-right-ref.html": [
+ "9a4a11b22cb0ea13f38a7dded8469f4848550ed4",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-right.html": [
+ "f79c0e3e99085e483652950b141fe15c3c4d01d8",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-stacking-context-ref.html": [
+ "dd6e5d4734c924c1ad08d14db986fb89d7cb03f6",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-stacking-context.html": [
+ "ac1e643ac9f03d0fe415c3f52a4db7c407dd3537",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-table-tfoot-bottom-ref.html": [
+ "b902bec7e12fd6d9cad02c61f332a44f5818f8ee",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-table-tfoot-bottom.html": [
+ "4caf24ad5f783119598d52b40425ae3433b61da6",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-table-th-bottom-ref.html": [
+ "14d74f652a67cad449bd7ef75ed18bd906ba3bf0",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-table-th-bottom.html": [
+ "84df11e668ada7f89586b2b82774f3f43e07457c",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-table-th-left-ref.html": [
+ "a9057263cf68862de453cf857961ece6003eb589",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-table-th-left.html": [
+ "e58662697cbf38f24c38562417864ffa38a312ee",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-table-th-right-ref.html": [
+ "8d42e0ff2920782dbfbd060b23817e75125dfb5e",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-table-th-right.html": [
+ "6ea7a6d2e456022d3ebfb91869f751013e27991d",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-table-th-top-ref.html": [
+ "b30c817f263e6e7b695508983952a46eb32494f9",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-table-th-top.html": [
+ "ce4a56682101dc24c9170d3d265fc6a021b5b7ab",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-table-thead-top-ref.html": [
+ "a2b03215ddc3aed6bc81ed678f6339d72132d4df",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-table-thead-top.html": [
+ "2c3d4dd8028d7d79f74aa1460c57f10d087a45f0",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-table-tr-bottom-ref.html": [
+ "77fcc16040cff06c53f18cfe96296f495b752c18",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-table-tr-bottom.html": [
+ "f66365f4ceed9072bf25b98ecc36e49c89625b8b",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-table-tr-top-ref.html": [
+ "8a54752c532080b652ae5ffb384b34ad2c8d8cb4",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-table-tr-top.html": [
+ "7a030d17358067b78c879bf17171b60d1dc3acd9",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-top-ref.html": [
+ "e5a05c21494a2e2923d1ed37050ec75db7ab55cd",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-top.html": [
+ "30c0c00c6313a747b51c8b6d4f1301056af56560",
+ "reftest"
+ ],
+ "css/css-position-3/position-sticky-writing-modes-ref.html": [
+ "407a1831479ccca61f6f7b268abcbf97f667f0bf",
+ "support"
+ ],
+ "css/css-position-3/position-sticky-writing-modes.html": [
+ "b6d16a38b73d4c107e587194818a542fee9d0716",
+ "reftest"
+ ],
"css/css/ahem.css": [
"16a4dd68da41156fbdd139b4a56547f94ad4dbe7",
"support"
diff --git a/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-get-bounding-client-rect.html.ini b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-get-bounding-client-rect.html.ini
new file mode 100644
index 00000000000..dda30195722
--- /dev/null
+++ b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-get-bounding-client-rect.html.ini
@@ -0,0 +1,17 @@
+[position-sticky-get-bounding-client-rect.html]
+ type: testharness
+ [Untitled]
+ expected: FAIL
+
+ [sticky positioned element should be observable by getBoundingClientRect.]
+ bug: https://github.com/servo/servo/issues/18378
+ expected: FAIL
+
+ [getBoundingClientRect should be correct for sticky after script insertion]
+ bug: https://github.com/servo/servo/issues/18378
+ expected: FAIL
+
+ [getBoundingClientRect should be correct for sticky after script-caused layout]
+ bug: https://github.com/servo/servo/issues/18378
+ expected: FAIL
+
diff --git a/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-grid.html.ini b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-grid.html.ini
new file mode 100644
index 00000000000..6addc8bd48f
--- /dev/null
+++ b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-grid.html.ini
@@ -0,0 +1,7 @@
+[position-sticky-grid.html]
+ type: reftest
+ expected: FAIL
+ bug: https://github.com/servo/servo/issues/18379
+ [Untitled]
+ expected: FAIL
+
diff --git a/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-inline.html.ini b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-inline.html.ini
new file mode 100644
index 00000000000..ea196f37eaf
--- /dev/null
+++ b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-inline.html.ini
@@ -0,0 +1,7 @@
+[position-sticky-inline.html]
+ type: reftest
+ expected: FAIL
+ bug: https://github.com/servo/servo/issues/18379
+ [Untitled]
+ expected: FAIL
+
diff --git a/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-bottom.html.ini b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-bottom.html.ini
new file mode 100644
index 00000000000..54aa5d5dac5
--- /dev/null
+++ b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-bottom.html.ini
@@ -0,0 +1,8 @@
+[position-sticky-nested-bottom.html]
+ type: reftest
+ expected: FAIL
+ bug: https://github.com/servo/servo/issues/18377
+ [Untitled]
+ expected: FAIL
+
+
diff --git a/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-inline.html.ini b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-inline.html.ini
new file mode 100644
index 00000000000..ce556804061
--- /dev/null
+++ b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-inline.html.ini
@@ -0,0 +1,7 @@
+[position-sticky-nested-inline.html]
+ type: reftest
+ expected: FAIL
+ bug: https://github.com/servo/servo/issues/18377
+ [Untitled]
+ expected: FAIL
+
diff --git a/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-left.html.ini b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-left.html.ini
new file mode 100644
index 00000000000..82d0898354d
--- /dev/null
+++ b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-left.html.ini
@@ -0,0 +1,7 @@
+[position-sticky-nested-left.html]
+ type: reftest
+ expected: FAIL
+ bug: https://github.com/servo/servo/issues/18377
+ [Untitled]
+ expected: FAIL
+
diff --git a/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-right.html.ini b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-right.html.ini
new file mode 100644
index 00000000000..a2c00e7e01a
--- /dev/null
+++ b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-right.html.ini
@@ -0,0 +1,7 @@
+[position-sticky-nested-right.html]
+ type: reftest
+ expected: FAIL
+ bug: https://github.com/servo/servo/issues/18377
+ [Untitled]
+ expected: FAIL
+
diff --git a/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-table.html.ini b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-table.html.ini
new file mode 100644
index 00000000000..a683e0857d5
--- /dev/null
+++ b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-table.html.ini
@@ -0,0 +1,7 @@
+[position-sticky-nested-table.html]
+ type: reftest
+ expected: FAIL
+ bug: https://github.com/servo/servo/issues/18377
+ [Untitled]
+ expected: FAIL
+
diff --git a/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-top.html.ini b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-top.html.ini
new file mode 100644
index 00000000000..5546b107335
--- /dev/null
+++ b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-nested-top.html.ini
@@ -0,0 +1,7 @@
+[position-sticky-nested-top.html]
+ type: reftest
+ expected: FAIL
+ bug: https://github.com/servo/servo/issues/18377
+ [Untitled]
+ expected: FAIL
+
diff --git a/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-offset-top-left.html.ini b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-offset-top-left.html.ini
new file mode 100644
index 00000000000..b3f2ba9f7f7
--- /dev/null
+++ b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-offset-top-left.html.ini
@@ -0,0 +1,10 @@
+[position-sticky-offset-top-left.html]
+ type: testharness
+ [offsetTop/offsetLeft should be correct for sticky after script insertion]
+ bug: https://github.com/servo/servo/issues/18378
+ expected: FAIL
+
+ [offsetTop/offsetLeft should be correct for sticky after script-caused layout]
+ bug: https://github.com/servo/servo/issues/18378
+ expected: FAIL
+
diff --git a/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-overflow-padding.html.ini b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-overflow-padding.html.ini
new file mode 100644
index 00000000000..19b8e90d5b7
--- /dev/null
+++ b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-overflow-padding.html.ini
@@ -0,0 +1,7 @@
+[position-sticky-overflow-padding.html]
+ type: reftest
+ expected: FAIL
+ bug: https://github.com/servo/servo/issues/18379
+ [Untitled]
+ expected: FAIL
+
diff --git a/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-parsing.html.ini b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-parsing.html.ini
new file mode 100644
index 00000000000..cd280be0963
--- /dev/null
+++ b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-parsing.html.ini
@@ -0,0 +1,6 @@
+[position-sticky-parsing.html]
+ type: testharness
+ [The value of sticky for the position property should be parsed correctly]
+ bug: https://github.com/servo/servo/issues/18378
+ expected: FAIL
+
diff --git a/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-writing-modes.html.ini b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-writing-modes.html.ini
new file mode 100644
index 00000000000..2b068da9664
--- /dev/null
+++ b/tests/wpt/mozilla/meta/css/css-position-3/position-sticky-writing-modes.html.ini
@@ -0,0 +1,7 @@
+[position-sticky-writing-modes.html]
+ type: reftest
+ expected: FAIL
+ bug: https://github.com/servo/servo/issues/18379
+ [Untitled]
+ expected: FAIL
+
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-bottom-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-bottom-ref.html
new file mode 100644
index 00000000000..7d4953d7797
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-bottom-ref.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky elements should respect the top constraint</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 500px;
+}
+
+.indicator {
+ background-color: green;
+ position: absolute;
+ left: 0;
+}
+
+.box {
+ width: 100%;
+ height: 100px;
+}
+
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 100;
+ document.getElementById('scroller2').scrollTop = 175;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="contents">
+ <div class="indicator box" style="top: 200px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="contents">
+ <div class="indicator box" style="top: 250px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="contents">
+ <div class="indicator box" style="top: 300px;"></div>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-bottom.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-bottom.html
new file mode 100644
index 00000000000..18c5cc415c5
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-bottom.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<title>position:sticky elements should respect the bottom constraint</title>
+<link rel="match" href="position-sticky-bottom-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky elements obey their bottom anchor after scrolling" />
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 500px;
+}
+
+.prepadding {
+ height: 200px;
+}
+
+.container {
+ height: 200px;
+}
+
+.filler {
+ height: 100px;
+}
+
+.indicator {
+ background-color: red;
+ position: absolute;
+ left: 0;
+}
+
+.sticky {
+ background-color: green;
+ position: sticky;
+ bottom: 25px;
+}
+
+.box {
+ width: 100%;
+ height: 100px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 100;
+ document.getElementById('scroller2').scrollTop = 175;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator box" style="top: 200px;"></div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="filler"></div>
+ <div class="sticky box"></div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator box" style="top: 250px;"></div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="filler"></div>
+ <div class="sticky box"></div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator box" style="top: 300px;"></div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="filler"></div>
+ <div class="sticky box"></div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-flexbox-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-flexbox-ref.html
new file mode 100644
index 00000000000..554ffa6d6bc
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-flexbox-ref.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky elements should work correctly with flexbox</title>
+
+<style>
+.scroller {
+ overflow: scroll;
+ width: 350px;
+ height: 100px;
+ margin-bottom: 15px;
+}
+
+.flex-container {
+ width: 600px;
+ position: relative;
+ display: flex;
+ flex-flow: row wrap;
+}
+
+.green {
+ background-color: green;
+}
+
+.flex-item {
+ height: 85px;
+ width: 100px;
+ display: flex;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 50;
+ document.getElementById('scroller2').scrollLeft = 150;
+ document.getElementById('scroller3').scrollLeft = 250;
+});
+</script>
+
+<div id="scroller1" class="scroller">
+ <div class="flex-container">
+ <div class="flex-item"></div>
+ <div class="green flex-item"></div>
+ <div class="green flex-item"></div>
+ </div>
+</div>
+
+<div id="scroller2" class="scroller">
+ <div class="flex-container">
+ <div class="flex-item"></div>
+ <div class="flex-item"></div>
+ <div class="green flex-item"></div>
+ </div>
+</div>
+
+<div id="scroller3" class="scroller">
+ <div class="flex-container">
+ <div class="flex-item"></div>
+ <div class="flex-item"></div>
+ <div class="green flex-item"></div>
+ <div class="green flex-item"></div>
+ </div>
+</div>
+
+<p>You should see three green boxes of varying size above. There should be no red.</p>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-flexbox.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-flexbox.html
new file mode 100644
index 00000000000..cf4e8cbcdaa
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-flexbox.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<title>position:sticky elements should work correctly with flexbox</title>
+<link rel="match" href="position-sticky-flexbox-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky elements interoperate correctly with flexbox" />
+
+<style>
+.scroller {
+ overflow: scroll;
+ width: 350px;
+ height: 100px;
+ margin-bottom: 15px;
+}
+
+.flex-container {
+ width: 600px;
+ position: relative;
+ display: flex;
+ flex-flow: row wrap;
+}
+
+.sticky {
+ position: sticky;
+ left: 50px;
+}
+
+.green {
+ background-color: green;
+}
+
+.flex-item {
+ width: 100px;
+ height: 85px;
+ display: flex;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ width: 100px;
+ height: 85px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 50;
+ document.getElementById('scroller2').scrollLeft = 150;
+ document.getElementById('scroller3').scrollLeft = 250;
+});
+</script>
+
+<div id="scroller1" class="scroller">
+ <div class="flex-container">
+ <div class="indicator" style="left: 100px;"></div>
+ <div class="flex-item"></div>
+ <div class="sticky green flex-item"></div>
+ <div class="green flex-item"></div>
+ </div>
+</div>
+
+<div id="scroller2" class="scroller">
+ <div class="flex-container">
+ <div class="indicator" style="left: 200px;"></div>
+ <div class="flex-item"></div>
+ <div class="sticky green flex-item"></div>
+ <div class="green flex-item"></div>
+ </div>
+</div>
+
+<div id="scroller3" class="scroller">
+ <div class="flex-container">
+ <div class="indicator" style="left: 300px;"></div>
+ <div class="flex-item"></div>
+ <div class="sticky green flex-item"></div>
+ <div class="green flex-item"></div>
+ </div>
+</div>
+
+<p>You should see three green boxes of varying size above. There should be no red.</p>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-get-bounding-client-rect.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-get-bounding-client-rect.html
new file mode 100644
index 00000000000..18b2acfe16f
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-get-bounding-client-rect.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<title>Sticky positioned element should be observable by getBoundingClientRect.</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that sticky positioned element
+should be observable by getBoundingClientRect." />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+
+.container {
+ overflow: scroll;
+ width: 200px;
+ height: 200px;
+}
+
+.spacer {
+ width: 2000px;
+ height: 2000px;
+}
+
+.box {
+ width: 100px;
+ height: 100px;
+ background-color: green;
+}
+
+.sticky {
+ position: sticky;
+ top: 50px;
+ left: 20px;
+}
+</style>
+
+<div id="scroller1" class="container">
+ <div id="sticky1" class="sticky box"></div>
+ <div class="spacer"></div>
+</div>
+
+<script>
+test(() => {
+ var sticky = document.getElementById('sticky1');
+ assert_equals(sticky.getBoundingClientRect().top, 50);
+ document.getElementById('scroller1').scrollTop = 100;
+ assert_equals(sticky.getBoundingClientRect().top, 50);
+ sticky.style.position = 'relative';
+ assert_equals(sticky.getBoundingClientRect().top, -50);
+ sticky.style.position = 'sticky';
+ assert_equals(sticky.getBoundingClientRect().top, 50);
+}, 'sticky positioned element should be observable by getBoundingClientRect.');
+</script>
+
+<div id="scroller2" class="container">
+ <div class="spacer"></div>
+</div>
+
+<script>
+test(() => {
+ var scroller = document.getElementById('scroller2');
+ scroller.scrollTop = 100;
+ scroller.scrollLeft = 75;
+
+ var sticky = document.createElement('div');
+ sticky.className = 'sticky box';
+ scroller.insertBefore(sticky, scroller.querySelector('.spacer'));
+
+ var sticky_bounds = sticky.getBoundingClientRect();
+ var scroller_bounds = scroller.getBoundingClientRect();
+ assert_equals(sticky_bounds.top, scroller_bounds.top + 50);
+ assert_equals(sticky_bounds.left, scroller_bounds.left + 20);
+}, 'getBoundingClientRect should be correct for sticky after script insertion');
+</script>
+
+<div id="scroller3" class="container">
+ <div id="sticky3" class="sticky box"></div>
+ <div class="spacer"></div>
+</div>
+
+<script>
+test(() => {
+ var scroller = document.getElementById('scroller3');
+ var sticky = document.getElementById('sticky3');
+ scroller.scrollTop = 100;
+ scroller.scrollLeft = 75;
+
+ var div = document.createElement('div');
+ div.style.height = '65px';
+ scroller.insertBefore(div, sticky);
+
+ var sticky_bounds = sticky.getBoundingClientRect();
+ var scroller_bounds = scroller.getBoundingClientRect();
+ assert_equals(sticky_bounds.top, scroller_bounds.top + 50);
+ assert_equals(sticky_bounds.left, scroller_bounds.left + 20);
+}, 'getBoundingClientRect should be correct for sticky after script-caused layout');
+</script>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-grid-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-grid-ref.html
new file mode 100644
index 00000000000..9af81129a63
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-grid-ref.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky elements should work correctly with grid layout</title>
+
+<style>
+.scroller {
+ position: relative;
+ overflow-x: scroll;
+ overflow-y: hidden;
+ width: 300px;
+ height: 100px;
+ margin-bottom: 15px;
+}
+
+.grid-container {
+ display: grid;
+ grid-template-columns: 25% 25% 25% 25%;
+ grid-template-rows: 100%;
+ width: 400px;
+ height: 90px;
+}
+
+.green {
+ background-color: green;
+}
+
+.grid-item {
+ width: 100%;
+ height: 100%;
+ grid-row: 1;
+}
+
+.padding {
+ height: 1px;
+ width: 700px;
+}
+
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 0;
+ document.getElementById('scroller2').scrollLeft = 150;
+ document.getElementById('scroller3').scrollLeft = 300;
+});
+</script>
+
+<div id="scroller1" class="scroller">
+ <div class="grid-container">
+ <div class="grid-item" style="grid-column: 1;"></div>
+ <div class="green grid-item" style="grid-column: 2;"></div>
+ <div class="green grid-item" style="grid-column: 3;"></div>
+ </div>
+ <div class="padding"></div>
+</div>
+
+<div id="scroller2" class="scroller">
+ <div class="grid-container">
+ <div class="grid-item" style="grid-column: 1;"></div>
+ <div class="grid-item" style="grid-column: 2;"></div>
+ <div class="green grid-item" style="grid-column: 3;"></div>
+ </div>
+ <div class="padding"></div>
+</div>
+
+<div id="scroller3" class="scroller">
+ <div class="grid-container">
+ <div class="grid-item" style="grid-column: 1;"></div>
+ <div class="grid-item" style="grid-column: 2;"></div>
+ <div class="grid-item" style="grid-column: 3;"></div>
+ <div class="green grid-item" style="grid-column: 4;"></div>
+ </div>
+ <div class="padding"></div>
+</div>
+
+<p>You should see three green boxes of varying size above. There should be no red.</p>
+
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-grid.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-grid.html
new file mode 100644
index 00000000000..c2c1b8ec74c
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-grid.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<title>position:sticky elements should work correctly with grid layout</title>
+<link rel="match" href="position-sticky-grid-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky elements interoperate correctly with grid" />
+
+<style>
+.scroller {
+ position: relative;
+ overflow-x: scroll;
+ overflow-y: hidden;
+ width: 300px;
+ height: 100px;
+ margin-bottom: 15px;
+}
+
+.grid-container {
+ display: grid;
+ grid-template-columns: 25% 25% 25% 25%;
+ grid-template-rows: 100%;
+ width: 400px;
+ height: 90px;
+}
+
+.sticky {
+ position: sticky;
+ left: 50px;
+}
+
+.green {
+ background-color: green;
+}
+
+.grid-item {
+ width: 100%;
+ height: 100%;
+ grid-row: 1;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ width: 100px;
+ height: 90px;
+}
+
+.padding {
+ height: 1px;
+ width: 700px;
+}
+
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 0;
+ document.getElementById('scroller2').scrollLeft = 150;
+ document.getElementById('scroller3').scrollLeft = 300;
+});
+</script>
+
+<div id="scroller1" class="scroller">
+ <div class="grid-container">
+ <div class="indicator" style="left: 100px;"></div>
+ <div class="grid-item" style="grid-column: 1;"></div>
+ <div class="sticky green grid-item" style="grid-column: 2;"></div>
+ <div class="green grid-item" style="grid-column: 3;"></div>
+ </div>
+ <div class="padding"></div>
+</div>
+
+<div id="scroller2" class="scroller">
+ <div class="grid-container">
+ <div class="indicator" style="left: 200px;"></div>
+ <div class="grid-item" style="grid-column: 1;"></div>
+ <div class="sticky green grid-item" style="grid-column: 2;"></div>
+ <div class="green grid-item" style="grid-column: 3;"></div>
+ </div>
+ <div class="padding"></div>
+</div>
+
+<div id="scroller3" class="scroller">
+ <div class="grid-container">
+ <div class="indicator" style="left: 300px;"></div>
+ <div class="grid-item" style="grid-column: 1;"></div>
+ <div class="sticky green grid-item" style="grid-column: 2;"></div>
+ <div class="green grid-item" style="grid-column: 3;"></div>
+ </div>
+ <div class="padding"></div>
+</div>
+
+<p>You should see three green boxes of varying size above. There should be no red.</p>
+
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-inflow-position-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-inflow-position-ref.html
new file mode 100644
index 00000000000..beebd7e5aeb
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-inflow-position-ref.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky elements should not affect the flow position of other elements</title>
+
+<style>
+.scroller {
+ height: 200px;
+ width: 100px;
+ overflow-y: scroll;
+ margin-bottom: 15px;
+}
+
+.sticky {
+ background-color: green;
+}
+
+.box {
+ height: 50px;
+ width: 50px;
+}
+
+.before {
+ background-color: fuchsia;
+}
+
+.after {
+ background-color: orange;
+}
+
+.padding {
+ height: 450px;
+}
+</style>
+
+<div class="scroller">
+ <div class="before box"></div>
+ <div class="box"></div>
+ <div class="after box"></div>
+ <div class="sticky box"></div>
+ <div class="padding"></div>
+</div>
+
+<div>You should see a fuchsia box, a one-box gap, an orange box, and then a green box above.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-inflow-position.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-inflow-position.html
new file mode 100644
index 00000000000..fc7e8290b15
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-inflow-position.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<title>position:sticky elements should not affect the flow position of other elements</title>
+<link rel="match" href="position-sticky-inflow-position-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky elements do not affect the flow position of other elements" />
+
+<style>
+.scroller {
+ height: 200px;
+ width: 100px;
+ overflow-y: scroll;
+ margin-bottom: 15px;
+}
+
+.sticky {
+ background-color: green;
+ position: sticky;
+ top: 150px;
+}
+
+.box {
+ height: 50px;
+ width: 50px;
+}
+
+.before {
+ background-color: fuchsia;
+}
+
+.after {
+ background-color: orange;
+}
+
+.padding {
+ height: 500px;
+}
+</style>
+
+<div class="scroller">
+ <div class="before box"></div>
+ <div class="sticky box"></div>
+ <div class="after box"></div>
+ <div class="padding"></div>
+</div>
+
+<div>You should see a fuchsia box, a one-box gap, an orange box, and then a green box above.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-inline-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-inline-ref.html
new file mode 100644
index 00000000000..11e2909f47b
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-inline-ref.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky should work for inline elements</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+ font: 25px/1 Ahem;
+}
+
+.contents {
+ height: 500px;
+}
+
+.indicator {
+ position: absolute;
+ left: 0;
+ color: green;
+}
+
+.inline {
+ display: inline;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="contents">
+ <div class="indicator inline" style="top: 150px;">XXX</div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="contents">
+ <div class="indicator inline" style="top: 175px;">XXX</div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="contents">
+ <div class="indicator inline" style="top: 275px;">XXX</div>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green rectangles above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-inline.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-inline.html
new file mode 100644
index 00000000000..cc8694d4236
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-inline.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<title>position:sticky should work for inline elements</title>
+<link rel="match" href="position-sticky-inline-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky works for inline elements" />
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+ font: 25px/1 Ahem;
+}
+
+.contents {
+ height: 500px;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+.container {
+ height: 200px;
+}
+
+.innerpadding {
+ height: 50px;
+}
+
+.indicator {
+ position: absolute;
+ left: 0;
+ color: red;
+}
+
+.sticky {
+ position: sticky;
+ top: 50px;
+ color: green;
+}
+
+.inline {
+ display: inline;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator inline" style="top: 150px;">XXX</div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding"></div>
+ <div class="sticky inline">XXX</div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator inline" style="top: 175px;">XXX</div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding"></div>
+ <div class="sticky inline">XXX</div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator inline" style="top: 275px;">XXX</div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding"></div>
+ <div class="sticky inline">XXX</div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green rectangles above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-input-box-gets-focused-after-scroll.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-input-box-gets-focused-after-scroll.html
new file mode 100644
index 00000000000..5b2d705e2d0
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-input-box-gets-focused-after-scroll.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>Focusing on visible sticky input box should not scroll the page.</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that focusing on visible sticky
+positioned input box should not scroll the page." />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+body {
+ height: 2000px;
+}
+
+input {
+ position: sticky;
+ top: 10px;
+}
+</style>
+
+<input type="text" id="input"/>
+
+<script>
+test(() => {
+ var input = document.getElementById('input');
+ window.scrollTo(0, 100);
+ input.focus();
+ assert_equals(window.scrollY, 100);
+}, 'Focusing on visible sticky input box should not scroll the page.');
+</script>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-left-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-left-ref.html
new file mode 100644
index 00000000000..e0de6fbb812
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-left-ref.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky elements should respect the left constraint</title>
+
+<style>
+.group {
+ position: relative;
+ width: 250px;
+ height: 150px;
+}
+
+.scroller {
+ position: relative;
+ width: 200px;
+ height: 100px;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.contents {
+ height: 100%;
+ width: 500px;
+}
+
+.indicator {
+ background-color: green;
+ position: absolute;
+ top: 0;
+}
+
+.box {
+ height: 100%;
+ width: 100px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 50;
+ document.getElementById('scroller2').scrollLeft = 125;
+ document.getElementById('scroller3').scrollLeft = 200;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="contents">
+ <div class="indicator box" style="left: 150px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="contents">
+ <div class="indicator box" style="left: 175px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="contents">
+ <div class="indicator box" style="left: 200px;"></div>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-left.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-left.html
new file mode 100644
index 00000000000..40a4d7282db
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-left.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<title>position:sticky elements should respect the left constraint</title>
+<link rel="match" href="position-sticky-left-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky elements obey their left anchor after scrolling" />
+
+<style>
+.group {
+ position: relative;
+ width: 250px;
+ height: 150px;
+}
+
+.scroller {
+ position: relative;
+ width: 200px;
+ height: 100px;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.contents {
+ height: 100%;
+ width: 500px;
+}
+
+.prepadding {
+ display: inline-block;
+ height: 100%;
+ width: 100px;
+}
+
+.container {
+ display: inline-block;
+ height: 100%;
+ width: 200px;
+}
+
+.innerpadding {
+ display: inline-block;
+ height: 100%;
+ width: 50px;
+}
+
+.indicator {
+ background-color: red;
+ position: absolute;
+ top: 0;
+}
+
+.sticky {
+ background-color: green;
+ position: sticky;
+ left: 50px;
+}
+
+.box {
+ display: inline-block;
+ height: 100%;
+ width: 100px;
+}
+
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 50;
+ document.getElementById('scroller2').scrollLeft = 125;
+ document.getElementById('scroller3').scrollLeft = 200;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator box" style="left: 150px;"></div>
+ <div class="contents">
+ <!-- As these elements are inline, they are whitespace sensitive. -->
+ <div class="prepadding"></div><div class="container"><div class="innerpadding"></div><div class="sticky box"></div></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator box" style="left: 175px;"></div>
+ <div class="contents">
+ <!-- As these elements are inline, they are whitespace sensitive. -->
+ <div class="prepadding"></div><div class="container"><div class="innerpadding"></div><div class="sticky box"></div></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator box" style="left: 200px;"></div>
+ <div class="contents">
+ <!-- As these elements are inline, they are whitespace sensitive. -->
+ <div class="prepadding"></div><div class="container"><div class="innerpadding"></div><div class="sticky box"></div></div>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-margins-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-margins-ref.html
new file mode 100644
index 00000000000..30490398923
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-margins-ref.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky elements should properly interact with margins</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 180px;
+ height: 400px;
+}
+
+.scroller {
+ width: 150px;
+ height: 300px;
+ overflow-y: scroll;
+ overflow-x: hidden;
+}
+
+.indicator {
+ position: relative;
+ background-color: green;
+ margin: 15px;
+}
+
+.box {
+ width: 100px;
+ height: 100px;
+}
+
+.padding {
+ height: 385px;
+}
+</style>
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 0;
+ document.getElementById('scroller2').scrollTop = 60;
+ document.getElementById('scroller3').scrollTop = 120;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator box" style="top: 0;"></div>
+ <div class="padding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator box" style="top: 50px;"></div>
+ <div class="padding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator box" style="top: 85px;"></div>
+ <div class="padding"></div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-margins.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-margins.html
new file mode 100644
index 00000000000..5b38ab4c996
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-margins.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<title>position:sticky elements should properly interact with margins</title>
+<link rel="match" href="position-sticky-margins-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="position:sticky elements should ignore margins when sticking, but consider them when making sure sticky elements do not escape their containing block" />
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 180px;
+ height: 400px;
+}
+
+.scroller {
+ position: relative;
+ width: 150px;
+ height: 300px;
+ overflow-y: scroll;
+ overflow-x: hidden;
+}
+
+.holder {
+ width: 130px;
+ height: 200px;
+}
+
+.sticky {
+ position: sticky;
+ background-color: green;
+ top: 5px;
+ margin: 15px;
+}
+
+.indicator {
+ left: 15px;
+ position: absolute;
+ background-color: red;
+}
+
+.box {
+ width: 100px;
+ height: 100px;
+}
+
+.padding {
+ height: 300px;
+}
+</style>
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 0;
+ document.getElementById('scroller2').scrollTop = 60;
+ document.getElementById('scroller3').scrollTop = 120;
+});
+</script>
+
+<!-- Before sticking, the margin should be obeyed. -->
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator box" style="top: 15px;"></div>
+ <div class="holder">
+ <div class="sticky box"></div>
+ </div>
+ <div class="padding"></div>
+ </div>
+</div>
+
+<!-- Whilst stuck, the margin is irrelevant. -->
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator box" style="top: 65px;"></div>
+ <div class="holder">
+ <div class="sticky box"></div>
+ </div>
+ <div class="padding"></div>
+ </div>
+</div>
+
+<!-- The margin does count when making sure the sticky element does not escape
+ its containing block. -->
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator box" style="top: 100px;"></div>
+ <div class="holder">
+ <div class="sticky box"></div>
+ </div>
+ <div class="padding"></div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-bottom-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-bottom-ref.html
new file mode 100644
index 00000000000..6be2b5a7b1d
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-bottom-ref.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<title>Reference for nested bottom-constrained position:sticky elements should render correctly</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 500px;
+}
+
+.outerIndicator {
+ background-color: green;
+ position: absolute;
+ left: 0;
+ width: 100%;
+ height: 100px;
+}
+
+.innerIndicator {
+ background-color: blue;
+ position: absolute;
+ left: 0;
+ width: 100%;
+ height: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 75;
+ document.getElementById('scroller2').scrollTop = 175;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="contents">
+ <div class="outerIndicator" style="top: 200px;"></div>
+ <div class="innerIndicator" style="top: 200px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="contents">
+ <div class="outerIndicator" style="top: 250px;"></div>
+ <div class="innerIndicator" style="top: 290px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="contents">
+ <div class="outerIndicator" style="top: 300px;"></div>
+ <div class="innerIndicator" style="top: 350px;"></div>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green and three blue boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-bottom.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-bottom.html
new file mode 100644
index 00000000000..d4d20e571a3
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-bottom.html
@@ -0,0 +1,135 @@
+<!DOCTYPE html>
+<title>Nested bottom-constrained position:sticky elements should render correctly</title>
+<link rel="match" href="position-sticky-nested-bottom-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that nested position:sticky elements with a bottom constraint render correctly" />
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 500px;
+}
+
+.prepadding {
+ height: 200px;
+}
+
+.container {
+ height: 200px;
+}
+
+.filler {
+ height: 100px;
+}
+
+.outerIndicator {
+ background-color: red;
+ position: absolute;
+ left: 0;
+ width: 100%;
+ height: 100px;
+}
+
+.innerIndicator {
+ background-color: red;
+ position: absolute;
+ left: 0;
+ width: 100%;
+ height: 50px;
+}
+
+.outerSticky {
+ background-color: green;
+ position: sticky;
+ bottom: 25px;
+ width: 100%;
+ height: 100px;
+}
+
+.innerFiller {
+ height: 50px;
+}
+
+.innerSticky {
+ background-color: blue;
+ position: sticky;
+ bottom: 35px;
+ width: 100%;
+ height: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 75;
+ document.getElementById('scroller2').scrollTop = 175;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="outerIndicator" style="top: 200px;"></div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="filler"></div>
+ <div class="outerSticky">
+ <div class="innerIndicator" style="top: 0;"></div>
+ <div class="innerFiller"></div>
+ <div class="innerSticky"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="outerIndicator" style="top: 250px;"></div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="filler"></div>
+ <div class="outerSticky">
+ <div class="innerIndicator" style="top: 40px;"></div>
+ <div class="innerFiller"></div>
+ <div class="innerSticky"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="outerIndicator" style="top: 300px;"></div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="filler"></div>
+ <div class="outerSticky">
+ <div class="innerIndicator" style="top: 50px;"></div>
+ <div class="innerFiller"></div>
+ <div class="innerSticky"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green and three blue boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-inline-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-inline-ref.html
new file mode 100644
index 00000000000..694a3cc8403
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-inline-ref.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<title>Reference for nested inline position:sticky elements should render correctly</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+ font: 25px/1 Ahem;
+}
+
+.contents {
+ height: 500px;
+}
+
+.outerIndicator {
+ color: green;
+ position: absolute;
+ left: 0;
+}
+
+.innerIndicator {
+ color: blue;
+ position: absolute;
+ left: 25px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 225;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="contents">
+ <div class="outerIndicator" style="top: 150px;">X</div>
+ <div class="innerIndicator" style="top: 150px;">XX</div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="contents">
+ <div class="outerIndicator" style="top: 175px;">X</div>
+ <div class="innerIndicator" style="top: 185px;">XX</div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="contents">
+ <div class="outerIndicator" style="top: 275px;">X</div>
+ <div class="innerIndicator" style="top: 275px;">XX</div>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green and three blue rectangles above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-inline.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-inline.html
new file mode 100644
index 00000000000..f44c0e69b59
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-inline.html
@@ -0,0 +1,114 @@
+<!DOCTYPE html>
+<title>Nested inline position:sticky elements should render correctly</title>
+<link rel="match" href="position-sticky-nested-inline-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that nested inline position:sticky elements render correctly" />
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+ font: 25px/1 Ahem;
+}
+
+.contents {
+ height: 500px;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+.container {
+ height: 200px;
+}
+
+.innerpadding {
+ height: 50px;
+}
+
+.outerIndicator {
+ color: red;
+ position: absolute;
+ left: 0;
+}
+
+.innerIndicator {
+ color: red;
+ position: absolute;
+ left: 25px;
+}
+
+.outerSticky {
+ display: inline;
+ color: green;
+ position: sticky;
+ top: 50px;
+}
+
+.innerSticky {
+ display: inline;
+ color: blue;
+ position: sticky;
+ top: 60px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 225;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="outerIndicator" style="top: 150px;">X</div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding"></div>
+ <div class="outerSticky">X<div class="innerIndicator" style="top: 0;">XX</div><div class="innerSticky">XX</div></div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="outerIndicator" style="top: 175px;">X</div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding"></div>
+ <div class="outerSticky">X<div class="innerIndicator" style="top: 10px;">XX</div><div class="innerSticky">XX</div></div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="outerIndicator" style="top: 200px;">X</div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding"></div>
+ <div class="outerSticky">X<div class="innerIndicator" style="top: 0;">XX</div><div class="innerSticky">XX</div></div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green and three blue rectangles above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-left-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-left-ref.html
new file mode 100644
index 00000000000..d3ab0d593d6
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-left-ref.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<title>Reference for nested left-constrained position:sticky elements should render correctly</title>
+
+<style>
+.group {
+ position: relative;
+ width: 250px;
+ height: 150px;
+}
+
+.scroller {
+ position: relative;
+ width: 200px;
+ height: 100px;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.contents {
+ height: 100%;
+ width: 500px;
+}
+
+.outerIndicator {
+ background-color: green;
+ position: absolute;
+ top: 0;
+ height: 100%;
+ width: 100px;
+}
+
+.innerIndicator {
+ background-color: blue;
+ position: absolute;
+ top: 0;
+ height: 100%;
+ width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 50;
+ document.getElementById('scroller2').scrollLeft = 125;
+ document.getElementById('scroller3').scrollLeft = 225;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="contents">
+ <div class="outerIndicator" style="left: 150px;"></div>
+ <div class="innerIndicator" style="left: 150px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="contents">
+ <div class="outerIndicator" style="left: 175px;"></div>
+ <div class="innerIndicator" style="left: 185px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="contents">
+ <div class="outerIndicator" style="left: 200px;"></div>
+ <div class="innerIndicator" style="left: 250px;"></div>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green and three blue boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-left.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-left.html
new file mode 100644
index 00000000000..b5a23abeccf
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-left.html
@@ -0,0 +1,141 @@
+<!DOCTYPE html>
+<title>Nested left-constrained position:sticky elements should render correctly</title>
+<link rel="match" href="position-sticky-nested-left-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that nested position:sticky elements with a left constraint render correctly" />
+
+<style>
+.group {
+ position: relative;
+ width: 250px;
+ height: 150px;
+}
+
+.scroller {
+ position: relative;
+ width: 200px;
+ height: 100px;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.contents {
+ height: 100%;
+ width: 500px;
+ /* Allow nice formatting of inline divs. Fonts are not used in this test. */
+ font-size: 0;
+}
+
+.prepadding {
+ display: inline-block;
+ height: 100%;
+ width: 100px;
+}
+
+.container {
+ display: inline-block;
+ height: 100%;
+ width: 200px;
+}
+
+.innerpadding {
+ display: inline-block;
+ height: 100%;
+ width: 50px;
+}
+
+.outerIndicator {
+ background-color: red;
+ position: absolute;
+ top: 0;
+ display: inline-block;
+ height: 100%;
+ width: 100px;
+}
+
+.innerIndicator {
+ background-color: red;
+ position: absolute;
+ top: 0;
+ display: inline-block;
+ height: 100%;
+ width: 50px;
+}
+
+.outerSticky {
+ background-color: green;
+ position: sticky;
+ left: 50px;
+ display: inline-block;
+ height: 100%;
+ width: 100px;
+}
+
+.innerSticky {
+ background-color: blue;
+ position: sticky;
+ left: 60px;
+ display: inline-block;
+ height: 100%;
+ width: 50px;
+}
+
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 50;
+ document.getElementById('scroller2').scrollLeft = 125;
+ document.getElementById('scroller3').scrollLeft = 225;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="outerIndicator" style="left: 150px;"></div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding"></div>
+ <div class="outerSticky">
+ <div class="innerIndicator" style="left: 0;"></div>
+ <div class="innerSticky"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="outerIndicator" style="left: 175px;"></div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding">
+ </div><div class="outerSticky">
+ <div class="innerIndicator" style="left: 10px;"></div>
+ <div class="innerSticky"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="outerIndicator" style="left: 200px;"></div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding"></div>
+ <div class="outerSticky">
+ <div class="innerIndicator" style="left: 50px;"></div>
+ <div class="innerSticky"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green and three blue boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-right-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-right-ref.html
new file mode 100644
index 00000000000..07852ba6041
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-right-ref.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky elements should respect the right constraint</title>
+
+<style>
+.group {
+ position: relative;
+ width: 250px;
+ height: 150px;
+}
+
+.scroller {
+ position: relative;
+ width: 200px;
+ height: 100px;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.contents {
+ height: 100%;
+ width: 500px;
+}
+
+.outerIndicator {
+ background-color: green;
+ position: absolute;
+ top: 0;
+ height: 100%;
+ width: 100px;
+}
+
+.innerIndicator {
+ background-color: blue;
+ position: absolute;
+ top: 0;
+ height: 100%;
+ width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 75;
+ document.getElementById('scroller2').scrollLeft = 175;
+ document.getElementById('scroller3').scrollLeft = 250;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="contents">
+ <div class="outerIndicator" style="left: 200px;"></div>
+ <div class="innerIndicator" style="left: 200px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="contents">
+ <div class="outerIndicator" style="left: 250px;"></div>
+ <div class="innerIndicator" style="left: 290px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="contents">
+ <div class="outerIndicator" style="left: 300px;"></div>
+ <div class="innerIndicator" style="left: 350px;"></div>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green and three blue boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-right.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-right.html
new file mode 100644
index 00000000000..411f722eec7
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-right.html
@@ -0,0 +1,149 @@
+<!DOCTYPE html>
+<title>Nested right-constrained position:sticky elements should render correctly</title>
+<link rel="match" href="position-sticky-nested-right-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that nested position:sticky elements with a right constraint render correctly" />
+
+<style>
+.group {
+ position: relative;
+ width: 250px;
+ height: 150px;
+}
+
+.scroller {
+ position: relative;
+ width: 200px;
+ height: 100px;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.contents {
+ height: 100%;
+ width: 500px;
+ /* Allow nice formatting of inline divs. Fonts are not used in this test. */
+ font-size: 0;
+}
+
+.prepadding {
+ display: inline-block;
+ height: 100%;
+ width: 200px;
+}
+
+.container {
+ display: inline-block;
+ height: 100%;
+ width: 200px;
+}
+
+.filler {
+ display: inline-block;
+ height: 100%;
+ width: 100px;
+}
+
+.outerIndicator {
+ background-color: red;
+ position: absolute;
+ top: 0;
+ display: inline-block;
+ height: 100%;
+ width: 100px;
+}
+
+.innerIndicator {
+ background-color: red;
+ position: absolute;
+ top: 0;
+ display: inline-block;
+ height: 100%;
+ width: 50px;
+}
+
+.outerSticky {
+ background-color: green;
+ position: sticky;
+ right: 25px;
+ display: inline-block;
+ height: 100%;
+ width: 100px;
+}
+
+.innerFiller {
+ display: inline-block;
+ height: 100%;
+ width: 50px;
+}
+
+.innerSticky {
+ background-color: blue;
+ position: sticky;
+ right: 35px;
+ display: inline-block;
+ height: 100%;
+ width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 75;
+ document.getElementById('scroller2').scrollLeft = 175;
+ document.getElementById('scroller3').scrollLeft = 250;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="outerIndicator" style="left: 200px;"></div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="filler"></div>
+ <div class="outerSticky">
+ <div class="innerIndicator" style="left: 0;"></div>
+ <div class="innerFiller"></div>
+ <div class="innerSticky"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="outerIndicator" style="left: 250px;"></div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="filler"></div>
+ <div class="outerSticky">
+ <div class="innerIndicator" style="left: 40px;"></div>
+ <div class="innerFiller"></div>
+ <div class="innerSticky"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="outerIndicator" style="left: 300px;"></div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="filler"></div>
+ <div class="outerSticky">
+ <div class="innerIndicator" style="left: 50px;"></div>
+ <div class="innerFiller"></div>
+ <div class="innerSticky"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green and three blue boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-table-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-table-ref.html
new file mode 100644
index 00000000000..9327d04e83f
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-table-ref.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<title>Reference for nested position:sticky table elements should render correctly</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 700px;
+}
+
+.indicator {
+ position: absolute;
+ left: 0;
+ background-color: green;
+ height: 50px;
+ width: 50px;
+}
+
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="contents">
+ <div class="indicator" style="top: 100px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="contents">
+ <div class="indicator" style="top: 150px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="contents">
+ <div class="indicator" style="top: 250px;"></div>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green rectangles above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-table.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-table.html
new file mode 100644
index 00000000000..be3fbce1560
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-table.html
@@ -0,0 +1,131 @@
+<!DOCTYPE html>
+<title>Nested position:sticky table elements should render correctly</title>
+<link rel="match" href="position-sticky-nested-table-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that nested position:sticky table elements render correctly" />
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 700px;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+table {
+ border-collapse: collapse;
+}
+
+td, th {
+ height: 50px;
+ width: 50px;
+ padding: 0;
+}
+
+th {
+ background: green;
+}
+
+.sticky {
+ position: sticky;
+ top: 25px;
+}
+
+.indicator {
+ position: absolute;
+ left: 0;
+ background-color: red;
+ height: 50px;
+ width: 50px;
+}
+
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="contents">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <thead class="sticky">
+ <tr class="sticky">
+ <th class="sticky"></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="contents">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <thead class="sticky">
+ <tr class="sticky">
+ <th class="sticky"></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="contents">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <thead class="sticky">
+ <tr class="sticky">
+ <th class="sticky"></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ <tr><td></td></tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green rectangles above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-top-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-top-ref.html
new file mode 100644
index 00000000000..f4696ce7d0a
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-top-ref.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<title>Reference for nested top-constrained position:sticky elements should render correctly</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 500px;
+}
+
+.green {
+ background-color: green;
+}
+
+.blue {
+ background-color: blue;
+}
+
+.indicator {
+ position: absolute;
+ left: 0;
+}
+
+.bigBox {
+ width: 100%;
+ height: 100px;
+}
+
+.smallBox {
+ width: 100%;
+ height: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 225;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="contents">
+ <div class="green indicator bigBox" style="top: 150px;"></div>
+ <div class="blue indicator smallBox" style="top: 150px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="contents">
+ <div class="green indicator bigBox" style="top: 175px;"></div>
+ <div class="blue indicator smallBox" style="top: 185px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="contents">
+ <div class="green indicator bigBox" style="top: 200px;"></div>
+ <div class="blue indicator smallBox" style="top: 250px;"></div>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green and three blue boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-top.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-top.html
new file mode 100644
index 00000000000..1ed9dc2a210
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-nested-top.html
@@ -0,0 +1,128 @@
+<!DOCTYPE html>
+<title>Nested top-constrainted position:sticky elements should render correctly</title>
+<link rel="match" href="position-sticky-nested-top-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that nested position:sticky elements with a top constraint render correctly" />
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 500px;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+.container {
+ height: 200px;
+}
+
+.innerpadding {
+ height: 50px;
+}
+
+.outerIndicator {
+ background-color: red;
+ position: absolute;
+ left: 0;
+ height: 100px;
+ width: 100%;
+}
+
+.innerIndicator {
+ background-color: red;
+ position: absolute;
+ left: 0;
+ height: 50px;
+ width: 100%;
+}
+
+.outerSticky {
+ background-color: green;
+ position: sticky;
+ top: 50px;
+ width: 100%;
+ height: 100px;
+}
+
+.innerSticky {
+ background-color: blue;
+ position: sticky;
+ top: 60px;
+ width: 100%;
+ height: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 225;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="outerIndicator" style="top: 150px;"></div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding"></div>
+ <div class="outerSticky">
+ <div class="innerIndicator" style="top: 0;"></div>
+ <div class="innerSticky"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="outerIndicator" style="top: 175px;"></div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding"></div>
+ <div class="outerSticky">
+ <div class="innerIndicator" style="top: 10px;"></div>
+ <div class="innerSticky"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="outerIndicator" style="top: 200px;"></div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding"></div>
+ <div class="outerSticky">
+ <div class="innerIndicator" style="top: 50px;"></div>
+ <div class="innerSticky"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green and three blue boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-offset-top-left.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-offset-top-left.html
new file mode 100644
index 00000000000..ade9e108cf4
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-offset-top-left.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<title>Sticky positioned element should be observable by offsetTop and offsetLeft</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that a sticky positioned element
+should be observable by offsetTop/offsetLeft." />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+
+.container {
+ position: relative; /* Required for offsetTop/offsetLeft tests. */
+ overflow: scroll;
+ width: 200px;
+ height: 200px;
+}
+
+.spacer {
+ width: 2000px;
+ height: 2000px;
+}
+
+.box {
+ width: 100px;
+ height: 100px;
+ background-color: green;
+}
+
+.sticky {
+ position: sticky;
+ top: 50px;
+ left: 20px;
+}
+</style>
+
+<div id="scroller1" class="container">
+ <div class="spacer"></div>
+</div>
+
+<script>
+test(() => {
+ var scroller = document.getElementById('scroller1');
+ scroller.scrollTop = 100;
+ scroller.scrollLeft = 75;
+
+ var sticky = document.createElement('div');
+ sticky.className = 'sticky box';
+ scroller.insertBefore(sticky, scroller.querySelector('.spacer'));
+
+ assert_equals(sticky.offsetTop, scroller.scrollTop + 50);
+ assert_equals(sticky.offsetLeft, scroller.scrollLeft + 20);
+}, 'offsetTop/offsetLeft should be correct for sticky after script insertion');
+</script>
+
+<div id="scroller2" class="container">
+ <div id="sticky2" class="sticky box"></div>
+ <div class="spacer"></div>
+</div>
+
+<script>
+test(function() {
+ var scroller = document.getElementById('scroller2');
+ var sticky = document.getElementById('sticky2');
+ scroller.scrollTop = 100;
+ scroller.scrollLeft = 75;
+
+ var div = document.createElement('div');
+ div.style.height = '65px';
+ scroller.insertBefore(div, sticky);
+
+ assert_equals(sticky.offsetTop, scroller.scrollTop + 50);
+ assert_equals(sticky.offsetLeft, scroller.scrollLeft + 20);
+}, 'offsetTop/offsetLeft should be correct for sticky after script-caused layout');
+</script>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-overflow-padding-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-overflow-padding-ref.html
new file mode 100644
index 00000000000..b0e1d4680bc
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-overflow-padding-ref.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky elements should respect padding on their ancestor overflow element</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.scroller {
+ padding: 20px 0;
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 500px;
+}
+
+.indicator {
+ background-color: green;
+ position: absolute;
+ left: 0;
+}
+
+.box {
+ width: 100%;
+ height: 100px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 175;
+ document.getElementById('scroller3').scrollTop = 220;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="contents">
+ <div class="indicator box" style="top: 170px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="contents">
+ <div class="indicator box" style="top: 195px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="contents">
+ <div class="indicator box" style="top: 220px;"></div>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-overflow-padding.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-overflow-padding.html
new file mode 100644
index 00000000000..0324861219b
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-overflow-padding.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+<title>position:sticky elements should respect padding on their ancestor overflow element</title>
+<link rel="match" href="position-sticky-overflow-padding-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky elements respect padding on their ancestor overflow element" />
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.scroller {
+ /* The target sticky position should be offset by this padding. */
+ padding: 20px 0;
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 500px;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+.container {
+ height: 200px;
+}
+
+.innerpadding {
+ height: 50px;
+}
+
+.indicator {
+ background-color: red;
+ position: absolute;
+ left: 0;
+}
+
+.sticky {
+ background-color: green;
+ position: sticky;
+ top: 0;
+}
+
+.box {
+ width: 100%;
+ height: 100px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 175;
+ document.getElementById('scroller3').scrollTop = 220;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator box" style="top: 170px;"></div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding"></div>
+ <div class="sticky box"></div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator box" style="top: 195px;"></div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding"></div>
+ <div class="sticky box"></div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator box" style="top: 220px;"></div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding"></div>
+ <div class="sticky box"></div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-parsing.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-parsing.html
new file mode 100644
index 00000000000..f6f587e615f
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-parsing.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<title>Position value 'sticky' should be a valid value</title>
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#position-property" />
+<meta name="assert" content="This test checks that setting position to 'sticky'
+should be allowed." />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- We need something to create elements in. -->
+<body></body>
+
+<script>
+// Sticky is valid for all elements except table-column-group and table-column.
+const VALID_STICKY_DISPLAY_TYPES = [
+ 'block',
+ 'inline',
+ 'run-in',
+ 'flow',
+ 'flow-root',
+ 'table',
+ 'flex',
+ 'grid',
+ 'ruby',
+ 'subgrid',
+ 'list-item',
+ 'table-row-group',
+ 'table-header-group',
+ 'table-footer-group',
+ 'table-row',
+ 'table-cell',
+ 'table-caption',
+ 'ruby-base',
+ 'ruby-text',
+ 'ruby-base-container',
+ 'ruby-text-container',
+ 'contents',
+ 'none',
+];
+
+const INVALID_STICKY_DISPLAY_TYPES = [
+ 'table-column-group',
+ 'table-column',
+];
+
+test(() => {
+ for (displayValue of VALID_STICKY_DISPLAY_TYPES) {
+ let div = document.createElement('div');
+ let style = `position: sticky; display: ${displayValue};`;
+ div.setAttribute('style', style);
+ document.body.appendChild(div);
+
+ // We only check display values that the browser under test recognizes.
+ if (div.style.display == displayValue) {
+ assert_equals(getComputedStyle(div).position, 'sticky',
+ `Expected sticky to be valid for display: ${displayValue}`);
+ }
+ document.body.removeChild(div);
+ }
+
+ for (displayValue of INVALID_STICKY_DISPLAY_TYPES) {
+ let div = document.createElement('div');
+ let style = `position: sticky; display: ${displayValue};`;
+ div.setAttribute('style', style);
+ document.body.appendChild(div);
+
+ assert_not_equals(getComputedStyle(div).position, 'sticky',
+ `Expected sticky to be invalid for display: ${displayValue}`);
+ document.body.removeChild(div);
+ }
+}, 'The value of sticky for the position property should be parsed correctly');
+</script>
+
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-right-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-right-ref.html
new file mode 100644
index 00000000000..14ed476834b
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-right-ref.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky elements should respect the right constraint</title>
+
+<style>
+.group {
+ position: relative;
+ width: 250px;
+ height: 150px;
+}
+
+.scroller {
+ position: relative;
+ width: 200px;
+ height: 100px;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.contents {
+ height: 100%;
+ width: 500px;
+}
+
+.indicator {
+ background-color: green;
+ position: absolute;
+ top: 0;
+}
+
+.box {
+ height: 100%;
+ width: 100px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 100;
+ document.getElementById('scroller2').scrollLeft = 175;
+ document.getElementById('scroller3').scrollLeft = 250;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="contents">
+ <div class="indicator box" style="left: 200px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="contents">
+ <div class="indicator box" style="left: 250px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="contents">
+ <div class="indicator box" style="left: 300px;"></div>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-right.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-right.html
new file mode 100644
index 00000000000..6c4e696de8d
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-right.html
@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<title>position:sticky elements should respect the right constraint</title>
+<link rel="match" href="position-sticky-right-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky elements obey their right anchor after scrolling" />
+
+<style>
+.group {
+ position: relative;
+ width: 250px;
+ height: 150px;
+}
+
+.scroller {
+ position: relative;
+ width: 200px;
+ height: 100px;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.contents {
+ height: 100%;
+ width: 500px;
+}
+
+.prepadding {
+ display: inline-block;
+ height: 100%;
+ width: 200px;
+}
+
+.container {
+ display: inline-block;
+ height: 100%;
+ width: 200px;
+}
+
+.filler {
+ display: inline-block;
+ height: 100%;
+ width: 100px;
+}
+
+.indicator {
+ background-color: red;
+ position: absolute;
+ top: 0;
+}
+
+.sticky {
+ background-color: green;
+ position: sticky;
+ right: 25px;
+}
+
+.box {
+ display: inline-block;
+ height: 100%;
+ width: 100px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 100;
+ document.getElementById('scroller2').scrollLeft = 175;
+ document.getElementById('scroller3').scrollLeft = 250;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator box" style="left: 200px;"></div>
+ <div class="contents">
+ <!-- As these elements are inline, they are whitespace sensitive. -->
+ <div class="prepadding"></div><div class="container"><div class="filler"></div><div class="sticky box"></div></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator box" style="left: 250px;"></div>
+ <div class="contents">
+ <!-- As these elements are inline, they are whitespace sensitive. -->
+ <div class="prepadding"></div><div class="container"><div class="filler"></div><div class="sticky box"></div></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator box" style="left: 300px;"></div>
+ <div class="contents">
+ <!-- As these elements are inline, they are whitespace sensitive. -->
+ <div class="prepadding"></div><div class="container"><div class="filler"></div><div class="sticky box"></div></div>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-stacking-context-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-stacking-context-ref.html
new file mode 100644
index 00000000000..0fe20bd3c33
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-stacking-context-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>Reference for position: sticky should create a stacking context</title>
+
+<style>
+.indicator {
+ background-color: green;
+}
+
+.box {
+ width: 200px;
+ height: 200px;
+}
+</style>
+
+<div class="indicator box"></div>
+
+<div>You should see a single green box above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-stacking-context.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-stacking-context.html
new file mode 100644
index 00000000000..36acf82f4f9
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-stacking-context.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<title>position: sticky should create a stacking context</title>
+<link rel="match" href="position-sticky-stacking-context-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="position:sticky elements should create a stacking context" />
+
+<style>
+.indicator {
+ position: absolute;
+ background-color: green;
+ z-index: 1;
+}
+
+.sticky {
+ position: sticky;
+ z-index: 0;
+}
+
+.child {
+ position: relative;
+ background-color: red;
+ z-index: 2;
+}
+
+.box {
+ width: 200px;
+ height: 200px;
+}
+</style>
+
+<div class="indicator box"></div>
+<div class="sticky box">
+ <!-- Because sticky forms a stacking context, this child remains on bottom
+ even though it has a higher z-index than the indicator box. -->
+ <div class="child box"></div>
+</div>
+
+<div>You should see a single green box above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tfoot-bottom-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tfoot-bottom-ref.html
new file mode 100644
index 00000000000..a89dd6a485e
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tfoot-bottom-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky bottom constraint should behave correctly for &lt;tfoot&gt; elements</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 550px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: green;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 0;
+ document.getElementById('scroller2').scrollTop = 75;
+ document.getElementById('scroller3').scrollTop = 200;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tfoot-bottom.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tfoot-bottom.html
new file mode 100644
index 00000000000..17fe359948c
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tfoot-bottom.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<title>position:sticky bottom constraint should behave correctly for &lt;tfoot&gt; elements</title>
+<link rel="match" href="position-sticky-table-tfoot-bottom-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that the position:sticky bottom constraint behaves correctly for &lt;tfoot&gt; elements" />
+
+<style>
+table {
+ border-collapse:collapse;
+}
+
+td, th {
+ padding: 0;
+}
+
+td > div, th > div {
+ height: 50px;
+ width: 50px;
+}
+
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+.postpadding {
+ height: 250px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+
+.sticky {
+ position: sticky;
+ bottom: 25px;
+ background-color: green;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 0;
+ document.getElementById('scroller2').scrollTop = 75;
+ document.getElementById('scroller3').scrollTop = 200;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ <tfoot class="sticky">
+ <tr><th><div></div></th></tr>
+ </tfoot>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ <tfoot class="sticky">
+ <tr><th><div></div></th></tr>
+ </tfoot>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ <tfoot class="sticky">
+ <tr><th><div></div></th></tr>
+ </tfoot>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-bottom-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-bottom-ref.html
new file mode 100644
index 00000000000..2aa5c08a55d
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-bottom-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky bottom constraint should behave correctly for &lt;th&gt; elements</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 550px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: green;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 0;
+ document.getElementById('scroller2').scrollTop = 75;
+ document.getElementById('scroller3').scrollTop = 200;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-bottom.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-bottom.html
new file mode 100644
index 00000000000..878732fd5bc
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-bottom.html
@@ -0,0 +1,127 @@
+<!DOCTYPE html>
+<title>position:sticky bottom constraint should behave correctly for &lt;th&gt; elements</title>
+<link rel="match" href="position-sticky-table-th-bottom-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that the position:sticky bottom constraint behaves correctly for &lt;th&gt; elements" />
+
+<style>
+table {
+ border-collapse:collapse;
+}
+
+td, th {
+ padding: 0;
+}
+
+td > div, th > div {
+ height: 50px;
+ width: 50px;
+}
+
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+.postpadding {
+ height: 250px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+
+.sticky {
+ position: sticky;
+ bottom: 25px;
+ background-color: green;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 0;
+ document.getElementById('scroller2').scrollTop = 75;
+ document.getElementById('scroller3').scrollTop = 200;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ <tfoot>
+ <tr>
+ <th class="sticky"><div></div></th>
+ </tr>
+ </tfoot>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ <tfoot>
+ <tr>
+ <th class="sticky"><div></div></th>
+ </tr>
+ </tfoot>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ <tfoot>
+ <tr>
+ <th class="sticky"><div></div></th>
+ </tr>
+ </tfoot>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-left-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-left-ref.html
new file mode 100644
index 00000000000..cef7539c16c
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-left-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky left constraint should behave correctly for &lt;th&gt; elements</title>
+
+<style>
+.group {
+ position: relative;
+ width: 250px;
+ height: 150px;
+}
+
+.scroller {
+ position: relative;
+ width: 200px;
+ height: 100px;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.contents {
+ height: 10px;
+ width: 500px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: green;
+ top: 0;
+ height: 50px;
+ width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 50;
+ document.getElementById('scroller2').scrollLeft = 125;
+ document.getElementById('scroller3').scrollLeft = 250;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="left: 100px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="left: 150px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="left: 250px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-left.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-left.html
new file mode 100644
index 00000000000..45643506dc9
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-left.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<title>position:sticky left constraint should behave correctly for &lt;th&gt; elements</title>
+<link rel="match" href="position-sticky-table-th-left-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that the position:sticky left constraint behaves correctly for &lt;th&gt; elements" />
+
+<style>
+table {
+ border-collapse: collapse;
+ margin-left: 100px;
+}
+
+td, th {
+ padding: 0;
+}
+
+td > div, th > div {
+ height: 50px;
+ width: 50px;
+}
+
+.group {
+ position: relative;
+ width: 250px;
+ height: 150px;
+}
+
+.scroller {
+ position: relative;
+ width: 200px;
+ height: 100px;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.postpadding {
+ height: 10px;
+ width: 500px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ top: 0;
+ height: 50px;
+ width: 50px;
+}
+
+.sticky {
+ position: sticky;
+ left: 25px;
+ background-color: green;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 50;
+ document.getElementById('scroller2').scrollLeft = 125;
+ document.getElementById('scroller3').scrollLeft = 250;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="left: 100px;"></div>
+ <table>
+ <tbody>
+ <tr>
+ <th class="sticky"><div></div></th>
+ <td><div></div></td>
+ <td><div></div></td>
+ <td><div></div></td>
+ </tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="left: 150px;"></div>
+ <table>
+ <tbody>
+ <tr>
+ <th class="sticky"><div></div></th>
+ <td><div></div></td>
+ <td><div></div></td>
+ <td><div></div></td>
+ </tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="left: 250px;"></div>
+ <table>
+ <tbody>
+ <tr>
+ <th class="sticky"><div></div></th>
+ <td><div></div></td>
+ <td><div></div></td>
+ <td><div></div></td>
+ </tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-right-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-right-ref.html
new file mode 100644
index 00000000000..5d778d1bfc1
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-right-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky right constraint should behave correctly for &lt;th&gt; elements</title>
+
+<style>
+.group {
+ position: relative;
+ width: 250px;
+ height: 150px;
+}
+
+.scroller {
+ position: relative;
+ width: 200px;
+ height: 100px;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.contents {
+ height: 10px;
+ width: 500px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: green;
+ top: 0;
+ height: 50px;
+ width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 0;
+ document.getElementById('scroller2').scrollLeft = 75;
+ document.getElementById('scroller3').scrollLeft = 200;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="left: 150px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="left: 200px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="left: 300px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-right.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-right.html
new file mode 100644
index 00000000000..204b6e5d043
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-right.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<title>position:sticky right constraint should behave correctly for &lt;th&gt; elements</title>
+<link rel="match" href="position-sticky-table-th-right-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that the position:sticky right constraint behaves correctly for &lt;th&gt; elements" />
+
+<style>
+table {
+ border-collapse: collapse;
+ margin-left: 150px;
+}
+
+td, th {
+ padding: 0;
+}
+
+td > div, th > div {
+ height: 50px;
+ width: 50px;
+}
+
+.group {
+ position: relative;
+ width: 250px;
+ height: 150px;
+}
+
+.scroller {
+ position: relative;
+ width: 200px;
+ height: 100px;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.postpadding {
+ height: 10px;
+ width: 500px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ top: 0;
+ height: 50px;
+ width: 50px;
+}
+
+.sticky {
+ position: sticky;
+ right: 25px;
+ background-color: green;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollLeft = 0;
+ document.getElementById('scroller2').scrollLeft = 75;
+ document.getElementById('scroller3').scrollLeft = 200;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="left: 150px;"></div>
+ <table>
+ <tbody>
+ <tr>
+ <td><div></div></td>
+ <td><div></div></td>
+ <td><div></div></td>
+ <th class="sticky"><div></div></th>
+ </tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="left: 200px;"></div>
+ <table>
+ <tbody>
+ <tr>
+ <td><div></div></td>
+ <td><div></div></td>
+ <td><div></div></td>
+ <th class="sticky"><div></div></th>
+ </tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="left: 300px;"></div>
+ <table>
+ <tbody>
+ <tr>
+ <td><div></div></td>
+ <td><div></div></td>
+ <td><div></div></td>
+ <th class="sticky"><div></div></th>
+ </tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-top-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-top-ref.html
new file mode 100644
index 00000000000..692dbcfdffd
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-top-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky top constraint should behave correctly for &lt;th&gt; elements</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 550px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: green;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-top.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-top.html
new file mode 100644
index 00000000000..7e57a690084
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-th-top.html
@@ -0,0 +1,127 @@
+<!DOCTYPE html>
+<title>position:sticky top constraint should behave correctly for &lt;th&gt; elements</title>
+<link rel="match" href="position-sticky-table-th-top-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that the position:sticky top constraint behaves correctly for &lt;th&gt; elements" />
+
+<style>
+table {
+ border-collapse:collapse;
+}
+
+td, th {
+ padding: 0;
+}
+
+td > div, th > div {
+ height: 50px;
+ width: 50px;
+}
+
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+.postpadding {
+ height: 250px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+
+.sticky {
+ position: sticky;
+ top: 25px;
+ background-color: green;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <thead>
+ <tr>
+ <th class="sticky"><div></div></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="prepadding" style="background: pink;"></div>
+ <table>
+ <thead>
+ <tr>
+ <th class="sticky"><div></div></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <thead>
+ <tr>
+ <th class="sticky"><div></div></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-thead-top-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-thead-top-ref.html
new file mode 100644
index 00000000000..f313d60859c
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-thead-top-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky top constraint should behave correctly for &lt;thead&gt; elements</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 550px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: green;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-thead-top.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-thead-top.html
new file mode 100644
index 00000000000..560a45efeb1
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-thead-top.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<title>position:sticky top constraint should behave correctly for &lt;thead&gt; elements</title>
+<link rel="match" href="position-sticky-table-thead-top-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that the position:sticky top constraint behaves correctly for &lt;thead&gt; elements" />
+
+<style>
+table {
+ border-collapse:collapse;
+}
+
+td, th {
+ padding: 0;
+}
+
+td > div, th > div {
+ height: 50px;
+ width: 50px;
+}
+
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+.postpadding {
+ height: 250px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+
+.sticky {
+ position: sticky;
+ top: 25px;
+ background-color: green;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <thead class="sticky">
+ <tr><th><div></div></th></tr>
+ </thead>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <thead class="sticky">
+ <tr><th><div></div></th></tr>
+ </thead>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <thead class="sticky">
+ <tr><th><div></div></th></tr>
+ </thead>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tr-bottom-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tr-bottom-ref.html
new file mode 100644
index 00000000000..7f9ef074962
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tr-bottom-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky bottom constraint should behave correctly for &lt;tr&gt; elements</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 550px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: green;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 0;
+ document.getElementById('scroller2').scrollTop = 75;
+ document.getElementById('scroller3').scrollTop = 200;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tr-bottom.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tr-bottom.html
new file mode 100644
index 00000000000..e306d9654a2
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tr-bottom.html
@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<title>position:sticky bottom constraint should behave correctly for &lt;tr&gt; elements</title>
+<link rel="match" href="position-sticky-table-tr-bottom-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that the position:sticky bottom constraint behaves correctly for &lt;tr&gt; elements" />
+
+<style>
+table {
+ border-collapse:collapse;
+}
+
+td {
+ padding: 0;
+}
+
+td > div {
+ height: 50px;
+ width: 50px;
+}
+
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+.postpadding {
+ height: 250px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+
+.sticky {
+ position: sticky;
+ bottom: 25px;
+ background-color: green;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 0;
+ document.getElementById('scroller2').scrollTop = 75;
+ document.getElementById('scroller3').scrollTop = 200;
+});
+</script>
+
+<!-- .sticky element pushed as far up as possible to table edge -->
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr class="sticky"><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<!-- .sticky element stuck to bottom of .scroller -->
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr class="sticky"><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<!-- .sticky element unstuck -->
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr class="sticky"><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tr-top-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tr-top-ref.html
new file mode 100644
index 00000000000..9aa9242d9ce
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tr-top-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky top constraint should behave correctly for &lt;tr&gt; elements</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 550px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: green;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="contents"></div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tr-top.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tr-top.html
new file mode 100644
index 00000000000..361535c99c2
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-table-tr-top.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<title>position:sticky top constraint should behave correctly for &lt;tr&gt; elements</title>
+<link rel="match" href="position-sticky-table-tr-top-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that the position:sticky top constraint behaves correctly for &lt;tr&gt; elements" />
+
+<style>
+table {
+ border-collapse:collapse;
+}
+
+td {
+ padding: 0;
+}
+
+td > div {
+ height: 50px;
+ width: 50px;
+}
+
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 200px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 150px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+.postpadding {
+ height: 250px;
+}
+
+.indicator {
+ position: absolute;
+ background-color: red;
+ left: 0;
+ height: 50px;
+ width: 50px;
+}
+
+.sticky {
+ position: sticky;
+ top: 25px;
+ background-color: green;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 250;
+});
+</script>
+
+<!-- .sticky element not yet stuck -->
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator" style="top: 100px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr class="sticky"><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<!-- .sticky element stuck to top of .scroller -->
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator" style="top: 150px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr class="sticky"><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<!-- .sticky element pushed as down as possible to table edge -->
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator" style="top: 250px;"></div>
+ <div class="prepadding"></div>
+ <table>
+ <tbody>
+ <tr class="sticky"><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ <tr><td><div></div></td></tr>
+ </tbody>
+ </table>
+ <div class="postpadding"></div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
+
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-top-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-top-ref.html
new file mode 100644
index 00000000000..3003b94e5db
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-top-ref.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky elements should respect the top constraint</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 500px;
+}
+
+.indicator {
+ background-color: green;
+ position: absolute;
+ left: 0;
+}
+
+.box {
+ width: 100%;
+ height: 100px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 200;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="contents">
+ <div class="indicator box" style="top: 150px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="contents">
+ <div class="indicator box" style="top: 175px;"></div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="contents">
+ <div class="indicator box" style="top: 200px;"></div>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-top.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-top.html
new file mode 100644
index 00000000000..fc07313a20d
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-top.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<title>position:sticky elements should respect the top constraint</title>
+<link rel="match" href="position-sticky-top-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky elements obey their top anchor after scrolling" />
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 150px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 100px;
+ height: 200px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.contents {
+ height: 500px;
+}
+
+.prepadding {
+ height: 100px;
+}
+
+.container {
+ height: 200px;
+}
+
+.innerpadding {
+ height: 50px;
+}
+
+.indicator {
+ background-color: red;
+ position: absolute;
+ left: 0;
+}
+
+.sticky {
+ background-color: green;
+ position: sticky;
+ top: 50px;
+}
+
+.box {
+ width: 100%;
+ height: 100px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller2').scrollTop = 125;
+ document.getElementById('scroller3').scrollTop = 200;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller">
+ <div class="indicator box" style="top: 150px;"></div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding"></div>
+ <div class="sticky box"></div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller">
+ <div class="indicator box" style="top: 175px;"></div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding"></div>
+ <div class="sticky box"></div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller3" class="scroller">
+ <div class="indicator box" style="top: 200px;"></div>
+ <div class="contents">
+ <div class="prepadding"></div>
+ <div class="container">
+ <div class="innerpadding"></div>
+ <div class="sticky box"></div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div>You should see three green boxes above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-writing-modes-ref.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-writing-modes-ref.html
new file mode 100644
index 00000000000..8b171fa5583
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-writing-modes-ref.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<title>Reference for position:sticky constraints are independent of writing mode</title>
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 180px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 130px;
+ height: 200px;
+ overflow: scroll;
+ font: 25px/1 Ahem;
+}
+
+.contents {
+ height: 500px;
+ width: 200px;
+}
+
+.indicator {
+ display: inline;
+ color: green;
+ position: relative;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller1').scrollLeft = 20;
+ document.getElementById('scroller2').scrollTop = 50;
+ document.getElementById('scroller2').scrollLeft = 60;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller" style="writing-mode: vertical-lr;">
+ <div class="contents">
+ <div class="indicator" style="left: 40px; top: 100px;">XXX</div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller" style="writing-mode: vertical-rl;">
+ <div class="contents">
+ <div class="indicator" style="right: 45px; top: 100px;">XXX</div>
+ </div>
+ </div>
+</div>
+
+<div>You should see two green blocks above. No red should be visible.</div>
diff --git a/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-writing-modes.html b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-writing-modes.html
new file mode 100644
index 00000000000..7f10ff5daf3
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/css-position-3/position-sticky-writing-modes.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<title>position:sticky constraints are independent of writing mode</title>
+<link rel="match" href="position-sticky-writing-modes-ref.html" />
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
+<meta name="assert" content="This test checks that position:sticky constraints are independent of the writing mode" />
+
+<style>
+.group {
+ display: inline-block;
+ position: relative;
+ width: 180px;
+ height: 250px;
+}
+
+.scroller {
+ position: relative;
+ width: 130px;
+ height: 200px;
+ overflow: scroll;
+ font: 25px/1 Ahem;
+}
+
+.contents {
+ height: 500px;
+ width: 200px;
+}
+
+.indicator {
+ position: absolute;
+ color: red;
+}
+
+.sticky {
+ display: inline;
+ color: green;
+ position: sticky;
+ top: 50px;
+}
+</style>
+
+<script>
+window.addEventListener('load', function() {
+ document.getElementById('scroller1').scrollTop = 50;
+ document.getElementById('scroller1').scrollLeft = 20;
+ document.getElementById('scroller2').scrollTop = 50;
+ document.getElementById('scroller2').scrollLeft = 60;
+});
+</script>
+
+<div class="group">
+ <div id="scroller1" class="scroller" style="writing-mode: vertical-lr;">
+ <div class="indicator" style="left: 40px; top: 100px;">XXX</div>
+ <div class="contents">
+ <div class="sticky" style="left: 20px;">XXX</div>
+ </div>
+ </div>
+</div>
+
+<div class="group">
+ <div id="scroller2" class="scroller" style="writing-mode: vertical-rl;">
+ <div class="contents">
+ <div class="indicator" style="left: 45px; top: 100px;">XXX</div>
+ <div class="sticky" style="right: 20px;">XXX</div>
+ </div>
+ </div>
+</div>
+
+<div>You should see two green blocks above. No red should be visible.</div>