138 lines
3.8 KiB
JavaScript
138 lines
3.8 KiB
JavaScript
waitForExplicitFinish();
|
|
|
|
var pageSource =
|
|
"<html><body>" +
|
|
'<img id="testImg" src="' +
|
|
TESTROOT +
|
|
'big.png">' +
|
|
"</body></html>";
|
|
|
|
var oldDiscardingPref, oldTab, newTab;
|
|
var prefBranch = Services.prefs.getBranch("image.mem.");
|
|
|
|
var gWaitingForDiscard = false;
|
|
var gScriptedObserver;
|
|
var gClonedRequest;
|
|
|
|
function ImageObserver(decodeCallback, discardCallback) {
|
|
this.decodeComplete = function onDecodeComplete() {
|
|
decodeCallback();
|
|
};
|
|
|
|
this.discard = function onDiscard() {
|
|
if (!gWaitingForDiscard) {
|
|
return;
|
|
}
|
|
|
|
this.synchronous = false;
|
|
discardCallback();
|
|
};
|
|
|
|
this.synchronous = true;
|
|
}
|
|
|
|
function currentRequest() {
|
|
let img = gBrowser
|
|
.getBrowserForTab(newTab)
|
|
.contentWindow.document.getElementById("testImg");
|
|
return img.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
|
|
}
|
|
|
|
function isImgDecoded() {
|
|
let request = currentRequest();
|
|
return !!(request.imageStatus & Ci.imgIRequest.STATUS_DECODE_COMPLETE);
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
function runAfterAsyncEvents(aCallback) {
|
|
function handlePostMessage(aEvent) {
|
|
if (aEvent.data == "next") {
|
|
window.removeEventListener("message", handlePostMessage);
|
|
aCallback();
|
|
}
|
|
}
|
|
|
|
window.addEventListener("message", handlePostMessage);
|
|
|
|
// We'll receive the 'message' event after everything else that's currently in
|
|
// the event queue (which is a stronger guarantee than setTimeout, because
|
|
// setTimeout events may be coalesced). This lets us ensure that we run
|
|
// aCallback *after* any asynchronous events are delivered.
|
|
window.postMessage("next", "*");
|
|
}
|
|
|
|
function test() {
|
|
// Enable the discarding pref.
|
|
oldDiscardingPref = prefBranch.getBoolPref("discardable");
|
|
prefBranch.setBoolPref("discardable", true);
|
|
|
|
// Create and focus a new tab.
|
|
oldTab = gBrowser.selectedTab;
|
|
newTab = BrowserTestUtils.addTab(gBrowser, "data:text/html," + pageSource);
|
|
gBrowser.selectedTab = newTab;
|
|
|
|
// Run step2 after the tab loads.
|
|
gBrowser.getBrowserForTab(newTab).addEventListener("pageshow", step2);
|
|
}
|
|
|
|
function step2() {
|
|
// Create the image observer.
|
|
var observer = new ImageObserver(
|
|
() => runAfterAsyncEvents(step3), // DECODE_COMPLETE
|
|
() => runAfterAsyncEvents(step5)
|
|
); // DISCARD
|
|
gScriptedObserver = Cc["@mozilla.org/image/tools;1"]
|
|
.getService(Ci.imgITools)
|
|
.createScriptedObserver(observer);
|
|
|
|
// Clone the current imgIRequest with our new observer.
|
|
var request = currentRequest();
|
|
gClonedRequest = request.clone(gScriptedObserver);
|
|
|
|
// Check that the image is decoded.
|
|
forceDecodeImg();
|
|
|
|
// The DECODE_COMPLETE notification is delivered asynchronously. ImageObserver will
|
|
// eventually call step3.
|
|
}
|
|
|
|
function step3() {
|
|
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;
|
|
|
|
// Allow time to process the tab change.
|
|
runAfterAsyncEvents(step4);
|
|
}
|
|
|
|
function step4() {
|
|
gWaitingForDiscard = true;
|
|
|
|
var os = Services.obs;
|
|
os.notifyObservers(null, "memory-pressure", "heap-minimize");
|
|
|
|
// The DISCARD notification is delivered asynchronously. ImageObserver will
|
|
// eventually call step5. (Or else, sadly, the test will time out.)
|
|
}
|
|
|
|
function step5() {
|
|
ok(true, "Image should be discarded.");
|
|
|
|
// And we're done.
|
|
gBrowser.removeTab(newTab);
|
|
prefBranch.setBoolPref("discardable", oldDiscardingPref);
|
|
|
|
gClonedRequest.cancelAndForgetObserver(0);
|
|
|
|
finish();
|
|
}
|