I wanted a "simple" extension:
It grabs some information of the initial site
It loads a new site (a local HTML file)
It takes the new site as a template and puts the information into the
right places.
I think I only need:
Content Script (to grab, save and replace the information form the old
to the new site)
Background Script (to act on the click on the extension)
Manifest
the local HTML template (to load it and to use it as a template)
The main problem is, that I get no output from the Content Script on the new loaded site (HTML template) even though there is no error message when passing a message from the Background Script to the Content Script..
Content Script:
chrome.runtime.onConnect.addListener(function (port) {
console.assert(port.name == "knockknock");
port.onMessage.addListener(function (msg, port) {
gotMessage(msg, port);
});
});
function gotMessage(message, port) {
if (message.txt == "Sammle Rechnungsinformationen") {
getBestellInformationen();
port.postMessage({ response: "true" });
}
else if (message.tee == "Aendere das Template") {
setBestellInformationen();
port.postMessage({ response: "true" });
}
}
function getBestellInformationen() {
let bestellInformationen = {};
let tables = document.getElementsByTagName('td');
for (elt of tables) {
elt.style['background-color'] = '#FF00FF';
}
for (i = 0; i < tables.length; i++) {
content = tables[i].textContent;
switch (content) {
...
case String(content.match("Net.Grand.Total:")):
bestellInformationen["Gesamtbetrag Netto"] = (tables[i + 1].textContent);
break;
}
}
// Save the current myArray value.
chrome.storage.local.set({ key: bestellInformationen }, function () {
console.log('Value is set to ' + bestellInformationen);
});
}
function setBestellInformationen() {
// Retrieve the the stored value, defaulting to an empty array.
chrome.storage.local.get(['key'], function (result) {
console.log('Value currently is ' + result.key);
let bestellInformationen = result.key;
// Aendere das geoeffnete Template
console.log("Template Bearbeiten");
console.log(bestellInformationen);
for (key in bestellInformationen) {
console.log(bestellInformationen[key]);
}
});
chrome.storage.local.clear(function () { console.log("storage cleared successfully"); });
}
Backgroud Script:
chrome.browserAction.onClicked.addListener(buttonClicked);
function buttonClicked(tab) {
var port = chrome.tabs.connect(tab.id, { name: "knockknock" });
if (!((String(tab.url.match("\X*Die_HTML_Datei.html"))) == "Die_HTML_Datei.html")) {
let msg = { txt: "Sammle Rechnungsinformationen" };
port.postMessage(msg);
port.onMessage.addListener(function (msg) {
if (msg.response == "true") {
oeffneDieRechnungHTML();
port.disconnect();
});
} else {
let msg = {txt: "Aendere das Template" };
port.postMessage({ txt: "Aendere das Template" });
port.onMessage.addListener(function (msg) {
});
port.disconnect();
}
}
function oeffneDieRechnungHTML() {
chrome.tabs.update({ url: chrome.runtime.getURL("Die_HTML_Datei.html") },
function () { console.log("Seite wurde Erfolgreich zur HTML-Rechnung gewechselt") });
}
Manifest:
{
"manifest_version": 2,
"name": "Codingtrain",
"version": "0.1",
"permissions": [ "activeTab", "tabs", "storage" ],
"web_accessible_resources": [ "Die_HTML_Datei.html" ],
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": [ "content.js" ]
}
],
"background": {
"scripts": [ "background.js" ]
},
"browser_action": {
"default_icon": "get_started48.png"
}
}
EDIT: Reworded for easier understanding.
Related
I can find plenty of questions for sending a message from Bbackground to content scripts or from popup to background scripts, but I can't find any on how to send a message from options.js (triggered from my options page) to background.js for my Chrome extension
manifest.js
{
...
"manifest_version": 3,
"permissions": [
"alarms",
"contextMenus",
"notifications",
"storage"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_popup": "popup.html"
},
"options_page": "options.html",
"icons": {
"48": "/assets/icons/icon-48.png",
"128": "/assets/icons/icon-128.png"
}
}
Code in options.js
// Save options
document.querySelector('#save').addEventListener('click', ()=> {
// Check for selected regions
let selectedRegions = [];
regionChecks.forEach(elm => {
if (elm.checked)
selectedRegions.push(elm.id.replace('region-', ''))
});
// Save selections
chrome.storage.sync.set({ 'regions': selectedRegions });
// Check for refresh period
let refreshPeriod = document.querySelector('#refresh');
if (refreshPeriod) {
refreshPeriod = parseInt(refreshPeriod.value);
if (refreshPeriod === NaN) {
refreshPeriod = 5;
} else if(refreshPeriod < 2) {
refreshPeriod = 2;
} else if (refreshPeriod > 120) {
refreshPeriod = 120;
}
// Save period
chrome.storage.sync.set({ 'refresh': refreshPeriod });
}
// Loop through features and remove if region gone
chrome.storage.local.get(['features'], featuresData => {
let featureCount = 0;
if (featuresData.features) {
featureSet = featuresData.features;
Object.keys(featureSet).forEach(key => {
if (!selectedRegions.includes(featureSet[key].Region)) {
delete featureSet[key];
} else if (featureSet[key].Status !== "read") {
featureCount = featureCount + 1;
}
});
// Update features data
chrome.storage.local.set({ 'features': featureSet });
// Update the badge
if (featureCount > 0) {
chrome.action.setBadgeText({ text: featureCount.toString() });
} else {
chrome.action.setBadgeText({ text: '' });
}
}
});
// Trigger an update
chrome.runtime.sendMessage('', {
type: 'update'
});
// Close the options page
close();
});
Code in background.js
// Listen for messages
chrome.runtime.onMessage.addListener(data => {
console.log(data);
if (data.type === 'update') {
scheduleRequest();
}
});
But nothing is happening
I've tried using tabs.sendMessage, but even if I put tabs in permissions it tells me that it isn't defined when I add it to the function chrome.tabs.sendMessage(tabs[0].id, {...
My test results contradict your claim.
manifest.json
{
"manifest_version": 3,
"name": "hoge",
"version": "1.0",
"background": {
"service_worker": "background.js"
},
"options_page": "options.html"
}
background.js
// Listen for messages
chrome.runtime.onMessage.addListener(data => {
console.log(data);
if (data.type === 'update') {
console.log("hoge")
// scheduleRequest();
}
});
options.js
// Trigger an update
chrome.runtime.sendMessage('', {
type: 'update'
});
options.html
<html>
<body>
options page
<script src="options.js"></script>
</body>
</html>
After some input from #woxxom and #Norio, and a little bit more troubleshooting, this is how I managed to get it to work
option.js:
// Trigger an update
chrome.runtime.sendMessage({ type: 'update' }, result => {
// Close the options page
close();
});
I had to move the close(); event into the return function so it didn't prematurely close off the code. I also had to have a paramter in the return function for some reason otherwise it wouldn't run with an empty arrow function.
background.js:
// Listen for messages
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'update') {
startRequest(true).then(sendResponse);
return true;
}
});
My function to be triggered in the background.js file was already an async promise function, so I had to add a .then() for the onMessage response function to trigger once it had run. It also wouldn't work without having return true; in there.
I want to make a simple browser extension like Font Face Ninja which toggles UI when page_action or browser_action is clicked.
The following code using browser_action works -
background.js
chrome.browserAction.onClicked.addListener(function(tab) {
console.log(`clicked browserAction`)
})
manifest.json
{
...
"browser_action": {
"default_icon": {
"19": "icon19.png",
"38": "icon38.png"
}
},
...
}
While the following code using page_action does not work -
background.js
chrome.pageAction.onClicked.addListener(function(tab) {
console.log(`clicked pageAction`)
})
manifest.json
{
...
"page_action": {
"default_icon": {
"19": "icon19.png",
"38": "icon38.png"
}
},
...
}
According to MDN docs,
Page actions are like browser actions, except that they are associated with particular web pages rather than with the browser as a whole. If an action is only relevant on certain pages, then you should use a page action and display it only on relevant pages. If an action is relevant to all pages or to the browser itself, use a browser action.
Which confirms I want to use page_action but its not working.
How do I make it work using page_action?
Page Action
A way to make page_action work can be found on my Github → https://github.com/deadcoder0904/insert-remove-ui-chrome-extension/tree/page_action
background.js
var hasExecutedOnce = false
function addUI(tabId) {
chrome.tabs.sendMessage(tabId, {
from: 'background',
subject: 'isUIAdded?',
})
}
chrome.runtime.onInstalled.addListener(function() {
chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
chrome.declarativeContent.onPageChanged.addRules([
{
conditions: [
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostEquals: 'www.google.co.in' },
}),
],
actions: [new chrome.declarativeContent.ShowPageAction()],
},
])
})
})
chrome.pageAction.onClicked.addListener(function(tab) {
if (!hasExecutedOnce) {
chrome.tabs.executeScript(
tab.id,
{
file: 'contentScript.js',
},
function() {
addUI(tab.id)
},
)
hasExecutedOnce = true
}
addUI(tab.id)
})
contentScript.js
var body = document.getElementsByTagName('body')[0]
function insertUI() {
var div = document.createElement('div')
div.setAttribute('id', 'sample-extension-12345')
div.innerHTML = `<h1>Sample Extension</h1>`
body.appendChild(div)
}
function removeUI() {
document.getElementById('sample-extension-12345').remove()
}
function main() {
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.subject === 'isUIAdded?') {
const id = document.getElementById('sample-extension-12345')
if (id === null) insertUI()
else removeUI()
}
})
}
main()
Browser Action
It also has a solution for browser_action on the master branch → https://github.com/deadcoder0904/insert-remove-ui-chrome-extension/
background.js
var hasExecutedOnce = false
function addUI(tabId) {
chrome.tabs.sendMessage(tabId, {
from: 'background',
subject: 'isUIAdded?',
})
}
chrome.browserAction.onClicked.addListener(function(tab) {
if (!hasExecutedOnce) {
chrome.tabs.executeScript(
tab.id,
{
file: 'contentScript.js',
},
function() {
addUI(tab.id)
},
)
hasExecutedOnce = true
}
addUI(tab.id)
})
contentScript.js
var body = document.getElementsByTagName('body')[0]
function insertUI() {
var div = document.createElement('div')
div.setAttribute('id', 'sample-extension-12345')
div.innerHTML = `<h1>Sample Extension</h1>`
body.appendChild(div)
}
function removeUI() {
document.getElementById('sample-extension-12345').remove()
}
function main() {
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.subject === 'isUIAdded?') {
const id = document.getElementById('sample-extension-12345')
if (id === null) insertUI()
else removeUI()
}
})
}
main()
I have an extension that declaratively specifies using a content_script:
manifest.json:
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"run_at": "document_end"
}
],
I'm reading that by instead specifying the activeTab permission, it won't alert about permissions during installation:
https://developer.chrome.com/extensions/activeTab
My question is: how can you switch to using
"permissions":["activeTab"]
from using content_scripts?
Here's my popup.js code that calls the content_script:
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, { action: "checkForCode" }, function (response) {
if (!!response) { showResults(response.results); }
});
});
and the content_script's event handler:
chrome.runtime.onMessage.addListener(
function (request, sender, sendResponse) {
if (request.action == "checkForCode") {
getCode(request, sender, sendResponse);//method sends callback.
return true;
}
});
This code works just fine, but I'm wondering how to use it with the activeTab permissions. Should I just add the content.js via chrome.tags.executeScript(), then reference it the same way?
In your manifest.json you need to set
"permissions": ["activeTab", "tabs"],
and
"background": {
"scripts": ["content.js"],
"persistent": false
},
your content.js as example:
// metaCode will be injected and executed in the current tab
// the returned results you can use in callbackFunction
var metaCode = 'var descr = document.querySelector("meta[name=\'description\']");'
+ 'var keyw = document.querySelector("meta[name=\'keywords\']");'
+ 'if (descr) var descr = descr.getAttribute("content");'
+ 'if (keyw) var keyw = keyw.getAttribute("content");'
+ '({'
+ ' description: descr || "",'
+ ' keywords: keyw || "",'
+ '})';
chrome.tabs.executeScript(
tab.id,
{code: metaCode}, // get meta key words
callbackFunktion
);
function callbackFunktion(results) {
var result = results[0];
var description = result.description;
var keywords = result.keywords;
// and so on ... whatever you want
// to do with results from the
// injected script from 'metaCode'
}
I am working with a webpage which have an <iframe> to hold the university website.
However, some of the link in the university website will open a new tab inside the browser, normally it is not a problem but my webpage is going to be display on a 55" touchscreen in full screen mode . Therefore, my webpage will be blocked by the new tabs.
So, now I am using Chrome Extension to handle the new tabs. I modified one of the Extension from internet to close and redirect the new tabs back to <iframe> by using contentscript and background. And it WORK for most of the new tabs. (not all new tabs)
Because some of the <a>s' href is using javascript like this one :
High Hybrid Reinforced Soil Slope as Runway Support - Tana Toraja Airport Case Study
tab.url didn't return anything (it return empty string). The alert() like
alert('link:'+tab.url)
this. Will become this :
alert('link:'+'')
Anyway to tackle this problem?
----Code for reference----
manifest.json
{
https://clients2.google.com/service/update2/crx",
"manifest_version": 2,
"name": "Inteset Chrome Kiosk Helper",
"short_name": "Kiosk Helper",
"description": "Traps network errors. Shows user friendly error page allowing user to return to previous page. Blocks new windows and new tabs.",
"version": "1.1.12",
"options_page": "options.html",
"options_ui": {
"page": "options.html",
"chrome_style": true
},
"content_scripts": [
{
"matches": ["http://localhost/newyear/index.php"],
"js": ["myjs.js"]
}
],
"background": {
"persistent": false,
"scripts": ["background.js"]
},
"web_accessible_resources": [
"style.css",
"popup.js",
"options.js"
],
"permissions": [
"webNavigation",
"tabs",
"http://*/*", "https://*/*",
"history",
"activeTab",
"storage"
],
"icons": {
"16" : "Logo16x16.png",
"48" : "Logo48x48.png",
"128": "Logo128x128.png"
}
}
background.js I only modify the code inside : chrome.tabs.onCreated.addListener(...)
chrome.webNavigation.onErrorOccurred.addListener(function (data) {
var errs = "net::ERR_BLOCKED_BY_ADMINISTRATOR;net::ERR_NAME_NOT_RESOLVED;net::ERR_CONNECTION_FAILED;net::ERR_CONNECTION_RESET;net::ERR_EMPTY_RESPONSE;net::ERR_FAILED;net::ERR_CONNECTION_REFUSED;net::ERR_INTERNET_DISCONNECTED";
if (errs.indexOf(data.error) != -1) {
var Page = GetPageName( data.error )
chrome.tabs.update(data.tabId, {
url: Page
});
}
});
chrome.tabs.onCreated.addListener(function (tab) {
chrome.tabs.query(
{ currentWindow: true },
function (tabs) {
if (tabs.length > 1) {
chrome.storage.local.get({
blockWindows: false,
blockTabs: false
}, function (items) {
chrome.windows.getCurrent({}, function (window) {
if (window.state == "fullscreen"||true) {
if (items.blockTabs) {
setTimeout(function(){
var _url = tabs[tabs.length - 1].url;
alert("New tabs [" + tabs.length + "] { " + _url + " }");
for(var i=0;i<tabs.length;i++){
// alert(tabs[i].url);
if(tabs[i].url == "http://localhost/UST_VW_web/index.php"){
alert('bg found url ' + tabs[i].url + '\n' + _url);
chrome.tabs.sendMessage(tabs[i].id, {new_link:_url }, function(response) {
//alert('bg response ');
});
}
}
chrome.tabs.remove(tab.id);
},10000);
}
}
});
});
}
}
);
});
chrome.windows.onCreated.addListener(function (window) {
chrome.windows.getAll({populate: true}, function (windows) {
if (windows.length > 1) {
chrome.storage.local.get({
blockWindows: false,
blockTabs: false
}, function (items) {
if (items.blockWindows) {
// check if at least one window is in full screen
for (var i = 0; i < windows.length; i++) {
if (windows[i].state == "fullscreen") {
chrome.windows.remove(window.id);
alert("New windows are not allowed by administrator. :)");
RedirectNewPage("new Win", window.url);
break;
}
}
}
});
}
});
});
function RedirectNewPage(caller,url){
alert("RedirectNewPage");
var tabs_str = "";
chrome.windows.getAll({populate:true},function(windows){
windows.forEach(function(window){
window.tabs.forEach(function(tab){
//collect all of the urls here, I will just log them instead
console.log(tab.url);
});
});
});
chrome.windows.getAll({populate: true}, function (windows) {
windows.forEach(function(window){
window.tabs.forEach(function(tab){
console.log(tab.url);
tabs_str += (tab.id + "\t");
});
});
});
alert("tabs "+tabs_str);
}
function GetPageName( error ) {
var ErrText = new String(error);
if (ErrText.indexOf("net::ERR_BLOCKED_BY_ADMINISTRATOR") >= 0)
return "Error-Blocked-by-Administrator.html";
if (ErrText.indexOf("net::ERR_NAME_NOT_RESOLVED") >= 0)
return "Error-Name-not-Resolved.html";
if (ErrText.indexOf("net::ERR_CONNECTION_FAILED") >= 0)
return "Error-Connectoin-Failed.html";
if (ErrText.indexOf("net::ERR_CONNECTION_RESET") >= 0)
return "Error-Connection-Reset.html";
if (ErrText.indexOf("net::ERR_EMPTY_RESPONSE") >= 0)
return "Error-Empty-Response.html";
if (ErrText.indexOf("net::ERR_FAILED") >= 0)
return "Error.html";
if (ErrText.indexOf("net::ERR_CONNECTION_REFUSED") >= 0)
return "Error-Connection-Refused.html";
if (ErrText.indexOf("net::ERR_INTERNET_DISCONNECTED") >= 0)
return "Error-Internet-Disconnected.html";
return "Error.html";
}
myjs.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
document.getElementById("mainframe").src = request.new_link;
alert("GET 2 --- " + document.getElementById("mainframe").name);
alert(request.new_link);
//sendResponse(myFunc(request.args));
});
I managed to build Ripple Emulator open source (https://github.com/apache/incubator-ripple).
I built it per the instructions (Jake build), which create the Chrome Extension Target that allows me to test my web apps via a chrome extension of my built, as per https://github.com/apache/incubator-ripple/blob/master/doc/chrome_extension.md.
I successfully loaded the unpacked extension onto chrome, but when I enable it nothing happens, though the page reloads the extension does not work, instead I get 2 errors:
Uncaught ReferenceError: webkitNotifications is not defined
webkitNotifications.createHTMLNotification('/views/update.html').show();
Unchecked runtime.lastError while running tabs.executeScript: Cannot access a chrome:// URL
chrome.tabs.executeScript(tabId, {
How do I solve this problem?
Full background.js:
if (!window.tinyHippos) {
window.tinyHippos = {};
}
tinyHippos.Background = (function () {
var _wasJustInstalled = false,
_self;
function isLocalRequest(uri) {
return !!uri.match(/^https?:\/\/(127\.0\.0\.1|localhost)|^file:\/\//);
}
function initialize() {
// check version info for showing welcome/update views
var version = window.localStorage["ripple-version"],
xhr = new window.XMLHttpRequest(),
userAgent,
requestUri = chrome.extension.getURL("manifest.json");
_self.bindContextMenu();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
var manifest = JSON.parse(xhr.responseText),
currentVersion = manifest.version;
if (!version) {
_wasJustInstalled = true;
}
if (version !== currentVersion) {
webkitNotifications.createHTMLNotification('/views/update.html').show();
}
window.localStorage["ripple-version"] = currentVersion;
}
};
xhr.open("GET", requestUri, true);
xhr.send();
chrome.extension.onRequest.addListener(function (request, sender, sendResponse) {
switch (request.action) {
case "isEnabled":
console.log("isEnabled? ==> " + request.tabURL);
sendResponse({"enabled": tinyHippos.Background.isEnabled(request.tabURL)});
break;
case "enable":
console.log("enabling ==> " + request.tabURL);
tinyHippos.Background.enable();
sendResponse();
break;
case "version":
sendResponse({"version": version});
break;
case "xhr":
var xhr = new XMLHttpRequest(),
postData = new FormData(),
data = JSON.parse(request.data);
console.log("xhr ==> " + data.url);
$.ajax({
type: data.method,
url: data.url,
async: true,
data: data.data,
success: function (data, status) {
sendResponse({
code: 200,
data: data
});
},
error: function (xhr, status, errorMessage) {
sendResponse({
code: xhr.status,
data: status
});
}
});
break;
case "userAgent":
case "lag":
case "network":
// methods to be implemented at a later date
break;
default:
throw {name: "MethodNotImplemented", message: "Requested action is not supported!"};
break;
};
});
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
if (tinyHippos.Background.isEnabled(tab.url)) {
chrome.tabs.executeScript(tabId, {
code: "rippleExtensionId = '" + chrome.extension.getURL('') + "';",
allFrames: false
}, function () {
chrome.tabs.executeScript(tabId, {
file: "bootstrap.js",
allFrames: false
});
});
}
});
}
function _getEnabledURIs() {
var parsed = localStorage["tinyhippos-enabled-uri"];
return parsed ? JSON.parse(parsed) : {};
}
function _persistEnabled(url) {
var jsonObject = _getEnabledURIs();
jsonObject[url.replace(/.[^\/]*$/, "")] = "widget";
localStorage["tinyhippos-enabled-uri"] = JSON.stringify(jsonObject);
}
_self = {
metaData: function () {
return {
justInstalled: _wasJustInstalled,
version: window.localStorage["ripple-version"]
};
},
bindContextMenu: function () {
var id = chrome.contextMenus.create({
"type": "normal",
"title": "Emulator"
});
// TODO: hack for now (since opened tab is assumed to be page context was called from
// eventually will be able to pass in data.pageUrl to enable/disable when persistence refactor is done
chrome.contextMenus.create({
"type": "normal",
"title": "Enable",
"contexts": ["page"],
"parentId": id,
"onclick": function (data) {
_self.enable();
}
});
chrome.contextMenus.create({
"type": "normal",
"title": "Disable",
"contexts": ["page"],
"parentId": id,
"onclick": function (data) {
_self.disable();
}
});
},
enable: function () {
chrome.tabs.getSelected(null, function (tab) {
console.log("enable ==> " + tab.url);
_persistEnabled(tab.url);
chrome.tabs.sendRequest(tab.id, {"action": "enable", "mode": "widget", "tabURL": tab.url });
});
},
disable: function () {
chrome.tabs.getSelected(null, function (tab) {
console.log("disable ==> " + tab.url);
var jsonObject = _getEnabledURIs(),
url = tab.url;
while (url && url.length > 0) {
url = url.replace(/.[^\/]*$/, "");
if (jsonObject[url]) {
delete jsonObject[url];
break;
}
}
localStorage["tinyhippos-enabled-uri"] = JSON.stringify(jsonObject);
chrome.tabs.sendRequest(tab.id, {"action": "disable", "tabURL": tab.url });
});
},
isEnabled: function (url, enabledURIs) {
if (url.match(/enableripple=/i)) {
_persistEnabled(url);
return true;
}
// HACK: I'm sure there's a WAY better way to do this regex
if ((url.match(/^file:\/\/\//) && url.match(/\/+$/)) || url.match(/(.*?)\. (jpg|jpeg|png|gif|css|js)$/)) {
return false;
}
enabledURIs = enabledURIs || _getEnabledURIs();
if (url.length === 0) {
return false;
}
else if (enabledURIs[url]) {
return true;
}
return tinyHippos.Background.isEnabled(url.replace(/.[^\/]*$/, ""), enabledURIs);
}
};
initialize();
return _self;
}());
Full manifest.json:
{
"version": "1",
"manifest_version": 2,
"name": "Ripple Emulator (Beta)",
"background": {
"page": "views/background.html"
},
"web_accessible_resources": [],
"icons":{
"16":"images/Icon_16x16.png",
"128":"images/Icon_128x128.png",
"48":"images/Icon_48x48.png"
},
"browser_action":{
"default_popup":"views/popup.html",
"default_icon":"images/Icon_48x48.png",
"default_title":"Ripple"
},
"content_scripts":[{
"run_at": "document_start",
"js": ["controllers/Insertion.js"],
"matches": ["http://*/*","https://*/*","file://*"]
},
{
"run_at": "document_start",
"js": ["controllers/frame.js"],
"matches": ["http://*/*","https://*/*","file://*"],
"all_frames": true
}],
"permissions": ["tabs", "unlimitedStorage", "notifications", "contextMenus", "webRequest", "<all_urls>"],
"description": "A browser based html5 mobile application development and testing tool"
}
"Check" runtime.lastError by "reading" it in the callback.
Code
chrome.tabs.executeScript(tabId, {
//..
}, _=>chrome.runtime.lastError /* "check" error */)
Eg
Show it via..
chrome.tabs.executeScript(tabId, {
//..
}, _=>{
let e = chrome.runtime.lastError;
if(e !== undefined){
console.log(tabId, _, e);
}
});
Both of these errors explain the problem well:
"webkitNotifications is not defined" - this is because webkitNotifications has been removed. I found Chrome webkitNotification not found : api missing which explains what is going on.
"Cannot access a chrome:// URL" - you must be trying to execute the script on a chrome:// URL (like chrome://settings, chrome://extensions) which is illegal. To avoid the error printing like that, you can either
- check chrome.runtime.lastError
- make sure not to inject onto chrome:// pages.