Bug 948194 - Fix races related to CPOWs and tab switching. Original patch by Alexandru Tifrea. r=seth
☠☠ backed out by df5c7392ecbf ☠ ☠
authorBlake Kaplan <mrbkap@gmail.com>
Wed, 26 Nov 2014 14:29:52 -0800
changeset 217650 01093b5a774b84f2e7342a8dccaf6031e9c43ec2
parent 217649 b8a4e5137c55b7aea3182da15b98bc8ceee47b3d
child 217651 21a338c8ce9ecceff4cb3ce4f4b5f8253eb24643
push id52353
push usermrbkap@mozilla.com
push dateWed, 26 Nov 2014 22:30:14 +0000
treeherdermozilla-inbound@01093b5a774b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersseth
bugs948194
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 948194 - Fix races related to CPOWs and tab switching. Original patch by Alexandru Tifrea. r=seth
image/test/browser/browser.ini
image/test/browser/browser_bug666317.js
--- a/image/test/browser/browser.ini
+++ b/image/test/browser/browser.ini
@@ -3,11 +3,10 @@ support-files =
   animated.gif
   animated2.gif
   big.png
   head.js
   image.html
   imageX2.html
 
 [browser_bug666317.js]
-skip-if = e10s # Bug 948194 - Decoded Images seem to not be discarded on memory-pressure notification with e10s enabled
 [browser_image.js]
 skip-if = e10s || !debug # Test is only to run on debug builds
--- a/image/test/browser/browser_bug666317.js
+++ b/image/test/browser/browser_bug666317.js
@@ -1,61 +1,83 @@
+"use strict";
+
 waitForExplicitFinish();
 
 let pageSource =
-  '<html><body>' +
+  '<html><meta charset=UTF-8><body>' +
     '<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 ImageDiscardObserver(result) {
-  this.discard = function onDiscard(request)
-  {
-    result.wasDiscarded = true;
-    this.synchronous = false;
+function imgDiscardingFrameScript() {
+  const Cc = Components.classes;
+  const Ci = Components.interfaces;
+
+  function ImageDiscardObserver(result) {
+    this.discard = function onDiscard(request) {
+      result.wasDiscarded = true;
+    }
+  }
+
+  function currentRequest() {
+    let img = content.document.getElementById('testImg');
+    img.QueryInterface(Ci.nsIImageLoadingContent);
+    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), scriptedObserver ];
   }
 
-  this.synchronous = true;
-}
+  // Attach a discard listener and create a place to hold the result.
+  var result = { wasDiscarded: false };
+  var scriptedObserver;
+  var clonedRequest;
 
-function currentRequest() {
-  let img = gBrowser.getBrowserForTab(newTab).contentWindow
-            .document.getElementById('testImg');
-  img.QueryInterface(Ci.nsIImageLoadingContent);
-  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);
+  addMessageListener("test666317:testPart1", function(message) {
+    // Ensure that the image is decoded by drawing it to a canvas.
+    let doc = content.document;
+    let img = doc.getElementById('testImg');
+    let canvas = doc.createElement('canvas');
+    let ctx = canvas.getContext('2d');
+    ctx.drawImage(img, 0, 0);
 
-  // Clone the current imgIRequest with our new observer.
-  let request = currentRequest();
-  return request.clone(scriptedObserver);
-}
+    // Verify that we decoded the image.
+    // Note: We grab a reference to the scripted observer because the image
+    // holds a weak ref to it. If we don't hold a strong reference, we might
+    // end up using a deleted pointer.
+    [ clonedRequest, scriptedObserver ] = attachDiscardObserver(result)
+    let decoded = clonedRequest.imageStatus & Ci.imgIRequest.STATUS_FRAME_COMPLETE ? true : false;
 
-function isImgDecoded() {
-  let request = currentRequest();
-  return request.imageStatus & Ci.imgIRequest.STATUS_FRAME_COMPLETE ? true : false;
-}
+    message.target.sendAsyncMessage("test666317:testPart1:Answer",
+                                    { decoded });
+  });
+
+  addMessageListener("test666317:wasImgDiscarded", function(message) {
+    let discarded = result.wasDiscarded;
 
-// 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');
-  let ctx = canvas.getContext('2d');
-  ctx.drawImage(img, 0, 0);
+    // NOTE: We know that this is the last test.
+    clonedRequest.cancelAndForgetObserver(0);
+    scriptedObserver = null;
+    message.target.sendAsyncMessage("test666317:wasImgDiscarded:Answer",
+                                    { discarded });
+  });
 }
 
 function test() {
   // Enable the discarding pref.
   oldDiscardingPref = prefBranch.getBoolPref('discardable');
   prefBranch.setBoolPref('discardable', true);
 
   // Create and focus a new tab.
@@ -64,30 +86,43 @@ 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);
+  let mm = gBrowser.getBrowserForTab(newTab).QueryInterface(Ci.nsIFrameLoaderOwner)
+           .frameLoader.messageManager;
+  mm.loadFrameScript("data:,(" + escape(imgDiscardingFrameScript.toString()) + ")();", false);
 
   // Check that the image is decoded.
-  forceDecodeImg();
-  ok(isImgDecoded(), 'Image should initially be decoded.');
+  mm.addMessageListener("test666317:testPart1:Answer", function(message) {
+    let decoded = message.data.decoded;
+    ok(decoded, '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;
+    waitForFocus(() => {
+      var os = Cc["@mozilla.org/observer-service;1"]
+                 .getService(Ci.nsIObserverService);
+
+      os.notifyObservers(null, 'memory-pressure', 'heap-minimize');
 
-  // 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(result.wasDiscarded, 'Image should be discarded.');
+      // Now ask if it was.
+      mm.sendAsyncMessage("test666317:wasImgDiscarded");
+    }, oldTab.contentWindow);
+  });
+
+  mm.addMessageListener("test666317:wasImgDiscarded:Answer", function(message) {
+    let discarded = message.data.discarded;
+    ok(discarded, 'Image should be discarded.');
 
-  // And we're done.
-  gBrowser.removeTab(newTab);
-  prefBranch.setBoolPref('discardable', oldDiscardingPref);
-  clonedRequest.cancelAndForgetObserver(0);
-  finish();
+    // And we're done.
+    gBrowser.removeTab(newTab);
+    prefBranch.setBoolPref('discardable', oldDiscardingPref);
+    finish();
+  });
+
+  mm.sendAsyncMessage("test666317:testPart1");
 }