Skip to content
Advertisement

Can Chrome content and background scripts share access to blob: URLs?

I’m creating a getUserMedia stream in a Chrome extension content script, and I’d like to pass it to the background script.

You can’t pass non-JSON’able data between them, so my strategy is to pass instead the generated blob URL to the stream.

Content script:

function get_stream() {
    navigator.mediaDevices.getUserMedia({video: 1}).then(stream => {
        chrome.runtime.sendMessage({action: 'got_stream', params: {stream_url: URL.createObjectURL(stream)}});
    });

Background script:

chrome.runtime.onMessage.addListener(function(data) {
    switch (data.action) {
        case 'got_stream': got_stream(data.params); break;
    }
});

function got_stream(params) {
    let vid = document.createElement('video');
    alert(params.stream_url); //blob:http://...
    vid.src = params.stream_url; //error - file not found
}

This is fine… until I try to apply it to a generated <video /> element, at which point the console says the file is not found.

I assume it’s because the background and content scripts are in sandboxed environments.

Is there any way around this without having to do something nuclear like transfer the stream literally via WebRTC or something?

Advertisement

Answer

I figured out this is an origins issue.

The content script runs in the context of the present webpage, whereas the background script runs in the context of the extension.

Blob URLs are grouped by origin, so, in the same way you can’t ordinarily AJAX from one domain to another, two domains also can’t share blob URLs.

This is solved by running the content script not in the current webpage (so not specified in the manifest under content_scripts) but in a new tab or pop-up.

Background:

window.open('content-page.html');

Content page:

<script src='content-script.js'></script>

Then, any blob URL generated by content-script.js will be readable to the background, as they are now both running in the context of the extension, i.e. a shared origin.

[EDIT]

If you don’t like the idea of a pop-up window (after all, on Mac these are rendered as full tabs), you could instead inject an iframe into the current tab and run your content script from there.

To do this, call a content script from your manifest:

{
    "content_scripts": [{
        "matches": ["<all_urls>"],
        "js": ["content-script-curr-tab.js"]
    }]
}

Then in that:

let ifr = document.createElement('iframe');
ifr.setAttribute('allow', 'microphone; camera'); //necessary for cross-origin frames that request permissions
ifr.style.display = 'none';
ifr.src = chrome.runtime.getURL('page-to-inject-into-iframe.html');
document.body.appendChild(ifr);

Note chrome.runtime.getURL() – that’s the key to hosting and running a page in the context of the extension not the webpage.

Then, finally, in page-to-inject-into-iframe.html:

<script src='script-to-inject-into-iframe.js'></script>

Then do your thing in there!

User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement