Giter VIP home page Giter VIP logo

chrome-remote-interface's Introduction

chrome-remote-interface

CI 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');

async function example() {
    let client;
    try {
        // connect to endpoint
        client = await CDP();
        // extract domains
        const {Network, Page} = client;
        // setup handlers
        Network.requestWillBeSent((params) => {
            console.log(params.request.url);
        });
        // enable events then start!
        await Network.enable();
        await Page.enable();
        await Page.navigate({url: 'https://github.com'});
        await Page.loadEventFired();
    } catch (err) {
        console.error(err);
    } finally {
        if (client) {
            await client.close();
        }
    }
}

example();

Find more examples in the wiki. You may also want to take a look at the FAQ.

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
Chrome tip-of-tree yes¹ yes yes yes yes yes
Opera tip-of-tree yes yes yes yes yes yes
Node.js (v6.3.0+) node yes no no no no yes
Safari (iOS) partial no yes no no no no
Edge partial yes yes no no no yes
Firefox (Nightly) partial yes yes no yes yes yes

¹ Not available on Chrome for Android, hence a local version of the protocol must be used.

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 59, additionally use the --headless option, for example:

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

Android

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

adb forward tcp:9222 localabstract:chrome_devtools_remote

Note that in Android, Chrome does not have its own protocol available, a local version must be used. See here for more information.

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>

Opera

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

opera --remote-debugging-port=9222

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. Then use it with the local option set to true to use the local version of the protocol or pass a custom descriptor upon connection (protocol option).

Edge

Start Edge with the --devtools-server-port option, for example:

MicrosoftEdge.exe --devtools-server-port 9222 about:blank

Please find more information here.

Firefox (Nightly)

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

firefox --remote-debugging-port 9222

Bear in mind that this is an experimental feature of Firefox.

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 that provides completion.

Here is a sample session:

$ chrome-remote-interface inspect
>>> Runtime.evaluate({expression: 'window.location.toString()'})
{ result: { type: 'string', value: 'about:blank' } }
>>> Page.enable()
{}
>>> Page.loadEventFired(console.log)
[Function]
>>> Page.navigate({url: 'https://github.com'})
{ frameId: 'E1657E22F06E6E0BE13DFA8130C20298',
  loaderId: '439236ADE39978F98C20E8939A32D3A5' }
>>> { timestamp: 7454.721299 } // from Page.loadEventFired
>>> Runtime.evaluate({expression: 'window.location.toString()'})
{ result: { type: 'string', value: 'https://github.com/' } }

Additionally there are some custom commands available:

>>> .help
[...]
.reset    Remove all the registered event handlers
.target   Display the current target

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

By default chrome-remote-interface asks the remote instance to provide its own protocol.

This behavior can be changed by setting the local option to true upon connection, in which case the local version of the protocol descriptor is used. This file is manually updated from time to time using scripts/update-protocol.sh and pushed to this repository.

To further override the above behavior there are basically two options:

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

  • use the raw version of the commands and events interface to use bleeding-edge features that do not appear in the local version of the protocol descriptor;

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');

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
    
  3. use as:

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

TypeScript Support

TypeScript definitions are kindly provided by Khairul Azhar Kasmiran and Seth Westphal, and can be installed from DefinitelyTyped:

npm install --save-dev @types/chrome-remote-interface

Note that the TypeScript definitions are automatically generated from the npm package [email protected]. For other versions of devtools-protocol:

  1. Install patch-package using the instructions given.
  2. Copy the contents of the corresponding https://github.com/ChromeDevTools/devtools-protocol/tree/master/types folder (according to commit) into node_modules/devtools-protocol/types.
  3. Run npx patch-package devtools-protocol so that the changes persist across an npm install.

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;

  • useHostName: do not perform a DNS lookup of the host. Defaults to false;

  • alterPath: a function taking and returning the path fragment of a URL before that a request happens. Defaults to the identity function;

  • 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, yet they are used to complete the URL if relative;
    • a string representing the target id.

    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 local option;

  • local: 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. In addition to that, the webSocketUrl field contains the currently used WebSocket URL.

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 the error event is 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: '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;
  • useHostName: do not perform a DNS lookup of the host. Defaults to false;
  • alterPath: a function taking and returning the path fragment of a URL before that a request happens. Defaults to the identity function;
  • local: a boolean indicating whether the protocol must be fetched remotely or if the local version must be returned. Defaults to false.

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

When callback is omitted a Promise object is returned.

For example:

