diff options
Diffstat (limited to 'browser/components/originattributes/test/browser/browser_cache.js')
-rw-r--r-- | browser/components/originattributes/test/browser/browser_cache.js | 350 |
1 files changed, 350 insertions, 0 deletions
diff --git a/browser/components/originattributes/test/browser/browser_cache.js b/browser/components/originattributes/test/browser/browser_cache.js new file mode 100644 index 0000000000..ea8f0fe803 --- /dev/null +++ b/browser/components/originattributes/test/browser/browser_cache.js @@ -0,0 +1,350 @@ +/* + * Bug 1264577 - A test case for testing caches of various submodules. + * This test case will load two pages that each page loads various resources + * within the same third party domain for the same originAttributes or different + * originAttributes. And then, it verifies the number of cache entries and + * the originAttributes of loading channels. If these two pages belong to + * the same originAttributes, the number of cache entries for a certain + * resource would be one. Otherwise, it would be two. + */ + +const CC = Components.Constructor; + +let protocolProxyService = Cc[ + "@mozilla.org/network/protocol-proxy-service;1" +].getService(Ci.nsIProtocolProxyService); + +const TEST_DOMAIN = "http://example.net"; +const TEST_PATH = "/browser/browser/components/originattributes/test/browser/"; +const TEST_PAGE = TEST_DOMAIN + TEST_PATH + "file_cache.html"; + +let suffixes = [ + "iframe.html", + "link.css", + "script.js", + "img.png", + "object.png", + "embed.png", + "xhr.html", + "worker.xhr.html", + "audio.ogg", + "video.ogv", + "track.vtt", + "fetch.html", + "worker.fetch.html", + "request.html", + "worker.request.html", + "import.js", + "worker.js", + "sharedworker.js", + "font.woff", +]; + +// A random value for isolating video/audio elements across different tests. +let randomSuffix; + +function clearAllImageCaches() { + let tools = SpecialPowers.Cc["@mozilla.org/image/tools;1"].getService( + SpecialPowers.Ci.imgITools + ); + let imageCache = tools.getImgCacheForDocument(window.document); + imageCache.clearCache(true); // true=chrome + imageCache.clearCache(false); // false=content +} + +function cacheDataForContext(loadContextInfo) { + return new Promise(resolve => { + let cacheEntries = []; + let cacheVisitor = { + onCacheStorageInfo(num, consumption) {}, + onCacheEntryInfo(uri, idEnhance) { + cacheEntries.push({ uri, idEnhance }); + }, + onCacheEntryVisitCompleted() { + resolve(cacheEntries); + }, + QueryInterface: ChromeUtils.generateQI(["nsICacheStorageVisitor"]), + }; + // Visiting the disk cache also visits memory storage so we do not + // need to use Services.cache2.memoryCacheStorage() here. + let storage = Services.cache2.diskCacheStorage(loadContextInfo); + storage.asyncVisitStorage(cacheVisitor, true); + }); +} + +let countMatchingCacheEntries = function (cacheEntries, domain, fileSuffix) { + return cacheEntries + .map(entry => entry.uri.asciiSpec) + .filter(spec => spec.includes(domain)) + .filter(spec => spec.includes("file_thirdPartyChild." + fileSuffix)).length; +}; + +function observeChannels(onChannel) { + // We use a dummy proxy filter to catch all channels, even those that do not + // generate an "http-on-modify-request" notification, such as link preconnects. + let proxyFilter = { + applyFilter(aChannel, aProxy, aCallback) { + // We have the channel; provide it to the callback. + onChannel(aChannel); + // Pass on aProxy unmodified. + aCallback.onProxyFilterResult(aProxy); + }, + }; + protocolProxyService.registerChannelFilter(proxyFilter, 0); + // Return the stop() function: + return () => protocolProxyService.unregisterChannelFilter(proxyFilter); +} + +function startObservingChannels(aMode) { + let stopObservingChannels = observeChannels(function (channel) { + let originalURISpec = channel.originalURI.spec; + if (originalURISpec.includes("example.net")) { + let loadInfo = channel.loadInfo; + + switch (aMode) { + case TEST_MODE_FIRSTPARTY: + ok( + loadInfo.originAttributes.firstPartyDomain === "example.com" || + loadInfo.originAttributes.firstPartyDomain === "example.org", + "first party for " + + originalURISpec + + " is " + + loadInfo.originAttributes.firstPartyDomain + ); + break; + + case TEST_MODE_NO_ISOLATION: + ok( + ChromeUtils.isOriginAttributesEqual( + loadInfo.originAttributes, + ChromeUtils.fillNonDefaultOriginAttributes() + ), + "OriginAttributes for " + originalURISpec + " is default." + ); + break; + + case TEST_MODE_CONTAINERS: + ok( + loadInfo.originAttributes.userContextId === 1 || + loadInfo.originAttributes.userContextId === 2, + "userContextId for " + + originalURISpec + + " is " + + loadInfo.originAttributes.userContextId + ); + break; + + default: + ok(false, "Unknown test mode."); + } + } + }); + return stopObservingChannels; +} + +let stopObservingChannels; + +// The init function, which clears image and network caches, and generates +// the random value for isolating video and audio elements across different +// test runs. +async function doInit(aMode) { + await SpecialPowers.pushPrefEnv({ + set: [ + ["network.predictor.enabled", false], + ["network.predictor.enable-prefetch", false], + ["privacy.partition.network_state", false], + ["dom.security.https_first", false], + ], + }); + clearAllImageCaches(); + + Services.cache2.clear(); + + randomSuffix = Math.random(); + stopObservingChannels = startObservingChannels(aMode); +} + +// In the test function, we dynamically generate the video and audio element, +// and assign a random suffix to their URL to isolate them across different +// test runs. +async function doTest(aBrowser) { + let argObj = { + randomSuffix, + urlPrefix: TEST_DOMAIN + TEST_PATH, + }; + + await SpecialPowers.spawn(aBrowser, [argObj], async function (arg) { + content.windowUtils.clearSharedStyleSheetCache(); + + let videoURL = arg.urlPrefix + "file_thirdPartyChild.video.ogv"; + let audioURL = arg.urlPrefix + "file_thirdPartyChild.audio.ogg"; + let trackURL = arg.urlPrefix + "file_thirdPartyChild.track.vtt"; + let URLSuffix = "?r=" + arg.randomSuffix; + + // Create the audio and video elements. + let audio = content.document.createElement("audio"); + let video = content.document.createElement("video"); + let audioSource = content.document.createElement("source"); + let audioTrack = content.document.createElement("track"); + + // Append the audio and track element into the body, and wait until they're finished. + await new content.Promise(resolve => { + let audioLoaded = false; + let trackLoaded = false; + + let audioListener = () => { + Assert.ok(true, `Audio suspended: ${audioURL + URLSuffix}`); + audio.removeEventListener("suspend", audioListener); + + audioLoaded = true; + if (audioLoaded && trackLoaded) { + resolve(); + } + }; + + let trackListener = () => { + Assert.ok(true, `Audio track loaded: ${audioURL + URLSuffix}`); + audioTrack.removeEventListener("load", trackListener); + + trackLoaded = true; + if (audioLoaded && trackLoaded) { + resolve(); + } + }; + + Assert.ok(true, `Loading audio: ${audioURL + URLSuffix}`); + + // Add the event listeners before everything in case we lose events. + audioTrack.addEventListener("load", trackListener); + audio.addEventListener("suspend", audioListener); + + // Assign attributes for the audio element. + audioSource.setAttribute("src", audioURL + URLSuffix); + audioSource.setAttribute("type", "audio/ogg"); + audioTrack.setAttribute("src", trackURL); + audioTrack.setAttribute("kind", "subtitles"); + audioTrack.setAttribute("default", true); + + audio.appendChild(audioSource); + audio.appendChild(audioTrack); + audio.autoplay = true; + + content.document.body.appendChild(audio); + }); + + // Append the video element into the body, and wait until it's finished. + await new content.Promise(resolve => { + let listener = () => { + Assert.ok(true, `Video suspended: ${videoURL + URLSuffix}`); + video.removeEventListener("suspend", listener); + resolve(); + }; + + Assert.ok(true, `Loading video: ${videoURL + URLSuffix}`); + + // Add the event listener before everything in case we lose the event. + video.addEventListener("suspend", listener); + + // Assign attributes for the video element. + video.setAttribute("src", videoURL + URLSuffix); + video.setAttribute("type", "video/ogg"); + + content.document.body.appendChild(video); + }); + }); + + return 0; +} + +// The check function, which checks the number of cache entries. +async function doCheck(aShouldIsolate, aInputA, aInputB) { + let expectedEntryCount = 1; + let data = []; + data = data.concat( + await cacheDataForContext(Services.loadContextInfo.default) + ); + data = data.concat( + await cacheDataForContext(Services.loadContextInfo.private) + ); + data = data.concat( + await cacheDataForContext(Services.loadContextInfo.custom(true, {})) + ); + data = data.concat( + await cacheDataForContext( + Services.loadContextInfo.custom(false, { userContextId: 1 }) + ) + ); + data = data.concat( + await cacheDataForContext( + Services.loadContextInfo.custom(true, { userContextId: 1 }) + ) + ); + data = data.concat( + await cacheDataForContext( + Services.loadContextInfo.custom(false, { userContextId: 2 }) + ) + ); + data = data.concat( + await cacheDataForContext( + Services.loadContextInfo.custom(true, { userContextId: 2 }) + ) + ); + data = data.concat( + await cacheDataForContext( + Services.loadContextInfo.custom(false, { + firstPartyDomain: "example.com", + }) + ) + ); + data = data.concat( + await cacheDataForContext( + Services.loadContextInfo.custom(true, { firstPartyDomain: "example.com" }) + ) + ); + data = data.concat( + await cacheDataForContext( + Services.loadContextInfo.custom(false, { + firstPartyDomain: "example.org", + }) + ) + ); + data = data.concat( + await cacheDataForContext( + Services.loadContextInfo.custom(true, { firstPartyDomain: "example.org" }) + ) + ); + + if (aShouldIsolate) { + expectedEntryCount = 2; + } + + for (let suffix of suffixes) { + let foundEntryCount = countMatchingCacheEntries( + data, + "example.net", + suffix + ); + let result = expectedEntryCount === foundEntryCount; + ok( + result, + "Cache entries expected for " + + suffix + + ": " + + expectedEntryCount + + ", and found " + + foundEntryCount + ); + } + + stopObservingChannels(); + stopObservingChannels = undefined; + return true; +} + +let testArgs = { + url: TEST_PAGE, + firstFrameSetting: DEFAULT_FRAME_SETTING, + secondFrameSetting: [TEST_TYPE_FRAME], +}; + +IsolationTestTools.runTests(testArgs, doTest, doCheck, doInit); |