test_cache_orphaned_body.html (6875B)
1 <!-- Any copyright is dedicated to the Public Domain. 2 - http://creativecommons.org/publicdomain/zero/1.0/ --> 3 <!DOCTYPE HTML> 4 <html> 5 <head> 6 <title>Test Cache with QuotaManager Restart</title> 7 <script src="/tests/SimpleTest/SimpleTest.js"></script> 8 <script type="text/javascript" src="large_url_list.js"></script> 9 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> 10 </head> 11 <body> 12 <script class="testbody" type="text/javascript"> 13 function setupTestIframe() { 14 return new Promise(function(resolve) { 15 var iframe = document.createElement("iframe"); 16 iframe.src = "empty.html"; 17 iframe.onload = function() { 18 window.caches = iframe.contentWindow.caches; 19 resolve(); 20 }; 21 document.body.appendChild(iframe); 22 }); 23 } 24 25 function clearStorage() { 26 return new Promise(function(resolve) { 27 var qms = SpecialPowers.Services.qms; 28 var principal = SpecialPowers.wrap(document).nodePrincipal; 29 var request = qms.clearStoragesForPrincipal(principal); 30 var cb = SpecialPowers.wrapCallback(resolve); 31 request.callback = cb; 32 }); 33 } 34 35 function storageUsage() { 36 return new Promise(function(resolve) { 37 var qms = SpecialPowers.Services.qms; 38 var principal = SpecialPowers.wrap(document).nodePrincipal; 39 var cb = SpecialPowers.wrapCallback(function(request) { 40 var result = request.result; 41 resolve(result.usage); 42 }); 43 qms.getUsageForPrincipal(principal, cb); 44 }); 45 } 46 47 function groupUsage() { 48 return new Promise(function(resolve) { 49 navigator.storage.estimate().then(storageEstimation => { 50 resolve(storageEstimation.usage, 0); 51 }); 52 }); 53 } 54 55 function workerGroupUsage() { 56 return new Promise(function(resolve) { 57 function workerScript() { 58 navigator.storage.estimate().then(storageEstimation => { 59 postMessage(storageEstimation.usage); 60 }); 61 } 62 63 let url = 64 URL.createObjectURL(new Blob(["(", workerScript.toString(), ")()"])); 65 66 let worker = new Worker(url); 67 worker.onmessage = function(e) { 68 resolve(e.data, 0); 69 }; 70 }); 71 } 72 73 function resetStorage() { 74 return new Promise(function(resolve) { 75 var qms = SpecialPowers.Services.qms; 76 var principal = SpecialPowers.wrap(document).nodePrincipal; 77 var request = qms.resetStoragesForPrincipal(principal); 78 var cb = SpecialPowers.wrapCallback(resolve); 79 request.callback = cb; 80 }); 81 } 82 83 function gc() { 84 return new Promise(function(resolve) { 85 SpecialPowers.exactGC(resolve); 86 }); 87 } 88 89 SimpleTest.waitForExplicitFinish(); 90 SpecialPowers.pushPrefEnv({ 91 "set": [["dom.caches.testing.enabled", true], 92 ["dom.quotaManager.testing", true]] 93 }, function() { 94 var name = "orphanedBodyOwner"; 95 var cache = null; 96 var response = null; 97 var initialUsage = 0; 98 var fullUsage = 0; 99 var endUsage = 0; 100 var url = "test_cache_add.js"; 101 102 // start from a fresh origin directory so other tests do not influence our 103 // results 104 setupTestIframe().then(function() { 105 return clearStorage(); 106 }).then(function() { 107 return storageUsage(); 108 }).then(function(usage) { 109 is(0, usage, "disk usage should be zero to start"); 110 }) 111 112 // Initialize and populate an initial cache to get the base sqlite pages 113 // and directory structure allocated. 114 .then(function() { 115 return caches.open(name); 116 }).then(function(c) { 117 return c.add(url); 118 }).then(function() { 119 return gc(); 120 }).then(function() { 121 return caches.delete(name); 122 }).then(function(deleted) { 123 ok(deleted, "cache should be deleted"); 124 125 // This is a bit superfluous, but its necessary to make sure the Cache is 126 // fully deleted before we proceed. The deletion actually takes place in 127 // two async steps. We don't want to resetStorage() until the second step 128 // has taken place. This extra Cache operation ensure that all the 129 // runnables have been flushed through the threads, etc. 130 return caches.has(name); 131 }) 132 133 // Now measure initial disk usage 134 .then(function() { 135 return resetStorage(); 136 }).then(function() { 137 return storageUsage(); 138 }).then(function(usage) { 139 initialUsage = usage; 140 }) 141 142 // Now re-populate the Cache object 143 .then(function() { 144 return caches.open(name); 145 }).then(function(c) { 146 cache = c; 147 return cache.add(url); 148 }) 149 150 // Get a reference to the body we've stored in the Cache. 151 .then(function() { 152 return cache.match(url); 153 }).then(function(r) { 154 response = r; 155 return cache.delete(url); 156 }).then(function(result) { 157 ok(result, "Cache entry should be deleted"); 158 }) 159 160 // Reset the quota dir while the cache entry is deleted, but still referenced 161 // from the DOM. This forces the body to be orphaned. 162 .then(function() { 163 return resetStorage(); 164 }).then(function() { 165 return storageUsage(); 166 }).then(function(usage) { 167 fullUsage = usage; 168 ok(fullUsage > initialUsage, "disk usage should have grown"); 169 }) 170 171 // Test groupUsage() 172 .then(function() { 173 return resetStorage(); 174 }).then(function() { 175 return groupUsage(); 176 }).then(function(usage) { 177 fullUsage = usage; 178 ok(fullUsage > initialUsage, "disk group usage should have grown"); 179 }) 180 181 // Test workerGroupUsage() 182 .then(function() { 183 return resetStorage(); 184 }).then(function() { 185 return workerGroupUsage(); 186 }).then(function(usage) { 187 fullUsage = usage; 188 ok(fullUsage > initialUsage, "disk group usage on worker should have grown"); 189 }) 190 191 // Now perform a new Cache operation that will reopen the origin. This 192 // should clean up the orphaned body. 193 .then(function() { 194 return caches.match(url); 195 }).then(function(r) { 196 ok(!r, "response should not exist in storage"); 197 }) 198 199 // Finally, verify orphaned data was cleaned up by re-checking the disk 200 // usage. Reset the storage first to ensure any WAL transaction files 201 // are flushed before measuring the usage. 202 .then(function() { 203 return resetStorage(); 204 }).then(function() { 205 return storageUsage(); 206 }).then(function(usage) { 207 endUsage = usage; 208 dump("### ### initial:" + initialUsage + ", full:" + fullUsage + 209 ", end:" + endUsage + "\n"); 210 ok(endUsage < fullUsage, "disk usage should have shrank"); 211 is(endUsage, initialUsage, "disk usage should return to original"); 212 }) 213 214 // Verify that the stale, orphaned response cannot be put back into 215 // the cache. 216 .then(function() { 217 ok(!response.bodyUsed, "response body should not be considered used"); 218 return cache.put(url, response).then(function() { 219 ok(false, "Should not be able to store stale orphaned body."); 220 }).catch(function(e) { 221 is(e.name, "TypeError", "storing a stale orphaned body should throw TypeError"); 222 }); 223 }).then(function() { 224 ok(response.bodyUsed, "attempting to store response should mark body used"); 225 }) 226 227 .then(function() { 228 SimpleTest.finish(); 229 }); 230 }); 231 </script> 232 </body> 233 </html>