bug 625248 - During each paint, build a list (stored as a FrameProperty of the root nsIFrame) of all the PresShells that are visible during this paint, so that we can increment their paint count correctly during an empty transaction; r=roc a=blocking-final+
authorKevin Gadd <kgadd@mozilla.com>
Thu, 27 Jan 2011 13:53:10 -0800
changeset 61717 8679bd6e8290e37c60a13ee17c04e4cd174ccec3
parent 61716 27e81aa25025037973a39210c5d87d04484c9173
child 61718 c7290e6e795b8eb7bc257a71416f1dc985ee1627
push id1
push userroot
push dateTue, 10 Dec 2013 15:46:25 +0000
reviewersroc, blocking-final
bugs625248
milestone2.0b11pre
bug 625248 - During each paint, build a list (stored as a FrameProperty of the root nsIFrame) of all the PresShells that are visible during this paint, so that we can increment their paint count correctly during an empty transaction; r=roc a=blocking-final+
layout/base/nsDisplayList.cpp
layout/base/nsPresShell.cpp
layout/base/tests/test_mozPaintCount.html
layout/generic/nsIFrame.h
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -261,16 +261,18 @@ nsDisplayListBuilder::EnterPresShell(nsI
     return;
   state->mPresShell = aReferenceFrame->PresContext()->PresShell();
   state->mCaretFrame = nsnull;
   state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length();
 
   state->mPresShell->UpdateCanvasBackground();
 
   if (mIsPaintingToWindow) {
+    mReferenceFrame->AddPaintedPresShell(state->mPresShell);
+    
     state->mPresShell->IncrementPaintCount();
   }
 
   PRBool buildCaret = mBuildCaret;
   if (mIgnoreSuppression || !state->mPresShell->IsPaintingSuppressed()) {
     if (state->mPresShell->IsPaintingSuppressed()) {
       mHadToIgnoreSuppression = PR_TRUE;
     }
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -6139,19 +6139,24 @@ PresShell::Paint(nsIView*           aDis
       ? nsnull : static_cast<nsIFrame*>(aDisplayRoot->GetClientData());
 
   LayerManager* layerManager = aWidgetToPaint->GetLayerManager();
   NS_ASSERTION(layerManager, "Must be in paint event");
   layerManager->BeginTransaction();
 
   if (frame) {
     if (!(frame->GetStateBits() & NS_FRAME_UPDATE_LAYER_TREE)) {
-      if (layerManager->EndEmptyTransaction())
+      if (layerManager->EndEmptyTransaction()) {
+        frame->UpdatePaintCountForPaintedPresShells();
+        
         return NS_OK;
-    }
+      }
+    }
+    
+    frame->ClearPresShellsFromLastPaint();
     frame->RemoveStateBits(NS_FRAME_UPDATE_LAYER_TREE);
   }
 
   nscolor bgcolor = ComputeBackstopColor(aDisplayRoot);
 
   if (frame && aViewToPaint == aDisplayRoot) {
     // Defer invalidates that are triggered during painting, and discard
     // invalidates of areas that are already being repainted.
--- a/layout/base/tests/test_mozPaintCount.html
+++ b/layout/base/tests/test_mozPaintCount.html
@@ -1,41 +1,63 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Tests for mozPaintCount</title>
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
-<body onload="doFlicker()">
-<p id="display"></p>
+<body onload="doBackgroundFlicker()">
+<p id="display">
+<embed type="application/x-test" width="100" height="100" id="p"
+       drawmode="solid" color="FF00FF00"></embed>
+</p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
 var startPaintCount = window.mozPaintCount;
 ok(true, "Got to initial paint count: " + startPaintCount);
 var color = 0;
 
-function doFlicker() {
-  ok(true, "Iteration " + color + ", paint count: " + window.mozPaintCount);
+function doPluginFlicker() {
+  ok(true, "Plugin color iteration " + color + ", paint count: " + window.mozPaintCount);
   if (window.mozPaintCount - startPaintCount > 20) {
-    ok(true, "Got enough paints");
+    ok(true, "Got enough paints from plugin color changes");
     SimpleTest.finish();
     return;
   }
 
   color = (color + 1) % 256;
+  var str = color.toString(16);
+  if (str.length < 2) {
+    str = "0" + str;
+  }
+  str = "FF" + str + str + str;
+  document.getElementById("p").setColor(str);
+  setTimeout(doPluginFlicker, 0);
+}
+
+function doBackgroundFlicker() {
+  ok(true, "Background color iteration " + color + ", paint count: " + window.mozPaintCount);
+  if (window.mozPaintCount - startPaintCount > 20) {
+    ok(true, "Got enough paints from background color changes");
+    startPaintCount = window.mozPaintCount;
+    doPluginFlicker();
+    return;
+  }
+
+  color = (color + 1) % 256;
   document.body.style.backgroundColor = "rgb(" + color + "," + color + "," + color + ")";
-  setTimeout(doFlicker, 0);
+  setTimeout(doBackgroundFlicker, 0);
 }
 
 </script>
 </pre>
 
 <div style="height:4000px"></div>
 <a id="first"  href="http://www.mozilla.org/">first<br>link</a>
 <a id="second" href="http://www.mozilla.org/">second link</a>
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -2644,25 +2644,81 @@ NS_PTR_TO_INT32(frame->Properties().Get(
   virtual nsILineIterator* GetLineIterator() = 0;
 
   /**
    * If this frame is a next-in-flow, and its prev-in-flow has something on its
    * overflow list, pull those frames into the child list of this one.
    */
   virtual void PullOverflowsFromPrevInFlow() {}
 
+  /**
+   * Clear the list of child PresShells generated during the last paint
+   * so that we can begin generating a new one.
+   */  
+  void ClearPresShellsFromLastPaint() { 
+    PaintedPresShellList()->Clear(); 
+  }
+  
+  /**
+   * Flag a child PresShell as painted so that it will get its paint count
+   * incremented during empty transactions.
+   */  
+  void AddPaintedPresShell(nsIPresShell* shell) { 
+    PaintedPresShellList()->AppendElement(do_GetWeakReference(shell)); 
+  }
+  
+  /**
+   * Increment the paint count of all child PresShells that were painted during
+   * the last repaint.
+   */  
+  void UpdatePaintCountForPaintedPresShells() {
+    nsTArray<nsWeakPtr> * list = PaintedPresShellList();
+    for (int i = 0, l = list->Length(); i < l; i++) {
+      nsCOMPtr<nsIPresShell> shell = do_QueryReferent(list->ElementAt(i));
+      
+      if (shell) {
+        shell->IncrementPaintCount();
+      }
+    }
+  }  
+  
 protected:
   // Members
   nsRect           mRect;
   nsIContent*      mContent;
   nsStyleContext*  mStyleContext;
   nsIFrame*        mParent;
 private:
   nsIFrame*        mNextSibling;  // doubly-linked list of frames
   nsIFrame*        mPrevSibling;  // Do not touch outside SetNextSibling!
+
+  static void DestroyPaintedPresShellList(void* propertyValue) {
+    nsTArray<nsWeakPtr>* list = static_cast<nsTArray<nsWeakPtr>*>(propertyValue);
+    list->Clear();
+    delete list;
+  }
+
+  // Stores weak references to all the PresShells that were painted during
+  // the last paint event so that we can increment their paint count during
+  // empty transactions
+  NS_DECLARE_FRAME_PROPERTY(PaintedPresShellsProperty, DestroyPaintedPresShellList)
+  
+  nsTArray<nsWeakPtr>* PaintedPresShellList() {
+    nsTArray<nsWeakPtr>* list = static_cast<nsTArray<nsWeakPtr>*>(
+      Properties().Get(PaintedPresShellsProperty())
+    );
+    
+    if (!list) {
+      list = new nsTArray<nsWeakPtr>();
+      Properties().Set(PaintedPresShellsProperty(), list);
+    }
+    
+    return list;
+  }
+
 protected:
   nsFrameState     mState;
 
   // When there is an overflow area only slightly larger than mRect,
   // we store a set of four 1-byte deltas from the edges of mRect
   // rather than allocating a whole separate rectangle property.
   // Note that these are unsigned values, all measured "outwards"
   // from the edges of mRect, so /mLeft/ and /mTop/ are reversed from