browser_bug666317.js (3840B)
1 waitForExplicitFinish(); 2 3 var pageSource = 4 "<html><body>" + 5 '<img id="testImg" src="' + 6 TESTROOT + 7 'big.png">' + 8 "</body></html>"; 9 10 var oldDiscardingPref, oldTab, newTab; 11 var prefBranch = Services.prefs.getBranch("image.mem."); 12 13 var gWaitingForDiscard = false; 14 var gScriptedObserver; 15 var gClonedRequest; 16 17 function ImageObserver(decodeCallback, discardCallback) { 18 this.decodeComplete = function onDecodeComplete() { 19 decodeCallback(); 20 }; 21 22 this.discard = function onDiscard() { 23 if (!gWaitingForDiscard) { 24 return; 25 } 26 27 this.synchronous = false; 28 discardCallback(); 29 }; 30 31 this.synchronous = true; 32 } 33 34 function currentRequest() { 35 let img = gBrowser 36 .getBrowserForTab(newTab) 37 .contentWindow.document.getElementById("testImg"); 38 return img.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST); 39 } 40 41 function isImgDecoded() { 42 let request = currentRequest(); 43 return !!(request.imageStatus & Ci.imgIRequest.STATUS_DECODE_COMPLETE); 44 } 45 46 // Ensure that the image is decoded by drawing it to a canvas. 47 function forceDecodeImg() { 48 let doc = gBrowser.getBrowserForTab(newTab).contentWindow.document; 49 let img = doc.getElementById("testImg"); 50 let canvas = doc.createElement("canvas"); 51 let ctx = canvas.getContext("2d"); 52 ctx.drawImage(img, 0, 0); 53 } 54 55 function runAfterAsyncEvents(aCallback) { 56 function handlePostMessage(aEvent) { 57 if (aEvent.data == "next") { 58 window.removeEventListener("message", handlePostMessage); 59 aCallback(); 60 } 61 } 62 63 window.addEventListener("message", handlePostMessage); 64 65 // We'll receive the 'message' event after everything else that's currently in 66 // the event queue (which is a stronger guarantee than setTimeout, because 67 // setTimeout events may be coalesced). This lets us ensure that we run 68 // aCallback *after* any asynchronous events are delivered. 69 window.postMessage("next", "*"); 70 } 71 72 function test() { 73 // Enable the discarding pref. 74 oldDiscardingPref = prefBranch.getBoolPref("discardable"); 75 prefBranch.setBoolPref("discardable", true); 76 77 // Create and focus a new tab. 78 oldTab = gBrowser.selectedTab; 79 newTab = BrowserTestUtils.addTab(gBrowser, "data:text/html," + pageSource); 80 gBrowser.selectedTab = newTab; 81 82 // Run step2 after the tab loads. 83 gBrowser.getBrowserForTab(newTab).addEventListener("pageshow", step2); 84 } 85 86 function step2() { 87 // Create the image observer. 88 var observer = new ImageObserver( 89 () => runAfterAsyncEvents(step3), // DECODE_COMPLETE 90 () => runAfterAsyncEvents(step5) 91 ); // DISCARD 92 gScriptedObserver = Cc["@mozilla.org/image/tools;1"] 93 .getService(Ci.imgITools) 94 .createScriptedObserver(observer); 95 96 // Clone the current imgIRequest with our new observer. 97 var request = currentRequest(); 98 gClonedRequest = request.clone(gScriptedObserver); 99 100 // Check that the image is decoded. 101 forceDecodeImg(); 102 103 // The DECODE_COMPLETE notification is delivered asynchronously. ImageObserver will 104 // eventually call step3. 105 } 106 107 function step3() { 108 ok(isImgDecoded(), "Image should initially be decoded."); 109 110 // Focus the old tab, then fire a memory-pressure notification. This should 111 // cause the decoded image in the new tab to be discarded. 112 gBrowser.selectedTab = oldTab; 113 114 // Allow time to process the tab change. 115 runAfterAsyncEvents(step4); 116 } 117 118 function step4() { 119 gWaitingForDiscard = true; 120 121 var os = Services.obs; 122 os.notifyObservers(null, "memory-pressure", "heap-minimize"); 123 124 // The DISCARD notification is delivered asynchronously. ImageObserver will 125 // eventually call step5. (Or else, sadly, the test will time out.) 126 } 127 128 function step5() { 129 ok(true, "Image should be discarded."); 130 131 // And we're done. 132 gBrowser.removeTab(newTab); 133 prefBranch.setBoolPref("discardable", oldDiscardingPref); 134 135 gClonedRequest.cancelAndForgetObserver(0); 136 137 finish(); 138 }