Garen Checkley
Garen Checkley

Reputation: 5572

Do Chrome extensions access iframes?

if I write a Chrome extension that runs a snippet of JS on a page, will it also run in any iframes I create? If so, does this apply for iframes created by Javascript after the DOM has loaded?

Thanks!

Upvotes: 26

Views: 33990

Answers (2)

Adam Ayres
Adam Ayres

Reputation: 8910

Yes, a Chrome Extension content script can run in all iframes if you specify "all_frames": true in manifest.json, https://developer.chrome.com/docs/extensions/reference/manifest/content-scripts:

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["*://www.google.com/*"],
      "css": ["mystyles.css"],
      "js": ["jquery.js", "myscript.js"],
      "all_frames": true
    }
  ],
  ...
}

These scripts will run in each matching iframe as separate instances i.e. the script running in an iframe can't directly access variables of the parent script. You can check whether the instance runs inside an iframe: if (window !== top) { console.log('in iframe') }.

The about:blank iframes

The above example matches only those iframes that contain a www.google.com inside them, but sometimes you may want to run your content scripts in the so-called about:blank iframes (their internal location.href is "about:blank") either without src or a src like "about:blank" or "about:srcdoc".

          "all_frames": true,
          "match_about_blank": true,
          "match_origin_as_fallback": true

Alternatively, you can just access their content directly via iframeElement.contentDocument or iframeElement.contentWindow from your content script running in the main document as such iframes are same-origin. Note that running an instance of a content script consumes some resources (time, CPU, and memory), which becomes a problem for users with many tabs open.

Caveats:

  • Currently Chrome can't run content scripts in iframes with javascript: URL in src.

  • src attribute only tells you the initial URL of the iframe, but you can't trust it as the iframe may have been navigated inside and thus become cross-origin, in which case accessing its actual contents will throw an exception. The solution is either to do it inside try{}catch(e){} or check the availability of URL getter to avoid triggering an exception:

    let ok;
    try {
      ok = !!Object.getOwnPropertyDescriptor(elem.contentWindow.location, 'href').get;
    } catch (e) { /* Chrome 85 and older throws in sandboxed frames */ }
    if (ok) {
      console.log(elem.contentDocument);
    }
    

Upvotes: 52

Xan
Xan

Reputation: 77531

Content scripts defined in the manifest (with "all_frames": true) will run on newly created iframes. What matters is that a new navigation starts for every frame, and content scripts are scheduled to be injected at that point.

In contrast, if you dynamically inject code with chrome.tabs.executeScript(), then it will only be injected in the frames present at the time you call it. You'd need some mechanism to detect new frames (Mutation observers? webNavigation API?) if you want to keep up with them.

Upvotes: 6

Related Questions