10

Is it possible to load a remote webpage into a background page using a chrome extension?

"background": {
    "page": "local.html"
  }

works, but

"background": {
    "page": "http://...."
  }

fails with the following error:

Could not load background page http://....
Anirudh Ramanathan
  • 46,179
  • 22
  • 132
  • 191

1 Answers1

13

No, that's not possible. It is possible since Chrome 22 - see the bottom of the answer.

You can whitelist a https: resource in the manifest file file, so that your background script can manually be constructed. Make sure that you include a fallback resource in your extension, in the case that the network is down:

<!-- ... doctype etc ... (background.html) -->
<script src="https://..../external_bg.js"></script>
<script src="bg.js"></script>

Because of the Content security policy (CSP), you cannot run inline JavaScript, so you have to use external JS files. bg.js may look like:

if (!window.namespace_of_external_bg) {
    // Fallback, by defining fallback methods or injecting a new script:
    document.write('<script src="fallback_bg.js"></script>');
}

If you want to dynamically construct a page, avoid use of eval-like methods, because these are also forbidden by the CSP. You can write a template, and request external values to populate your template. localStorage can be used to cache variables. For an example on caching external resources, see Chrome extension adding external javascript to current page's html. This answer referred to Content scripts, so the exact method cannot be used to enable caching scripts (because you would need to use eval to load the script). However, the caching technique can still be used.


I have also tried to use the following method, which does not work (included in this answer, so that you don't have to try it yourself):
Create a Blob from the AJAX response, then use webkitURL.createObjectURL to create a temporary URL to load the resource.

// Modification of https://stackoverflow.com/a/10371025
// Instead of `chrome.tabs.executeScript`, use 
// x.responseText  or  x.response (eg when using x.responseType='arraybuffer')
var blob = new Blob([x.responseText], {type: 'application/javascript'});
var url = (window.URL || window.webkitURL).createObjectURL(blob);
var s = document.createElement('script');
s.src = url;
document.head.appendChild(s);

The previous code yields the following error:

Refused to load the script 'blob:chrome-extension%3A//damgmplfpicjkeogacmlgiceidmilllf/96356d24-3680-4188-812e-5661d23e81df' because it violates the following Content Security Policy directive: "script-src 'self' chrome-extension-resource:".

Loading external resources in the background page

Since Chrome 22, it is technically possible (using the unsafe-eval CSP policy) to load non-https resources in the background page. This obviously not recommended because of security concerns (because it's susceptible to the MITM attack, for instance).

Here's an example to load an arbitrary resource and run it in the context of the background script.

function loadScript(url) {
    var x = new XMLHttpRequest();
    x.onload = function() {
        eval(x.responseText); // <---- !!!
    };
    x.open('GET', url);
    x.send();
}
// Usage:
loadScript('http://badpractic.es/insecure.js');
  • The unsafe-eval CSP policy must be specified.
  • As usual, to make cross-origin requests, the URL must be whitelisted in the manifest at the permissions section, or the server must enable CORS.

So, the manifest should at least contain:

"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"permissions": ["http://badpractic.es/insecure.js"],
"background": {"scripts": ["background.js"] }
Community
  • 1
  • 1
Rob W
  • 341,306
  • 83
  • 791
  • 678
  • Thank you for the extremely detailed answer. Another approach I tried was to embed an iframe of the external website in `local.html`. It loads up, but I am left with no way to call javascript functions within the iframe, due to same-origin policy. I will give your solution a whirl. – Anirudh Ramanathan Aug 07 '12 at 13:17
  • @DarkXphenomenon If you have control over the external page, then implement [`window.postMessage`](https://developer.mozilla.org/en/DOM/window.postMessage) (`reference_to_frame.contentWindow.postMessage`) to communicate with the frame. – Rob W Aug 07 '12 at 13:26
  • Unfortunately, I have no control over the external page. – Anirudh Ramanathan Aug 07 '12 at 13:34
  • @Cthulhu Technologies have evolved, and have I updated my answer. – Rob W Dec 19 '12 at 15:12
  • Very cool. I ended up redesigning my solution. I don't think this should be preferred though. – Anirudh Ramanathan Dec 19 '12 at 16:52
  • Thank you! This was perfect! I was stuck using a non-HTTP js; fortunately it's only accessible inside our work LAN. – Stefan May 29 '13 at 20:39