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:
Oriol Brufau 2025-09-27 00:12:37 +02:00 committed by GitHub
parent b38bf3e606
commit 89293995f0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 262 additions and 124 deletions

View file

@ -468191,6 +468191,10 @@
"b22ebf8c21f7216507988bb85df4cf4e09ec7698",
[]
],
"scrollIntoView-iframes-child.html": [
"04c4b2f913fa8a5d908efb1f838c5195269c9cae",
[]
],
"square-purple.png": [
"0f522d78728417b0f74b694e2e47cd41c00359d1",
[]
@ -632494,6 +632498,13 @@
{}
]
],
"scrollIntoView-iframes.html": [
"4e7b28f94e5ddcc22e676d850b110d1b4dbc0f26",
[
null,
{}
]
],
"scrollIntoView-inline-image.html": [
"1bdc75a27a78823e4084d4f97a567813c96ac70f",
[

View file

@ -1,6 +1,3 @@
[scrollIntoView-fixed.html]
[[Box B\] scrollIntoView from unscrollable position:fixed in iframe]
expected: FAIL
[[Box D\] scrollIntoView from scrollable position:fixed in iframe]
expected: FAIL

View 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>

View 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>