I'm observing iframes having their requestAnimationFrame callbacks getting incorrectly throttled because WebKit thinks that they are outside of the viewport, when they are in fact visible. To reproduce (in either Safari 11.0 or more recent WebKit builds - I tried r223536):
1. Go to http://persistent.info/webkit/test-cases/frame-raf-throttling/container.html. There is an "overflow: auto" scrollable div (gray background, 300 pixels wide) in which there is a composited iframe (yellow, 2000 pixels wide). The iframe uses requestAnimationFrame (for how it just reports the interval between successive calls, to make throttling more apparent)
2. Scroll the container sideways
3. Once the scroll offset goes past the window width (there's a "Scroll to Window Width" button to do this) then the requestAnimationFrame cadence drops down every 10 seconds, corresponding to the aggressiveThrottlingAnimationInterval value in ScriptedAnimationController (which is triggered by ThrottlingReason::OutsideViewport)
What I've traced is that when the area gets scrolled, we end up in FrameView::viewportContentsChanged, which uses applyRecursivelyWithVisibleRect() to get the visible rect to determine if the iframe is visible. That in turn relies on windowClipRect(), and that returns a 0x0 sized rectangle.
Digging into windowClipRect, we end up in windowClipRectForFrameOwner, which since the frame has an enclosing layer uses that layer's childrenClipRect. The return value of that is (-<scroll offset>, y - <window width> x <window height>). Thus if the scroll offset is greater than the window width, when we intersect the rectangle with the window's clip rectangle.
The reason why childrenClipRect's return value appears to refleect the window's dimensions is because it passes renderer().view().unscaledDocumentRect() as the paintDirtyRect parameter to calculateRects (which forms the basis of the return value).
I'm out of my depth here as far as my WebKit knowledge, so someone with more familiarity will need to attempt a fix. Though the manifestation of this bug is that requestAnimationFrame gets throttled incorrectly, since the problem is at a lower level (as far as determining its visible rectangle) there may be other side effects too.
Given that unscaledDocumentRect is used as the initial value, a workaround is to add an element to the main frame document that is as wide as the iframe. This is what the "Add Filler" button does, and once that element is present (even if the main frame document uses "overflow: hidden") then throttling does not occur.
I think the issue is that FrameView::windowClipRectForFrameOwner() uses enclosingLayer->childrenClipRect() which is sensitive to compositing.
RenderLayer::childrenClipRect() takes the document rect (renderer().view().unscaledDocumentRect()) and calls localToAbsoluteQuad() on it, which maps it through the scroll offset, so it can end up with some large negative offset.
This seems wrong; we should probably map LayoutRect::infiniteRect() through and then clip to the document rect.
I also note that RenderWidget::setWidgetGeometry() uses childrenClipRect() and may have a similar bug.
Created attachment 328497 [details]
Comment on attachment 328497 [details]
Clearing flags on attachment: 328497
Committed r225554: <https://trac.webkit.org/changeset/225554>
All reviewed patches have been landed. Closing bug.