Bug 1354913: Fix nsDeckFrame, nsImageBoxFrame and nsImageFrame so that nsDeckFrame does not tick the refresh driver when its child images are animated and hidden. r=aosmond
authorWill Hawkins <whawkins@mozilla.com>
Mon, 25 Mar 2019 19:11:22 +0000
changeset 466013 f5dd833c3b60b3365d2c49d930748e33a22651b6
parent 466012 d121ec2b3d8a395b6cfb635b3f3bea3ced1883f0
child 466014 7b0a87b6f9402fd9377da61aa839541e3ef192f1
push id112550
push userrgurzau@mozilla.com
push dateTue, 26 Mar 2019 09:57:15 +0000
treeherdermozilla-inbound@b8be99473610 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaosmond
bugs1354913
milestone68.0a1
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 1354913: Fix nsDeckFrame, nsImageBoxFrame and nsImageFrame so that nsDeckFrame does not tick the refresh driver when its child images are animated and hidden. r=aosmond Differential Revision: https://phabricator.services.mozilla.com/D24598
layout/generic/nsImageFrame.cpp
layout/generic/nsImageFrame.h
layout/xul/nsDeckFrame.cpp
layout/xul/nsDeckFrame.h
layout/xul/nsImageBoxFrame.cpp
layout/xul/nsImageBoxFrame.h
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -2543,16 +2543,48 @@ void nsImageFrame::IconLoad::GetPrefs() 
 
   mPrefShowPlaceholders =
       Preferences::GetBool("browser.display.show_image_placeholders", true);
 
   mPrefShowLoadingPlaceholder = Preferences::GetBool(
       "browser.display.show_loading_image_placeholder", true);
 }
 
