Bug 1084136 (Part 6) - Don't reset any imgStatusTracker state when discarding. r=tn
authorSeth Fowler <seth@mozilla.com>
Thu, 06 Nov 2014 17:33:59 -0800
changeset 214444 443d2ef1ba033afd84eeb220d126e2d9489b1457
parent 214443 00faf185fa5bd65d678e9c05e56f40d1cc966043
child 214445 70b9dd23c3f972b7ed5534bde0ca651da6c0e4e6
push id51493
push usermfowler@mozilla.com
push dateFri, 07 Nov 2014 02:17:12 +0000
treeherdermozilla-inbound@f245578c4fa4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstn
bugs1084136
milestone36.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 1084136 (Part 6) - Don't reset any imgStatusTracker state when discarding. r=tn
image/src/imgDecoderObserver.h
image/src/imgStatusTracker.cpp
image/src/imgStatusTracker.h
image/test/browser/browser_bug666317.js
--- a/image/src/imgDecoderObserver.h
+++ b/image/src/imgDecoderObserver.h
@@ -105,23 +105,16 @@ public:
    *
    * called at the same time that nsIRequestObserver::onStopRequest would be
    * (used only for observers of imgIRequest objects, which are nsIRequests,
    * not imgIDecoder objects)
    */
   virtual void OnStopRequest(bool aIsLastPart, nsresult aStatus) = 0;
 
   /**
-   * Called when the decoded image data is discarded. This means that the frames
-   * no longer exist in decoded form, and any attempt to access or draw the
-   * image will initiate a new series of progressive decode notifications.
-   */
-  virtual void OnDiscard() = 0;
-
-  /**
    * Called when we are asked to Draw an image that is not locked.
    */
   virtual void OnUnlockedDraw() = 0;
 
   /**
    * Called when an image is realized to be in error state.
    */
   virtual void OnError() = 0;
--- a/image/src/imgStatusTracker.cpp
+++ b/image/src/imgStatusTracker.cpp
@@ -102,24 +102,16 @@ public:
   virtual void OnStopRequest(bool aLastPart, nsresult aStatus) MOZ_OVERRIDE
   {
     LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStopRequest");
     nsRefPtr<imgStatusTracker> tracker = mTracker.get();
     if (!tracker) { return; }
     tracker->RecordStopRequest(aLastPart, aStatus);
   }
 
-  virtual void OnDiscard() MOZ_OVERRIDE
-  {
-    LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnDiscard");
-    nsRefPtr<imgStatusTracker> tracker = mTracker.get();
-    if (!tracker) { return; }
-    tracker->RecordDiscard();
-  }
-
   virtual void OnUnlockedDraw() MOZ_OVERRIDE
   {
     LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnUnlockedDraw");
     nsRefPtr<imgStatusTracker> tracker = mTracker.get();
     if (!tracker) { return; }
     NS_ABORT_IF_FALSE(tracker->HasImage(),
                       "OnUnlockedDraw callback before we've created our image");
     tracker->RecordUnlockedDraw();
@@ -711,32 +703,16 @@ imgStatusTracker::SendStopDecode(imgRequ
                                  nsresult aStatus)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!aProxy->NotificationsDeferred())
     aProxy->OnStopDecode();
 }
 
 void
-imgStatusTracker::RecordDiscard()
-{
-  NS_ABORT_IF_FALSE(mImage,
-                    "RecordDiscard called before we have an Image");
-  // Clear the state bits we no longer deserve.
-  uint32_t stateBitsToClear = FLAG_DECODE_STOPPED;
-  mState &= ~stateBitsToClear;
-
-  // Clear the status bits we no longer deserve.
-  uint32_t statusBitsToClear = imgIRequest::STATUS_DECODE_STARTED |
-                               imgIRequest::STATUS_FRAME_COMPLETE |
-                               imgIRequest::STATUS_DECODE_COMPLETE;
-  mImageStatus &= ~statusBitsToClear;
-}
-
-void
 imgStatusTracker::SendDiscard(imgRequestProxy* aProxy)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!aProxy->NotificationsDeferred())
     aProxy->OnDiscard();
 }
 
 
