position:fixed that doesn't render any content should not force compositing
https://bugs.webkit.org/show_bug.cgi?id=108112

Source/WebCore:

Reviewed by Beth Dakin.

It's not uncommon for pages to have position:fixed elements with no content.
When these are behind other elements, they can cause those other elements
to become composited, using lots of backing store memory.

Optimize for the case where the position:fixed element has no rendered
content and no children by not making it composited in that case.

Test: compositing/layer-creation/fixed-position-no-content.html

* rendering/RenderLayer.cpp:
(WebCore::RenderLayer::hasNonEmptyChildRenderers): Moved from RenderLayerBacking.cpp.
(WebCore::hasBoxDecorations): Ditto.
(WebCore::RenderLayer::hasBoxDecorationsOrBackground): Ditto.
(WebCore::RenderLayer::hasVisibleBoxDecorations): Check for visibility:visible, box decorations and
overflow controls.
(WebCore::RenderLayer::isVisuallyNonEmpty): Returns true if this layer has some visible
representation.
* rendering/RenderLayer.h:
* rendering/RenderLayerBacking.cpp:
(WebCore::RenderLayerBacking::updateGraphicsLayerConfiguration): Need to call updateDescendantDependentFlags()
to ensure that the visibility flags are up to date.
(WebCore::RenderLayerBacking::updateDrawsContent): Call RenderLayer::hasBoxDecorationsOrBackground() now.
(WebCore::RenderLayerBacking::paintsBoxDecorations): Call RenderLayer::hasVisibleBoxDecorations() now.
(WebCore::RenderLayerBacking::paintsChildren): Call RenderLayer::hasNonEmptyChildRenderers().
(WebCore::RenderLayerBacking::isSimpleContainerCompositingLayer): Whitespace.
(WebCore::RenderLayerBacking::containsPaintedContent): Call RenderLayer::hasBoxDecorationsOrBackground().
(WebCore::RenderLayerBacking::isDirectlyCompositedImage): Ditto.
* rendering/RenderLayerBacking.h:
(RenderLayerBacking):
* rendering/RenderLayerCompositor.cpp:
(WebCore::RenderLayerCompositor::requiresCompositingForPosition): If the layer has no content to paint,
or visible descendant layers, then don't make it composited.

LayoutTests:

Reviewed by Beth Dakin.

Change tests to put a background color on position:fixed elements that need to be composited.
New test with an empty fixed position element.

* compositing/layer-creation/fixed-position-change-out-of-view-in-view.html:
* compositing/layer-creation/fixed-position-no-content-expected.txt: Added.
* compositing/layer-creation/fixed-position-no-content.html: Copied from LayoutTests/compositing/layer-creation/fixed-position-out-of-view.html.
* compositing/layer-creation/fixed-position-out-of-view-scaled-scroll.html:
* compositing/layer-creation/fixed-position-out-of-view-scaled.html:
* compositing/layer-creation/fixed-position-out-of-view.html:
* platform/mac/tiled-drawing/fixed/fixed-position-out-of-view-negative-zindex.html:
* platform/mac/tiled-drawing/fixed/fixed-position-out-of-view.html:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@141039 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 4a8c43e..4470bbc 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,22 @@
+2013-01-28  Simon Fraser  <simon.fraser@apple.com>
+
+        position:fixed that doesn't render any content should not force compositing
+        https://bugs.webkit.org/show_bug.cgi?id=108112
+
+        Reviewed by Beth Dakin.
+        
+        Change tests to put a background color on position:fixed elements that need to be composited.
+        New test with an empty fixed position element.
+
+        * compositing/layer-creation/fixed-position-change-out-of-view-in-view.html:
+        * compositing/layer-creation/fixed-position-no-content-expected.txt: Added.
+        * compositing/layer-creation/fixed-position-no-content.html: Copied from LayoutTests/compositing/layer-creation/fixed-position-out-of-view.html.
+        * compositing/layer-creation/fixed-position-out-of-view-scaled-scroll.html:
+        * compositing/layer-creation/fixed-position-out-of-view-scaled.html:
+        * compositing/layer-creation/fixed-position-out-of-view.html:
+        * platform/mac/tiled-drawing/fixed/fixed-position-out-of-view-negative-zindex.html:
+        * platform/mac/tiled-drawing/fixed/fixed-position-out-of-view.html:
+
 2013-01-28  Keishi Hattori  <keishi@webkit.org>
 
         [Chromium] Skipping mathml tests because MATHML was disasbled.
