chore: fix bidi/core navigation (#11878)

This commit is contained in:
jrandolf 2024-02-08 15:22:17 +01:00 committed by GitHub
parent 8a9fb1051a
commit bfcd65d2f7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 65 additions and 37 deletions

View File

@ -210,7 +210,16 @@ export class BrowsingContext extends EventEmitter<{
}
this.#url = info.url;
this.#requests.clear();
for (const [id, request] of this.#requests) {
if (request.disposed) {
this.#requests.delete(id);
}
}
// If the navigation hasn't finished, then this is nested navigation. The
// current navigation will handle this.
if (this.#navigation !== undefined && !this.#navigation.disposed) {
return;
}
// Note the navigation ID is null for this event.
this.#navigation = Navigation.from(this);
@ -232,10 +241,10 @@ export class BrowsingContext extends EventEmitter<{
if (event.context !== this.id) {
return;
}
// Means the request is a redirect. This is handled in Request.
if (this.#requests.has(event.request.request)) {
return;
}
const request = Request.from(this, event);
this.#requests.set(request.id, request);
this.emit('request', {request});
@ -353,33 +362,23 @@ export class BrowsingContext extends EventEmitter<{
async navigate(
url: string,
wait?: Bidi.BrowsingContext.ReadinessState
): Promise<Navigation> {
): Promise<void> {
await this.#session.send('browsingContext.navigate', {
context: this.id,
url,
wait,
});
return await new Promise(resolve => {
this.once('navigation', ({navigation}) => {
resolve(navigation);
});
});
}
@throwIfDisposed<BrowsingContext>(context => {
// SAFETY: Disposal implies this exists.
return context.#reason!;
})
async reload(options: ReloadOptions = {}): Promise<Navigation> {
async reload(options: ReloadOptions = {}): Promise<void> {
await this.#session.send('browsingContext.reload', {
context: this.id,
...options,
});
return await new Promise(resolve => {
this.once('navigation', ({navigation}) => {
resolve(navigation);
});
});
}
@throwIfDisposed<BrowsingContext>(context => {

View File

@ -41,9 +41,10 @@ export class Navigation extends EventEmitter<{
// keep-sorted start
#request: Request | undefined;
#navigation: Navigation | undefined;
readonly #browsingContext: BrowsingContext;
readonly #disposables = new DisposableStack();
readonly #id = new Deferred<string>();
readonly #id = new Deferred<string | null>();
// keep-sorted end
private constructor(context: BrowsingContext) {
@ -65,31 +66,48 @@ export class Navigation extends EventEmitter<{
this.dispose();
});
this.#browsingContext.on('request', ({request}) => {
if (request.navigation === this.#id.value()) {
this.#request = request;
this.emit('request', request);
browsingContextEmitter.on('request', ({request}) => {
if (
request.navigation === undefined ||
this.#request !== undefined ||
// If a request with a navigation ID comes in, then the navigation ID is
// for this navigation.
!this.#matches(request.navigation)
) {
return;
}
this.#request = request;
this.emit('request', request);
});
const sessionEmitter = this.#disposables.use(
new EventEmitter(this.#session)
);
// To get the navigation ID if any.
sessionEmitter.on('browsingContext.navigationStarted', info => {
if (
info.context !== this.#browsingContext.id ||
this.#navigation !== undefined
) {
return;
}
this.#navigation = Navigation.from(this.#browsingContext);
});
for (const eventName of [
'browsingContext.domContentLoaded',
'browsingContext.load',
] as const) {
sessionEmitter.on(eventName, info => {
if (info.context !== this.#browsingContext.id) {
if (
info.context !== this.#browsingContext.id ||
info.navigation === null ||
!this.#matches(info.navigation)
) {
return;
}
if (!info.navigation) {
return;
}
if (!this.#id.resolved()) {
this.#id.resolve(info.navigation);
}
this.dispose();
});
}
@ -99,18 +117,15 @@ export class Navigation extends EventEmitter<{
['browsingContext.navigationAborted', 'aborted'],
] as const) {
sessionEmitter.on(eventName, info => {
if (info.context !== this.#browsingContext.id) {
return;
}
if (!info.navigation) {
return;
}
if (!this.#id.resolved()) {
this.#id.resolve(info.navigation);
}
if (this.#id.value() !== info.navigation) {
if (
info.context !== this.#browsingContext.id ||
// Note we don't check if `navigation` is null since `null` means the
// fragment navigated.
!this.#matches(info.navigation)
) {
return;
}
this.emit(event, {
url: info.url,
timestamp: new Date(info.timestamp),
@ -120,6 +135,17 @@ export class Navigation extends EventEmitter<{
}
}
#matches(navigation: string | null): boolean {
if (this.#navigation !== undefined && !this.#navigation.disposed) {
return false;
}
if (!this.#id.resolved()) {
this.#id.resolve(navigation);
return true;
}
return this.#id.value() === navigation;
}
// keep-sorted start block=yes
get #session() {
return this.#browsingContext.userContext.browser.session;
@ -130,6 +156,9 @@ export class Navigation extends EventEmitter<{
get request(): Request | undefined {
return this.#request;
}
get navigation(): Navigation | undefined {
return this.#navigation;
}
// keep-sorted end
@inertIfDisposed