Search code examples
javascriptcanvasdynamicpermissionsfavicon

How do I trigger ask for permission to access HTML5 canvas data without "via user interaction" on Firefox? (no canvas in Permissions API?)


This is a fairly firefox specific issue it seems... not sure ofc! (but I tested in chrome, and it works, whereas i get garbled canvas data in firefox)

In short, I wanted to dynamically insert an emoji to the favicon, leading me on a small journey through rendering an emoji to a canvas (dynamically), reading that canvas thru .toDataURL(), and finally setting the favicon href to point to that "url". Problems arose when reading the canvas data!

I read stuff here https://bugzilla.mozilla.org/show_bug.cgi?id=1376865 about this feature being implemented, but they also seem to mention "canvas permissions"... they're not referring to the Permissions API right? (i looked here https://w3c.github.io/permissions/#permission-registry), and there's no such thing :(

I've encountered various internet hearsay about canvas being added to the Permissions API, but I haven't found anything concrete... do I give up on this? is this coming in the future?

I have no "user input" on my page, so adding this in retrospect just to trigger the pop-up asking for permission seems... hacky!

If you go to a page like http://www.faviconer.com/ where you can draw your favicon, you'll see what I mean. Firefox shows a pop-up on "user input triggered canvas data access", but chromium seems to just allow it outright.

code:

const createIconElement = () => {
    var link = document.createElement('link');
    link.rel = 'icon';
    link.type = 'image/png';
    return link;
};

// original: https://github.com/EvanHahn/canvas-to-favicon/blob/master/canvas-to-favicon.js
const canvasToFavicon = (canvas) => {
    let head = document.head;

    var existingIcons = document.querySelectorAll('link[rel="icon"]');
    for (var i = 0, len = existingIcons.length; i < len; i++) {
        head.removeChild(existingIcons[i]);
    }

    iconElement = createIconElement();
    head.appendChild(iconElement);

    iconElement.href = canvas.toDataURL('image/png');
}

document.addEventListener("DOMContentLoaded", () => {
    console.log('running setFavicon.js');

    // get an emoji from the DOM somewhere
    // const emojicontainingstring = document.querySelectorAll('*');
    // const emojis = emojicontainingstring.map(extractEmoji).filter(e => !!e);
    // const emoji = emojis[0];

    const emoji = '📋';

    // render emoji to canvas
    const canvas = document.createElement('canvas');
    canvas.width = 32;
    canvas.height = 32;

    const ctx = canvas.getContext("2d");
    ctx.font = '26px serif'
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    ctx.fillText(emoji, canvas.width / 2, canvas.height / 2)

    canvasToFavicon(canvas);
});

Solution

  • ok, turns out it's my CanvasBlocker plugin (even though I specifically whitelisted my own domain?... tested on vanilla FF and it works!)