The network predictor used the HTTP cache as a database to learn browsing patterns and make speculative DNS prefetches, TCP preconnects, and resource prefetches. While this provided performance benefits for repeat visits to frequently-accessed sites, it came with significant costs: I/O on every page load, CPU overhead for prediction algorithms, memory for caching learned patterns, and substantial IPC complexity with six message types coordinating predictions between processes. A large part of the functionality the predictor was meant to improve is now available to webdevelopers. HTTP 103 Early Hints allow servers to explicitly specify resources to preload while generating the response. And Speculation Rules API will enable content authors to declaratively specify prefetch/prerender behavior based on their knowledge of site structure and user intent, providing immediate accuracy without a learning period. The network.predictor.enable-hover-on-ssl preference is retained as it controls simpler hover-triggered speculative connections that don't require the predictor's learning system. This removal simplifies the codebase while steering users toward more accurate, standards-based optimization mechanisms. The one feature of the predictor which was never used was learning websites that are opened soon after startup. There was no action taken based on this info, and we could already predict this info based on the history database. Differential Revision: https://phabricator.services.mozilla.com/D276437
175 lines
5.2 KiB
JavaScript
175 lines
5.2 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
* http://creativecommons.org/publicdomain/zero/1.0/
|
|
*/
|
|
|
|
const cacheURL =
|
|
"http://example.org/browser/browser/components/originattributes/test/browser/file_cache.html";
|
|
|
|
function countMatchingCacheEntries(cacheEntries, domain, fileSuffix) {
|
|
return cacheEntries
|
|
.map(entry => entry.uri.asciiSpec)
|
|
.filter(spec => spec.includes(domain))
|
|
.filter(spec => spec.includes("file_thirdPartyChild." + fileSuffix)).length;
|
|
}
|
|
|
|
async function checkCache(suffixes, originAttributes) {
|
|
const loadContextInfo = Services.loadContextInfo.custom(
|
|
false,
|
|
originAttributes
|
|
);
|
|
|
|
const data = await new Promise(resolve => {
|
|
let cacheEntries = [];
|
|
let cacheVisitor = {
|
|
onCacheStorageInfo() {},
|
|
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);
|
|
});
|
|
|
|
for (let suffix of suffixes) {
|
|
let foundEntryCount = countMatchingCacheEntries(
|
|
data,
|
|
"example.net",
|
|
suffix
|
|
);
|
|
ok(
|
|
foundEntryCount > 0,
|
|
`Cache entries expected for ${suffix} and OA=${JSON.stringify(
|
|
originAttributes
|
|
)}`
|
|
);
|
|
}
|
|
}
|
|
|
|
add_task(async function () {
|
|
info("accept all");
|
|
await SpecialPowers.pushPrefEnv({
|
|
set: [
|
|
["test.wait300msAfterTabSwitch", true],
|
|
["network.cookie.cookieBehavior", 0],
|
|
],
|
|
});
|
|
|
|
const originAttributes = { partitionKey: "(http,example.org)" };
|
|
|
|
info("Clear image and network caches");
|
|
let tools = SpecialPowers.Cc["@mozilla.org/image/tools;1"].getService(
|
|
SpecialPowers.Ci.imgITools
|
|
);
|
|
let imageCache = tools.getImgCacheForDocument(window.document);
|
|
imageCache.clearCache(); // no parameter=all
|
|
Services.cache2.clear();
|
|
|
|
info("Let's load a page to populate some entries");
|
|
let tab = (gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser));
|
|
BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, cacheURL);
|
|
await BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, cacheURL);
|
|
|
|
let argObj = {
|
|
randomSuffix: Math.random(),
|
|
urlPrefix:
|
|
"http://example.net/browser/browser/components/originattributes/test/browser/",
|
|
};
|
|
|
|
await SpecialPowers.spawn(tab.linkedBrowser, [argObj], async function (arg) {
|
|
// The CSS/JS cache needs to be cleared in-process.
|
|
ChromeUtils.clearResourceCache({
|
|
types: ["stylesheet", "script"],
|
|
});
|
|
|
|
let videoURL = arg.urlPrefix + "file_thirdPartyChild.video.webm";
|
|
let audioURL = arg.urlPrefix + "file_thirdPartyChild.audio.ogg";
|
|
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");
|
|
|
|
// Append the audio element into the body, and wait until they're finished.
|
|
await new content.Promise(resolve => {
|
|
let audioLoaded = false;
|
|
|
|
let audioListener = () => {
|
|
Assert.ok(true, `Audio suspended: ${audioURL + URLSuffix}`);
|
|
audio.removeEventListener("suspend", audioListener);
|
|
|
|
audioLoaded = true;
|
|
if (audioLoaded) {
|
|
resolve();
|
|
}
|
|
};
|
|
|
|
Assert.ok(true, `Loading audio: ${audioURL + URLSuffix}`);
|
|
|
|
// Add the event listeners before everything in case we lose events.
|
|
audio.addEventListener("suspend", audioListener);
|
|
|
|
// Assign attributes for the audio element.
|
|
audioSource.setAttribute("src", audioURL + URLSuffix);
|
|
audioSource.setAttribute("type", "audio/ogg");
|
|
|
|
audio.appendChild(audioSource);
|
|
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);
|
|
});
|
|
});
|
|
|
|
let maybePartitionedSuffixes = [
|
|
"iframe.html",
|
|
"link.css",
|
|
"script.js",
|
|
"img.png",
|
|
"favicon.png",
|
|
"object.png",
|
|
"embed.png",
|
|
"xhr.html",
|
|
"worker.xhr.html",
|
|
"audio.ogg",
|
|
"video.webm",
|
|
"fetch.html",
|
|
"worker.fetch.html",
|
|
"request.html",
|
|
"worker.request.html",
|
|
"import.js",
|
|
"worker.js",
|
|
"sharedworker.js",
|
|
];
|
|
|
|
info("Query the partitioned cache");
|
|
await checkCache(maybePartitionedSuffixes, originAttributes);
|
|
|
|
gBrowser.removeCurrentTab();
|
|
});
|