Bug 666446, Part 8/10 - Change xul tree implementation to utilize refresh driver based animations for performance improvements with animated images. [r=roc]
authorScott Johnson <sjohnson@mozilla.com>
Tue, 01 Nov 2011 11:06:53 -0400
changeset 79542 8cfeffcdec35cc5962c3e9b3968363456aa95ee9
parent 79541 cb8c432c0092d1bf97f0add4d025c4d19ca34a20
child 79543 5596034f204bd030e75d114692433fde484439de
push id277
push userrcampbell@mozilla.com
push dateThu, 03 Nov 2011 12:29:43 +0000
treeherderfx-team@e81e47f8e5bb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs666446
milestone10.0a1
Bug 666446, Part 8/10 - Change xul tree implementation to utilize refresh driver based animations for performance improvements with animated images. [r=roc]
layout/xul/base/src/tree/src/nsITreeImageListener.h
layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
layout/xul/base/src/tree/src/nsTreeBodyFrame.h
layout/xul/base/src/tree/src/nsTreeImageListener.cpp
layout/xul/base/src/tree/src/nsTreeImageListener.h
new file mode 100644
--- /dev/null
+++ b/layout/xul/base/src/tree/src/nsITreeImageListener.h
@@ -0,0 +1,64 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Dave Hyatt <hyatt@mozilla.org> (Original Author)
+ *   Jan Varga <varga@ku.sk>
+ *   Scott Johnson <sjohnson@mozilla.com>, Mozilla Corporation
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsITreeImageListener_h__
+#define nsITreeImageListener_h__
+
+// The interface for our image listener.
+// {90586540-2D50-403e-8DCE-981CAA778444}
+#define NS_ITREEIMAGELISTENER_IID \
+{ 0x90586540, 0x2d50, 0x403e, { 0x8d, 0xce, 0x98, 0x1c, 0xaa, 0x77, 0x84, 0x44 } }
+
+class nsITreeImageListener : public nsISupports
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_ITREEIMAGELISTENER_IID)
+
+  NS_IMETHOD AddCell(PRInt32 aIndex, nsITreeColumn* aCol) = 0;
+
+  /**
+   * Clear the internal frame pointer to prevent dereferencing an object
+   * that no longer exists.
+   */
+  NS_IMETHOD ClearFrame() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsITreeImageListener, NS_ITREEIMAGELISTENER_IID)
+
+#endif
--- a/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
+++ b/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
@@ -49,16 +49,17 @@
 
 #include "nsCOMPtr.h"
 #include "nsISupportsArray.h"
 #include "nsPresContext.h"
 #include "nsINameSpaceManager.h"
 
 #include "nsTreeBodyFrame.h"
 #include "nsTreeSelection.h"
+#include "nsTreeImageListener.h"
 
 #include "nsGkAtoms.h"
 #include "nsCSSAnonBoxes.h"
 
 #include "nsIContent.h"
 #include "nsStyleContext.h"
 #include "nsIBoxObject.h"
 #include "nsGUIEvent.h"
@@ -109,16 +110,24 @@
 
 using namespace mozilla;
 
 // Enumeration function that cancels all the image requests in our cache
 static PLDHashOperator
 CancelImageRequest(const nsAString& aKey,
                    nsTreeImageCacheEntry aEntry, void* aData)
 {
+
+  // If our imgIRequest object was registered with the refresh driver,
+  // then we need to deregister it.
+  nsTreeBodyFrame* frame = static_cast<nsTreeBodyFrame*>(aData);
+
+  nsLayoutUtils::DeregisterImageRequest(frame->PresContext(), aEntry.request,
+                                        nsnull);
+
   aEntry.request->CancelAndForgetObserver(NS_BINDING_ABORTED);
   return PL_DHASH_NEXT;
 }
 
 //
 // NS_NewTreeFrame
 //
 // Creates a new tree frame
