RESOLVED FIXED 55257
Support creating compositing layers for scrollable frames and iframes
https://bugs.webkit.org/show_bug.cgi?id=55257
Summary Support creating compositing layers for scrollable frames and iframes
Daniel Sievers
Reported 2011-02-25 13:52:34 PST
The existing logic should be extended to allow creating hardware compositing layers for scrollable (I)Frames, DIVS, and others. A new compositing trigger should be added to turn on/off this feature, and layers should only be created if the object is both scrollable and has actual overflow.
Attachments
Patch (13.50 KB, patch)
2011-02-25 15:38 PST, Daniel Sievers
no flags
Patch (25.19 KB, patch)
2011-03-03 17:32 PST, Daniel Sievers
no flags
Patch (26.14 KB, patch)
2011-03-03 17:42 PST, Daniel Sievers
no flags
Patch (26.15 KB, patch)
2011-03-03 17:53 PST, Daniel Sievers
no flags
Patch (26.20 KB, patch)
2011-03-03 18:08 PST, Daniel Sievers
no flags
Patch (23.99 KB, patch)
2011-03-03 18:30 PST, Daniel Sievers
no flags
Patch (23.86 KB, patch)
2011-03-04 10:42 PST, Daniel Sievers
no flags
Testcase (1.57 KB, text/html)
2011-03-04 11:23 PST, Simon Fraser (smfr)
no flags
Patch (18.53 KB, patch)
2011-03-07 16:59 PST, Daniel Sievers
no flags
Patch (18.34 KB, patch)
2011-03-07 21:09 PST, Daniel Sievers
no flags
Patch (25.82 KB, patch)
2011-04-14 14:39 PDT, Daniel Sievers
no flags
Patch (26.41 KB, patch)
2011-07-14 15:45 PDT, Daniel Sievers
no flags
Archive of layout-test-results from ec2-cr-linux-01 (6.31 MB, application/zip)
2011-07-14 23:27 PDT, WebKit Review Bot
no flags
Patch (5.38 KB, patch)
2011-08-03 15:55 PDT, Adrienne Walker
no flags
Patch (5.54 KB, patch)
2011-08-03 16:18 PDT, Adrienne Walker
no flags
Patch (18.25 KB, patch)
2011-08-08 16:35 PDT, Adrienne Walker
no flags
Patch (18.13 KB, patch)
2011-08-09 12:34 PDT, Adrienne Walker
no flags
Patch (17.35 KB, patch)
2011-08-10 13:08 PDT, Adrienne Walker
no flags
Patch (19.33 KB, patch)
2011-08-10 14:19 PDT, Adrienne Walker
jamesr: review+
Daniel Sievers
Comment 1 2011-02-25 15:38:52 PST
James Robinson
Comment 2 2011-02-25 15:46:19 PST
Can we have some tests?
James Robinson
Comment 3 2011-02-25 15:52:15 PST
Comment on attachment 83890 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=83890&action=review > Source/WebCore/rendering/RenderLayer.cpp:3816 > + || isComposited(); Why do you need to change this logic? It doesn't seem like this patch should change this logic. > Source/WebCore/rendering/RenderLayerCompositor.cpp:-1206 > - return m_hasAcceleratedCompositing && layer->isSelfPaintingLayer(); Why do you need to change this? > Source/WebCore/rendering/RenderLayerCompositor.cpp:1305 > + FrameView* frameView = m_renderView->frameView(); > + // Some sites use tiny iframes to load hidden content, so don't composite those. > + if (frameView->layoutWidth() <= 1 || frameView->layoutHeight() <= 1) > + return false; This feels awkward - is not redundant with the check at 1309-1310? > Source/WebCore/rendering/RenderLayerCompositor.h:244 > // Whether a running transition or animation enforces the need for a compositing layer. > + bool requiresCompositingForScrollableOverflow(RenderObject*) const; This comment seems out of date now.
Daniel Sievers
Comment 4 2011-02-25 16:17:46 PST
Comment on attachment 83890 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=83890&action=review >> Source/WebCore/rendering/RenderLayer.cpp:3816 >> + || isComposited(); > > Why do you need to change this logic? It doesn't seem like this patch should change this logic. From what I understand isSelfPaintingLayer() decides whether a layer paints itself or some parent does it and is used in a bunch of places that deal with painting and hit testing. Especially with the latter I'm concerned that we need to give the correct answer here. Doesn't a layer have to be self-painting anyway, if we are using hw compositing and it got its own compositing layer? So pre-existing cases should not be affected. The thing here is that the pre-existing cases rely on things known before layout only, and the decision can be made more statically based on type and style. For the more general 'blocks that overflow' case, it seemed to work best to force the decision from requiresCompositingLayerForScrollableOverflow() and then just make sure isSelfPainting() layer gives good answers if we happened to make the layer composited. >> Source/WebCore/rendering/RenderLayerCompositor.cpp:-1206 >> - return m_hasAcceleratedCompositing && layer->isSelfPaintingLayer(); > > Why do you need to change this? This function is used in needsToBeComposited() and other places to see if the layer could be composited. It doesn't mean it necessarily will (the requiresCompositing*() functions will determine if it actually will be). Usually the check to create compositing layers is done before layout. If we set m_compositingDependsOnGeometry we will revisit things after layout. Since we don't know until after layout whether we really want to composite it, we have to be permissive here and then make the final decision after layout. Also I only want to set m_compositingDependsOnGeometry if there is a reasonable chance (based on style known before layout, i.e. overflow : 'scroll' or 'auto') that we might want to create a layer. >> Source/WebCore/rendering/RenderLayerCompositor.cpp:1305 >> + return false; > > This feels awkward - is not redundant with the check at 1309-1310? Actually, the check below uses contentSize, while the layout size would be the visible size. If a site uses a hidden (e.g. 1x1 iframe) to preload flash or so, I think the content size would have the full content dimensions, while the visible/layout size is 1x1. >> Source/WebCore/rendering/RenderLayerCompositor.h:244 >> + bool requiresCompositingForScrollableOverflow(RenderObject*) const; > > This comment seems out of date now. Oops, will move this back down.
Daniel Sievers
Comment 5 2011-03-03 17:32:44 PST
Daniel Sievers
Comment 6 2011-03-03 17:42:06 PST
Daniel Sievers
Comment 7 2011-03-03 17:46:38 PST
I have made the following changes: * added layout tests with expected output (graphics layer tree). I had problems adding a runtime property to LayoutTestController::overridePreference(), because RenderLayerCompositor() caches m_compositingTriggers when it is first created so triggering a change from Javascript won't propagate properly. Therefore I have marked the tests as SKIPPED for now. * In RenderLayerCompositor::requiresCompositingForScrollableOverflow() I have moved the root layer check up so that we don't always set m_compositingDependsOnGeometry. * I have overwritten canBeProgramaticallyScrolled() for RenderView so that it correctly returns false now for frames and iframes with the 'scrolling' attribute set to 'no', and we don't create an unwanted layer.
Daniel Sievers
Comment 8 2011-03-03 17:53:24 PST
Daniel Sievers
Comment 9 2011-03-03 17:54:10 PST
Fixed typo "should create" -> "should not create" in overflow-iframe-layer.html.
Daniel Sievers
Comment 10 2011-03-03 18:08:30 PST
Daniel Sievers
Comment 11 2011-03-03 18:08:59 PST
Made the expected result reflect the typo fix.
James Robinson
Comment 12 2011-03-03 18:14:29 PST
Comment on attachment 84672 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=84672&action=review > LayoutTests/platform/chromium/test_expectations.txt:3111 > +// Needs ScrollableOverflow compositing trigger. This test should be enabled on platforms allowing > +// this trigger. > +BUGWK55257 GPU SKIP : platform/chromium/compositing/layer-creation/overflow-block-layer.html = PASS > +BUGWK55257 GPU SKIP : platform/chromium/compositing/layer-creation/overflow-iframe-layer.html = PASS In chromium we don't SKIP tests unless they cause the test harness to crash in some way. Instead set the appropriate expectation ( = TEXT or whatnot). > Source/WebCore/rendering/RenderLayerCompositor.cpp:1292 > + bool isRootLayer = box->layer()->isRootLayer(); > + if (isRootLayer && !m_renderView->document()->frame()->tree()->parent()) { > + // Do not create a backing for root layers, these are handled differently. I'm a bit confused here - why do we need the two checks? Does isRootLayer not indicate whether the layer is a root layer? > Source/WebCore/rendering/RenderLayerCompositor.cpp:1305 > + // Some sites use tiny iframes to load hidden content, so don't composite those. > + if (frameView->layoutWidth() <= 1 || frameView->layoutHeight() <= 1) This still feels redundant with the check at 1310. If we don't want to make compositing layers for small things, we should just do the size check once IMO. > Source/WebCore/rendering/RenderView.cpp:818 > +bool RenderView::canBeProgramaticallyScrolled(bool) const > +{ > + if (node() && node()->isDocumentNode()) { > + // Handle scrolling="no" style for (i)frames. > + HTMLFrameOwnerElement* ownerElement = document()->ownerElement(); > + if (ownerElement && (ownerElement->hasTagName(HTMLNames::iframeTag) || ownerElement->hasTagName(HTMLNames::frameTag))) { > + HTMLFrameElementBase* frame = static_cast<HTMLFrameElementBase*>(ownerElement); > + if (frame->scrollingMode() == ScrollbarAlwaysOff) > + return false; > + } > + } > + > + return RenderBox::canBeProgramaticallyScrolled(false); > +} Supporting scrolling="no" on an iframe is a separate issue - please file another bug on this and attach this code + the corresponding test(s) to that bug.
James Robinson
Comment 13 2011-03-03 18:15:13 PST
(In reply to comment #7) > I have made the following changes: > > * added layout tests with expected output (graphics layer tree). > I had problems adding a runtime property to LayoutTestController::overridePreference(), because RenderLayerCompositor() caches m_compositingTriggers when it is first created so triggering a change from Javascript won't propagate properly. Therefore I have marked the tests as SKIPPED for now. Take a look at RenderLayerCompositor::cacheAcceleratedCompositingFlags() and its caller - I think the problem you are having has been solved. > > * In RenderLayerCompositor::requiresCompositingForScrollableOverflow() I have moved the root layer check up so that we don't always set m_compositingDependsOnGeometry. > > * I have overwritten canBeProgramaticallyScrolled() for RenderView so that it correctly returns false now for frames and iframes with the 'scrolling' attribute set to 'no', and we don't create an unwanted layer. This is nifty, but should be done in a separate bug.
Daniel Sievers
Comment 14 2011-03-03 18:18:06 PST
Comment on attachment 84672 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=84672&action=review >> Source/WebCore/rendering/RenderLayerCompositor.cpp:1292 >> + // Do not create a backing for root layers, these are handled differently. > > I'm a bit confused here - why do we need the two checks? Does isRootLayer not indicate whether the layer is a root layer? Actually isRootLayer() returns 'true' for RenderViews that belong to a frame or iframe also: bool isRootLayer() const { return renderer()->isRenderView(); } And we don't want to bail out for those.
Daniel Sievers
Comment 15 2011-03-03 18:30:29 PST
Daniel Sievers
Comment 16 2011-03-03 18:32:44 PST
> In chromium we don't SKIP tests unless they cause the test harness to crash in some way. Instead set the appropriate expectation ( = TEXT or whatnot). Fixed. > I'm a bit confused here - why do we need the two checks? Does isRootLayer not indicate whether the layer is a root layer? > > + if (frameView->layoutWidth() <= 1 || frameView->layoutHeight() <= 1) see comment above, unfortunately isRootLayer() is misleading and just returns whether it's a RenderView, so could also be an (i)frame. > > This still feels redundant with the check at 1310. If we don't want to make compositing layers for small things, we should just do the size check once IMO. Removed. > Supporting scrolling="no" on an iframe is a separate issue - please file another bug on this and attach this code + the corresponding test(s) to that bug. Removed, will file a separate bug.
Daniel Sievers
Comment 17 2011-03-03 18:39:48 PST
https://bugs.webkit.org/show_bug.cgi?id=55737 filed for the nonscrollable iframes.
Daniel Sievers
Comment 18 2011-03-04 10:42:37 PST
Daniel Sievers
Comment 19 2011-03-04 10:43:32 PST
Removed the iframe 'scrolling=no' testcase... let's allow a layer to be created here for now.
Simon Fraser (smfr)
Comment 20 2011-03-04 11:15:14 PST
Kinda blown away that I wasn't cc'd on this bug.
Simon Fraser (smfr)
Comment 21 2011-03-04 11:22:13 PST
Comment on attachment 84779 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=84779&action=review > Source/WebCore/rendering/RenderLayer.cpp:3816 > + || renderer()->isRenderIFrame() > + || isComposited(); You can't do this; the test becomes circular. See RenderLayerCompositor::canBeComposited(). Non-self-painting layers are used for overflow, so your changes here will break rendering in some cases. > Source/WebCore/rendering/RenderLayerCompositor.cpp:1230 > - return m_hasAcceleratedCompositing && layer->isSelfPaintingLayer(); > + if (!m_hasAcceleratedCompositing) > + return false; > + > + bool canBeComposited = layer->isSelfPaintingLayer(); > + > + if ((m_compositingTriggers & ChromeClient::ScrollableOverflowTrigger) > + && layer->renderer()->isRenderBlock() && !layer->renderer()->isTextControl()) { > + // Only do a loose check now, as this function will be queried before layout. > + // Whether there is actual overflow will be determined after layout > + // by requiresCompositingForScrollableOverflow(). > + canBeComposited |= toRenderBox(layer->renderer())->scrollsOverflow(); > + } > + > + return canBeComposited; This seems wrong.
Simon Fraser (smfr)
Comment 22 2011-03-04 11:23:17 PST
Created attachment 84784 [details] Testcase Compositing for overflow is very hard, because overflow does not create stacking context. Try the attached test case with your changes.
Daniel Sievers
Comment 23 2011-03-04 14:27:23 PST
Simon, thanks for your feedback and apologies for missing to CC you on this bug, my bad! (In reply to comment #21) > (From update of attachment 84779 [details]) > View in context: https://bugs.webkit.org/attachment.cgi?id=84779&action=review > > > Source/WebCore/rendering/RenderLayer.cpp:3816 > > + || renderer()->isRenderIFrame() > > + || isComposited(); > > You can't do this; the test becomes circular. See RenderLayerCompositor::canBeComposited(). > But isComposited() != canBeComposited(). The problem I had is that the more generic elements that I want to composite (like overflowing DIVs) are currently not self-painting. Also I didn't want to make all kinds of elements self-painting, if they will not be composited. Since I really only know until after layout, I did the inverse logic here of saying that if we eventually made the layer composited (because it is scrollable and does overflow), we should now be returning true from isSelfPaintingLayer(). In other words, I added isComposited() to the logic here to make sure we return true for 'self-painting' after we have decided to give this layers its own graphics layer in requiresCompositingForScrollableOverflow() (without duplicating the logic). > Non-self-painting layers are used for overflow, so your changes here will break rendering in some cases. > Wouldn't RenderLayers that have a GraphicsLayer (i.e. are composited) always be self-painting? I'm probably missing something, can you elaborate on what would break? On the other hand, so what would happen if I created a composited layer that ends up returning 'false' from isSelfPaintingLayer()? This seems to be used quite a bit including in hit testing logic. > > Source/WebCore/rendering/RenderLayerCompositor.cpp:1230 > > - return m_hasAcceleratedCompositing && layer->isSelfPaintingLayer(); > > + if (!m_hasAcceleratedCompositing) > > + return false; > > + > > + bool canBeComposited = layer->isSelfPaintingLayer(); > > + > > + if ((m_compositingTriggers & ChromeClient::ScrollableOverflowTrigger) > > + && layer->renderer()->isRenderBlock() && !layer->renderer()->isTextControl()) { > > + // Only do a loose check now, as this function will be queried before layout. > > + // Whether there is actual overflow will be determined after layout > > + // by requiresCompositingForScrollableOverflow(). > > + canBeComposited |= toRenderBox(layer->renderer())->scrollsOverflow(); > > + } > > + > > + return canBeComposited; > > This seems wrong. This change is basically related to the one above. If I don't want to make all sorts of render block elements self-painting, I'd have to somehow avoid to bail out from needsToBeComposited()->canBeComposited() early, because the layer is not self-painting. I thought this change would be safe, because it only says whether a layer can theoretically be composited, and it seems like all RenderBlocks would be able to do that, and also because it doesn't mean that we will actually create a compositing layer - the latter would only happen if requiresCompositingLayer() or some other explicit logic would dictate the need for that.
Daniel Sievers
Comment 24 2011-03-04 14:30:07 PST
(In reply to comment #22) > Created an attachment (id=84784) [details] > Testcase > > Compositing for overflow is very hard, because overflow does not create stacking context. Try the attached test case with your changes. Thanks for the testcase! I don't seem to see a problem with this particular case. In Chrome, the results look same with and without my changes. All 3 nodes (container, interleaved, inner) are part of the same (root) stacking context. With compositing, we create 3 sibling layers in the expected order (container, interleaved, inner from back to front), with the 'inner' div layer having a clipping layer inserted.
Simon Fraser (smfr)
Comment 25 2011-03-04 14:38:44 PST
(In reply to comment #23) > Simon, thanks for your feedback and apologies for missing to CC you on this bug, my bad! > > (In reply to comment #21) > > (From update of attachment 84779 [details] [details]) > > View in context: https://bugs.webkit.org/attachment.cgi?id=84779&action=review > > > > > Source/WebCore/rendering/RenderLayer.cpp:3816 > > > + || renderer()->isRenderIFrame() > > > + || isComposited(); > > > > You can't do this; the test becomes circular. See RenderLayerCompositor::canBeComposited(). > > > > But isComposited() != canBeComposited(). The problem I had is that the more generic elements that I want to composite (like overflowing DIVs) are currently not self-painting. In the current design, non-self-painting layers cannot be composited. Changing whether a layer is self-painting will change rendering. Non self-painting layers are used for overflow (they hold a clip). > Also I didn't want to make all kinds of elements self-painting, if they will not be composited. Since I really only know until after layout, I did the inverse logic here of saying that if we eventually made the layer composited (because it is scrollable and does overflow), we should now be returning true from isSelfPaintingLayer(). > > In other words, I added isComposited() to the logic here to make sure we return true for 'self-painting' after we have decided to give this layers its own graphics layer in requiresCompositingForScrollableOverflow() (without duplicating the logic). > > > > Non-self-painting layers are used for overflow, so your changes here will break rendering in some cases. > > > > Wouldn't RenderLayers that have a GraphicsLayer (i.e. are composited) always be self-painting? I'm probably missing something, can you elaborate on what would break? Compositing logic should never change whether something is self-painting. We've chosen to make some replaced elements self-painting so we can accelerate them (video, plugins etc), but doing so for container elements like divs will break the web. > > On the other hand, so what would happen if I created a composited layer that ends up returning 'false' from isSelfPaintingLayer()? This seems to be used quite a bit including in hit testing logic. It don't render any content, and probably invalidates some assumptions that the compositing code makes.
Daniel Sievers
Comment 26 2011-03-07 15:52:19 PST
Simon, without touching canBeComposited() and isSelfPaintingLayer() my proposed change allows for compositing (i)frames that overflow. Assuming we would want to allow overflowing divs to be composited into their own layers also, what would be the right way to implement it? I was wondering if you were suggesting that even in order to achieve this goal we should not make such layers self-painting, or were you rather suggesting that it is risky/experimental to start forcing such elements through the self-painting codepaths? Also, about the testcase: Were you worried that with my change we would create an unwanted stacking context at the 'container' node?
Simon Fraser (smfr)
Comment 27 2011-03-07 16:02:23 PST
(In reply to comment #26) > Simon, > > without touching canBeComposited() and isSelfPaintingLayer() my proposed change allows for compositing (i)frames that overflow. Please use separate patches for (i)frames and overflow: scroll divs. I think they share little code. > Assuming we would want to allow overflowing divs to be composited into their own layers also, what would be the right way to implement it? I'm not sure. One approach is break the web, and cause overflow to create stacking context. This was suggested to the CSS-WG a couple of years ago, and rejected. Otherwise, we have to be very careful to allow compositing for overflow without breaking things. I'd have to probably just try it to figure out if it's possible. One approach is to make a series of testcases similar to mine, where it matters that overflow does not create a SC. > Also, about the testcase: Were you worried that with my change we would create an unwanted stacking context at the 'container' node? I was worried that your changes would alter the rendering of the test.
Daniel Sievers
Comment 28 2011-03-07 16:59:03 PST
Daniel Sievers
Comment 29 2011-03-07 17:00:58 PST
(In reply to comment #27) > (In reply to comment #26) > > Simon, > > > > without touching canBeComposited() and isSelfPaintingLayer() my proposed change allows for compositing (i)frames that overflow. > > Please use separate patches for (i)frames and overflow: scroll divs. I think they share little code. > Done. I have undone changes to canBeComposited() and isSelfPaintingLayer(). Also, I removed the div testcase. See latest patch.
Simon Fraser (smfr)
Comment 30 2011-03-07 17:05:29 PST
Comment on attachment 85000 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=85000&action=review > Source/WebCore/rendering/RenderLayerCompositor.cpp:1262 > +bool RenderLayerCompositor::requiresCompositingForScrollableOverflow(RenderObject* renderer) const > +{ > + if (!(m_compositingTriggers & ChromeClient::ScrollableOverflowTrigger)) > + return false; > + > + if (!renderer->isRenderBlock() || renderer->isTextControl()) > + return false; > + > + RenderBox* box = toRenderBox(renderer); I think you should treat iframes and overflow:scroll totally separately, rather than mushing "scrollable areas" together.
James Robinson
Comment 31 2011-03-07 17:09:38 PST
Comment on attachment 85000 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=85000&action=review Does the bug title still accurately describe what this patch does? > Source/WebCore/rendering/RenderLayerCompositor.cpp:1264 > + if (!box->canBeProgramaticallyScrolled(false)) It seems like this isn't quite the right test - at least to start it sounds like you are more interested in boxes that can be scrolled by user interaction, not just by javascript manipulating scrollTop/scrollLeft. There's also a canBeScrolledAndHasScrollableArea() helper function on RenderBox that encapsulates this and the scrollWidth/Height vs clientWidth/Height checks you are doing on lines 1289-1290. > Source/WebCore/rendering/RenderLayerCompositor.cpp:1282 > + if (contentBox.height() * contentBox.width() <= 1) This deserves a comment and a test.
Simon Fraser (smfr)
Comment 32 2011-03-07 17:17:02 PST
Comment on attachment 85000 [details] Patch r- based on the new focus for this bug.
Daniel Sievers
Comment 33 2011-03-07 21:09:38 PST
Daniel Sievers
Comment 34 2011-03-10 13:31:46 PST
Simon, how does this look to you? (In reply to comment #33) > Created an attachment (id=85016) [details] > Patch
Antonio Gomes
Comment 35 2011-03-18 12:39:43 PDT
Comment on attachment 85016 [details] Patch LGTM
Simon Fraser (smfr)
Comment 36 2011-03-18 12:54:11 PDT
Comment on attachment 85016 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=85016&action=review > Source/WebCore/rendering/RenderLayerCompositor.cpp:1259 > +bool RenderLayerCompositor::requiresCompositingForScrollableFrame(RenderObject* renderer) const > +{ > + if (!(m_compositingTriggers & ChromeClient::ScrollableFrameTrigger)) > + return false; > + > + if (!renderer->isRenderView()) Given the phrasing works, I'd expect this to be called on the renderer in the parent document (the RenderIFrame or whatever), and affect compositing in the parent document. However, that's not what you're doing. So I think it's wrong to add a requiresCompositingForScrollableFrame(), and wasteful to call requiresCompositingForScrollableFrame() for every renderer in the document when it would only ever apply to the root. I think you should just do the root compositing check in one place. > Source/WebCore/rendering/RenderLayerCompositor.cpp:1289 > + return (view->scrollWidth() != view->clientWidth()) > + || (view->scrollHeight() != view->clientHeight()); What will cause compositing to be re-evaluated when the contents of the iframe grow such that it becomes scrollable?
Daniel Sievers
Comment 37 2011-04-05 19:35:25 PDT
Simon, I picked this up again today and looked at how this would work differently with respect to what you suggested when we chatted the other day. With respect to your question to what causes the compositing to be re-evaluated when e.g. the iframe size changes: With my attached patch, the compositing layers will always be updated after layout (even if we previously were not compositing, because I set 'm_compositingDependendsOnGeometry'). About handling the overflow from the RenderIFrame: needsToBeComposited() is currently not called for RenderIFrames, because RenderIFrames usually don't have a RenderLayer. They only create a RenderLayer it if the child is compositing. That's why it plays nicely with the attached change. Also it seems like the overflow would have to be detected by the child in one form or another anyway, because it has to happen after the child FrameView has completed layout. About using 'RenderLayerCompositor::m_forceCompositing', that seems to work fine too. However, not sure what a good place to force it would be. Somewhere at the end of FrameView::layout() is called? It could probably go into FrameView::layout before updateCompositingLayers() is called after the actual layout, so that if the document changes and does not overflow anymore we can disable m_forceCompositing again (compositing will still have to be updated because we don't know if 'force' was the only reason to turn it on). Otherwise, the graphics layers created by m_forceCompositing vs. using needsToBeComposited() for the RenderView's layer seem to be the same. Speaking of which, I don't understand the '(inCompositingMode() && layer->isRootLayer())' part in needsToBeComposited(). Why does the root layer need a backing when it already creates a 'root platform layer' (the latter being what seems to be used in compositing)?
Simon Fraser (smfr)
Comment 38 2011-04-06 09:53:28 PDT
(In reply to comment #37) > With respect to your question to what causes the compositing to be re-evaluated when e.g. the iframe size changes: With my attached patch, the compositing layers will always be updated after layout (even if we previously were not compositing, because I set 'm_compositingDependendsOnGeometry'). But the parent document many not see any layout when the iframe contents change. > About handling the overflow from the RenderIFrame: needsToBeComposited() is currently not called for RenderIFrames, because RenderIFrames usually don't have a RenderLayer. They only create a RenderLayer it if the child is compositing. That's why it plays nicely with the attached change. That's not true. <iframe style="position: relative"> will create a RenderIFrame with a RenderLayer. > Also it seems like the overflow would have to be detected by the child in one form or another anyway, because it has to happen after the child FrameView has completed layout. > > About using 'RenderLayerCompositor::m_forceCompositing', that seems to work fine too. However, not sure what a good place to force it would be. Somewhere at the end of FrameView::layout() is called? It could probably go into FrameView::layout before updateCompositingLayers() is called after the actual layout, so that if the document changes and does not overflow anymore we can disable m_forceCompositing again (compositing will still have to be updated because we don't know if 'force' was the only reason to turn it on). Something like that. > Otherwise, the graphics layers created by m_forceCompositing vs. using needsToBeComposited() for the RenderView's layer seem to be the same. > Speaking of which, I don't understand the '(inCompositingMode() && layer->isRootLayer())' part in needsToBeComposited(). Why does the root layer need a backing when it already creates a 'root platform layer' (the latter being what seems to be used in compositing)? m_rootPlatformLayer is the layer handed off to the native layer-hosting code. The root RenderLayer's GraphicsLayer (which is often a layer with no backing) exists to host other compositing layers in the document, and is required to get the geometry right.
Daniel Sievers
Comment 39 2011-04-14 14:39:48 PDT
Daniel Sievers
Comment 40 2011-04-14 14:53:28 PDT
> > Also it seems like the overflow would have to be detected by the child in one form or another anyway, because it has to happen after the child FrameView has completed layout. > > > > About using 'RenderLayerCompositor::m_forceCompositing', that seems to work fine too. However, not sure what a good place to force it would be. Somewhere at the end of FrameView::layout() is called? It could probably go into FrameView::layout before updateCompositingLayers() is called after the actual layout, so that if the document changes and does not overflow anymore we can disable m_forceCompositing again (compositing will still have to be updated because we don't know if 'force' was the only reason to turn it on). > > Something like that. > Ok, see new patch. I have tried to add tests for entering and leaving compositing when the iframe content changes, but can someone tell me if there is a way to do this that is not conceptually flaky and ugly with respect to the setTimeout() (which I copied from other tests)?
Daniel Sievers
Comment 41 2011-05-09 12:44:54 PDT
Simon, how does the new patch look? Thanks.
Simon Fraser (smfr)
Comment 42 2011-05-11 18:16:11 PDT
Comment on attachment 89651 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=89651&action=review Still not a big fan of this change. > Source/WebCore/page/FrameView.cpp:948 > + if (root->view()->document()->frame()->tree()->parent()) There has to be a better way than root->view()->document()->frame()->tree()->parent() > Source/WebCore/page/FrameView.cpp:949 > + compositor->setForceCompositingMode(compositor->requiresCompositingForScrollableFrame(view) ? true : false); No need for foo ? true : false. Also, this could be just one method on the compositor: compositor->forceCompostingForScrollableFrame(). So you want scrollable frames to composite even if nothing else is triggering composting mode? Why not just always use compositing? > Source/WebCore/page/Settings.h:313 > + void setAcceleratedCompositingForScrollableFrameEnabled(bool); > + bool acceleratedCompositingForScrollableFrameEnabled() const { return m_acceleratedCompositingForScrollableFrameEnabled; } I'd name these acceleratedCompositingForScrollableFrame_s_Enabled I don't really like the fact that you're adding a setting. Will this get toggled at runtime? Can it just be a compile-time thing? > Source/WebCore/rendering/RenderLayerCompositor.cpp:1278 > + if (!ownerElement || (!ownerElement->hasTagName(iframeTag) && !ownerElement->hasTagName(frameTag))) > + return false; What about <object src="foo.html">?
Eric Seidel (no email)
Comment 43 2011-05-23 15:18:32 PDT
Comment on attachment 89651 [details] Patch Simon's comments sound like an r- to me.
Daniel Sievers
Comment 44 2011-07-14 15:45:45 PDT
Daniel Sievers
Comment 45 2011-07-14 15:49:24 PDT
> > Source/WebCore/page/FrameView.cpp:948 > > + if (root->view()->document()->frame()->tree()->parent()) > > There has to be a better way than root->view()->document()->frame()->tree()->parent() Done. > > > Source/WebCore/page/FrameView.cpp:949 > > + compositor->setForceCompositingMode(compositor->requiresCompositingForScrollableFrame(view) ? true : false); > > No need for foo ? true : false. > Done. > Also, this could be just one method on the compositor: compositor->forceCompostingForScrollableFrame(). > Done. > So you want scrollable frames to composite even if nothing else is triggering composting mode? Why not just always use compositing? Yes, I wanted it separate. Also the 'force compositing' setting only forces it on the root layer but not on frame/child compositors. > > > Source/WebCore/page/Settings.h:313 > > + void setAcceleratedCompositingForScrollableFrameEnabled(bool); > > + bool acceleratedCompositingForScrollableFrameEnabled() const { return m_acceleratedCompositingForScrollableFrameEnabled; } > > I'd name these acceleratedCompositingForScrollableFrame_s_Enabled > Done. > I don't really like the fact that you're adding a setting. Will this get toggled at runtime? Can it just be a compile-time thing? > At runtime would be nice to be able to support a cmdline flag. Also, I tried to mimick the other compositing triggers which have similar settings. > > Source/WebCore/rendering/RenderLayerCompositor.cpp:1278 > > + if (!ownerElement || (!ownerElement->hasTagName(iframeTag) && !ownerElement->hasTagName(frameTag))) > > + return false; > > What about <object src="foo.html">? Maybe handle separately?
Antonio Gomes
Comment 46 2011-07-14 16:47:29 PDT
> > Source/WebCore/rendering/RenderLayerCompositor.cpp:1289 > > + return (view->scrollWidth() != view->clientWidth()) > > + || (view->scrollHeight() != view->clientHeight()); > > What will cause compositing to be re-evaluated when the contents of the iframe grow such that it becomes scrollable? I think this is a valid point. How are you handling this?
Daniel Sievers
Comment 47 2011-07-14 16:56:59 PDT
(In reply to comment #46) > > > Source/WebCore/rendering/RenderLayerCompositor.cpp:1289 > > > + return (view->scrollWidth() != view->clientWidth()) > > > + || (view->scrollHeight() != view->clientHeight()); > > > > What will cause compositing to be re-evaluated when the contents of the iframe grow such that it becomes scrollable? > > I think this is a valid point. How are you handling this? The evaluation is always performed after layout in FrameView.cpp, followed by a call to updateCompositingLayers(). When we were already compositing, and the iframe is not scrollable anymore, we will do a hierarchical/recursive update as is (which means we will leave it if there is no other reason left to be compositing). When we were not compositing, and the iframe became scrollable, we will force compositing on this frame and enableCompositingMode() from updateCompositingLayers(). Also see attached test cases for enter-compositing and leave-compositing.
WebKit Review Bot
Comment 48 2011-07-14 23:27:52 PDT
Comment on attachment 100879 [details] Patch Attachment 100879 [details] did not pass chromium-ews (chromium-xvfb): Output: http://queues.webkit.org/results/9092113 New failing tests: editing/pasteboard/paste-noscript.html fast/repaint/line-flow-with-floats-2.html fast/events/page-visibility-iframe-move-test.html fast/dom/shadow/activeelement-should-be-shadowhost.html fast/frames/iframe-reparenting.html fast/frames/sandboxed-iframe-attribute-parsing.html fast/dom/HTMLDocument/active-element-frames.html fast/forms/input-number-large-padding.html fast/forms/input-spinbutton-capturing.html fast/loader/grandparent-completion-starts-redirect.html http/tests/misc/drag-not-loaded-image.html fast/events/pageshow-pagehide.html fast/events/gc-freeze-with-attribute-listeners.html fast/frames/iframe-no-src-set-location.html fast/repaint/line-flow-with-floats-1.html fast/repaint/line-flow-with-floats-3.html fast/repaint/fixed-move-after-keyboard-scroll.html fast/frames/sandboxed-iframe-navigation-allowed.html fast/forms/input-number-events.html http/tests/inspector/console-resource-errors.html
WebKit Review Bot
Comment 49 2011-07-14 23:27:59 PDT
Created attachment 100937 [details] Archive of layout-test-results from ec2-cr-linux-01 The attached test failures were seen while running run-webkit-tests on the chromium-ews. Bot: ec2-cr-linux-01 Port: Chromium Platform: Linux-2.6.35-28-virtual-x86_64-with-Ubuntu-10.10-maverick
Vangelis Kokkevis
Comment 50 2011-07-22 16:21:42 PDT
Comment on attachment 100879 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=100879&action=review > Source/WebCore/page/ChromeClient.h:268 > + ScrollableFrameTrigger = 1 << 5, Ports other than chromium set m_compositingTriggers = AllTriggers, meaning that they will also get composited iframes. Do we want that? > Source/WebCore/rendering/RenderLayerCompositor.cpp:1351 > +bool RenderLayerCompositor::requiresCompositingForScrollableFrame(RenderView* view) const Doesn't this method need to be called from requiresCompositingLayer()? If not, what is it that guarantees that the iframe gets its own backing? > Source/WebCore/rendering/RenderLayerCompositor.cpp:1907 > +void RenderLayerCompositor::forceCompostingForScrollableFrame(RenderView* view) Typo: forceCompos_i_tingForScrollableFrame
Adrienne Walker
Comment 51 2011-08-02 17:11:53 PDT
Daniel's out for a little bit, so I'm going to pick up this patch and see if I can get it to a landable state.
Adrienne Walker
Comment 52 2011-08-02 18:04:34 PDT
(In reply to comment #50) > (From update of attachment 100879 [details]) > View in context: https://bugs.webkit.org/attachment.cgi?id=100879&action=review > > > Source/WebCore/page/ChromeClient.h:268 > > + ScrollableFrameTrigger = 1 << 5, > > Ports other than chromium set m_compositingTriggers = AllTriggers, meaning that they will also get composited iframes. Do we want that? Grep says this is just the Qt port. I'll CC noamr to see if ChromeClientQt should be adjusted or if this behavior is ok. > > Source/WebCore/rendering/RenderLayerCompositor.cpp:1351 > > +bool RenderLayerCompositor::requiresCompositingForScrollableFrame(RenderView* view) const > > Doesn't this method need to be called from requiresCompositingLayer()? If not, what is it that guarantees that the iframe gets its own backing? requiresCompositingLayer is a check for if a layer needs to be composited. This function checks if a view needs to be composited. It gets a backing via the force compositing mode flag which triggers enableCompositingMode() during updateCompositedLayers(), which in turn calls ensureRootLayer().
Noam Rosenthal
Comment 53 2011-08-02 18:42:20 PDT
> > Ports other than chromium set m_compositingTriggers = AllTriggers, meaning that they will also get composited iframes. Do we want that? > > Grep says this is just the Qt port. I'll CC noamr to see if ChromeClientQt should be adjusted or if this behavior is ok. For now it's ok. We can always change our triggers if we have to. Is there anything specific we need to add in GraphicsLayer to support composited IFrames? My assumption was that the changes are mainly in the DOM<->RenderTree domain.
Adrienne Walker
Comment 54 2011-08-02 18:50:14 PDT
(In reply to comment #53) > Is there anything specific we need to add in GraphicsLayer to support composited IFrames? My assumption was that the changes are mainly in the DOM<->RenderTree domain. IFrames can already become composited if there's another compositing trigger inside that frame. So, no extra changes in GraphicsLayer should be needed; this is just a change about when that compositing behavior is triggered.
Vangelis Kokkevis
Comment 55 2011-08-02 23:01:34 PDT
Comment on attachment 100879 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=100879&action=review >>> Source/WebCore/page/ChromeClient.h:268 >>> + ScrollableFrameTrigger = 1 << 5, >> >> Ports other than chromium set m_compositingTriggers = AllTriggers, meaning that they will also get composited iframes. Do we want that? > > Grep says this is just the Qt port. I'll CC noamr to see if ChromeClientQt should be adjusted or if this behavior is ok. The value for m_compositingTriggers is set by calling allowedCompositingTriggers() on the ChromeClient. The default implementation used by all other ports returns AllTriggers: http://www.google.com/codesearch#OAMlx_jo-ck/src/third_party/WebKit/Source/WebCore/page/ChromeClient.h&exact_package=chromium&q=m_compositingtriggers&type=cs&l=274 Chromium overrides this method but I don't believe other ports (including Safari) do.
Adrienne Walker
Comment 56 2011-08-03 10:13:22 PDT
(In reply to comment #55) > Chromium overrides this method but I don't believe other ports (including Safari) do. Hmm. Thanks for pointing that out. I'm not sure that I agree with the idea that a iframe with overflow on an otherwise non-composited page should *trigger* compositing. If the page is already composited, then it makes sense to require compositing for the iframe. If this were how it behaved, I'd have no problem turning this on by default for other ports. Given that, what we'd really want is to be able to conditionally require compositing on these iframes. (Please correct me if I'm wrong here, but) in my understanding of how layout and computing compositing requirements works, we may not know when we're looking at an subframe whether or not the main frame is going to be composited or not, because some later part of the page might be the compositing trigger. I'm not sure that there's a good way to solve this. Needing to do layout again to get to a stable result seems like a bad approach. jamesr suggested the other day that maybe instead of a setting we should just tie this to the force compositing flag. I'm coming back around to that idea. This would sidestep the problem of other ports and would provide a much more definitive trigger for when to composite these subframes.
Adrienne Walker
Comment 57 2011-08-03 15:55:19 PDT
Adrienne Walker
Comment 58 2011-08-03 16:16:11 PDT
(In reply to comment #57) > Created an attachment (id=102845) [details] > Patch Here's something along the lines of my previous comment, where the trigger has been removed and the force compositing mode flag is used instead. I also cleaned up the requiresCompositingForScrollableFrame function to use functions that existed elsewhere rather than duplicating code. I also pushed everything into updateCompositingLayers rather than having FrameView call a function to set an internal RLC flag. As the tests don't apply with force compositing mode, I removed them. I continue to be unhappy that force compositing mode can't be tested in DRT and would love to add this to layoutTestController in a future patch.
Adrienne Walker
Comment 59 2011-08-03 16:18:27 PDT
Adrienne Walker
Comment 60 2011-08-03 16:19:17 PDT
(In reply to comment #59) > Created an attachment (id=102849) [details] > Patch Edited to also add a scrollbar check. Subframes without scrollbars don't get composited, as they seem less likely to be scrolled.
Vangelis Kokkevis
Comment 61 2011-08-04 18:42:47 PDT
Comment on attachment 102849 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=102849&action=review This is looking nice and simple now! > Source/WebCore/rendering/RenderLayerCompositor.cpp:1381 > + // Don't go into compositing mode if height or width are zero, or size is 1x1. Watch indent.
Antonio Gomes
Comment 62 2011-08-04 21:40:27 PDT
Comment on attachment 102849 [details] Patch Needs tests.
Adrienne Walker
Comment 63 2011-08-05 09:51:38 PDT
(In reply to comment #62) > (From update of attachment 102849 [details]) > Needs tests. In that case, I'll need to add support to LayoutTestController so that the force compositing mode flag can be turned on dynamically in DumpRenderTree. As it stands, this patch is untestable as-is other than via manual testing.
Simon Fraser (smfr)
Comment 64 2011-08-05 09:57:09 PDT
You should think about using window.internal of that kind of thing.
Adrienne Walker
Comment 65 2011-08-08 16:35:28 PDT
Adrienne Walker
Comment 66 2011-08-08 16:38:07 PDT
(In reply to comment #65) > Created an attachment (id=103311) [details] > Patch Now with tests (which depend on functionality from bug 65777).
Adrienne Walker
Comment 67 2011-08-09 12:34:26 PDT
Adrienne Walker
Comment 68 2011-08-09 12:36:38 PDT
(In reply to comment #67) > Created an attachment (id=103385) [details] > Patch Same patch, just giving ews another shot now that it applies cleanly against ToT.
Adrienne Walker
Comment 69 2011-08-09 16:17:50 PDT
(In reply to comment #68) > (In reply to comment #67) > > Created an attachment (id=103385) [details] [details] > > Patch > > Same patch, just giving ews another shot now that it applies cleanly against ToT. smfr, jamesr: ping?
James Robinson
Comment 70 2011-08-10 11:50:28 PDT
Comment on attachment 103385 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=103385&action=review Looks pretty good, except the 1x1 stuff. > Source/WebCore/rendering/RenderLayerCompositor.cpp:1388 > + // Don't go into compositing mode if height or width are zero, or size is 1x1. > + // (This is sometimes used to load flash in a hidden iframe). > + IntRect contentBox = view->contentBoxRect(); > + if (contentBox.height() * contentBox.width() <= 1) > + return false; If we're going to have this code, we should have tests for it. The comment is also incorrect - a 1x1 frame will not make us 'go into compositing mode', since this behavior is behind --force-compositing-mode. I'd honestly prefer that we just delete this unless there's some compelling reason to leave it around, 1x1 composited frames should not be expensive for us.
Adrienne Walker
Comment 71 2011-08-10 13:08:38 PDT
Adrienne Walker
Comment 72 2011-08-10 13:12:17 PDT
(In reply to comment #70) > (From update of attachment 103385 [details]) > View in context: https://bugs.webkit.org/attachment.cgi?id=103385&action=review > > Looks pretty good, except the 1x1 stuff. > > > Source/WebCore/rendering/RenderLayerCompositor.cpp:1388 > > + // Don't go into compositing mode if height or width are zero, or size is 1x1. > > + // (This is sometimes used to load flash in a hidden iframe). > > + IntRect contentBox = view->contentBoxRect(); > > + if (contentBox.height() * contentBox.width() <= 1) > > + return false; > > If we're going to have this code, we should have tests for it. > > The comment is also incorrect - a 1x1 frame will not make us 'go into compositing mode', since this behavior is behind --force-compositing-mode. I'd honestly prefer that we just delete this unless there's some compelling reason to leave it around, 1x1 composited frames should not be expensive for us. Removed. Thanks for pointing that out. (This last patch also merges where force compositing mode is set so that subframe and main frames assign its value in the same block of code.)
James Robinson
Comment 73 2011-08-10 13:22:55 PDT
Comment on attachment 103522 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=103522&action=review > Source/WebCore/rendering/RenderLayerCompositor.cpp:1377 > + ScrollView* scrollView = m_renderView->frameView(); > + if (!scrollView->verticalScrollbar() && !scrollView->horizontalScrollbar()) > + return false; > + > + return view->canBeScrolledAndHasScrollableArea(); This is still a little funky. view == m_renderView here always, right? Why are we checking for scrollbars on the FrameView and checking for scroll overflow on the view? Suggestions: Don't pass a RenderView* as a parameter, just use m_renderView. Also figure out if you really need to check for scrollbars existing and for canBeScrolled..(), since the latter will check for the case where there's no overflow.
Adrienne Walker
Comment 74 2011-08-10 13:24:55 PDT
(In reply to comment #73) > Also figure out if you really need to check for scrollbars existing and for canBeScrolled..(), since the latter will check for the case where there's no overflow. This needed a test anyway.
Adrienne Walker
Comment 75 2011-08-10 14:19:51 PDT
Adrienne Walker
Comment 76 2011-08-10 14:22:46 PDT
(In reply to comment #75) > Created an attachment (id=103533) [details] > Patch The scrollbar check is required if we want to not composite overflow:hidden iframes (which I think is reasonable). I can't think of any case where canBeScrolledEtc() would be true but scrollbars would be false. The function is now simplified even more and I added a test for overflow:hidden.
James Robinson
Comment 77 2011-08-10 14:36:14 PDT
Comment on attachment 103533 [details] Patch Cool, R=me.
Adrienne Walker
Comment 78 2011-08-11 12:45:01 PDT
Note You need to log in before you can comment on or make changes to this bug.