3

I am developing a Chrome extension and need my content script to access all frames of the web page in order to detect some specific fields.

Once detected I would attempt to store that field element inside its respective property in the global object. The problem is that if the elements are all located in different iframes, each would get a copy of that script and populate its own copy of the object and not one single global object.

Is there a way to have one single global instance of the object and append values to its properties every time a field is detected?

Eg:

Original object

{
    userName: [],
    email: []
}

userName field detected in iframe1

{
    userName: [<input id="username">...</input>],
    email: []
}

email field detected in iframe2

{
    userName: [],
    email: [<input id="email">...</input>]
}

Desired outcome

{
    userName: [<input id="username">...</input>],
    email: [<input id="email">...</input>]
}
Noam Suissa
  • 428
  • 4
  • 23
  • I can't help but imagine a chrome extension installed via adware capable of grabbing credit card information from stripe's iframe This shouldn't be possible by design – Salim Djerbouh Sep 04 '19 at 21:33
  • @CaddyDZ I believe it is possible to have embedded page communication via `window.postMessage` but I am not sure how to detect whether the window is embedded within the DOM (iframe) and how the parent window should handle these messages all within the same `content-script.js` file. – Noam Suissa Sep 05 '19 at 00:21
  • 1
    Nothing's wrong with the intent, this is a perfectly valid task. I've just written something similar in [how to fix multi confirm windows on chrome extension](//stackoverflow.com/a/57744203) but there might be better examples of course. Or maybe I'll adapt my code to your use case later today. – wOxxOm Sep 05 '19 at 05:58

1 Answers1

2

It'd be more secure and reliable to organize the communication via extension messaging to the background script. Instances of content script run in the main page and all iframes, the main one waits for the data, the others in frames send the data through the background page relay.

The content script:

if (window === top) {
  // main page
  const data = {};
  chrome.runtime.onMessage.addListener(msg => {
    Object.assign(data, msg.data);
    if (data.email && data.username) {
      console.log('Got both values', data);
    }
  });
} else {
  // inside a frame
  const data = {};
  for (const id of ['username', 'email']) {
    const el = document.getElementById(id);
    if (el) data[id] = el.value; 
  }
  if (Object.keys(data).length)
    chrome.runtime.sendMessage({data});
}

The background script:

chrome.runtime.onMessage.addListener((msg, sender) => {
  // message from a frame?
  if (msg.data && sender.frameId) {
    // relay it to the main page (frameId: 0)
    chrome.tabs.sendMessage(sender.tab.id, msg, {frameId: 0});
  }
});
wOxxOm
  • 65,848
  • 11
  • 132
  • 136
  • Thank you! This makes a lot of sense. However, is to relay the whole element itself. For some reason when I log the `msg` in the background.js file, the element is blank, yet is detected to be there according to the logs. Is there a workaround to fix this? – Noam Suissa Sep 06 '19 at 19:23
  • I don't understand. Maybe you're looking at the [wrong console](/a/10258029) – wOxxOm Sep 07 '19 at 04:12