Title: [183777] trunk
Revision
183777
Author
[email protected]
Date
2015-05-04 16:31:10 -0700 (Mon, 04 May 2015)

Log Message

display:none iframes cause repeated compositing flushing
https://bugs.webkit.org/show_bug.cgi?id=144529

Reviewed by Darin Adler.

Source/WebCore:

FrameView::updateLayoutAndStyleIfNeededRecursive() only forces layout on rendered
frames, by virtue of using its Widget children which are FrameViews.

However, FrameView::flushCompositingStateIncludingSubframes() iterated over
all frames, and return false if any subframe needed layout. Thus, if it saw
non-rendered frames (which are never laid out), it would return false,
which causes the CFRunLoopObserver that drives flushing to run again.

Fix by having FrameView::flushCompositingStateIncludingSubframes() only check
rendered frames, using FrameTree::traverseNextRendered() (which needs to be public).

Also change FrameView::needsStyleRecalcOrLayout() and FrameView::updateLayoutAndStyleIfNeededRecursive()
to fetch the list of FrameViews using FrameTree's nextRenderedSibling(), rather than using
the Widget tree, since we'd like to eventually remove Widgets, and using the Frame
tree matches flushCompositingStateIncludingSubframes() and other code.

Test: compositing/iframes/display-none-subframe.html

* page/FrameTree.h:
* page/FrameView.cpp:
(WebCore::FrameView::flushCompositingStateIncludingSubframes):
(WebCore::FrameView::needsStyleRecalcOrLayout):
(WebCore::FrameView::renderedChildFrameViews): Helper that returns a vector
of Ref<FrameView>s for rendered frames only.
(WebCore::FrameView::updateLayoutAndStyleIfNeededRecursive):
* page/FrameView.h:

LayoutTests:

Test with a display:none iframe that triggers a single compositing flush,
then counts how many occur in 10ms.

* compositing/iframes/display-none-subframe-expected.txt: Added.
* compositing/iframes/display-none-subframe.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (183776 => 183777)


--- trunk/LayoutTests/ChangeLog	2015-05-04 23:10:34 UTC (rev 183776)
+++ trunk/LayoutTests/ChangeLog	2015-05-04 23:31:10 UTC (rev 183777)
@@ -1,5 +1,18 @@
 2015-05-04  Simon Fraser  <[email protected]>
 
+        display:none iframes cause repeated compositing flushing
+        https://bugs.webkit.org/show_bug.cgi?id=144529
+
+        Reviewed by Darin Adler.
+        
+        Test with a display:none iframe that triggers a single compositing flush,
+        then counts how many occur in 10ms.
+
+        * compositing/iframes/display-none-subframe-expected.txt: Added.
+        * compositing/iframes/display-none-subframe.html: Added.
+
+2015-05-04  Simon Fraser  <[email protected]>
+
         Fix updating of tiled backing opaquenss when the page background color changes
         https://bugs.webkit.org/show_bug.cgi?id=144600
         rdar://problem/20723035

Added: trunk/LayoutTests/compositing/iframes/display-none-subframe-expected.txt (0 => 183777)


--- trunk/LayoutTests/compositing/iframes/display-none-subframe-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/compositing/iframes/display-none-subframe-expected.txt	2015-05-04 23:31:10 UTC (rev 183777)
@@ -0,0 +1,3 @@
+PASS: saw 1 or 2 layer flushes
+
+

Added: trunk/LayoutTests/compositing/iframes/display-none-subframe.html (0 => 183777)


--- trunk/LayoutTests/compositing/iframes/display-none-subframe.html	                        (rev 0)
+++ trunk/LayoutTests/compositing/iframes/display-none-subframe.html	2015-05-04 23:31:10 UTC (rev 183777)
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+    <style>
+        #box {
+            width: 100px;
+            height: 100px;
+            margin: 10px;
+            background-color: blue;
+        }
+        
+        .composited {
+            -webkit-transform: translateZ(0);
+        }
+        
+        iframe {
+            display: none;
+        }
+    </style>
+    <script>
+        if (window.testRunner) {
+            testRunner.dumpAsText();
+            testRunner.waitUntilDone();
+        }
+
+        function doTest()
+        {
+            document.body.offsetWidth;
+            
+            if (window.internals)
+                internals.startTrackingLayerFlushes();
+
+            document.getElementById('box').classList.add('composited');
+
+            if (window.internals)
+                internals.updateLayoutIgnorePendingStylesheetsAndRunPostLayoutTasks();
+
+            window.setTimeout(function() {
+                var flushCount = 0;
+                if (window.internals)
+                    flushCount = internals.layerFlushCount();
+                
+                var result;
+                if (flushCount == 1 || flushCount == 2)
+                    result = 'PASS: saw 1 or 2 layer flushes';
+                else
+                    result = 'FAIL: saw ' + flushCount + ' layer flushes, expected 1 or possibly 2.';
+                    
+                document.getElementById('result').textContent = result;
+
+                if (window.testRunner)
+                    testRunner.notifyDone();
+            }, 10);
+        }
+        
+        window.addEventListener('load', doTest, false);
+    </script>
+</head>
+<body>
+    <p id="result"></p>
+    <div id="box"></div>
+    <iframe src=""
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (183776 => 183777)


