Bug 607417 - Part 3: Visibility tricks for fixed items r=roc
authorTatiana Meshkova <tanya.meshkova@gmail.com>
Fri, 08 Apr 2011 09:35:16 -0700
changeset 67670 288be41b247321bc01af7a584712f2eb5dcc91c9
parent 67669 48f385ad9f0cf30b7e9913d17c4ea59e891ce43d
child 67671 0f077c086750bdf13fadb05f1efd08a0d18cbdcf
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs607417
milestone2.2a1pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 607417 - Part 3: Visibility tricks for fixed items r=roc
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/base/nsLayoutUtils.cpp
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -59,16 +59,17 @@
 #include "nsLayoutUtils.h"
 #include "nsIScrollableFrame.h"
 #include "nsThemeConstants.h"
 
 #include "imgIContainer.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "BasicLayers.h"
 #include "nsBoxFrame.h"
+#include "nsViewportFrame.h"
 
 using namespace mozilla;
 using namespace mozilla::layers;
 typedef FrameMetrics::ViewID ViewID;
 
 nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
     Mode aMode, PRBool aBuildCaret)
     : mReferenceFrame(aReferenceFrame),
@@ -81,31 +82,41 @@ nsDisplayListBuilder::nsDisplayListBuild
       mHadToIgnoreSuppression(PR_FALSE),
       mIsAtRootOfPseudoStackingContext(PR_FALSE),
       mIncludeAllOutOfFlows(PR_FALSE),
       mSelectedFramesOnly(PR_FALSE),
       mAccurateVisibleRegions(PR_FALSE),
       mInTransform(PR_FALSE),
       mSyncDecodeImages(PR_FALSE),
       mIsPaintingToWindow(PR_FALSE),
-      mSnappingEnabled(PR_TRUE) {
+      mSnappingEnabled(PR_TRUE),
+      mHasDisplayPort(PR_FALSE),
+      mHasFixedItems(PR_FALSE)
+{
   MOZ_COUNT_CTOR(nsDisplayListBuilder);
   PL_InitArenaPool(&mPool, "displayListArena", 1024,
                    NS_MAX(NS_ALIGNMENT_OF(void*),NS_ALIGNMENT_OF(double))-1);
 
   nsPresContext* pc = aReferenceFrame->PresContext();
   nsIPresShell *shell = pc->PresShell();
   if (pc->IsRenderingOnlySelection()) {
     nsCOMPtr<nsISelectionController> selcon(do_QueryInterface(shell));
     if (selcon) {
       selcon->GetSelection(nsISelectionController::SELECTION_NORMAL,
                            getter_AddRefs(mBoundingSelection));
     }
   }
 
+  if(mReferenceFrame->GetType() == nsGkAtoms::viewportFrame) {
+    ViewportFrame* viewportFrame = static_cast<ViewportFrame*>(mReferenceFrame);
+    if (!viewportFrame->GetChildList(nsGkAtoms::fixedList).IsEmpty()) {
+      mHasFixedItems = PR_TRUE;
+    }
+  }
+
   LayerBuilder()->Init(this);
 
   PR_STATIC_ASSERT(nsDisplayItem::TYPE_MAX < (1 << nsDisplayItem::TYPE_BITS));
 }
 
 static void MarkFrameForDisplay(nsIFrame* aFrame, nsIFrame* aStopAtFrame) {
   nsFrameManager* frameManager = aFrame->PresContext()->PresShell()->FrameManager();
 
@@ -116,20 +127,48 @@ static void MarkFrameForDisplay(nsIFrame
     f->AddStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
     if (f == aStopAtFrame) {
       // we've reached a frame that we know will be painted, so we can stop.
       break;
     }
   }
 }
 
-static void MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame, nsIFrame* aFrame,
-                                         const nsRect& aDirtyRect) {
+static PRBool IsFixedFrame(nsIFrame* aFrame)
+{
+  return aFrame && aFrame->GetParent() && !aFrame->GetParent()->GetParent();
+}
+
+static PRBool IsFixedItem(nsDisplayItem *aItem, nsDisplayListBuilder* aBuilder)
+{
+  nsIFrame* activeScrolledRoot =
+    nsLayoutUtils::GetActiveScrolledRootFor(aItem, aBuilder);
+  return activeScrolledRoot &&
+         !nsLayoutUtils::ScrolledByViewportScrolling(activeScrolledRoot,
+                                                     aBuilder);
+}
+
+static PRBool ForceVisiblityForFixedItem(nsDisplayListBuilder* aBuilder,
+                                         nsDisplayItem* aItem)
+{
+  return aBuilder->GetHasDisplayPort() && aBuilder->GetHasFixedItems() &&
+         IsFixedItem(aItem, aBuilder);
+}
+
+void nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame,
+                                                        nsIFrame* aFrame,
+                                                        const nsRect& aDirtyRect)
+{
   nsRect dirty = aDirtyRect - aFrame->GetOffsetTo(aDirtyFrame);
   nsRect overflowRect = aFrame->GetVisualOverflowRect();
+
+  if (mHasDisplayPort && IsFixedFrame(aFrame)) {
+    dirty = overflowRect;
+  }
+
   if (!dirty.IntersectRect(dirty, overflowRect))
     return;
   aFrame->Properties().Set(nsDisplayListBuilder::OutOfFlowDirtyRectProperty(),
                            new nsRect(dirty));
 
   MarkFrameForDisplay(aFrame, aDirtyFrame);
 }
 
