В хроме есть расширение copy-base64. Добавляет пункт контекстного меню, который копирует картинку в буфер в формате base64. Код всего-ничего.

Выделить код

Код:

chrome.contextMenus.onClicked.addListener(function (info) {
    var img = document.createElement('img');
    img.setAttribute('src', info.srcUrl);
    img.addEventListener('load', copyBase64);
    document.body.appendChild(img);
});
function copyBase64 () {
    var canvas = document.createElement('canvas');
    canvas.width = this.width;
    canvas.height = this.height;
    var ctx = canvas.getContext("2d");
    ctx.drawImage(this, 0, 0);

    var textarea = document.createElement('textarea');
    document.body.appendChild(textarea);

    textarea.value = canvas.toDataURL("image/png");
    textarea.select();

    document.execCommand('copy');

    textarea.remove();
    this.remove();
}
chrome.contextMenus.create({
    'id' : 'copy_base64',
    'title' : 'Copy base64',
    'contexts' : ['image']
});

Можно сделать кнопку с подобным функционалом? Или я плохо искал и такая уже есть?

У Save+ есть опция для включения такого пункта в контекстном меню.

И лучше не плодить темы по поиску, а спрашивать в Обсуждение кнопок CB или Custom Buttons. Там и ответят с большей вероятностью.

Болванка

скрытый текст

Выделить код

Код:

(popup => {
    var before = document.getElementById("context-copyimage-contents").nextSibling;
    var menuitem = popup.insertBefore(document.createElement("menuitem"), before);
    addDestructor(() => menuitem.remove());

    menuitem.id = "context-copyasbase64";
    menuitem.className = "menuitem-iconic";
    menuitem.setAttribute("label", "Copy image as base64");
    menuitem.setAttribute("image", "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AIAQ/wCAEf8AgA//AIAR/wCAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AgBX/AIAVAAAAAAAAAAD/AIAo/wCA//8AgP//AID//wCA//8AgP//AIAoAAAAAAAAAAAAAAAAAAAAAP8AgBL/AID//wCA//8AgA3/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgBAAAAAAAAAAAAAAAAD/AIAR/wCA//8AgP//AIAK/wCACv8AgP//AID//wCAIf8AgAX/AIAh/wCA//8AgP//AIAQAAAAAAAAAAAAAAAA/wCACv8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAUAAAAA/wCABf8AgP//AID//wCACgAAAAD/AIAQ/wCADP8AgCH/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCABf8AgCH/AID//wCA//8AgAv/AIAh/wCA//8AgP//AID//wCA//8AgP//AIAH/wCAB/8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AIAg/wCA//8AgP//AID//wCA//8AgP//AID//wCAB/8AgAf/AID//wCA//8AgP//AID//wCA//8AgP//AIAh/wCAC/8AgP//AID//wCAHP8AgBz/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCACf8AgA7/AIAMAAAAAP8AgAj/AID//wCA//8AgAP/AIAD/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCABQAAAAAAAAAA/wCADf8AgAr/AIAL/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCAB/8AgAr/AID//wCA//8AgCH/AIAH/wCAJf8AgP//AID//wCAI/8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAf/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgCT/AID//wCA//8AgAr/AIAK/wCA//8AgP//AIAKAAAAAP8AgCj/AID//wCA//8AgP//AID//wCA//8AgCP/AIAM/wCA//8AgP//AIAN/wCADf8AgP//AID//wCADQAAAAAAAAAA/wCAEP8AgBH/AIAP/wCAEf8AgBAAAAAAAAAAAP8AgBT/AIAVAAAAAAAAAAD/AIAV/wCAFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//+sQcH5rEGA8KxBAHCsQQBwrEEIQKxBAACsQQAArEEAAKxBAQCsQQwArEEAAKxBAACsQYAArEHBmaxB//+sQQ==");
    menuitem.setAttribute("oncommand", "this.handleCommand();");

    var stream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
    var mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);

    menuitem.handleCommand = () => {
        var array = [], uri = makeURI(gContextMenu.imageURL);
        var channel = Services.io.newChannelFromURI(uri);
        channel.loadFlags |= channel.LOAD_FROM_CACHE;
        channel.asyncOpen({
            onStartRequest: () => {},
            onDataAvailable: (request, context, istream, offset, count) => {
                stream.setInputStream(istream);
                array = array.concat(stream.readByteArray(count));
            },
            onStopRequest: () => {
                var contentType = "image/png";
                try {contentType = channel.contentType;}
                catch(ex) {
                    try {contentType = mimeService.getTypeFromURI(uri);}
                    catch(ex2) {}
                }
                gClipboard.write(
                    "data:" + contentType + ";base64," + btoa(String.fromCharCode.apply(null, array))
                );
            }
        }, null);
    }
    addEventListener("popupshowing", e => {
        if (e.target != popup) return;
        menuitem.hidden = !gContextMenu.onImage;
    }, false, popup);

})(document.getElementById("contentAreaContextMenu"));

turbot, спасибо.
Постараюсь не плодить новых тем.

Dumby, спасибо.
Что изменить в коде, что бы при включенной кнопке пункт меню появлялся сразу, а не после нажатия на кнопку?

ifln пишет

Что изменить в коде, что бы при включенной кнопке пункт меню появлялся сразу, а не после нажатия на кнопку?

В этом смысле, в коде ничего изменять не нужно.
Код предназначен быть добавленным во вкладку «Инициализация (I)».

Вот вариант и для фоновых изображений, наверно.

скрытый текст

Выделить код

Код:

(popup => {

    var copyimage = document.getElementById("context-copyimage-contents");
    var copyimageLabel = "Copy image as base64";

    var viewbgimage = document.getElementById("context-viewbgimage");
    var viewbgimageLabel = "Copy background image as base64";

    var menuitem = document.createElement("menuitem");
    addDestructor(() => menuitem.remove());

    menuitem.id = "context-copyimageasbase64";
    menuitem.className = "menuitem-iconic";
    menuitem.setAttribute("image", "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AIAQ/wCAEf8AgA//AIAR/wCAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AgBX/AIAVAAAAAAAAAAD/AIAo/wCA//8AgP//AID//wCA//8AgP//AIAoAAAAAAAAAAAAAAAAAAAAAP8AgBL/AID//wCA//8AgA3/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgBAAAAAAAAAAAAAAAAD/AIAR/wCA//8AgP//AIAK/wCACv8AgP//AID//wCAIf8AgAX/AIAh/wCA//8AgP//AIAQAAAAAAAAAAAAAAAA/wCACv8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAUAAAAA/wCABf8AgP//AID//wCACgAAAAD/AIAQ/wCADP8AgCH/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCABf8AgCH/AID//wCA//8AgAv/AIAh/wCA//8AgP//AID//wCA//8AgP//AIAH/wCAB/8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AIAg/wCA//8AgP//AID//wCA//8AgP//AID//wCAB/8AgAf/AID//wCA//8AgP//AID//wCA//8AgP//AIAh/wCAC/8AgP//AID//wCAHP8AgBz/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCACf8AgA7/AIAMAAAAAP8AgAj/AID//wCA//8AgAP/AIAD/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCABQAAAAAAAAAA/wCADf8AgAr/AIAL/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCAB/8AgAr/AID//wCA//8AgCH/AIAH/wCAJf8AgP//AID//wCAI/8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAf/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgCT/AID//wCA//8AgAr/AIAK/wCA//8AgP//AIAKAAAAAP8AgCj/AID//wCA//8AgP//AID//wCA//8AgCP/AIAM/wCA//8AgP//AIAN/wCADf8AgP//AID//wCADQAAAAAAAAAA/wCAEP8AgBH/AIAP/wCAEf8AgBAAAAAAAAAAAP8AgBT/AIAVAAAAAAAAAAD/AIAV/wCAFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//+sQcH5rEGA8KxBAHCsQQBwrEEIQKxBAACsQQAArEEAAKxBAQCsQQwArEEAAKxBAACsQYAArEHBmaxB//+sQQ==");
    menuitem.setAttribute("oncommand", "this.handleCommand();");

    var stream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
    var mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);

    menuitem.handleCommand = () => {
        var array = [], uri = makeURI(gContextMenu.imageURL || gContextMenu.bgImageURL);
        var channel = Services.io.newChannelFromURI(uri);
        channel.loadFlags |= channel.LOAD_FROM_CACHE;
        channel.asyncOpen({
            onStartRequest: () => {},
            onDataAvailable: (request, context, istream, offset, count) => {
                stream.setInputStream(istream);
                array = array.concat(stream.readByteArray(count));
            },
            onStopRequest: () => {
                var contentType = "image/png";
                try {contentType = channel.contentType;}
                catch(ex) {
                    try {contentType = mimeService.getTypeFromURI(uri);}
                    catch(ex2) {}
                }
                var step = 500000, str = "";
                while(array.length)
                    str += String.fromCharCode.apply(null, array.splice(0, step))
                gClipboard.write("data:" + contentType + ";base64," + btoa(str));
            }
        }, null);
    }
    addEventListener("popupshowing", e => {
        if (
            e.target != popup || 
            (
                menuitem.hidden = copyimage.hidden && (
                    (viewbgimage.hidden || viewbgimage.disabled) && !(
                        gContextMenu.onLink && gContextMenu.target.ownerGlobal
                            .getComputedStyle(gContextMenu.target).backgroundImage.startsWith("url")
                    )
                )
            )
        ) return;
        menuitem.tooltipText = gContextMenu.imageURL || gContextMenu.bgImageURL;
        if (gContextMenu.onImage) {
            if (menuitem.previousSibling != copyimage) {
                popup.insertBefore(menuitem, copyimage.nextSibling);
                menuitem.setAttribute("label", copyimageLabel);
            }
        } else {
            if (menuitem.previousSibling != viewbgimage) {
                popup.insertBefore(menuitem, viewbgimage.nextSibling);
                menuitem.setAttribute("label", viewbgimageLabel);
            }
        }
    }, false, popup);

})(document.getElementById("contentAreaContextMenu"));

Dumby пишет

Код предназначен быть добавленным во вкладку «Инициализация (I)».

:dumb: Еще раз большое спасибо.
Второй вариант тоже отлично работает.