diff --git a/LayoutTests/compositing/layer-creation/fixed-position-change-out-of-view-in-view.html b/LayoutTests/compositing/layer-creation/fixed-position-change-out-of-view-in-view.html
index 17347c8..480557c 100644
--- a/LayoutTests/compositing/layer-creation/fixed-position-change-out-of-view-in-view.html
+++ b/LayoutTests/compositing/layer-creation/fixed-position-change-out-of-view-in-view.html
@@ -2,6 +2,11 @@
 
 <html>
 <head>
+  <style>
+    #fixed1, #fixed2 {
+        background-color: silver;
+    }
+  </style>
   <script type="text/javascript">
     if (window.testRunner && window.internals) {
       testRunner.dumpAsText();
diff --git a/LayoutTests/compositing/layer-creation/fixed-position-no-content-expected.txt b/LayoutTests/compositing/layer-creation/fixed-position-no-content-expected.txt
new file mode 100644
index 0000000..cf04ad6
--- /dev/null
+++ b/LayoutTests/compositing/layer-creation/fixed-position-no-content-expected.txt
@@ -0,0 +1,3 @@
+There should be no layers.
+
+
diff --git a/LayoutTests/compositing/layer-creation/fixed-position-no-content.html b/LayoutTests/compositing/layer-creation/fixed-position-no-content.html
new file mode 100644
index 0000000..122f90a
--- /dev/null
+++ b/LayoutTests/compositing/layer-creation/fixed-position-no-content.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+  <style>
+    .fixed {
+      position: fixed;
+      width: 50px;
+      height: 50px;
+    }
+    
+    .absolute {
+        position: absolute;
+        top: 40px;
+        left: 40px;
+        height: 100px;
+        width: 100px;
+        background-color: gray;
+    }
+  </style>
+
+  <script type="text/javascript">
+    if (window.internals)
+      window.internals.settings.setAcceleratedCompositingForFixedPositionEnabled(true);
+
+    if (window.testRunner) {
+      testRunner.dumpAsText();
+
+      window.addEventListener("load", function() {
+        document.getElementById("layertree").innerText = window.internals.layerTreeAsText(document);
+      }, false);
+    }
+  </script>
+</head>
+
+<body>
+  <div style="height: 1000px">
+    <p>There should be no layers.</p>
+    <pre id="layertree"></pre>
+  </div>
+
+  <!-- This should not be composited -->
+  <div class="fixed" style="top: 10px; left: 10px"></div>
+  <!-- And this should not be promoted into a layer -->
+  <div class="absolute"></div>
+</body>
+</html>
+
diff --git a/LayoutTests/compositing/layer-creation/fixed-position-out-of-view-scaled-scroll.html b/LayoutTests/compositing/layer-creation/fixed-position-out-of-view-scaled-scroll.html
index c51d708..f966491 100644
--- a/LayoutTests/compositing/layer-creation/fixed-position-out-of-view-scaled-scroll.html
+++ b/LayoutTests/compositing/layer-creation/fixed-position-out-of-view-scaled-scroll.html
@@ -6,6 +6,7 @@
     position: fixed;
     width: 10px;
     height: 10px;
+    background-color: silver;
   }
 </style>
 <script>
diff --git a/LayoutTests/compositing/layer-creation/fixed-position-out-of-view-scaled.html b/LayoutTests/compositing/layer-creation/fixed-position-out-of-view-scaled.html
index 8edd46f..246cd71 100644
--- a/LayoutTests/compositing/layer-creation/fixed-position-out-of-view-scaled.html
+++ b/LayoutTests/compositing/layer-creation/fixed-position-out-of-view-scaled.html
@@ -6,6 +6,7 @@
     position: fixed;
     width: 10px;
     height: 10px;
+    background-color: silver;
   }
 </style>
 <script>
diff --git a/LayoutTests/compositing/layer-creation/fixed-position-out-of-view.html b/LayoutTests/compositing/layer-creation/fixed-position-out-of-view.html
index fc57260..3fda5e5 100644
--- a/LayoutTests/compositing/layer-creation/fixed-position-out-of-view.html
+++ b/LayoutTests/compositing/layer-creation/fixed-position-out-of-view.html
@@ -7,6 +7,7 @@
       position: fixed;
       width: 10px;
       height: 10px;
+      background-color: silver;
     }
   </style>
 
diff --git a/LayoutTests/platform/mac/tiled-drawing/fixed/fixed-position-out-of-view-negative-zindex.html b/LayoutTests/platform/mac/tiled-drawing/fixed/fixed-position-out-of-view-negative-zindex.html
index ba57534..d7f4f53 100644
--- a/LayoutTests/platform/mac/tiled-drawing/fixed/fixed-position-out-of-view-negative-zindex.html
+++ b/LayoutTests/platform/mac/tiled-drawing/fixed/fixed-position-out-of-view-negative-zindex.html
@@ -7,6 +7,7 @@
       position: fixed;
       width: 10px;
       height: 10px;
+      background-color: silver;
     }
   </style>
 
diff --git a/LayoutTests/platform/mac/tiled-drawing/fixed/fixed-position-out-of-view.html b/LayoutTests/platform/mac/tiled-drawing/fixed/fixed-position-out-of-view.html
index 344788b..b72cbf9 100644
--- a/LayoutTests/platform/mac/tiled-drawing/fixed/fixed-position-out-of-view.html
+++ b/LayoutTests/platform/mac/tiled-drawing/fixed/fixed-position-out-of-view.html
@@ -7,6 +7,7 @@
       position: fixed;
       width: 10px;
       height: 10px;
+      background-color: silver;
     }
   </style>
 
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 3696761..0c24d88 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,5 +1,45 @@
 2013-01-28  Simon Fraser  <simon.fraser@apple.com>
 
+        position:fixed that doesn't render any content should not force compositing
+        https://bugs.webkit.org/show_bug.cgi?id=108112
+
+        Reviewed by Beth Dakin.
+        
+        It's not uncommon for pages to have position:fixed elements with no content.
+        When these are behind other elements, they can cause those other elements
+        to become composited, using lots of backing store memory.
+        
+        Optimize for the case where the position:fixed element has no rendered
+        content and no children by not making it composited in that case.
+
+        Test: compositing/layer-creation/fixed-position-no-content.html
+
+        * rendering/RenderLayer.cpp:
+        (WebCore::RenderLayer::hasNonEmptyChildRenderers): Moved from RenderLayerBacking.cpp.
+        (WebCore::hasBoxDecorations): Ditto.
+        (WebCore::RenderLayer::hasBoxDecorationsOrBackground): Ditto.
+        (WebCore::RenderLayer::hasVisibleBoxDecorations): Check for visibility:visible, box decorations and
+        overflow controls.
+        (WebCore::RenderLayer::isVisuallyNonEmpty): Returns true if this layer has some visible
+        representation.
+        * rendering/RenderLayer.h:
+        * rendering/RenderLayerBacking.cpp:
+        (WebCore::RenderLayerBacking::updateGraphicsLayerConfiguration): Need to call updateDescendantDependentFlags()
+        to ensure that the visibility flags are up to date.
+        (WebCore::RenderLayerBacking::updateDrawsContent): Call RenderLayer::hasBoxDecorationsOrBackground() now.
+        (WebCore::RenderLayerBacking::paintsBoxDecorations): Call RenderLayer::hasVisibleBoxDecorations() now.
+        (WebCore::RenderLayerBacking::paintsChildren): Call RenderLayer::hasNonEmptyChildRenderers().
+        (WebCore::RenderLayerBacking::isSimpleContainerCompositingLayer): Whitespace.
+        (WebCore::RenderLayerBacking::containsPaintedContent): Call RenderLayer::hasBoxDecorationsOrBackground().
+        (WebCore::RenderLayerBacking::isDirectlyCompositedImage): Ditto.
+        * rendering/RenderLayerBacking.h:
+        (RenderLayerBacking):
+        * rendering/RenderLayerCompositor.cpp:
+        (WebCore::RenderLayerCompositor::requiresCompositingForPosition): If the layer has no content to paint,
+        or visible descendant layers, then don't make it composited.
+
+2013-01-28  Simon Fraser  <simon.fraser@apple.com>
+
         Avoid doing work at 60fps for tiled layers when not necessary
         https://bugs.webkit.org/show_bug.cgi?id=108135
 
diff --git a/Source/WebCore/rendering/RenderLayer.cpp b/Source/WebCore/rendering/RenderLayer.cpp
index 199740e..45ee35b 100644
--- a/Source/WebCore/rendering/RenderLayer.cpp
+++ b/Source/WebCore/rendering/RenderLayer.cpp
@@ -5404,6 +5404,59 @@
         parent()->dirtyAncestorChainHasSelfPaintingLayerDescendantStatus();
 }
 
+bool RenderLayer::hasNonEmptyChildRenderers() const
+{
+    // Some HTML can cause whitespace text nodes to have renderers, like:
+    // <div>
+    // <img src=...>
+    // </div>
+    // so test for 0x0 RenderTexts here
+    for (RenderObject* child = renderer()->firstChild(); child; child = child->nextSibling()) {
+        if (!child->hasLayer()) {
+            if (child->isRenderInline() || !child->isBox())
+                return true;
+        
+            if (toRenderBox(child)->width() > 0 || toRenderBox(child)->height() > 0)
+                return true;
+        }
+    }
+    return false;
+}
+
+static bool hasBoxDecorations(const RenderStyle* style)
+{
+    return style->hasBorder() || style->hasBorderRadius() || style->hasOutline() || style->hasAppearance() || style->boxShadow() || style->hasFilter();
+}
+
+bool RenderLayer::hasBoxDecorationsOrBackground() const
+{
+    return hasBoxDecorations(renderer()->style()) || renderer()->hasBackground();
+}
+
+bool RenderLayer::hasVisibleBoxDecorations() const
+{
+    if (!hasVisibleContent())
+        return false;
+
+    return hasBoxDecorationsOrBackground() || hasOverflowControls();
+}
+
+bool RenderLayer::isVisuallyNonEmpty() const
+{
+    ASSERT(!m_visibleDescendantStatusDirty);
+
+    if (hasVisibleContent() && hasNonEmptyChildRenderers())
+        return true;
+
+    if (renderer()->isReplaced() || renderer()->hasMask())
+        return true;
+
+    if (hasVisibleBoxDecorations())
+        return true;
+
+    return false;
+}
+
 void RenderLayer::updateStackingContextsAfterStyleChange(const RenderStyle* oldStyle)
 {
     if (!oldStyle)
diff --git a/Source/WebCore/rendering/RenderLayer.h b/Source/WebCore/rendering/RenderLayer.h
index c126022..880b848 100644
--- a/Source/WebCore/rendering/RenderLayer.h
+++ b/Source/WebCore/rendering/RenderLayer.h
@@ -473,6 +473,13 @@
     void setHasVisibleContent();
     void dirtyVisibleContentStatus();
 
+    bool hasBoxDecorationsOrBackground() const;
+    bool hasVisibleBoxDecorations() const;
+    // Returns true if this layer has visible content (ignoring any child layers).
+    bool isVisuallyNonEmpty() const;
+    // True if this layer container renderers that paint.
+    bool hasNonEmptyChildRenderers() const;
+
     // FIXME: We should ASSERT(!m_hasSelfPaintingLayerDescendantDirty); here but we hit the same bugs as visible content above.
     // Part of the issue is with subtree relayout: we don't check if our ancestors have some descendant flags dirty, missing some updates.
     bool hasSelfPaintingLayerDescendant() const { return m_hasSelfPaintingLayerDescendant; }
diff --git a/Source/WebCore/rendering/RenderLayerBacking.cpp b/Source/WebCore/rendering/RenderLayerBacking.cpp
index 06a60da..efa601b 100644
--- a/Source/WebCore/rendering/RenderLayerBacking.cpp
+++ b/Source/WebCore/rendering/RenderLayerBacking.cpp
@@ -76,8 +76,6 @@
 
 using namespace HTMLNames;
 
-static bool hasBoxDecorations(const RenderStyle*);
-static bool hasBoxDecorationsOrBackground(const RenderObject*);
 static bool hasBoxDecorationsOrBackgroundImage(const RenderStyle*);
 static IntRect clipBox(RenderBox* renderer);
 
@@ -474,6 +472,7 @@
     RenderLayerCompositor* compositor = this->compositor();
     RenderObject* renderer = this->renderer();
 
+    m_owningLayer->updateDescendantDependentFlags();
     m_owningLayer->updateZOrderLists();
 
     bool layerConfigChanged = false;
@@ -912,7 +911,7 @@
         // m_graphicsLayer only needs backing store if the non-scrolling parts (background, outlines, borders, shadows etc) need to paint.
         // m_scrollingLayer never has backing store.
         // m_scrollingContentsLayer only needs backing store if the scrolled contents need to paint.
-        bool hasNonScrollingPaintedContent = m_owningLayer->hasVisibleContent() && hasBoxDecorationsOrBackground(renderer());
+        bool hasNonScrollingPaintedContent = m_owningLayer->hasVisibleContent() && m_owningLayer->hasBoxDecorationsOrBackground();
         m_graphicsLayer->setDrawsContent(hasNonScrollingPaintedContent);
 
         bool hasScrollingPaintedContent = m_owningLayer->hasVisibleContent() && (renderer()->hasBackground() || paintsChildren());
@@ -1284,11 +1283,6 @@
     return style->hasBorder() || style->hasBorderRadius() || style->hasOutline() || style->hasAppearance() || style->boxShadow() || style->hasFilter();
 }
 
