Open Bug 1977161 Opened 1 year ago Updated 1 year ago

[IntersectionObserver] observe doesn't fire when iframe is hidden by parent frame

Categories

(Core :: Layout, defect, P3)

Firefox 141
defect

Tracking

()

Tracking Status
firefox-esr128 --- unaffected
firefox-esr140 --- affected
firefox141 --- wontfix
firefox142 --- fix-optional
firefox143 --- fix-optional

People

(Reporter: dwaraa, Unassigned)

References

(Blocks 1 open bug, Regression)

Details

(Keywords: regression)

Attachments

(1 file)

Attached file test.html

User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36

Steps to reproduce:

See the reproduction file test.html. It contains an iframe (in our case, a same-origin iframe). Two IntersectionObservers, one inside the iframe, one outside the iframe, observe an element (#host) inside the iframe. When the iframe itself is hidden from the outside, only the outer IntersectionObserver fires. You can see the output from the console logs.

This behavior broke starting from v139, but I can reproduce on v141 (developer edition).

Actual results:

  • When the page first loads, both "isIntersecting from inside" and "isIntersecting from outside" are logged. This is expected.
  • When "Show/hide iframe parent" is toggled, only "isIntersecting from outside" and "Not isIntersecting from outside" are logged. The inner iframe still thinks that it's visible even though it's not.

Expected results:

In Firefox <139, as well as in Chrome and Safari, both IntersectionObservers should fire. As far as I'm aware, even though it's an iframe thing, we don't set "trackVisibility ", so it shouldn't be an Intersection Observer v2 thing.

Since the problem doesn't occur from the outside, I managed to find a workaround for Firefox, which is to always use window.top.IntersectionObserver where possible (see https://github.com/cloudscape-design/components/commit/a512a914e2752dd5e2b56b4ede7d27fe12b04cd9), but this isn't ideal, of course.

We don't provide an explicit root either, so according to my understanding of the Intersection Observer spec, it should always track relative to the top-level browsing context.

The Bugbug bot thinks this bug should belong to the 'Core::Layout' component, and is moving the bug to that component. Please correct in case you think the bot is wrong.

Component: Untriaged → Layout
Product: Firefox → Core

Thanks for filing. Mozregression says that this regressed with bug 1958942, and that's not really surprising looking at the test-case.

We don't provide an explicit root either, so according to my understanding of the Intersection Observer spec, it should always track relative to the top-level browsing context.

That's correct. What seems to be going on, however, is that the intersection observer steps aren't running at all for the <iframe> (this is why creating the observer in top works).

I believe the HTML spec allows for this behavior as per https://html.spec.whatwg.org/#update-the-rendering:

Unnecessary rendering: Remove from docs any Document object doc for which all of the following are true:

  • the user agent believes that updating the rendering of doc's node navigable would have no visible effect; and
  • doc's map of animation frame callbacks is empty.

So in this case we're hitting the first bullet point here. That said, I agree it is a bit surprising, and the fact that it used to work is, indeed, annoying. Before that patch we very inconsistently skipped some rendering phases for display: none iframes and not others. Unfortunately intersection observations is one of those which we didn't skip and now skip.

For this particular case, you could argue that at the start of "update the rendering" (before we have updated style / layout) the frame is not hidden yet, and thus the IntersectionObserver steps should run. That's a bit edge casey in this case however, since if you called .getBoundingClientRect() after setting .style.display = "none", then that wouldn't be true anymore. It seems other browsers have some interesting statefulness going on as well in that area, e.g. if in your test-case I add this to the inner frame:

        window.addEventListener("resize", function() {
          console.log("inner resize");
        });

And this to the click handler:

        if (parent.style.display !== "none") {
          iframe.style.width = iframe.getBoundingClientRect().width + 2 + "px";
          // If I uncomment this, Chrome and WebKit fire the resize event :'/
          // iframe.getBoundingClientRect().width;
        }

Whether they fire a resize event (which is supposed to happen in the same loop as IntersectionObserver) or not depends on whether I uncomment the getBoundingClientRect call (again, not totally surprising, but... I bet the behavior is different for a cross-process iframe).

https://github.com/whatwg/html/issues/10333 is the relevant HTML spec issue that tracks this, for what is worth... If you could comment there with the particular use case it would be appreciated.

Also, I'm curious about what kind of stuff you are doing on a display: none iframe? Generally all browsers throttle those very aggressively.

Severity: -- → S3
Status: UNCONFIRMED → NEW
Ever confirmed: true
Flags: needinfo?(dwaraa)
Keywords: regression
Priority: -- → P3
Regressed by: 1958942

Set release status flags based on info from the regressing bug 1958942

Redirect a needinfo that is pending on an inactive user to the triage owner.
:dholbert, since the bug has recent activity, could you have a look please?

For more information, please visit BugBot documentation.

Flags: needinfo?(dwaraa) → needinfo?(dholbert)
Flags: needinfo?(dholbert)
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: