Skip to content

Commit fbae295

Browse files
authored
fix(har): save popup's main request/response (#6562)
This migrates server side code from networks events on the Page to network events on the BrowserContext.
1 parent e87fbfc commit fbae295

File tree

5 files changed

+84
-58
lines changed

5 files changed

+84
-58
lines changed

src/server/frames.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,6 @@ export class FrameManager {
271271
if (response.request()._isFavicon)
272272
return;
273273
this._responses.push(response);
274-
this._page.emit(Page.Events.Response, response);
275274
this._page._browserContext.emit(BrowserContext.Events.Response, response);
276275
}
277276

@@ -280,7 +279,6 @@ export class FrameManager {
280279
if (request._isFavicon)
281280
return;
282281
this._page._browserContext.emit(BrowserContext.Events.RequestFinished, request);
283-
this._page.emit(Page.Events.RequestFinished, request);
284282
}
285283

286284
requestFailed(request: network.Request, canceled: boolean) {
@@ -295,7 +293,6 @@ export class FrameManager {
295293
if (request._isFavicon)
296294
return;
297295
this._page._browserContext.emit(BrowserContext.Events.RequestFailed, request);
298-
this._page.emit(Page.Events.RequestFailed, request);
299296
}
300297

301298
removeChildFramesRecursively(frame: Frame) {

src/server/page.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,6 @@ export class Page extends SdkObject {
104104
// Can't use just 'error' due to node.js special treatment of error events.
105105
// @see https://nodejs.org/api/events.html#events_error_events
106106
PageError: 'pageerror',
107-
Request: 'request',
108-
Response: 'response',
109-
RequestFailed: 'requestfailed',
110-
RequestFinished: 'requestfinished',
111107
FrameAttached: 'frameattached',
112108
FrameDetached: 'framedetached',
113109
InternalFrameNavigatedToNewDocument: 'internalframenavigatedtonewdocument',
@@ -411,7 +407,6 @@ export class Page extends SdkObject {
411407
}
412408

413409
_requestStarted(request: network.Request) {
414-
this.emit(Page.Events.Request, request);
415410
const route = request._route();
416411
if (!route)
417412
return;

src/server/snapshot/snapshotter.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export class Snapshotter {
6767
// Replay resources loaded in all pages.
6868
for (const page of this._context.pages()) {
6969
for (const response of page._frameManager._responses)
70-
this._saveResource(page, response).catch(e => debugLogger.log('error', e));
70+
this._saveResource(response).catch(e => debugLogger.log('error', e));
7171
}
7272
}
7373

@@ -80,6 +80,9 @@ export class Snapshotter {
8080
this._onPage(page);
8181
this._eventListeners = [
8282
helper.addEventListener(this._context, BrowserContext.Events.Page, this._onPage.bind(this)),
83+
helper.addEventListener(this._context, BrowserContext.Events.Response, (response: network.Response) => {
84+
this._saveResource(response).catch(e => debugLogger.log('error', e));
85+
}),
8386
];
8487

8588
const initScript = `(${frameSnapshotStreamer})("${this._snapshotStreamer}")`;
@@ -149,13 +152,9 @@ export class Snapshotter {
149152
for (const frame of page.frames())
150153
this._annotateFrameHierarchy(frame);
151154
this._eventListeners.push(helper.addEventListener(page, Page.Events.FrameAttached, frame => this._annotateFrameHierarchy(frame)));
152-
153-
this._eventListeners.push(helper.addEventListener(page, Page.Events.Response, (response: network.Response) => {
154-
this._saveResource(page, response).catch(e => debugLogger.log('error', e));
155-
}));
156155
}
157156

158-
private async _saveResource(page: Page, response: network.Response) {
157+
private async _saveResource(response: network.Response) {
159158
if (!this._started)
160159
return;
161160
const isRedirect = response.status() >= 300 && response.status() <= 399;
@@ -198,7 +197,7 @@ export class Snapshotter {
198197
}
199198

200199
const resource: ResourceSnapshot = {
201-
pageId: page.guid,
200+
pageId: response.frame()._page.guid,
202201
frameId: response.frame().guid,
203202
resourceId: 'resource@' + createGuid(),
204203
url,

src/server/supplements/har/harTracer.ts

Lines changed: 55 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -52,48 +52,58 @@ export class HarTracer {
5252
pages: [],
5353
entries: []
5454
};
55-
context.on(BrowserContext.Events.Page, page => this._onPage(page));
55+
context.on(BrowserContext.Events.Page, (page: Page) => this._ensurePageEntry(page));
56+
context.on(BrowserContext.Events.Request, (request: network.Request) => this._onRequest(request));
57+
context.on(BrowserContext.Events.Response, (response: network.Response) => this._onResponse(response));
5658
}
5759

58-
private _onPage(page: Page) {
59-
const pageEntry: har.Page = {
60-
startedDateTime: new Date(),
61-
id: `page_${this._lastPage++}`,
62-
title: '',
63-
pageTimings: {
64-
onContentLoad: -1,
65-
onLoad: -1,
66-
},
67-
};
68-
this._pageEntries.set(page, pageEntry);
69-
this._log.pages.push(pageEntry);
70-
page.on(Page.Events.Request, (request: network.Request) => this._onRequest(page, request));
71-
page.on(Page.Events.Response, (response: network.Response) => this._onResponse(page, response));
60+
private _ensurePageEntry(page: Page) {
61+
let pageEntry = this._pageEntries.get(page);
62+
if (!pageEntry) {
63+
page.on(Page.Events.DOMContentLoaded, () => this._onDOMContentLoaded(page));
64+
page.on(Page.Events.Load, () => this._onLoad(page));
7265

73-
page.on(Page.Events.DOMContentLoaded, () => {
74-
const promise = page.mainFrame().evaluateExpression(String(() => {
75-
return {
76-
title: document.title,
77-
domContentLoaded: performance.timing.domContentLoadedEventStart,
78-
};
79-
}), true, undefined, 'utility').then(result => {
80-
pageEntry.title = result.title;
81-
pageEntry.pageTimings.onContentLoad = result.domContentLoaded;
82-
}).catch(() => {});
83-
this._addBarrier(page, promise);
84-
});
85-
page.on(Page.Events.Load, () => {
86-
const promise = page.mainFrame().evaluateExpression(String(() => {
87-
return {
88-
title: document.title,
89-
loaded: performance.timing.loadEventStart,
90-
};
91-
}), true, undefined, 'utility').then(result => {
92-
pageEntry.title = result.title;
93-
pageEntry.pageTimings.onLoad = result.loaded;
94-
}).catch(() => {});
95-
this._addBarrier(page, promise);
96-
});
66+
pageEntry = {
67+
startedDateTime: new Date(),
68+
id: `page_${this._lastPage++}`,
69+
title: '',
70+
pageTimings: {
71+
onContentLoad: -1,
72+
onLoad: -1,
73+
},
74+
};
75+
this._pageEntries.set(page, pageEntry);
76+
this._log.pages.push(pageEntry);
77+
}
78+
return pageEntry;
79+
}
80+
81+
private _onDOMContentLoaded(page: Page) {
82+
const pageEntry = this._ensurePageEntry(page);
83+
const promise = page.mainFrame().evaluateExpression(String(() => {
84+
return {
85+
title: document.title,
86+
domContentLoaded: performance.timing.domContentLoadedEventStart,
87+
};
88+
}), true, undefined, 'utility').then(result => {
89+
pageEntry.title = result.title;
90+
pageEntry.pageTimings.onContentLoad = result.domContentLoaded;
91+
}).catch(() => {});
92+
this._addBarrier(page, promise);
93+
}
94+
95+
private _onLoad(page: Page) {
96+
const pageEntry = this._ensurePageEntry(page);
97+
const promise = page.mainFrame().evaluateExpression(String(() => {
98+
return {
99+
title: document.title,
100+
loaded: performance.timing.loadEventStart,
101+
};
102+
}), true, undefined, 'utility').then(result => {
103+
pageEntry.title = result.title;
104+
pageEntry.pageTimings.onLoad = result.loaded;
105+
}).catch(() => {});
106+
this._addBarrier(page, promise);
97107
}
98108

99109
private _addBarrier(page: Page, promise: Promise<void>) {
@@ -107,12 +117,13 @@ export class HarTracer {
107117
this._barrierPromises.add(race);
108118
}
109119

110-
private _onRequest(page: Page, request: network.Request) {
111-
const pageEntry = this._pageEntries.get(page)!;
120+
private _onRequest(request: network.Request) {
121+
const page = request.frame()._page;
112122
const url = network.parsedURL(request.url());
113123
if (!url)
114124
return;
115125

126+
const pageEntry = this._ensurePageEntry(page);
116127
const harEntry: har.Entry = {
117128
pageref: pageEntry.id,
118129
startedDateTime: new Date(),
@@ -160,8 +171,9 @@ export class HarTracer {
160171
this._entries.set(request, harEntry);
161172
}
162173

163-
private _onResponse(page: Page, response: network.Response) {
164-
const pageEntry = this._pageEntries.get(page)!;
174+
private _onResponse(response: network.Response) {
175+
const page = response.frame()._page;
176+
const pageEntry = this._ensurePageEntry(page);
165177
const harEntry = this._entries.get(response.request())!;
166178
// Rewrite provisional headers with actual
167179
const request = response.request();

tests/har.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,3 +258,26 @@ it('should calculate time', async ({ contextFactory, server }, testInfo) => {
258258
const log = await getLog();
259259
expect(log.entries[0].time).toBeGreaterThan(0);
260260
});
261+
262+
it('should have popup requests', async ({ contextFactory, server }, testInfo) => {
263+
const { page, getLog } = await pageWithHar(contextFactory, testInfo);
264+
await page.goto(server.EMPTY_PAGE);
265+
await page.setContent('<a target=_blank rel=noopener href="/one-style.html">yo</a>');
266+
const [popup] = await Promise.all([
267+
page.waitForEvent('popup'),
268+
page.click('a'),
269+
]);
270+
await popup.waitForLoadState();
271+
const log = await getLog();
272+
273+
expect(log.pages.length).toBe(2);
274+
expect(log.pages[0].id).toBe('page_0');
275+
expect(log.pages[1].id).toBe('page_1');
276+
277+
const entries = log.entries.filter(entry => entry.pageref === 'page_1');
278+
expect(entries.length).toBe(2);
279+
expect(entries[0].request.url).toBe(server.PREFIX + '/one-style.html');
280+
expect(entries[0].response.status).toBe(200);
281+
expect(entries[1].request.url).toBe(server.PREFIX + '/one-style.css');
282+
expect(entries[1].response.status).toBe(200);
283+
});

0 commit comments

Comments
 (0)