of metadata block Installing an existing one Cross-browser issues: Firefox, Chrome, Opera Update mechanism Dependency management with @require / @resource Debugging a userscript in Firefox Special functions: GM_xmlhttprequest, GM_{set|get}Value @grant, unsafeWindow and security implications Use cases
Userscript Piece of JS code to be run automatically by the browser in certain circumstances Greasemonkey userscript An userscript with some additional capabilities (GM_* functions) that are unavailable to ordinary JS code running on a standard website GM_setValue / GM_getValue for storing preferences GM_xmlhttprequest for unrestricted XHR I’ll be talking mostly about Greasemonkey userscripts
browser, and perhaps an extension Firefox Greasemonkey (recommended) or Scriptish Chrome Runs userscripts natively... But you can install Tampermonkey for the ease of use and some additional capabilities Opera Runs basic (plain JS) userscripts natively Doesn’t offer GM_* APIs Doesn’t run on HTTPS by default – needs tweaking Installation of scripts only by copying to a folder on a disk (not very handy) By convention, scripts’ names must match *.user.js to be run Safari, IE There are some plugins for them also, although I haven’t tried them Details: https://github.com/jakub-g/gh-code-review-assistant#installation-guide
to run plain JS supported by the browser Special functions (GM_*) available for Unrestricted cross-domain XHR Saving preferences inside the browser See more: http://wiki.greasespot.net/Category:API_Reference Possibilities to run: automatically on given URLs Before any script on the page has run (Firefox only) After the page is loaded (DOMContentLoaded) on demand also when JS is disabled in the browser in interaction with HTTPS pages with CSP enabled Scripts do not leak into the global scope
them -- even more http://userscripts.org/ - best existing resource for userscripts With Greasemonkey or Tampermonkey installed, opening an URL finishing with .user.js triggers installation popup Scripts can be auto-updated when @downloadURL / @updateURL are specified in metadata block
myFile http://www.example.com/somefile.txt // ==/UserScript== var myText = GM_getResourceText("myFile"); Resources and required files are downloaded the first time the script is executed. They’re stored in the profile folder along with the script itself. Whenever the list of dependencies changes, they’re all re- fetched.
XHR that is not subject to same-domain policy (without CORS!) Hence we can request any arbitrary resource (cookies will be attached automatically) Read / write arbitrary data to the browser’s internal storage Data is independent of the page and doesn’t go away when emptying cache / cookies etc. Data is stored in a global storage, so it can be shared cross-domain (unlike localStorage which is per-origin). In Firefox, this global storage are about:config entries extensions.greasemonkey.scriptvals.(namespace)/(scriptname).(entryname) GM_getValue / GM_setValue
means that the page is using just plain JS. then window in userscript === window in the page scope If we want to use some special GM_* functions, we have to explicitly @grant the permission for the script to use it (since GM 1.0), one-by-one. Otherwise, those identifiers will be undefined. // ==UserScript== // ... // @grant GM_setValue // @grant GM_getValue // ==/UserScript== If we @grant’ed anything, then window in userscript !== window in the page scope window in userscript is a „safe window”, to separate the privileged and non- privileged execution scope Like a newly created window object, without any alterations or user-defined global variables This is to prevent the access to GM_* functions by the malicious script on a page unsafeWindow in userscript === window in the page scope
... // @grant GM_registerMenuCommand // ==/UserScript== GM_registerMenuCommand("Say hello", function () { alert("Hello"); }); Note that in all current implementations, bookmarklets are subject to CSP (Content Security Policy) which makes it impossible to run them in CSP-enabled HTTPS pages. Userscripts are above those limitations as they run in privileged environment.
// @run-at document-start // @grant none // ==/UserScript== // let’s override addEventListener & log all the invocations into console var evtTgt = window.EventTarget.prototype; var originalAEL = evtTgt.addEventListener; evtTgt.addEventListener = function (type, func, useCapture){ var title = "Registering event listener: " + type; if(useCapture) title += " (capture)"; console.groupCollapsed(title); console.info(this); // element on which addEventListener is invoked console.info(func.toSource()); // „func” to have link within Firebug console.groupEnd(); originalAEL.apply(this, arguments); } @run-at document-start to run before any other JS on the page (works only in Firefox) @grant none to have access to „real” window, not sandboxed one
objects addEventListener Array.push With the notable exception of some constructors like Array, Object that can’t be hijacked in modern browsers Overriding getters and setters Adding frequently used utility functions to the global scope Extending console ... Note that @run-at document-start is required, which for now is only supported in Firefox + Greasemonkey
pages by tweaking the stylesheets / source HTML Note Stylish and userstyles.org are just for that (overriding CSS) However it’s always good to have some JS in your sleeve :) Prevent hijacking of browser’s native keyboard shortcuts Adding keyboard shortcuts for pages lacking accessibility Automatic hiding / unhiding certain elements of the page Reordering of entries on the page etc.
selected text, translates it into other language with GM_xmlhttpRequest, and displays it as a popup <div> on the page GitHub Assistant (https://github.com/jakub-g/gh-code-review-assistant/)
Automatically redirect from HTTP to HTTPS ... Ok ,all those presented before were silly examples. Here comes the power: Script that retweets random posts of the people you’re following Script that will automatically plays Facebook games for you!