Scriptish 0.1 Released!

Well, I'm very proud to announce that Scriptish 0.1 has finally been released!

Thank you

This is an event that has taken a long time to achieve, and I have had so much help from so many wonderful people that it has been a great pleasure to work on this project.

Greg Parris

Greg forked Scriptish and started contributing back in Oct '10 and he's been a constant help ever since. Greg has written plenty of code, made the wiki readable, improved the usability and user experience, and no doubt has spent countless hours testing. Thank you Greg!

Olivier Cornu

Olivier really showed me the way, he had forked Greasemonkey long before I ever started Scriptish, making a extension called Webmonkey which used Firefox 3 as the minimum supported version and taking advantage of the benefits that Firefox 3 had to offer. I pulled some code from Webmonkey and made Olivier the author (which he told me would be alright), but he really provided me with a proof by example of a simple idea, that a far better user script engine could be made by increasing the minimum supported version and taking advantage of the new features. Thank you Olivier!

Kirs Maglione

Kris has helped out with some error handling contributions early on, answering more than a few of my questions on IRC, and for doing the last few reviews on AMO which I'm sure took some time. Thank you Kris!

Nils Maier

Nils wrote some wonderful commits revamping the install window and really improving the UX of user scripts in the extension manager. I think these changes are the most visible to the average user, and are great improvements imo, so his help has been invaluable. Thank you Nils!

Dave Townsend

Dave is one of the developers of the new Addon Manager in Firefox, and he made a restartless extension called SlipperyMonkey (which he blogged about here) showing off a bunch of Firefox 4's new features. I've pulled a lot of this code in to Scriptish, and had the pleasure to meet Dave at the 2010 Mozilla summit in Whistler when he was showing SlipperyMonkey off, and got a change for a quick ~5min QA which proved to be most helpful to me. Thank you Dave! (and thank you David Dahl for telling me to speak with him, which I might not have done).

Mozillians

A community really makes all of the difference, and I've met no better. Just look at the documentation, look at the support, look at the inspiration, and look at the goals. Thank you Mozillians!

Github

I quite honestly wouldn't have bothered spending so many unpaid hours managing a open source project without a site that helps me administrate it like Github does, because it'd just be a waste of time. Thank you Github!

Scriptish 0.1b4 Can Update Your User Scripts

For a long time now both user script authors and Greasemonkey users have wanted Greasemonkey to be able to update user scripts. In absence of this feature user script authors have resorted to rolling their own code to do the updating, which has lead to a minefield of problems ranging from simple javascript errors to causing long downtimes on userscripts.org. Besides the obvious risks that third-party user script updaters have of not working, over using bandwidth, and harming the uptime of important websites, there is also a variability of design and usefulness which means it's hard for a user to know things that they should know, like:

  1. Which of their installed user scripts are update-able?
  2. Which of their installed user scripts have updates available?
  3. Where will they be notified of updates for there user scripts?
  4. What do they need to do in order to update a user script?
  5. How can they disable updates for a user script?

Hopefully it's now plain to see why a user script engine must update user scripts, and yet none that I know of do. Greasemonkey for Firefox does not. Google Chrome runs user scripts natively, but also does not update them. Those two engines are by far the most popular, but there are a few others, all of which do not update user scripts..

Well late last year I started discussing this feature with Olivier Cornu for his fork of Greasemonkey which he called Webmonkey. Months later I decided to start a Greasemonkey fork of my own, which I call Scriptish. Then, as this year went by I discovered Mozilla's plans for the new Addon manager and the stars seemed to align.

So a couple of days ago I finally got around to implementing user script updating in Scriptish, and released the feature in Scriptish 0.1b4 last night; now I'd like to discuss it a little bit for those that are interested.

User Script Updating in Scriptish

@updateURL

You may specify a URL for Scriptish to check for updates using the @updateURL metadata block key. The @updateURL must be a secure url (ie: starts with "https://"), otherwise it will be ignored.

The optional use of ".meta.js"

To save bandwidth, and improve response times, you may optionally provide a ".meta.js" version of your user script as the @updateURL, which should be the same as the user script, but only containing the user script's metadata block. If you provide a @updateURL that ends with ".meta.js", then Scriptish will check then ".meta.js" file for updates, and download an update from the same url where ".meta.js" is replaced with ".user.js".

The option to use the download URL as the update URL if no @updateURL was provided.

If you go to the options window for Scriptish you will see a preference which allows you to use a user script's download URL as the update URL if there was no @updateURL defined; this option is turned off by default.

Using ".meta.js" by default from userscripts.org

If the update URL that should be used to check for updates is a userscripts.org URL, then the ".meta.js" version of the user script will be used by default.