@@ -924,17 +900,16 @@ imgStatusTracker::OnStopRequest(bool aLa
     FireFailureNotification();
   }
 }
 
 void
 imgStatusTracker::OnDiscard()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  RecordDiscard();
 
   /* notify the kids */
   ProxyArray::ForwardIterator iter(mConsumers);
   while (iter.HasMore()) {
     nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
     if (proxy) {
       SendDiscard(proxy);
     }
--- a/image/src/imgStatusTracker.h
+++ b/image/src/imgStatusTracker.h
@@ -205,17 +205,16 @@ public:
   void RecordStartFrame();
   // No SendStartFrame since it's not observed below us.
   void RecordFrameChanged(const nsIntRect* aDirtyRect);
   void SendFrameChanged(imgRequestProxy* aProxy, const nsIntRect* aDirtyRect);
   void RecordStopFrame();
   void SendStopFrame(imgRequestProxy* aProxy);
   void RecordStopDecode(nsresult statusg);
   void SendStopDecode(imgRequestProxy* aProxy, nsresult aStatus);
-  void RecordDiscard();
   void SendDiscard(imgRequestProxy* aProxy);
   void RecordUnlockedDraw();
   void SendUnlockedDraw(imgRequestProxy* aProxy);
   void RecordImageIsAnimated();
   void SendImageIsAnimated(imgRequestProxy *aProxy);
 
   /* non-virtual sort-of-nsIRequestObserver methods */
   // Functions with prefix Send- are main thread only, since they contain calls
--- a/image/test/browser/browser_bug666317.js
+++ b/image/test/browser/browser_bug666317.js
@@ -5,21 +5,47 @@ let pageSource =
     '<img id="testImg" src="' + TESTROOT + 'big.png">' +
   '</body></html>';
 
 let oldDiscardingPref, oldTab, newTab;
 let prefBranch = Cc["@mozilla.org/preferences-service;1"]
                    .getService(Ci.nsIPrefService)
                    .getBranch('image.mem.');
 
-function isImgDecoded() {
+function ImageDiscardObserver(result) {
+  this.discard = function onDiscard(request)
+  {
+    result.wasDiscarded = true;
+    this.synchronous = false;
+  }
+
+  this.synchronous = true;
+}
+
+function currentRequest() {
   let img = gBrowser.getBrowserForTab(newTab).contentWindow
             .document.getElementById('testImg');
   img.QueryInterface(Ci.nsIImageLoadingContent);
-  let request = img.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
+  return img.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
+}
+
+function attachDiscardObserver(result) {
+  // Create the discard observer.
+  let observer = new ImageDiscardObserver(result);
+  let scriptedObserver = Cc["@mozilla.org/image/tools;1"]
+                           .getService(Ci.imgITools)
+                           .createScriptedObserver(observer);
+
+  // Clone the current imgIRequest with our new observer.
+  let request = currentRequest();
+  return request.clone(scriptedObserver);
+}
+
+function isImgDecoded() {
+  let request = currentRequest();
   return request.imageStatus & Ci.imgIRequest.STATUS_FRAME_COMPLETE ? true : false;
 }
 
 // Ensure that the image is decoded by drawing it to a canvas.
 function forceDecodeImg() {
   let doc = gBrowser.getBrowserForTab(newTab).contentWindow.document;
   let img = doc.getElementById('testImg');
   let canvas = doc.createElement('canvas');
@@ -38,25 +64,30 @@ function test() {
   gBrowser.selectedTab = newTab;
 
   // Run step2 after the tab loads.
   gBrowser.getBrowserForTab(newTab)
           .addEventListener("pageshow", step2 );
 }
 
 function step2() {
+  // Attach a discard listener and create a place to hold the result.
+  var result = { wasDiscarded: false };
+  var clonedRequest = attachDiscardObserver(result);
+
   // Check that the image is decoded.
   forceDecodeImg();
   ok(isImgDecoded(), 'Image should initially be decoded.');
 
   // Focus the old tab, then fire a memory-pressure notification.  This should
   // cause the decoded image in the new tab to be discarded.
   gBrowser.selectedTab = oldTab;
   var os = Cc["@mozilla.org/observer-service;1"]
              .getService(Ci.nsIObserverService);
   os.notifyObservers(null, 'memory-pressure', 'heap-minimize');
-  ok(!isImgDecoded(), 'Image should be discarded.');
+  ok(result.wasDiscarded, 'Image should be discarded.');
 
   // And we're done.
   gBrowser.removeTab(newTab);
   prefBranch.setBoolPref('discardable', oldDiscardingPref);
+  clonedRequest.cancelAndForgetObserver(0);
   finish();
 }