This allows script hosters to detect Tampermonkey and to query by name whether a script is installed.
A simple example how to get the Tampermonkey version and check whether Download YouTube Videos as MP4 is installed could look like this:
var waitForExternalTm = function(cb) {
var external, tm;
if (!(external = window.external)) {
return false;
} else {
if ((tm = external.Tampermonkey)) {
cb(tm);
} else {
window.setTimeout(function() {
if ((tm = external.Tampermonkey)) {
cb(tm);
}
}, 1000);
}
}
};
waitForExternalTm(function(tm) {
tm.getVersion(function(i) {
console.log('Hooray, found Tampermonkey version ' + i.version);
});
// https://greasyfork.org/en/scripts/1317-download-youtube-videos-as-mp4
tm.isInstalled('Download YouTube Videos as MP4', 'http://googlesystem.blogspot.com', function(i) {
if (i.installed) {
console.log('Yes, ' + i.name + ' version ' + i.version + ' is installed and ' + (i.enabled ? 'enabled' : 'disabled') + '.');
} else {
console.log('No, ' + i.name + ' is not installed');
}
});
});
And at the moment these functions are whitelisted to the following URLs:
{
'*://tampermonkey.net/*': [ 'getVersion' ],
'https://greasyfork.org/*': [ '*' ],
'https://openuserjs.org/*': [ '*' ],
'https://userstyles.org/*': [ '*' ]
}
Edit: updated example
Why would you let servers to detect Tampermonkey?
I think it would be better to conduct any check on client-side.
Only the pages mentioned above are able to detect Tampermonkey. This feature enables them to know whether Tampermonkey is installed. If so then there is no need to show install instructions.
It would also be possible to detect whether a script is already installed and to check if the version is the current one.
Examples of such a behavior are:
If a user doesn't like it then this feature can be disabled and it's also disabled by default in incognito mode.
I think it would be better to conduct any check on client-side.
This would require me to monitor all the pages mentioned above and release a new Tampermonkey version or to update a pre-installed userscript once a change was detected. Also the page might look different dependent on the viewer's country, A/B tests, ...
I now understand the problem of conducting this check on client-side.
I think this option should be disabled by default in any mode, but if it won't be disabled by default, inform users about this new option upon update.
Maybe TM should show a confirmation page the first time a whitelisted site wants to get the info. With buttons to allow/disallow permanently/temporarily.
What is Tampermonkey's behaviour if a user installs a script, the script gets renamed, then the user attempts to install again? Is the script updated or is another copy installed?
Are there other attributes that could be searched for? I'm concerned about the rename case as well as the case where a user has multiple scripts with the same name.
How does this work with localized script names?
What is your example script waiting to happen before it can do its thing?
@JasonBarnabe
What is Tampermonkey's behaviour if a user installs a script, the script gets renamed, then the user attempts to install again? Is the script updated or is another copy installed?
If the user manually renamed a script or the author renamed it and the user installs it again before an automatic update happened, then a new copy is installed. If the script was renamed at the server, then this API allows you to query both names and display a warning. Do you know how often scripts get renamed?
Are there other attributes that could be searched for?
Yes, I thought about using the @updateURL or install URL for this. However, this also has some disadvantages:
I'm concerned about the rename case as well as the case where a user has multiple scripts with the same name.
Tampermonkey internally handles scripts using a UUID. However, on installation (from URLs) existing scripts with the same @name and @namespace are replaced. Therefore I don't think this is a big issue. I'll add a way to query by @name and @namespace.
How does this work with localized script names?
You always need to query for non-localized names (== the value of @name)
What is your example script waiting to happen before it can do its thing?
Since this feature is user-configurable the content script needs to contact the background page. Unfortunately this is asynchronous communication and therefore it can not be guaranteed to be finished earlier.
Edit: added @namespace
@JasonBarnabe @Martii I've added the possibility to query by name and namespace in order to identify the script more reliably and updated the example above.
So is this interesting for you in general?
@derjanb
This can be used for example to not show installation hints or to modify download/install buttons to say something like "already installed".
I seriously doubt that OUJS is going to restrict clicking it more than once other than the brute force prevention. Sometimes there are unexpected failures in the pipeline and waiting a few is the only thing to do. One of my pet peeves in Chromium/Chrome is how google prevents you from reinstalling something when it's profile gets somewhat corrupted or failure to update. I still have to manually update TM in those browsers all the time... which means uninstall the current version then reinstall it from the store... you can see where I'm going with this.
in order to identify the script more reliably...
That is one of my concerns... if the UID _(or whatever acronym is chosen here for script signature)_ is constant OUJS won't know when to signal "already installed"... perhaps if a user has hand edited a script, what then... if you catch my drift.
It isn't revolting or appealing right now as far as a I can tell but I may be missing some extra UI logic that you are aiming for. Proper identification is a plus for communication of this sort but as you already know another extension/add-on can wreak some havoc with that. As I noted over at the OUJS issue it won't be in our _node_ stack but the "Content Scope" should be okay as long as another Userscript isn't messing with the results or identifiers/naming conventions _(think the alert bypass in the global scope)_. Wouldn't object to a soft-notice I think but beyond that we'd have to be convinced to do something otherwise.
Since this feature is user-configurable the content script needs to contact the background page. Unfortunately this is asynchronous communication and therefore it can not be guaranteed to be finished earlier.
Just to be clear, I was referencing the part where you're waiting for external.Tampermonkey to become available. I'm not sure if you're answering based on that. Can you not assume that external.Tampermonkey, if it is going to be available, will be available by DOMContentLoaded or load or something? I just don't want to be forever checking for it when it's not going to show up (e.g. Tampermonkey is not loaded). Though I suppose I could modify the script to just give up after a while...
Is there a way to add to the whitelist (by console if necessary)? I'd like to test this out locally before deploying anything to greasyfork.org.
@JasonBarnabe
Can you not assume that external.Tampermonkey, if it is going to be available, will be available by DOMContentLoaded or load or something?
Unfortunately not. All methods to communicate with the background page (which contains the config) are asynchronous. However, I think after one second you can definitely give up waiting for Tampermonkey.
Is there a way to add to the whitelist (by console if necessary)? I'd like to test this out locally before deploying anything to greasyfork.org.
Unfortunately not, but I've build a version with localhost support. You have to drag and drop it to Chrome's extension page to install it.
Edit: updated test crx URL
@Martii
Wouldn't object to a soft-notice I think but beyond that we'd have to be convinced to do something otherwise.
A soft-notice is fine with me. I just wanted to give the script hosters a little bit more information about the user that is viewing the page.
I implemented this on greasyfork.org. Code is at https://github.com/JasonBarnabe/greasyfork/blob/master/app/assets/javascripts/versioncheck.js
I really like this, but I don't use congregated userscript sites. Do you think you could enable "isInstalled" for "https://*.github.io/*"?
It would be pretty nice if I could detect script versions on my landing pages for userscript.
I have to agree with @feildmaster, but I would really prefer if the whitelist goes bye-bye. I want to prevent users from tampering with the anti-cheat I have installed, and the only way to check is if Tampermonkey tells me.
However, if this feature doesn't have a whitelist, any site can prevent user from using userscript.
And it is totally unacceptable.
Maybe a better solution is enable user to control a whitelist to expose tampermonkey api.
@Kio-td I don't think it is a valid reason for doing this, because there is many ways to do so.
A proper way is check them in your server instead of checking them in client.
Can it be used to prevent userscripts? I thought it was simply a getter for data. I mean ,I guess you could code something to say "We don't like you using userscripts" but really?
For example, a website can simply document.write('') if any userscript is detected.
It will force user to disable the script.
I see. Personally that'd put me off of using a website that is so adamant against user scripts. And for those cases I suspect it would be more convenient to have a blacklist for the API, rather than a whitelist.
I still would like github.io to be whitelisted though.
I get your points, but still, I have very vulnerable parts of the code that I'd rather not leave exposed in the open, even if I take preventative measures on my server. The only thing I can see TM being used on my server is to disable Anticheat, or to change servers, and I'm not about to take that 50/50 risk, which is why I'd rather the whitelist be gone. After all, the point of the game is to be fast, and I'm not going to have anything on there that'll slow down the game, or anything else about it, like particles.
To go against your point, of course, I get that people like userscripts. But how do you check to make sure someone is not cheating, or using Automovers, or something else without this sort of feature? God, anyone can see how some games have become much shittier because of cheaters.
Let's take this example to the airport, yeah? In essence, you're asking me to watch for anyone trying to run past security, instead of using metal detectors. I get it, you want to keep your glasses, iPhones and knives on your body, but it just becomes a really really dangerous and -guessish- process that can end up in disappointed clients, and worst case - lost money because of it.
For these reasons, I see this as valid, mainly because I want as many preemptive measures to keep people from breaking the TOS.
From my perspective, the only use of client side verification is better UX, so you should never trust the data sent by users by all means.
Besides, Tampermonkey is neither the only way to inject code in the website nor is the only way to cheat.
Violentmonkey, Greasemonkey and so on can do this job as well, and you can achieve it without userscript managers by devtool, proxy...
I thought this debate is somewhat like whether a website should be able to know if adblocker enabled or not.
Lets see, to address Kio with his anti-cheat now that I understand what he's doing... Clients are never to be trusted, even if you add a hell of a lot of anti-cheat methods on the client side. The best place for anti-cheat is on the server, to validate the data being sent.
Now for wanting to block userscripts simply because you don't want cheaters... I'm not sure I understand really. I make scripts for games. It's usually to add in features the game developers refuse to incorporate. Do some people think it's cheating? I'm sure they do. However I never consider any of the scripts I make to be cheating.
And if I understand the API any, you can't just get all of the scripts being run. You have to actually query them. You can't just say... "Tampermonkey is enabled, disable it to play the game." That just isn't... fair.
Do you think you could enable "isInstalled" for "https://.github.io/"?
Unfortunately this is not possible.
It's not? That's a shame. How come?