The GM_updatingEnabled constant

All versions of Scriptish that provide user script updating will include a GM_updatingEnabled constant in the user script sandbox, this means that if you are a user script author that has implemented a updater for your script already, then your code can check if this variable is defined and equal to 'true' before attempting to check for updates.

Conclusion

A user script engine that updates user scripts has been long desired, so I'm proud to introduce the feature for the first time in Scriptish, and I hope you give it a try!

Why is Scriptish only for Firefox 4?

One little tidbit I didn't go over yesterday about Scriptish was why I decided not to support any version of Firefox less than 4.0, so I thought I should mention my reasons behind that decision.

The primary reason I did it was to shave a large amount of time off of the time to release tbh. Another reason was that by raising the min version of Firefox supported to 4.0 I was able to delete large amounts of legacy code, which means performance gains. The final reason was that there was a Add-on Manager API being introduced in Firefox 4 which allows Firefox extensions to easily add extension types to the add-on manager, which means Scriptish can rely on that entirely, and ditch even more legacy code, Dave Townsend (Mossop) describes how to extend the add-on manager here.

Scriptish Beta! A New Greasemonkey

My favorite browser extension for quite a while has been Greasemonkey (GM), I loved how simple it made customizing the web with JavaScript (JS) which allows all users to both filter out the crap that site owners try to provide us (ie: ads, share links, suggested junk) and add new useful features anywhere you that wish. Greasemonkey allowed us to customize the waves as we surfed the web, as well as making a few modifications to the board, and I loved it for that.

Some problems arose with Greasemonkey though, because it was built for Firefox (FF) 1.5, and even the latest GM version (0.8.6) claimed to support 1.5, which meant that Greasemonkey the GM maintainers did not want to introduce new features that would not be available in FF 1.5, and they did not want to take advantage a of newer version, like say FF 3.0's JavaScript Modules (JSM), which would allow GM to use much less memory and improve performance when a user is using multiple windows, and on startup because less JS would need to be loaded at startup.

