Skip to content

Commit 77d8e2e

Browse files
committed
fix(puppet): extract navigation loader
1 parent 67bc0b6 commit 77d8e2e

File tree

16 files changed

+339
-204
lines changed

16 files changed

+339
-204
lines changed

commons/Resolvable.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,15 @@ export default class Resolvable<T = any> implements IResolvablePromise<T>, Promi
3131

3232
public resolve(value: T | PromiseLike<T>): void {
3333
if (this.isResolved) return;
34-
this.isResolved = true;
3534
clearTimeout(this.timeout);
3635
this.resolveFn(value);
3736
Promise.resolve(value)
3837
// eslint-disable-next-line promise/always-return
3938
.then(x => {
39+
this.isResolved = true;
4040
this.resolved = x;
4141
})
42-
.catch(() => null);
42+
.catch(this.reject);
4343
}
4444

4545
public reject(error: Error): void {

core/lib/DetachedTabState.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export default class DetachedTabState {
5656
);
5757
await Promise.all([
5858
page.mainFrame.waitForLoader(loader.loaderId),
59-
page.mainFrame.waitForLoad('DOMContentLoaded'),
59+
page.mainFrame.waitForLifecycleEvent('DOMContentLoaded', loader.loaderId),
6060
InjectedScripts.installDetachedScripts(page),
6161
]);
6262
await InjectedScripts.restoreDom(page, this.domChanges);

core/lib/FrameEnvironment.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export default class FrameEnvironment {
6060
}
6161

6262
public get isAttached(): boolean {
63-
return this.puppetFrame.isAttached();
63+
return this.puppetFrame.isAttached;
6464
}
6565

6666
public get securityOrigin(): string {
@@ -442,8 +442,10 @@ b) Use the UserProfile feature to set cookies for 1 or more domains before they'
442442

443443
for (const [event, url, timestamp] of loadEvents) {
444444
const incomingStatus = pageStateToLoadStatus[event];
445-
446-
this.navigations.onLoadStateChanged(incomingStatus, url, null, new Date(timestamp));
445+
// only record the content paint
446+
if (incomingStatus === LoadStatus.ContentPaint) {
447+
this.navigations.onLoadStateChanged(incomingStatus, url, null, new Date(timestamp));
448+
}
447449
}
448450

449451
this.sessionState.captureDomEvents(
@@ -571,7 +573,11 @@ b) Use the UserProfile feature to set cookies for 1 or more domains before they'
571573
else if (lowerEventName === 'domcontentloaded') status = LoadStatus.DomContentLoaded;
572574

573575
if (status) {
574-
this.navigations.onLoadStateChanged(status, event.frame.url, event.loaderId);
576+
this.navigations.onLoadStateChanged(
577+
status,
578+
event.loader.url ?? event.frame.url,
579+
event.loader.id,
580+
);
575581
}
576582
}
577583

core/lib/FrameNavigations.ts

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export default class FrameNavigations extends TypedEventEmitter<IFrameNavigation
3737

3838
public logger: IBoundLog;
3939

40-
private loaderIds = new Set<string>();
40+
private historyByLoaderId: { [loaderId: string]: INavigation } = {};
4141

4242
private nextNavigationReason: { url: string; reason: NavigationReason };
4343

@@ -73,7 +73,7 @@ export default class FrameNavigations extends TypedEventEmitter<IFrameNavigation
7373
resourceId: createPromise(),
7474
browserRequestId,
7575
};
76-
if (loaderId) this.loaderIds.add(loaderId);
76+
if (loaderId) this.historyByLoaderId[loaderId] = nextTop;
7777

7878
this.checkStoredNavigationReason(nextTop, url);
7979

@@ -149,7 +149,7 @@ export default class FrameNavigations extends TypedEventEmitter<IFrameNavigation
149149
else if (
150150
top.stateChanges.has(LocationStatus.HttpRequested) === true &&
151151
// add new entries for redirects
152-
(!this.loaderIds.has(loaderId) || redirectedFromUrl)
152+
(!this.historyByLoaderId[loaderId] || redirectedFromUrl)
153153
) {
154154
this.onNavigationRequested(reason, url, lastCommandId, loaderId, browserRequestId);
155155
}
@@ -184,7 +184,7 @@ export default class FrameNavigations extends TypedEventEmitter<IFrameNavigation
184184
}
185185

