feat(rpc): make sure filechooser is only intercepted when needed (#3482)

So that user can choose a file manually in headful mode.
This commit is contained in:
Dmitry Gozman 2020-08-14 18:24:36 -07:00 committed by GitHub
parent 0c798f0572
commit 244c2f37b6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 55 additions and 3 deletions

View file

@ -536,6 +536,22 @@ export class Page extends ChannelOwner<PageChannel, PageInitializer> {
return this;
}
addListener(event: string | symbol, listener: Listener): this {
if (event === Events.Page.FileChooser) {
if (!this.listenerCount(event))
this._channel.setFileChooserInterceptedNoReply({ intercepted: true });
}
super.addListener(event, listener);
return this;
}
off(event: string | symbol, listener: Listener): this {
super.off(event, listener);
if (event === Events.Page.FileChooser && !this.listenerCount(event))
this._channel.setFileChooserInterceptedNoReply({ intercepted: false });
return this;
}
removeListener(event: string | symbol, listener: Listener): this {
super.removeListener(event, listener);
if (event === Events.Page.FileChooser && !this.listenerCount(event))

View file

@ -36,6 +36,7 @@ import { CRCoverage } from '../../chromium/crCoverage';
export class PageDispatcher extends Dispatcher<Page, PageInitializer> implements PageChannel {
private _page: Page;
private _onFileChooser: (fileChooser: FileChooser) => void;
constructor(scope: DispatcherScope, page: Page) {
// TODO: theoretically, there could be more than one frame already.
@ -52,10 +53,11 @@ export class PageDispatcher extends Dispatcher<Page, PageInitializer> implements
page.on(Events.Page.DOMContentLoaded, () => this._dispatchEvent('domcontentloaded'));
page.on(Events.Page.Dialog, dialog => this._dispatchEvent('dialog', { dialog: new DialogDispatcher(this._scope, dialog) }));
page.on(Events.Page.Download, dialog => this._dispatchEvent('download', { download: new DownloadDispatcher(this._scope, dialog) }));
page.on(Events.Page.FileChooser, (fileChooser: FileChooser) => this._dispatchEvent('fileChooser', {
// We add this listener lazily, to avoid intercepting file chooser when noone listens.
this._onFileChooser = fileChooser => this._dispatchEvent('fileChooser', {
element: new ElementHandleDispatcher(this._scope, fileChooser.element()),
isMultiple: fileChooser.isMultiple()
}));
});
page.on(Events.Page.FrameAttached, frame => this._onFrameAttached(frame));
page.on(Events.Page.FrameDetached, frame => this._onFrameDetached(frame));
page.on(Events.Page.Load, () => this._dispatchEvent('load'));
@ -141,6 +143,10 @@ export class PageDispatcher extends Dispatcher<Page, PageInitializer> implements
}
async setFileChooserInterceptedNoReply(params: { intercepted: boolean }) {
if (params.intercepted)
this._page.on(Events.Page.FileChooser, this._onFileChooser);
else
this._page.removeListener(Events.Page.FileChooser, this._onFileChooser);
}
async title() {

View file

@ -54,7 +54,7 @@ it('should set from memory', async({page}) => {
expect(await page.$eval('input', input => input.files[0].name)).toBe('test.txt');
});
it('should emit event', async({page, server}) => {
it('should emit event once', async({page, server}) => {
await page.setContent(`<input type=file>`);
const [chooser] = await Promise.all([
new Promise(f => page.once('filechooser', f)),
@ -63,6 +63,36 @@ it('should emit event', async({page, server}) => {
expect(chooser).toBeTruthy();
});
it('should emit event on/off', async({page, server}) => {
await page.setContent(`<input type=file>`);
const [chooser] = await Promise.all([
new Promise(f => {
const listener = chooser => {
page.off('filechooser', listener);
f(chooser);
}
page.on('filechooser', listener);
}),
page.click('input'),
]);
expect(chooser).toBeTruthy();
});
it('should emit event addListener/removeListener', async({page, server}) => {
await page.setContent(`<input type=file>`);
const [chooser] = await Promise.all([
new Promise(f => {
const listener = chooser => {
page.removeListener('filechooser', listener);
f(chooser);
}
page.addListener('filechooser', listener);
}),
page.click('input'),
]);
expect(chooser).toBeTruthy();
});
it('should work when file input is attached to DOM', async({page, server}) => {
await page.setContent(`<input type=file>`);
const [chooser] = await Promise.all([