@@ -159,17 +168,18 @@ nsTreeBodyFrame::nsTreeBodyFrame(nsIPres
 {
   mColumns = new nsTreeColumns(nsnull);
   NS_NewISupportsArray(getter_AddRefs(mScratchArray));
 }
 
 // Destructor
 nsTreeBodyFrame::~nsTreeBodyFrame()
 {
-  mImageCache.EnumerateRead(CancelImageRequest, nsnull);
+  mImageCache.EnumerateRead(CancelImageRequest, this);
+  DetachImageListeners();
   delete mSlots;
 }
 
 static void
 GetBorderPadding(nsStyleContext* aContext, nsMargin& aMargin)
 {
   aMargin.SizeTo(0, 0, 0, 0);
   if (!aContext->GetStylePadding()->GetPadding(aMargin)) {
@@ -192,18 +202,21 @@ nsTreeBodyFrame::Init(nsIContent*     aC
                       nsIFrame*       aPrevInFlow)
 {
   nsresult rv = nsLeafBoxFrame::Init(aContent, aParent, aPrevInFlow);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mIndentation = GetIndentation();
   mRowHeight = GetRowHeight();
 
+  NS_ENSURE_TRUE(mCreatedListeners.Init(), NS_ERROR_OUT_OF_MEMORY);
+
   NS_ENSURE_TRUE(mImageCache.Init(16), NS_ERROR_OUT_OF_MEMORY);
   EnsureBoxObject();
+
   return rv;
 }
 
 nsSize
 nsTreeBodyFrame::GetMinSize(nsBoxLayoutState& aBoxLayoutState)
 {
   EnsureView();
 
@@ -2149,20 +2162,24 @@ nsTreeBodyFrame::GetImage(PRInt32 aRowIn
         listener->AddCell(aRowIndex, aCol);
       return NS_OK;
     }
   }
 
   if (!*aResult) {
     // Create a new nsTreeImageListener object and pass it our row and column
     // information.
-    nsTreeImageListener* listener = new nsTreeImageListener(mTreeBoxObject);
+    nsTreeImageListener* listener = new nsTreeImageListener(this);
     if (!listener)
       return NS_ERROR_OUT_OF_MEMORY;
 
+    if (!mCreatedListeners.PutEntry(listener)) {
+      return NS_ERROR_FAILURE;
+    }
+
     listener->AddCell(aRowIndex, aCol);
     nsCOMPtr<imgIDecoderObserver> imgDecoderObserver = listener;
 
     nsCOMPtr<imgIRequest> imageRequest;
     if (styleRequest) {
       styleRequest->Clone(imgDecoderObserver, getter_AddRefs(imageRequest));
     } else {
       nsIDocument* doc = mContent->GetDocument();
@@ -4231,17 +4248,17 @@ nsTreeBodyFrame::GetBaseElement()
 
   return nsnull;
 }
 
 nsresult
 nsTreeBodyFrame::ClearStyleAndImageCaches()
 {
   mStyleCache.Clear();
-  mImageCache.EnumerateRead(CancelImageRequest, nsnull);
+  mImageCache.EnumerateRead(CancelImageRequest, this);
   mImageCache.Clear();
   return NS_OK;
 }
 
 /* virtual */ void
 nsTreeBodyFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
 {
   nsLeafBoxFrame::DidSetStyleContext(aOldStyleContext);
@@ -4463,16 +4480,30 @@ nsTreeBodyFrame::PostScrollEvent()
   nsRefPtr<ScrollEvent> ev = new ScrollEvent(this);
   if (NS_FAILED(NS_DispatchToCurrentThread(ev))) {
     NS_WARNING("failed to dispatch ScrollEvent");
   } else {
     mScrollEvent = ev;
   }
 }
 
+void
+nsTreeBodyFrame::DetachImageListeners()
+{
+  mCreatedListeners.Clear();
+}
+
+void
+nsTreeBodyFrame::RemoveTreeImageListener(nsTreeImageListener* aListener)
+{
+  if (aListener) {
+    mCreatedListeners.RemoveEntry(aListener);
+  }
+}
+
 #ifdef ACCESSIBILITY
 void
 nsTreeBodyFrame::FireRowCountChangedEvent(PRInt32 aIndex, PRInt32 aCount)
 {
   nsCOMPtr<nsIContent> content(GetBaseElement());
   if (!content)
     return;
 
@@ -4636,8 +4667,24 @@ nsTreeBodyFrame::FullScrollbarsUpdate(bo
   if (aNeedsFullInvalidation) {
     Invalidate();
   }
   InvalidateScrollbars(parts, weakColumnsFrame);
   NS_ENSURE_TRUE(weakFrame.IsAlive(), false);
   nsContentUtils::AddScriptRunner(new nsOverflowChecker(this));
   return weakFrame.IsAlive();
 }
+
+nsresult
+nsTreeBodyFrame::OnStartDecode(imgIRequest* aRequest)
+{
+  nsLayoutUtils::RegisterImageRequest(PresContext(), aRequest, nsnull);
+  return NS_OK;
+}
+
+nsresult
+nsTreeBodyFrame::OnStopDecode(imgIRequest* aRequest, nsresult aStatus,
+                              const PRUnichar* aStatusArg)
+{
+  nsLayoutUtils::DeregisterImageRequestIfNotAnimated(PresContext(), aRequest,
+                                                     nsnull);
+  return NS_OK;
+}
--- a/layout/xul/base/src/tree/src/nsTreeBodyFrame.h
+++ b/layout/xul/base/src/tree/src/nsTreeBodyFrame.h
@@ -49,26 +49,27 @@
 #include "nsICSSPseudoComparator.h"
 #include "nsIScrollbarMediator.h"
 #include "nsIDragSession.h"
 #include "nsITimer.h"
 #include "nsIReflowCallback.h"
 #include "nsTArray.h"
 #include "nsTreeStyleCache.h"
 #include "nsTreeColumns.h"
-#include "nsTreeImageListener.h"
 #include "nsAutoPtr.h"
 #include "nsDataHashtable.h"
 #include "imgIRequest.h"
 #include "imgIDecoderObserver.h"
 #include "nsScrollbarFrame.h"
 #include "nsThreadUtils.h"
 #include "mozilla/LookAndFeel.h"
+#include "nsITreeImageListener.h"
 
 class nsOverflowChecker;
+class nsTreeImageListener;
 
 // An entry in the tree's image cache
 struct nsTreeImageCacheEntry
 {
   nsTreeImageCacheEntry() {}
   nsTreeImageCacheEntry(imgIRequest *aRequest, imgIDecoderObserver *aListener)
     : request(aRequest), listener(aListener) {}
 
@@ -86,16 +87,23 @@ class NS_FINAL_CLASS nsTreeBodyFrame
 public:
   nsTreeBodyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
   ~nsTreeBodyFrame();
 
   NS_DECL_QUERYFRAME_TARGET(nsTreeBodyFrame)
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS
 
+  // Callback handler methods for refresh driver based animations.
+  // Calls to these functions are forwarded from nsTreeImageListener. These
+  // mirror how nsImageFrame works.
+  nsresult OnStartDecode(imgIRequest* aRequest);
+  nsresult OnStopDecode(imgIRequest* aRequest, nsresult aStatus,
+                        const PRUnichar* aStatusArg);
+
   // non-virtual signatures like nsITreeBodyFrame
   nsresult GetColumns(nsITreeColumns **aColumns);
   nsresult GetView(nsITreeView **aView);
   nsresult SetView(nsITreeView *aView);
   nsresult GetFocused(bool *aFocused);
   nsresult SetFocused(bool aFocused);
   nsresult GetTreeBody(nsIDOMElement **aElement);
   nsresult GetRowHeight(PRInt32 *aValue);
@@ -430,16 +438,25 @@ public:
     if (!aUnknownCol)
       return nsnull;
 
     nsTreeColumn* col;
     aUnknownCol->QueryInterface(NS_GET_IID(nsTreeColumn), (void**)&col);
     return col;
   }
 
+  /**
+   * Remove an nsITreeImageListener from being tracked by this frame. Only tree
+   * image listeners that are created by this frame are tracked.
+   *
+   * @param aListener A pointer to an nsTreeImageListener to no longer
+   *        track.
+   */
+  void RemoveTreeImageListener(nsTreeImageListener* aListener);
+
 protected:
 
   // Create a new timer. This method is used to delay various actions like
   // opening/closing folders or tree scrolling.
   // aID is type of the action, aFunc is the function to be called when
   // the timer fires and aType is type of timer - one shot or repeating.
   nsresult CreateTimer(const mozilla::LookAndFeel::IntID aID,
                        nsTimerCallbackFunc aFunc, PRInt32 aType,
@@ -460,16 +477,22 @@ protected:
     void Revoke() { mInner = nsnull; }
   private:
     nsTreeBodyFrame* mInner;
   };
 
   void PostScrollEvent();
   void FireScrollEvent();
 
+  /**
+   * Clear the pointer to this frame for all nsTreeImageListeners that were
+   * created by this frame.
+   */
+  void DetachImageListeners();
+
 #ifdef ACCESSIBILITY
   /**
    * Fires 'treeRowCountChanged' event asynchronously. The event supports
    * nsIDOMDataContainerEvent interface that is used to expose the following
    * information structures.
    *
    * @param aIndex  the row index rows are added/removed from
    * @param aCount  the number of added/removed rows (the sign points to
@@ -597,11 +620,16 @@ protected: // Data Members
 
   // Do we have a fixed number of onscreen rows?
   bool mHasFixedRowCount;
 
   bool mVerticalOverflow;
   bool mHorizontalOverflow;
 
   bool mReflowCallbackPosted;
+
+  // Hash table to keep track of which listeners we created and thus
+  // have pointers to us.
+  nsTHashtable<nsPtrHashKey<nsTreeImageListener> > mCreatedListeners;
+
 }; // class nsTreeBodyFrame
 
 #endif
--- a/layout/xul/base/src/tree/src/nsTreeImageListener.cpp
+++ b/layout/xul/base/src/tree/src/nsTreeImageListener.cpp
@@ -39,28 +39,51 @@
 
 #include "nsTreeImageListener.h"
 #include "nsITreeBoxObject.h"
 #include "imgIRequest.h"
 #include "imgIContainer.h"
 
 NS_IMPL_ISUPPORTS3(nsTreeImageListener, imgIDecoderObserver, imgIContainerObserver, nsITreeImageListener)
 
-nsTreeImageListener::nsTreeImageListener(nsITreeBoxObject* aTree)
-  : mTree(aTree),
+nsTreeImageListener::nsTreeImageListener(nsTreeBodyFrame* aTreeFrame)
+  : mTreeFrame(aTreeFrame),
     mInvalidationSuppressed(true),
     mInvalidationArea(nsnull)
 {
 }
 
 nsTreeImageListener::~nsTreeImageListener()
 {
   delete mInvalidationArea;
 }
 
+NS_IMETHODIMP
+nsTreeImageListener::OnStartDecode(imgIRequest *aRequest)
+{
+  if (!mTreeFrame) {
+    return NS_OK;
+  }
+
+  // grab the frame we want to use
+  return mTreeFrame->OnStartDecode(aRequest);
+}
+
+NS_IMETHODIMP
+nsTreeImageListener::OnStopDecode(imgIRequest *aRequest,
+                                  nsresult aStatus,
+                                  const PRUnichar *aStatusArg)
+{
+  if (!mTreeFrame) {
+    return NS_OK;
+  }
+
+  return mTreeFrame->OnStopDecode(aRequest, aStatus, aStatusArg);
+}
+
 NS_IMETHODIMP nsTreeImageListener::OnStartContainer(imgIRequest *aRequest,
                                                     imgIContainer *aImage)
 {
   // Ensure the animation (if any) is started. Note: There is no
   // corresponding call to Decrement for this. This Increment will be
   // 'cleaned up' by the Request when it is destroyed, but only then.
   aRequest->IncrementAnimationConsumers();
   return NS_OK;
@@ -108,20 +131,26 @@ nsTreeImageListener::AddCell(PRInt32 aIn
   return NS_OK;
 }
 
 
 void
 nsTreeImageListener::Invalidate()
 {
   if (!mInvalidationSuppressed) {
-    for (InvalidationArea* currArea = mInvalidationArea; currArea; currArea = currArea->GetNext()) {
+    for (InvalidationArea* currArea = mInvalidationArea; currArea;
+         currArea = currArea->GetNext()) {
       // Loop from min to max, invalidating each cell that was listening for this image.
       for (PRInt32 i = currArea->GetMin(); i <= currArea->GetMax(); ++i) {
-        mTree->InvalidateCell(i, currArea->GetCol());
+        if (mTreeFrame) {
+          nsITreeBoxObject* tree = mTreeFrame->GetTreeBoxObject();
+          if (tree) {
+            tree->InvalidateCell(i, currArea->GetCol());
+          }
+        }
       }
     }
   }
 }
 
 nsTreeImageListener::InvalidationArea::InvalidationArea(nsITreeColumn* aCol)
   : mCol(aCol),
     mMin(-1), // min should start out "undefined"
@@ -135,8 +164,15 @@ nsTreeImageListener::InvalidationArea::A
 {
   if (mMin == -1)
     mMin = mMax = aIndex;
   else if (aIndex < mMin)
     mMin = aIndex;
   else if (aIndex > mMax)
     mMax = aIndex;
 }
+
+NS_IMETHODIMP
+nsTreeImageListener::ClearFrame()
+{
+  mTreeFrame = nsnull;
+  return NS_OK;
+}
--- a/layout/xul/base/src/tree/src/nsTreeImageListener.h
+++ b/layout/xul/base/src/tree/src/nsTreeImageListener.h
@@ -39,60 +39,49 @@
 
 #ifndef nsTreeImageListener_h__
 #define nsTreeImageListener_h__
 
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsITreeColumns.h"
 #include "nsStubImageDecoderObserver.h"
-
-class nsITreeBoxObject;
-
-// The interface for our image listener.
-// {90586540-2D50-403e-8DCE-981CAA778444}
-#define NS_ITREEIMAGELISTENER_IID \
-{ 0x90586540, 0x2d50, 0x403e, { 0x8d, 0xce, 0x98, 0x1c, 0xaa, 0x77, 0x84, 0x44 } }
-
-class nsITreeImageListener : public nsISupports
-{
-public:
-  NS_DECLARE_STATIC_IID_ACCESSOR(NS_ITREEIMAGELISTENER_IID)
-
-  NS_IMETHOD AddCell(PRInt32 aIndex, nsITreeColumn* aCol) = 0;
-};
-
-NS_DEFINE_STATIC_IID_ACCESSOR(nsITreeImageListener, NS_ITREEIMAGELISTENER_IID)
+#include "nsTreeBodyFrame.h"
+#include "nsITreeImageListener.h"
 
 // This class handles image load observation.
 class nsTreeImageListener : public nsStubImageDecoderObserver, public nsITreeImageListener
 {
 public:
-  nsTreeImageListener(nsITreeBoxObject* aTree);
+  nsTreeImageListener(nsTreeBodyFrame *aTreeFrame);
   ~nsTreeImageListener();
 
   NS_DECL_ISUPPORTS
   // imgIDecoderObserver (override nsStubImageDecoderObserver)
+  NS_IMETHOD OnStartDecode(imgIRequest *aRequest);
+  NS_IMETHOD OnStopDecode(imgIRequest *aRequest,
+                          nsresult aStatus, const PRUnichar *aStatusArg);
   NS_IMETHOD OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage);
   NS_IMETHOD OnDataAvailable(imgIRequest *aRequest, bool aCurrentFrame,
                              const nsIntRect *aRect);
   // imgIContainerObserver (override nsStubImageDecoderObserver)
   NS_IMETHOD FrameChanged(imgIContainer *aContainer,
                           const nsIntRect *aDirtyRect);
 
   NS_IMETHOD AddCell(PRInt32 aIndex, nsITreeColumn* aCol);
- 
+  NS_IMETHOD ClearFrame();
+
   friend class nsTreeBodyFrame;
 
 protected:
   void UnsuppressInvalidation() { mInvalidationSuppressed = false; }
   void Invalidate();
 
 private:
-  nsITreeBoxObject* mTree;
+  nsTreeBodyFrame* mTreeFrame;
 
   // A guard that prevents us from recursive painting.
   bool mInvalidationSuppressed;
 
   class InvalidationArea {
     public:
       InvalidationArea(nsITreeColumn* aCol);
       ~InvalidationArea() { delete mNext; }