--- trunk/Source/WebCore/ChangeLog	2015-05-04 23:10:34 UTC (rev 183776)
+++ trunk/Source/WebCore/ChangeLog	2015-05-04 23:31:10 UTC (rev 183777)
@@ -1,3 +1,37 @@
+2015-05-04  Simon Fraser  <[email protected]>
+
+        display:none iframes cause repeated compositing flushing
+        https://bugs.webkit.org/show_bug.cgi?id=144529
+
+        Reviewed by Darin Adler.
+        
+        FrameView::updateLayoutAndStyleIfNeededRecursive() only forces layout on rendered
+        frames, by virtue of using its Widget children which are FrameViews.
+        
+        However, FrameView::flushCompositingStateIncludingSubframes() iterated over
+        all frames, and return false if any subframe needed layout. Thus, if it saw
+        non-rendered frames (which are never laid out), it would return false,
+        which causes the CFRunLoopObserver that drives flushing to run again.
+        
+        Fix by having FrameView::flushCompositingStateIncludingSubframes() only check
+        rendered frames, using FrameTree::traverseNextRendered() (which needs to be public).
+        
+        Also change FrameView::needsStyleRecalcOrLayout() and FrameView::updateLayoutAndStyleIfNeededRecursive()
+        to fetch the list of FrameViews using FrameTree's nextRenderedSibling(), rather than using
+        the Widget tree, since we'd like to eventually remove Widgets, and using the Frame
+        tree matches flushCompositingStateIncludingSubframes() and other code.
+
+        Test: compositing/iframes/display-none-subframe.html
+
+        * page/FrameTree.h:
+        * page/FrameView.cpp:
+        (WebCore::FrameView::flushCompositingStateIncludingSubframes):
+        (WebCore::FrameView::needsStyleRecalcOrLayout):
+        (WebCore::FrameView::renderedChildFrameViews): Helper that returns a vector
+        of Ref<FrameView>s for rendered frames only.
+        (WebCore::FrameView::updateLayoutAndStyleIfNeededRecursive):
+        * page/FrameView.h:
+
 2015-05-04  Chris Dumez  <[email protected]>
 
         Unreviewed. Fix build with SECURITY_ASSERTIONS enabled.

Modified: trunk/Source/WebCore/page/FrameTree.h (183776 => 183777)


--- trunk/Source/WebCore/page/FrameTree.h	2015-05-04 23:10:34 UTC (rev 183776)
+++ trunk/Source/WebCore/page/FrameTree.h	2015-05-04 23:31:10 UTC (rev 183777)
@@ -55,6 +55,9 @@
         Frame* firstChild() const { return m_firstChild.get(); }
         Frame* lastChild() const { return m_lastChild; }
 
+        Frame* firstRenderedChild() const;
+        Frame* nextRenderedSibling() const;
+
         WEBCORE_EXPORT bool isDescendantOf(const Frame* ancestor) const;
         
         WEBCORE_EXPORT Frame* traverseNext(const Frame* stayWithin = nullptr) const;
@@ -90,9 +93,6 @@
         Frame* scopedChild(const AtomicString& name, TreeScope*) const;
         unsigned scopedChildCount(TreeScope*) const;
 
-        Frame* firstRenderedChild() const;
-        Frame* nextRenderedSibling() const;
-
         Frame& m_thisFrame;
 
         Frame* m_parent;

Modified: trunk/Source/WebCore/page/FrameView.cpp (183776 => 183777)


