I need to add prompt for ADD TO HOME SCREEN using manifest.json but it is not showing while my pwa score in audit is 100%
I have dist folder like below:-
My Manifest json consisting below:-
{
"name": "xyz",
"short_name": "xyz",
"icons": [
{
"src": "/xyz/static/img/icons/xy-icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/xyz/static/img/icons/xy-icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"start_url": "xyz/index.html",
"scope": ".",
"display": "standalone",
"background_color": "#0628b9",
"theme_color": "#000000"
}
and i am using workbox plugin for service worker and i tried with normal service worker too like below :-
sw.js
var VERSION = '20';
self.addEventListener('install', function(e) {
e.waitUntil(caches.open(VERSION).then(cache => {
return cache.addAll([
'https://cfjedimaster.github.io/nomanssky/client/index.html'
]);
}))
});
self.addEventListener('fetch', function(e) {
var tryInCachesFirst = caches.open(VERSION).then(cache => {
return cache.match(e.request).then(response => {
if (!response) {
return handleNoCacheMatch(e);
}
// Update cache record in the background
fetchFromNetworkAndCache(e);
// Reply with stale data
return response
});
});
e.respondWith(tryInCachesFirst);
});
self.addEventListener('activate', function(e) {
e.waitUntil(caches.keys().then(keys => {
return Promise.all(keys.map(key => {
if (key !== VERSION)
return caches.delete(key);
}));
}));
});
function fetchFromNetworkAndCache(e) {
// DevTools opening will trigger these o-i-c requests, which this SW can't handle.
// There's probaly more going on here, but I'd rather just ignore this problem. :)
// https://github.com/paulirish/caltrainschedule.io/issues/49
if (e.request.cache === 'only-if-cached' && e.request.mode !== 'same-origin') return;
return fetch(e.request).then(res => {
// foreign requests may be res.type === 'opaque' and missing a url
if (!res.url) return res;
// regardless, we don't want to cache other origin's assets
if (new URL(res.url).origin !== location.origin) return res;
return caches.open(VERSION).then(cache => {
// TODO: figure out if the content is new and therefore the page needs a reload.
cache.put(e.request, res.clone());
return res;
});
}).catch(err => console.error(e.request.url, err));
}
function handleNoCacheMatch(e) {
return fetchFromNetworkAndCache(e);
}
and my pwa score in lighthouse is 100%.
but i am not able to see the prompt add to home screen.
When testing/debugging A2HS, most of the work is clearing out previous tests and installs.
Over and over again.
Browsers intentionally remember what was done last time so the user is not pestered with install prompts.
Totally clearing out the cache may be necessary if you want to see the prompt again.
If you said no to a previous prompt, it will not ask again for XX months.
If you already installed, it should not ask again.
A few things to look for
-- in Desktop Chrome
-- Remove icon from chrome://apps/
-- If that does not work, you may need to clear the cache
Related
I'm new to extension creation and have a problem, which I've already been able to find various ways to solve, but which are all different from mine and/or fixed with manifest V2 instead of V3 which I need.
Also, some fixes found work on their end, but not on mine, so I really don't understand the problem.
Here is my problem:
I want to make a chrome extension to take screenshots of my browser and apps
I found an online tutorial that seemed correct to me (by the way, the only tutorial that uses AND the screenshots AND the V3 manifest, so perfect!)
Following the tutorial, I got the following error: Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.
I looked for various ways, but nothing worked, I ended up downloading the git code of the tutorial, but it does not change anything, the error is still present
From what I understand, the error is in the following line:
chrome.action.onClicked.addListener(function (tab) {
chrome.desktopCapture.chooseDesktopMedia(
["screen", "window", "tab"],
tab,
(streamId) => {
if (streamId && streamId.length) {
setTimeout(() => {
chrome.tabs.sendMessage(
tab.id,
{ name: "stream", streamId },
(response) => console.log("received user data", response) // error is here, response is undefined
);
}, 200);
}
}
);
});
I get undefined instead of the response, and I think it's from there that it's a problem, because it never goes on and therefore never activates the onMessage function, nor the content_script
Here is the full background.js code :
chrome.action.onClicked.addListener(function (tab) {
chrome.desktopCapture.chooseDesktopMedia(
["screen", "window", "tab"],
tab,
(streamId) => {
if (streamId && streamId.length) {
setTimeout(() => {
chrome.tabs.sendMessage(
tab.id,
{ name: "stream", streamId },
(response) => console.log("received user data", response)
);
}, 200);
}
}
);
});
chrome.runtime.onMessage.addListener((message, sender, senderResponse) => {
if (message.name === "download" && message.url) {
chrome.downloads.download(
{
filename: "screenshot.png",
url: message.url,
},
(downloadId) => {
senderResponse({ success: true });
}
);
return true;
}
});
Content_script
chrome.runtime.onMessage.addListener((message, sender, senderResponse) => {
if (message.name === 'stream' && message.streamId) {
let track, canvas
navigator.mediaDevices.getUserMedia({
video: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: message.streamId
},
}
}).then((stream) => {
track = stream.getVideoTracks()[0]
const imageCapture = new ImageCapture(track)
return imageCapture.grabFrame()
}).then((bitmap) => {
track.stop()
canvas = document.createElement('canvas');
canvas.width = bitmap.width; //if not set, the width will default to 200px
canvas.height = bitmap.height;//if not set, the height will default to 200px
let context = canvas.getContext('2d');
context.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height)
return canvas.toDataURL();
}).then((url) => {
chrome.runtime.sendMessage({name: 'download', url}, (response) => {
if (response.success) {
alert("Screenshot saved");
} else {
alert("Could not save screenshot")
}
canvas.remove()
senderResponse({success: true})
})
}).catch((err) => {
alert("Could not take screenshot")
senderResponse({success: false, message: err})
})
return true;
}
})
manifest v3
{
"name": "Screenshots",
"version": "0.0.1",
"description": "Take screenshots",
"manifest_version": 3,
"background": {
"service_worker": "background.js"
},
"permissions": ["desktopCapture", "downloads", "tabs", "nativeMessaging"],
"action": {
"default_title": "Take a Screenshot"
},
"icons": {
"16": "/assets/icon-16.png",
"32": "/assets/icon-32.png",
"48": "/assets/icon-48.png",
"128": "/assets/icon-128.png"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content_script.js"]
}
]
}
I tried several things after various research like
Disable my extensions (which makes no sense, but you never know)
Add a timeout for the response, I tried up to 20 seconds delay, but without success
Added breakpoints everywhere to see if it crosses the line or not
Here is an implementation without service worker and content scripts.
manifest.json
{
"name": "desktopCapture",
"version": "1.0",
"manifest_version": 3,
"permissions": [
"desktopCapture",
"tabs",
"downloads"
],
"action": {
"default_popup": "popup.html"
}
}
popup.html
<html>
<body>
<script src="popup.js"></script>
</body>
</html>
popup.js
const createDate = {
url: "desktopCaptuer.html",
type: "popup",
width: 800,
height: 600
};
chrome.windows.create(createDate);
desktopCaptuer.html
<html>
<body>
<input type="button" id="captuer" value="Captuer">
<script src="desktopCaptuer.js"></script>
</body>
</html>
desktopCaptuer.js
chrome.windows.getCurrent({}, w => {
chrome.windows.update(w.id, { focused: true }, () => {
document.getElementById("captuer").onclick = () => {
const sources = ["screen", "window", "tab"];
chrome.tabs.getCurrent((tab) => {
chrome.desktopCapture.chooseDesktopMedia(sources, tab, (streamId) => {
let track, canvas;
navigator.mediaDevices.getUserMedia({
video: {
mandatory: {
chromeMediaSource: "desktop",
chromeMediaSourceId: streamId
},
}
}).then((stream) => {
track = stream.getVideoTracks()[0];
const imageCapture = new ImageCapture(track);
return imageCapture.grabFrame();
}).then((bitmap) => {
track.stop();
canvas = document.createElement("canvas");
canvas.width = bitmap.width;
canvas.height = bitmap.height;
let context = canvas.getContext("2d");
context.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height);
return canvas.toDataURL();
}).then((url) => {
chrome.downloads.download({
filename: "screenshot.png",
url: url,
}, () => {
canvas.remove();
});
}).catch((err) => {
console.log(err);
alert("Could not take screenshot");
})
});
});
}
});
});
Works for me, using Chromium 107.0.5304.121 (Official. Build) Arch Linux (64-Bit).
Go to https://stackoverflow.com/
Click on the extension icon.
A new window opens, with the text "Select what you want to share. Screenshots wants to share the contents of your screen with stackoverflow.com"
Click on one of the tabs: Entire Screen, Window, Chromium Tab
Click on a screenshot preview or tab title
Click "Share"
The browser displays an alert with the text "Screenshot saved", and a file named "Screenshot.png" is created in the default downloads directory.
So, #Norio Yamamoto 's solution suits me perfectly, because I then need to make a popup to give a name and do other processing on my screen, so thanks to your help, I'm already moving on by starting to understand it HTML popups on extensions! Thanks !
For the problem itself, I was able to "fix" it in the end by reinstalling chrome, and it works as #Thomas Muller tells me... not sure why, maybe I had to break something with many tests, so the app was already working
But I noticed a problem on the version of the tutorial compared to the one with popup, the tutorial version does not work on: non-reload pages (thanks #wOxxOm for the tip by the way), nor on chrome home pages, nor on the extension page, so I really prefer the popup version, but I need to dig more to improve that
Thanks again !
All of the information I can find on this is pretty old. Like the title says I am trying to make a global variable in one script and access it from another. The purpose of the extension is to search for a class named "page-title" and then return the innerHTML of that HTML element. Once I get the code working I will specify the URL I want the extension to run on so it's not constantly running.
After a couple iterations trying to accomplish this in different ways I followed the method explained in this answer but my needs have different requirements and I am receiving the error "Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist." tied to the popup.html.
I tried the Unchecked runtime error solution found here but it's been awhile (~ 7 years) since I've dived into any coding and I'm not sure I implemented it correctly.
I've also tried to pass the value between JS documents is the HTML injection method, but without overriding security defaults in the manifest that doesn't really work. It also seemed super bootstrappy and I wanted to pass the information in a more conventional way. I tried creating a global variable by simply declaring the variable outside of a function/class/if statement and loading that .js file first, but that was unsuccessful as well.
Manifest
"name": "P.P. to Sharepoint",
"version": "1.0.0",
"description": "Open P.P. client folder in sharepoint",
"manifest_version": 3,
"author": "Zach Morris",
"action":{
"default_popup": "popup.html",
"default_title": "Open Sharepoint Folder"
},
"background": {
"service_worker": "background.js"
},
"permissions": [
"activeTab",
"tabs",
"scripting",
"notifications"
],
"content_scripts": [{
"js": ["contentScript.js"],
"matches": ["<all_urls>"]
}]
}
popup.html
My popup.html is super simple and really just has a button to press. I included all the .js files in the order I thought necessary
<script src="globalVariable.js"></script>
<script src="contentScript.js"></script>
<script src="popup.js"></script>
<script src="script.js"></script>
<script src="background.js"></script>
globalVariable.js
This one is straight forward. I need to pull the client's name out of the HTML of the page then use it in an API call when I click the button in popup.js This initializes the variable and uses it as place holder.
var clientInfo = {
name: 'test name'
};
ContentScript.js
I only want to run this if importScripts is not undefined. So I threw it in the if statement. Then I make sure I pulled a client name from the page. If not I throw an error message saying no client was found.
if( 'function' === typeof importScripts) {
importScripts('globalVariable.js');
addEventListener('message', onMessage);
function onMessage(e) {
if(b[0]) {
clientInfo.name = b[0].innerHTML;
alert(clientInfo.name + ' was assigned!');
} else {
alert('There is no client on this screen ' + 'b[0] is ' + b[0] + " clientInfo = " + clientInfo.name);
};
};
} else {
console.log("Your stupid code didn't work. ");
}
popup.js
This one pulls up the globalVariable.js to use the clientInfo. and makes a call to the button in background.js
if( 'function' === typeof importScripts) {
importScripts('globalVariable.js');
addEventListener('message', onMessage);
function onMessage(e) {
const text = clientInfo.name;
const notify = document.getElementById( 'myButton' );
notify.addEventListener( 'click', () => {
chrome.runtime.sendMessage( '', {
type: 'notification',
message: text });
} );
}
}
background.js
Same thing here. I import the globalVariable script to use the global variable. The notification will eventually be replaced with the API call when the rest of the code is working properly. I probably don't need to import the script here to access the variable because I can mass it with the event listener in popup.js, but I put it in here out of desperation.
if( 'function' === typeof importScripts) {
importScripts('globalVariable.js');
addEventListener('message', onMessage);
function onMessage(e) {
// do some work here
chrome.runtime.onMessage.addListener( data => {
if ( data.type === 'notification' ) {
chrome.notifications.create(
'',
{
type: 'basic',
title: 'Notify!',
message: data.message || 'Notify!',
iconUrl: 'notify.png',
}
);
console.log("sent notification");
};
});
}
}
You can have the popup.js listen for a button click and content.js handle all the logic of finding the correct element.
popup.js
document.querySelector('#btn').addEventListener('click', () => {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) =>
chrome.tabs.sendMessage(tabs[0].id, { command: 'getClientName' })
);
});
content.js
chrome.runtime.onMessage.addListener((msg, sender, response) => {
if (msg.command === 'getClientName')
findClientName(document.querySelectorAll('h3.page-title'));
});
Example of findClientName function:
const findClientName = async (element) => {
let clientName;
if (element.length > 0) {
element.length === 1
? (clientName = setClientName(element[0]))
: handleMultipleElements(element);
} else {
handleNoClientNameFound();
}
clientName ? await makeAPIRequest(clientName) : null;
};
Try this method instead maybe?
{
var x = 2;
}
so:
{
var clientInfo = {
name: 'test name'
};
}
Not very good at this language, so I thought maybe you're missing the brackets?
So I was tinkering with a firefox extension and came across something I can't explain. This extension downloads images from a certain site when a browser action (button) is clicked. Can confirm that the rest of the extension works perfectly and the code below has proper access to the response object.
const downloading = browser.downloads.download({
filename:response.fileName + '.jpg',
url:response.src,
headers:[{name:"Content-Type", value:"image/jpeg"}],
saveAs:true,
conflictAction:'uniquify'
});
const onStart = (id) => {console.log('started: '+id)};
const onError = (error) => {console.log(error)};
downloading.then(onStart, onError);
So the saveAs dialog pops up (filename with file extension populated), I click save, and then it downloads. As soon as the file finishes downloading it disappears from the folder it was saved in. I have no idea how this is happening.
Is this something wrong with my code, Firefox, or maybe a OS security action? Any help would be greatly appreciated.
Extra Information:
Firefox - 95.0.2 (64-bit)
macOS - 11.4 (20F71)
I had the same issue. You have to put download in background, background.js.
Attached sample of Thunderbird addon creates new menu entry in the message list and save raw message to the file on click.
If you look to the manifest.json, "background.js" script is defined in the "background" section. The background.js script is automatically loaded when the add-on is enabled during Thunderbird start or after the add-on has been manually enabled or installed.
See: onClicked event from the browserAction (John Bieling)
manifest.json:
{
"description": "buttons",
"manifest_version": 2,
"name": "button",
"version": "1.0",
"background": {
"scripts": ["background.js"]
},
"permissions": [
"menus","messagesRead","downloads"
],
"browser_action": {
"default_icon": {
"16": "icons/page-16.png",
"32": "icons/page-32.png"
}
}
}
background.js:
async function main() {
// create a new context menu entry in the message list
// the function defined in onclick will get passed a OnClickData obj
// https://thunderbird-webextensions.readthedocs.io/en/latest/menus.html#menus-onclickdata
await messenger.menus.create({
contexts: ["all"],
id: "edit_email_subject_entry",
onclick: (onClickData) => {
saveMsg(onClickData.selectedMessages?.messages);
},
title: "iktatEml"
});
messenger.browserAction.onClicked.addListener(async (tab) => {
let msgs = await messenger.messageDisplay.getDisplayedMessages(tab.id);
saveMsg(msgs);
})
}
async function saveMsg(MessageHeaders) {
if (MessageHeaders && MessageHeaders.length > 0) {
// get MessageHeader of first selected messages
// https://thunderbird-webextensions.readthedocs.io/en/latest/messages.html#messageheader
let MessageHeader = MessageHeaders[0];
let raw = await messenger.messages.getRaw(MessageHeader.id);
let blob = new Blob([raw], { type: "text;charset=utf-8" })
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/downloads
await browser.downloads.download({
'url': URL.createObjectURL(blob),
'filename': "xiktatx.eml",
'conflictAction': "overwrite",
'saveAs': false
});
} else {
console.log("No message selected");
}
}
main();
There have been quite a few similar questions on setInterval in background.js in a Chrome extension but none of the answers worked for me. I have a simple extension that checks connectivity to a server by calling an API and checking whether there is a 200 response and then updating the extension icon in the tray accordingly.
background.js
chrome.runtime.onInstalled.addListener(() => {
chrome.browserAction.setIcon({path: "/images/checking.png"});
console.log('VPN check extension started');
// main block - make API calls periodically and monitor the server response
async function ping() {
try {
const response = await axios.get('http://[IP]/api/v1/ping', {
timeout: 4000
});
access = response.status;
if (access == 200) {
chrome.browserAction.setIcon({path: "/images/OK_53017.png"});
} else {
chrome.browserAction.setIcon({path: "/images/not_OK.png"});
}
} catch (error) {
chrome.browserAction.setIcon({path: "/images/not_OK.png"});
}
}
window.setInterval(ping, 1000 * 10);
});
chrome.runtime.onStartup.addListener(() => {
chrome.browserAction.setIcon({path: "/images/checking.png"});
console.log('VPN check extension started');
// main block - make API calls periodically and monitor the server response
async function ping() {
try {
const response = await axios.get('http://[IP]/api/v1/ping', {
timeout: 4000
});
access = response.status;
if (access == 200) {
chrome.browserAction.setIcon({path: "/images/OK_53017.png"});
} else {
chrome.browserAction.setIcon({path: "/images/not_OK.png"});
}
} catch (error) {
chrome.browserAction.setIcon({path: "/images/not_OK.png"});
console.log('error');
}
}
window.setInterval(ping, 1000 * 10);
});
Neither onStartup nor onInstalled works well - when I restart Chrome or switch windows the extension becomes unresponsive.
Manifest
{
"name": "Access status",
"version": "0.0.3",
"description": "Chrome extension to check if access to the network is provided.",
"background": {
"scripts": ["axios.min.js", "background.js"],
"persistent": false
},
"browser_action": {
"default_popup": "popup.html",
"default_icon": {
"16": "images/checking.png"
},
"icons": {
"16": "images/checking.png"
}
},
"permissions": ["<all_urls>"],
"manifest_version": 2,
}
content.js is empty
Any suggestion how to make it work, regardless of which tab is in focus or which window is open and get it to set the interval when fresh Chrome opens? Thanks
A quick google brought up this post. Basically you should probably try using the alarms API instead since chrome will just kill your background script in order to optimize performance.
Your code would probably work at least until someone restarts the browser if you had the background as persistent: true. However that option seems to be deprecated now.
I'm trying to access chrome.storage.sync where I store some user options in my background.js but the asynchronous nature of chrome.storage.sync.get is causing me issues.
If I try and use chrome.storage.sync.get within my chrome.webRequest.onBeforeRequest.addListener the callback isn't quick enough for the function to use it.
I have tried adding the user options as a global variable within background.js but it appears to me that that value doesn't persist.
Anyone else using user options in background.js?
function getoption(){
chrome.storage.sync.get({
radarpref: 'nothing',
}, function(items) {
console.log(items.radarpref);
return items.radarpref;
});
}
var hold = getoption();
console.log (hold) //this returns hold value
chrome.webRequest.onBeforeRequest.addListener(
function(info) {
//this doesn't work - yet
console.log('i dont see the hold');
console.log(hold) //hold not returned when callback ran
...
If you need to synchronously use settings from any async storage - the best way to do it is to cache it.
You need to load the settings to the cache on background.js start and then you need to update cache each time chrome.storage.onChanged event triggered.
Example how to do it:
manifest.js
{
"manifest_version": 2,
"name": "Settings Online demo",
"description": "Settings Online demo",
"applications": {
"gecko": {
"id": "852a5a44289192c3cd3d71e06fdcdb43b1437971#j2me.ws"
}
},
"version": "0.0.1",
"background": {
"scripts": ["background.js"]
},
"permissions": [
"storage",
"webRequest",
"webRequestBlocking",
"<all_urls>"
],
"options_ui": {
"page":"properties.html",
"chrome_style": true
}
}
Note that you need to have non-temporary application id if you need to work it on firefox, <all_urls> permission is needed to get access to any url request processing.
background.js
((storage) => {
let settings = (function(properties) {
// Save settings
this.set = (properties,ok) => {
for(key in properties || {}){
this[key]=properties[key];
}
storage.set(
properties
,() => {
ok(settings);
});
};
//Default values processing
for(key in properties || {}){
this[key]=properties[key];
}
// Initial settings read
storage.get(properties,(properties) => {
for(key in properties){
this[key]=properties[key];
}
});
// Listen settings change and cache it
chrome.storage.onChanged.addListener((msg) => {
for(key in msg){
this[key]=msg[key].newValue;
}
});
return this;
}).call({},{"property":"default","name":"me"})
chrome.webRequest.onBeforeRequest.addListener(
function(info) {
// Update and persist settings
settings.set({"lastRequest":info},()=>{console.log("Settings saved")});
console.log('Catch', settings.name,settings.property);
},{urls:["https://*/*"]});
})(chrome.storage.sync || chrome.storage.local);
Note that I use chrome.storage.sync || chrome.storage.local because some browsers (Opera, mobile browsers) do not support sync-storage, but support local storage.
And the property page to see how can property changes are processing:
properties.html
<html>
<head>
<script src="properties.js" type="text/javascript"></script>
</head>
<body>
<label>Property:<input id="property" type="text"></label>
<input id="save-properties" value="save" type="submit">
</body>
</html>
properties.js
((storage) => {
let saveOptions = () => {
let property = document.getElementById("property").value;
storage.set({
"property": property
},() => {
window.close();
});
}
let restoreOptions = () => {
storage.get({
"property": "default"
}, (properties) => {
document.getElementById("property").value = properties.property;
});
document.getElementById("save-properties").addEventListener("click", saveOptions);
}
document.addEventListener("DOMContentLoaded", restoreOptions);
})(chrome.storage.sync || chrome.storage.local);
That's all :)
P.S> This solution has a weak point: if your app is settings-sensitive and can't work with default settings, or you need to be sure that you're using custom settings on start - you need to delay background.js start while settings are not loaded. You may to it with callback or with promise:
background.js - wait while settings will be loaded with callback
((storage) => {
let settings = (function(properties) {
// Update settings
this.set = (properties,ok) => {
for(key in properties || {}){
this[key]=properties[key];
}
storage.set(
properties
,() => {
ok(settings);
});
};
//Default values processing
for(key in properties || {}){
this[key]=properties[key];
}
// Listen settings change and cache it
chrome.storage.onChanged.addListener((msg) => {
for(key in msg){
this[key]=msg[key].newValue;
}
});
// Initial settings read
storage.get(properties,(properties) => {
for(key in properties){
this[key]=properties[key];
}
mainLoop();
});
return this;
}).call({},{"property":"default","name":"me"})
let mainLoop = () => {
//.. all you settings-sensitive code
chrome.webRequest.onBeforeRequest.addListener(
function(info) {
// Update settings and persist it
settings.set({"lastRequest":info},()=>{console.log("Settings saved")});
console.log('Catch', settings.name,settings.property);
},{urls:["https://*/*"]});
};
})(chrome.storage.sync || chrome.storage.local);
background.js - wait while settings will be loaded with promise
((storage) => {
let settings = ((properties) => {
this.set = (properties) => {
for(key in properties || {}){
this[key]=properties[key];
}
return new Promise((ok,err) => {
storage.set(
properties
,() => {
ok(settings);
});
});
};
return new Promise((ok,err) => {
//Default values processing
for(key in properties || {}){
this[key]=properties[key];
}
// Listen settings change and cache it
chrome.storage.onChanged.addListener((msg) => {
for(key in msg){
this[key]=msg[key].newValue;
}
});
// Initial settings read
storage.get(properties,(properties) => {
for(key in properties){
this[key]=properties[key];
}
ok(this);
});
});
}).call({},{"property":"default","name":"me"}).then((settings) => {
//.. all you settings-sensitive code
chrome.webRequest.onBeforeRequest.addListener(
function(info) {
// Update settings and persist it
settings.set({"lastRequest":info}).then(()=>{console.log("Settings saved")});
console.log('Catch', settings.name,settings.property);
},{urls:["https://*/*"]});
}).catch(()=>{});
})(chrome.storage.sync || chrome.storage.local);
Read more
Storage specs/firefox:
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage
Storage spect/chrome: https://developer.chrome.com/apps/storage
Permission requests/firefox:
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Request_the_right_permissions