Skip to content

Commit d117d0b

Browse files
authored
feat(scopes): make page a scope (#4300)
1 parent 1255289 commit d117d0b

File tree

7 files changed

+33
-22
lines changed

7 files changed

+33
-22
lines changed

src/dispatchers/dispatcher.ts

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export class Dispatcher<Type, Initializer> extends EventEmitter implements chann
7373

7474
(object as any)[dispatcherSymbol] = this;
7575
if (this._parent)
76-
this._connection.sendMessageToClient(this._parent._guid, '__create__', { type, initializer, guid }, !!isScope);
76+
this._connection.sendMessageToClient(this._parent._guid, '__create__', { type, initializer, guid });
7777
}
7878

7979
_dispatchEvent(method: string, params: Dispatcher<any, any> | any = {}) {
@@ -126,9 +126,8 @@ export class DispatcherConnection {
126126
private _validateParams: (type: string, method: string, params: any) => any;
127127
private _validateMetadata: (metadata: any) => any;
128128

129-
sendMessageToClient(guid: string, method: string, params: any, disallowDispatchers?: boolean) {
130-
const allowDispatchers = !disallowDispatchers;
131-
this.onmessage({ guid, method, params: this._replaceDispatchersWithGuids(params, allowDispatchers) });
129+
sendMessageToClient(guid: string, method: string, params: any) {
130+
this.onmessage({ guid, method, params: this._replaceDispatchersWithGuids(params) });
132131
}
133132

134133
constructor() {
@@ -178,26 +177,23 @@ export class DispatcherConnection {
178177
try {
179178
const validated = this._validateParams(dispatcher._type, method, params);
180179
const result = await (dispatcher as any)[method](validated, this._validateMetadata(metadata));
181-
this.onmessage({ id, result: this._replaceDispatchersWithGuids(result, true) });
180+
this.onmessage({ id, result: this._replaceDispatchersWithGuids(result) });
182181
} catch (e) {
183182
this.onmessage({ id, error: serializeError(e) });
184183
}
185184
}
186185

187-
private _replaceDispatchersWithGuids(payload: any, allowDispatchers: boolean): any {
186+
private _replaceDispatchersWithGuids(payload: any): any {
188187
if (!payload)
189188
return payload;
190-
if (payload instanceof Dispatcher) {
191-
if (!allowDispatchers)
192-
throw new Error(`Channels are not allowed in the scope's initialzier`);
189+
if (payload instanceof Dispatcher)
193190
return { guid: payload._guid };
194-
}
195191
if (Array.isArray(payload))
196-
return payload.map(p => this._replaceDispatchersWithGuids(p, allowDispatchers));
192+
return payload.map(p => this._replaceDispatchersWithGuids(p));
197193
if (typeof payload === 'object') {
198194
const result: any = {};
199195
for (const key of Object.keys(payload))
200-
result[key] = this._replaceDispatchersWithGuids(payload[key], allowDispatchers);
196+
result[key] = this._replaceDispatchersWithGuids(payload[key]);
201197
return result;
202198
}
203199
return payload;

src/dispatchers/pageDispatcher.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,17 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageInitializer> i
4343
videoRelativePath: page._video ? page._video._relativePath : undefined,
4444
viewportSize: page.viewportSize() || undefined,
4545
isClosed: page.isClosed()
46-
});
46+
}, true);
4747
this._page = page;
48-
page.on(Page.Events.Close, () => this._dispatchEvent('close'));
48+
page.on(Page.Events.Close, () => {
49+
this._dispatchEvent('close');
50+
this._dispose();
51+
});
4952
page.on(Page.Events.Console, message => this._dispatchEvent('console', { message: new ConsoleMessageDispatcher(this._scope, message) }));
5053
page.on(Page.Events.Crash, () => this._dispatchEvent('crash'));
5154
page.on(Page.Events.DOMContentLoaded, () => this._dispatchEvent('domcontentloaded'));
5255
page.on(Page.Events.Dialog, dialog => this._dispatchEvent('dialog', { dialog: new DialogDispatcher(this._scope, dialog) }));
53-
page.on(Page.Events.Download, download => this._dispatchEvent('download', { download: new DownloadDispatcher(this._scope, download) }));
56+
page.on(Page.Events.Download, download => this._dispatchEvent('download', { download: new DownloadDispatcher(scope, download) }));
5457
this._page.on(Page.Events.FileChooser, (fileChooser: FileChooser) => this._dispatchEvent('fileChooser', {
5558
element: new ElementHandleDispatcher(this._scope, fileChooser.element()),
5659
isMultiple: fileChooser.isMultiple()

src/server/frames.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,8 @@ export class FrameManager {
296296
this.removeChildFramesRecursively(frame);
297297
frame._onDetached();
298298
this._frames.delete(frame._id);
299-
this._page.emit(Page.Events.FrameDetached, frame);
299+
if (!this._page.isClosed())
300+
this._page.emit(Page.Events.FrameDetached, frame);
300301
}
301302

302303
private _inflightRequestFinished(request: network.Request) {

src/utils/errors.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class CustomError extends Error {
2626
export class TimeoutError extends CustomError {}
2727

2828
export const kBrowserClosedError = 'Browser has been closed';
29-
export const kBrowserOrContextClosedError = 'Target browser or context has been closed';
29+
export const kBrowserOrContextClosedError = 'Target page, context or browser has been closed';
3030

3131
export function isSafeCloseError(error: Error) {
3232
return error.message.endsWith(kBrowserClosedError) || error.message.endsWith(kBrowserOrContextClosedError);

test/channels.spec.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,10 @@ it('should scope context handles', async ({browser, server}) => {
6363
{ _guid: 'Browser', objects: [
6464
{ _guid: 'BrowserContext', objects: [
6565
{ _guid: 'Frame', objects: [] },
66-
{ _guid: 'Page', objects: [] },
67-
{ _guid: 'Request', objects: [] },
68-
{ _guid: 'Response', objects: [] },
66+
{ _guid: 'Page', objects: [
67+
{ _guid: 'Request', objects: [] },
68+
{ _guid: 'Response', objects: [] },
69+
]},
6970
]},
7071
] },
7172
] },

test/chromium/session.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ describe('session', (suite, { browserName }) => {
7575
} catch (e) {
7676
error = e;
7777
}
78-
expect(error.message).toContain('Target browser or context has been closed');
78+
expect(error.message).toContain('Target page, context or browser has been closed');
7979
});
8080

8181
it('should throw nice errors', async function({page}) {

test/page-close.spec.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { it } from './fixtures';
18+
import { it, expect } from './fixtures';
1919

2020
it('should close page with active dialog', (test, { browserName, platform }) => {
2121
test.fixme(browserName === 'webkit' && platform === 'darwin', 'WebKit hangs on a Mac');
@@ -43,3 +43,13 @@ it('should access page after beforeunload', (test, { browserName }) => {
4343
await dialog.dismiss();
4444
await page.evaluate(() => document.title);
4545
});
46+
47+
it('should not accept after close', (test, { browserName, platform }) => {
48+
test.fixme(browserName === 'webkit' && platform === 'darwin', 'WebKit hangs on a Mac');
49+
}, async ({page}) => {
50+
page.evaluate(() => alert()).catch(() => {});
51+
const dialog = await page.waitForEvent('dialog');
52+
await page.close();
53+
const e = await dialog.dismiss().catch(e => e);
54+
expect(e.message).toContain('Target page, context or browser has been closed');
55+
});

0 commit comments

Comments
 (0)