npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

shimo-chrome-remote-interface

v0.19.1

Published

Chrome Debugging Protocol interface

Downloads

5

Readme

chrome-remote-interface Build Status

Chrome Debugging Protocol interface that helps to instrument Chrome (or any other suitable implementation) by providing a simple abstraction of commands and notifications using a straightforward JavaScript API.

This module is one of the many third-party protocol clients.

Sample API usage

The following snippet loads https://github.com and dumps every request made:

const CDP = require('chrome-remote-interface');

CDP((client) => {
    // extract domains
    const {Network, Page} = client;
    // setup handlers
    Network.requestWillBeSent((params) => {
        console.log(params.request.url);
    });
    Page.loadEventFired(() => {
        client.close();
    });
    // enable events then start!
    Promise.all([
        Network.enable(),
        Page.enable()
    ]).then(() => {
        return Page.navigate({url: 'https://github.com'});
    }).catch((err) => {
        console.error(err);
        client.close();
    });
}).on('error', (err) => {
    // cannot connect to the remote endpoint
    console.error(err);
});

Find more examples in the wiki, in particular notice how the above can be rewritten using the async/await primitives.

Installation

npm install chrome-remote-interface

Install globally (-g) to just use the bundled client.

Implementations

This module should work with every application implementing the Chrome Debugging Protocol. In particular, it has been tested against the following implementations:

Implementation | Protocol version | Protocol | List | New | Activate | Close | Version ---------------------------|--------------------|------------|--------|-------|------------|---------|----------- Google Chrome | tip-of-tree | yes | yes | yes | yes | yes | yes Microsoft Edge | partial | yes | yes | no | no | no | yes Node.js (v6.3.0+) | node | yes | no | no | no | no | yes Safari (iOS) | partial | no | yes | no | no | no | no

The meaning of target varies according to the implementation, for example, each Chrome tab represents a target whereas for Node.js a target is the currently inspected script.

Setup

An instance of either Chrome itself or another implementation needs to be running on a known port in order to use this module (defaults to localhost:9222).

Chrome/Chromium

Desktop

Start Chrome with the --remote-debugging-port option, for example:

google-chrome --remote-debugging-port=9222
Headless

Since version 57, additionally use the --headless option, for example:

google-chrome --headless --remote-debugging-port=9222

Please note that currently the DevTools methods are not properly supported in headless mode; use the Target domain instead. See #83 and #84 for more information.

Android

Plug the device and enable the port forwarding, for example:

adb forward tcp:9222 localabstract:chrome_devtools_remote
WebView

In order to be inspectable, a WebView must be configured for debugging and the corresponding process ID must be known. There are several ways to obtain it, for example:

adb shell grep -a webview_devtools_remote /proc/net/unix

Finally, port forwarding can be enabled as follows:

adb forward tcp:9222 localabstract:webview_devtools_remote_<pid>

Edge

Install and run the Edge Diagnostics Adapter.

Node.js

Start Node.js with the --inspect option, for example:

node --inspect=9222 script.js

Safari (iOS)

Install and run the iOS WebKit Debug Proxy.

Bundled client

This module comes with a bundled client application that can be used to interactively control a remote instance.

Target management

The bundled client exposes subcommands to interact with the HTTP frontend (e.g., List, New, etc.), run with --help to display the list of available options.

Here are some examples:

$ chrome-remote-interface new 'http://example.com'
{
    "description": "",
    "devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:9222/devtools/page/b049bb56-de7d-424c-a331-6ae44cf7ae01",
    "id": "b049bb56-de7d-424c-a331-6ae44cf7ae01",
    "thumbnailUrl": "/thumb/b049bb56-de7d-424c-a331-6ae44cf7ae01",
    "title": "",
    "type": "page",
    "url": "http://example.com/",
    "webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/b049bb56-de7d-424c-a331-6ae44cf7ae01"
}
$ chrome-remote-interface close 'b049bb56-de7d-424c-a331-6ae44cf7ae01'

Inspection

Using the inspect subcommand it is possible to perform command execution and event binding in a REPL fashion. But unlike the regular API the callbacks are overridden to conveniently display the result of the commands and the message of the events. Also, the event binding is simplified here, executing a shorthand method (e.g., Page.loadEventFired()) toggles the event registration.

Remember that the REPL interface provides completion.

Here is a sample session:

$ chrome-remote-interface inspect
>>> Runtime.evaluate({expression: 'window.location.toString()'})
{ result:
   { result:
      { type: 'string',
        value: 'https://www.google.it/_/chrome/newtab?espv=2&ie=UTF-8' },
     wasThrown: false } }
>>> Page.enable()
{ result: {} }
>>> Page.loadEventFired() // registered
{ 'Page.loadEventFired': true }
>>> Page.loadEventFired() // unregistered
{ 'Page.loadEventFired': false }
>>> Page.loadEventFired() // registered
{ 'Page.loadEventFired': true }
>>> Page.navigate({url: 'https://github.com'})
{ result: { frameId: '28677.1' } }
{ 'Page.loadEventFired': { timestamp: 21385.383076 } }
>>> Runtime.evaluate({expression: 'window.location.toString()'})
{ result:
   { result: { type: 'string', value: 'https://github.com/' },
     wasThrown: false } }

Event filtering

To reduce the amount of data displayed by the event listeners it is possible to provide a filter function. In this example only the resource URL is shown:

$ chrome-remote-interface inspect
>>> Network.enable()
{ result: {} }
>>> Network.requestWillBeSent(params => params.request.url)
{ 'Network.requestWillBeSent': 'params => params.request.url' }
>>> Page.navigate({url: 'https://www.wikipedia.org'})
{ 'Network.requestWillBeSent': 'https://www.wikipedia.org/' }
{ result: { frameId: '5530.1' } }
{ 'Network.requestWillBeSent': 'https://www.wikipedia.org/portal/wikipedia.org/assets/img/Wikipedia_wordmark.png' }
{ 'Network.requestWillBeSent': 'https://www.wikipedia.org/portal/wikipedia.org/assets/img/Wikipedia-logo-v2.png' }
{ 'Network.requestWillBeSent': 'https://www.wikipedia.org/portal/wikipedia.org/assets/js/index-3b68787aa6.js' }
{ 'Network.requestWillBeSent': 'https://www.wikipedia.org/portal/wikipedia.org/assets/js/gt-ie9-c84bf66d33.js' }
{ 'Network.requestWillBeSent': 'https://www.wikipedia.org/portal/wikipedia.org/assets/img/sprite-bookshelf_icons.png?16ed124e8ca7c5ce9d463e8f99b2064427366360' }
{ 'Network.requestWillBeSent': 'https://www.wikipedia.org/portal/wikipedia.org/assets/img/sprite-project-logos.png?9afc01c5efe0a8fb6512c776955e2ad3eb48fbca' }

Embedded documentation

In both the REPL and the regular API every object of the protocol is decorated with the meta information found within the descriptor. In addition The category field is added, which determines if the member is a command, an event or a type.

For example to learn how to call Page.navigate:

>>> Page.navigate
{ [Function]
  category: 'command',
  parameters: { url: { type: 'string', description: 'URL to navigate the page to.' } },
  returns:
   [ { name: 'frameId',
       '$ref': 'FrameId',
       hidden: true,
       description: 'Frame id that will be navigated.' } ],
  description: 'Navigates current page to the given URL.',
  handlers: [ 'browser', 'renderer' ] }

To learn about the parameters returned by the Network.requestWillBeSent event:

>>> Network.requestWillBeSent
{ [Function]
  category: 'event',
  description: 'Fired when page is about to send HTTP request.',
  parameters:
   { requestId: { '$ref': 'RequestId', description: 'Request identifier.' },
     frameId:
      { '$ref': 'Page.FrameId',
        description: 'Frame identifier.',
        hidden: true },
     loaderId: { '$ref': 'LoaderId', description: 'Loader identifier.' },
     documentURL:
      { type: 'string',
        description: 'URL of the document this request is loaded for.' },
     request: { '$ref': 'Request', description: 'Request data.' },
     timestamp: { '$ref': 'Timestamp', description: 'Timestamp.' },
     wallTime:
      { '$ref': 'Timestamp',
        hidden: true,
        description: 'UTC Timestamp.' },
     initiator: { '$ref': 'Initiator', description: 'Request initiator.' },
     redirectResponse:
      { optional: true,
        '$ref': 'Response',
        description: 'Redirect response data.' },
     type:
      { '$ref': 'Page.ResourceType',
        optional: true,
        hidden: true,
        description: 'Type of this resource.' } } }

To inspect the Network.Request (note that unlike commands and events, types are named in upper camel case) type:

>>> Network.Request
{ category: 'type',
  id: 'Request',
  type: 'object',
  description: 'HTTP request data.',
  properties:
   { url: { type: 'string', description: 'Request URL.' },
     method: { type: 'string', description: 'HTTP request method.' },
     headers: { '$ref': 'Headers', description: 'HTTP request headers.' },
     postData:
      { type: 'string',
        optional: true,
        description: 'HTTP POST request data.' },
     mixedContentType:
      { optional: true,
        type: 'string',
        enum: [Object],
        description: 'The mixed content status of the request, as defined in http://www.w3.org/TR/mixed-content/' },
     initialPriority:
      { '$ref': 'ResourcePriority',
        description: 'Priority of the resource request at the time request is sent.' } } }

Chrome Debugging Protocol versions

chrome-remote-interface uses the local version of the protocol descriptor by default. This file is manually updated from time to time using scripts/update-protocol.sh and pushed to this repository.

This behavior can be changed by setting the remote option to true upon connection, in which case the remote instance is asked to provide its own protocol descriptor.

