UPDATE:
I figured out what the problem was, and as is usually the case, it was something silly on my end. In case anyone else has a problem with this type of functionality, I can verify that it works. The “injected_content.js” file will be injected into all frames in the content context, but since the “window.PARENT_FRAME” variable is undefined in the child frames, only those frames will send the “Message from injected js” message to the background context. Also, while creating a global “PARENT_FRAME” var on window works for detecting logic to only run on child frames, I am now simply avoiding injecting the script into the parent frame using the “frameId” property on the “executeScript” config object. Just make sure that scripts are not injected into the frame that has a “frameId” of 0, which is the top-most, parent frame on the DOM. The code fragments are provided below.
I am creating an extension that needs access to content the user types into form input fields. Everything is going well, but I’m now at a point where I need to add support for iFrames, which is proving to be quite a challenge.
So far as I understand, here are the steps I need to follow to accomplish the transfer of data:
- From the content script, detect when the iFrame is loaded
- Send a message to the background script notifying it that the iFrame has loaded
- Inject a new content script into the active tab (this script will be injected into all frames in the active tab)
- Add event listeners to the inputs needing to capture user input in the iFrame
- After the input is captured, send the data to the background script for processing
I have yet to create my own implementation code, which kicks off in step #4 above. So far, I’m only using the example code taken from MDN’s “executeScript” documentation to simply make sure I have all the access I need to the iFrame content loaded into a couple real sites.
At this point, I’m stuck on step #3. I’m able to detect the iFrame is loaded, send the message to the background script, and, based on one statement logged to the console, I’m able to inject the new content script into at least one of the frames in the active tab. However, I know at least two statements should be logged because there are at least two frames into which the script should be getting injected, but again, only one statement is getting logged. On top of that, I’m running into an “error is not defined” error in the console only when I have the “allFrames” property set to true for the “browser.tabs.executeScript” function. If “allFrames” is set to false, everything works as it should with no errors.
I’m using jQuery to simplify DOM traversal/selection. Here are the code examples.
backgound.js:
browser.runtime.onMessage.addListener(function(request, sender, response) {
if (request.message) {
console.log('>>> MESSAGE: %s <<<', request.message);
}
if (request.command === 'inject') {
function onExecuted(result) {
console.log(`We executed in all subframes`);
}
function onError(err) {
console.log('ERROR:', err.message);
}
var executing = browser.tabs.executeScript({
file: "./content/MSFrameTest.js",
allFrames: true
});
executing.then(onExecuted, onError);
}
});
content.js:
$("iframe").on("load", function(e) {
console.log(">>> IFRAME LOADED <<<");
browser.runtime.sendMessage({ command: 'inject' });
});
window.PARENT_FRAME = true;
injected_content.js:
console.log('>>> CONTENT INJECTED <<<');
if (!window.PARENT_FRAME) {
browser.runtime.sendMessage({ message: 'Message from injected js' });
}