cyrus-and/chrome-remote-interface

Tab management questions

jotto opened this issue ยท 17 comments

jotto commented

Can you verify the following?

  • CDP.List, CDP.New, CDP.Activate, CDP.Close use the HTTP transport
    • although on headless Chrome, CDP.New() fails for me with: Error: Could not create new page
  • CDP() returns a websocket connection to the currently active tab
  • CDP().Target.createTarget({url: 'about:blank'}) creates a new tab, and returns { targetId: 'some-hash' }
    • but I can't connect to that new tab with CDP({tab: {targetId: 'some-hash}}), I have to query all the tabs to get the webSocketDebuggerUrl URL of that matching targetId/id first?
  • can I maintain websocket connections to multiple tabs doing something like: CDP.List().then(tabs => Promise.all(tabs.map(tab => CDP({ tab }))))

CDP.List, CDP.New, CDP.Activate, CDP.Close use the HTTP transport

That's correct.

although, CDP.New() fails for me with: Error: Could not create new page

Well, this should not happen can you provide a minimal script reproducing this, along with Chrome and Node.js versions.

CDP() returns a websocket connection to the currently active tab

In abstract terms, yes, it actually return an instance of the CDP class.

CDP().Target.createTarget({url: 'about:blank'}) creates a new tab, and returns { targetId: 'some-hash' }

Proper syntax is: CDP((client) => { client.Target.createTarget({url: 'about:blank'}) }); but yes.

but I can't connect to that new tab with CDP({tab: {targetId: 'some-hash}}), I have to query all the tabs to get the webSocketDebuggerUrl URL of that matching targetId/id first?

You can't connect in that way because there's no documented way to build a WebSocket URL out of the target id, you can either:

  • build the WebSocket URL manually ws://localhost:9222/devtools/page/${targetId} but I cannot guarantee that it will always work;

  • or, basically do what you said with:

    CDP({tab: tabs => tabs.findIndex(tab => tab.id == targetId)}, (client) => { /*...*/ });

can I maintain websocket connections to multiple tabs doing something like: CDP.List().then(tabs => Promise.all(tabs.map(tab => CDP({ tab }))))

Absolutely, as long as, at any given time, there's at most one CDP instance for each tab.

jotto commented

Well, this should not happen can you provide a minimal script reproducing this, along with Chrome and Node.js versions.

const CDP = require('chrome-remote-interface');
CDP.New().then(console.log).catch(console.error)
Error: Could not create new page
    at IncomingMessage.<anonymous> (./node_modules/chrome-remote-interface/lib/external-request.js:14:26)
    at emitNone (events.js:91:20)
    at IncomingMessage.emit (events.js:185:7)
    at endReadableNT (_stream_readable.js:974:12)
    at _combinedTickCallback (internal/process/next_tick.js:74:11)
    at process._tickDomainCallback (internal/process/next_tick.js:122:9)

I suspect that headless Chrome cannot create new tabs via the HTTP endpoint, can you test the following?

curl http://localhost:9222/json/new

You can even navigate that URL by any other means.

Possibly compare with a regular (non-headless) Chrome version.

jotto commented

curl http://localhost:9222/json/new

Could not create new page

must be headless then. I also checked the non-headless and it works fine. Sorry for false alarm.

No worries! Good to know, it might be worth it to file an issue to Chromium about that, I can't see no reason why /json/list is available via HTTP whereas /json/new is not...

Good to know, it might be worth it to file an issue to Chromium about that, I can't see no reason why /json/list is available via HTTP whereas /json/new is not...

FYI https://bugs.chromium.org/p/chromium/issues/detail?id=696198

jotto commented

Thank you for filing and thank you for maintaining this great project!

I don't think I'm allowed to add labels to that issues.

Yeah label creation is tricket with monorail. If you file the bug via: https://bugs.chromium.org/p/chromium/issues/entry?labels=Proj-Headless then it'll tag it.

Additionally the headless-dev mailing list has been super helpful so feel free to post it there if you need it tagged.

although on headless Chrome, CDP.New() fails for me with: Error: Could not create new page

This must have broken recently, because I remember reading about and testing CDP.New() working fine with headless shell.

Meanwhile, is there any other way to open/manage tabs, or load isolated pages, with a single running instance of Chrome headless?

Meanwhile, is there any other way to open/manage tabs, or load isolated pages, with a single running instance of Chrome headless?

@VoidMonk you can use the Target domain as suggested here.

@cyrus-and Thanks, I think for now I'll just stick to CDP.New() with an old Chrome headless shell binary that supports it, or maybe not if Target is the primary API for headless in the future.

Am I correct in understanding that the main difference between /json methods (e.g. CDP.New()) and the new 'experimental' Target Domain (/devtools/browser) is that the former works over HTTP and the latter works over WebSockets? What's special about Target Domain anyway (all its viewer summary says is Supports additional targets discovery and allows to attach to them?

I don't know why would the Chrome/Chromium team break/discontinue the /json API on headless, when it was working earlier, and many developers would've based their implementation on it.

Update: found some discussion on their bug tracker.

For headless shell I don't think we're planning on supporting the webserver that implements /json/new, although I'd imagine chrome.exe --headless would have it by default.

Instead we have the new Browser.createTarget and Browser.createBrowserContext devtools protocol commands implemented by that patch. NB I think the ability to have isolated request contexts will be useful for webdriver style testing since it's very much easier now to open a tab with known state.

So, looks like essentially there will be two different protocol APIs going forward, /json for full browser and Target /devtools/browser for headless. Does it make sense for CRI to abstract it into a single API?

Am I correct in understanding that the main difference between DevTools methods (e.g. CDP.New()) and the new 'experimental' Target Domain is that the former works over HTTP and the latter works over WebSockets?

That's right. FYI you can use the Target domain with regular (non-headless) Chrome.

I don't understand why the Chrome/Chromium team will break/discontinue the DevTools API on headless, when it was working earlier, and many developers would've based their implementation on it.

It's not clear whether they plan to discontinue the HTTP endpoint for headless Chrome (or even altogether) or not. But this kind of makes sense if you consider that there's a special target ws://localhost:9222/devtools/browser (see this comment) which can be used (via the regular WebSocket protocol) to manage targets. Unfortunately this special target doesn't seem to work properly at the moment, e.g., Target.GetTargets doesn't exist.

Said that, the only thing I hope is to not end up with two different DevTools APIs for tabs/targets management, one for regular Chrome and one for headless mode. Maybe @paulirish can shed some light on this?

In the meanwhile, I encourage you to star and/or comment the related Chromium issue.

FYI you can use the Target domain with regular (non-headless) Chrome.

@cyrus-and Doesn't work in Chrome on Windows, as it doesn't support headless mode. Quite a mess if dev OS is Windows (where non-headless supports /json but not Target Domain), and prod OS is Linux (where headless supports Target Domain but not /json anymore) -- two backward-incompatible, forward-conflicting, platform-dependent APIs; despite effort towards a standardized RemoteDebug spec.

Looks like /json/new endpoint has been implemented in headless: https://bugs.chromium.org/p/chromium/issues/detail?id=699392

@VoidMonk thanks for the heads up, I'll update the README notice as soon as the fix hits the stable channel.