Request permissions using the Permissions API from a <select> onchange handler in Firefox

I'm currently working on a Firefox Add-on that makes use of the Permissions API to request permissions dynamically for specific origins as needed based on the value of a <select>, as the user changes the value, without having a separate submit button. When I call browser.permissions.request the promise is rejected immediately with the error:

permissions.request may only be called from a user input handler

The relevant HTML and JS code is below. I'd like to avoid using a separate save button, but I don't know how I could achieve that. Is it possible, and if so, how?

<div id="domain-settings">
    Choose your preferred domain:
    <select>
        <option>example.com</option>
        <option>one.example.com</option>
        <option>other.example.com</option>
    </select>
</form>
const settings = { domain: 'example.com' };
const $select = $('#domain-settings').find('select');
$select.on('change',function(e){
    e.stopPropagation();
    const oldValue = settings.domain;
    const perm = { origins: [ `https://${$select.val()}/` ] };
    browser.permissions.contains(perm)
        .then(result => {
            if (result)
                someFunction();
            else browser.permissions.request(perm)
                .then(granted => {
                    if (granted)
                        someFunction();
                    else $select.val(oldValue);
                });
        })
})();

Answers:

Answer

A couple days ago I somehow stumbled across this question when I re-discovered this bug in my codebase, not even remembering I posted about it here. That question has an almost identical answer, but in my case the issue was specifically the fact that I was checking for the permission before I tried requesting it, which, as it turns out, is wholly unnecessary.

The same explanation applies here as well. Firefox did not recognize the handler as coming from user input due to being chained in a promise call, which destroys the call stack that it could use to verify this. If a permissions.request call is chained after any promise it will not pass this validation.

My solution: drop the permissions.contains check. Now everything works as expected.

const settings = { domain: 'example.com' };
const $select = $('#domain-settings').find('select');
$select.on('change',function(e){
    e.stopPropagation();
    const oldValue = settings.domain;
    const perm = { origins: [ `https://${$select.val()}/` ] };
    browser.permissions.request(perm)
        .then(granted => {
            if (granted)
                someFunction();
            else $select.val(oldValue);
        });
})();

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.