186186
public onLoadStateChanged(
187-
incomingStatus: LoadStatus.DomContentLoaded | LoadStatus.Load,
187+
incomingStatus: LoadStatus.DomContentLoaded | LoadStatus.Load | LoadStatus.ContentPaint,
188188
url: string,
189189
loaderId: string,
190190
statusChangeDate?: Date,
@@ -194,7 +194,8 @@ export default class FrameNavigations extends TypedEventEmitter<IFrameNavigation
194194
if (!loaderId) {
195195
for (let i = this.history.length - 1; i >= 0; i -= 1) {
196196
const nav = this.history[i];
197-
if (nav && nav.finalUrl === url && nav.stateChanges.has(LoadStatus.HttpResponded)) {
197+
const isUrlMatch = nav.finalUrl === url || nav.requestedUrl === url;
198+
if (isUrlMatch && nav.stateChanges.has(LoadStatus.HttpResponded)) {
198199
loaderId = nav.loaderId;
199200
break;
200201
}
@@ -219,7 +220,8 @@ export default class FrameNavigations extends TypedEventEmitter<IFrameNavigation
219220

220221
public assignLoaderId(navigation: INavigation, loaderId: string, url?: string): void {
221222
if (!loaderId) return;
222-
this.loaderIds.add(loaderId);
223+
224+
this.historyByLoaderId[loaderId] = navigation;
223225
navigation.loaderId = loaderId;
224226
if (
225227
url &&
@@ -232,13 +234,17 @@ export default class FrameNavigations extends TypedEventEmitter<IFrameNavigation
232234

233235
public getLastLoadedNavigation(): INavigation {
234236
let navigation: INavigation;
237+
let hasInPageNav = false;
235238
for (let i = this.history.length - 1; i >= 0; i -= 1) {
236239
navigation = this.history[i];
237-
if (
238-
navigation.stateChanges.has(LoadStatus.DomContentLoaded) &&
239-
navigation.navigationReason !== 'inPage' &&
240-
!!navigation.finalUrl
241-
) {
240+
if (navigation.navigationReason === 'inPage') {
241+
hasInPageNav = true;
242+
continue;
243+
}
244+
if (!navigation.finalUrl || !navigation.stateChanges.has(LoadStatus.HttpResponded)) continue;
245+
246+
// if we have an in-page nav, return the first non "inPage" url. Otherwise, use if DomContentLoaded was triggered
247+
if (hasInPageNav || navigation.stateChanges.has(LoadStatus.DomContentLoaded)) {
242248
return navigation;
243249
}
244250
}
@@ -257,18 +263,7 @@ export default class FrameNavigations extends TypedEventEmitter<IFrameNavigation
257263
}
258264

259265
private findMatchingNavigation(loaderId: string): INavigation {
260-
const navigation = this.top;
261-
if (!navigation) return undefined;
262-
if (loaderId && navigation.loaderId && navigation.loaderId !== loaderId) {
263-
// find the right loader id
264-
for (let i = this.history.length - 1; i >= 0; i -= 1) {
265-
const nav = this.history[i];
266-
if (nav && nav.loaderId === loaderId) {
267-
return nav;
268-
}
269-
}
270-
}
271-
return navigation;
266+
return this.historyByLoaderId[loaderId] ?? this.top;
272267
}
273268

274269
private recordRedirect(requestedUrl: string, finalUrl: string, loaderId: string): INavigation {
@@ -281,6 +276,7 @@ export default class FrameNavigations extends TypedEventEmitter<IFrameNavigation
281276
}
282277

283278
// find the right loader id
279+
// NOTE: loop through history since loaderId is reused across requests in a redirect
284280
for (let i = this.history.length - 1; i >= 0; i -= 1) {
285281
const navigation = this.history[i];
286282
if (navigation && navigation.loaderId === loaderId) {
@@ -301,16 +297,24 @@ export default class FrameNavigations extends TypedEventEmitter<IFrameNavigation
301297
loaderId?: string,
302298
statusChangeDate?: Date,
303299
): void {
300+
this.logger.info('FrameNavigations.changeNavigationState', {
301+
newStatus,
302+
loaderId,
303+
statusChangeDate,
304+
});
304305
const navigation = this.findMatchingNavigation(loaderId);
305306
if (!navigation) return;
307+
if (!navigation.loaderId && loaderId) {
308+
navigation.loaderId = loaderId;
309+
this.historyByLoaderId[loaderId] = navigation;
310+
}
306311
if (navigation.stateChanges.has(newStatus)) {
307312
if (statusChangeDate && statusChangeDate < navigation.stateChanges.get(newStatus)) {
308313
navigation.stateChanges.set(newStatus, statusChangeDate);
309314
}
310315
return;
311316
}
312317
this.recordStatusChange(navigation, newStatus, statusChangeDate);
313-
if (loaderId) this.loaderIds.add(loaderId);
314318
}
315319

316320
private recordStatusChange(

core/lib/Session.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,7 @@ export default class Session extends TypedEventEmitter<{
432432
) {
433433
const tab = Tab.create(this, page, false, parentTab, {
434434
...openParams,
435-
loaderId: page.mainFrame.isDefaultUrl ? null : page.mainFrame.activeLoaderId,
435+
loaderId: page.mainFrame.isDefaultUrl ? null : page.mainFrame.activeLoader.id,
436436
});
437437
this.sessionState.captureTab(tab.id, page.id, page.devtoolsSession.id, parentTab.id);
438438
this.registerTab(tab, page);

core/lib/Tab.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ export default class Tab extends TypedEventEmitter<ITabEventParams> {
173173
resource.request?.url,
174174
resource.response?.url,
175175
);
176-
if (frame) {
176+
if (frame && !resource.isRedirect) {
177177
frame.navigations.onResourceLoaded(resource.id, resource.response?.statusCode, error);
178178
return true;
179179
}
@@ -366,8 +366,11 @@ export default class Tab extends TypedEventEmitter<ITabEventParams> {
366366
const timeoutMessage = `Timeout waiting for "tab.goto(${url})"`;
367367

368368
const timer = new Timer(timeoutMs, this.waitTimeouts);
369-
await timer.waitForPromise(this.puppetPage.navigate(formattedUrl), timeoutMessage);
370-
this.navigations.assignLoaderId(navigation, this.puppetPage.mainFrame.activeLoaderId);
369+
const loader = await timer.waitForPromise(
370+
this.puppetPage.navigate(formattedUrl),
371+
timeoutMessage,
372+
);
373+
this.navigations.assignLoaderId(navigation, loader.loaderId);
371374

372375
const resource = await timer.waitForPromise(
373376
this.navigationsObserver.waitForNavigationResourceId(),
@@ -384,7 +387,11 @@ export default class Tab extends TypedEventEmitter<ITabEventParams> {
384387
null,
385388
);
386389
const backUrl = await this.puppetPage.goBack();
387-
this.navigations.assignLoaderId(navigation, this.puppetPage.mainFrame.activeLoaderId, backUrl);
390+
this.navigations.assignLoaderId(
391+
navigation,
392+
this.puppetPage.mainFrame.activeLoader?.id,
393+
backUrl,
394+
);
388395

389396
await this.navigationsObserver.waitForLoad('PaintingStable', { timeoutMs });
390397
return this.url;
@@ -398,7 +405,7 @@ export default class Tab extends TypedEventEmitter<ITabEventParams> {
398405
null,
399406
);
400407
const url = await this.puppetPage.goForward();
401-
this.navigations.assignLoaderId(navigation, this.puppetPage.mainFrame.activeLoaderId, url);
408+
this.navigations.assignLoaderId(navigation, this.puppetPage.mainFrame.activeLoader?.id, url);
402409
await this.navigationsObserver.waitForLoad('PaintingStable', { timeoutMs });
403410
return this.url;
404411
}
@@ -415,7 +422,7 @@ export default class Tab extends TypedEventEmitter<ITabEventParams> {
415422
const timeoutMessage = `Timeout waiting for "tab.reload()"`;
416423

417424
await timer.waitForPromise(this.puppetPage.reload(), timeoutMessage);
418-
this.navigations.assignLoaderId(navigation, this.puppetPage.mainFrame.activeLoaderId);
425+
this.navigations.assignLoaderId(navigation, this.puppetPage.mainFrame.activeLoader?.id);
419426

420427
const resource = await timer.waitForPromise(
421428
this.navigationsObserver.waitForNavigationResourceId(),
@@ -462,7 +469,7 @@ export default class Tab extends TypedEventEmitter<ITabEventParams> {
462469
await newTab.navigations.waitOn('navigation-requested', null, timeoutMs).catch(() => null);
463470
}
464471

465-
await newTab.mainFrameEnvironment.navigationsObserver.waitForNavigationResourceId();
472+
await newTab.navigationsObserver.waitForNavigationResourceId();
466473
return newTab;
467474
}
468475

core/test/domRecorder.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,8 @@ function sort() {
216216
const state = tab.sessionState;
217217

218218
expect(tab.puppetPage.frames).toHaveLength(4);
219-
await tab.puppetPage.frames[1].waitForLoad();
220-
await tab.puppetPage.frames[2].waitForLoad();
219+
await tab.puppetPage.frames[1].waitForLifecycleEvent('load');
220+
await tab.puppetPage.frames[2].waitForLifecycleEvent('load');
221221
// await tab.puppetPage.frames[3].waitOn('frame-lifecycle', f => f.name === 'load');
222222

223223
await state.db.flush();

0 commit comments

Comments
 (0)