Search code examples
javascriptgoogle-chrome-extensionglobal-variablescontent-script

How to get all frames to access one single global variable


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>]
}

Solution

  • 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});
      }
    });