Userscripts: Augmenting and automating web browsing and debugging

Userscripts: Augmenting and automating web browsing and debugging


Jakub Gieryluk

April 30, 2013


  1. Userscripts Augmenting and automating web browsing and debugging Jakub Gieryluk 02.05.2013
  2. Agenda  Creating a userscript from scratch  Brief explanation

    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
  3. Prerequisites (1)  Necessary differentiation (perhaps not very precise): 

    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
  4. Prerequisites (2): environment  To run userscripts, we’ll need a

    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: 
  5. Advantages of userscripts (Some of them are Firefox-specific)  Ability

    to run plain JS supported by the browser  Special functions (GM_*) available for  Unrestricted cross-domain XHR  Saving preferences inside the browser  See more:  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
  6. Our first userscript // ==UserScript== // @name My cool userscript

    // @description Just a training script // @namespace // @author John Smith // @version // @run-at document-end // @grant none // @include http*://** // @exclude http*://* // @downloadURL // @updateURL // ==/UserScript== console.log("It works!") Explanation of all metadata entries: Or: document-start GM_setValue, GM_getValue, GM_xmlhttprequest etc. (one per line)
  7. Installing third-party US  Writing userscripts is cool, but sharing

    them -- even more  - 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
  8. Dependencies // ==UserScript== // ... // @require // @resource

    myFile // ==/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.
  9. Debugging a userscript in Firefox  Recommended toolkit : Firebug

    + Console2 + All-in-One-Sidebar  console.log’s go to Firebug  Runtime errors go to Error Console (Ctrl+Shift+J to open it)
  10. GM_xmlhttpRequest  Allows sending (and receiving a response of) an

    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
  11. @grant and security implications  @grant none in metadata block

    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
  12. Userscripts on demand Viable replacement for bookmarklets // ==UserScript== //

    ... // @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.
  13. Use case: pre-load JS injection Logging addEventListener invocations // ==UserScript==

    // @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);; // element on which addEventListener is invoked; // „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
  14. Use case: pre-load JS injection Logging addEventListener invocations

  15. Use case: pre-load JS injection  Debugging:  Hijacking host

    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
  16. Use case: fixing broken pages!  Improving readability of the

    pages by tweaking the stylesheets / source HTML  Note Stylish and 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.
  17. Use case: enhancing pages  Right-click dictionary that takes the

    selected text, translates it into other language with GM_xmlhttpRequest, and displays it as a popup <div> on the page  GitHub Assistant (
  18. Use case: automation  Automatically log in to certain pages

     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!
  19. Thanks for your attention! Enjoy hacking the userscripts!