--- trunk/Source/WebCore/page/FrameView.cpp	2015-05-04 23:10:34 UTC (rev 183776)
+++ trunk/Source/WebCore/page/FrameView.cpp	2015-05-04 23:31:10 UTC (rev 183777)
@@ -1066,8 +1066,10 @@
 bool FrameView::flushCompositingStateIncludingSubframes()
 {
     bool allFramesFlushed = flushCompositingStateForThisFrame(&frame());
-    
-    for (Frame* child = frame().tree().firstChild(); child; child = child->tree().traverseNext(m_frame.ptr())) {
+
+    for (Frame* child = frame().tree().firstRenderedChild(); child; child = child->tree().traverseNextRendered(m_frame.ptr())) {
+        if (!child->view())
+            continue;
         bool flushed = child->view()->flushCompositingStateForThisFrame(&frame());
         allFramesFlushed &= flushed;
     }
@@ -2593,16 +2595,8 @@
     if (!includeSubframes)
         return false;
 
-    // Find child frames via the Widget tree, as updateLayoutAndStyleIfNeededRecursive() does.
-    Vector<Ref<FrameView>, 16> childViews;
-    childViews.reserveInitialCapacity(children().size());
-    for (auto& widget : children()) {
-        if (is<FrameView>(*widget))
-            childViews.uncheckedAppend(downcast<FrameView>(*widget));
-    }
-
-    for (unsigned i = 0; i < childViews.size(); ++i) {
-        if (childViews[i]->needsStyleRecalcOrLayout())
+    for (auto& frameView : renderedChildFrameViews()) {
+        if (frameView->needsStyleRecalcOrLayout())
             return true;
     }
 
@@ -4036,6 +4030,17 @@
     ScrollView::paintOverhangAreas(context, horizontalOverhangArea, verticalOverhangArea, dirtyRect);
 }
 
+FrameView::FrameViewList FrameView::renderedChildFrameViews() const
+{
+    FrameViewList childViews;
+    for (Frame* frame = m_frame->tree().firstRenderedChild(); frame; frame = frame->tree().nextRenderedSibling()) {
+        if (frame->view())
+            childViews.append(*frame->view());
+    }
+    
+    return childViews;
+}
+
 void FrameView::updateLayoutAndStyleIfNeededRecursive()
 {
     // We have to crawl our entire tree looking for any FrameViews that need
@@ -4054,21 +4059,11 @@
     if (needsLayout())
         layout();
 
-    // Grab a copy of the children() set, as it may be mutated by the following updateLayoutAndStyleIfNeededRecursive
-    // calls, as they can potentially re-enter a layout of the parent frame view, which may add/remove scrollbars
-    // and thus mutates the children() set.
-    // We use the Widget children because walking the Frame tree would include display:none frames.
-    // FIXME: use FrameTree::traverseNextRendered().
-    Vector<Ref<FrameView>, 16> childViews;
-    childViews.reserveInitialCapacity(children().size());
-    for (auto& widget : children()) {
-        if (is<FrameView>(*widget))
-            childViews.uncheckedAppend(downcast<FrameView>(*widget));
-    }
+    // Grab a copy of the child views, as the list may be mutated by the following updateLayoutAndStyleIfNeededRecursive
+    // calls, as they can potentially re-enter a layout of the parent frame view.
+    for (auto& frameView : renderedChildFrameViews())
+        frameView->updateLayoutAndStyleIfNeededRecursive();
 
-    for (unsigned i = 0; i < childViews.size(); ++i)
-        childViews[i]->updateLayoutAndStyleIfNeededRecursive();
-
     // A child frame may have dirtied us during its layout.
     frame().document()->updateStyleIfNeeded();
     if (needsLayout())

Modified: trunk/Source/WebCore/page/FrameView.h (183776 => 183777)


--- trunk/Source/WebCore/page/FrameView.h	2015-05-04 23:10:34 UTC (rev 183776)
+++ trunk/Source/WebCore/page/FrameView.h	2015-05-04 23:31:10 UTC (rev 183777)
@@ -517,6 +517,9 @@
 
     const HashSet<Widget*>& widgetsInRenderTree() const { return m_widgetsInRenderTree; }
 
+    typedef Vector<Ref<FrameView>, 16> FrameViewList;
+    FrameViewList renderedChildFrameViews() const;
+
     void addTrackedRepaintRect(const FloatRect&);
 
     // exposedRect represents WebKit's understanding of what part
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to