The good news for GM is that with the next major release, 0.9 (which contributors including myself have been working on for quite some time now) is that the minimum version of Firefox that Greasemonkey will support will be FF 3.0, which means it can now take advantage of the benefits that FF 3.0 offers, finally. The trouble I found while trying to contribute to GM is that the maintainers don't really want to alter the code base, they're actually seem to be fine with doing the minimum necessary and bug fixing for the rest of the future, at least that is how it seemed to me after writing quite a few patches which were rejected, either outright, or by degradation (meaning I would have to basically redo everything, on crappy architecture, because they've sat on their hands for so long). Patches such as using JSM, reg exp @includes, @icon, and a few more.

So the reason that I decided to bypass Greasemonkey and start working on a fork, which I now call Scriptish, is because I was tired of beating my head against a wall, I wanted to have and use the Greasemonkey that I always wanted asap, so I'm making it now. I've always enjoyed working with others, and I hope that I can convince some GM contributors to start working on Scriptish in the future.

Scriptish

Scriptish is a fork of GM, you can think of it as a superset of Greasemonkey, it can do whatever GM does, and more. Some of it's new features are:

  • @author - displayed to users in the addon manager
  • @contributor - displayed to users in the addon manager
  • @homepage or @homepageURL - displayed to users in the addon manager
  • @icon or @iconURL - Include a icon for your user script which is displayed in the addon manager and for notifications from the script.
  • @screenshot - displayed to users in the addon manager.
  • @match - a include pattern introduced by Google Chrome user scripts.
  • @noframes - a simple way to prevent a user script from running in iframes.
  • GM_worker - use a Worker within user scripts.
  • GM_notification - send a Growl style notification to the user.
  • GM_setClipboard - save data to the clipboard from a user script.
If you'd like to do some reading on these features, then checkout the Scriptish wiki, which should explain them all in detail.

Other changes include major reorganization of the internal code, using JSM, in a way that only loads code into memory that will actually be used, by loading the req'd code the first time that it is going to be used. I was also able to remove a bunch of legacy code, because Scriptish will only support Firefox 4.0 or higher for the moment.

Give it a try!

If you'd like to give Scriptish a try, then you can download Scriptish here. If you want to get involved here are some links for you:

I hope you like it, I'll be working on it for the next few months at least, pretty hard I imagine; I'd like to make it restartless, and implement user script updates & communication asap. If you think you'd like to open it up and hack on it, then please do!!

Webxtend

Something that I don't think many web developers and extension developers seem to be picking up on, but yet some are, is what I'm calling webxtending, which is providing APIs that allow their users to extend their website/webapp or extension with a browser extension. To illustrate this idea take the Gmail-Greasemonkey API, this is a front-end JS API that a user script can easily access to be notified of Gmail's custom events, change state variables, and perform actions with the Gmail interface (afaik the api used to work, and some parts do not work at the moment). This was a wonderful idea because it allowed user script authors to save a lot of development time, also maintenance time, and write more cpu efficient scripts. Furthermore I imagine such a API would be extremely useful to internal development, it would at least reduce the chance of sloppiness.

Webxtending doesn't always mean providing a Javascript API though, it often simply means writing semantic code by using microformats, and structuring the markup in a logical manner, taking into account how others may want to change the UI, and making those changes as easy for them to make as possible. This means using the id attribute even when you don't need it, because others may, but it also means thinking carefully about every facet of the structure. XUL overlays used in Firefox extension development are a good example of this structural extendibility idea.

Webxtend is a gold standard quality requirement, if your website or browser extension is not webxtendable then it's not gold standard quality. =p

User Script Tip: Using jQuery

A very common question that I see asked on stackoverflow.com, the greasemonkey-users mailing list, and in #greasemonkey on freenode, is how to use third party libraries with a user script.

The common response is to use @require, but these days people want their user scripts to work on Google Chrome too, and unfortunately Google Chrome user scripts do not support a lot of the Greasemonkey metadata block, including @require.. so another method is required.

Cross Browser Solutions

Use script element & setTimeouts

One method that has been mentioned elsewhere is to create a script element and add it to the page, and use setTimeout's to wait until the third party js, in this example jQuery, is loaded. The example in the link I provided is for Greasemonkey, but with little effort it can be made to work with Google Chrome.

Use script element & associated onload event

This method creates script element for jQuery (or some other third party script), and appends it to the body element, and listens to the load event to figure out when the library is loaded and ready to be used.

Google Chrome does not allow user scripts to access javascript objects in the page scope from the user script's scope, so in order to use jQuery at all on Google Chrome you must load jQuery into the page scope and inject your script into the page scope. So this is what my example below does essentially, more specifically it stringifys a callback function which is called when jQuery is done loading.

Example

Easier GitHub Fork Switching

There are many issues I have with my favorite site to host git repositories, GitHub, but today I decided to solve one of them.

The Problem

When you are on a page in one of your project forks on GitHub for say some file in the repository, and you want to switch to see the same page for the project that you forked from, then you have a number of clicks and page loads ahead of you. My feeling is that there should be a single link that allows you to do this, and vice versa.

[More]

Greasemonkey Optimization: Convert2RegExp

Over the last week I've been spending a great deal of time looking over the Greasemonkey and Webmonkey source code, both because I want to understand both code bases more, but also because I'm interested in Firefox extension internals in general. While looking through these two code bases I saw a common file which could be optimized. This file was the convert2RegExp.js file, which looks like it came from Adblock at some point, at least in part. In fact I saw a number of changes that I could try in order to speed up the function, so I decided to time them all.

Test Factors

Factor I: Check for /\.[^\.ld]*t[^\.td]*l[^\.lt]*d/ in pattern string character loop

The first thing I noticed was that the regular expression that checks the pattern string for a ".tld" string was being run on every pattern input, this seemed obviously bad to me since there was a loop prior to the regular expression which runs through the pattern string's characters, so why didn't that loop at least check for the ".tld" substring first? the check could be as simple as making sure all of the required letters exist in the string first, or make sure that the ".tld" substring, exactly, is in the pattern string, or what I found to be the best way was to make sure the regular expression /\.[^\.ld]*t[^\.td]*l[^\.lt]*d/ matched the pattern string via the character loop through the pattern string. The average cases and best cases will go much faster despite the fact that the latter case would be matching some pattern strings that following tld regular expression (that is already in convert2regexp) would not match, thus just adding work in this case, and making the worst possible case even slower.

Factor II: Cache tldStr and tldRegExp

The convert2RegExp( pattern ) function creates the tldRegExp and tldStr from literals on every execution! that might be a faster operation I thought, but I expected it to be slower than simply looking up the value of a variable when I timed it, and even though I haven't figured out how to test the memory consumption yet, I expect creating a large string from a string literal over and over again would increase the peak memory usage, and thus the garbage collector pause time as well. So, caching the tldStr and tldRegExp seemed like it would be a small win.

Factor III: Use array.push()/array.unshift() and array.join() instead of +=

From what I've read about Firefox 3.6 the += operator in loops is optimized to use a single StringBuffer, but there were a few += outside of the loop, and I figured some people will probably use versions of Firefox < 3.5 for some time to come still, and using an array there would certainly improve/decrease the peak memory usage. I didn't know what the affect on performance would be, but this change is commonly said to be the better approach for JavaScript in the past to present, mainly because of the memory issue. Furthermore, the more memory that is used, the more work there is for the garbage collector to deal with, which means your computer is even slower, and that time is hard to measure. I do know that a new feature to Firefox 3.6 is that the garbage collector frees the memory in a new thread, which means that older versions of Firefox did not, and that means that the chances of pauses were even greater. For more reading on these changes I am mentioning in Firefox 3.6 please read this article.

The Tests

Description

In order to test the factors listed above I knew I needed a number of different versions of the convert2RegExp function, but the other piece I needed was a collection of pattern strings to test. I made the different versions of convert2RegExp easily, but when it came to making the pattern set(s) to test I had to do some more thinking.

Test Pattern Sets

I may not have made the best choice, but I decided to use two pattern sets:

  1. Pattern Set 0: 3/50 patterns use the magic tld expression, 2/50 do not use the tld expression, but match /\.[^\.ld]*t[^\.td]*l[^\.lt]*d/
  2. Pattern Set 1: 2/50 patterns use the magic tld expression
In retrospect I think testing an even worse case might be advantages, although I don't suspect the tld expression is used very often. It's hard to say however, I would like to see an audit of userscripts.org, but even with that it'd hard to know what the average case is for all Greasemonkey users, even when you take the install counts from userscripts.org into account, although all of that perspective would be nice to have.

I am an avid user of Greasemonkey and I write quite a few userscripts, and I find that in practice that there are very few times when I would need to userscript to use the magic tld expression, so my feeling is that the ratio is probably closer to 1 tld pattern per 100 or more.

Test Pages/Versions

I decided to run my tests of the different factors listed above for FF3.5/FF3.6 on WinXP, OSX, and Ubuntu (only FF3.5). The test pages worth pointing out are:

  • Original
  • Alternate 3: test of only factor I
  • Alternate 5: test of only factor II
  • Alternate 6: test of factor II + minor change to return a value asap.
  • Alternate 11: test of factor I + II, uses array.unshift() and array.join(), and returns a value asap
  • Alternate 12: test of factor I + II, uses array.push() and array.join(), and returns a value asap
  • Alternate 13: test of factor I + II, and returns a value asap
  • Alternate 15: test using array.unshift() and array.join() as only change

Test Results

Here are the tests I ran and the results I record (the links are to published google docs spreadsheets, all values are in seconds):

AMD 2.21Ghz, DDR2, WindowsXP

Intel Duo 2.0Ghz, DDR3, Mac OSX

Intel Duo 2.0Ghz, DDR3, Windows XP

Intel 2.0Ghz, DDR, Ubuntu

Each test was using a version of convert2RegExp, on a set of 50 patterns, 2500 times.

Results

  • In all cases alternate 3 was faster than the original.
  • In all cases alternate 5 and 6 were faster than the original.
  • In all cases alternate 6 seemed to be slightly faster than 5.
  • In all cases alternate 15 is much faster than the original for FF3.6, and slightly slower for FF3.5.
  • For FF3.5 alternate 13 > alternate 12 ~= original ~> alternate 11
  • For FF3.6 alternate 11 > alternate 13 > alternate 12 > original.
Although for FF3.5 we know that alternate 13 is a bad choice because it uses the += operator which result in O(N^2) characters copied, so we can't use that in my opinion. The final choice has to be between alternate 11 and alternate 12.

This is where I need your help, I have no idea why using array.unshift() is so much faster in Firefox 3.6, and taken that it is, and that the majority of Greasemonkey users will be using the latest version of Firefox, should we use array.unshift() and not array.push()?

Tweet Me!

A Jetpack: Greasemonkey Context Menu

This Jetpack will add the Greasemonkey menu commands to your context menu.

Screen Shot

Get it at:

Greasemonkey Tip: Adding CSS With GM_addStyle

Adding style to a webpage with Greasemonkey is extremely easy, but I have noticed a lot of userscript code that either ignores the GM_addStyle( css ) api method, or doesn't use E4X which would save the author's time typing, and save the reader's time reading. So the following are some tips when adding CSS to a webpage with Greasemonkey.

Tip 1: Use GM_addStyle( css )

Don't add multiple style attributes, don't use that code you have that adds a style tag to the page, use the built-in GM_addStyle( css ) api method; usually you should only need to call this method it once.

Tip 2: Use E4X

Typically when your adding css to a page, there are quite a few lines, and you don't want to escape every newline character, or join a bunch of partial strings together; the simplest way to go is to use E4X like so:

GM_addStyle((<><![CDATA[
   body { color: white; background-color: black }
   img { border: 0 }
   .footer {width:875px;}
]]></>).toString());

More Entries

© Erik Vold 2007-2013. Contact Erik Vold. Top ^