Open cyfung1031 opened 1 year ago
according to this, userscript pages rely on the window.external.xxxx to check whether the script is installed or not.
Window: external property is deprecated. Furthermore, window
object can be accessible to the webpage which then can interfere with the userscript.
If you mean that the userscript can check which manager it is running on, then the proper method would be GM_info.scriptHandler
and GM.info.scriptHandler
.
Manager version is also available under GM.info.version
.
Script name and other details are available under GM.info.script
.
So how can we know other scripts are installed or not?
For example, I can run in the devTools to check whether a script "MyUserscript" is installed or not.
window.external.Violentmonkey.isInstalled('MyUserscript', 'namespace.com').then(result=>{
if(result) console.log(`MyUserscript is installed, version = ${result}`);
else console.log(`MyUserscript is not installed.`);
});
Even I create a UserScript installed in Firemonkey, I can just have GM.info refer to the current script. It is still unable to obtain the installation status of a particular userscript.
So how can we know other scripts are installed or not?
Userscript managers show the active userscripts in their UI. Each userscript manager should have details of its own installation, not another extension's installation.
window
in GM|TM|VM in content
context is not the same window
as userScript
context used by FireMonkey. Even if such a variable is added in FM, it wont be accessible to GM|TM|VM.
content
context window
object is accessible to the webpage.
Webpages should not be allowed to check if user has installed other extensions or userscripts.
Furthermore, a userscript shall not be allowed to check if user has installed other extensions or userscripts.
I am still unsure of what are you trying to achieve.
That means it is never possible to let GreasyFork to detect the script is installed or not? Even through there is a userscript written by someone to do this intentionally ?
That means it is never possible to let GreasyFork to detect the script is installed or not?
ATM, webpages won't be able to check if a userscript is installed in FireMonkey. Why should it be allowed?
Please note the following:
GreasyFork only checks TM|VM & Stylus
export function getTampermonkey() {
return window.external?.Tampermonkey
}
export function getViolentmonkey() {
return window.external?.Violentmonkey
}
export function canInstallUserJS() {
return getTampermonkey() || getViolentmonkey()
}
export async function canInstallUserCSS() {
if (localStorage.getItem('stylusDetected') == '1') {
return true;
}
postMessage({ type: 'style-version-query', name: "whatever", namespace: "whatever", url: location.href }, location.origin);
return new Promise(resolve => setTimeout(function() {
resolve(localStorage.getItem('stylusDetected') == '1')
}, 200));
}
function getInstalledVersion(name, namespace) {
return new Promise(function(resolve, reject) {
let tm = getTampermonkey()
if (tm) {
tm.isInstalled(name, namespace, function (i) {
if (i.installed) {
resolve(i.version);
} else {
resolve(null);
}
});
return;
}
let vm = getViolentmonkey();
if (vm) {
vm.isInstalled(name, namespace).then(resolve);
return;
};
reject()
});
}
window.external
is not supportednamespace
is optionalobject
window.external.Tampermonkey = {
getVersion() { },
openOptions() { },
isInstalled() { }
}
!!window.external?.Tampermonkey
// true
window.external.Tampermonkey.getVersion(console.log)
// Object { version: "4.19.0", id: "fire" }
window.external.Tampermonkey.isInstalled('W.A.R. Links Checker Premium', 'premium version', console.log)
window.external.Tampermonkey.isInstalled('W.A.R. Links Checker Premium', console.log)
// Object { name: "W.A.R. Links Checker Premium", installed: true, enabled: true, version: "1.5.7.8" }
window.external.Tampermonkey.isInstalled('xyz', console.log)
// Object { name: "xyz", installed: false, enabled: undefined, version: undefined }
window.external.Tampermonkey.openOptions()
// open options page, allow web site to navigate user to the TM options page
// background.js
else if("openOptions"==e.method){let r=e.params||"";r&&!r.startsWith("#")&&(r="#"+r),q.tabs.update(t.tab.id,{url:q.extension.getURL("options.html")+r,active:!0},()=>n({}))
window.external.Tampermonkey.openOptions('hash')
// open options page and navigate to the hash anchor, allow web site to navigate user to the TM options page
namespace
is mandatorystring
or null
window.external.Violentmonkey = {
version: '2.15.0',
isInstalled() { }
}
!!window.external?.Violentmonkey
// true
window.external.Violentmonkey.version
// "2.15.0"
window.external.Violentmonkey.isInstalled('W.A.R. Links Checker Premium', 'premium version').then(console.log)
// "1.6.2.2"
window.external.Violentmonkey.isInstalled('W.A.R. Links Checker Premium').then(console.log)
window.external.Violentmonkey.isInstalled('xyz', 'premium').then(console.log)
// null
Wherever the object is available:
🔶 🔷 🔶 🔷 🔶 🔷 🔶 🔷 🔶 🔷 🔶
However, a userscript can announce itself by creating the object. Here is an example:
// ==UserScript==
// @name window.external #582
// @description Please add FireMonkey to window.external #582
// @match https://greasyfork.org/*
// @match https://sleazyfork.org/*
// @version 1.0
// ==/UserScript==
const js = `window.external.FireMonkey = {
version: ${JSON.stringify(GM.info.version)},
isInstalled(name, namespace) {
// only for self, return script version
return name === ${JSON.stringify(GM.info.script.name)} ? ${JSON.stringify(GM.info.script.version)} : null;
}
};`;
GM.addElement('script', {textContent: js});
window.external.FireMonkey.version
// "2.73"
window.external.FireMonkey.isInstalled('window.external #582')
// "1.0"
// ==UserScript==
// @name window.external #582
// @description Please add FireMonkey to window.external #582
// @match https://greasyfork.org/*
// @match https://sleazyfork.org/*
// @version 1.0
// ==/UserScript==
const js = `window.external.FireMonkey = {
version: ${JSON.stringify(GM.info.version)},
async isInstalled(name, namespace) {
// only for self, return script version
return name === ${JSON.stringify(GM.info.script.name)} ? ${JSON.stringify(GM.info.script.version)} : null;
}
};`;
GM.addElement('script', {textContent: js});
window.external.FireMonkey.isInstalled('window.external #582').then(console.log)
// 1.0
Honestly this feature looks dangerous, and it's not that useful rather not relying on it and have a better coding practice.
according to this, userscript pages rely on the
window.external.xxxx
to check whether the script is installed or not.This is already well implemented in Tampermonkey and Violentmonkey, FireMonkey shall follow this practice and ask those pages like "Greasy Fork" to update accordingly.
Without
window.external.xxxx
, no one can detect the script is installed or not in FireMonkey.Suggested Usage
window.external.FireMonkey (or Firemonkey)
version
- indicate the installed version of FireMonkey.isInstalled
- accepts two string parameters (name
andnamespace
) and return aPromise
.