cyrus-and / chrome-remote-interface

Chrome Debugging Protocol interface for Node.js
MIT License
4.29k stars 309 forks source link

Question: access elements in iframe #276

Closed ilanc closed 7 years ago

ilanc commented 7 years ago

Hi Andrea, I'd like to be able to access elements contained within an iframe. It doesn't look like this is possible using chrome-remote-interface atm however it is possible using selenium/chromedriver, and of course within chrome it's possible to change the frame in the devtools.

My questions are:

I put together a prototype to demonstrate how you can find elements inside an iframe using selenium/chromedriver (nb python implementation):

The code does the following:

I've started looking into the chromedriver source but have not yet figured out what they're doing:

anuraagvaidya commented 7 years ago

Just use

var myframe=document.querySelector('#<frameId>');
var findme=myframe.contentDocument.querySelector.querySelector('#findme');
ilanc commented 7 years ago

Thanks @anuraagvaidya! This works:

document.querySelector('iframe').contentDocument.querySelector('#findme')
cyrus-and commented 7 years ago

That works as long as it doesn't violate the SOP (as in your prototype). The general solution is to pass the proper contextId to Runtime.evaluate. You can keep track of execution contexts with the Runtime.executionContextCreated event.

Please let me know if this solves your issue/answers your questions. :)

ilanc commented 7 years ago

Yup all resolved - many thanks for pointing that out.

jsoverson commented 6 years ago

@cyrus-and I can't seem to find a way to actually execute JavaScript in any attached frames and getFrameTree doesn't seem to regularly report what I would expect (no childFrames).

Expected:

[addScriptToEvaluateOnNewDocument] in frame for url https://output.jsbin.com/guwojab
[Runtime.evaluate] in frame for url https://output.jsbin.com/guwojab
[addScriptToEvaluateOnNewDocument] in frame for url https://example.com
[Runtime.evaluate] in frame for url https://example.com

Actual:

[addScriptToEvaluateOnNewDocument] in frame for url https://output.jsbin.com/guwojab
[Runtime.evaluate] in frame for url https://output.jsbin.com/guwojab

Versions:

├─┬ chrome-launcher@0.10.5
├─┬ chrome-remote-interface@0.26.1

I'm sure I'm missing something obvious, any help you can give would be great. The script below should be sufficient to reproduce, it requests https://output.jsbin.com/guwojab which includes an iframe for https://example.com.

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

(async function () {

  const chrome = await chromeLauncher.launch({
    chromeFlags: [
      '--window-size=1024,762',
      '--user-data-dir=/tmp/foobar',
      '--enable-logging',
      '--auto-open-devtools-for-tabs'
    ]
  });

  const protocol = await CDP({ port: chrome.port });

  const { Page, Runtime } = protocol;
  await Promise.all([Page.enable(), Runtime.enable()]);

  Runtime.consoleAPICalled(({ args, type }) => console[type].apply(console, args.map(a => a.value)));

  Page.navigate({ url: 'https://output.jsbin.com/guwojab' });

  await Page.addScriptToEvaluateOnNewDocument({
    source: `console.log('[addScriptToEvaluateOnNewDocument] in frame for url ' + document.URL);`
  });

  Runtime.executionContextCreated(async evt => {
    await Runtime.evaluate({
      contextId: evt.context.id, 
      expression: `console.log('[Runtime.evaluate] in frame for url ' + document.URL);`
    });
  })

  Page.loadEventFired(async evt => {
    const tree = await Page.getFrameTree();
    console.log(tree);
  });

})();
paulirish commented 6 years ago

@jsoverson It's possible that chrome is using Site Isolation aka OOPIF (out of process iframes) for this jsbin iframe. In that case, the iframe is a brand new target.

Hypothetically you can disable OOPIF with -disable-features=IsolateOrigins,site-per-process but I haven't verified this still works. If you can easily disable it, then the child frames should exist as you expect.

jsoverson commented 6 years ago

Thanks @paulirish, --disable-features=IsolateOrigins,site-per-process gives me the behavior I was expecting. I was banging my head against that for quite a long time, a warning or notice on Page.getFrameTree() may be worthwhile in order to save people the headache in the future.

Thanks again 🙂

lachesis commented 5 years ago

In the event that Chrome is using this feature, how does DevTools access these multiple frame contexts? Can we perform that same operation using chrome-remote-interface somehow?

cyrus-and commented 5 years ago

@lachesis you can see the protocol messages used by DevTools by inspecting the inspector.