Skip to content

Commit 4736372

Browse files
authored
fix(trace viewer): prevent stale content for resource overrides (#38404)
1 parent ded7392 commit 4736372

File tree

3 files changed

+56
-1
lines changed

3 files changed

+56
-1
lines changed

packages/playwright-core/src/utils/isomorphic/trace/snapshotServer.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,10 @@ export class SnapshotServer {
104104
headers.set('Access-Control-Allow-Origin', '*');
105105
headers.delete('Content-Length');
106106
headers.set('Content-Length', String(content.size));
107-
headers.set('Cache-Control', 'public, max-age=31536000');
107+
if (this._snapshotStorage.hasResourceOverride(resource.request.url))
108+
headers.set('Cache-Control', 'no-store, no-cache, max-age=0');
109+
else
110+
headers.set('Cache-Control', 'public, max-age=31536000');
108111
const { status } = resource.response;
109112
const isNullBodyStatus = status === 101 || status === 204 || status === 205 || status === 304;
110113
return new Response(isNullBodyStatus ? null : content, {

packages/playwright-core/src/utils/isomorphic/trace/snapshotStorage.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export class SnapshotStorage {
2828
}>();
2929
private _cache = new LRUCache<SnapshotRenderer, string>(100_000_000); // 100MB per each trace
3030
private _contextToResources = new Map<string, ResourceSnapshot[]>();
31+
private _resourceUrlsWithOverrides = new Set<string>();
3132

3233
addResource(contextId: string, resource: ResourceSnapshot): void {
3334
resource.request.url = rewriteURLForCustomProtocol(resource.request.url);
@@ -67,6 +68,18 @@ export class SnapshotStorage {
6768
// Resources are not necessarily sorted in the trace file, so sort them now.
6869
for (const resources of this._contextToResources.values())
6970
resources.sort((a, b) => (a._monotonicTime || 0) - (b._monotonicTime || 0));
71+
// Resources that have overrides should not be cached, otherwise we might get stale content
72+
// while serving snapshots with different override values.
73+
for (const frameSnapshots of this._frameSnapshots.values()) {
74+
for (const snapshot of frameSnapshots.raw) {
75+
for (const override of snapshot.resourceOverrides)
76+
this._resourceUrlsWithOverrides.add(override.url);
77+
}
78+
}
79+
}
80+
81+
hasResourceOverride(url: string) {
82+
return this._resourceUrlsWithOverrides.has(url);
7083
}
7184

7285
private _ensureResourcesForContext(contextId: string): ResourceSnapshot[] {

tests/library/trace-viewer.spec.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2129,3 +2129,42 @@ test('should not navigate on anchor clicks', async ({ runAndTrace, page, server
21292129
await checkLink('link2');
21302130
await checkLink('link3');
21312131
});
2132+
2133+
test('should respect CSSOM changes', async ({ runAndTrace, page, server }) => {
2134+
const traceViewer = await runAndTrace(async () => {
2135+
await page.setContent('<style>button { color: red; }</style><button>Hello</button>');
2136+
await page.evaluate(() => { (document.styleSheets[0].cssRules[0] as any).style.color = 'blue'; });
2137+
2138+
await page.setContent('<style>@media { button { color: red; } }</style><button>Hello</button>');
2139+
await page.evaluate(() => {
2140+
window['rule'] = document.styleSheets[0].cssRules[0];
2141+
void 0;
2142+
});
2143+
await page.evaluate(() => { window['rule'].cssRules[0].style.color = 'black'; });
2144+
await page.evaluate(() => { window['rule'].insertRule('button:not(.disabled) { color: green; }', 1); });
2145+
2146+
await page.route('**/style.css', route => {
2147+
route.fulfill({ body: 'button { color: red; }', }).catch(() => {});
2148+
});
2149+
await page.goto(server.EMPTY_PAGE);
2150+
await page.setContent('<link rel="stylesheet" href="style.css"><button>Hello</button>');
2151+
await page.evaluate(() => { (document.styleSheets[0].cssRules[0] as any).style.color = 'blue'; });
2152+
});
2153+
2154+
const frame1 = await traceViewer.snapshotFrame('Set content', 0);
2155+
await expect(frame1.locator('button')).toHaveCSS('color', 'rgb(255, 0, 0)');
2156+
const frame2 = await traceViewer.snapshotFrame('Evaluate', 0);
2157+
await expect(frame2.locator('button')).toHaveCSS('color', 'rgb(0, 0, 255)');
2158+
2159+
const frame3 = await traceViewer.snapshotFrame('Set content', 1);
2160+
await expect(frame3.locator('button')).toHaveCSS('color', 'rgb(255, 0, 0)');
2161+
const frame4 = await traceViewer.snapshotFrame('Evaluate', 2);
2162+
await expect(frame4.locator('button')).toHaveCSS('color', 'rgb(0, 0, 0)');
2163+
const frame5 = await traceViewer.snapshotFrame('Evaluate', 3);
2164+
await expect(frame5.locator('button')).toHaveCSS('color', 'rgb(0, 128, 0)');
2165+
2166+
const frame6 = await traceViewer.snapshotFrame('Set content', 2);
2167+
await expect(frame6.locator('button')).toHaveCSS('color', 'rgb(255, 0, 0)');
2168+
const frame7 = await traceViewer.snapshotFrame('Evaluate', 4);
2169+
await expect(frame7.locator('button')).toHaveCSS('color', 'rgb(0, 0, 255)');
2170+
});

0 commit comments

Comments
 (0)