diff options
Diffstat (limited to 'dom/cache/test/mochitest/test_cache_orphaned_body.html')
-rw-r--r-- | dom/cache/test/mochitest/test_cache_orphaned_body.html | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/dom/cache/test/mochitest/test_cache_orphaned_body.html b/dom/cache/test/mochitest/test_cache_orphaned_body.html new file mode 100644 index 000000000..049a97a79 --- /dev/null +++ b/dom/cache/test/mochitest/test_cache_orphaned_body.html @@ -0,0 +1,235 @@ +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<!DOCTYPE HTML> +<html> +<head> + <title>Test Cache with QuotaManager Restart</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="large_url_list.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<script class="testbody" type="text/javascript"> +function setupTestIframe() { + return new Promise(function(resolve) { + var iframe = document.createElement("iframe"); + iframe.src = "empty.html"; + iframe.onload = function() { + window.caches = iframe.contentWindow.caches; + resolve(); + }; + document.body.appendChild(iframe); + }); +} + +function clearStorage() { + return new Promise(function(resolve, reject) { + var qms = SpecialPowers.Services.qms; + var principal = SpecialPowers.wrap(document).nodePrincipal; + var request = qms.clearStoragesForPrincipal(principal); + var cb = SpecialPowers.wrapCallback(resolve); + request.callback = cb; + }); +} + +function storageUsage() { + return new Promise(function(resolve, reject) { + var qms = SpecialPowers.Services.qms; + var principal = SpecialPowers.wrap(document).nodePrincipal; + var cb = SpecialPowers.wrapCallback(function(request) { + var result = request.result; + resolve(result.usage, result.fileUsage); + }); + qms.getUsageForPrincipal(principal, cb); + }); +} + +function groupUsage() { + return new Promise(function(resolve, reject) { + navigator.storage.estimate().then(storageEstimation => { + resolve(storageEstimation.usage, 0); + }); + }); +} + +function workerGroupUsage() { + return new Promise(function(resolve, reject) { + function workerScript() { + navigator.storage.estimate().then(storageEstimation => { + postMessage(storageEstimation.usage); + }); + } + + let url = + URL.createObjectURL(new Blob(["(", workerScript.toSource(), ")()"])); + + let worker = new Worker(url); + worker.onmessage = function (e) { + resolve(e.data, 0); + }; + }); +} + +function resetStorage() { + return new Promise(function(resolve, reject) { + var qms = SpecialPowers.Services.qms; + var request = qms.reset(); + var cb = SpecialPowers.wrapCallback(resolve); + request.callback = cb; + }); +} + +function gc() { + return new Promise(function(resolve, reject) { + SpecialPowers.exactGC(resolve); + }); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPrefEnv({ + "set": [["dom.caches.enabled", true], + ["dom.caches.testing.enabled", true], + ["dom.quotaManager.testing", true], + ["dom.storageManager.enabled", true]], +}, function() { + var name = 'orphanedBodyOwner'; + var cache = null; + var response = null; + var initialUsage = 0; + var fullUsage = 0; + var resetUsage = 0; + var endUsage = 0; + var url = 'test_cache_add.js'; + + // start from a fresh origin directory so other tests do not influence our + // results + setupTestIframe().then(function() { + return clearStorage(); + }).then(function() { + return storageUsage(); + }).then(function(usage) { + is(0, usage, 'disk usage should be zero to start'); + }) + + // Initialize and populate an initial cache to get the base sqlite pages + // and directory structure allocated. + .then(function() { + return caches.open(name); + }).then(function(c) { + return c.add(url); + }).then(function() { + return gc(); + }).then(function() { + return caches.delete(name); + }).then(function(deleted) { + ok(deleted, 'cache should be deleted'); + + // This is a bit superfluous, but its necessary to make sure the Cache is + // fully deleted before we proceed. The deletion actually takes place in + // two async steps. We don't want to resetStorage() until the second step + // has taken place. This extra Cache operation ensure that all the + // runnables have been flushed through the threads, etc. + return caches.has(name); + }) + + // Now measure initial disk usage + .then(function() { + return resetStorage(); + }).then(function() { + return storageUsage(); + }).then(function(usage) { + initialUsage = usage; + }) + + // Now re-populate the Cache object + .then(function() { + return caches.open(name); + }).then(function(c) { + cache = c; + return cache.add(url); + }) + + // Get a reference to the body we've stored in the Cache. + .then(function() { + return cache.match(url); + }).then(function(r) { + response = r; + return cache.delete(url); + }).then(function(result) { + ok(result, "Cache entry should be deleted"); + }) + + // Reset the quota dir while the cache entry is deleted, but still referenced + // from the DOM. This forces the body to be orphaned. + .then(function() { + return resetStorage(); + }).then(function() { + return storageUsage(); + }).then(function(usage) { + fullUsage = usage; + ok(fullUsage > initialUsage, 'disk usage should have grown'); + }) + + // Test groupUsage() + .then(function() { + return resetStorage(); + }).then(function() { + return groupUsage(); + }).then(function(usage) { + fullUsage = usage; + ok(fullUsage > initialUsage, 'disk group usage should have grown'); + }) + + // Test workerGroupUsage() + .then(function() { + return resetStorage(); + }).then(function() { + return workerGroupUsage(); + }).then(function(usage) { + fullUsage = usage; + ok(fullUsage > initialUsage, 'disk group usage on worker should have grown'); + }) + + // Now perform a new Cache operation that will reopen the origin. This + // should clean up the orphaned body. + .then(function() { + return caches.match(url); + }).then(function(r) { + ok(!r, 'response should not exist in storage'); + }) + + // Finally, verify orphaned data was cleaned up by re-checking the disk + // usage. Reset the storage first to ensure any WAL transaction files + // are flushed before measuring the usage. + .then(function() { + return resetStorage(); + }).then(function() { + return storageUsage(); + }).then(function(usage) { + endUsage = usage; + dump("### ### initial:" + initialUsage + ", full:" + fullUsage + + ", end:" + endUsage + "\n"); + ok(endUsage < fullUsage, 'disk usage should have shrank'); + is(endUsage, initialUsage, 'disk usage should return to original'); + }) + + // Verify that the stale, orphaned response cannot be put back into + // the cache. + .then(function() { + ok(!response.bodyUsed, 'response body should not be considered used'); + return cache.put(url, response).then(function() { + ok(false, 'Should not be able to store stale orphaned body.'); + }).catch(function(e) { + is(e.name, 'TypeError', 'storing a stale orphaned body should throw TypeError'); + }); + }).then(function() { + ok(response.bodyUsed, 'attempting to store response should mark body used'); + }) + + .then(function() { + SimpleTest.finish(); + }); +}); +</script> +</body> +</html> |