Skip to content

Commit

Permalink
feat(firefox): support downloads
Browse files Browse the repository at this point in the history
  • Loading branch information
yury-s committed Apr 7, 2020
1 parent e0c8fbf commit d4f2975
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 13 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"main": "index.js",
"playwright": {
"chromium_revision": "754895",
"firefox_revision": "1072",
"firefox_revision": "1074",
"webkit_revision": "1188"
},
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion src/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/download.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
21 changes: 20 additions & 1 deletion src/firefox/ffBrowser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down
14 changes: 8 additions & 6 deletions src/server/firefox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,34 +49,36 @@ 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> {
const {
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 = [],
Expand Down Expand Up @@ -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' ? {
Expand Down Expand Up @@ -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> {
Expand Down
6 changes: 3 additions & 3 deletions test/download.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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>`);
Expand Down

0 comments on commit d4f2975

Please sign in to comment.