const CDP = require('chrome-remote-interface');
CDP.Protocol((err, protocol) => {
    if (!err) {
        console.log(JSON.stringify(protocol, 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;
  • useHostName: do not perform a DNS lookup of the host. Defaults to false;
  • alterPath: a function taking and returning the path fragment of a URL before that a request happens. Defaults to the identity function.

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((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;
  • useHostName: do not perform a DNS lookup of the host. Defaults to false;
  • alterPath: a function taking and returning the path fragment of a URL before that a request happens. Defaults to the identity function;
  • 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((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;
  • useHostName: do not perform a DNS lookup of the host. Defaults to false;
  • alterPath: a function taking and returning the path fragment of a URL before that a request happens. Defaults to the identity function;
  • 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'}, (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;
  • useHostName: do not perform a DNS lookup of the host. Defaults to false;
  • alterPath: a function taking and returning the path fragment of a URL before that a request happens. Defaults to the identity function;
  • 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'}, (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;
  • useHostName: do not perform a DNS lookup of the host. Defaults to false;
  • alterPath: a function taking and returning the path fragment of a URL before that a request happens. Defaults to the identity function.

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((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;
  • sessionId: an optional string representing the session identifier.

Refer to the Chrome Debugging Protocol specification for more information.

For example:

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

Event: '<domain>.<method>'

function (params, sessionId) {}

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

params is an object containing the payload.

sessionId is an optional string representing the session identifier.

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

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

Additionally, the equivalent <domain>.on('<method>', ...) syntax is available, for example:

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

Event: '<domain>.<method>.<sessionId>'

function (params, sessionId) {}

Equivalent to the following but only for those events belonging to the given session:

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

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', () => {
    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.

Event: 'disconnect'

function () {}

Emitted when the instance closes the WebSocket connection.

This may happen for example when the user opens DevTools or when the tab is closed.

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

Issue a command to the remote instance.

method is a string describing the command.

params is an object containing the payload.

sessionId is a string representing the session identifier.

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. The Error object returned contains two additional parameters: request and response which contain the raw massages, useful for debugging purposes. In case of low-level WebSocket errors, the error parameter contains the originating Error object and no response is returned.

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], [sessionId], [callback])

Just a shorthand for:

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

For example:

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

client.<domain>.<event>([sessionId], [callback])

Just a shorthand for:

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

When callback is omitted the event is registered only once and a Promise object is returned. Notice though that in this case the optional sessionId usually passed to callback is not returned.

When callback is provided, it returns a function that can be used to unsubscribe callback from the event, it can be useful when anonymous functions are used as callbacks.

For example:

const unsubscribe = client.Network.requestWillBeSent((params, sessionId) => {
    console.log(params.request.url);
});
unsubscribe();

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.

client['<domain>.<name>']

Just a shorthand for:

client.<domain>.<name>

Where <name> can be a command, an event, or a type.

FAQ

Invoking Domain.methodOrEvent I obtain Domain.methodOrEvent is not a function

This means that you are trying to use a method or an event that are not present in the protocol descriptor that you are using.

If the protocol is fetched from Chrome directly, then it means that this version of Chrome does not support that feature. The solution is to update it.

If you are using a local or custom version of the protocol, then it means that the version is obsolete. The solution is to provide an up-to-date one, or if you are using the protocol embedded in chrome-remote-interface, make sure to be running the latest version of this module. In case the embedded protocol is obsolete, please file an issue.

See here for more information.

Invoking Domain.method I obtain Domain.method wasn't found

This means that you are providing a custom or local protocol descriptor (CDP({protocol: customProtocol})) which declares Domain.method while the Chrome version that you are using does not support it.

To inspect the currently available protocol descriptor use:

$ chrome-remote-interface inspect

See here for more information.

Why my program stalls or behave unexpectedly if I run Chrome in a Docker container?

This happens because the size of /dev/shm is set to 64MB by default in Docker and may not be enough for Chrome to navigate certain web pages.

You can change this value by running your container with, say, --shm-size=256m.

Using Runtime.evaluate with awaitPromise: true I sometimes obtain Error: Promise was collected

This is thrown by Runtime.evaluate when the browser-side promise gets collected by the Chrome's garbage collector, this happens when the whole JavaScript execution environment is invalidated, e.g., a when page is navigated or reloaded while a promise is still waiting to be resolved.

Here is an example:

$ chrome-remote-interface inspect
>>> Runtime.evaluate({expression: `new Promise(() => {})`, awaitPromise: true})
>>> Page.reload() // then wait several seconds
{ result: {} }
{ error: { code: -32000, message: 'Promise was collected' } }

To fix this, just make sure there are no pending promises before closing, reloading, etc. a page.

How does this compare to Puppeteer?

Puppeteer is an additional high-level API built upon the Chrome Debugging Protocol which, among the other things, may start and use a bundled version of Chromium instead of the one installed on your system. Use it if its API meets your needs as it would probably be easier to work with.

chrome-remote-interface instead is just a general purpose 1:1 Node.js binding for the Chrome Debugging Protocol. Use it if you need all the power of the raw protocol, e.g., to implement your own high-level API.

See #240 for a more thorough discussion.

Contributors

Resources

chrome-remote-interface's People

Contributors

anber avatar benjaminwood avatar bobajeff avatar c0b avatar cyrus-and avatar dependabot[bot] avatar derevnjuk avatar diego-fernandez-santos avatar gcochard avatar hbakhtiyor avatar heipei avatar hgsgtk avatar janpot avatar jasonlaster avatar kazarmy avatar nehashri avatar nikolai-at-brightdata avatar paambaati avatar paulirish avatar perpetuallight avatar sidorares avatar slhck avatar stephenmathieson avatar walshie4 avatar westy92 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

chrome-remote-interface's Issues

Add support for Microsoft Edge via Edge Diagnostics Adaptor

We at Microsoft has now made it possible to debug Microsoft Edge via Edge Diagnostics Adaptor over the Chrome Debugging Protocol.

It has a protocol.json file which could be fetched from the repo at https://github.com/Microsoft/edge-diagnostics-adaptor/blob/master/protocol.json, or we could add an HTTP endpoint to fetch it from something like /json/protocol directly from the adaptor.

We think it cold be exciting to have this library working with multiple browsers supporting the Chrome Debugging Protocol.

Are you interested in collaboration?

Dynamic port to profile

Hi there,

Not sure if this is a bug but anyways let me put forward what i am trying to do using the interface. Let me know if I am missing anything obvious.

I am trying to automate chrome using webdriver and webdriver opens chrome on any random port, which can not be forced as of now (a bug). I want to read the port it opens the chrome in and pass that to chrome-remote-interface every time. I tried changing the port in defaults.js with the new port after chrome opens but it did not work. Do you have any suggestions in make debugging port dynamic?

Thanks,
Xhings

Error connecting when using 'iOS WebKit Debug Proxy'

Hi there,

I am currently running into errors when trying to connect the chrome-remote-interface module to the debug server running on 'http://localhost:9222/' when it has been started with the iOS WebKit Debug Proxy.

I feel this might be a compatibility issue but just wanted to confirm that I am not missing anything obvious here, since to my understanding the iOS WebKit Debug Proxy uses the Chrome Remote Debugging Protocol and therefore the chrome-remote-interface should be able to connect. Also confirming that I can open the Dev Tools Frontend Url and see the proper network requests from my local iPhone simulator being emitted, so I know that this part is working as expected.

When running this code:

var chrome = require('chrome-remote-interface')

var url = 'https://github.com/'
var options = {
    chooseTab: function(tabs) {
      var targetTab = tabs.filter(function(tab) {
        return tab.url == url
      })

      if(targetTab.length) {
        return tabs.indexOf(targetTab[0])
      } 

      return 0
    }
  }

chrome(options, function(client) {

  console.log('Connected!');

  client.Page.enable();

  client.once('ready', function () {
      client.Page.navigate({
        'url': 'https://github.com'
      });
  });
}).on('error', function(err) {
  console.error('Cannot connect to IOS:');
  console.log(err);
});

I get this error (with no other console messages, it does not make it to the callback):

[Error: <html><title>Error 404 (Not Found)</title>
<p><b>404.</b> <ins>That's an error.</ins>
<p>The requested URL <code>/json/list</code> was not found.
</html>]

I seems the root of the issue is that the debug server for iwdp returns a 404 error when visiting route 'json/list'.

My main question is, should iwdp be providing the route 'json/list' for the chrome-remote-interface to be able to connect, or am I missing something else in setting up this connection?

Thanks again for your help here.

Max number of connections

I am having 200 nw-js instances and every instance has a different port. I am connecting with this library to them dependent on port. The issue is, that I cannot have more then 115 connections opened.

Is there some sort of a limit on the library?

We know it is not the hardware, we still have a lot in reserve.

ReferenceError: Promise is not defined

Hi,

I used this interface with success in a ubuntu based docker container.
I've upgraded now the system to the latest docker version and I have this error...
ReferenceError: Promise is not defined. See bellow

Not sure where to start... Or what is the cause of this. Any ideas?

root@b17e220955cf:/producer# node /scripts/capture-test.js 

/node_modules/chrome-remote-interface/lib/chrome.js:42
        return new Promise(function (fulfill, reject) {
                   ^
ReferenceError: Promise is not defined
    at Chrome.send (/node_modules/chrome-remote-interface/lib/chrome.js:42:20)
    at Object.handler [as enable] (/node_modules/chrome-remote-interface/lib/chrome.js:108:21)
    at EventEmitter.<anonymous> (/scripts/capture-test.js:9:17)
    at EventEmitter.emit (events.js:95:17)
    at WebSocket.<anonymous> (/node_modules/chrome-remote-interface/lib/chrome.js:251:23)
    at WebSocket.EventEmitter.emit (events.js:92:17)
    at WebSocket.establishConnection (/node_modules/chrome-remote-interface/node_modules/ws/lib/WebSocket.js:887:8)
    at ClientRequest.upgrade (/node_modules/chrome-remote-interface/node_modules/ws/lib/WebSocket.js:778:25)
    at ClientRequest.g (events.js:180:16)
    at ClientRequest.EventEmitter.emit (events.js:106:17)

capture-test.js - contains your sample API usage code. Copy pasted.

chrome.DOM.getDocument return documentURL font

Hello,
It happens that a document returns a font element
This occurs when there will be two or more inspectors, how to choose the right?

chrome.DOM.getDocument(function (error, params) {
    console.log(error, params);
    chrome.DOM.querySelectorAll({
        'nodeId': params.root.nodeId,
        'selector': 'img'
    }, function (error, params) {
        console.log(params);
        chrome.close();
    });
});

{ root:
{ nodeId: 1,
nodeType: 9,
nodeName: '#document',
localName: '',
nodeValue: '',
childNodeCount: 1,
children: [ [Object] ],
documentURL: 'https://fonts.googleapis.com/css?family=Roboto:400,300,500,700|Neucha&subset=latin,greek,cyrillic,greek-ext,cyrillic-ext,latin-ext',
baseURL: 'https://fonts.googleapis.com/css?family=Roboto:400,300,500,700|Neucha&subset=latin,greek,cyrillic,greek-ext,cyrillic-ext,latin-ext',
xmlVersion: '' } }

Network.getResponseBody callback results are switched

When trying to get body of a request, body is always boolean and base64Encoded is the body response.

Body: false
base64Encoded:  { body: '<html><body style="margin:0;padding:0"></body></html>',
  base64Encoded: false }

How to wait for browser event?

Hi,

This is a question not an issue, so if it's not the right place to post this, sorry.
I'm trying out this piece of code, but I can't figure out how to "wait" for something from the browser.

For example:
I want to go to a url, load a page and wait for something in the console or for an event.
The url is opening, but the nodejs code is immediately closed(which makes sense).

Page.navigate({'url': 'http://mypage.com'});
// how to wait? 

This is your sample code:

var Chrome = require('chrome-remote-interface');
Chrome(function (chrome) {
    with (chrome) {
        Network.requestWillBeSent(function (params) {
            console.log(params.request.url);
        });
        Page.loadEventFired(close);
        Network.enable();
        Page.enable();
        once('ready', function () {
            Page.navigate({'url': 'https://github.com'});
            //how to wait for a console message or event?
        });
    }
}).on('error', function () {
    console.error('Cannot connect to Chrome');
});

Thanks in advance!

'Debugger.getScriptSource' method is not working

While the event "Debugger.scriptParsed" is correctly handled, getScriptSource() always returns "undefined" value.

I guess I was invoking the method (getScriptSource()) in a wrong way as inspector.js of blink is calling the getScriptSource() via DebuggerAgent, which seems to be the wrapper of Debugger interface.


var spawn=require('child_process').spawn;
var Chrome = require('chrome-remote-interface');
var initWaitTime = 2000;
var ChromePath = "/usr/bin/chromium-browser";

function runChrome(extensionDir) {
var args = [];
args.push("--remote-debugging-port=9222");
args.push("--user-data-dir=remote-profile");
args.push("-no-default-browser-check");
spawn(ChromePath, args)
};

function runChromeDebugger(urlToVisit) {
console.log("runChromeDebugger");
options = {};
Chrome(options, function (chrome) {
with (chrome) {
on('Debugger.scriptParsed', function (msg) {
console.log("==================================");
console.log(JSON.stringify(msg));
console.log("scriptParsed scriptId: " + msg.scriptId); // scriptId is correctly returned
var ssrc = chrome.Debugger.getScriptSource({'scriptId':msg.scriptId});
console.log("scriptSource :" + ssrc); // bug? always undefined
});
Page.enable();
Debugger.enable();
Page.navigate({'url': urlToVisit});
console.log("[*] opened [" + urlToVisit + "]");
}
}).on('error', function () {
console.error('Cannot connect to Chrome');
});
}

if (require.main == module) {
runChrome("/home/blee/project/chrome-extension-integrity/sample/buzzdoc");
var urlToVisit = "http://google.com"
setTimeout(function() {runChromeDebugger(urlToVisit)}, initWaitTime);
}

node-inspector support

node-inspector spawns debugger protocol compatible API on ws://127.0.0.1:8080/?port=5858. Path goes where?

Closing and opening a new connection

I see you have a test for this very case, but I am still getting some issues where I am closing a connection and opening a new one in my test suite, and it has issues sometimes.

I'm not sure what the root cause is, the only thing I can think is it isn't completely finished closing before starting to open a new connection. Since it also removes all listeners when calling chrome.close(), I can't add my own to the websocket.

I'm bypassing that method and just using this for now:

closeConnection(){
  return new Promise((resolve, reject) => {
    this.chrome.ws.onclose = function(event){
      event.target.removeAllListeners();
      resolve(true);
    }
    this.chrome.ws.close();
  });
}

Would you be interested in a pull request adding a promise/callback to chrome.close()?

Split repl into a different repo, keep this as just the library

Is there any possibility of splitting this up so that chrome-remote-interface is just an npm package containing the library only? i.e. get rid of the repl client and move it into a separate npm package which depends on this one?

For tools that are controlling DevTools directly, having the repl client built into this package doesn't make much sense.

Messages with non-specific response do not fulfill promises

if (message.result) {
    callback(false, message.result);
} else if (message.error) {
    callback(true, message.error);
}

For Debugger.stepInto() the resulting promise is neither fulfilled nor rejected, because server replies with {id: #} return object. Tested for node-inspector.

fetch developer version protocol need a fix

recently I found fetching protocol failed with this; so the protocol.json has divided into 2 parts, maybe here just need to switch to the browser_protocol.json ?

➸ curl -s 'https://chromium.googlesource.com/chromium/src/+/84cc4cdf21633b6209f2561e78fea87d40e84329/third_party/WebKit/Source/devtools/protocol.json?format=TEXT'
VGhlIHByb3RvY29sLmpzb24gaGFzIHNwbGl0IGludG8gc2VwYXJhdGUgZmlsZXMuIGh0dHBzOi8vY3JidWcuY29tLzU4MDMzNwoKT25lIGZvciB0aGUgYnJvd3Nlciwgb25lIGZvciBhIFY4IGphdmFzY3JpcHQgZW52aXJvbm1lbnQ6CiAgICBzcmMvdGhpcmRfcGFydHkvV2ViS2l0L1NvdXJjZS9jb3JlL2luc3BlY3Rvci9icm93c2VyX3Byb3RvY29sLmpzb24KICAgIHNyYy90aGlyZF9wYXJ0eS9XZWJLaXQvU291cmNlL3BsYXRmb3JtL3Y4X2luc3BlY3Rvci9qc19wcm90b2NvbC5qc29uCgpCcm93c2FibGUgaGVyZToKICAgIGh0dHBzOi8vY2hyb21pdW0uZ29vZ2xlc291cmNlLmNvbS9jaHJvbWl1bS9zcmMvKy9tYXN0ZXIvdGhpcmRfcGFydHkvV2ViS2l0L1NvdXJjZS9jb3JlL2luc3BlY3Rvci9icm93c2VyX3Byb3RvY29sLmpzb24KICAgIGh0dHBzOi8vY2hyb21pdW0uZ29vZ2xlc291cmNlLmNvbS9jaHJvbWl1bS9zcmMvKy9tYXN0ZXIvdGhpcmRfcGFydHkvV2ViS2l0L1NvdXJjZS9wbGF0Zm9ybS92OF9pbnNwZWN0b3IvanNfcHJvdG9jb2wuanNvbgoKTW9yZSBkZXRhaWxzIG9uIHRoZSBwcm90b2NvbDoKICAgIGh0dHBzOi8vZGV2ZWxvcGVyLmNocm9tZS5jb20vZGV2dG9vbHMvZG9jcy9kZWJ1Z2dlci1wcm90b2NvbAo=

➸ node -p 'Buffer.from("VGhlIHByb3RvY29sLmpzb24gaGFzIHNwbGl0IGludG8gc2VwYXJhdGUgZmlsZXMuIGh0dHBzOi8vY3JidWcuY29tLzU4MDMzNwoKT25lIGZvciB0aGUgYnJvd3Nlciwgb25lIGZvciBhIFY4IGphdmFzY3JpcHQgZW52aXJvbm1lbnQ6CiAgICBzcmMvdGhpcmRfcGFydHkvV2ViS2l0L1NvdXJjZS9jb3JlL2luc3BlY3Rvci9icm93c2VyX3Byb3RvY29sLmpzb24KICAgIHNyYy90aGlyZF9wYXJ0eS9XZWJLaXQvU291cmNlL3BsYXRmb3JtL3Y4X2luc3BlY3Rvci9qc19wcm90b2NvbC5qc29uCgpCcm93c2FibGUgaGVyZToKICAgIGh0dHBzOi8vY2hyb21pdW0uZ29vZ2xlc291cmNlLmNvbS9jaHJvbWl1bS9zcmMvKy9tYXN0ZXIvdGhpcmRfcGFydHkvV2ViS2l0L1NvdXJjZS9jb3JlL2luc3BlY3Rvci9icm93c2VyX3Byb3RvY29sLmpzb24KICAgIGh0dHBzOi8vY2hyb21pdW0uZ29vZ2xlc291cmNlLmNvbS9jaHJvbWl1bS9zcmMvKy9tYXN0ZXIvdGhpcmRfcGFydHkvV2ViS2l0L1NvdXJjZS9wbGF0Zm9ybS92OF9pbnNwZWN0b3IvanNfcHJvdG9jb2wuanNvbgoKTW9yZSBkZXRhaWxzIG9uIHRoZSBwcm90b2NvbDoKICAgIGh0dHBzOi8vZGV2ZWxvcGVyLmNocm9tZS5jb20vZGV2dG9vbHMvZG9jcy9kZWJ1Z2dlci1wcm90b2NvbAo=", "base64").toString()'
The protocol.json has split into separate files. https://crbug.com/580337

One for the browser, one for a V8 javascript environment:
    src/third_party/WebKit/Source/core/inspector/browser_protocol.json
    src/third_party/WebKit/Source/platform/v8_inspector/js_protocol.json

Browsable here:
    https://chromium.googlesource.com/chromium/src/+/master/third_party/WebKit/Source/core/inspector/browser_protocol.json
    https://chromium.googlesource.com/chromium/src/+/master/third_party/WebKit/Source/platform/v8_inspector/js_protocol.json

More details on the protocol:
    https://developer.chrome.com/devtools/docs/debugger-protocol

Callback is not a function

I am pasting an error, don't know exactly how this happened.

/app/node_modules/chrome-remote-interface/lib/chrome.js:60
callback();
^

TypeError: callback is not a function
at WebSocket. (/app/node_modules/chrome-remote-interface/lib/chrome.js:60:13)
at WebSocket.g (events.js:286:16)
at emitTwo (events.js:106:13)
at WebSocket.emit (events.js:191:7)
at WebSocket.cleanupWebsocketResources (/app/node_modules/ws/lib/WebSocket.js:950:8)
at emitNone (events.js:91:20)
at Socket.emit (events.js:185:7)
at endReadableNT (_stream_readable.js:926:12)
at _combinedTickCallback (internal/process/next_tick.js:74:11)
at process._tickCallback (internal/process/next_tick.js:98:9)
(node:84862) fs: re-evaluating native module sources is not supported. If you are using the graceful-fs module, please update it to a more recent version.

avoid `with`?

isn't it a consensus since many years back that javascript with keyword is bad and should be avoided at any time?

https://www.barryvan.com.au/2009/05/avoid-javascripts-with-keyword/

The Sample API usage section actually reminded me it's still there; for the first time I saw it I almost couldn't understand how did it work, on this line Page.loadEventFired(close); where is the close variable from, or is it a magic function, finally I realized it's actually the chrome.close
https://github.com/cyrus-and/chrome-remote-interface#sample-api-usage

it's a bad use case I can make a PR to change it this is agreed.

Runtime.evaluate does not handle complex html

I am trying to inject the html from another page into a blank tab. Below is the code I am trying to use to do so. When I put a simple element <div>something</div> the script works as expected. However when I use something more complex, like the body below, it fails silently. Any thoughts?

  Chrome.New(() => {
    Chrome((chromeInstance: any) => {
      chromeInstance.Runtime.evaluate({
        'expression': `document.getElementsByTagName('html')[0].innerHTML = ${body}`
      }, function (error: any, params: any) {
        if (!error) {
          imageExport(chromeInstance, res, {})
        }
      });
    });
  })
const body = `
    <script async="" src="//www.google-analytics.com/analytics.js"></script><script>
      (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
      (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
      m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
      })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
      ga('create', 'UA-9369909-101', 'auto');
      ga('send', 'pageview');
    </script>
    <div class="top-bar">The one and only Goat.com<div class="visible-xs"><br></div><div class="hidden-xs">&nbsp;-&nbsp;</div>is available <a href="https://domainnamesales.com/domain/goat.com" target="_blank">For Lease</a></div>
    <h1>Goat.com</h1>
    <div class="goat-holder">
        <img src="assets/img/goat.png" alt="Greatest of All Time" onclick="document.getElementById('goatPlay').play()">
    </div>
    <div id="beaver" style="bottom: -10px;">
      <a href="http://beaver.com" target="_blank">
        <img src="assets/img/beaver-canada.png" alt="beaver.com">
      </a>
    </div>
    <audio id="goatPlay" autoplay="">
        <source src="assets/audio/wav/goat.wav" id="wavSource" type="audio/wav">
        <source src="assets/audio/mp3/goat.mp3" id="mp3Source" type="audio/mpeg">
        <source src="assets/audio/ogg/goat.ogg" id="oggSource" type="audio/ogg">
        Your browser does not support the audio element.
    </audio>
    <script src="bower_components/jquery/dist/jquery.min.js"></script>
    <script src="assets/js/main.js"></script>
`

http://goat.com/

How to Fix the Error "Cannot connect to Chrome: Error: socket hang up"?

C:\Windows\system32>npm install -g chrome-remote-interface

C:\Program Files (x86)\Google\Chrome\Application>chrome --remote-debugging-port=9222

C:\Windows\system32>chrome-remote-interface inspect --host localhost --port 9222
Cannot connect to Chrome: Error: socket hang up

C:\Windows\system32>node -v
v6.9.1

C:\Windows\system32>npm -v
3.10.8

C:\Windows\system32>git --version
git version 2.10.1.windows.1

Browser: Chrome/54.0.2840.71
Microsoft Windows: Version 10.0.10586

Allow connection to arbitrary duplex stream

I'd like to be able to connect to android device without intermediate local port-forwarding adb server. With https://github.com/sidorares/node-adbhost a can create duplex node stream connected to chrome devtools on android device.

From initial research it looks like it's possible to pass custom Agent as an option to ws client, which should use android-connected stream instead of creating new tcp socket from url parameter.

Connect to multiple windows in parallel

Is it possible to connect to multiple chromium windows at the same time? Now we connect to a chromium browser through --remote-debugging-port, and we would like to run multiple evaluation tests on multiple windows at the same time.

Any suggestions?

Cannot read property 'startsWith' of undefined

Unfortunattly I don't know how this error was produced, only that it was logged in exceptions and I thought you may want to know about it.

/chrome-remote-interface/lib/devtools.js:23:25","  
 at /chrome-remote-interface/lib/devtools.js:94:13","  
 at IncomingMessage.<anonymous> (/chrome-remote-interface/lib/devtools.js:142:17)"," 
 at emitNone (events.js:85:20)","   
 at IncomingMessage.emit (events.js:179:7)","    
 at endReadableNT (_stream_readable.js:913:12)","   
 at _combinedTickCallback (internal/process/next_tick.js:74:11)","   
 at process._tickCallback (internal/process/next_tick.js:98:9)"],"level":"error","message":"uncaughtException: Cannot read property 'startsWith' of undefined","timestamp":"2016-06-14T04:45:55.134Z"}

errors from Runtime.evaluate({expression: ''}) are passed unconventionally

inspired from https://github.com/cyrus-and/chrome-har-capturer/blob/a62406a5a32bb6a4ef0b5fbcc8e64fd6dba78552/lib/client.js#L82

the following:

chromeInstance.Runtime.evaluate({expression: CLEANUP_SCRIPT})

will pass the following error to the 2nd argument of the callback or to a .then

{ result: 
   { type: 'object',
     subtype: 'error',
     className: 'ReferenceError',
     description: 'ReferenceError: chrome is not defined\n    at <anonymous>:1:1',
     objectId: '{"injectedScriptId":1,"id":1}' },
  exceptionDetails: 
   { exceptionId: 1,
     text: 'Uncaught',
     lineNumber: 0,
     columnNumber: 0,
     scriptId: '30',
     exception: 
      { type: 'object',
        subtype: 'error',
        className: 'ReferenceError',
        description: 'ReferenceError: chrome is not defined\n    at <anonymous>:1:1',
        objectId: '{"injectedScriptId":1,"id":2}' } } } 

Max number of connections

I am having 200 nw-js instances and every instance has a different port. I am connecting with this library to them dependent on port. The issue is, that I cannot have more then 115 connections opened.

Is there some sort of a limit on the library?

We know it is not the hardware, we still have a lot in reserve.

excute "GetDocumentCommand" returns null data.

I want run "GetDocumentCommand",but the result is null.

    //my test method

    private static void Test()
    {
        try
        {
            string url = "ws://localhost:9222/devtools/page/66CB09FA-3637-4C41-A1D8-DC4A3EDE428D";//url is ok,point to a chrome process with debug port 9222.
            ChromeSessionFactory sf = new ChromeSessionFactory();
            IChromeSession chromeSession = sf.Create(url);

            var x = chromeSession.SendAsync<GetDocumentCommand>().Result;

            var y = x as CommandResponse<GetDocumentCommandResponse>;

//here ,y.Resut=null,y.Method=null。but y.Id has value.

            WriteMessage(y.Result, "GetDocumentCommand=");
        }
        catch (Exception exp) { Console.WriteLine("exception="+exp.Message); }
        Console.ReadKey();
    }

I fix this , But I don't know why this happend, Is other where influence by this error?


my fix

1.open ChromeProcessFactory.cs
2. add some code into "Create" method as follow:
public ICommandResponse Create(string responseText)
{
var jObject = JObject.Parse(responseText);
if (null != jObject["error"])
{
return jObject.ToObject();
}
var methodString = GetMethod(jObject);
if (null == methodString)
{
return null;
}
var typeInferredFromMethod = _methodTypeMap.GetCommandResponse(methodString);
if (null == typeInferredFromMethod)
{
return null;
}
var genericEventType = typeof(CommandResponse<>);
var commandResponseType = genericEventType.MakeGenericType(typeInferredFromMethod);
var result = jObject.ToObject(commandResponseType);//here,don't know why: result.Result=null,result.Method=null

        //new code begin
        var cqs_r = jObject.GetValue("result");
        var cqs_o = cqs_r.ToObject(typeInferredFromMethod);
        commandResponseType.GetProperty("Result").SetValue(result, cqs_o);
        commandResponseType.GetProperty("Method").SetValue(result, methodString);
        //new code end

        return result as ICommandResponse;
    }

Can't console.log chrome instance

Here's a fun one. Trying to console.log the chrome instance throws an error

Chrome(function (chrome) {
  console.log(chrome);
})

In node, if you console.log an object that has a property named inspect with a function as the value, it will try to call that function (source code, documentation).

Anyways, in the case of this library, chrome.Inspector.inspect happens to be a function so trying to console.log the chrome instance will call that function as it recurses through the properties. This results in an error being thrown because chrome.Inspector.inspect expects to get an event handler as an argument.

TypeError: listener must be a function
    at TypeError (native)
    at Chrome.addListener (events.js:142:11)
    at Object.self.(anonymous function).(anonymous function) [as inspect] (.../node_modules/chrome-remote-interface/lib/chrome.js:163:14)
    ...

Not getting Callbacks after send

Likely this is a stupid error on my side:

For this siimple send the page is reloading but the callback is never called.
What am I doing wrong? Thanks for any feedback.
//Ralf

Page.reload(function(error,response){
console.log(error);
console.log(response);
});

Max number of connections

I am having 200 nw-js instances and every instance has a different port. I am connecting with this library to them dependent on port. The issue is, that I cannot have more then 113-115 connections opened.

Is there some sort of a limit on the library?

We know it is not the hardware, we still have a lot in reserve.

Abort requests

Is there a way to abort certain requests? ( based on the url for example )
So we can block unnecessary ads and images loading in a "headless" browser...

There is the requestWillBeSent hook, but I didn't figure out any way to abort the request.

Thanks.

Have not figured how to catch "Inspector.detached"

Hello "cyrus-and",

I am trying to catch the event when an existing remote chrome debugger session is terminated because the user opened in the tab the chrome developer tools.

chrome.Inspector.enable();
chrome.Page.enable();
chrome.once('ready', function() {
chrome.on('event', function (message) {
console.log("chrome event:"+JSON.stringify(message));
});
chrome.Inspector.detached(function(response){
console.log('Inspector for tab detached');
});
});

This code will capture events like "Page.loadEventFired" without any issue but it will never capture "Inspector.detached".

I am using "chrome-remote-interface": "^0.12.3", thank you for your help.

JLS01

For reference:
Upon disconnnection, the outgoing client will receive a detached event. For example: {"method":"Inspector.detached","params":{"reason":"replaced_with_devtools"}}
From: https://developer.chrome.com/devtools/docs/debugger-protocol

querySelectorAll returns invalid nodeId

chrome.Page.domContentEventFired(function () {
    chrome.DOM.getDocument(function (err, document) {
        console.log(document);
    });
});

the above code returns:

{
    nodeId: 1,
    nodeType: 9,
    nodeName: '#document',
    localName: '',
    nodeValue: '',
    childNodeCount: 2,
    children: [ [Object], [Object] ],
    documentURL: 'https://example-url.com/',
    baseURL: 'https://example-url.com/',
    xmlVersion: ''
 }

now that we have root nodeId I would like to search for all img nodes like so:

chrome.DOM.querySelectorAll({
    nodeId: 1,
    selector: "img"
}, function (err, nodes) {
    console.log(err, nodes);
});

which always returns

true { code: -32000, message: 'Could not find node with given id' }

any help would be greatly appreciated.

Certain API methods are not available

When I try to use the CSS.toggleProperty method defined in the Chrome Debugger Protocol Viewer docs, I get an error:

>>> CSS.toggleProperty({ 'styleId': { 'styleSheetId': '56219.1', 'ordinal': 1 }, 'propertyIndex': 1, 'disable': true })
TypeError: CSS.toggleProperty is not a function
    at repl:1:5
    at REPLServer.defaultEval (repl.js:250:27)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:412:12)
    at emitOne (events.js:82:20)
    at REPLServer.emit (events.js:169:7)
    at REPLServer.Interface._onLine (readline.js:210:10)
    at REPLServer.Interface._line (readline.js:549:8)
    at REPLServer.Interface._ttyWrite (readline.js:826:14)

The same happens for DOM.setInspectModeEnabled.

>>> DOM.setInspectModeEnabled({ 'enabled': true })
TypeError: DOM.setInspectModeEnabled is not a function
    at repl:1:5
    at REPLServer.defaultEval (repl.js:250:27)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:412:12)
    at emitOne (events.js:82:20)
    at REPLServer.emit (events.js:169:7)
    at REPLServer.Interface._onLine (readline.js:210:10)
    at REPLServer.Interface._line (readline.js:549:8)
    at REPLServer.Interface._ttyWrite (readline.js:826:14)

Error Message "SystemInfo.getInfo wasn't found"

Hi there,

I am trying to access the SystemInfo through the chrome-remote-interface, and when running the code snippet pasted below I get the following return values:

returnValue1 = true
returnValue2 = { code: -32601, message: '\'SystemInfo.getInfo\' wasn\'t found' }

While I understand that systemInfo is experimental currently in the tip-of-tree API, I am trying to figure out if the returned error is from the chrome-remote-interface, or from the remote debugging protocol itself as I had hard time tracking down this error code in either..

My hunch is the later, but just wanted to confirm. Any info you could provide here would be greatly appreciated. Thanks again.

Code Snippet ran to gather SystemInfo..

var Chrome = require('chrome-remote-interface');

Chrome(function (chrome) {
    with (chrome) {

Page.enable();

        Page.navigate({'url': 'http://example.com'});

        Page.loadEventFired(function () {

            SystemInfo.getInfo(function(returnValue1, returnValue2, returnValue3) {
                console.log(returnValue1, returnValue2, returnValue3);
                close();
            });
        });
    }
}).on('error', function (e) {
    console.error('Cannot connect to Chrome', e);
});

Collaboration with RemoteDebug?

Now that we have multiple adaptors, http://remotedebug.org/adaptors/, in place for RemoteDebug, which all use the chrome remote debugging protocol, I was considering writing a Promise-based node client, as the "official RemoteDebug client", ala webdriver.io

Since you already did most of the work I'm thinking if you were interesting in collaborating? To me it's mostly a naming thing, as it doesn't make sense to use a "chrome client" when talking to IE11 via an adapter.

Cannot evaluate page

When page is ready, I do Page.navigate({ url: new-url }) and then I try to evaluate code, but unfortunately I get an error:

/nwjs-ui-testing/node_modules/chrome-remote-interface/node_modules/ws/lib/WebSocket.js:219
    else throw new Error('not opened');
               ^
Error: not opened
    at WebSocket.send (/nwjs-ui-testing/node_modules/chrome-remote-interface/node_modules/ws/lib/WebSocket.js:219:16)
    at Chrome.send (/nwjs-ui-testing/node_modules/chrome-remote-interface/lib/chrome.js:199:13)
    at Object.self.(anonymous function).(anonymous function) [as evaluate] (/nwjs-ui-testing/node_modules/chrome-remote-interface/lib/chrome.js:227:14)
    at evalFindPos (/wjs-ui-testing/test.js:56:16)
    at nextTask (/nwjs-ui-testing/node_modules/async/dist/async.js:3921:18)
    at Object.waterfall (/nwjs-ui-testing/node_modules/async/dist/async.js:3924:9)
    at getElementPosition (/nwjs-ui-testing/test.js:52:8)
    at null.<anonymous> (/nwjs-ui-testing/test.js:32:26)
    at wrapper [as _onTimeout] (timers.js:265:14)
    at Timer.listOnTimeout `(timers.js:110:15)

Browser and page are opened, what could be the reason for this issue?

Multiple Chrome instances

Now when I want to connect to multiple chromium instances in one file, only the last connect works, all other are returning
Cannot connect to Chrome { [Error: connect ECONNREFUSED]
code: 'ECONNREFUSED',
errno: 'ECONNREFUSED',
syscall: 'connect' }

Is there a way to connect to multiple chromium instances at the same time?

Not getting callbacks for Profiler.stop and start

I'm using this framework to get data from the profiler but whenever I send Profiler.stop and Profiler.start my callback functions don't get called.

Example:
chrome.send('Profiler.stop', function(){console.log("Stop concluded.");});

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.