chore: do not cache between reused context (#20052)

Fixes https://github.com/microsoft/playwright/issues/19926
This commit is contained in:
Max Schmitt 2023-03-03 12:37:44 +01:00 committed by GitHub
parent 6769a311ed
commit 778f8e65d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 82 additions and 0 deletions

View File

@ -209,6 +209,7 @@ export abstract class BrowserContext extends SdkObject {
await this.setGeolocation(this._options.geolocation);
await this.setOffline(!!this._options.offline);
await this.setUserAgent(this._options.userAgent);
await this.clearCache();
await this._resetCookies();
await page?.resetForReuse(metadata);
@ -246,6 +247,7 @@ export abstract class BrowserContext extends SdkObject {
abstract setUserAgent(userAgent: string | undefined): Promise<void>;
abstract setOffline(offline: boolean): Promise<void>;
abstract cancelDownload(uuid: string): Promise<void>;
abstract clearCache(): Promise<void>;
protected abstract doGetCookies(urls: string[]): Promise<channels.NetworkCookie[]>;
protected abstract doGrantPermissions(origin: string, permissions: string[]): Promise<void>;
protected abstract doClearPermissions(): Promise<void>;

View File

@ -543,6 +543,11 @@ export class CRBrowserContext extends BrowserContext {
}
}
override async clearCache(): Promise<void> {
for (const page of this._crPages())
await page._mainFrameSession._networkManager.clearCache();
}
async cancelDownload(guid: string) {
// The upstream CDP method is implemented in a way that no explicit error would be given
// regarding the requested `guid`, even if the download is in a state not suitable for

View File

@ -127,6 +127,14 @@ export class CRNetworkManager {
}
}
async clearCache() {
// Sending 'Network.setCacheDisabled' with 'cacheDisabled = true' will clear the MemoryCache.
await this._client.send('Network.setCacheDisabled', { cacheDisabled: true });
if (!this._protocolRequestInterceptionEnabled)
await this._client.send('Network.setCacheDisabled', { cacheDisabled: false });
await this._client.send('Network.clearBrowserCache');
}
_onRequestWillBeSent(workerFrame: frames.Frame | undefined, event: Protocol.Network.requestWillBeSentPayload) {
// Request interception doesn't happen for data URLs with Network Service.
if (this._protocolRequestInterceptionEnabled && !event.request.url.startsWith('data:')) {

View File

@ -358,6 +358,11 @@ export class FFBrowserContext extends BrowserContext {
onClosePersistent() {}
override async clearCache(): Promise<void> {
// Clearing only the context cache does not work: https://bugzilla.mozilla.org/show_bug.cgi?id=1819147
await this._browser._connection.send('Browser.clearCache');
}
async doClose() {
if (!this._browserContextId) {
if (this._options.recordVideo) {

View File

@ -339,6 +339,13 @@ export class WKBrowserContext extends BrowserContext {
onClosePersistent() {}
override async clearCache(): Promise<void> {
// We use ephemeral contexts so there is no disk cache.
await this._browser._browserSession.send('Playwright.clearMemoryCache', {
browserContextId: this._browserContextId!
});
}
async doClose() {
if (!this._browserContextId) {
await Promise.all(this._wkPages().map(wkPage => wkPage._stopVideo()));

View File

@ -127,3 +127,58 @@ test('should reset serviceworker that hangs in importScripts', async ({ reusedCo
await page.goto(server.PREFIX + '/page.html');
await expect(page).toHaveTitle('Page Title');
});
test('should not cache resources', async ({ reusedContext, server }) => {
test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/19926' });
const requestCountMap = new Map<string, number>();
server.setRoute('/page.html', (req, res) => {
res.setHeader('Content-Type', 'text/html');
res.setHeader(`Cache-Control`, `max-age=3600`);
const requestCount = requestCountMap.get(req.url) || 0;
res.end(`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Count: ${requestCount}</title>
<link rel="stylesheet" href="style.css">
<script>
fetch('simple.json').then(() => {});
</script>
</head>
</html>
`);
requestCountMap.set(req.url, requestCount + 1);
});
server.setRoute('/style.css', (req, res) => {
res.setHeader('Content-Type', 'text/css');
res.setHeader(`Cache-Control`, `max-age=3600`);
res.end(`body { background-color: red; }`);
requestCountMap.set(req.url, (requestCountMap.get(req.url) || 0) + 1);
});
server.setRoute('/simple.json', (req, res) => {
res.setHeader(`Cache-Control`, `max-age=3600`);
res.setHeader('Content-Type', 'application/json');
res.end(`{ "foo": "bar" }`);
requestCountMap.set(req.url, (requestCountMap.get(req.url) || 0) + 1);
});
{
const context = await reusedContext();
const page = await context.newPage();
await page.goto(server.PREFIX + '/page.html');
await expect(page).toHaveTitle('Count: 0');
expect(requestCountMap.get('/page.html')).toBe(1);
expect(requestCountMap.get('/style.css')).toBe(1);
expect(requestCountMap.get('/simple.json')).toBe(1);
}
{
const context = await reusedContext();
const page = context.pages()[0];
await page.goto(server.PREFIX + '/page.html');
await expect(page).toHaveTitle('Count: 1');
expect(requestCountMap.get('/page.html')).toBe(2);
expect(requestCountMap.get('/style.css')).toBe(2);
expect(requestCountMap.get('/simple.json')).toBe(2);
}
});