mirror of
https://github.com/microsoft/playwright.git
synced 2025-01-05 19:04:43 +03:00
chore(tracing): remove resource id (#8131)
This commit is contained in:
parent
75dfc15e62
commit
21b510c6e7
@ -21,11 +21,13 @@ export class SnapshotRenderer {
|
||||
private _index: number;
|
||||
readonly snapshotName: string | undefined;
|
||||
private _resources: ResourceSnapshot[];
|
||||
private _snapshot: FrameSnapshot;
|
||||
|
||||
constructor(resources: ResourceSnapshot[], snapshots: FrameSnapshot[], index: number) {
|
||||
this._resources = resources;
|
||||
this._snapshots = snapshots;
|
||||
this._index = index;
|
||||
this._snapshot = snapshots[index];
|
||||
this.snapshotName = snapshots[index].snapshotName;
|
||||
}
|
||||
|
||||
@ -73,10 +75,10 @@ export class SnapshotRenderer {
|
||||
return (n as any)._string;
|
||||
};
|
||||
|
||||
const snapshot = this._snapshots[this._index];
|
||||
const snapshot = this._snapshot;
|
||||
let html = visit(snapshot.html, this._index);
|
||||
if (!html)
|
||||
return { html: '', resources: {} };
|
||||
return { html: '', pageId: snapshot.pageId, frameId: snapshot.frameId, index: this._index };
|
||||
|
||||
if (snapshot.doctype)
|
||||
html = `<!DOCTYPE ${snapshot.doctype}>` + html;
|
||||
@ -85,26 +87,46 @@ export class SnapshotRenderer {
|
||||
<script>${snapshotScript()}</script>
|
||||
`;
|
||||
|
||||
const resources: { [key: string]: { resourceId: string, sha1?: string } } = {};
|
||||
// First capture all resources for all frames, to account for memory cache.
|
||||
for (const resource of this._resources) {
|
||||
if (resource.timestamp >= snapshot.timestamp)
|
||||
break;
|
||||
resources[resource.url] = { resourceId: resource.resourceId };
|
||||
}
|
||||
// Then overwrite with the ones from our frame.
|
||||
return { html, pageId: snapshot.pageId, frameId: snapshot.frameId, index: this._index };
|
||||
}
|
||||
|
||||
resourceByUrl(url: string): ResourceSnapshot | undefined {
|
||||
const snapshot = this._snapshot;
|
||||
let result: ResourceSnapshot | undefined;
|
||||
|
||||
// First try locating exact resource belonging to this frame.
|
||||
for (const resource of this._resources) {
|
||||
if (resource.timestamp >= snapshot.timestamp)
|
||||
break;
|
||||
if (resource.frameId !== snapshot.frameId)
|
||||
continue;
|
||||
resources[resource.url] = { resourceId: resource.resourceId };
|
||||
if (resource.url === url) {
|
||||
result = resource;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (const o of snapshot.resourceOverrides) {
|
||||
const resource = resources[o.url];
|
||||
resource.sha1 = o.sha1;
|
||||
|
||||
if (!result) {
|
||||
// Then fall back to resource with this URL to account for memory cache.
|
||||
for (const resource of this._resources) {
|
||||
if (resource.timestamp >= snapshot.timestamp)
|
||||
break;
|
||||
if (resource.url === url)
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
return { html, resources };
|
||||
|
||||
if (result) {
|
||||
// Patch override if necessary.
|
||||
for (const o of snapshot.resourceOverrides) {
|
||||
if (url === o.url && o.sha1) {
|
||||
result = { ...result, responseSha1: o.sha1 };
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ export class SnapshotServer {
|
||||
|
||||
private _serveServiceWorker(request: http.IncomingMessage, response: http.ServerResponse): boolean {
|
||||
function serviceWorkerMain(self: any /* ServiceWorkerGlobalScope */) {
|
||||
const snapshotResources = new Map<string, { [key: string]: { resourceId?: string, sha1?: string } }>();
|
||||
const snapshotIds = new Map<string, { frameId: string, index: number }>();
|
||||
|
||||
self.addEventListener('install', function(event: any) {
|
||||
});
|
||||
@ -71,10 +71,6 @@ export class SnapshotServer {
|
||||
event.waitUntil(self.clients.claim());
|
||||
});
|
||||
|
||||
function respond404(): Response {
|
||||
return new Response(null, { status: 404 });
|
||||
}
|
||||
|
||||
function respondNotAvailable(): Response {
|
||||
return new Response('<body style="background: #ddd"></body>', { status: 200, headers: { 'Content-Type': 'text/html' } });
|
||||
}
|
||||
@ -100,34 +96,26 @@ export class SnapshotServer {
|
||||
|
||||
if (request.mode === 'navigate') {
|
||||
const htmlResponse = await fetch(event.request);
|
||||
const { html, resources }: RenderedFrameSnapshot = await htmlResponse.json();
|
||||
const { html, frameId, index }: RenderedFrameSnapshot = await htmlResponse.json();
|
||||
if (!html)
|
||||
return respondNotAvailable();
|
||||
snapshotResources.set(snapshotUrl, resources);
|
||||
snapshotIds.set(snapshotUrl, { frameId, index });
|
||||
const response = new Response(html, { status: 200, headers: { 'Content-Type': 'text/html' } });
|
||||
return response;
|
||||
}
|
||||
|
||||
const resources = snapshotResources.get(snapshotUrl)!;
|
||||
const urlWithoutHash = removeHash(request.url);
|
||||
const resource = resources[urlWithoutHash];
|
||||
if (!resource)
|
||||
return respond404();
|
||||
|
||||
const fetchUrl = resource.sha1 ?
|
||||
`/resources/${resource.resourceId}/override/${resource.sha1}` :
|
||||
`/resources/${resource.resourceId}`;
|
||||
const { frameId, index } = snapshotIds.get(snapshotUrl)!;
|
||||
const url = removeHash(request.url);
|
||||
const complexUrl = btoa(JSON.stringify({ frameId, index, url }));
|
||||
const fetchUrl = `/resources/${complexUrl}`;
|
||||
const fetchedResponse = await fetch(fetchUrl);
|
||||
const headers = new Headers(fetchedResponse.headers);
|
||||
// We make a copy of the response, instead of just forwarding,
|
||||
// so that response url is not inherited as "/resources/...", but instead
|
||||
// as the original request url.
|
||||
|
||||
// Response url turns into resource base uri that is used to resolve
|
||||
// relative links, e.g. url(/foo/bar) in style sheets.
|
||||
if (resource.sha1) {
|
||||
// No cache, so that we refetch overridden resources.
|
||||
headers.set('Cache-Control', 'no-cache');
|
||||
}
|
||||
const headers = new Headers(fetchedResponse.headers);
|
||||
const response = new Response(fetchedResponse.body, {
|
||||
status: fetchedResponse.status,
|
||||
statusText: fetchedResponse.statusText,
|
||||
@ -178,32 +166,13 @@ export class SnapshotServer {
|
||||
}
|
||||
|
||||
private _serveResource(request: http.IncomingMessage, response: http.ServerResponse): boolean {
|
||||
// - /resources/<resourceId>
|
||||
// - /resources/<resourceId>/override/<overrideSha1>
|
||||
const parts = request.url!.split('/');
|
||||
if (!parts[0])
|
||||
parts.shift();
|
||||
if (!parts[parts.length - 1])
|
||||
parts.pop();
|
||||
if (parts[0] !== 'resources')
|
||||
return false;
|
||||
|
||||
let resourceId;
|
||||
let overrideSha1;
|
||||
if (parts.length === 2) {
|
||||
resourceId = parts[1];
|
||||
} else if (parts.length === 4 && parts[2] === 'override') {
|
||||
resourceId = parts[1];
|
||||
overrideSha1 = parts[3];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
const resource = this._snapshotStorage.resourceById(resourceId);
|
||||
const { frameId, index, url } = JSON.parse(Buffer.from(request.url!.substring('/resources/'.length), 'base64').toString());
|
||||
const snapshot = this._snapshotStorage.snapshotByIndex(frameId, index);
|
||||
const resource = snapshot?.resourceByUrl(url);
|
||||
if (!resource)
|
||||
return false;
|
||||
|
||||
const sha1 = overrideSha1 || resource.responseSha1;
|
||||
const sha1 = resource.responseSha1;
|
||||
try {
|
||||
const content = this._snapshotStorage.resourceContent(sha1);
|
||||
if (!content)
|
||||
|
@ -21,13 +21,12 @@ import { SnapshotRenderer } from './snapshotRenderer';
|
||||
export interface SnapshotStorage {
|
||||
resources(): ResourceSnapshot[];
|
||||
resourceContent(sha1: string): Buffer | undefined;
|
||||
resourceById(resourceId: string): ResourceSnapshot | undefined;
|
||||
snapshotByName(pageOrFrameId: string, snapshotName: string): SnapshotRenderer | undefined;
|
||||
snapshotByIndex(frameId: string, index: number): SnapshotRenderer | undefined;
|
||||
}
|
||||
|
||||
export abstract class BaseSnapshotStorage extends EventEmitter implements SnapshotStorage {
|
||||
protected _resources: ResourceSnapshot[] = [];
|
||||
protected _resourceMap = new Map<string, ResourceSnapshot>();
|
||||
protected _frameSnapshots = new Map<string, {
|
||||
raw: FrameSnapshot[],
|
||||
renderer: SnapshotRenderer[]
|
||||
@ -35,12 +34,10 @@ export abstract class BaseSnapshotStorage extends EventEmitter implements Snapsh
|
||||
|
||||
clear() {
|
||||
this._resources = [];
|
||||
this._resourceMap.clear();
|
||||
this._frameSnapshots.clear();
|
||||
}
|
||||
|
||||
addResource(resource: ResourceSnapshot): void {
|
||||
this._resourceMap.set(resource.resourceId, resource);
|
||||
this._resources.push(resource);
|
||||
}
|
||||
|
||||
@ -63,10 +60,6 @@ export abstract class BaseSnapshotStorage extends EventEmitter implements Snapsh
|
||||
|
||||
abstract resourceContent(sha1: string): Buffer | undefined;
|
||||
|
||||
resourceById(resourceId: string): ResourceSnapshot | undefined {
|
||||
return this._resourceMap.get(resourceId)!;
|
||||
}
|
||||
|
||||
resources(): ResourceSnapshot[] {
|
||||
return this._resources.slice();
|
||||
}
|
||||
@ -75,4 +68,10 @@ export abstract class BaseSnapshotStorage extends EventEmitter implements Snapsh
|
||||
const snapshot = this._frameSnapshots.get(pageOrFrameId);
|
||||
return snapshot?.renderer.find(r => r.snapshotName === snapshotName);
|
||||
}
|
||||
|
||||
snapshotByIndex(frameId: string, index: number): SnapshotRenderer | undefined {
|
||||
const snapshot = this._frameSnapshots.get(frameId);
|
||||
return snapshot?.renderer[index];
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
export type ResourceSnapshot = {
|
||||
resourceId: string,
|
||||
pageId: string,
|
||||
frameId: string,
|
||||
url: string,
|
||||
@ -65,5 +64,7 @@ export type FrameSnapshot = {
|
||||
|
||||
export type RenderedFrameSnapshot = {
|
||||
html: string;
|
||||
resources: { [key: string]: { resourceId: string, sha1?: string } };
|
||||
pageId: string;
|
||||
frameId: string;
|
||||
index: number;
|
||||
};
|
||||
|
@ -207,7 +207,6 @@ export class Snapshotter {
|
||||
const resource: ResourceSnapshot = {
|
||||
pageId: response.frame()._page.guid,
|
||||
frameId: response.frame().guid,
|
||||
resourceId: response.guid,
|
||||
url,
|
||||
type: response.request().resourceType(),
|
||||
contentType,
|
||||
|
@ -49,11 +49,11 @@ export class TraceViewer {
|
||||
// - "/sha1/<sha1>" - trace resource bodies, used by network previews.
|
||||
//
|
||||
// Served by SnapshotServer
|
||||
// - "/resources/<resourceId>" - network resources from the trace.
|
||||
// - "/resources/" - network resources from the trace.
|
||||
// - "/snapshot/" - root for snapshot frame.
|
||||
// - "/snapshot/pageId/..." - actual snapshot html.
|
||||
// - "/snapshot/service-worker.js" - service worker that intercepts snapshot resources
|
||||
// and translates them into "/resources/<resourceId>".
|
||||
// and translates them into network requests.
|
||||
const actionTraces = fs.readdirSync(tracesDir).filter(name => name.endsWith('.trace'));
|
||||
const debugNames = actionTraces.map(name => {
|
||||
const tracePrefix = path.join(tracesDir, name.substring(0, name.indexOf('.trace')));
|
||||
|
@ -62,9 +62,8 @@ it.describe('snapshots', () => {
|
||||
});
|
||||
await page.setContent('<link rel="stylesheet" href="style.css"><button>Hello</button>');
|
||||
const snapshot = await snapshotter.captureSnapshot(toImpl(page), 'snapshot');
|
||||
const { resources } = snapshot.render();
|
||||
const cssHref = `http://localhost:${server.PORT}/style.css`;
|
||||
expect(resources[cssHref]).toBeTruthy();
|
||||
const resource = snapshot.resourceByUrl(`http://localhost:${server.PORT}/style.css`);
|
||||
expect(resource).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should collect multiple', async ({ page, toImpl, snapshotter }) => {
|
||||
@ -126,10 +125,8 @@ it.describe('snapshots', () => {
|
||||
|
||||
await page.evaluate(() => { (document.styleSheets[0].cssRules[0] as any).style.color = 'blue'; });
|
||||
const snapshot2 = await snapshotter.captureSnapshot(toImpl(page), 'snapshot1');
|
||||
const { resources } = snapshot2.render();
|
||||
const cssHref = `http://localhost:${server.PORT}/style.css`;
|
||||
const { sha1 } = resources[cssHref];
|
||||
expect(snapshotter.resourceContent(sha1).toString()).toBe('button { color: blue; }');
|
||||
const resource = snapshot2.resourceByUrl(`http://localhost:${server.PORT}/style.css`);
|
||||
expect(snapshotter.resourceContent(resource.responseSha1).toString()).toBe('button { color: blue; }');
|
||||
});
|
||||
|
||||
it('should capture iframe', async ({ page, contextFactory, server, toImpl, browserName, snapshotter, snapshotPort }) => {
|
||||
|
Loading…
Reference in New Issue
Block a user