@@ -425,16 +464,20 @@ nsDisplayList::ComputeVisibilityForSubli
     }
 
     nsRect bounds = item->GetBounds(aBuilder);
 
     nsRegion itemVisible;
     itemVisible.And(*aVisibleRegion, bounds);
     item->mVisibleRect = itemVisible.GetBounds();
 
+    if (ForceVisiblityForFixedItem(aBuilder, item)) {
+      item->mVisibleRect = bounds;
+    }
+
     PRBool containsRootContentDocBG = PR_FALSE;
     if (item->ComputeVisibility(aBuilder, aVisibleRegion, aAllowVisibleRegionExpansion,
                                 containsRootContentDocBG)) {
       if (containsRootContentDocBG) {
         aContainsRootContentDocBG = PR_TRUE;
       }
       anyVisible = PR_TRUE;
       PRBool transparentBackground = PR_FALSE;
@@ -752,16 +795,20 @@ void nsDisplayList::Sort(nsDisplayListBu
 PRBool nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder,
                                           nsRegion* aVisibleRegion) {
   nsRect bounds = GetBounds(aBuilder);
 
   nsRegion itemVisible;
   itemVisible.And(*aVisibleRegion, bounds);
   mVisibleRect = itemVisible.GetBounds();
 
+  if (ForceVisiblityForFixedItem(aBuilder, this)) {
+    mVisibleRect = bounds;
+  }
+
   // When we recompute visibility within layers we don't need to
   // expand the visible region for content behind plugins (the plugin
   // is not in the layer).
   PRBool notUsed;
   if (!ComputeVisibility(aBuilder, aVisibleRegion, nsRect(), notUsed))
     return PR_FALSE;
 
   PRBool forceTransparentBackground;
@@ -814,21 +861,30 @@ nsDisplayBackground::nsDisplayBackground
                                          nsIFrame* aFrame)
   : nsDisplayItem(aBuilder, aFrame),
     mSnappingEnabled(aBuilder->IsSnappingEnabled() && !aBuilder->IsInTransform())
 {
   MOZ_COUNT_CTOR(nsDisplayBackground);
   const nsStyleDisplay* disp = mFrame->GetStyleDisplay();
   mIsThemed = mFrame->IsThemed(disp, &mThemeTransparency);
 
-  // Perform necessary RegisterThemeGeometry
-  if (mIsThemed &&
-      (disp->mAppearance == NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR ||
-       disp->mAppearance == NS_THEME_TOOLBAR)) {
-    RegisterThemeGeometry(aBuilder, aFrame);
+  if (mIsThemed) {
+    // Perform necessary RegisterThemeGeometry
+    if (disp->mAppearance == NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR ||
+        disp->mAppearance == NS_THEME_TOOLBAR) {
+      RegisterThemeGeometry(aBuilder, aFrame);
+    }
+  } else {
+    // Set HasFixedItems if we construct a background-attachment:fixed item
+    nsPresContext* presContext = mFrame->PresContext();
+    nsStyleContext* bgSC;
+    PRBool hasBG = nsCSSRendering::FindBackground(presContext, mFrame, &bgSC);
+    if (hasBG && bgSC->GetStyleBackground()->HasFixedBackground()) {
+      aBuilder->SetHasFixedItems();
+    }
   }
 }
 
 // Helper for RoundedRectIntersectsRect.
 static PRBool
 CheckCorner(nscoord aXOffset, nscoord aYOffset,
             nscoord aXRadius, nscoord aYRadius)
 {
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -288,16 +288,30 @@ public:
   PRBool IsInTransform() { return mInTransform; }
   /**
    * Indicate whether or not we're directly or indirectly under and
    * nsDisplayTransform or SVG foreignObject.
    */
   void SetInTransform(PRBool aInTransform) { mInTransform = aInTransform; }
 
   /**
+   * Call this if using display port for scrolling.
+   */
+  void SetHasDisplayPort() { mHasDisplayPort = PR_TRUE; }
+  PRBool GetHasDisplayPort() { return mHasDisplayPort; }
+
+  /**
+   * Call this if ReferenceFrame() is a viewport frame with fixed-position
+   * children, or when we construct an item which will return true from
+   * ShouldFixToViewport()
+   */
+  void SetHasFixedItems() { mHasFixedItems = PR_TRUE; }
+  PRBool GetHasFixedItems() { return mHasFixedItems; }
+
+  /**
    * Returns true if snapping is enabled for the final drawing context.
    * The default is true.
    */
   PRBool IsSnappingEnabled() { return mSnappingEnabled; }
   /**
    * Set if snapping is enabled for the final drawing context.
    */
   void SetSnappingEnabled(PRBool aSnappingEnabled) { mSnappingEnabled = aSnappingEnabled; }
@@ -451,16 +465,19 @@ public:
 
   NS_DECLARE_FRAME_PROPERTY(OutOfFlowDirtyRectProperty, nsIFrame::DestroyRect)
 
   nsPresContext* CurrentPresContext() {
     return CurrentPresShellState()->mPresShell->GetPresContext();
   }
 
 private:
+  void MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame, nsIFrame* aFrame,
+                                    const nsRect& aDirtyRect);
+
   struct PresShellState {
     nsIPresShell* mPresShell;
     nsIFrame*     mCaretFrame;
     PRUint32      mFirstFrameMarkedForDisplay;
     PRPackedBool  mIsBackgroundOnly;
   };
   PresShellState* CurrentPresShellState() {
     NS_ASSERTION(mPresShellStates.Length() > 0,
@@ -487,16 +504,18 @@ private:
   PRPackedBool                   mSelectedFramesOnly;
   PRPackedBool                   mAccurateVisibleRegions;
   // True when we're building a display list that's directly or indirectly
   // under an nsDisplayTransform
   PRPackedBool                   mInTransform;
   PRPackedBool                   mSyncDecodeImages;
   PRPackedBool                   mIsPaintingToWindow;
   PRPackedBool                   mSnappingEnabled;
+  PRPackedBool                   mHasDisplayPort;
+  PRPackedBool                   mHasFixedItems;
 };
 
 class nsDisplayItem;
 class nsDisplayList;
 /**
  * nsDisplayItems are put in singly-linked lists rooted in an nsDisplayList.
  * nsDisplayItemLink holds the link. The lists are linked from lowest to
  * highest in z-order.
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -1446,16 +1446,20 @@ nsLayoutUtils::PaintFrame(nsIRenderingCo
 
   // If we're going to display something different from what we'd normally
   // paint in a window then we will flush out any retained layer trees before
   // *and after* we draw.
   PRBool willFlushRetainedLayers = (aFlags & PAINT_HIDE_CARET) != 0;
 
   nsDisplayListBuilder builder(aFrame, nsDisplayListBuilder::PAINTING,
 		                       !(aFlags & PAINT_HIDE_CARET));
+  if (usingDisplayPort) {
+    builder.SetHasDisplayPort();
+  }
+
   nsDisplayList list;
   if (aFlags & PAINT_IN_TRANSFORM) {
     builder.SetInTransform(PR_TRUE);
   }
   if (aFlags & PAINT_SYNC_DECODE_IMAGES) {
     builder.SetSyncDecodeImages(PR_TRUE);
   }
   if (aFlags & (PAINT_WIDGET_LAYERS | PAINT_TO_WINDOW)) {