The Resurrection of Bookmarklets


Illegitimi non carborundum

• Friday, May 8, 2015 – William Donnelly

NOTE: Apparently there is a CSP ability to stop inline scripts from executing. I have not come across any sites that use that feature and/or the browser I am using does not support it. (currently Pale Moon 25.4.0) So this is not an "absolute" fix. Unfortunately, CSP is taking away too much of the user's power and control over their browser use. (unless the browser creators somehow come up with a work-around — they need to develop a security model that works for everyone involved without crippling the (power) user)

The increased implementation of Content Security Policy (CSP) in websites has been heralded as "The End / Death of Bookmarklets". I created a solution in 2015 that resurrects the ability to use (library-based) bookmarklets on CSP and https:// websites using a Greasemonkey userscript.

You can find more info about this issue here:
(Bug 866522 - Bookmarklets affected by CSP (Content Security Policy))

After months of trying to figure out what was going on, and multiple attempts to find a work-around or SOMETHING to "fix" it, I FINALLY found a solution to the problem.

The solution is fairly simple using a Greasemonkey userscript. For some reason, people "at Greasemonkey" couldn't offer the solution I created, or even hints or tips to get there. I'm not sure what that was all about. I explained my problem and asked what I can possibly do about it, and no one offered an answer, so I had to figure it out for myself. (after some headbashing and such)

It's kind of "stupid", because, although you are limited in what URL you can use to inject a script into certain CSP-protected documents, you can insert ANY text DIRECTLY into the document. (now watch them take that away from us, too) Of course, there is (still?) usually some limit to the size of a saved bookmarklet, but I (needed to) get around that. (plus the "squishing" and such needed in bookmarks, which I've NEVER liked)

I use the @resource command to access my 56K JavaScript library file, get the contents of it, and directly insert that .JS file document text using the script object .text property instead of the .src property.

Note that, upon installation of the userscript, the library file is loaded as a STATIC SNAPSHOT into the Greasemonkey directory that holds the userscript. There are some issues with that and modifications/reload to the referenced source file. So you MUST directly edit the snapshot file if you have changes. (that is, there is no built-in mechanism to auto-reload that file if it has been modified at the source location -- hopefully they will correct that sometime in the future -- there are some "work-arounds" that are not very good, imo -- so I just "load it once" into the userscript directory and edit that file from then on)

I also didn't want to load the library into every page, so I made the bookmarklet "communicate" with the userscript and let it know that it wants the library loaded, and then it executes the library bookmarklet function after 1/5th of a second.

Below is the script I use to load my bookmarklets library now: (with some names 'obfuscated')

// ==UserScript== // @name Bookmarklet Library // @namespace choose-a-namespace.com // @description Loads bookmarklet library into page // @include * // @version 1.0 // @resource bmlib http://www.blah-blah-blah.com/bookmarkletlibrary.js // @grant GM_getResourceText // @grant unsafeWindow // ==/UserScript== // NOTE that the library file is a STATIC SNAPSHOT saved into the directory that contains this userscript function checkLibraryLoadRequest() { if (typeof (unsafeWindow.Bm_bLibraryRequest) != 'undefined') { // value set as request from bookmarklet if (typeof (unsafeWindow.Bm_bLibraryLoaded) == 'undefined') { // value set inside library script var sBMLibSource = GM_getResourceText ('bmlib'); var oScript = document.createElement ('script'); oScript.type = 'text/javascript'; oScript.text = sBMLibSource; // document.getElementsByTagName ('head')[0].appendChild (oScript); document.body.appendChild (oScript); } } else { setTimeout (checkLibraryLoadRequest, 100); // check every 100 ms } return; } // checkLibraryLoadRequest checkLibraryLoadRequest(); // EXAMPLE Bookmarklet shell/template: // javascript:(function(){window.Bm_bLibraryRequest=true;setTimeout(function(){window['***FUNCTION-NAME***']();},200);})() /**********************************************************************/

At the top of the library script file I have:

var Bm_bLibraryLoaded = true; // stops library from being loaded twice (just in case)

And then my bookmarklets all have the same format as the EXAMPLE noted at the bottom of the userscript above. I just change the "***FUNCTION-NAME***" value to the name of the function I want to call in the library.

I kept the "communication" between the userscript and the bookmarklet simple. So far it has worked. Although one time it may not have, so you might want to either decrease the "check for request time" to 50 and/or increase the "execute bookmarklet function" time to 250.

I haven't noticed any noticeable lag time because it's only about 1/5th of a second.

So far it works great. I can now execute my bookmarklets from Twitter, Facebook, Google, and anywhere else, including all https:// "secure" websites. So I suppose this will work until someone does something else to screw us over.

btw -- I am using Pale Moon now ("old-style Firefox"), so I don't know if there are any differences in Firefox, Chrome, etc. But it should be fairly easily modified to work under any browser that has a Greasemonkey-like add-on extension.

I haven't made all of this "easy to use / install" because I figure if you are a bookmarklet person (well, a JavaScript programmer), you can easily do it and figure it out on your own. People who are not power-users may have some issues. But the latter must be getting their bookmarklets from someone, so that source will probably have to be the one to set up their bookmarklets (and library) for their users. NOTE that if people do this, they have to PREFIX their global variable names so there is no conflict!

If you have questions or comments, you can contact the author at whd1802Donnelly-House.net.


Bookmarklets are Dead

The Slow Death of Bookmarklets

Github Content Security Policy

Content Security Policy 1.0 Lands In Firefox

CSP policy directives


Copyright © 2013 by Donnelly House dot net — All rights reserved — Best viewed at 1024 width resolution with the Firefox browser.