Data deleted on another tab must also have been deleted on the current tab. More explanatory video is available here : https://streamable.com/dmr6yb
Data is still available without refreshing or changing the current tab.
Open the script below in two tabs. Save data in someone and delete it later. You will see that the other one has not been deleted.
// ==UserScript==
// @name Test GM_deleteValue
// @include http*://www.google.com/*
// @version 0.1
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @grant GM_listValues
// ==/UserScript==
let key = "name";
(function() {
document.documentElement.innerHTML = `
<p> Check: <label id="count">0</label> </p>
<p> Key lists: <label id="list_keys">[${GM_listValues().join(", ")}]</label></p>
<p> <label>GM_getValue("${key}")</label>: <label id="value">${GM_getValue(key)}</label> </p>
<p> <label>GM_setValue("${key}") : <input id="new_value" type="text"></input></label> <input type="button" id="set" value="GM_setValue('${key}',...)"> </p>
<p> <input type="button" value="GM_deleteValue('${key}')" id="delete"> </p>
`;
setInterval(function(){
document.getElementById('value').innerHTML = GM_getValue(key);
document.getElementById('count').textContent = parseInt(document.getElementById('count').textContent)+1;
},1000);
document.getElementById('set').addEventListener("click",function(){
GM_setValue(key,document.getElementById('new_value').value);
document.getElementById('new_value').value = "";
document.getElementById('list_keys').innerHTML = "["+GM_listValues().join(", ")+"]";
});
document.getElementById('delete').addEventListener("click",function(){
GM_deleteValue(key);
document.getElementById('list_keys').innerHTML = "["+GM_listValues().join(", ")+"]";
});
})();
I've also met this bug.
I put an extra line of code as the following snippet to the OP's userscript,
// ... headers
// @grant GM_addValueChangeListener
(function() {
// ... skipped
GM_addValueChangeListener(key, console.log);
})();
and found that only the tab where the delete button was clicked logged the value change (with remote equal to false), while the other tab was not aware of the change.
I wrote the code below to solve this problem. I also added expiry time for data to be deleted. video
let GM_data = new class{
constructor(){
this.prefix = "GM_data_";
this.keyList = "GM_dataKeys";
for(let k of this.getKeys()){
let e = this.get(k,{}).e;
if(e!=undefined && this.checkExpiryTime(e)) this.delete(k); //data will be deleted automatically here
}
}
getKeys(){
let self=this;
return GM_getValue(this.keyList,[]).filter(function(key){
let o = GM_getValue(self.prefix+key);
if(typeof o != 'object' || o.constructor != Object || !o.hasOwnProperty('v') //Format control
|| o.hasOwnProperty('d') //This data deleted on a other tab.
) return false;
return true;
});
}
exist(key){ return this.getKeys().includes(key); }
set(key,value,seconds=null){
let o = {v: value};
if(seconds!=null){
if(!Number.isInteger(seconds)) throw new Error("Parameter {seconds} is not a number!");
else if(seconds<1) throw new RangeError("Parameter {seconds} must be positive!")
o.e = new Date().getTime()+seconds*1000; //expiry time
}
GM_setValue(this.prefix+key,o);
if(!this.exist(key)) GM_setValue(this.keyList,this.getKeys().concat(key));
}
get(key,defaultValue=null){
if(!this.exist(key)) return defaultValue;
let o = GM_getValue(this.prefix+key,defaultValue);
if(o.e!=undefined && !this.checkExpiryTime(o.e)){
this.delete(key);
return defaultValue;
}
return o.v;
}
checkExpiryTime(e){ return new Date().getTime() < e;}
delete(key){
let keys = this.getKeys(),
index = keys.indexOf(key);
if(index!=-1){
keys.splice(index,1);
GM_setValue(this.keyList,keys);
}
GM_setValue(this.prefix+key,Object.assign(GM_getValue(this.prefix+key,{}),{d:1}));
GM_deleteValue(this.prefix+key);
}
getExpires(key){
if(!this.exist(key)) return -1;
let o = GM_getValue(this.prefix+key);
if(o.e == undefined) return Infinity;
return o.e;
}
getRemainingTime(key){
let t = this.getExpires(key);
if(t==-1 || t==Infinity) return t;
t -= new Date().getTime();
if(t<0){
this.delete(key);
return -1;
}
return parseFloat(t/1000);
}
logAllData(){
let keys = this.getKeys()
console.log(this.keyList+"(len:"+keys.length+"): "+keys.map(k=>{return JSON.stringify(k);}).join(', '));
if(keys.length==0){
console.log("\t[GM_data] No data!");
return;
}
for(let key of keys){
let o = GM_getValue(this.prefix+key);
if(o == undefined) continue;
if(o.hasOwnProperty('e')) console.log("\t["+key+"] %o , remaining time: "+this.getRemainingTime(key) +" sec ["+new Date(this.getExpires(key)).toLocaleString()+"]",o);
else console.log("\t["+key+"] %o , remaining time: ∞",o);
}
}
}();
I got your point. You mark a value as deleted instead of actually deleting it, and prune the storage on access.
However, it's actually a workaround since the problem is not solved yet, it is still there in the TamperMonkey.
Thank you for the snippet anyway, and let's look forward to v4.11.
Should be fixed at TM BETA 4.11.6113
Thanks for reporting.
Most helpful comment
Should be fixed at TM BETA 4.11.6113
Thanks for reporting.