Socket
Socket
Sign inDemoInstall

chrome-remote-interface

Package Overview
Dependencies
Maintainers
1
Versions
77
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

chrome-remote-interface - npm Package Compare versions

Comparing version 0.14.2 to 0.14.3

lib/api.js

47

bin/client.js
#!/usr/bin/env node
var repl = require('repl');
var util = require('util');
var fs = require('fs');
var path = require('path');
const repl = require('repl');
const util = require('util');
const fs = require('fs');
const path = require('path');
var program = require('commander');
var Chrome = require('../');
const program = require('commander');
const Chrome = require('../');
function display(object) {

@@ -19,5 +20,5 @@ return util.inspect(object, {

function inheritProperties(from, to) {
for (var property in from) {
Object.keys(from).forEach(function (property) {
to[property] = from[property];
}
});
}

@@ -49,5 +50,5 @@

// keep track of registered events
var registeredEvents = {};
const registeredEvents = {};
var chromeRepl = repl.start({
const chromeRepl = repl.start({
'prompt': '\033[32m>>>\033[0m ',

@@ -59,3 +60,3 @@ 'ignoreUndefined': true,

// make the history persistent
var history_file = path.join(process.env.HOME, '.cri_history');
const history_file = path.join(process.env.HOME, '.cri_history');
require('repl.history')(chromeRepl, history_file);

@@ -71,5 +72,5 @@

// hard code a callback to display the result
var override = function (params) {
const override = function (params) {
command(params, function (error, response) {
var repr = {};
const repr = {};
repr[error ? 'error' : 'result'] = response;

@@ -85,9 +86,9 @@ overridePrompt(display(repr));

function overrideEvent(chrome, domainName, itemName) {
var event = chrome[domainName][itemName];
var eventName = domainName + '.' + itemName;
const event = chrome[domainName][itemName];
const eventName = domainName + '.' + itemName;
// hard code a callback to display the event data
var override = function (filter) {
const override = function (filter) {
// remove all the listeners (just one actually) anyway
chrome.removeAllListeners(eventName);
var status = {};
const status = {};
// a filter will always enable/update the listener

@@ -99,6 +100,6 @@ if (!filter && registeredEvents[eventName]) {

// use the filter (or true) as a status token
var statusToken = (filter ? filter.toString() : true);
const statusToken = (filter ? filter.toString() : true);
status[eventName] = registeredEvents[eventName] = statusToken;
event(function (params) {
var repr = {};
const repr = {};
if (filter) {

@@ -134,5 +135,5 @@ params = filter(params);

// walk the domain names
var domainName = domainObject.domain;
const domainName = domainObject.domain;
chromeRepl.context[domainName] = {};
for (var itemName in chrome[domainName]) {
Object.keys(chrome[domainName]).forEach(function (itemName) {
// walk the items in the domain and override commands and events

@@ -149,3 +150,3 @@ var item = chrome[domainName][itemName];

chromeRepl.context[domainName][itemName] = item;
}
});
});

@@ -282,3 +283,3 @@ }).on('error', function (err) {

// common options
var options = {
const options = {
'host': program.host,

@@ -285,0 +286,0 @@ 'port': program.port

@@ -1,5 +0,6 @@

var events = require('events');
var Chrome = require('./lib/chrome.js');
var devtools = require('./lib/devtools.js');
const events = require('events');
const devtools = require('./lib/devtools.js');
const Chrome = require('./lib/chrome.js');
module.exports = function (options, callback) {

@@ -10,3 +11,3 @@ if (typeof options === 'function') {

}
var notifier = new events.EventEmitter();
const notifier = new events.EventEmitter();
if (typeof callback === 'function') {

@@ -22,2 +23,5 @@ // allow to register the error callback later

notifier.on('error', reject);
notifier.on('disconnect', function () {
reject(new Error('Disconnected'));
});
new Chrome(options, notifier);

@@ -24,0 +28,0 @@ });

@@ -1,23 +0,25 @@

var defaults = require('./defaults.js');
var devtools = require('./devtools.js');
var util = require('util');
var events = require('events');
var WebSocket = require('ws');
const events = require('events');
const util = require('util');
const WebSocket = require('ws');
const api = require('./api.js');
const defaults = require('./defaults.js');
const devtools = require('./devtools.js');
function Chrome(options, notifier) {
var self = this;
// options
options = options || {};
self.host = options.host || defaults.HOST;
self.port = options.port || defaults.PORT;
self.protocol = options.protocol;
self.remote = !!(options.remote);
self.chooseTab = options.chooseTab || function () { return 0; };
this.host = options.host || defaults.HOST;
this.port = options.port || defaults.PORT;
this.protocol = options.protocol;
this.remote = !!(options.remote);
this.chooseTab = options.chooseTab || function () { return 0; };
// locals
events.EventEmitter.call(this);
self.notifier = notifier;
self.callbacks = {};
self.nextCommandId = 1;
this.notifier = notifier;
this.callbacks = {};
this.nextCommandId = 1;
// operations
connectToChrome.call(self);
start.call(this);
}

@@ -27,2 +29,3 @@

// avoid misinterpreting protocol's members as custom util.inspect functions
Chrome.prototype.inspect = function (depth, options) {

@@ -34,3 +37,3 @@ options.customInspect = false;

Chrome.prototype.send = function (method, params, callback) {
var self = this;
const chrome = this;
if (typeof params === 'function') {

@@ -42,6 +45,6 @@ callback = params;

if (typeof callback === 'function') {
enqueueCommand.call(self, method, params, callback);
enqueueCommand.call(chrome, method, params, callback);
} else {
return new Promise(function (fulfill, reject) {
enqueueCommand.call(self, method, params, function (error, response) {
enqueueCommand.call(chrome, method, params, function (error, response) {
if (error) {

@@ -58,9 +61,9 @@ reject(response);

Chrome.prototype.close = function (callback) {
var self = this;
const chrome = this;
function closeWebSocket(callback) {
// don't notify on user-initiated shutdown ('disconnect' event)
self.ws.removeAllListeners('close');
self.ws.close();
self.ws.once('close', function () {
self.ws.removeAllListeners();
chrome.ws.removeAllListeners('close');
chrome.ws.close();
chrome.ws.once('close', function () {
chrome.ws.removeAllListeners();
callback();

@@ -78,112 +81,69 @@ });

// send a command to the remote endpoint and register a callback for the reply
function enqueueCommand(method, params, callback) {
var self = this;
var id = self.nextCommandId++;
var message = {'id': id, 'method': method, 'params': params};
self.ws.send(JSON.stringify(message));
self.callbacks[id] = callback;
const chrome = this;
const id = chrome.nextCommandId++;
const message = {'id': id, 'method': method, 'params': params};
chrome.ws.send(JSON.stringify(message));
chrome.callbacks[id] = callback;
}
function arrayToObject(parameters) {
var keyValue = {};
parameters.forEach(function (parameter) {
var name = parameter.name;
delete parameter.name;
keyValue[name] = parameter;
// initiate the connection process
function start() {
const chrome = this;
const options = {'host': chrome.host, 'port': chrome.port};
Promise.all([
// fetch the protocol and prepare the API
fetchProtocol.call(chrome, options).then(api.prepare.bind(chrome)),
// in the meanwhile fetch the WebSocket debugger URL
fetchDebuggerURL.call(chrome, options)
]).then(function (values) {
// finally connect to the WebSocket
const url = values[1];
return connectToWebSocket.call(chrome, url);
}).then(function () {
chrome.notifier.emit('connect', chrome);
}).catch(function (err) {
chrome.notifier.emit('error', err);
});
return keyValue;
}
function decorate(to, category, object) {
to.category = category;
for (var field in object) {
// commands and events have parameters whereas types have properties
if (category === 'type' && field === 'properties' ||
field === 'parameters') {
to[field] = arrayToObject(object[field]);
} else {
to[field] = object[field];
// fetch the protocol according to 'protocol' and 'remote'
function fetchProtocol(options) {
const chrome = this;
return new Promise(function (fulfill, reject) {
// if a protocol has been provided then use it
if (chrome.protocol) {
fulfill(chrome.protocol);
}
}
}
function addCommand(domainName, command) {
var self = this;
var handler = function (params, callback) {
return self.send(domainName + '.' + command.name, params, callback);
};
decorate(handler, 'command', command);
self[domainName][command.name] = handler;
}
function addEvent(domainName, event) {
var self = this;
var handler = function (handler) {
self.on(domainName + '.' + event.name, handler);
};
decorate(handler, 'event', event);
self[domainName][event.name] = handler;
}
function addType(domainName, type) {
var self = this;
var help = {};
decorate(help, 'type', type);
self[domainName][type.id] = help;
}
function addCommandShorthands() {
var self = this;
for (var domainIdx in self.protocol.domains) {
var domain = self.protocol.domains[domainIdx];
var domainName = domain.domain;
self[domainName] = {};
// add commands
var commands = domain.commands;
if (commands) {
for (var commandIdx in commands) {
var command = commands[commandIdx];
addCommand.call(self, domainName, command);
}
// otherwise user either the local or the remote version
else {
options.remote = chrome.remote;
devtools.Protocol(options).then(function (protocol) {
fulfill(protocol.descriptor);
}).catch(reject);
}
// add events
var events = domain.events;
if (events) {
for (var eventIdx in events) {
var event = events[eventIdx];
addEvent.call(self, domainName, event);
}
}
// add types
var types = domain.types;
if (types) {
for (var typeIdx in types) {
var type = types[typeIdx];
addType.call(self, domainName, type);
}
}
}
});
}
function connectToChrome() {
var self = this;
var options = {'host': self.host, 'port': self.port};
// fetch the WebSocket debugger URL from the user choice (`chooseTab`)
var fetchDebuggerURL = function (callback) {
// fetch the WebSocket URL according to 'chooseTab'
function fetchDebuggerURL(options) {
const chrome = this;
return new Promise(function (fulfill, reject) {
// when DevTools are open or another WebSocket is connected to a given
// tab the `webSocketDebuggerUrl` filed is not available
var busyTabError = new Error('Tab does not support inspection');
// tab the 'webSocketDebuggerUrl' field is not available
const busyTabError = new Error('Tab does not support inspection');
var url;
switch (typeof self.chooseTab) {
switch (typeof chrome.chooseTab) {
case 'string':
// a WebSocket URL is specified by the user (e.g., node-inspector)
callback(null, self.chooseTab);
fulfill(chrome.chooseTab);
break;
case 'object':
// a tab object is specified by the user
url = self.chooseTab.webSocketDebuggerUrl;
url = chrome.chooseTab.webSocketDebuggerUrl;
if (url) {
callback(null, url);
fulfill(url);
} else {
callback(busyTabError);
reject(busyTabError);
}

@@ -193,102 +153,83 @@ break;

// a function is specified by the user (get tab by index)
devtools.List(options, function (err, tabs) {
if (err) {
callback(err);
} else {
// the index is used to fetch the proper tab from the list
var tab = tabs[self.chooseTab(tabs)];
if (tab) {
url = tab.webSocketDebuggerUrl;
if (url) {
callback(null, url);
} else {
callback(busyTabError);
}
devtools.List(options).then(function (tabs) {
// the index is used to fetch the proper tab from the list
const tab = tabs[chrome.chooseTab(tabs)];
if (tab) {
url = tab.webSocketDebuggerUrl;
if (url) {
fulfill(url);
} else {
callback(new Error('Invalid tab index'));
reject(busyTabError);
}
} else {
reject(new Error('Invalid tab index'));
}
});
}).catch(reject);
break;
default:
callback(new Error('Invalid requested tab'));
reject(new Error('Invalid requested tab'));
}
};
// parse the protocol and finalize connection
var continueConnection = function () {
// build the API from the protocol descriptor
addCommandShorthands.call(self);
// get the WebSocket debugger URL
fetchDebuggerURL(function (err, url) {
if (err) {
self.notifier.emit('error', err);
} else {
connectToWebSocket.call(self, url);
}
});
}
// establish the WebSocket connection and start processing user commands
function connectToWebSocket(url) {
const chrome = this;
return new Promise(function (fulfill, reject) {
// create the WebSocket
try {
// disable the permessage-deflate as a temporary fix for #39
chrome.ws = new WebSocket(url, {'perMessageDeflate': false});
} catch (err) {
// handles bad URLs
reject(err);
return;
}
// set up event handlers
chrome.ws.on('open', function () {
fulfill();
});
};
// if a protocol has been provided then use it
if (self.protocol) {
continueConnection();
} else {
// otherwise user either the local or the remote version
options.remote = self.remote;
devtools.Protocol(options, function (err, protocol) {
if (err) {
self.notifier.emit('error', err);
} else {
self.protocol = protocol.descriptor;
continueConnection();
}
chrome.ws.on('message', function (data) {
const message = JSON.parse(data);
handleMessage.call(chrome, message);
});
}
chrome.ws.on('close', function () {
chrome.notifier.emit('disconnect');
});
chrome.ws.on('error', function (err) {
reject(err);
});
});
}
function connectToWebSocket(url) {
var self = this;
try {
// disable the permessage-deflate as a temporary fix for #39
self.ws = new WebSocket(url, {'perMessageDeflate': false});
} catch (err) {
self.notifier.emit('error', err);
return;
}
self.ws.on('open', function () {
self.notifier.emit('connect', self);
});
self.ws.on('message', function (data) {
var message = JSON.parse(data);
// command response
if (message.id) {
var callback = self.callbacks[message.id];
if (callback) {
// interpret the lack of both 'error' and 'result' as success
// (this may happen with node-inspector)
if (message.error) {
callback(true, message.error);
} else {
callback(false, message.result || {});
}
// unregister command response callback
delete self.callbacks[message.id];
// notify when there are no more pending commands
if (Object.keys(self.callbacks).length === 0) {
self.emit('ready');
}
}
// handle the messages read from the WebSocket
function handleMessage(message) {
const chrome = this;
// command response
if (message.id) {
const callback = chrome.callbacks[message.id];
if (!callback) {
return;
}
// event
else if (message.method) {
self.emit('event', message);
self.emit(message.method, message.params);
// interpret the lack of both 'error' and 'result' as success
// (this may happen with node-inspector)
if (message.error) {
callback(true, message.error);
} else {
callback(false, message.result || {});
}
});
self.ws.on('close', function () {
self.notifier.emit('disconnect');
});
self.ws.on('error', function (err) {
self.notifier.emit('error', err);
});
// unregister command response callback
delete chrome.callbacks[message.id];
// notify when there are no more pending commands
if (Object.keys(chrome.callbacks).length === 0) {
chrome.emit('ready');
}
}
// event
else if (message.method) {
chrome.emit('event', message);
chrome.emit(message.method, message.params);
}
}
module.exports = Chrome;

@@ -1,10 +0,11 @@

var defaults = require('./defaults.js');
var util = require('util');
var http = require('http');
var https = require('https');
const http = require('http');
const https = require('https');
const util = require('util');
const defaults = require('./defaults.js');
// callback(err, protocol)
module.exports.Protocol = promisesWrapper(function (options, callback) {
var localProtocol = require('./protocol.json');
var protocol = {'remote': false, 'descriptor': localProtocol};
const localProtocol = require('./protocol.json');
const protocol = {'remote': false, 'descriptor': localProtocol};
// if the local protocol is explicitly requested

@@ -135,3 +136,3 @@ if (!options.remote) {

function fetchObject(transport, options, callback) {
var request = transport.get(options, function (response) {
const request = transport.get(options, function (response) {
var data = '';

@@ -168,6 +169,6 @@ response.on('data', function (chunk) {

// (see https://github.com/cyrus-and/chrome-remote-interface/issues/10#issuecomment-146032907)
var webKitVersion = info['WebKit-Version'];
var match = webKitVersion.match(/\s\(@(\b[0-9a-f]{5,40}\b)/);
var hash = match[1];
var fromChromiumDotOrg = (hash <= 202666);
const webKitVersion = info['WebKit-Version'];
const match = webKitVersion.match(/\s\(@(\b[0-9a-f]{5,40}\b)/);
const hash = match[1];
const fromChromiumDotOrg = (hash <= 202666);
var templates;

@@ -177,6 +178,6 @@ if (fromChromiumDotOrg) {

} else {
var chromeVersion = explodeVersion(info.Browser.split('/')[1]);
var lastChromeVersion = explodeVersion('53.0.2758.1'); // before the split (https://crbug.com/580337)
const chromeVersion = explodeVersion(info.Browser.split('/')[1]);
const lastChromeVersion = explodeVersion('53.0.2758.1'); // before the split (https://crbug.com/580337)
// according to https://www.chromium.org/developers/version-numbers
var beforeSplit = (chromeVersion[2] <= lastChromeVersion[2]); // patch not meaningful
const beforeSplit = (chromeVersion[2] <= lastChromeVersion[2]); // patch not meaningful
templates = (beforeSplit ?

@@ -187,6 +188,6 @@ ['https://chromium.googlesource.com/chromium/src/+/%s/third_party/WebKit/Source/devtools/protocol.json?format=TEXT'] :

}
var urls = templates.map(function (template) {
const urls = templates.map(function (template) {
return util.format(template, hash);
});
var descriptors = [];
const descriptors = [];
urls.forEach(function (url) {

@@ -193,0 +194,0 @@ fetchObject(https, url, function (err, data) {

@@ -12,3 +12,3 @@ {

"homepage": "https://github.com/cyrus-and/chrome-remote-interface",
"version": "0.14.2",
"version": "0.14.3",
"repository": {

@@ -39,6 +39,3 @@ "type": "git",

"mocha": "*"
},
"scripts": {
"test": "make test"
}
}

@@ -13,16 +13,19 @@ chrome-remote-interface

Debugging Protocol][rdp]. In particular, it has been tested against the
following implementations.
following implementations:
Implementation | Notes
---------------------|------
[Google Chrome][1.1] | native support; enable [port forwarding][1.2] in Chrome for Android
[Microsoft Edge][2] | via the [Edge Diagnostics Adapter][edge-diagnostics-adapter]
[Node.js][3.1] | via [`--inspect`][3.3] (with `--port 9229`) or via [node-inspector][3.2] (by connecting to `ws://127.0.0.1:8080/?port=5858` by default)
[clients-cri]: https://developer.chrome.com/devtools/docs/debugging-clients#chrome-remote-interface
Implementation | Notes
----------------------|------
[Google Chrome][1.1] | native support; enable [port forwarding][1.2] in Chrome for Android
[Microsoft Edge][2.1] | via the [Edge Diagnostics Adapter][2.2]
[Node.js][3.1] | via [`--inspect`][3.2] (with `--port 9229`) or via [node-inspector][3.3] (by connecting to `ws://127.0.0.1:8080/?port=5858` by default)
[1.1]: https://www.chromium.org/
[1.2]: https://developer.chrome.com/devtools/docs/remote-debugging-legacy
[2]: https://www.microsoft.com/windows/microsoft-edge
[2.1]: https://www.microsoft.com/windows/microsoft-edge
[2.2]: https://github.com/Microsoft/edge-diagnostics-adapter
[3.1]: https://nodejs.org/
[3.2]: https://github.com/node-inspector/node-inspector
[3.3]: https://chromedevtools.github.io/debugger-protocol-viewer/v8/
[3.2]: https://chromedevtools.github.io/debugger-protocol-viewer/v8/
[3.3]: https://github.com/node-inspector/node-inspector

@@ -45,6 +48,6 @@ Installation

The following snippet loads `https://github.com` and dumps every request made.
The following snippet loads `https://github.com` and dumps every request made:
```javascript
var Chrome = require('chrome-remote-interface');
const Chrome = require('chrome-remote-interface');
Chrome(function (chrome) {

@@ -244,6 +247,6 @@ with (chrome) {

([`protocol.json`][local-json]) directly from the instrumented Chrome instance
(see [#10][issue]). Rather, that file can be fetched from the proper [source
(see [#10][issue-10]). Rather, that file can be fetched from the proper [source
repository][remote-json] at every connection. By default, the [local
version][local-json] is used. That file is manually updated from time to time
using `make update-protocol` and pushed to this repository.
using `scripts/update-protocol.sh` and pushed to this repository.

@@ -257,5 +260,9 @@ To override the above behavior there are basically three options:

3. update the local copy with `make update-protocol` (not present when fetched
with `npm install`).
3. update the local copy with `scripts/update-protocol.sh` (not present when
fetched with `npm install`).
[issue-10]: https://github.com/cyrus-and/chrome-remote-interface/issues/10
[local-json]: lib/protocol.json
[remote-json]: https://chromium.googlesource.com/chromium/src/+/master/third_party/WebKit/Source/
API

@@ -291,6 +298,7 @@ ---

`callback` is a listener automatically added to the `connect` event of the
returned `EventEmitter`; when `callback` is omitted a `Promise` object is
returned.
returned `EventEmitter`. When `callback` is omitted a `Promise` object is
returned which becomes fulfilled if the `connect` event is triggered and
rejected if any of the `disconnect` or `error` events are triggered.
Returns an `EventEmitter` that supports the following events:
The `EventEmitter` supports the following events:

@@ -353,3 +361,3 @@ #### Event: 'connect'

```javascript
var Chrome = require('chrome-remote-interface');
const Chrome = require('chrome-remote-interface');
Chrome.Protocol(function (err, protocol) {

@@ -383,3 +391,3 @@ if (!err) {

```javascript
var Chrome = require('chrome-remote-interface');
const Chrome = require('chrome-remote-interface');
Chrome.List(function (err, tabs) {

@@ -399,3 +407,3 @@ if (!err) {

- `host`: HTTP frontend host. Defaults to `localhost`;
- `port`: HTTP frontend port. Defaults to `9222`.
- `port`: HTTP frontend port. Defaults to `9222`;
- `url`: URL to load in the new tab. Defaults to `about:blank`.

@@ -414,3 +422,3 @@

```javascript
var Chrome = require('chrome-remote-interface');
const Chrome = require('chrome-remote-interface');
Chrome.New(function (err, tab) {

@@ -430,3 +438,3 @@ if (!err) {

- `host`: HTTP frontend host. Defaults to `localhost`;
- `port`: HTTP frontend port. Defaults to `9222`.
- `port`: HTTP frontend port. Defaults to `9222`;
- `id`: Tab id. Required, no default.

@@ -444,3 +452,3 @@

```javascript
var Chrome = require('chrome-remote-interface');
const Chrome = require('chrome-remote-interface');
Chrome.Activate({'id': 'CC46FBFA-3BDA-493B-B2E4-2BE6EB0D97EC'}, function (err) {

@@ -460,3 +468,3 @@ if (!err) {

- `host`: HTTP frontend host. Defaults to `localhost`;
- `port`: HTTP frontend port. Defaults to `9222`.
- `port`: HTTP frontend port. Defaults to `9222`;
- `id`: Tab id. Required, no default.

@@ -474,3 +482,3 @@

```javascript
var Chrome = require('chrome-remote-interface');
const Chrome = require('chrome-remote-interface');
Chrome.Close({'id': 'CC46FBFA-3BDA-493B-B2E4-2BE6EB0D97EC'}, function (err) {

@@ -507,3 +515,3 @@ if (!err) {

```javascript
var Chrome = require('chrome-remote-interface');
const Chrome = require('chrome-remote-interface');
Chrome.Version(function (err, info) {

@@ -529,3 +537,3 @@ if (!err) {

- `method`: a string describing the notification (e.g.,
`'Network.requestWillBeSent'`).
`'Network.requestWillBeSent'`);
- `params`: an object containing the payload.

@@ -663,2 +671,3 @@

- [Chrome Debugging Protocol Viewer][rdp-viewer]
- [Chrome Debugging Protocol Google group][goole-group]
- [Showcase Chrome Debugging Protocol Clients][clients]

@@ -668,9 +677,3 @@

[rdp-viewer]: https://chromedevtools.github.io/debugger-protocol-viewer/
[clients-cri]: https://developer.chrome.com/devtools/docs/debugging-clients#chrome-remote-interface
[goole-group]: https://groups.google.com/forum/#!forum/chrome-debugging-protocol
[clients]: https://developer.chrome.com/devtools/docs/debugging-clients
[edge-diagnostics-adapter]: https://github.com/Microsoft/edge-diagnostics-adapter
<!-- related to #10 -->
[local-json]: lib/protocol.json
[remote-json]: https://chromium.googlesource.com/chromium/src/+/master/third_party/WebKit/Source/
[issue]: https://github.com/cyrus-and/chrome-remote-interface/issues/10
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc