How to convert a bookmarklet into a Greasemonkey userscript? - javascript

Is there a easy way to do this. And is there anything that needs to be changed due to differences in how it is ran?

The easiest way to do this:
Run the bookmarklet code through a URL decoder. so that javascript:alert%20('Hi%20Boss!')%3B, for example, becomes:
javascript:alert ('Hi Boss!');
Strip the leading javascript: off.   Result: alert ('Hi Boss!');
Add this code to the end of your Greasemonkey file. For example, create a file named,
Hello World.user.js, with this code:
// ==UserScript==
// #name Hello World!
// #description My first GM script from a bookmarklet
// #include https://stackoverflow.com/questions/*
// #grant none
// ==/UserScript==
alert ('Hi Boss!');
Open Hello World.user.js with Firefox (CtrlO ).   Greasemonkey will prompt to install the script.
Now the bookmarklet code will run automatically on whatever pages you specified with the #include and #exclude directives.
Update: To ensure maximum compatibility, use the #grant none directive that was added in later versions of Greasemonkey and Tampermonkey.
IMPORTANT:
The userscript will run much sooner than you could ever activate a bookmark. Normally, this is not a problem.
But in some cases, you might need to wait for some part of the page to fully load.
In that case, you can use techniques/utilities like waitForKeyElements.
See also, Choosing and activating the right controls on an AJAX-driven site .
If you still can't get your new script to work, be sure to read My very simple Greasemonkey script is not running?. Follow the steps and include the specified information in any question you open about problems with the new script.

Here is a very good article to avoid common pitfalls because of differences between "normal" JS and Greasemonkey.
The most important things at the beginning:
Do not use functions as strings, like: window.setTimeout("my_func()", 1000); but rather
window.setTimeout(my_func, 1000); or
window.setTimeout(function(){doSomething(); doSomethingOther();}, 1000);
Do not set element.onclick but rather element.addEventListener("click", my_func, true);
Some code that normally returns various DOM objects, in Greasemonkey environment returns those objects wrapped in XPCNativeWrapper. This is for security reasons.
Some methods and properties are "transparent" and you can invoke them on wrapped object, but some not. Read in the mentioned article about how to circumvent this; you can also use (this is not recommended generally, but for testing etc.) wrappedJSObject property. It is, when obj.something/obj.something() doesn't work in Greasemonkey, try obj.wrappedJSObject.something/obj.wrappedJSObject.something().

Related

How to manipulate JWPLAYER with Tampermonkey?

I've been trying to manipulate a JWPlayer externally, using tampermonkey.
The problem I get is that "JWPlayer" is not Defined.
var player = jwplayer("myVideo");
Simply declaring a player as a jwplayer is not possible.
To fix I've been to "import" the jwplayer.js:
// #require https://vidstreaming.io/js/jw8.9/jwplayer.js
Which didn't work...
My objective: is to to the following through tampermonkey
When I'm at browser, using developer tools I can use
getPosition() to get the current playtime of the video
then seek() to play from there..
jwplayer().getPosition()
jwplayer().seek(X)
Any idea how to "import" jwpalyer.js into Tampermonkey script?
If this works for you from dev tools:
jwplayer().getPosition()
then what you want is to access window.jwplayer global variable. If you are not using any GM specific features, all you need is to make sure your script starts after the player is loaded. Eg. this should be late enough:
// #run-at document-idle
If you're using some #grant privileges, it is still possible, via unsafe window permission:
#grant unsafeWindow
Then, this is what you run:
unsafeWindow.jwplayer().getPosition()
Please pay attention to the warning:
This command can open certain security holes in your user script, and it is recommended to use this command sparingly.
Please be sure to read the entire article and understand it before using it in a script.
So make sure to read the article and understand not to expose any greasemonkey functions to the outside webpages. Never use it with #include *.
// #grant unsafeWindow
// #require https://urlto/jwplayer.js
;(function() {
'use strict';
document.addEventListener("DOMContentLoaded", function(event) {
var ownPlayer = jwplayer();
ownPlayer.on("ready",function(e) {
console.log(this.getPlaylist());
});
});
})();

Can a webpage detect a tampermonkey userscript?

My question is sort of two-fold. First, how the sandbox model works, how it impacts the userscript, what is accessible / seen from the webpage and userscript point of view, and if using a different sandbox model affects the page being able to notice your script being injected into the page (or not). Second, how scripts are injected into the page, and can the page detect it?
First
From what I can see, when you use #grant none, the sandbox is disabled and you will have access to the webpage and its javascript. IF you make any changes to the javascript and/or DOM, it is possibly detectable by the page.
My understanding is, if you use #grant unsafeWindow, your script will be isolated in its own js context, anything you do to window will NOT be seen by the webpage, BUT you can access the webpage and javascript through unsafeWindow. You will have regular access to the DOM, e.g. document returns the regular page document rather than you needing to say unsafeWindow.document. Obviously, any changes you make to the DOM or page js context (e.g. unsafeWindow.foo = 'bar';) will still be detectable. The reason it is unsafe is not because of being detected or not, but because you are able to potentially give the untrusted page access to privileged GM_* functions in this mode, (which are not granted in regular mode, which means that #grant GM_* for any function will isolate the js context, and you'll lose access to the page's js context unless you #grant unsafeWindow)
Second
How are scripts injected into the page? Is it possible that the webpage can notice the userscript injection (assuming the userscript modifies NOTHING on the page).
For example, if a script was injected using a script tag, I think the page could possibly notice the script injection, even get a look at its code?
Does the sandbox model have any role in the way this happens, and make it "safer" to not be seen? For example, if the js contexts are isolated if you use #grant unsafeWindow, then perhaps the js on the webpage can't even see any userscript load event, making #grant unsafeWindow fundamentally safer, UNLESS you go modifying the DOM or unsafeWindow of course.
I'm also assuming that there's no leak of special functions, objects, properties, etc (such as GM_info to the webpage which would betray the existence of tampermonkey?). Neither in #grant none mode or #grant unsafeWindow mode (provided you didn't leak anything to the page)
This lets me feel that unsafeWindow is actually safer in terms of not being detected (because the js contexts are isolated), as long as you don't go modifying anything (and especially DON'T expose privileged GM_* functions to unsafeWindow). For example, if you used an eventListener on #grant none mode, it may possibly be detected, but if you use it in #grant unsafeWindow mode, it may not be detected because of the isolation? Furthermore, IF it was possible for a page to detect the userscript loading (I don't know if this is actually possible or not), it wouldn't know if the js contexts are isolated
In a brief summary, can a page detect either your userscript's or tampermonkey's existence IF you don't betray it?
Are any of my above thoughts above incorrect in any area, and if so, how does it actually work?
Update
A little information for clarification:
A userscript only reads information passively from the page (perhaps using a MutationObserver). It doesn't alter anything in any way, does not use any js libraries (neither from the userscript nor from the webpage) no ajax calls, no script nodes, definitely no clicks, etc. The script MAY read some information from JS vars on the page (let's assume those vars and functions are not booby trapped), as well as using a WebSocket (internal service). Using an IIFE too. So the question mostly is, is tampermonkey in and of itself (and if it runs a page script) detectable?
In this answer: https://stackoverflow.com/a/8548311
I can rule out 1, 4, 5, 6, and 7; probably 2 and 3 as well, but I don't know if tampermonkey in and of itself would affect any of these
Browsers and Greasemonkey/Tampermonkey/Violentmonkey have (mostly) improved how they do injection, scoping, and sand-boxing. Userscripts are not injected using ordinary <script> tags (although your script may need to create such tags in some occasions).
In fact, there's almost no need to use an IIFE nowadays.
But, in addition to the detection methods in the previously linked question:
In #grant none mode, if you #require a library that copies itself to window scope, the page can see it. Most libraries do not do that, but one that does is jQuery.
Tampermonkey actually provides the installed script version to sites that are whitelisted in the advanced settings. This is mainly for script hosts like greasyfork.org.
I don't know if a page can detect WebSockets in use by a userscript. I doubt it.
Bottom line, is for a "read only" userscript, that does not require global libraries in #grant none mode, the page cannot detect it.
(Unless the page is greasyfork.org, etc., and you have the Allow communication with cooperate pages setting at the default value.)
If you discover some leak whereby a page can detect a "passive" script, let us know and chances are it can get plugged.
As mentioned by the answer https://stackoverflow.com/a/8548311 if you do something of the likes it is definitely detectable. But, depending on what you want to do with the tampermonkey script, it will be easier or more difficult to detect, and in some cases impossible.
From what you are asking, it seems like what you want to make is just invoke an IIFE from the page, and just stop there, "let's say it just reads information".
This is really tricky to capture, and usually for this, the page should have to compare profilers and execution times and such of other users against you, or some other funky things, and there is no real easy way to find out if the user executed extra JS in the page (as long as you use an IIFE) that has NO SIDE EFFECT. I am not saying that it is 100% undetectable, but let's say it's really really tricky.
If you are going to modify the DOM, make API calls to an external or internal service, fake movements of the user or other things of this kind, you are going to be detected. So, it depends on what you want to do with the page, but you can be detected "quite easily".
In a brief summary, can a page detect either your userscript's or tampermonkey's existence IF you don't betray it?
Yes a page can detect these in those cases in which you leave a trace in the page (as defined above). Keep in mind that this will happen only there is a reason for the page to want to know if that is happening. Also keep in mind that no page will implement something like this just for the sake of it, so don't expect normal pages to complain about this.
Amazon implemented something that waits 3 or 4 seconds and then retrieves all console information.
This is what is looks like
{
"logs": [{
"level": "error",
"message": "Cannot set property 'checked' of null",
"error": {
"errorMessage": "Cannot set property 'checked' of null",
"errorName": "TypeError",
"errorStackTrace": "TypeError: Cannot set property 'checked' of null\n at storageUpdate (chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?name=myUserscript).user.js&id=ea4d27bb-1f9a-44e5-847c-2f61122b4d75:14467:86)\n at Window.configModal (chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?name=myUserscript).user.js&id=ea4d27bb-1f9a-44e5-847c-2f61122b4d75:14488:29)\n at <anonymous>:3:100\n at E.z.<computed> (eval at exec_fn (:1:157), <anonymous>:43:442)"
},
"context": {
"logTime": 1610058101393
}
}]
}
It's new, and whether or not they are targeting me, they can clearly see that tampermonkey is working:
chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/

Develop Tampermonkey scripts in a real IDE with automatic deployment to OpenUserJs repo

I recently started development on a Tampermonkey script, which is hosted on OpenUserJs. It seems that I'm going to invest a bit more time in the future on this script by keep it up to date and extend his features when time is there. The first lines I wrote on the Tampermonkey editor which is integrated in chrome (edit button of a script).
But I don't like it, the most thing I'm missing is some kind of autocomplete/intellisense. Visual Studio is much better here, so I switched to VS. The problem: After any changes, I have to copy the hole code and paste it in the Tampermonkey editor (Google Chrome). Thats annoying and not very flexible, since I can't really split my code in multiple js files when the script grows.
So is there a way to automate this? My imagination would be: I save the js file in VS (ctrl + s), then the script is loaded in my local development instance of google chrome for testing purpose.
Extension:
I want to publish alpha/beta releases as hosted version on OpenUserJs. So I can test the release easily on different systems. And I also have at least one system, where I do the real update process over the OpenUserJs repo like my end users will do. I think this is important, I already saw some differences according to my manual workflow (c&p in the OpenUserJs editor).
My preferable soultion would be some kind of branches like I know from git. So that I install the script from OpenUserJs like my users do with the production one, but I can choose somewhere to get e.g. the branch development instead of master. OpenUserJs seems to support github as source base, but no kind of branches. I can't imagine, that there is no solution for such issues, which at least every developer with bigger scripts should have...
I found my way to it, so it's not official or anything. And it's easier than it looks, I just wanted to be thorough. We are just instructing the browser and Tampermonkey (TM) to load the script in our file system, which we'll then edit directly.
Coding to instant updates 👨‍💻
Go to Chrome => Extensions and find the TM 'card'. Click details. On the page that opens, allow it access to file URLs:
Save your script file wherever you want in your filesystem. Save the entire thing, including the ==UserScript== header. This works in all desktop OS's, but since I'm using macOS, my path will be: /Users/me/Scripts/SameWindowHref.user.js
Now, go to the TM extension's dashboard, open the script in question in its editor, and delete everything except the entire ==UserScript== header
Add to the header a #require property pointing to the script's absolute path.
At this point, TM's editor should look something like this:
Update: It seems that using the file:// URI scheme at the beginning of your path is now required. On windows systems would be:
// #require file://C:\path\to\userscript.user.js
For macOS and *nix, we'll need three slashes in a row:
// #require file:///path/to/userscript.user.js
Execution Contexts 💻 (advanced)
If you have multiple JavaScript files called with #require (like jQuery or when fragmenting a massive script into smaller pieces for a better experience), don't skip this part.
The #require paths can reference *.user.js or directly *.js files, and any UserScript-style comment headers in these files have no effect.
From the main script's ==UserScript== header, all #require files are text-concatenated in the order specified, with a single newline separating each file. This amalgamation runs as one large script. This means any global function or variable in any file will also be global in all your userscript's files, which isn't ideal.
Errors in one file may influence how subsequent files run. Additionally, to enable strict mode on all of your files, 'use strict'; must be the first statement of the first file listed with #require.
After all #require files run, your main UserScript (the one accessed by TamperMonkey's editor) is run in a separate context. If you want strict mode, you must also enable it here.
Workflow 🕺
Now every time that script matches (#match) the website you are visiting, TamperMonkey will directly load and run the code straight from the file on disk, pointed by the #require field.
I use VSCode (arguably the best multiplatform code editor ever. And free), so that's where I work on the script, but any text editor will do. It should look like this:
Notice how TM's editor and your IDE/Editor have the same header.
Every change in the code is saved automatically in VSCode, so if yours doesn't: remember to save. Then you'll have to reload the website to load the changes.
Bonus tips!
You can easily automate reloading the site on file change using a one-liner from browser-sync's CLI, to mention one tool, and have a great experience.
If you're not using git, you should consider using it with your userscripts, even if you are the sole developer. It will help keep track of your progress, sanely work on different features at the same time, roll back mistakes, and help you automatically release new updates to your users!
And please share all your creations here and here 😄
Working with GitHub or other SCMs
You have to add an #updateURL tag followed by the URL with the raw file from GitHub or whatever provider you chose. GitHub's example:
Note that a #version tag is required to make update checks work. The vast majority of users don't need the #downloadURL tag, so unless your script has a massive follower base, use #updateURL.
TM will check for updates as it's configured from the settings tab:
Externals sets how often the scripts called from your script's #require are checked to update (jQuery, for example).
You can also "force" an update check:
Using external libraries (like jQuery)
It must be present at least in TM's editor to load it. However, I recommend keeping both headers (the TM's and the file on disk's header) the same to avoid confusion. Simply #require it like this to use it:
// #require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js
Documentation
Take a look at TM's documentation page, it's very concise, and with a quick read, you'll have a clear picture of what you can do. Happy hacking! 💪
I want to publish alpha/beta release [...]
You can use the #updateURL userscript tag to point out a web URL [1] and use it along with git to achieve your need.
Here's how I implement it:
On my Gitlab instance https://foo.com/user/project/raw/develop/main.user.js points out the raw userscript file of the develop branch.
Links to develop and other important feature branches are available on the description of the project so that people can choose to follow a development version instead of the master one[2].
and I use this template for sharing:
// ==UserScript==
// #name New Userscript
// #namespace foo.com
// #version 0.3
// #description try to take over the world!
// #author user
// #match https://bar.org/*
// #grant none
// #updateURL https://foo.com/user/project/raw/develop/main.user.js
// ==/UserScript==
(function() {
'use strict';
// Your code here...
})();
Then upon triggering Check for userscript updates button on Greasemonkey or Tempermonkey, they will install the script available at this URL.
[1] Accessible one from where you want to install eg. public Github repo from your personal computer, or your companie's private Gitlab instance from your work computer
[2] note that in order to be installable upon a click on the link, the name of the file must ends with .user.js
Extension to Carles's answer
from time import *
import pathlib
from pyautogui import *
from glob import *
from pyperclip import *
import re
author='SmartManoj'
repo='SmartUserScripts'
namespace=f'https://github.com/{author}'
def local():
return f'''// ==UserScript==
// #name {name}
// #version 0.1
// #description try to take over the world!
// #author {author}
// #namespace {namespace}
// #match {link}
// #updateURL https://raw.githubusercontent.com/{author}/{repo}/master/{fn}
// ==/UserScript==
'''
def browser():
return f'''// ==UserScript==
// #name {name}
// #version 0.1
// #description try to take over the world!
// #author {author}
// #namespace {namespace}
// #match {link}
// #require {local_path}/{fn}
// #grant GM_setClipboard
// ==/UserScript==
'''
hotkey('win','3') # chrome index
hotkey('ctrl','shift','h')
fn=prompt('Enter File name')
name=prompt('Enter Script name',default=fn)
sleep(1)
hotkey('ctrl','a')
hotkey('ctrl','x')
local_path=pathlib.Path(__file__).parents[0].as_uri()
ext='.user.js'
l=len(glob(fn+ext))
if l:fn+=f'_{l+1}'
fn+=ext
a=paste()
link=re.search('#match\s*(.*)',a)[1].strip()
print(local(),file=open(fn,'w'))
copy(browser())
hotkey('ctrl','v')
Latest version
Need to do another script if header changes
Tampermonkey uses something called WebDAV to use an external editor to edit userscripts; TamperDAV.
I haven't tried it yet, but it looks like connecting with Visual Studio should be possible.
Trim21 provides, probably the best large-scale UserScript development solution so far, using webpack to cooperate LiveReloadPlugin realizes modular development and automated testing.
Here is the project.
It can use ES5/ES6 and TypeScript to develop modular scripts on IDE. It's really easy to use!
Integrated LiveReloadPlugin, you can directly refresh any #matchURL.
It is better than the previous scheme, which greatly improves the development efficiency of UserScript!
I've answered this in another question; I think someone should merge them. In the meantime, since it's I haven't seen a lot of info on this, I'll put it here for the next person looking for help.
for Mac users additional to carles answer and the #required URL - it need three slashes! Took me way too long to get it work.
#required file:///Users/me/STUFF/Code/Scripts/SameWindowHref.user.js

Error: Permission denied to access property 'handler'

I have a greasemonkey script for Firefox, which yesterday was working perfectly. I tried using it today (no code was modified) and I noticed that it stopped working. Upon further inspection, the script is now throwing the following error:
Error: Permission denied to access property 'handler'
This error is being thrown in the following block of code:
$('body').click(function() {
// code here
});
This error magically started happening today when the script was working just fine yesterday. I'm not understanding why this error is happening when just trying to do something so basic such as adding an event handler in jQuery.
My script uses jQuery which is already being used in the page the script executes on, so I used this code to make it accessible to GM:
var $ = unsafeWindow.jQuery;
For reference if need be, here are the following Greasemonkey functions I use in my script:
// #grant GM_getResourceText
// #grant GM_addStyle
// #grant GM_xmlhttpRequest
// #grant GM_getResourceURL
I have tried researching this error and I can't find any answer. All of the questions that look like they might be helpful involve iframes and there is not a single iframe to be found in my code or the website it's run on. I've also tried deleting and re-installing the script and that didn't fix the problem.
Greasemonkey 2.0 has just been pushed to all Firefox browsers set to auto-update. (GM 2 was released on June 17, 2014, but it can take a few weeks to get through the review process.)
Greasemonkey 2.0 radically changed unsafeWindow handling:
Backwards incompatible changes:
For stability, reliability, and security the privileged sandbox has been updated to match the new changes to unsafeWindow for the Add-on SDK. In order to write values to unsafeWindow you will need to use the new methods cloneInto(), exportFunction(), and/or createObjectIn().
The #grant none mode is now the default, and grants will no longer be implied when not explicitly provided. See the post Sandbox API Changes in Greasemonkey 2.0 for more detail.
Ordinarily, to spot-access a page function or variable, you could switch to the new methods but, in your case you are using var $ = unsafeWindow.jQuery; -- which was always a bad practice.
jQuery is a special case and cloning it back and forth is going to break things.
#require jQuery instead, EG:
// ==UserScript==
// #name _YOUR_SCRIPT_NAME
// #include http://YOUR_SERVER.COM/YOUR_PATH/*
// #require http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// #grant GM_getResourceText
// #grant GM_addStyle
// #grant GM_xmlhttpRequest
// #grant GM_getResourceURL
// ==/UserScript==
...
You're using unsafeWindow – that, as the name suggested, is not necessary "safe" to use – the problem probably relies there; a change was made in Firefox about objects across compartments:
https://blog.mozilla.org/addons/2014/04/10/changes-to-unsafewindow-for-the-add-on-sdk/
The blog post mention Add-on SDK, but the changes is in the platform, so it will affect Greasemonkey too.
So you basically try to get an object from one compartment (jQuery, from "unsafeWindow") and use in your greasemonkey sandbox. The way you're doing now probably can't work anymore. You can try to use the API mentioned in the article, but I'm afraid that a whole library like jQuery could have some issue to be cloned. In fact, the best way is probably load jQuery also in your Greasemonkey compartment instead of reuse the one from the page's one.
The error probably started "magically" 'cause you have updated your version of Firefox – or it gets the autoupdated.
This page explains how to load jQuery in a Greasemonkey script: http://wiki.greasespot.net/Third-Party_Libraries
The relevant parts are:
// #require http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
...
this.$ = this.jQuery = jQuery.noConflict(true);
According to the docs, jQuery.noConflict() will make sure the version of jQuery for your script won't interfere with the page.
See also: jQuery in Greasemonkey 1.0 conflicts with websites using jQuery

Calling a function before the document loads up in JS

My friend asks me to do this.
He needs to prevent his children from going to certain websites.
He has tamper monkey installed with chrome. I have to make a script in tamper-monkey so that when it reaches the website it will change the web content.
The source code is:
// ==UserScript==
// #name My Fancy New Userscript
// #namespace http://use.i.E.your.homepage/
// #version 0.1
// #description enter something useful
// #match http://www.harmful-website.com/*
// #copyright 2012+, You
// ==/UserScript==
document.write("<b>You are not allowed to visit this site</b>");
This script works only after the web-site is loaded full. But his children stop the loading of website in the middle and they are able to view a part of it.
Even document.onload=function(){document.write("...");} works after load. Are there any way to make the script run before the document is loaded i.e. Immediately after the web address is typed on the address bar or hyperlink is clicked.
Your code will work, you just need to set #run-at document-startDoc, like so:
// ==UserScript==
// #name site blocker
// #match http://www.harmful-website.com/*
// #run-at document-start
// ==/UserScript==
document.write ("<b>You are not allowed to visit this site</b>");
Important:
This will work, for now, on Chrome and with Tampermonkey, but it does not work on other browsers. For example, on Firefox, the document.write call will throw the error:
Error: The operation is insecure.
(But the page will still be completely blank.)
Although a userscript like this will work (mostly); it is a klugey, brittle, low performance approach and is easily defeated. Here are just a few ways that are better, faster, easier, and harder for savvy kids to kibosh:
Use your home router's siteblock and/or parental controls. Almost every router and/or modem has this feature, these days.
Install one of the many extensions that do this kind of blocking.
Block the site with an Adblock rule.
Block the site with the machine's hosts file.
Use a proxy server.
Search on Super User for more options.
Just call the function right after you define it. Like in the header or somehwere.
However, you need to consider if the function has actually everything it requires, like HTML elements on the page if it access any of them, as those won't necessarily be loaded when calling your function.
HTML file is parsed row by row. So if your write your < script > just after < html > tag it will be executed before anything else.

Categories