Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
- Manifest: layout/style/test/browser.toml
async function clearAllCache() {
await new Promise(function (resolve) {
Services.clearData.deleteData(
Ci.nsIClearDataService.CLEAR_ALL_CACHES,
resolve
);
});
}
function testFields(
entry,
{ hasBodyAccess, hasTimingAccess, isCacheOf },
desc
) {
Assert.equal(entry.entryType, "resource", "entryType should be available");
Assert.equal(
entry.initiatorType,
"link",
"initiatorType should be available"
);
if (hasTimingAccess) {
Assert.equal(
entry.nextHopProtocol,
"http/1.1",
`nextHopProtocol should be available for ${desc}`
);
} else {
Assert.equal(
entry.nextHopProtocol,
"",
`nextHopProtocol should be hidden for ${desc}`
);
}
if (hasBodyAccess) {
Assert.equal(
entry.responseStatus,
200,
`responseStatus should be available for ${desc}`
);
} else {
Assert.equal(
entry.responseStatus,
0,
`responseStatus should be hidden for ${desc}`
);
}
if (hasBodyAccess) {
Assert.equal(
entry.contentType,
"text/css",
`contentType should be available for ${desc}`
);
} else {
Assert.equal(
entry.contentType,
"",
`contentType should be hidden for ${desc}`
);
}
Assert.greater(
entry.startTime,
0,
`startTime should be non-zero for ${desc}`
);
Assert.greater(
entry.responseEnd,
0,
`responseEnd should be non-zero for ${desc}`
);
Assert.lessOrEqual(
entry.startTime,
entry.responseEnd,
`startTime <= responseEnd for ${desc}`
);
if (hasTimingAccess) {
Assert.deepEqual(
entry.serverTiming,
[
{ name: "name1", duration: 0, description: "" },
{ name: "name2", duration: 20, description: "" },
{ name: "name3", duration: 30, description: "desc3" },
],
`serverTiming should be available for ${desc}`
);
} else {
Assert.deepEqual(
entry.serverTiming,
[],
`serverTiming should be hidden for ${desc}`
);
}
if (hasBodyAccess) {
Assert.greater(
entry.encodedBodySize,
0,
`encodedBodySize should be available for ${desc}`
);
} else {
Assert.equal(
entry.encodedBodySize,
0,
`encodedBodySize should be hidden for ${desc}`
);
}
if (isCacheOf) {
Assert.equal(
entry.encodedBodySize,
isCacheOf.encodedBodySize,
`encodedBodySize should equal to non-cache case for ${desc}`
);
}
if (hasBodyAccess) {
Assert.greater(
entry.decodedBodySize,
0,
`decodedBodySize should be available for ${desc}`
);
} else {
Assert.equal(
entry.decodedBodySize,
0,
`decodedBodySize should be hidden for ${desc}`
);
}
if (isCacheOf) {
Assert.equal(
entry.decodedBodySize,
isCacheOf.decodedBodySize,
`decodedBodySize should equal to non-cache case for ${desc}`
);
}
if (hasTimingAccess) {
if (isCacheOf) {
Assert.equal(
entry.transferSize,
0,
`transferSize should be zero for ${desc}`
);
} else if (hasBodyAccess) {
Assert.greater(
entry.transferSize,
300,
`transferSize should be non-zero +300 for ${desc}`
);
} else {
Assert.equal(
entry.transferSize,
300,
`transferSize should be zero +300 for ${desc}`
);
}
} else {
Assert.equal(
entry.transferSize,
0,
`transferSize should be hidden for ${desc}`
);
}
}
add_task(async function testCompleteCacheAfterReload() {
await clearAllCache();
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: URL_BASE + "empty.html",
},
async function (browser) {
const CSS_URL = URL_BASE + "css_server.sjs?cacheable";
const task = async url => {
await new Promise(resolve => {
const link = content.document.createElement("link");
link.rel = "stylesheet";
link.href = url;
link.addEventListener("load", resolve);
content.document.head.append(link);
});
const entries = content.performance
.getEntriesByType("resource")
.filter(entry => entry.name.includes("css_server.sjs"));
if (entries.length != 1) {
throw new Error(`Expect one entry, got ${entries.length} entries`);
}
// NOTE: entries[0].toJSON() doesn't convert serverTiming items.
return JSON.parse(JSON.stringify(entries[0]));
};
const entry = await SpecialPowers.spawn(browser, [CSS_URL], task);
Assert.equal(entry.name, CSS_URL);
testFields(
entry,
{
hasBodyAccess: true,
hasTimingAccess: true,
},
"same origin (non-cached)"
);
await BrowserTestUtils.reloadTab(gBrowser.selectedTab);
const cacheEntry = await SpecialPowers.spawn(browser, [CSS_URL], task);
Assert.equal(cacheEntry.name, CSS_URL);
testFields(
cacheEntry,
{
hasBodyAccess: true,
hasTimingAccess: true,
isCacheOf: entry,
},
"same origin (cached)"
);
}
);
});
add_task(async function testCompleteCacheInSameDocument() {
await clearAllCache();
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: URL_BASE + "empty.html",
},
async function (browser) {
const CSS_URL = URL_BASE + "css_server.sjs?cacheable";
const task = async url => {
// Before reload:
// * The first load is not cache
// * The second load is complete cache
// After reload:
// * Both loads are complete cache
for (let i = 0; i < 2; i++) {
await new Promise(resolve => {
const link = content.document.createElement("link");
link.rel = "stylesheet";
link.href = url;
link.addEventListener("load", () => {
resolve();
});
content.document.head.append(link);
});
}
const entries = content.performance
.getEntriesByType("resource")
.filter(entry => entry.name.includes("css_server.sjs"));
if (entries.length != 1) {
throw new Error(`Expect one entry, got ${entries.length} entries`);
}
return JSON.parse(JSON.stringify(entries[0]));
};
const entry = await SpecialPowers.spawn(browser, [CSS_URL], task);
Assert.equal(entry.name, CSS_URL);
testFields(
entry,
{
hasBodyAccess: true,
hasTimingAccess: true,
},
"same origin (non-cached)"
);
await BrowserTestUtils.reloadTab(gBrowser.selectedTab);
const cacheEntry = await SpecialPowers.spawn(browser, [CSS_URL], task);
Assert.equal(cacheEntry.name, CSS_URL);
testFields(
cacheEntry,
{
hasBodyAccess: true,
hasTimingAccess: true,
isCacheOf: entry,
},
"same origin (cached)"
);
}
);
});
add_task(async function testIncompleteCacheInSameDocument() {
await clearAllCache();
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: URL_BASE + "empty.html",
},
async function (browser) {
const CSS_URL = URL_BASE + "css_server.sjs?cacheable,slow";
const task = async url => {
const promises = [];
for (let i = 0; i < 2; i++) {
// The first load is not cache.
// The load load uses pending or loading cache, which is
// created by the first load.
promises.push(
new Promise(resolve => {
const link = content.document.createElement("link");
link.rel = "stylesheet";
link.href = url;
link.addEventListener("load", () => {
resolve();
});
content.document.head.append(link);
})
);
}
await Promise.all(promises);
const entries = content.performance
.getEntriesByType("resource")
.filter(entry => entry.name.includes("css_server.sjs"));
if (entries.length != 1) {
throw new Error(`Expect one entry, got ${entries.length} entries`);
}
return JSON.parse(JSON.stringify(entries[0]));
};
const entry = await SpecialPowers.spawn(browser, [CSS_URL], task);
Assert.equal(entry.name, CSS_URL);
testFields(
entry,
{
hasBodyAccess: true,
hasTimingAccess: true,
},
"same origin (non-cached)"
);
}
);
});
add_task(async function testIncompleteCacheInAnotherTab() {
await clearAllCache();
const CSS_URL = URL_BASE + "css_server.sjs?cacheable,slow";
// Prepare 2 tabs in the same process.
const tab1 = await BrowserTestUtils.openNewForegroundTab({
gBrowser,
url: URL_BASE + "empty.html",
});
const tab2Promise = BrowserTestUtils.waitForNewTab(gBrowser, null, true);
SpecialPowers.spawn(tab1.linkedBrowser, [], () => {
content.window.open("empty.html");
});
const tab2 = await tab2Promise;
const task = async url => {
await new Promise(resolve => {
const link = content.document.createElement("link");
link.rel = "stylesheet";
link.href = url;
link.addEventListener("load", () => {
resolve();
});
content.document.head.append(link);
});
const entries = content.performance
.getEntriesByType("resource")
.filter(entry => entry.name.includes("css_server.sjs"));
if (entries.length != 1) {
throw new Error(`Expect one entry, got ${entries.length} entries`);
}
return JSON.parse(JSON.stringify(entries[0]));
};
// Tab1's load is not cache.
// Tab2's load uses the pending or loading cache, which is created by the
// tab1's load.
const p1 = SpecialPowers.spawn(tab1.linkedBrowser, [CSS_URL], task);
const p2 = SpecialPowers.spawn(tab2.linkedBrowser, [CSS_URL], task);
const entry1 = await p1;
Assert.equal(entry1.name, CSS_URL);
testFields(
entry1,
{
hasBodyAccess: true,
hasTimingAccess: true,
},
"same origin (non-cached)"
);
const entry2 = await p2;
Assert.equal(entry2.name, CSS_URL);
testFields(
entry2,
{
hasBodyAccess: true,
hasTimingAccess: true,
isCacheOf: entry1,
},
"same origin (cached)"
);
BrowserTestUtils.removeTab(tab1);
BrowserTestUtils.removeTab(tab2);
});
add_task(async function testNoCacheReload() {
await clearAllCache();
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: URL_BASE + "empty.html",
},
async function (browser) {
const CSS_URL = URL_BASE + "css_server.sjs?";
const task = async url => {
await new Promise(resolve => {
const link = content.document.createElement("link");
link.rel = "stylesheet";
link.href = url;
link.addEventListener("load", resolve);
content.document.head.append(link);
});
const entries = content.performance
.getEntriesByType("resource")
.filter(entry => entry.name.includes("css_server.sjs"));
if (entries.length != 1) {
throw new Error(`Expect one entry, got ${entries.length} entries`);
}
return JSON.parse(JSON.stringify(entries[0]));
};
const entry = await SpecialPowers.spawn(browser, [CSS_URL], task);
Assert.equal(entry.name, CSS_URL);
testFields(
entry,
{
hasBodyAccess: true,
hasTimingAccess: true,
},
"same origin (non-cached)"
);
await BrowserTestUtils.reloadTab(gBrowser.selectedTab);
// Reloading the CSS shouldn't hit any cache.
const reloadEntry = await SpecialPowers.spawn(browser, [CSS_URL], task);
Assert.equal(reloadEntry.name, CSS_URL);
testFields(
reloadEntry,
{
hasBodyAccess: true,
hasTimingAccess: true,
},
"same origin (non-cached)"
);
}
);
});
add_task(async function test_NoCORS() {
await clearAllCache();
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: URL_BASE + "empty.html",
},
async function (browser) {
const CSS_URL = URL_BASE2 + "css_server.sjs?cacheable";
const task = async url => {
await new Promise(resolve => {
const link = content.document.createElement("link");
link.rel = "stylesheet";
link.href = url;
link.addEventListener("load", resolve);
content.document.head.append(link);
});
const entries = content.performance
.getEntriesByType("resource")
.filter(entry => entry.name.includes("css_server.sjs"));
if (entries.length != 1) {
throw new Error(`Expect one entry, got ${entries.length} entries`);
}
return JSON.parse(JSON.stringify(entries[0]));
};
const entry = await SpecialPowers.spawn(browser, [CSS_URL], task);
Assert.equal(entry.name, CSS_URL);
testFields(
entry,
{
hasBodyAccess: false,
hasTimingAccess: false,
},
"cross origin (non-cached)"
);
await BrowserTestUtils.reloadTab(gBrowser.selectedTab);
const cacheEntry = await SpecialPowers.spawn(browser, [CSS_URL], task);
Assert.equal(cacheEntry.name, CSS_URL);
testFields(
cacheEntry,
{
hasBodyAccess: false,
hasTimingAccess: false,
isCacheOf: entry,
},
"cross origin (cached)"
);
}
);
});
add_task(async function test_NoCORS_TAO() {
await clearAllCache();
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: URL_BASE + "empty.html",
},
async function (browser) {
const CSS_URL = URL_BASE2 + "css_server.sjs?cacheable,tao";
const task = async url => {
await new Promise(resolve => {
const link = content.document.createElement("link");
link.rel = "stylesheet";
link.href = url;
link.addEventListener("load", resolve);
content.document.head.append(link);
});
const entries = content.performance
.getEntriesByType("resource")
.filter(entry => entry.name.includes("css_server.sjs"));
if (entries.length != 1) {
throw new Error(`Expect one entry, got ${entries.length} entries`);
}
return JSON.parse(JSON.stringify(entries[0]));
};
const entry = await SpecialPowers.spawn(browser, [CSS_URL], task);
Assert.equal(entry.name, CSS_URL);
testFields(
entry,
{
hasBodyAccess: false,
hasTimingAccess: true,
},
"cross origin with Timing-Allow-Origin (non-cached)"
);
await BrowserTestUtils.reloadTab(gBrowser.selectedTab);
const cacheEntry = await SpecialPowers.spawn(browser, [CSS_URL], task);
Assert.equal(cacheEntry.name, CSS_URL);
testFields(
cacheEntry,
{
hasBodyAccess: false,
hasTimingAccess: true,
isCacheOf: entry,
},
"cross origin with Timing-Allow-Origin (cached)"
);
}
);
});
add_task(async function test_CORS() {
await clearAllCache();
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: URL_BASE + "empty.html",
},
async function (browser) {
const CSS_URL = URL_BASE2 + "css_server.sjs?cacheable,cors";
const task = async url => {
await new Promise(resolve => {
const link = content.document.createElement("link");
link.rel = "stylesheet";
link.setAttribute("crossorigin", "anonymous");
link.href = url;
link.addEventListener("load", resolve);
content.document.head.append(link);
});
const entries = content.performance
.getEntriesByType("resource")
.filter(entry => entry.name.includes("css_server.sjs"));
if (entries.length != 1) {
throw new Error(`Expect one entry, got ${entries.length} entries`);
}
return JSON.parse(JSON.stringify(entries[0]));
};
const entry = await SpecialPowers.spawn(browser, [CSS_URL], task);
Assert.equal(entry.name, CSS_URL);
testFields(
entry,
{
hasBodyAccess: true,
hasTimingAccess: false,
},
"CORS (non-cached)"
);
await BrowserTestUtils.reloadTab(gBrowser.selectedTab);
const cacheEntry = await SpecialPowers.spawn(browser, [CSS_URL], task);
Assert.equal(cacheEntry.name, CSS_URL);
testFields(
cacheEntry,
{
hasBodyAccess: true,
hasTimingAccess: false,
isCacheOf: entry,
},
"cors-cached"
);
}
);
});
add_task(async function test_CORS_TAO() {
await clearAllCache();
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: URL_BASE + "empty.html",
},
async function (browser) {
const CSS_URL = URL_BASE2 + "css_server.sjs?cacheable,cors,tao";
const task = async url => {
await new Promise(resolve => {
const link = content.document.createElement("link");
link.rel = "stylesheet";
link.setAttribute("crossorigin", "anonymous");
link.href = url;
link.addEventListener("load", resolve);
content.document.head.append(link);
});
const entries = content.performance
.getEntriesByType("resource")
.filter(entry => entry.name.includes("css_server.sjs"));
if (entries.length != 1) {
throw new Error(`Expect one entry, got ${entries.length} entries`);
}
return JSON.parse(JSON.stringify(entries[0]));
};
const entry = await SpecialPowers.spawn(browser, [CSS_URL], task);
Assert.equal(entry.name, CSS_URL);
testFields(
entry,
{
hasBodyAccess: true,
hasTimingAccess: true,
},
"CORS with Timing-Allow-Origin (non-cached)"
);
await BrowserTestUtils.reloadTab(gBrowser.selectedTab);
const cacheEntry = await SpecialPowers.spawn(browser, [CSS_URL], task);
Assert.equal(cacheEntry.name, CSS_URL);
testFields(
cacheEntry,
{
hasBodyAccess: true,
hasTimingAccess: true,
isCacheOf: entry,
},
"CORS with Timing-Allow-Origin (cached)"
);
}
);
});