0

I'm trying to implement some JS library on Chrome extension that scans all images if contain nudity. Anyway, doing that by trying to read the context of canvas for each image, I'm having the exception below. This will happen on every page. I don't understand why I'm having this exception and how should I handle/avoid it.

Exception on script.js:33

Uncaught SecurityError: Failed to execute 'getImageData' on 
'CanvasRenderingContext2D': The canvas has been tainted by cross-origin

script.js:

$(window).load(function() {
    console.log('NudityBlocker -- Loaded');
    $('img:visible').each(function() {
        var uniqueId = guid();
        var thisImage = $(this);
        thisImage.css('visibility', 'hidden');
        thisImage.attr('data-nudejs-id', uniqueId);
        setTimeout(scan(thisImage), 0);
    });
    console.log('NudityBlocker -- Finished scanning');
});

function scan(image) {
    // Create necessary elements
    var nudeJsId = image.attr('data-nudejs-id');
    var canvas = document.createElement("canvas");
    var ctx = canvas.getContext('2d');

    // Draw image on canvas
    canvas.width = image[0].width;
    canvas.height = image[0].height;
    ctx.drawImage(image[0], 0, 0);

    // Get image date and create scanner worker
    var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
    var myWorker = new Worker(nudeUrl);
    var message = [imageData, canvas.width, canvas.height];
    myWorker.postMessage(message);
    myWorker.onmessage = function(event){
        var imageObj = $('[data-nudejs-id=' + nudeJsId + ']');
        if(event.data){ 
            // It's nude
            var width = imageObj.width();
            var height = imageObj.height();
            imageObj.attr('src', chrome.extension.getURL('assets/images/blocked.png')).width(width).height(height);
        }
        // Display images again either original or with blocked sign
        imageObj.css('visibility', 'visible');
    }
}

// Generate GUID's
function guid() {
    function s4() {
        return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
    }
    return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
}

background.js

function executeScripts(tabId) {
    console.log('NudityBlocker -- ExecuteScripts()');
    chrome.tabs.executeScript(tabId, {file: "assets/js/jquery.min.js"}, function() {
        chrome.tabs.executeScript(tabId, {file: "assets/js/nudejs.min.js"});
        chrome.tabs.executeScript(tabId, {file: "assets/js/script.js"});
    });
}

var onComplete = function (details) { 
    executeScripts(details.tabId); 
}

var onUpdate = function (tabId) { 
    executeScripts(tabId); 
}

function updateIcons() {
    console.log('NudityBlocker -- updateIcons()');
    chrome.storage.sync.get(['nudityBlockerStatus'], function(items) {
        status = items.nudityBlockerStatus;
        console.log('NudityBlocker -- updateIcons() - Status: ' + status);
        if(status === 'enabled') {
            chrome.browserAction.setIcon({path: "assets/icon/icon48a.png"});
        }
        else if(status === 'disabled') {
            chrome.browserAction.setIcon({path: "assets/icon/icon48d.png"});
        }
    });
}

function updateStatus(status) {
    console.log('NudityBlocker -- UpdateStatus()');
    console.log('NudityBlocker -- UpdateStatus() - Status: ' + status);
    var result;
    if (status === 'enabled') {
        result = "disabled";
        chrome.storage.sync.set({'nudityBlockerStatus' : result});
        chrome.tabs.onUpdated.removeListener(onUpdate);
        chrome.webNavigation.onCompleted.removeListener(onComplete);
    }
    else if (status === 'disabled') {
        result = "enabled";
        chrome.storage.sync.set({'nudityBlockerStatus' : result});
        addListeners();
    }
    updateIcons();
    return result;
}

function addListeners() {
    console.log('NudityBlocker -- AddListeners()');
    chrome.tabs.onUpdated.addListener(onUpdate);
    chrome.webNavigation.onCompleted.addListener(onComplete);
}

function initialize() {
    console.log('NudityBlocker -- Initialize()');
    chrome.storage.sync.set({'nudityBlockerStatus' : 'enabled'});
    console.log('NudityBlocker -- Initialize() - Status: ' + status);
    updateIcons();
    addListeners();
}

// Add event listeners
chrome.runtime.onInstalled.addListener(initialize());

P.s. The most similar issue to this that I found in StackOverflow is this below:

Canvas has been tainted by cross-origin data via local chrome:// extension URL

But this is not a duplicate, also I didn't fully understood the answer of it.

Trim Kadriu
  • 421
  • 3
  • 19
  • If you drawImage an image that is hosted on a different domain than the website domain, then your canvas will be tainted and `.getImageData` will be disabled. The simplest solution is to host the images and the webpage on the same domain. – markE Apr 29 '15 at 15:58
  • Also see this: https://stackoverflow.com/questions/15167738/xmlhttprequest-succeeds-without-manifest-permissions-maybe-cors –  Apr 29 '15 at 20:25
  • And this:https://stackoverflow.com/questions/11845118/load-remote-webpage-in-background-page-chrome-extension/11846136#11846136 –  Apr 29 '15 at 20:26

0 Answers0