Injecting iframe into page with restrictive Content Security Policy

The inability to insert an external iframe in Chrome is a bug (crbug.com/408932).

If you want to embed an external frame in an external website, then it must be loaded in a frame that is packaged with your extension.

manifest.json

{
    "name": "Embed external site",
    "version": "1",
    "manifest_version": 2,
    "content_scripts": [{
        "js": ["contentscript.js"],
        "matches": ["*://*/*"],
        "all_frames": true
    }],
    "web_accessible_resources": [
        "frame.html"
    ]
}

Do NOT use chrome.tabs.onUpdated + chrome.tabs.executeScript if you want a content script to always be inserted in a document. Your implementation is flawed and can cause the script to be run multiple times. Instead, you should declare the content script in the manifest file.

(remove "all_frames": true if you don't want to insert the frame in every subframe.)

contentscript.js

// Avoid recursive frame insertion...
var extensionOrigin = 'chrome-extension://' + chrome.runtime.id;
if (!location.ancestorOrigins.contains(extensionOrigin)) {
    var iframe = document.createElement('iframe');
    // Must be declared at web_accessible_resources in manifest.json
    iframe.src = chrome.runtime.getURL('frame.html');

    // Some styles for a fancy sidebar
    iframe.style.cssText = 'position:fixed;top:0;left:0;display:block;' +
                           'width:300px;height:100%;z-index:1000;';
    document.body.appendChild(iframe);
}

frame.html

<style>
html, body, iframe, h2 {
    margin: 0;
    border: 0;
    padding: 0;
    display: block;
    width: 100vw;
    height: 100vh;
    background: white;
    color: black;
}
h2 {
    height: 50px;
    font-size: 20px;
}
iframe {
    height: calc(100vh - 50px);
}
</style>
<h2>Displaying https://robwu.nl in a frame</h2>
<iframe src="https://robwu.nl/"></iframe>

It is important to observe that I loaded an https site in the frame. If you attempt to load an HTTP site in the frame, then the mixed content policy will block the frame from being loaded if one of the parent frames is an https page.

Replace https://robwu.nl/ with http://example.com/ and the frame will remain blank on https pages such as https://github.com. Simultaneously, the following message will be printed to the console.

[blocked] The page at 'https://github.com/' was loaded over HTTPS, but ran insecure content from 'http://example.com/': this content should also be loaded over HTTPS


Rob W 's answer is correct. You can follow this https://transitory.technology/browser-extensions-and-csp-headers/. I 've successfully make it work in my Chrome extension https://github.com/onmyway133/github-chat

Note that I use Chrome 59, so I can use most of ES6 features

Declare in manifest

"web_accessible_resources": [
  "iframe.html",
  "scripts/iframe.js"
]

Create an iframe in window.onload event

let url    = decodeURIComponent(window.location.search.replace('?url=', ''))
let iframe = document.createElement('iframe')
iframe.src = url

iframe.id = 'github-chat-box-iframe-inner'
iframe.style.width = '100%'
iframe.style.height = '350px'
iframe.style.border = '0px'

window.onload = () => {
  document.body.appendChild(iframe)
}

Your example should work in Chrome, but it currently does not because of a bug: https://code.google.com/p/chromium/issues/detail?id=408932. Rob W's answer contains a good work-around for the issue.