mirror of
https://github.com/servo/servo.git
synced 2025-09-27 15:20:09 +01:00
script: Chain up scrollIntoView()
scrolling to parent <iframe>
s (#39475)
Calling `scrollIntoView()` on an element within an `<iframe>` will now scroll scrolling boxes from the parent document(s), as long as they have the same origin. Testing: One existing subtest passes, and adding a new test. Signed-off-by: Oriol Brufau <obrufau@igalia.com> Co-authored-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
parent
b38bf3e606
commit
89293995f0
6 changed files with 262 additions and 124 deletions
|
@ -17,12 +17,11 @@ use cssparser::match_ignore_ascii_case;
|
||||||
use devtools_traits::AttrInfo;
|
use devtools_traits::AttrInfo;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use embedder_traits::InputMethodType;
|
use embedder_traits::InputMethodType;
|
||||||
use euclid::Vector2D;
|
|
||||||
use euclid::default::{Rect, Size2D};
|
use euclid::default::{Rect, Size2D};
|
||||||
use html5ever::serialize::TraversalScope;
|
use html5ever::serialize::TraversalScope;
|
||||||
use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode};
|
use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode};
|
||||||
use html5ever::{LocalName, Namespace, Prefix, QualName, local_name, namespace_prefix, ns};
|
use html5ever::{LocalName, Namespace, Prefix, QualName, local_name, namespace_prefix, ns};
|
||||||
use js::jsapi::Heap;
|
use js::jsapi::{Heap, JSAutoRealm};
|
||||||
use js::jsval::JSVal;
|
use js::jsval::JSVal;
|
||||||
use js::rust::HandleObject;
|
use js::rust::HandleObject;
|
||||||
use layout_api::{LayoutDamage, ScrollContainerQueryFlags};
|
use layout_api::{LayoutDamage, ScrollContainerQueryFlags};
|
||||||
|
@ -61,7 +60,6 @@ use style::values::{AtomIdent, AtomString, CSSFloat, computed, specified};
|
||||||
use style::{ArcSlice, CaseSensitivityExt, dom_apis, thread_state};
|
use style::{ArcSlice, CaseSensitivityExt, dom_apis, thread_state};
|
||||||
use stylo_atoms::Atom;
|
use stylo_atoms::Atom;
|
||||||
use stylo_dom::ElementState;
|
use stylo_dom::ElementState;
|
||||||
use webrender_api::units::LayoutVector2D;
|
|
||||||
use xml5ever::serialize::TraversalScope::{
|
use xml5ever::serialize::TraversalScope::{
|
||||||
ChildrenOnly as XmlChildrenOnly, IncludeNode as XmlIncludeNode,
|
ChildrenOnly as XmlChildrenOnly, IncludeNode as XmlIncludeNode,
|
||||||
};
|
};
|
||||||
|
@ -166,7 +164,7 @@ use crate::dom::node::{
|
||||||
use crate::dom::nodelist::NodeList;
|
use crate::dom::nodelist::NodeList;
|
||||||
use crate::dom::promise::Promise;
|
use crate::dom::promise::Promise;
|
||||||
use crate::dom::raredata::ElementRareData;
|
use crate::dom::raredata::ElementRareData;
|
||||||
use crate::dom::scrolling_box::{ScrollingBox, ScrollingBoxSource};
|
use crate::dom::scrolling_box::ScrollingBox;
|
||||||
use crate::dom::servoparser::ServoParser;
|
use crate::dom::servoparser::ServoParser;
|
||||||
use crate::dom::shadowroot::{IsUserAgentWidget, ShadowRoot};
|
use crate::dom::shadowroot::{IsUserAgentWidget, ShadowRoot};
|
||||||
use crate::dom::text::Text;
|
use crate::dom::text::Text;
|
||||||
|
@ -836,7 +834,19 @@ impl Element {
|
||||||
block: ScrollLogicalPosition,
|
block: ScrollLogicalPosition,
|
||||||
inline: ScrollLogicalPosition,
|
inline: ScrollLogicalPosition,
|
||||||
container: Option<&Element>,
|
container: Option<&Element>,
|
||||||
|
inner_target_rect: Option<Rect<Au>>,
|
||||||
) {
|
) {
|
||||||
|
let get_target_rect = || match inner_target_rect {
|
||||||
|
None => self.upcast::<Node>().border_box().unwrap_or_default(),
|
||||||
|
Some(inner_target_rect) => inner_target_rect.translate(
|
||||||
|
self.upcast::<Node>()
|
||||||
|
.content_box()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.origin
|
||||||
|
.to_vector(),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
// Step 1: For each ancestor element or viewport that establishes a scrolling box `scrolling
|
// Step 1: For each ancestor element or viewport that establishes a scrolling box `scrolling
|
||||||
// box`, in order of innermost to outermost scrolling box, run these substeps:
|
// box`, in order of innermost to outermost scrolling box, run these substeps:
|
||||||
let mut parent_scrolling_box = self.scrolling_box(ScrollContainerQueryFlags::empty());
|
let mut parent_scrolling_box = self.scrolling_box(ScrollContainerQueryFlags::empty());
|
||||||
|
@ -853,7 +863,8 @@ impl Element {
|
||||||
// determine the scroll-into-view position of `target` with `behavior` as the scroll
|
// determine the scroll-into-view position of `target` with `behavior` as the scroll
|
||||||
// behavior, `block` as the block flow position, `inline` as the inline base direction
|
// behavior, `block` as the block flow position, `inline` as the inline base direction
|
||||||
// position and `scrolling box` as the scrolling box.
|
// position and `scrolling box` as the scrolling box.
|
||||||
let position = self.determine_scroll_into_view_position(&scrolling_box, block, inline);
|
let position =
|
||||||
|
scrolling_box.determine_scroll_into_view_position(block, inline, get_target_rect());
|
||||||
|
|
||||||
// Step 1.3: If `position` is not the same as `scrolling box`’s current scroll position, or
|
// Step 1.3: If `position` is not the same as `scrolling box`’s current scroll position, or
|
||||||
// `scrolling box` has an ongoing smooth scroll,
|
// `scrolling box` has an ongoing smooth scroll,
|
||||||
|
@ -882,127 +893,27 @@ impl Element {
|
||||||
.node()
|
.node()
|
||||||
.is_shadow_including_inclusive_ancestor_of(container_node)
|
.is_shadow_including_inclusive_ancestor_of(container_node)
|
||||||
}) {
|
}) {
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <https://drafts.csswg.org/cssom-view/#determine-the-scroll-into-view-position>
|
let window_proxy = self.owner_window().window_proxy();
|
||||||
fn determine_scroll_into_view_position(
|
let Some(frame_element) = window_proxy.frame_element() else {
|
||||||
&self,
|
return;
|
||||||
scrolling_box: &ScrollingBox,
|
};
|
||||||
block: ScrollLogicalPosition,
|
|
||||||
inline: ScrollLogicalPosition,
|
|
||||||
) -> LayoutVector2D {
|
|
||||||
let device_pixel_ratio = self
|
|
||||||
.upcast::<Node>()
|
|
||||||
.owner_doc()
|
|
||||||
.window()
|
|
||||||
.device_pixel_ratio()
|
|
||||||
.get();
|
|
||||||
let to_pixel = |value: Au| value.to_nearest_pixel(device_pixel_ratio);
|
|
||||||
|
|
||||||
// > Step 1: Let target bounding border box be the box represented by the return
|
let inner_target_rect = Some(get_target_rect());
|
||||||
// > value of invoking Element’s getBoundingClientRect(), if target is an Element,
|
let parent_window = frame_element.owner_window();
|
||||||
// > or Range’s getBoundingClientRect(), if target is a Range.
|
let cx = GlobalScope::get_cx();
|
||||||
let target_border_box = self.upcast::<Node>().border_box().unwrap_or_default();
|
let _ac = JSAutoRealm::new(*cx, *parent_window.reflector().get_jsobject());
|
||||||
let target_top_left = target_border_box.origin.map(to_pixel).to_untyped();
|
frame_element.scroll_into_view_with_options(
|
||||||
let target_bottom_right = target_border_box.max().map(to_pixel);
|
behavior,
|
||||||
|
block,
|
||||||
// The rest of the steps diverge from the specification here, but essentially try
|
inline,
|
||||||
// to follow it using our own geometry types.
|
None,
|
||||||
//
|
inner_target_rect,
|
||||||
// TODO: This makes the code below wrong for the purposes of writing modes.
|
|
||||||
let (adjusted_element_top_left, adjusted_element_bottom_right) =
|
|
||||||
match scrolling_box.target() {
|
|
||||||
ScrollingBoxSource::Viewport(_) => (target_top_left, target_bottom_right),
|
|
||||||
ScrollingBoxSource::Element(scrolling_element) => {
|
|
||||||
let scrolling_padding_rect_top_left = scrolling_element
|
|
||||||
.upcast::<Node>()
|
|
||||||
.padding_box()
|
|
||||||
.unwrap_or_default()
|
|
||||||
.origin
|
|
||||||
.map(to_pixel);
|
|
||||||
(
|
|
||||||
target_top_left - scrolling_padding_rect_top_left.to_vector(),
|
|
||||||
target_bottom_right - scrolling_padding_rect_top_left.to_vector(),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let scrolling_box_size = scrolling_box.size();
|
|
||||||
let current_scroll_position = scrolling_box.scroll_position();
|
|
||||||
Vector2D::new(
|
|
||||||
self.calculate_scroll_position_one_axis(
|
|
||||||
inline,
|
|
||||||
adjusted_element_top_left.x,
|
|
||||||
adjusted_element_bottom_right.x,
|
|
||||||
scrolling_box_size.width,
|
|
||||||
current_scroll_position.x,
|
|
||||||
),
|
|
||||||
self.calculate_scroll_position_one_axis(
|
|
||||||
block,
|
|
||||||
adjusted_element_top_left.y,
|
|
||||||
adjusted_element_bottom_right.y,
|
|
||||||
scrolling_box_size.height,
|
|
||||||
current_scroll_position.y,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Step 10 from <https://drafts.csswg.org/cssom-view/#determine-the-scroll-into-view-position>:
|
|
||||||
fn calculate_scroll_position_one_axis(
|
|
||||||
&self,
|
|
||||||
alignment: ScrollLogicalPosition,
|
|
||||||
element_start: f32,
|
|
||||||
element_end: f32,
|
|
||||||
container_size: f32,
|
|
||||||
current_scroll_offset: f32,
|
|
||||||
) -> f32 {
|
|
||||||
let element_size = element_end - element_start;
|
|
||||||
current_scroll_offset +
|
|
||||||
match alignment {
|
|
||||||
// Step 1 & 5: If inline is "start", then align element start edge with scrolling box start edge.
|
|
||||||
ScrollLogicalPosition::Start => element_start,
|
|
||||||
// Step 2 & 6: If inline is "end", then align element end edge with
|
|
||||||
// scrolling box end edge.
|
|
||||||
ScrollLogicalPosition::End => element_end - container_size,
|
|
||||||
// Step 3 & 7: If inline is "center", then align the center of target bounding
|
|
||||||
// border box with the center of scrolling box in scrolling box’s inline base direction.
|
|
||||||
ScrollLogicalPosition::Center => {
|
|
||||||
element_start + (element_size - container_size) / 2.0
|
|
||||||
},
|
|
||||||
// Step 4 & 8: If inline is "nearest",
|
|
||||||
ScrollLogicalPosition::Nearest => {
|
|
||||||
let viewport_start = current_scroll_offset;
|
|
||||||
let viewport_end = current_scroll_offset + container_size;
|
|
||||||
|
|
||||||
// Step 4.2 & 8.2: If element start edge is outside scrolling box start edge and element
|
|
||||||
// size is less than scrolling box size or If element end edge is outside
|
|
||||||
// scrolling box end edge and element size is greater than scrolling box size:
|
|
||||||
// Align element start edge with scrolling box start edge.
|
|
||||||
if (element_start < viewport_start && element_size <= container_size) ||
|
|
||||||
(element_end > viewport_end && element_size >= container_size)
|
|
||||||
{
|
|
||||||
element_start
|
|
||||||
}
|
|
||||||
// Step 4.3 & 8.3: If element end edge is outside scrolling box start edge and element
|
|
||||||
// size is greater than scrolling box size or If element start edge is outside
|
|
||||||
// scrolling box end edge and element size is less than scrolling box size:
|
|
||||||
// Align element end edge with scrolling box end edge.
|
|
||||||
else if (element_end > viewport_end && element_size < container_size) ||
|
|
||||||
(element_start < viewport_start && element_size > container_size)
|
|
||||||
{
|
|
||||||
element_end - container_size
|
|
||||||
}
|
|
||||||
// Step 4.1 & 8.1: If element start edge and element end edge are both outside scrolling
|
|
||||||
// box start edge and scrolling box end edge or an invalid situation: Do nothing.
|
|
||||||
else {
|
|
||||||
current_scroll_offset
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://dom.spec.whatwg.org/#valid-shadow-host-name>
|
/// <https://dom.spec.whatwg.org/#valid-shadow-host-name>
|
||||||
|
@ -3530,7 +3441,7 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 8: Scroll the element into view with behavior, block, inline, and container.
|
// Step 8: Scroll the element into view with behavior, block, inline, and container.
|
||||||
self.scroll_into_view_with_options(behavior, block, inline, container);
|
self.scroll_into_view_with_options(behavior, block, inline, container, None);
|
||||||
|
|
||||||
// Step 9: Optionally perform some other action that brings the
|
// Step 9: Optionally perform some other action that brings the
|
||||||
// element to the user’s attention.
|
// element to the user’s attention.
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
|
||||||
|
use app_units::Au;
|
||||||
|
use euclid::Vector2D;
|
||||||
|
use euclid::default::Rect;
|
||||||
use layout_api::{AxesOverflow, ScrollContainerQueryFlags};
|
use layout_api::{AxesOverflow, ScrollContainerQueryFlags};
|
||||||
use script_bindings::codegen::GenericBindings::WindowBinding::ScrollBehavior;
|
use script_bindings::codegen::GenericBindings::WindowBinding::ScrollBehavior;
|
||||||
use script_bindings::inheritance::Castable;
|
use script_bindings::inheritance::Castable;
|
||||||
|
@ -11,6 +14,7 @@ use script_bindings::root::DomRoot;
|
||||||
use style::values::computed::Overflow;
|
use style::values::computed::Overflow;
|
||||||
use webrender_api::units::{LayoutSize, LayoutVector2D};
|
use webrender_api::units::{LayoutSize, LayoutVector2D};
|
||||||
|
|
||||||
|
use crate::dom::bindings::codegen::Bindings::ElementBinding::ScrollLogicalPosition;
|
||||||
use crate::dom::node::{Node, NodeTraits};
|
use crate::dom::node::{Node, NodeTraits};
|
||||||
use crate::dom::types::{Document, Element};
|
use crate::dom::types::{Document, Element};
|
||||||
|
|
||||||
|
@ -140,4 +144,114 @@ impl ScrollingBox {
|
||||||
ScrollingBoxAxis::Y => self.content_size().height > self.size().height,
|
ScrollingBoxAxis::Y => self.content_size().height > self.size().height,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <https://drafts.csswg.org/cssom-view/#determine-the-scroll-into-view-position>
|
||||||
|
pub(crate) fn determine_scroll_into_view_position(
|
||||||
|
&self,
|
||||||
|
block: ScrollLogicalPosition,
|
||||||
|
inline: ScrollLogicalPosition,
|
||||||
|
target_rect: Rect<Au>,
|
||||||
|
) -> LayoutVector2D {
|
||||||
|
let device_pixel_ratio = self.node().owner_window().device_pixel_ratio().get();
|
||||||
|
let to_pixel = |value: Au| value.to_nearest_pixel(device_pixel_ratio);
|
||||||
|
|
||||||
|
// Step 1 should be handled by the caller, and provided as |target_rect|.
|
||||||
|
// > Let target bounding border box be the box represented by the return value
|
||||||
|
// > of invoking Element’s getBoundingClientRect(), if target is an Element,
|
||||||
|
// > or Range’s getBoundingClientRect(), if target is a Range.
|
||||||
|
let target_top_left = target_rect.origin.map(to_pixel).to_untyped();
|
||||||
|
let target_bottom_right = target_rect.max().map(to_pixel);
|
||||||
|
|
||||||
|
// The rest of the steps diverge from the specification here, but essentially try
|
||||||
|
// to follow it using our own geometry types.
|
||||||
|
//
|
||||||
|
// TODO: This makes the code below wrong for the purposes of writing modes.
|
||||||
|
let (adjusted_element_top_left, adjusted_element_bottom_right) = match self.target() {
|
||||||
|
ScrollingBoxSource::Viewport(_) => (target_top_left, target_bottom_right),
|
||||||
|
ScrollingBoxSource::Element(scrolling_element) => {
|
||||||
|
let scrolling_padding_rect_top_left = scrolling_element
|
||||||
|
.upcast::<Node>()
|
||||||
|
.padding_box()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.origin
|
||||||
|
.map(to_pixel);
|
||||||
|
(
|
||||||
|
target_top_left - scrolling_padding_rect_top_left.to_vector(),
|
||||||
|
target_bottom_right - scrolling_padding_rect_top_left.to_vector(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let size = self.size();
|
||||||
|
let current_scroll_position = self.scroll_position();
|
||||||
|
Vector2D::new(
|
||||||
|
Self::calculate_scroll_position_one_axis(
|
||||||
|
inline,
|
||||||
|
adjusted_element_top_left.x,
|
||||||
|
adjusted_element_bottom_right.x,
|
||||||
|
size.width,
|
||||||
|
current_scroll_position.x,
|
||||||
|
),
|
||||||
|
Self::calculate_scroll_position_one_axis(
|
||||||
|
block,
|
||||||
|
adjusted_element_top_left.y,
|
||||||
|
adjusted_element_bottom_right.y,
|
||||||
|
size.height,
|
||||||
|
current_scroll_position.y,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Step 10 from <https://drafts.csswg.org/cssom-view/#determine-the-scroll-into-view-position>:
|
||||||
|
fn calculate_scroll_position_one_axis(
|
||||||
|
alignment: ScrollLogicalPosition,
|
||||||
|
element_start: f32,
|
||||||
|
element_end: f32,
|
||||||
|
container_size: f32,
|
||||||
|
current_scroll_offset: f32,
|
||||||
|
) -> f32 {
|
||||||
|
let element_size = element_end - element_start;
|
||||||
|
current_scroll_offset +
|
||||||
|
match alignment {
|
||||||
|
// Step 1 & 5: If inline is "start", then align element start edge with scrolling box start edge.
|
||||||
|
ScrollLogicalPosition::Start => element_start,
|
||||||
|
// Step 2 & 6: If inline is "end", then align element end edge with
|
||||||
|
// scrolling box end edge.
|
||||||
|
ScrollLogicalPosition::End => element_end - container_size,
|
||||||
|
// Step 3 & 7: If inline is "center", then align the center of target bounding
|
||||||
|
// border box with the center of scrolling box in scrolling box’s inline base direction.
|
||||||
|
ScrollLogicalPosition::Center => {
|
||||||
|
element_start + (element_size - container_size) / 2.0
|
||||||
|
},
|
||||||
|
// Step 4 & 8: If inline is "nearest",
|
||||||
|
ScrollLogicalPosition::Nearest => {
|
||||||
|
let viewport_start = current_scroll_offset;
|
||||||
|
let viewport_end = current_scroll_offset + container_size;
|
||||||
|
|
||||||
|
// Step 4.2 & 8.2: If element start edge is outside scrolling box start edge and element
|
||||||
|
// size is less than scrolling box size or If element end edge is outside
|
||||||
|
// scrolling box end edge and element size is greater than scrolling box size:
|
||||||
|
// Align element start edge with scrolling box start edge.
|
||||||
|
if (element_start < viewport_start && element_size <= container_size) ||
|
||||||
|
(element_end > viewport_end && element_size >= container_size)
|
||||||
|
{
|
||||||
|
element_start
|
||||||
|
}
|
||||||
|
// Step 4.3 & 8.3: If element end edge is outside scrolling box start edge and element
|
||||||
|
// size is greater than scrolling box size or If element start edge is outside
|
||||||
|
// scrolling box end edge and element size is less than scrolling box size:
|
||||||
|
// Align element end edge with scrolling box end edge.
|
||||||
|
else if (element_end > viewport_end && element_size < container_size) ||
|
||||||
|
(element_start < viewport_start && element_size > container_size)
|
||||||
|
{
|
||||||
|
element_end - container_size
|
||||||
|
}
|
||||||
|
// Step 4.1 & 8.1: If element start edge and element end edge are both outside scrolling
|
||||||
|
// box start edge and scrolling box end edge or an invalid situation: Do nothing.
|
||||||
|
else {
|
||||||
|
current_scroll_offset
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
11
tests/wpt/meta/MANIFEST.json
vendored
11
tests/wpt/meta/MANIFEST.json
vendored
|
@ -468191,6 +468191,10 @@
|
||||||
"b22ebf8c21f7216507988bb85df4cf4e09ec7698",
|
"b22ebf8c21f7216507988bb85df4cf4e09ec7698",
|
||||||
[]
|
[]
|
||||||
],
|
],
|
||||||
|
"scrollIntoView-iframes-child.html": [
|
||||||
|
"04c4b2f913fa8a5d908efb1f838c5195269c9cae",
|
||||||
|
[]
|
||||||
|
],
|
||||||
"square-purple.png": [
|
"square-purple.png": [
|
||||||
"0f522d78728417b0f74b694e2e47cd41c00359d1",
|
"0f522d78728417b0f74b694e2e47cd41c00359d1",
|
||||||
[]
|
[]
|
||||||
|
@ -632494,6 +632498,13 @@
|
||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
"scrollIntoView-iframes.html": [
|
||||||
|
"4e7b28f94e5ddcc22e676d850b110d1b4dbc0f26",
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
{}
|
||||||
|
]
|
||||||
|
],
|
||||||
"scrollIntoView-inline-image.html": [
|
"scrollIntoView-inline-image.html": [
|
||||||
"1bdc75a27a78823e4084d4f97a567813c96ac70f",
|
"1bdc75a27a78823e4084d4f97a567813c96ac70f",
|
||||||
[
|
[
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
[scrollIntoView-fixed.html]
|
[scrollIntoView-fixed.html]
|
||||||
[[Box B\] scrollIntoView from unscrollable position:fixed in iframe]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[[Box D\] scrollIntoView from scrollable position:fixed in iframe]
|
[[Box D\] scrollIntoView from scrollable position:fixed in iframe]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
82
tests/wpt/tests/css/cssom-view/scrollIntoView-iframes.html
vendored
Normal file
82
tests/wpt/tests/css/cssom-view/scrollIntoView-iframes.html
vendored
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>CSSOM View Test: scrollIntoView in iframes</title>
|
||||||
|
<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
|
||||||
|
<link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com">
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-scrollintoview">
|
||||||
|
<meta name="assert" content="Checks that scrollIntoView inside and iframe can scroll in the parent document if it has the same origin.">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.scroller {
|
||||||
|
overflow: hidden;
|
||||||
|
height: 500px;
|
||||||
|
border: solid;
|
||||||
|
}
|
||||||
|
.scroller::before {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
height: 500px;
|
||||||
|
}
|
||||||
|
.scroller::after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
iframe {
|
||||||
|
height: 1000px;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div id="log"></div>
|
||||||
|
|
||||||
|
<div class="scroller">
|
||||||
|
<iframe id="same-origin-iframe"></iframe>
|
||||||
|
</div>
|
||||||
|
<div class="scroller">
|
||||||
|
<iframe id="cross-origin-iframe" sandbox="allow-scripts"></iframe>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script>
|
||||||
|
let sameOriginIframe = document.getElementById("same-origin-iframe");
|
||||||
|
let crossOriginIframe = document.getElementById("cross-origin-iframe");
|
||||||
|
let sameOriginWindow = sameOriginIframe.contentWindow;
|
||||||
|
let crossOriginWindow = crossOriginIframe.contentWindow;
|
||||||
|
|
||||||
|
promise_setup(() => Promise.all([
|
||||||
|
new Promise(resolve => {
|
||||||
|
sameOriginIframe.addEventListener("load", resolve);
|
||||||
|
sameOriginIframe.src = "support/scrollIntoView-iframes-child.html";
|
||||||
|
}),
|
||||||
|
new Promise(resolve => {
|
||||||
|
crossOriginIframe.addEventListener("load", resolve);
|
||||||
|
crossOriginIframe.src = "support/scrollIntoView-iframes-child.html";
|
||||||
|
})
|
||||||
|
]));
|
||||||
|
|
||||||
|
promise_test(async () => {
|
||||||
|
assert_equals(sameOriginWindow.scrollY, 100, "scrollY");
|
||||||
|
}, "scrollIntoView in same-origin iframe can scroll in inner window");
|
||||||
|
|
||||||
|
promise_test(async () => {
|
||||||
|
assert_equals(sameOriginIframe.parentElement.scrollTop, 1200, "scrollTop");
|
||||||
|
}, "scrollIntoView in same-origin iframe can scroll in parent window");
|
||||||
|
|
||||||
|
promise_test(async () => {
|
||||||
|
let scrollY = await new Promise(resolve => {
|
||||||
|
addEventListener("message", event => {
|
||||||
|
if (event.source === crossOriginWindow) {
|
||||||
|
resolve(event.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
crossOriginWindow.postMessage("scrollY", "*");
|
||||||
|
});
|
||||||
|
assert_equals(scrollY, 100, "scrollY");
|
||||||
|
}, "scrollIntoView in cross-origin iframe can scroll in inner window");
|
||||||
|
|
||||||
|
promise_test(async () => {
|
||||||
|
assert_equals(crossOriginIframe.parentElement.scrollTop, 0, "scrollTop");
|
||||||
|
}, "scrollIntoView in cross-origin iframe can't scroll in parent window");
|
||||||
|
</script>
|
23
tests/wpt/tests/css/cssom-view/support/scrollIntoView-iframes-child.html
vendored
Normal file
23
tests/wpt/tests/css/cssom-view/support/scrollIntoView-iframes-child.html
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding-top: 1000px;
|
||||||
|
}
|
||||||
|
#target {
|
||||||
|
height: 100px;
|
||||||
|
width: 100px;
|
||||||
|
background: green;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div id="target"></div>
|
||||||
|
<script>
|
||||||
|
target.scrollIntoView({block: "center"});
|
||||||
|
|
||||||
|
addEventListener("message", event => {
|
||||||
|
if (event.data === "scrollY") {
|
||||||
|
event.source.postMessage(scrollY, "*");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
Loading…
Add table
Add a link
Reference in a new issue