feat(firefox): support downloads (#1689)

This commit is contained in:
Yury Semikhatsky 2020-04-07 15:01:42 -07:00 committed by GitHub
parent 949dc7b514
commit a7ae205254
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 35 additions and 15 deletions

View File

@ -9,7 +9,7 @@
"main": "index.js",
"playwright": {
"chromium_revision": "754895",
"firefox_revision": "1072",
"firefox_revision": "1074",
"webkit_revision": "1188"
},
"scripts": {

View File

@ -53,7 +53,7 @@ export abstract class BrowserBase extends EventEmitter implements Browser {
this._downloads.set(uuid, download);
}
_downloadFinished(uuid: string, error: string) {
_downloadFinished(uuid: string, error?: string) {
const download = this._downloads.get(uuid);
if (!download)
return;

View File

@ -81,7 +81,7 @@ export class Download {
await util.promisify(fs.unlink)(fileName).catch(e => {});
}
_reportFinished(error: string) {
_reportFinished(error?: string) {
this._failure = error || null;
this._finishedCallback();
}

View File

@ -59,6 +59,8 @@ export class FFBrowser extends BrowserBase {
this._eventListeners = [
helper.addEventListener(this._connection, 'Browser.attachedToTarget', this._onAttachedToTarget.bind(this)),
helper.addEventListener(this._connection, 'Browser.detachedFromTarget', this._onDetachedFromTarget.bind(this)),
helper.addEventListener(this._connection, 'Browser.downloadCreated', this._onDownloadCreated.bind(this)),
helper.addEventListener(this._connection, 'Browser.downloadFinished', this._onDownloadFinished.bind(this)),
];
this._firstPagePromise = new Promise(f => this._firstPageCallback = f);
}
@ -96,7 +98,11 @@ export class FFBrowser extends BrowserBase {
viewport,
locale: options.locale,
timezoneId: options.timezoneId,
removeOnDetach: true
removeOnDetach: true,
downloadOptions: {
behavior: options.acceptDownloads ? 'saveToDisk' : 'cancel',
downloadsDir: this._downloadsPath,
},
});
const context = new FFBrowserContext(this, browserContextId, options);
await context._initialize();
@ -135,6 +141,19 @@ export class FFBrowser extends BrowserBase {
});
}
_onDownloadCreated(payload: Protocol.Browser.downloadCreatedPayload) {
const ffPage = this._ffPages.get(payload.pageTargetId)!;
assert(ffPage);
if (!ffPage)
return;
this._downloadCreated(ffPage._page, payload.uuid, payload.url);
}
_onDownloadFinished(payload: Protocol.Browser.downloadFinishedPayload) {
const error = payload.canceled ? 'canceled' : payload.error;
this._downloadFinished(payload.uuid, error);
}
_disconnect() {
helper.removeEventListeners(this._eventListeners);
this._connection.close();

View File

@ -49,16 +49,17 @@ export class Firefox implements BrowserType<FFBrowser> {
async launch(options: LaunchOptions = {}): Promise<FFBrowser> {
assert(!(options as any).userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead');
const browserServer = await this._launchServer(options, 'local');
const {browserServer, downloadsPath} = await this._launchServer(options, 'local');
const browser = await WebSocketTransport.connect(browserServer.wsEndpoint()!, transport => {
return FFBrowser.connect(transport, false, options.slowMo);
});
browser._ownedServer = browserServer;
browser._downloadsPath = downloadsPath;
return browser;
}
async launchServer(options: LaunchServerOptions = {}): Promise<BrowserServer> {
return await this._launchServer(options, 'server');
return (await this._launchServer(options, 'server')).browserServer;
}
async launchPersistentContext(userDataDir: string, options: LaunchOptions = {}): Promise<BrowserContext> {
@ -66,17 +67,18 @@ export class Firefox implements BrowserType<FFBrowser> {
timeout = 30000,
slowMo = 0,
} = options;
const browserServer = await this._launchServer(options, 'persistent', userDataDir);
const {browserServer, downloadsPath} = await this._launchServer(options, 'persistent', userDataDir);
const browser = await WebSocketTransport.connect(browserServer.wsEndpoint()!, transport => {
return FFBrowser.connect(transport, true, slowMo);
});
browser._ownedServer = browserServer;
browser._downloadsPath = downloadsPath;
await helper.waitWithTimeout(browser._firstPagePromise, 'first page', timeout);
const browserContext = browser._defaultContext;
return browserContext;
}
private async _launchServer(options: LaunchServerOptions, launchType: LaunchType, userDataDir?: string): Promise<BrowserServer> {
private async _launchServer(options: LaunchServerOptions, launchType: LaunchType, userDataDir?: string): Promise<{ browserServer: BrowserServer, downloadsPath: string }> {
const {
ignoreDefaultArgs = false,
args = [],
@ -110,7 +112,7 @@ export class Firefox implements BrowserType<FFBrowser> {
if (!firefoxExecutable)
throw new Error(`No executable path is specified. Pass "executablePath" option directly.`);
const { launchedProcess, gracefullyClose } = await launchProcess({
const { launchedProcess, gracefullyClose, downloadsPath } = await launchProcess({
executablePath: firefoxExecutable,
args: firefoxArguments,
env: os.platform() === 'linux' ? {
@ -146,7 +148,7 @@ export class Firefox implements BrowserType<FFBrowser> {
const webSocketWrapper = launchType === 'server' ? (await WebSocketTransport.connect(innerEndpoint, t => wrapTransportWithWebSocket(t, port))) : new WebSocketWrapper(innerEndpoint, []);
browserWSEndpoint = webSocketWrapper.wsEndpoint;
browserServer = new BrowserServer(launchedProcess, gracefullyClose, webSocketWrapper);
return browserServer;
return {browserServer, downloadsPath};
}
async connect(options: ConnectOptions): Promise<FFBrowser> {

View File

@ -17,9 +17,9 @@
const fs = require('fs');
const path = require('path');
module.exports.describe = function({browserType, CHROMIUM, WEBKIT, FFOX, WIN, MAC}) {
module.exports.describe = function({browserType, defaultBrowserOptions, CHROMIUM, WEBKIT, FFOX, WIN, MAC}) {
describe.fail(FFOX)('Download', function() {
describe('Download', function() {
beforeEach(async(state) => {
state.server.setRoute('/download', (req, res) => {
res.setHeader('Content-Type', 'application/octet-stream');
@ -97,7 +97,7 @@ module.exports.describe = function({browserType, CHROMIUM, WEBKIT, FFOX, WIN, MA
expect(fs.existsSync(path1)).toBeFalsy();
expect(fs.existsSync(path2)).toBeFalsy();
});
it('should delete downloads on browser gone', async ({ server, defaultBrowserOptions }) => {
it('should delete downloads on browser gone', async ({ server }) => {
const browser = await browserType.launch(defaultBrowserOptions);
const page = await browser.newPage({ acceptDownloads: true });
await page.setContent(`<a download=true href="${server.PREFIX}/download">download</a>`);

View File

@ -30,8 +30,7 @@ const BROWSER_CONFIGS = [
...require('../lib/events').Events,
...require('../lib/chromium/events').Events,
},
missingCoverage: ['browserContext.setGeolocation', 'browserContext.setOffline', 'cDPSession.send', 'cDPSession.detach', 'page.emit("download")',
'download.url', 'download.path', 'download.failure', 'download.createReadStream', 'download.delete'],
missingCoverage: ['browserContext.setGeolocation', 'browserContext.setOffline', 'cDPSession.send', 'cDPSession.detach'],
},
{
name: 'WebKit',