-static bool hasBoxDecorationsOrBackground(const RenderObject* renderer)
-{
-    return hasBoxDecorations(renderer->style()) || renderer->hasBackground();
-}
-
 static bool hasBoxDecorationsOrBackgroundImage(const RenderStyle* style)
 {
     return hasBoxDecorations(style) || style->hasBackgroundImage();
@@ -1365,24 +1359,18 @@
 
 bool RenderLayerBacking::paintsBoxDecorations() const
 {
-    if (!m_owningLayer->hasVisibleContent())
-        return false;
-
-    if (!hasBoxDecorationsOrBackground(renderer()))
+    if (!m_owningLayer->hasVisibleBoxDecorations())
         return false;
 
     if (!supportsDirectBoxDecorationsComposition(renderer()))
         return true;
 
-    if (m_owningLayer->hasOverflowControls())
-        return true;
-
     return false;
 }
 
 bool RenderLayerBacking::paintsChildren() const
 {
-    if (m_owningLayer->hasVisibleContent() && containsNonEmptyRenderers())
+    if (m_owningLayer->hasVisibleContent() && m_owningLayer->hasNonEmptyChildRenderers())
         return true;
         
     if (hasVisibleNonCompositingDescendantLayers())
@@ -1406,7 +1394,7 @@
         return false;
 
     if (renderObject->isReplaced() && !isCompositedPlugin(renderObject))
-            return false;
+        return false;
     
     if (paintsBoxDecorations() || paintsChildren())
         return false;
@@ -1439,25 +1427,6 @@
     return true;
 }
 
-bool RenderLayerBacking::containsNonEmptyRenderers() const
-{
-    // Some HTML can cause whitespace text nodes to have renderers, like:
-    // <div>
-    // <img src=...>
-    // </div>
-    // so test for 0x0 RenderTexts here
-    for (RenderObject* child = renderer()->firstChild(); child; child = child->nextSibling()) {
-        if (!child->hasLayer()) {
-            if (child->isRenderInline() || !child->isBox())
-                return true;
-            
-            if (toRenderBox(child)->width() > 0 || toRenderBox(child)->height() > 0)
-                return true;
-        }
-    }
-    return false;
-}
-
 // Conservative test for having no rendered children.
 bool RenderLayerBacking::hasVisibleNonCompositingDescendantLayers() const
 {
@@ -1516,12 +1485,12 @@
     // and set background color on the layer in that case, instead of allocating backing store and painting.
 #if ENABLE(VIDEO)
     if (renderer()->isVideo() && toRenderVideo(renderer())->shouldDisplayVideo())
-        return hasBoxDecorationsOrBackground(renderer());
+        return m_owningLayer->hasBoxDecorationsOrBackground();
 #endif
 #if PLATFORM(MAC) && USE(CA) && (PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
 #elif ENABLE(WEBGL) || ENABLE(ACCELERATED_2D_CANVAS)
     if (isAcceleratedCanvas(renderer()))
-        return hasBoxDecorationsOrBackground(renderer());
+        return m_owningLayer->hasBoxDecorationsOrBackground();
 #endif
 
     return true;
@@ -1533,7 +1502,7 @@
 {
     RenderObject* renderObject = renderer();
     
-    if (!renderObject->isImage() || hasBoxDecorationsOrBackground(renderObject) || renderObject->hasClip())
+    if (!renderObject->isImage() || m_owningLayer->hasBoxDecorationsOrBackground() || renderObject->hasClip())
         return false;
 
     RenderImage* imageRenderer = toRenderImage(renderObject);
diff --git a/Source/WebCore/rendering/RenderLayerBacking.h b/Source/WebCore/rendering/RenderLayerBacking.h
index 58adf3a..c9abce3 100644
--- a/Source/WebCore/rendering/RenderLayerBacking.h
+++ b/Source/WebCore/rendering/RenderLayerBacking.h
@@ -152,7 +152,7 @@
     void adjustTileCacheCoverage();
     
     void updateDebugIndicators(bool showBorder, bool showRepaintCounter);
-    
+
     // GraphicsLayerClient interface
     virtual bool shouldUseTileCache(const GraphicsLayer*) const OVERRIDE;
     virtual void notifyAnimationStarted(const GraphicsLayer*, double startTime) OVERRIDE;
@@ -256,7 +256,6 @@
     void updateBackgroundColor(bool isSimpleContainer);
     void updateContentsRect(bool isSimpleContainer);
 
-    bool containsNonEmptyRenderers() const;
     bool hasVisibleNonCompositingDescendantLayers() const;
 
     bool shouldClipCompositedBounds() const;
diff --git a/Source/WebCore/rendering/RenderLayerCompositor.cpp b/Source/WebCore/rendering/RenderLayerCompositor.cpp
index 6b297ae..c292487 100644
--- a/Source/WebCore/rendering/RenderLayerCompositor.cpp
+++ b/Source/WebCore/rendering/RenderLayerCompositor.cpp
@@ -2077,6 +2077,13 @@
             return false;
         }
     }
+    
+    bool paintsContent = layer->isVisuallyNonEmpty() || layer->hasVisibleDescendant();
+    if (!paintsContent) {
+        // isVisuallyNonEmpty() depends on layout.
+        m_reevaluateCompositingAfterLayout = true;
+        return false;
+    }
 
     return true;
 }