I want to have several html files in my extension so I can open each of them according to some conditions or events. Say I want a.html to be opened when the user chooses an option on the context menu.
I tried the following:
manifest.json:
{
"name": "My extension",
"version": "1.1",
"background": { "page": ["background.html"] },
"incognito": "split",
"permissions": ["tabs", "<all_urls>", "contextMenus"],
"icons": { "16": "images/16.png" },
"manifest_version": 2
}
background.html:
<!DOCTYPE html>
<html>
<head>
<script src="background.js"></script>
<script src='someWindow.js'></script>
</head>
<body>
</body>
</html>
background.js:
var winID;
chrome.contextMenus.onClicked.addListener(function proccess_interested(info, tab){
chrome.tabs.create({active: false}, function(newTab) {
// After the tab has been created, open a window to inject the tab into it.
chrome.windows.create(
{
tabId: newTab.id,
type: "popup",
url: chrome.extension.getURL('a.html'),
focused: true
},function(window){
winID = newWindow.id;
});
});
})
chrome.extension.onMessage.addListener(function(Msg, sender, sendResponse) {
if(Msg.close_comment_win){
chrome.windows.remove(winID, function(){});
}
});
someWindow.js:
function hide_win()
{
chrome.extension.sendMessage({close_win: close}, function(response) {});
}
a.html:
<!DOCTYPE html>
<html>
<head>
<script src='someWindow.js'></script>
head //with tags, can't show it here
body
<input type='button' value=' Cancel ' onclick="hide_win()"></input>
</body>
</html>
The window is opened when context menu is clicked, but when hitting cancel, it's not closed. console.log says: Refused to execute inline event handler because it violates the following Content Security Policy directive: "script-src 'self' chrome-extension-resource:". I guess the reason is that a.html is not part of the extension, even though someWindow.js which triggers sendMessage is part of the extension.
Including a.html in the extension through manifest isn't an option as no more than one background html page can be included.
Of course I get the same when putting chrome.windows.remove(winID, function(){}); directly in hide_win() without using sendMessage.
Any ideas how to get this job done?
Just as the error says, it is against v2's content security policy to have any inline code in extension html pages. Simply move that handler to your js file and it should work fine.
Related
I'm developing a chrome extension. It will make it possible to press a button, and it will capture a screenshot of your screen, then you will be able to save it to your computer.
However after attempting to load my extension at chrome://extensions/
I'm getting either of 2 messages:
"Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist."
"Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' blob: filesystem:". Either the 'unsafe-inline' keyword, a hash ('sha256-0xoq3Fm+45tEb3FQIwY9RYnpdCuDu/FcIzz/s1HPTAM='), or a nonce ('nonce-...') is required to enable inline execution."
I've been at this for a long time now and can't seem to find the problem. I have inserted the codes below, can anyone help me fix this?
Might be even the smallest oversight from my part, but would be super grateful for any help.
See code below:
manifest.json
{
"manifest_version": 2,
"name": "MySnap",
"description": "Take a screenshot of a selected portion of the screen",
"version": "1.0",
"permissions": [
"tabs",
"activeTab"
],
"browser_action": {
"default_icon": "icon.png",
"default_title": "MySnap",
"default_popup": "popup.html"
},
"content_security_policy": "script-src 'self' 'sha256-Ws4H0eytNaM/o8NllzTlOPZFeyohSxu1N5dQ7JOcjMI='"
}
popup.html
<!DOCTYPE html>
<html>
<head>
<title>MySnap</title>
</head>
<body>
<h1>MySnap</h1>
<button id="screenshot-button">Take Screenshot</button>
<div id="screenshot-container"></div>
<script src="screenshot.js"></script>
<script>
document.getElementById("screenshot-button").addEventListener("click", function() {
chrome.runtime.sendMessage({message: "take_screenshot"}, function(response) {
// Do something with the response
});
});
</script>
<script>
function takeScreenshot() {
chrome.runtime.sendMessage({message: "take_screenshot"}, function(response) {
if (response && response.screenshotUrl) {
// Create a link element and set its href to the screenshot URL
var link = document.createElement("a");
link.href = response.screenshotUrl;
// Set the download attribute of the link element
link.download = "screenshot.png";
// Append the link element to the DOM
document.body.appendChild(link);
// Click the link to initiate the download
link.click();
// Remove the link element from the DOM
document.body.removeChild(link);
}
});
}
</script>
</body>
</html>
screenshot.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.message === "take_screenshot") {
chrome.tabs.captureVisibleTab(null, {}, function(screenshotUrl) {
sendResponse({screenshotUrl: screenshotUrl});
});
}
return true;
});
background.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.message === "take_screenshot") {
chrome.tabs.captureVisibleTab(null, {}, function(screenshotUrl) {
sendResponse({screenshotUrl: screenshotUrl});
});
}
});
You can make the screenshot in the popup script, no need for messaging.
remove content_security_policy from manifest.json
remove all <script> elements from popup.html except <script src="screenshot.js">
remove background.js
// popup.html:
<!DOCTYPE html>
<h1>MySnap</h1>
<button id="screenshot-button">Take Screenshot</button>
<div id="screenshot-container"></div>
<script src="screenshot.js"></script>
// screenshot.js:
document.getElementById('screenshot-button').onclick = () => {
chrome.tabs.captureVisibleTab(url => {
const el = document.createElement('a');
el.href = url;
el.download = 'screenshot.png';
document.body.appendChild(el);
el.click();
el.remove();
});
};
We have an extension that currently has host_permission access to 3 different domains. We want to add a 4th. The issue is that this 4th domain will be set by the user in the extension's options page, and could be anything.
I have read about using all_urls as the host_permission and believe it can be done that way. My questions are:
Is there no other way to give permission to the specific domain the user sets up in options? (Something like the background script would see a new domain there and prompt the user for permission to access that 1 domain)
Is there potentially any issues with getting pass Google's approval process when using all_urls? We would be using all_urls to access just 1 domain, which I read was an issue at least in the past, but in our case that 1 domain is unknown at install time.
This sample adds host to origins on request button click.
Also, click the getAll button to display origins and permissions on the console.
Adding to origins without user gesture will result in the following error:
"Unchecked runtime.lastError: This function must be called during a user gesture"
manifest.json
{
"name": "optional_host_permissions",
"version": "1.0",
"manifest_version": 3,
"host_permissions": [
"*://*.google.com/"
],
"optional_host_permissions": [
"<all_urls>"
],
"action": {
"default_popup": "popup.html"
}
}
myscript.js
const elmRequest = document.getElementById("request");
const elmGetAll = document.getElementById("getAll");
elmRequest.onclick = () => {
request();
}
elmGetAll.onclick = () => {
getAll();
}
function request() {
chrome.permissions.request({
origins: ["https://hoge.com/"]
}, (granted) => {
console.log(granted);
});
}
function getAll() {
chrome.permissions.getAll((permissions) => {
console.log(permissions);
});
}
popup.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body style="min-width:250px">
<input type="button" id="request" value="request"><br>
<input type="button" id="getAll" value="getAll">
<script src="myscript.js"></script>
</body>
</html>
This seems to be the easiest thing to do, but it's just not working. In a normal browser the .html and .js files works perfectly, but in the Chrome/Firefox extension the onClick function is not performing what it's supposed to do.
.js file:
function hellYeah(text) {
document.getElementById("text-holder").innerHTML = text;
}
.html file:
<!doctype html>
<html>
<head>
<title>
Getting Started Extension's Popup
</title>
<script src="popup.js"></script>
</head>
<body>
<div id="text-holder">
ha
</div>
<br />
<a onClick=hellYeah("xxx")>
hyhy
</a>
</body>
</html>
So basically once the user clicks "hyhy", "ha" should change into "xxx". And again - it works perfectly in the browser but does not work in the extension. Do you know why? Just in case I'm attaching the manifest.json below as well.
manifest.json:
{
"name": "My First Extension",
"version": "1.0",
"manifest_version": 2,
"description": "The first extension that I made.",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"http://api.flickr.com/"
]
}
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>
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".
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.
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>
As already mentioned, Chrome Extensions don't allow to have inline JavaScript due to security reasons so you can try this workaround as well.
HTML file
<!doctype html>
<html>
<head>
<title>
Getting Started Extension's Popup
</title>
<script src="popup.js"></script>
</head>
<body>
<div id="text-holder">ha</div><br />
<a class="clickableBtn">
hyhy
</a>
</body>
</html>
<!doctype html>
popup.js
window.onclick = function(event) {
var target = event.target ;
if(target.matches('.clickableBtn')) {
var clickedEle = document.activeElement.id ;
var ele = document.getElementById(clickedEle);
alert(ele.text);
}
}
Or if you are having a Jquery file included then
window.onclick = function(event) {
var target = event.target ;
if(target.matches('.clickableBtn')) {
alert($(target).text());
}
}
I have below files with me
manifest.json
{
"name": "Get Response URL",
"version": "1.0",
"manifest_version": 2,
"name": "Test" ,
"browser_action": {
"icon":"icon.png"
},
"background": {
"persistent": false,
"scripts": ["background.js"]
},
"browser_action": {
"default_icon": "icon.png"
},
"permissions":["https://myblog.com/*"] ,//Put All your URL here
"manifest_version": 2
}
background.js
chrome.browserAction.onClicked.addListener(function (tab) { //Fired when User Clicks ICON
if (tab.url.indexOf("https://myblog.com/page1.html")==0) { // Inspect whether the place where user clicked matches with our list of URL
chrome.tabs.executeScript(tab.id, {
"file": "page2.js"
}, function () { // Execute your code
console.log("Script Executed .. "); // Notification on Completion
});
}
else if (tab.url.indexOf("https://myblog.com/page2.html")==0) { // Inspect whether the place where user clicked matches with our list of URL
chrome.tabs.executeScript(tab.id, {
"file": "page1.js"
}, function () { // Execute your code
console.log("Script Executed .. "); // Notification on Completion
});
}
});
Page1.html
<html>
<head>
<title>Page1</title>
</head>
<body>
<input type='button' name='submit' id='myBtn' value='click here to move to next page' onclick="document.location.href='page2.html';" />
</body>
</html>
page2.html
<html>
<head>
<title>Page2</title>
</head>
<body>
<input type="text" name="textBox" id="myText" />
</body>
</html>
And to two JavaScript files page1.js and page2.
page1.js
var button=document.getElementById("myBtn");
button.click();
Page2.js
document.getElementById("myText").value="Text Box";
I have developed a Chrome extension. On first page when I click the Chrome extension icon the functionality is working good as per JavaScript file(page1.js) for https://myblog.com/page1 page.
And what I am doing on https://myblog.com/page1 page with the help of page1.js is to just click a button to move to second page that is https://myblog.com/page2. Now I want that page2.js should wrok on page https://myblog.com/page2 as scripted(page2.js) but its not working.
Script is working good when I click the extension icon on page1 and then again click the extension icon on page2.
But I want to extension icon should be clicked on page1 not repetitively.
Edited the Question
Added page1.html and page2.html
page1.js and page2.js
Is it possible to doing the same.
If yes where I am doing the mistake?
here's what you need to do.
Your code block chrome.browserAction.onClicked.addListener(function (tab) { makes the JS functions to execute only when you click the icon. That's why your Page 1 works whereas the Page 2 doesn't.
You want the page2.js to run as soon as the Page2.html is opened right? Hence change the coding of your Page2.html. Add the below code in the page2.html itself.
document.addEventListener("DOMContentLoaded", function(event) {
//Do work
});
What this will do is, as soon as the Page2.html is loaded, it will execute the Javascript. Eventhough it works in most of the browsers, but for IE 8 and below it won't.
If you need for IE 8 and below too, then try using the following code:
<script type="text/JavaScript">
function funtionToBeCalled(){
// code that will be exected after dom ready
}
window.onload=funtionToBeCalled;
</script>
I'm new to the Chrome extensions development, and i have the following questions:
My extension should work in background, with no UI, and show an alert dialog every time the user visits a specific web page. So it should work always, in backround, when the browser is executed.
I was trying with the following code without results:
manifest.json
{
"name": "My First Extension",
"version": "1.0",
"description": "The first extension that I made.",
"background_page": "background.html",
"permissions": [
"history"
]
}
background.html
<html>
<head>
<script>
chrome.history.onVisited.addListener(function(HistoryItem result) {
if (result.url == "http://my.url.com") {
alert("My message");
}
});
</script>
</head>
</html>
What's wrong with this code?
Thanks
Take HistoryItem out of the function and you are fine:
<html>
<head>
<script>
chrome.history.onVisited.addListener(function(result) {
if (result.url == "http://my.url.com/") {
alert("My message");
}
});
</script>
</head>
</html>
Also note that I added the slash at the end of "http://my.url.com/" since that is what will be returned in result.url.
Test this:
<script>
chrome.history.onVisited.addListener(function(e) { console.log(e)})
</script>
it's clearly