Skip to content

Commit

Permalink
Merge pull request #546 from GoogleChrome/eventemitter
Browse files Browse the repository at this point in the history
cleanup of extension driver attachment and event registration
  • Loading branch information
paulirish authored Aug 1, 2016
2 parents a0e8add + b6592d4 commit 606e74b
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 192 deletions.
90 changes: 23 additions & 67 deletions lighthouse-core/gather/drivers/cri.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,21 @@ const port = process.env.PORT || 9222;
const log = require('../../lib/log.js');

class CriDriver extends Driver {
constructor() {
super();

/**
* Chrome remote interface instance.
*/
this._cri = null;
}

/**
* @return {!Promise<null>}
* @return {!Promise<undefined>}
*/
connect() {
return new Promise((resolve, reject) => {
if (this._chrome) {
if (this._cri) {
return resolve();
}

Expand All @@ -43,8 +51,9 @@ class CriDriver extends Driver {

chromeRemoteInterface({port: port, chooseTab: tab}, chrome => {
this._tab = tab;
this._chrome = chrome;
this.beginLogging();
this._cri = chrome;
// The CRI instance is also an EventEmitter, so use directly for event dispatch.
this._eventEmitter = chrome;
this.enableRuntimeEvents().then(_ => {
resolve();
});
Expand All @@ -57,7 +66,7 @@ class CriDriver extends Driver {
disconnect() {
return new Promise((resolve, reject) => {
if (!this._tab) {
this._chrome.close();
this._cri.close();
return resolve();
}

Expand All @@ -74,93 +83,40 @@ class CriDriver extends Driver {
/* eslint-enable new-cap */
})
.then(() => {
if (this._chrome) {
this._chrome.close();
if (this._cri) {
this._cri.close();
}
this._tab = null;
this._chrome = null;
this._cri = null;
this._eventEmitter = null;
this.url = null;
});
}

beginLogging() {
// log events received
this._chrome.on('event', req => _log('<=', req, 'verbose'));
}

/**
* Bind listeners for protocol events
* @param {!string} eventName
* @param {function(...)} cb
*/
on(eventName, cb) {
if (this._chrome === null) {
throw new Error('connect() must be called before attempting to listen to events.');
}
// log event listeners being bound
_log('listen for event =>', {method: eventName});
this._chrome.on(eventName, cb);
}

/**
* Bind a one-time listener for protocol events. Listener is removed once it
* has been called.
* @param {!string} eventName
* @param {function(...)} cb
*/
once(eventName, cb) {
if (this._chrome === null) {
throw new Error('connect() must be called before attempting to listen to events.');
}
// log event listeners being bound
_log('listen once for event =>', {method: eventName});
this._chrome.once(eventName, cb);
}

/**
* Unbind event listeners
* @param {!string} eventName
* @param {function(...)} cb
*/
off(eventName, cb) {
if (this._chrome === null) {
throw new Error('connect() must be called before attempting to remove an event listener.');
}

this._chrome.removeListener(eventName, cb);
}

/**
* Call protocol methods
* @param {!string} command
* @param {!Object} params
* @return {!Promise}
*/
sendCommand(command, params) {
if (this._chrome === null) {
if (this._cri === null) {
throw new Error('connect() must be called before attempting to send a command.');
}

return new Promise((resolve, reject) => {
_log('method => browser', {method: command, params: params});
this.formattedLog('method => browser', {method: command, params: params});

this._chrome.send(command, params, (err, result) => {
this._cri.send(command, params, (err, result) => {
if (err) {
_log('method <= browser ERR', {method: command, params: result}, 'error');
this.formattedLog('method <= browser ERR', {method: command, params: result}, 'error');
return reject(result);
}
_log('method <= browser OK', {method: command, params: result});
this.formattedLog('method <= browser OK', {method: command, params: result});
resolve(result);
});
});
}
}

function _log(prefix, data, level) {
const columns = (typeof process === 'undefined') ? Infinity : process.stdout.columns;
const maxLength = columns - data.method.length - prefix.length - 18;
const snippet = data.params ? JSON.stringify(data.params).substr(0, maxLength) : '';
log[level ? level : 'log'](prefix, data.method, snippet);
}

module.exports = CriDriver;
50 changes: 43 additions & 7 deletions lighthouse-core/gather/drivers/driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@ const NetworkRecorder = require('../../lib/network-recorder');
const emulation = require('../../lib/emulation');
const Element = require('../../lib/element');

const log = require('../../lib/log.js');

class Driver {

constructor() {
this._url = null;
this.PAUSE_AFTER_LOAD = 500;
this._chrome = null;
this._traceEvents = [];
this._traceCategories = Driver.traceCategories;
this._eventEmitter = null;
}

get url() {
Expand Down Expand Up @@ -64,6 +66,19 @@ class Driver {
return this.sendCommand('Security.enable');
}

/**
* A simple formatting utility for event logging.
* @param {string} prefix
* @param {!Object} data A JSON-serializable object of event data to log.
* @param {string=} level Optional logging level. Defaults to 'log'.
*/
formattedLog(prefix, data, level) {
const columns = (!process || process.browser) ? Infinity : process.stdout.columns;
const maxLength = columns - data.method.length - prefix.length - 18;
const snippet = data.params ? JSON.stringify(data.params).substr(0, maxLength) : '';
log[level ? level : 'log'](prefix, data.method, snippet);
}

/**
* @return {!Promise<null>}
*/
Expand All @@ -77,24 +92,45 @@ class Driver {

/**
* Bind listeners for protocol events
* @param {!string} eventName
* @param {function(...)} cb
*/
on() {
return Promise.reject(new Error('Not implemented'));
on(eventName, cb) {
if (this._eventEmitter === null) {
throw new Error('connect() must be called before attempting to listen to events.');
}

// log event listeners being bound
this.formattedLog('listen for event =>', {method: eventName});
this._eventEmitter.on(eventName, cb);
}

/**
* Bind a one-time listener for protocol events. Listener is removed once it
* has been called.
* @param {!string} eventName
* @param {function(...)} cb
*/
once() {
return Promise.reject(new Error('Not implemented'));
once(eventName, cb) {
if (this._eventEmitter === null) {
throw new Error('connect() must be called before attempting to listen to events.');
}
// log event listeners being bound
this.formattedLog('listen once for event =>', {method: eventName});
this._eventEmitter.once(eventName, cb);
}

/**
* Unbind event listeners
* @param {!string} eventName
* @param {function(...)} cb
*/
off() {
return Promise.reject(new Error('Not implemented'));
off(eventName, cb) {
if (this._eventEmitter === null) {
throw new Error('connect() must be called before attempting to remove an event listener.');
}

this._eventEmitter.removeListener(eventName, cb);
}

/**
Expand Down
Loading

0 comments on commit 606e74b

Please sign in to comment.