Dynamically inject content scripts - Chrome Extension

Shryder :

I'm trying to make a chrome extension that receives javascript code from a backend and saves it in localStorage (as base64) so I can later inject it as a content script when the right page is loaded, it does work most of the time except there are a few issues... First issue (not that important) is that I cannot access Chrome APIs (like chrome.storage or chrome.runtime.sendMessage), second issue is that it doesn't inject the correct code to child iframes... because location.href returns the URL of the top webpage and I couldn't find a way to access current URL of iframe within the iframe itself.

This is my code so far:

manifest.json

//....
"content_scripts": [{
    "run_at": "document_end",
    "all_frames": true,
    "matches": [
        "<all_urls>"
    ],
    "js": [
        "src/inject/InjectManager.js"
    ]
}],
//...

InjectManager.js:

// Some functions were not included for brevity
chrome.runtime.sendMessage({ action: "get_supported_urls" }, function(supported_urls) {
    let current_url = window.location.href;

    // Check if we support current_url
    let js_code_to_inject = isWebsiteSupported(supported_urls, current_url); // this function returns string that is javascript code.
    if(js_code_to_inject){
        // Append the code to the body
        let script = document.createElement("script");
        script.type = "text/javascript";
        script.innerHTML = js_code_to_inject;

        document.body.appendChild(script);
    }
});

As you see, I'm kind of trying to recreate what chrome already does in manifest.json's "content_script" section because my javascript code is dynamic.

Note: I know this is not allowed on chrome store and such, this extension is not to be shared with anyone.

Thanks for reading. Any help would be greatly appreciated.

wOxxOm :

I cannot access Chrome APIs (like chrome.storage or chrome.runtime.sendMessage)

Your code currently makes a page script, not a content script. For the latter you need to use chrome.tabs.executeScript (see also content script documentation) in the background script.

location.href returns the URL of the top webpage and I couldn't find a way to access current URL of iframe within the iframe itself.

No, what you're describing simply cannot happen, it would be a doomsday-level violation of URL origin security, so something else is happening. For example, your manifest.json doesn't have match_about_blank meaning InjectManager.js doesn't process the dynamically added about:blank frames at all.


manifest.json:

"content_scripts": [{
  "match_about_blank": true,
  .....................

InjectManager.js:

chrome.runtime.sendMessage({ action: 'inject', data: location.href });

background script:

chrome.runtime.onMessage.addListener(({ action, data }, sender, sendResponse) => {
  if (action === 'inject') {
    chrome.tabs.executeScript(sender.tab.id, {
      code: getInjectionCode(data),
      frameId: sender.frameId,
    });
  }
});

Note that some iframes like javascript: or srcdoc: won't run content scripts in Chrome at all so you'll have to process them directly in InjectManager.js as they cannot be injected by executeScript(). For example you can find all iframes by using document.querySelectorAll('iframe') and create DOM script element inside like you do currently but you'll use frameElement.contentDocument instead of document and of course you'll check whether the iframe's real URL (frameElement.contentWindow.location.href) doesn't start with http because frames can be navigated inside without changing their src attribute on the outside. Do the check inside try/catch because accessing an iframe from a different origin will throw.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=29715&siteId=1
Recommended