Search code examples
javascriptgoogle-chromegoogle-chrome-extensiongetusermedia

Chrome Extension - getUserMedia throws "NotAllowedError: Failed due to shutdown"


I have a suite which records the user's webcam and mic. It works great on a webpage, but in a Chrome Extension the line:

navigator.mediaDevices.getUserMedia({video: true, audio: true})
    .then(this.record.bind(this))
    .catch(VidRA.error);

is throwing

NotAllowedError: Failed due to shutdown

I've searched and found almost nothing that might explain this. Has anyone else come across this or does anyone know what I can do about it?


Solution

  • Wow, this is a minefield.

    Firstly, it seems this is a bug (thanks, @wOxxOm).

    So we need to code around it.

    Since the background script generates this bug when requesting media access, we need to request it elsewhere. The same error is generated if you try from the popup JS, so that leaves content scripts.

    The steps, then, are these:

    • Content script requests access to media devices

    • On success, content script notifies background script

    • On receipt of message, background script requests acccess to media devices; since content script has already succeeded, background script will now also succeed

    Crucially, the content script must run in the context of the extension, NOT the current webpage (in the active tab.) That is, the content script must do so via a URL beginning chrome://, generated by chrome.runtime.getURL().

    This is so the user is asked for permissions only once, since their decision is remembered on a domain-by-domain basis. Running the script in the context of the extension, not the current webpage, means permission is always asked (and the decision remembered) in the same, extension-based domain, not again for every website domain visited.

    My approach was to have a persistent content script (specified in the manifest under content_scripts) whose job, when the extension is opened, is to inject an iframe into the current tab's page.

    Into that iframe is loaded a page from the extension, let's call it iframe.html, which is ultimately responsible for requesting access.

    Phew...