Extension gives this error: Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution [duplicate]

Solution 1:

Chrome Extensions don't allow you to have inline JavaScript (documentation).
The same goes for Firefox WebExtensions (documentation).

You are going to have to do something similar to this:

Assign an ID to the link (<a onClick=hellYeah("xxx")> becomes <a id="link">), and use addEventListener to bind the event. Put the following in your popup.js file:

document.addEventListener('DOMContentLoaded', function() {
    var link = document.getElementById('link');
    // onClick's logic below:
    link.addEventListener('click', function() {
        hellYeah('xxx');
    });
});

popup.js should be loaded as a separate script file:

<script src="popup.js"></script>

Solution 2:

Reason

This does not work, because Chrome forbids any kind of inline code in extensions via Content Security Policy.

Inline JavaScript will not be executed. This restriction bans both inline <script> blocks and inline event handlers (e.g. <button onclick="...">).

How to detect

If this is indeed the problem, Chrome would produce the following error in the console:

Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' chrome-extension-resource:". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution.

To access a popup's JavaScript console (which is useful for debug in general), right-click your extension's button and select "Inspect popup" from the context menu.

More information on debugging a popup is available here.

How to fix

One needs to remove all inline JavaScript. There is a guide in Chrome documentation.

Suppose the original looks like:

<a onclick="handler()">Click this</a> <!-- Bad -->

One needs to remove the onclick attribute and give the element a unique id:

<a id="click-this">Click this</a> <!-- Fixed -->

And then attach the listener from a script (which must be in a .js file, suppose popup.js):

// Pure JS:
document.addEventListener('DOMContentLoaded', function() {
  document.getElementById("click-this").addEventListener("click", handler);
});

// The handler also must go in a .js file
function handler() {
  /* ... */
}

Note the wrapping in a DOMContentLoaded event. This ensures that the element exists at the time of execution. Now add the script tag, for instance in the <head> of the document:

<script src="popup.js"></script>

Alternative if you're using jQuery:

// jQuery
$(document).ready(function() {
  $("#click-this").click(handler);
});

Relaxing the policy

Q: The error mentions ways to allow inline code. I don't want to / can't change my code, how do I enable inline scripts?

A: Despite what the error says, you cannot enable inline script:

There is no mechanism for relaxing the restriction against executing inline JavaScript. In particular, setting a script policy that includes 'unsafe-inline' will have no effect.

Update: Since Chrome 46, it's possible to whitelist specific inline code blocks:

As of Chrome 46, inline scripts can be whitelisted by specifying the base64-encoded hash of the source code in the policy. This hash must be prefixed by the used hash algorithm (sha256, sha384 or sha512). See Hash usage for <script> elements for an example.

However, I do not readily see a reason to use this, and it will not enable inline attributes like onclick="code".

Solution 3:

I had the same problem, and didn´t want to rewrite the code, so I wrote a function to modify the code and create the inline declarated events:

function compile(qSel){
    var matches = [];
    var match = null;
    var c = 0;

    var html = $(qSel).html();
    var pattern = /(<(.*?)on([a-zA-Z]+)\s*=\s*('|")(.*)('|")(.*?))(>)/mg;

    while (match = pattern.exec(html)) {
        var arr = [];
        for (i in match) {
            if (!isNaN(i)) {
                arr.push(match[i]);
            }
        }
        matches.push(arr);
    }
    var items_with_events = [];
    var compiledHtml = html;

    for ( var i in matches ){
        var item_with_event = {
            custom_id : "my_app_identifier_"+i,
            code : matches[i][5],
            on : matches[i][3],
        };
        items_with_events.push(item_with_event);
        compiledHtml = compiledHtml.replace(/(<(.*?)on([a-zA-Z]+)\s*=\s*('|")(.*)('|")(.*?))(>)/m, "<$2 custom_id='"+item_with_event.custom_id+"' $7 $8");
    }

    $(qSel).html(compiledHtml);

    for ( var i in items_with_events ){
        $("[custom_id='"+items_with_events[i].custom_id+"']").bind(items_with_events[i].on, function(){
            eval(items_with_events[i].code);
        });
    }
}

$(document).ready(function(){
    compile('#content');
})

This should remove all inline events from the selected node, and recreate them with jquery instead.

Solution 4:

I decide to publish my example that I used in my case. I tried to replace content in div using a script. My problem was that Chrome did not recognized / did not run that script.

In more detail What I wanted to do: To click on a link, and that link to "read" an external html file, that it will be loaded in a div section.

  • I found out that by placing the script before the DIV with ID that was called, the script did not work.
  • If the script was in another DIV, also it does not work
  • The script must be coded using document.addEventListener('DOMContentLoaded', function() as it was told

        <body>
        <a id=id_page href ="#loving"   onclick="load_services()"> loving   </a>
    
            <script>
                    // This script MUST BE under the "ID" that is calling
                    // Do not transfer it to a differ DIV than the caller "ID"
                    document.getElementById("id_page").addEventListener("click", function(){
                    document.getElementById("mainbody").innerHTML = '<object data="Services.html" class="loving_css_edit"; ></object>'; });
                </script>
        </body>
    
      <div id="mainbody" class="main_body">
            "here is loaded the external html file when the loving link will 
             be  clicked. "
      </div>