+nsresult nsImageFrame::RestartAnimation() {
+  nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest();
+  /*
+   * We cannot count on mContentURLRequestRegistered to make
+   * the deregistration work. So, we are going to force it.
+   */
+  bool deregister = true;
+
+  if (currentRequest && !mContentURLRequestRegistered) {
+    nsLayoutUtils::RegisterImageRequestIfAnimated(PresContext(), currentRequest,
+                                                  &deregister);
+    return currentRequest->StartDecoding(imgIContainer::FLAG_NONE);
+  }
+  return NS_OK;
+}
+
+nsresult nsImageFrame::StopAnimation() {
+  nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest();
+  /*
+   * We cannot count on mContentURLRequestRegistered to make
+   * the deregistration work. So, we are going to force it.
+   */
+  bool deregister = true;
+
+  if (currentRequest) {
+    nsLayoutUtils::DeregisterImageRequest(PresContext(), currentRequest,
+                                          &deregister);
+    return currentRequest->CancelAndForgetObserver(NS_BINDING_ABORTED);
+  }
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 nsImageFrame::IconLoad::Notify(imgIRequest* aRequest, int32_t aType,
                                const nsIntRect* aData) {
   MOZ_ASSERT(aRequest);
 
   if (aType != imgINotificationObserver::LOAD_COMPLETE &&
       aType != imgINotificationObserver::FRAME_UPDATE) {
     return NS_OK;
--- a/layout/generic/nsImageFrame.h
+++ b/layout/generic/nsImageFrame.h
@@ -129,16 +129,19 @@ class nsImageFrame : public nsAtomicCont
   static void ReleaseGlobals() {
     if (gIconLoad) {
       gIconLoad->Shutdown();
       gIconLoad = nullptr;
     }
     NS_IF_RELEASE(sIOService);
   }
 
+  virtual nsresult RestartAnimation();
+  virtual nsresult StopAnimation();
+
   already_AddRefed<imgIRequest> GetCurrentRequest() const;
   nsresult Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData);
 
   /**
    * Function to test whether aContent, which has aComputedStyle as its style,
    * should get an image frame.  Note that this method is only used by the
    * frame constructor; it's only here because it uses gIconLoad for now.
    */
--- a/layout/xul/nsDeckFrame.cpp
+++ b/layout/xul/nsDeckFrame.cpp
@@ -23,16 +23,18 @@
 #include "nsCSSRendering.h"
 #include "nsViewManager.h"
 #include "nsBoxLayoutState.h"
 #include "nsStackLayout.h"
 #include "nsDisplayList.h"
 #include "nsContainerFrame.h"
 #include "nsContentUtils.h"
 #include "nsXULPopupManager.h"
+#include "nsImageBoxFrame.h"
+#include "nsImageFrame.h"
 
 #ifdef ACCESSIBILITY
 #  include "nsAccessibilityService.h"
 #endif
 
 using namespace mozilla;
 
 nsIFrame* NS_NewDeckFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle) {
@@ -67,35 +69,41 @@ nsresult nsDeckFrame::AttributeChanged(i
 
 void nsDeckFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
                        nsIFrame* aPrevInFlow) {
   nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
 
   mIndex = GetSelectedIndex();
 }
 
+void nsDeckFrame::ShowBox(nsIFrame* aBox) { Animate(aBox, true); }
+
 void nsDeckFrame::HideBox(nsIFrame* aBox) {
   nsIPresShell::ClearMouseCapture(aBox);
+  Animate(aBox, false);
 }
 
 void nsDeckFrame::IndexChanged() {
   // did the index change?
   int32_t index = GetSelectedIndex();
+
   if (index == mIndex) return;
 
   // redraw
   InvalidateFrame();
 
   // hide the currently showing box
   nsIFrame* currentBox = GetSelectedBox();
   if (currentBox)  // only hide if it exists
     HideBox(currentBox);
 
   mIndex = index;
 
+  ShowBox(GetSelectedBox());
+
 #ifdef ACCESSIBILITY
   nsAccessibilityService* accService = GetAccService();
   if (accService) {
     accService->DeckPanelSwitched(PresContext()->GetPresShell(), mContent,
                                   currentBox, GetSelectedBox());
   }
 #endif
 
@@ -166,16 +174,44 @@ void nsDeckFrame::BuildDisplayListForChi
   if (!box) return;
 
   // Putting the child in the background list. This is a little weird but
   // it matches what we were doing before.
   nsDisplayListSet set(aLists, aLists.BlockBorderBackgrounds());
   BuildDisplayListForChild(aBuilder, box, set);
 }
 
+void nsDeckFrame::Animate(nsIFrame* aParentBox, bool start) {
+  if (!aParentBox) return;
+
+  nsImageBoxFrame* imgBoxFrame = do_QueryFrame(aParentBox);
+  nsImageFrame* imgFrame = do_QueryFrame(aParentBox);
+
+  if (imgBoxFrame) {
+    if (start)
+      imgBoxFrame->RestartAnimation();
+    else
+      imgBoxFrame->StopAnimation();
+  }
+
+  if (imgFrame) {
+    if (start)
+      imgFrame->RestartAnimation();
+    else
+      imgFrame->StopAnimation();
+  }
+
+  for (nsIFrame::ChildListIterator childLists(aParentBox); !childLists.IsDone();
+       childLists.Next()) {
+    for (nsIFrame* child : childLists.CurrentList()) {
+      Animate(child, start);
+    }
+  }
+}
+
 NS_IMETHODIMP
 nsDeckFrame::DoXULLayout(nsBoxLayoutState& aState) {
   // Make sure we tweak the state so it does not resize our children.
   // We will do that.
   uint32_t oldFlags = aState.LayoutFlags();
   aState.SetLayoutFlags(NS_FRAME_NO_SIZE_VIEW | NS_FRAME_NO_VISIBILITY);
 
   // do a normal layout
--- a/layout/xul/nsDeckFrame.h
+++ b/layout/xul/nsDeckFrame.h
@@ -51,15 +51,18 @@ class nsDeckFrame final : public nsBoxFr
   explicit nsDeckFrame(ComputedStyle* aStyle, nsPresContext* aPresContext);
 
   nsIFrame* GetSelectedBox();
 
  protected:
   void IndexChanged();
   int32_t GetSelectedIndex();
   void HideBox(nsIFrame* aBox);
+  void ShowBox(nsIFrame* aBox);
 
  private:
   int32_t mIndex;
 
+  void Animate(nsIFrame*, bool);
+
 };  // class nsDeckFrame
 
 #endif
--- a/layout/xul/nsImageBoxFrame.cpp
+++ b/layout/xul/nsImageBoxFrame.cpp
@@ -119,16 +119,19 @@ static void FireImageDOMEvent(nsIContent
 //
 // Creates a new image frame and returns it
 //
 nsIFrame* NS_NewImageBoxFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle) {
   return new (aPresShell) nsImageBoxFrame(aStyle, aPresShell->GetPresContext());
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsImageBoxFrame)
+NS_QUERYFRAME_HEAD(nsImageBoxFrame)
+  NS_QUERYFRAME_ENTRY(nsImageBoxFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsLeafBoxFrame)
 
 nsresult nsImageBoxFrame::AttributeChanged(int32_t aNameSpaceID,
                                            nsAtom* aAttribute,
                                            int32_t aModType) {
   nsresult rv =
       nsLeafBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
 
   if (aAttribute == nsGkAtoms::src) {
@@ -190,16 +193,26 @@ void nsImageBoxFrame::Init(nsIContent* a
   mSuppressStyleCheck = true;
   nsLeafBoxFrame::Init(aContent, aParent, aPrevInFlow);
   mSuppressStyleCheck = false;
 
   UpdateLoadFlags();
   UpdateImage();
 }
 
+void nsImageBoxFrame::RestartAnimation() { UpdateImage(); }
+
+void nsImageBoxFrame::StopAnimation() {
+  if (mImageRequest && mRequestRegistered) {
+    nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest,
+                                          &mRequestRegistered);
+    mImageRequest->CancelAndForgetObserver(NS_BINDING_ABORTED);
+  }
+}
+
 void nsImageBoxFrame::UpdateImage() {
   nsPresContext* presContext = PresContext();
 
   RefPtr<imgRequestProxy> oldImageRequest = mImageRequest;
 
   if (mImageRequest) {
     nsLayoutUtils::DeregisterImageRequest(presContext, mImageRequest,
                                           &mRequestRegistered);
--- a/layout/xul/nsImageBoxFrame.h
+++ b/layout/xul/nsImageBoxFrame.h
@@ -36,16 +36,17 @@ class nsImageBoxListener final : public 
 class nsImageBoxFrame final : public nsLeafBoxFrame {
  public:
   typedef mozilla::image::ImgDrawResult ImgDrawResult;
   typedef mozilla::layers::ImageContainer ImageContainer;
   typedef mozilla::layers::LayerManager LayerManager;
 
   friend class nsDisplayXULImage;
   NS_DECL_FRAMEARENA_HELPERS(nsImageBoxFrame)
+  NS_DECL_QUERYFRAME
 
   virtual nsSize GetXULPrefSize(nsBoxLayoutState& aBoxLayoutState) override;
   virtual nsSize GetXULMinSize(nsBoxLayoutState& aBoxLayoutState) override;
   virtual nscoord GetXULBoxAscent(nsBoxLayoutState& aBoxLayoutState) override;
   virtual void MarkIntrinsicISizesDirty() override;
 
   nsresult Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData);
 
@@ -75,16 +76,19 @@ class nsImageBoxFrame final : public nsL
   void UpdateImage();
 
   /**
    * Update mLoadFlags from content attributes. Does not attempt to reload the
    * image using the new load flags.
    */
   void UpdateLoadFlags();
 
+  void RestartAnimation();
+  void StopAnimation();
+
   virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
                                 const nsDisplayListSet& aLists) override;
 
   virtual ~nsImageBoxFrame();
 
   already_AddRefed<imgIContainer> GetImageContainerForPainting(
       const nsPoint& aPt, ImgDrawResult& aDrawResult,
       Maybe<nsPoint>& aAnchorPoint, nsRect& aDest);