feat(firefox): introduce async stacks for Puppeteer-Firefox (#3948)
This patch refactors Puppeteer-Firefox code to declare public API in `/lib/api.js` and use it to setup async stack hooks over the public API method calls.
This commit is contained in:
parent
9216056d12
commit
6b18e8cef5
@ -13,44 +13,13 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
const FirefoxLauncher = require('./lib/Launcher.js').Launcher;
|
|
||||||
const BrowserFetcher = require('./lib/BrowserFetcher.js');
|
|
||||||
|
|
||||||
class Puppeteer {
|
const {helper} = require('./lib/helper');
|
||||||
constructor() {
|
const api = require('./lib/api');
|
||||||
this._firefoxLauncher = new FirefoxLauncher();
|
for (const className in api)
|
||||||
}
|
helper.installAsyncStackHooks(api[className]);
|
||||||
|
|
||||||
async launch(options = {}) {
|
const {Puppeteer} = require('./lib/Puppeteer');
|
||||||
const {
|
const packageJson = require('./package.json');
|
||||||
args = [],
|
const preferredRevision = packageJson.puppeteer.firefox_revision;
|
||||||
dumpio = !!process.env.DUMPIO,
|
module.exports = new Puppeteer(__dirname, preferredRevision);
|
||||||
handleSIGHUP = true,
|
|
||||||
handleSIGINT = true,
|
|
||||||
handleSIGTERM = true,
|
|
||||||
headless = (process.env.HEADLESS || 'true').trim().toLowerCase() === 'true',
|
|
||||||
defaultViewport = {width: 800, height: 600},
|
|
||||||
ignoreHTTPSErrors = false,
|
|
||||||
slowMo = 0,
|
|
||||||
executablePath = this.executablePath(),
|
|
||||||
} = options;
|
|
||||||
options = {
|
|
||||||
args, slowMo, dumpio, executablePath, handleSIGHUP, handleSIGINT, handleSIGTERM, headless, defaultViewport,
|
|
||||||
ignoreHTTPSErrors
|
|
||||||
};
|
|
||||||
return await this._firefoxLauncher.launch(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
createBrowserFetcher(options) {
|
|
||||||
return new BrowserFetcher(__dirname, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
executablePath() {
|
|
||||||
const browserFetcher = new BrowserFetcher(__dirname, { product: 'firefox' });
|
|
||||||
const revision = require('./package.json').puppeteer.firefox_revision;
|
|
||||||
const revisionInfo = browserFetcher.revisionInfo(revision);
|
|
||||||
return revisionInfo.executablePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = new Puppeteer();
|
|
||||||
|
@ -312,4 +312,4 @@ BrowserContext.Events = {
|
|||||||
TargetDestroyed: 'targetdestroyed'
|
TargetDestroyed: 'targetdestroyed'
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {Browser, Target};
|
module.exports = {Browser, BrowserContext, Target};
|
||||||
|
@ -228,7 +228,7 @@ class BrowserFetcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = BrowserFetcher;
|
module.exports = {BrowserFetcher};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} folderPath
|
* @param {string} folderPath
|
||||||
|
@ -19,6 +19,7 @@ const removeFolder = require('rimraf');
|
|||||||
const childProcess = require('child_process');
|
const childProcess = require('child_process');
|
||||||
const {Connection} = require('./Connection');
|
const {Connection} = require('./Connection');
|
||||||
const {Browser} = require('./Browser');
|
const {Browser} = require('./Browser');
|
||||||
|
const {BrowserFetcher} = require('./BrowserFetcher');
|
||||||
const readline = require('readline');
|
const readline = require('readline');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const util = require('util');
|
const util = require('util');
|
||||||
@ -35,6 +36,11 @@ const FIREFOX_PROFILE_PATH = path.join(os.tmpdir(), 'puppeteer_firefox_profile-'
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
class Launcher {
|
class Launcher {
|
||||||
|
constructor(projectRoot, preferredRevision) {
|
||||||
|
this._projectRoot = projectRoot;
|
||||||
|
this._preferredRevision = preferredRevision;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
* @return {!Promise<!Browser>}
|
* @return {!Promise<!Browser>}
|
||||||
@ -43,7 +49,7 @@ class Launcher {
|
|||||||
const {
|
const {
|
||||||
args = [],
|
args = [],
|
||||||
dumpio = false,
|
dumpio = false,
|
||||||
executablePath = null,
|
executablePath = this.executablePath(),
|
||||||
handleSIGHUP = true,
|
handleSIGHUP = true,
|
||||||
handleSIGINT = true,
|
handleSIGINT = true,
|
||||||
handleSIGTERM = true,
|
handleSIGTERM = true,
|
||||||
@ -53,9 +59,6 @@ class Launcher {
|
|||||||
slowMo = 0,
|
slowMo = 0,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
if (!executablePath)
|
|
||||||
throw new Error('Firefox launching is only supported with local version of firefox!');
|
|
||||||
|
|
||||||
const firefoxArguments = args.slice();
|
const firefoxArguments = args.slice();
|
||||||
firefoxArguments.push('-no-remote');
|
firefoxArguments.push('-no-remote');
|
||||||
firefoxArguments.push('-juggler', '0');
|
firefoxArguments.push('-juggler', '0');
|
||||||
@ -152,6 +155,15 @@ class Launcher {
|
|||||||
} catch (e) { }
|
} catch (e) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
executablePath() {
|
||||||
|
const browserFetcher = new BrowserFetcher(this._projectRoot, { product: 'firefox' });
|
||||||
|
const revisionInfo = browserFetcher.revisionInfo(this._preferredRevision);
|
||||||
|
return revisionInfo.executablePath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1446,4 +1446,4 @@ class NavigationWatchdog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {Page};
|
module.exports = {Page, Frame, ConsoleMessage};
|
||||||
|
27
experimental/puppeteer-firefox/lib/Puppeteer.js
Normal file
27
experimental/puppeteer-firefox/lib/Puppeteer.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
const {Launcher} = require('./Launcher.js');
|
||||||
|
const {BrowserFetcher} = require('./BrowserFetcher.js');
|
||||||
|
|
||||||
|
class Puppeteer {
|
||||||
|
/**
|
||||||
|
* @param {string} projectRoot
|
||||||
|
* @param {string} preferredRevision
|
||||||
|
*/
|
||||||
|
constructor(projectRoot, preferredRevision) {
|
||||||
|
this._projectRoot = projectRoot;
|
||||||
|
this._launcher = new Launcher(projectRoot, preferredRevision);
|
||||||
|
}
|
||||||
|
|
||||||
|
async launch(options = {}) {
|
||||||
|
return this._launcher.launch(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
createBrowserFetcher(options) {
|
||||||
|
return new BrowserFetcher(this._projectRoot, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
executablePath() {
|
||||||
|
return this._launcher.executablePath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {Puppeteer};
|
16
experimental/puppeteer-firefox/lib/api.js
Normal file
16
experimental/puppeteer-firefox/lib/api.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
module.exports = {
|
||||||
|
Browser: require('./Browser').Browser,
|
||||||
|
BrowserContext: require('./Browser').BrowserContext,
|
||||||
|
BrowserFetcher: require('./BrowserFetcher').BrowserFetcher,
|
||||||
|
ConsoleMessage: require('./Page').ConsoleMessage,
|
||||||
|
Dialog: require('./Dialog').Dialog,
|
||||||
|
ElementHandle: require('./JSHandle').ElementHandle,
|
||||||
|
Frame: require('./Page').Frame,
|
||||||
|
JSHandle: require('./JSHandle').JSHandle,
|
||||||
|
Keyboard: require('./Input').Keyboard,
|
||||||
|
Mouse: require('./Input').Mouse,
|
||||||
|
Page: require('./Page').Page,
|
||||||
|
Puppeteer: require('./Puppeteer').Puppeteer,
|
||||||
|
Target: require('./Browser').Target,
|
||||||
|
TimeoutError: require('./Errors').TimeoutError,
|
||||||
|
};
|
@ -19,6 +19,27 @@ const {TimeoutError} = require('./Errors');
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
class Helper {
|
class Helper {
|
||||||
|
/**
|
||||||
|
* @param {!Object} classType
|
||||||
|
*/
|
||||||
|
static installAsyncStackHooks(classType) {
|
||||||
|
for (const methodName of Reflect.ownKeys(classType.prototype)) {
|
||||||
|
const method = Reflect.get(classType.prototype, methodName);
|
||||||
|
if (methodName === 'constructor' || typeof methodName !== 'string' || methodName.startsWith('_') || typeof method !== 'function' || method.constructor.name !== 'AsyncFunction')
|
||||||
|
continue;
|
||||||
|
Reflect.set(classType.prototype, methodName, function(...args) {
|
||||||
|
const syncStack = new Error();
|
||||||
|
return method.call(this, ...args).catch(e => {
|
||||||
|
const stack = syncStack.stack.substring(syncStack.stack.indexOf('\n') + 1);
|
||||||
|
const clientStack = stack.substring(stack.indexOf('\n'));
|
||||||
|
if (!e.stack.includes(clientStack))
|
||||||
|
e.stack += '\n -- ASYNC --\n' + stack;
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Function|string} fun
|
* @param {Function|string} fun
|
||||||
* @param {!Array<*>} args
|
* @param {!Array<*>} args
|
||||||
|
@ -79,7 +79,7 @@ module.exports.addTests = function({testRunner, expect, headless, Errors, Device
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
(asyncawait ? describe_fails_ffox : xdescribe)('Async stacks', () => {
|
(asyncawait ? describe : xdescribe)('Async stacks', () => {
|
||||||
it('should work', async({page, server}) => {
|
it('should work', async({page, server}) => {
|
||||||
server.setRoute('/empty.html', (req, res) => {
|
server.setRoute('/empty.html', (req, res) => {
|
||||||
res.statusCode = 204;
|
res.statusCode = 204;
|
||||||
@ -88,7 +88,7 @@ module.exports.addTests = function({testRunner, expect, headless, Errors, Device
|
|||||||
let error = null;
|
let error = null;
|
||||||
await page.goto(server.EMPTY_PAGE).catch(e => error = e);
|
await page.goto(server.EMPTY_PAGE).catch(e => error = e);
|
||||||
expect(error).not.toBe(null);
|
expect(error).not.toBe(null);
|
||||||
expect(error.message).toContain('net::ERR_ABORTED');
|
expect(error.stack).toContain(__filename);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user