Currently Chrome is not able to do that (see #10), so the protocol descriptor is fetched from the proper source repository.

To override the above behavior there are basically three options:

  • pass a custom protocol descriptor upon connection (protocol option);

  • use the raw version of the commands and events interface;

  • update the local copy with scripts/update-protocol.sh (not present when fetched with npm install).

Browser usage

This module is able to run within a web context, with obvious limitations though, namely external HTTP requests (List, New, etc.) cannot be performed directly, for this reason the user must provide a global criRequest in order to use them:

function criRequest(options, callback) {}

options is the same object used by the Node.js http module and callback is a function taking two arguments: err (JavaScript Error object or null) and data (string result).

Using webpack

It just works, simply require this module:

const CDP = require('chrome-remote-interface');

To use a non-minified version manually run webpack with:

DEBUG=true npm run webpack

Using vanilla JavaScript

To generate a JavaScript file that can be used with a <script> element:

  1. run npm install from the root directory;

  2. manually run webpack with:

     TARGET=var npm run webpack
     TARGET=var DEBUG=true npm run webpack
  3. use as:

    <script>
      function criRequest(options, callback) { /*...*/ }
    </script>
    <script src="chrome-remote-interface.js"></script>

API

The API consists of three parts:

CDP([options], [callback])

Connects to a remote instance using the Chrome Debugging Protocol.

options is an object with the following optional properties:

  • host: HTTP frontend host. Defaults to localhost;

  • port: HTTP frontend port. Defaults to 9222;

  • secure: HTTPS/WSS frontend. Defaults to false;

  • target: determines which target this client should attach to. The behavior changes according to the type:

    • a function that takes the array returned by the List method and returns a target or its numeric index relative to the array;
    • a target object like those returned by the New and List methods;
    • a string representing the raw WebSocket URL, in this case host and port are not used to fetch the target list.

    Defaults to a function which returns the first available target according to the implementation (note that at most one connection can be established to the same target);

  • protocol: Chrome Debugging Protocol descriptor object. Defaults to use the protocol chosen according to the remote option;

  • remote: a boolean indicating whether the protocol must be fetched remotely or if the local version must be used. It has no effect if the protocol option is set. Defaults to false.

These options are also valid properties of all the instances of the CDP class.

callback is a listener automatically added to the connect event of the 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.

The EventEmitter supports the following events:

Event: 'connect'

function (client) {}

Emitted when the connection to the WebSocket is established.

client is an instance of the CDP class.

Event: 'disconnect'

function () {}

Emitted when an instance closes the WebSocket connection.

This may happen for example when the user opens DevTools for the currently inspected Chrome target.

Event: 'error'

function (err) {}

Emitted when http://host:port/json cannot be reached or if it is not possible to connect to the WebSocket.

err is an instance of Error.

CDP.Protocol([options], [callback])

Fetch the Chrome Debugging Protocol descriptor.

options is an object with the following optional properties:

  • host: HTTP frontend host. Defaults to localhost;
  • port: HTTP frontend port. Defaults to 9222;
  • secure: HTTPS/WSS frontend. Defaults to false;
  • remote: a boolean indicating whether the protocol must be fetched remotely or if the local version must be returned. If it is not possible to fulfill the request then the local version is used. Defaults to false.

callback is executed when the protocol is fetched, it gets the following arguments:

  • err: a Error object indicating the success status;
  • protocol: an object with the following properties:
    • remote: a boolean indicating whether the returned descriptor is the remote version or not (due to user choice or error);
    • descriptor: the Chrome Debugging Protocol descriptor.

When callback is omitted a Promise object is returned.

For example:

const CDP = require('chrome-remote-interface');
CDP.Protocol(function (err, protocol) {
    if (!err) {
        console.log(JSON.stringify(protocol.descriptor, null, 4));
    }
});

CDP.List([options], [callback])

Request the list of the available open targets/tabs of the remote instance.

options is an object with the following optional properties:

  • host: HTTP frontend host. Defaults to localhost;
  • port: HTTP frontend port. Defaults to 9222;
  • secure: HTTPS/WSS frontend. Defaults to false.

callback is executed when the list is correctly received, it gets the following arguments:

  • err: a Error object indicating the success status;
  • targets: the array returned by http://host:port/json/list containing the target list.

When callback is omitted a Promise object is returned.

For example:

const CDP = require('chrome-remote-interface');
CDP.List(function (err, targets) {
    if (!err) {
        console.log(targets);
    }
});

CDP.New([options], [callback])

Create a new target/tab in the remote instance.

options is an object with the following optional properties:

  • host: HTTP frontend host. Defaults to localhost;
  • port: HTTP frontend port. Defaults to 9222;
  • secure: HTTPS/WSS frontend. Defaults to false;
  • url: URL to load in the new target/tab. Defaults to about:blank.

callback is executed when the target is created, it gets the following arguments:

  • err: a Error object indicating the success status;
  • target: the object returned by http://host:port/json/new containing the target.

When callback is omitted a Promise object is returned.

For example:

const CDP = require('chrome-remote-interface');
CDP.New(function (err, target) {
    if (!err) {
        console.log(target);
    }
});

CDP.Activate([options], [callback])

Activate an open target/tab of the remote instance.

options is an object with the following properties:

  • host: HTTP frontend host. Defaults to localhost;
  • port: HTTP frontend port. Defaults to 9222;
  • secure: HTTPS/WSS frontend. Defaults to false;
  • id: Target id. Required, no default.

callback is executed when the response to the activation request is received. It gets the following arguments:

  • err: a Error object indicating the success status;

When callback is omitted a Promise object is returned.

For example:

const CDP = require('chrome-remote-interface');
CDP.Activate({'id': 'CC46FBFA-3BDA-493B-B2E4-2BE6EB0D97EC'}, function (err) {
    if (!err) {
        console.log('target is activated');
    }
});

CDP.Close([options], [callback])

Close an open target/tab of the remote instance.

options is an object with the following properties:

  • host: HTTP frontend host. Defaults to localhost;
  • port: HTTP frontend port. Defaults to 9222;
  • secure: HTTPS/WSS frontend. Defaults to false;
  • id: Target id. Required, no default.

callback is executed when the response to the close request is received. It gets the following arguments:

  • err: a Error object indicating the success status;

When callback is omitted a Promise object is returned.

For example:

const CDP = require('chrome-remote-interface');
CDP.Close({'id': 'CC46FBFA-3BDA-493B-B2E4-2BE6EB0D97EC'}, function (err) {
    if (!err) {
        console.log('target is closing');
    }
});

Note that the callback is fired when the target is queued for removal, but the actual removal will occur asynchronously.

CDP.Version([options], [callback])

Request version information from the remote instance.

options is an object with the following optional properties:

  • host: HTTP frontend host. Defaults to localhost;
  • port: HTTP frontend port. Defaults to 9222;
  • secure: HTTPS/WSS frontend. Defaults to false.

callback is executed when the version information is correctly received, it gets the following arguments:

  • err: a Error object indicating the success status;
  • info: a JSON object returned by http://host:port/json/version containing the version information.

When callback is omitted a Promise object is returned.

For example:

const CDP = require('chrome-remote-interface');
CDP.Version(function (err, info) {
    if (!err) {
        console.log(info);
    }
});

Class: CDP

Event: 'event'

function (message) {}

Emitted when the remote instance sends any notification through the WebSocket.

message is the object received, it has the following properties:

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

Refer to the Chrome Debugging Protocol specification for more information.

For example:

client.on('event', function (message) {
    if (message.method === 'Network.requestWillBeSent') {
        console.log(message.params);
    }
});

Event: '<domain>.<method>'

function (params) {}

Emitted when the remote instance sends a notification for <domain>.<method> through the WebSocket.

params is an object containing the payload.

This is just a utility event which allows to easily listen for specific notifications (see 'event'), for example:

client.on('Network.requestWillBeSent', console.log);

Event: 'ready'

function () {}

Emitted every time that there are no more pending commands waiting for a response from the remote instance. The interaction is asynchronous so the only way to serialize a sequence of commands is to use the callback provided by the send method. This event acts as a barrier and it is useful to avoid the callback hell in certain simple situations.

Users are encouraged to extensively check the response of each method and should prefer the promises API when dealing with complex asynchronous program flows.

For example to load a URL only after having enabled the notifications of both Network and Page domains:

client.Network.enable();
client.Page.enable();
client.once('ready', function () {
    client.Page.navigate({'url': 'https://github.com'});
});

In this particular case, not enforcing this kind of serialization may cause that the remote instance does not properly deliver the desired notifications the client.

client.send(method, [params], [callback])

Issue a command to the remote instance.

method is a string describing the command.

params is an object containing the payload.

callback is executed when the remote instance sends a response to this command, it gets the following arguments:

  • error: a boolean value indicating the success status, as reported by the remote instance;
  • response: an object containing either the response (result field, if error === false) or the indication of the error (error field, if error === true).

When callback is omitted a Promise object is returned instead, with the fulfilled/rejected states implemented according to the error parameter.

Note that the field id mentioned in the Chrome Debugging Protocol specification is managed internally and it is not exposed to the user.

For example:

client.send('Page.navigate', {'url': 'https://github.com'}, console.log);

client.<domain>.<method>([params], [callback])

Just a shorthand for:

client.send('<domain>.<method>', params, callback);

For example:

client.Page.navigate({'url': 'https://github.com'}, console.log);

client.<domain>.<event>(callback)

Just a shorthand for:

client.on('<domain>.<event>', callback);

For example:

client.Network.requestWillBeSent(console.log);

client.close([callback])

Close the connection to the remote instance.

callback is executed when the WebSocket is successfully closed.

When callback is omitted a Promise object is returned.

Contributors

Resources