/** * Test for the "CacheEntryId" under several cases. */ "use strict"; const { HttpServer } = ChromeUtils.importESModule( "resource://testing-common/httpd.sys.mjs" ); ChromeUtils.defineLazyGetter(this, "URL", function () { return "http://localhost:" + httpServer.identity.primaryPort + "/content"; }); var httpServer = null; const responseContent = "response body"; const responseContent2 = "response body 2"; const altContent = "!@#$%^&*()"; const altContentType = "text/binary"; function isParentProcess() { let appInfo = Cc["@mozilla.org/xre/app-info;1"]; return ( !appInfo || Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT ); } var handlers = [ (m, r) => { r.bodyOutputStream.write(responseContent, responseContent.length); }, (m, r) => { r.setStatusLine(m.httpVersion, 304, "Not Modified"); }, (m, r) => { r.setStatusLine(m.httpVersion, 304, "Not Modified"); }, (m, r) => { r.setStatusLine(m.httpVersion, 304, "Not Modified"); }, (m, r) => { r.setStatusLine(m.httpVersion, 304, "Not Modified"); }, (m, r) => { r.bodyOutputStream.write(responseContent2, responseContent2.length); }, (m, r) => { r.setStatusLine(m.httpVersion, 304, "Not Modified"); }, ]; function contentHandler(metadata, response) { response.setHeader("Content-Type", "text/plain"); response.setHeader("Cache-Control", "no-cache"); var handler = handlers.shift(); if (handler) { handler(metadata, response); return; } Assert.ok(false, "Should not reach here."); } function fetch(preferredDataType = null) { return new Promise(resolve => { var chan = NetUtil.newChannel({ uri: URL, loadUsingSystemPrincipal: true }); if (preferredDataType) { var cc = chan.QueryInterface(Ci.nsICacheInfoChannel); cc.preferAlternativeDataType( altContentType, "", Ci.nsICacheInfoChannel.ASYNC ); } chan.asyncOpen( new ChannelListener((request, buffer, ctx, isFromCache, cacheEntryId) => { resolve({ request, buffer, isFromCache, cacheEntryId }); }, null) ); }); } function check( response, content, preferredDataType, isFromCache, cacheEntryIdChecker ) { var cc = response.request.QueryInterface(Ci.nsICacheInfoChannel); Assert.equal(response.buffer, content); Assert.equal(cc.alternativeDataType, preferredDataType); Assert.equal(response.isFromCache, isFromCache); Assert.ok(!cacheEntryIdChecker || cacheEntryIdChecker(response.cacheEntryId)); return response; } function writeAltData(request) { var cc = request.QueryInterface(Ci.nsICacheInfoChannel); var os = cc.openAlternativeOutputStream(altContentType, altContent.length); os.write(altContent, altContent.length); os.close(); gc(); // We need to do a GC pass to ensure the cache entry has been freed. return new Promise(resolve => { if (isParentProcess()) { Services.cache2.QueryInterface(Ci.nsICacheTesting).flush(resolve); } else { do_send_remote_message("flush"); do_await_remote_message("flushed").then(resolve); } }); } function run_test() { do_get_profile(); httpServer = new HttpServer(); httpServer.registerPathHandler("/content", contentHandler); httpServer.start(-1); do_test_pending(); var targetCacheEntryId = null; return ( Promise.resolve() // Setup testing environment: Placing alternative data into HTTP cache. .then(_ => fetch(altContentType)) .then(r => check( r, responseContent, "", false, cacheEntryId => cacheEntryId === undefined ) ) .then(r => writeAltData(r.request)) // Start testing. .then(_ => fetch(altContentType)) .then(r => check( r, altContent, altContentType, true, cacheEntryId => cacheEntryId !== undefined ) ) .then(r => (targetCacheEntryId = r.cacheEntryId)) .then(_ => fetch()) .then(r => check( r, responseContent, "", true, cacheEntryId => cacheEntryId === targetCacheEntryId ) ) .then(_ => fetch(altContentType)) .then(r => check( r, altContent, altContentType, true, cacheEntryId => cacheEntryId === targetCacheEntryId ) ) .then(_ => fetch()) .then(r => check( r, responseContent, "", true, cacheEntryId => cacheEntryId === targetCacheEntryId ) ) .then(_ => fetch()) // The response is changed here. .then(r => check( r, responseContent2, "", false, cacheEntryId => cacheEntryId === undefined ) ) .then(_ => fetch()) .then(r => check( r, responseContent2, "", true, cacheEntryId => cacheEntryId !== undefined && cacheEntryId !== targetCacheEntryId ) ) // Tear down. .catch(e => Assert.ok(false, "Unexpected exception: " + e)) .then(_ => Assert.equal(handlers.length, 0)) .then(_ => httpServer.stop(do_test_finished)) ); }