diff --git a/src/server/trace/viewer/frameSnapshot.ts b/src/server/trace/viewer/frameSnapshot.ts index 876a7b56ea..7885ab26da 100644 --- a/src/server/trace/viewer/frameSnapshot.ts +++ b/src/server/trace/viewer/frameSnapshot.ts @@ -15,23 +15,25 @@ */ import * as trace from '../common/traceEvents'; -import { ContextEntry } from './traceModel'; +import { ContextEntry, ContextResources } from './traceModel'; export * as trace from '../common/traceEvents'; export type SerializedFrameSnapshot = { html: string; - resourcesByUrl: { [key: string]: { resourceId: string, frameId: string }[] }; - overriddenUrls: { [key: string]: boolean }; - resourceOverrides: { [key: string]: string }; + resources: { [key: string]: { resourceId: string, sha1?: string } }; }; export class FrameSnapshot { private _snapshots: trace.FrameSnapshotTraceEvent[]; private _index: number; - contextEntry: ContextEntry; + private _contextEntry: ContextEntry; + private _contextResources: ContextResources; + private _frameId: string; - constructor(contextEntry: ContextEntry, events: trace.FrameSnapshotTraceEvent[], index: number) { - this.contextEntry = contextEntry; + constructor(frameId: string, contextEntry: ContextEntry, contextResources: ContextResources, events: trace.FrameSnapshotTraceEvent[], index: number) { + this._frameId = frameId; + this._contextEntry = contextEntry; + this._contextResources = contextResources; this._snapshots = events; this._index = index; } @@ -80,14 +82,19 @@ export class FrameSnapshot { let html = visit(snapshot.html, this._index); if (snapshot.doctype) html = `` + html; - html += ``; + html += ``; - const resourcesByUrl = this.contextEntry.resourcesByUrl; - const overriddenUrls = this.contextEntry.overriddenUrls; - const resourceOverrides: any = {}; - for (const o of this._snapshots[this._index].snapshot.resourceOverrides) - resourceOverrides[o.url] = o.sha1; - return { html, resourcesByUrl, overriddenUrls: overriddenUrls, resourceOverrides }; + const resources: { [key: string]: { resourceId: string, sha1?: string } } = {}; + for (const [url, contextResources] of this._contextResources) { + const contextResource = contextResources.find(r => r.frameId === this._frameId) || contextResources[0]; + if (contextResource) + resources[url] = { resourceId: contextResource.resourceId }; + } + for (const o of this.traceEvent().snapshot.resourceOverrides) { + const resource = resources[o.url]; + resource.sha1 = o.sha1; + } + return { html, resources }; } } diff --git a/src/server/trace/viewer/snapshotServer.ts b/src/server/trace/viewer/snapshotServer.ts index cf0ae41d69..979d971c1c 100644 --- a/src/server/trace/viewer/snapshotServer.ts +++ b/src/server/trace/viewer/snapshotServer.ts @@ -20,6 +20,7 @@ import path from 'path'; import querystring from 'querystring'; import type { TraceModel } from './traceModel'; import { TraceServer } from './traceServer'; +import type { SerializedFrameSnapshot } from './frameSnapshot'; export class SnapshotServer { private _resourcesDir: string | undefined; @@ -104,9 +105,7 @@ export class SnapshotServer { private _serveServiceWorker(request: http.IncomingMessage, response: http.ServerResponse): boolean { function serviceWorkerMain(self: any /* ServiceWorkerGlobalScope */) { - const pageToResourcesByUrl = new Map(); - const pageToOverriddenUrls = new Map(); - const snapshotToResourceOverrides = new Map(); + const snapshotResources = new Map(); self.addEventListener('install', function(event: any) { }); @@ -171,28 +170,22 @@ export class SnapshotServer { if (request.mode === 'navigate') { const htmlResponse = await fetch(`/snapshot-data?pageId=${parsed.pageId}&snapshotId=${parsed.snapshotId || ''}×tamp=${parsed.timestamp || ''}&frameId=${parsed.frameId || ''}`); - const { html, resourcesByUrl, overriddenUrls, resourceOverrides } = await htmlResponse.json(); + const { html, resources }: SerializedFrameSnapshot = await htmlResponse.json(); if (!html) return respondNotAvailable(); - pageToResourcesByUrl.set(parsed.pageId, resourcesByUrl); - pageToOverriddenUrls.set(parsed.pageId, overriddenUrls); - snapshotToResourceOverrides.set(parsed.snapshotId + '@' + parsed.timestamp, resourceOverrides); + snapshotResources.set(parsed.snapshotId + '@' + parsed.timestamp, resources); const response = new Response(html, { status: 200, headers: { 'Content-Type': 'text/html' } }); return response; } - const resourcesByUrl = pageToResourcesByUrl.get(parsed.pageId); - const overriddenUrls = pageToOverriddenUrls.get(parsed.pageId); - const resourceOverrides = snapshotToResourceOverrides.get(parsed.snapshotId + '@' + parsed.timestamp); + const resources = snapshotResources.get(parsed.snapshotId + '@' + parsed.timestamp)!; const urlWithoutHash = removeHash(request.url); - const resourcesWithUrl = resourcesByUrl?.[urlWithoutHash] || []; - const resource = resourcesWithUrl.find(r => r.frameId === parsed.frameId) || resourcesWithUrl[0]; + const resource = resources[urlWithoutHash]; if (!resource) return respond404(); - const overrideSha1 = resourceOverrides?.[urlWithoutHash]; - const fetchUrl = overrideSha1 ? - `/resources/${resource.resourceId}/override/${overrideSha1}` : + const fetchUrl = resource.sha1 ? + `/resources/${resource.resourceId}/override/${resource.sha1}` : `/resources/${resource.resourceId}`; const fetchedResponse = await fetch(fetchUrl); const headers = new Headers(fetchedResponse.headers); @@ -201,7 +194,7 @@ export class SnapshotServer { // 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 (overriddenUrls?.[urlWithoutHash]) { + if (resource.sha1) { // No cache, so that we refetch overridden resources. headers.set('Cache-Control', 'no-cache'); } diff --git a/src/server/trace/viewer/traceModel.ts b/src/server/trace/viewer/traceModel.ts index 1de711ce1d..8a65a29a50 100644 --- a/src/server/trace/viewer/traceModel.ts +++ b/src/server/trace/viewer/traceModel.ts @@ -23,6 +23,7 @@ export class TraceModel { contextEntries = new Map(); pageEntries = new Map(); resourceById = new Map(); + contextResources = new Map(); appendEvents(events: trace.TraceEvent[]) { for (const event of events) @@ -39,9 +40,8 @@ export class TraceModel { created: event, destroyed: undefined as any, pages: [], - resourcesByUrl: {}, - overriddenUrls: {} }); + this.contextResources.set(event.contextId, new Map()); break; } case 'context-destroyed': { @@ -104,14 +104,12 @@ export class TraceModel { pageEntry.snapshotsByFrameId[event.frameId] = snapshots; } snapshots.push(event); - const contextEntry = this.contextEntries.get(event.contextId)!; for (const override of event.snapshot.resourceOverrides) { if (override.ref) { const refOverride = snapshots[snapshots.length - 1 - override.ref]?.snapshot.resourceOverrides.find(o => o.url === override.url); override.sha1 = refOverride?.sha1; delete override.ref; } - contextEntry.overriddenUrls[override.url] = true; } break; } @@ -122,11 +120,11 @@ export class TraceModel { } appendResource(event: trace.NetworkResourceTraceEvent) { - const contextEntry = this.contextEntries.get(event.contextId)!; - let responseEvents = contextEntry.resourcesByUrl[event.url]; + const contextResources = this.contextResources.get(event.contextId)!; + let responseEvents = contextResources.get(event.url); if (!responseEvents) { responseEvents = []; - contextEntry.resourcesByUrl[event.url] = responseEvents; + contextResources.set(event.url, responseEvents); } responseEvents.push({ frameId: event.frameId, resourceId: event.resourceId }); this.resourceById.set(event.resourceId, event); @@ -159,7 +157,7 @@ export class TraceModel { const frameSnapshots = pageEntry.snapshotsByFrameId[frameId]; for (let index = 0; index < frameSnapshots.length; index++) { if (frameSnapshots[index].snapshotId === snapshotId) - return new FrameSnapshot(contextEntry, frameSnapshots, index); + return new FrameSnapshot(frameId, contextEntry, this.contextResources.get(contextEntry.created.contextId)!, frameSnapshots, index); } } @@ -172,7 +170,7 @@ export class TraceModel { if (timestamp && snapshot.timestamp <= timestamp) snapshotIndex = index; } - return snapshotIndex >= 0 ? new FrameSnapshot(contextEntry, frameSnapshots, snapshotIndex) : undefined; + return snapshotIndex >= 0 ? new FrameSnapshot(frameId, contextEntry, this.contextResources.get(contextEntry.created.contextId)!, frameSnapshots, snapshotIndex) : undefined; } } @@ -183,10 +181,10 @@ export type ContextEntry = { created: trace.ContextCreatedTraceEvent; destroyed: trace.ContextDestroyedTraceEvent; pages: PageEntry[]; - resourcesByUrl: { [key: string]: { resourceId: string, frameId: string }[] }; - overriddenUrls: { [key: string]: boolean }; } +export type ContextResources = Map; + export type InterestingPageEvent = trace.DialogOpenedEvent | trace.DialogClosedEvent | trace.NavigationEvent | trace.LoadEvent; export type PageEntry = {