0

So, I'm building an extension that autofills different types of forms. As it's not apparent from the url which form is used on a particular website, I need to match all the urls in my manifest. I'm trying to detect the form by the 'src'-attribute in the web page.

Some of the fields of a certain form are not in the first frame. So "all_frames" has to be true in my manifest. That means content.js fires once for each frame or iFrame.

**content.js:**

async function checkForPaymentType(value, attribute = 'src') {
    return document.querySelectorAll(`[${attribute}*="${value}"]`);
}

let hasThisForm;

document.addEventListener('DOMContentLoaded', () => {
    checkForPaymentType('formJs.js').then((value) => {    
    if(value.length) {
        hasThisForm = true;
    }
    if(hasThisForm)
        fillForm();
    });
});

The problem now is, that that only the first frame has the "src='formJs.js" attribute in one of its elements. So it only fills out those fields in the first frame.

My solution idea

What I am trying to do is some sort of global boolean variable ('hasThisForm') that can only be set true. So once the first frame detected that there is this form on the website the other frames fire fillForm() as well.

Problems

1.I'm not able to set a variable that can be read from all of the executions.

2.I need the other executions of content.js to wait for the first one.

Another solution would be to have some sort of window.querySelectorAll, so every execution of content.js searches in the whole page and not just in its frame.

Thanks in advance:)

crimbler2
  • 23
  • 7
  • 1
    Either use [postMessage](https://devdocs.io/dom/window/postmessage) or coordinate via a background script ([example](https://stackoverflow.com/a/57806064), find more yourself). – wOxxOm Apr 07 '20 at 09:51

1 Answers1

0

So I figured it out. As pointed out in the comment from @wOxxOm or in this question (How to get all frames to access one single global variable) you need to manage everything via the background page.

You want to set up a listener in every Frame and send a message only from the top frame (to the background page which sends it back to the whole tab).

After hours of trying I noticed that the listeners weren't even ready when the message from the topFrame was sent. I put in a sleeper before sending the message which is not the ideal way I guess. But there is no "listenerIsSet-Event".

This is my code:

content.js

document.addEventListener('DOMContentLoaded', () => {

    chrome.runtime.onMessage.addListener(
        function (msgFromTopFrame) {
            console.log(msgFromTopFrame)
        });

    if (window === top) {

        Sleep(1000).then(() => {
            const msgToOtherFrames = {'greeting': 'hello'};
            chrome.runtime.sendMessage(msgToOtherFrames);
        });

    }
});

background.js

chrome.runtime.onMessage.addListener((msg, sender) => {
    if(('greeting' in msg)) {
        chrome.tabs.sendMessage(sender.tab.id, msg);  
    }
});

You probably want to execute some code depending on the value received. You can write it only once in the listener. It will execute in all frames including the top frame as the background.js sends it to all frames in the tab.

Note: There may be some errors with the dicts/keys in the messages "sent around" in this code. Just log the message in all the listeners to get the right expressions.

crimbler2
  • 23
  • 7