csswg-drafts
csswg-drafts copied to clipboard
[css-contain] If a ResizeObserver callback changes the `content-visibility` of an element, should that prevent observations of the same resize event?
https://w3c.github.io/csswg-drafts/css-contain-2/#cv-notes states:
From the perspective of a ResizeObserver, the skipped contents of an element never change their size. If these elements become non-skipped later, the resize observation will be delivered if the new size differs from the last size used to notify the resize observer.
A ResizeObsever
observation callback might change the content-visibility
and force layout of an element though. Consider the situation where that freshly-hidden element also has a ResizeObsever
triggered by the same resize event. The question here is whether a web engine should trigger an observation callback for the freshly-hidden element.
To me it seems like the specification implies that immediately after content is hidden it should stop receiving any ResizeObserver
callbacks, but I believe that there could be multiple interpretations of the passage above.
cc @emilio
A WPT test demonstrating the situation
<!doctype HTML>
<html>
<meta charset="utf8">
<title>Content Visibility: behavior of ResizeObserver that changes content-visibility</title>
<link rel="author" title="Martin Robinson" href="mailto:[email protected]">
<link rel="help" href="https://drafts.csswg.org/css-contain/#content-visibility">
<meta name="assert" content="ResizeObservers that modify content-visibility in observation callbacks should prevent observation of newly skipped content">
<style>
.hidden {
content-visibility: hidden;
}
.resize > div {
width: 100px;
}
</style>
<div id="container">
<div id="resize">x</div>
<div id="hidden">x</div>
</div>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
async_test((t) => {
async function runTest() {
let didInitialCallback = false;
let didHiddenCallback = false;
let shouldHide = false;
const container = document.getElementById("container");
let resizeObserver = new ResizeObserver((entries) => {
didInitialCallback = true;
if (shouldHide) {
container.classList.add("hidden");
container.clientWidth;
}
});
resizeObserver.observe(resize);
let hiddenResizeObserver = new ResizeObserver((entries) => {
didHiddenCallback = true;
});
hiddenResizeObserver.observe(hidden);
await new Promise(requestAnimationFrame);
assert_equals(didInitialCallback, true, 'Resize observation should happen in first frame after registering');
assert_equals(didHiddenCallback, true, 'Resize observation should happen in first frame after registering');
didInitialCallback = false;
didHiddenCallback = false;
container.classList.add("resize");
await new Promise(requestAnimationFrame);
assert_equals(didInitialCallback, true, 'Resize observation should happen after resizing.');
assert_equals(didHiddenCallback, true, 'Resize observation should happen after resizing.');
didInitialCallback = false;
didHiddenCallback = false;
shouldHide = true;
// Change the size of all children of the container. This should cause a resize
// observation, but only for the first resize observer, which immediately
// hides the container, preventing the second observation.
container.classList.remove("resize");
await new Promise(requestAnimationFrame);
assert_equals(didInitialCallback, true, 'Resize observation should happen after resizing.');
assert_equals(didHiddenCallback, false, 'Resize observation should happen after resizing.');
t.done();
}
window.onload = function() {
requestAnimationFrame(() => requestAnimationFrame(runTest));
};
}, "ResizeObserver observation prevented for content hidden in ResizeObserver callback");
</script>
</html>
This seems somewhat analogous to #6493:
<div id="test">test</div>
<script>
var ro1 = new ResizeObserver(() => {
console.log("ro1");
ro2.unobserve(test);
});
ro1.observe(test);
var ro2 = new ResizeObserver(() => {
console.log("ro2");
});
ro2.observe(test);
</script>
This should log both ro1
and ro2
, i.e. the ro2 callback is invoked even if it's no longer observing any element.
So I think that your testcase should be consistent with this, first determine the active observations, then broadcast them regardless of whether some callback does something that would have avoided some observation.
This seems somewhat analogous to #6493:
In this case, should the specification be updated to clarify this?