Page-speed for Firefox 7 + the future of extensions?

The problem

I was trying to use Page Speed the other day – an awesome browser plugin (and/or apache module) made by Google to help make the web faster. However, I was tired of having 5 versions of Firefox to chose from (or remember where I’ve installed various plugins), so I removed everything else and just kept “Aurora“. Aurora is Mozilla’s equivalent of the dev channel build of Chrome (between nightly and beta). Unfortunately, Page Speed only works on Firefox 4-6.* (well, it doesn’t say the end range there, but just trust me).

The hack

Figuring there really couldn’t have been that big of a difference between the two, like a good hacker I changed the maxVersion in the plugin’s metadata (in this case install.pdf in page-speed.xpi). I’m not sure all plugins have these kinds of issues or are using the same method of versioning, but it seems relatively standard to Firefox plugins. So, I wrote a script that’ll cURL the .xpi for me, do a tiny little bit of sed to change the maxVersion to * and re-zips it back into the current working directory (did you know .xpi is just a glorified .zip?).

Here’s the script if you’re lazy like me and don’t like waiting for plugin authors to simply bump versions.

And guess what? It worked like a charm!



NOTE: I make no attempt to suggest this method of changing maxVersion isn’t totally stupid, dangerous, and just outright gross. However, if you feel any of these things about this script/method … just don’t do it.

The hope

I think as browsers continue to move toward versionless (or, er, version agnostic) software we’ll probably need to rethink the way we try to version packaged software. Something like this obviously has maintenance costs associated with it for extension developers (and Mozilla doesn’t seem to care much, as they force you to recompile on every release even if youre source code hasn’t changed), so I doubt people will continue manual version bumping for long. My guess is that they’ll either just bump to * or remove the maxVersion like I have, or will just simply stop maintaining plugins all together.

That said, using feature switches inside your plugin to check for a dependency or version of something sounds like a pretty good idea, if it’s technically feasible (not sure, never done large scale stuff with extensions). That is what various places I’ve worked (Yahoo!, Google) do to hold back something that needs to be continuous integrated/deployed during development (see “about:flags” in Chrome to understand what I’m talking about, ;) ).

What do you think?

Update:

Nimrod Bar, my former co-worker and maintainer of FoxyTunes (a Firefox add-on w/~150,000 users) also told me that you can achieve this same effect with the Nightly Tester Tools. I find it weird that installing an add-on would let you … install more add-ons (well, have enough access to do this), but it’s still cool.

Posted in Bash, Firefox, Hacks | 2 Comments

Flash 10 + Firefox 5 + Ubuntu = hell

For those fellow Linux nerds that are constantly annoyed by trying to have recent software that actually works, after an hour of messing with various stale (or not stale) guides, I have a one-liner for you that should install Flash 10 for you in Firefox 4 & 5 in Ubuntu (I’m using 10.10, but this probably works on more than one version).

Here’s the command:

sudo mkdir -p /usr/lib/flashplugin-installer && \
cd /usr/lib/flashplugin-installer && \
sudo curl -s http://fpdownload.macromedia.com/get/flashplayer/current/install_flash_player_10_linux.tar.gz -O && \
sudo tar xfz install_flash_player_10_linux.tar.gz && \
sudo ln -nsf /usr/lib/flashplugin-installer/libflashplayer.so /usr/lib64/mozilla/plugins/libflashplayer.so && \
sudo rm -rf install_flash_player_10_linux.tar.gz /usr/lib/flashplugin-installer/usr && \
cd - >/dev/null && \
echo "All done"

Of course you should understand what this does before you run it (especially because it requires sudo) and it doesn't have to be in one line, but why not? :)

sweet success

Posted in Bash, Firefox, Hacks, Linux, Ubuntu | 2 Comments

Turns out there is block scope in JavaScript … kinda

Block scope in JavaScript?!

Yup, using an interesting hack that works in browsers that like the open web.

You mean let?

No, I’m not talking about JavaScript 1.7′s let statement (currently only implemented in Firefox 3.0+). I’m talking about using try {} catch (){} in mischievous ways, :) .

Where it started

So, looking at an interesting StackOverflow question I started hacking around with variables inside of the parens following a catch statement. Here was my first attempt:

try {
    JSON.parse('Throw some error');
} catch (e) {
    var x = "It doesn't introduce a new scope, but";
}
document.write(x); // It doesn't introduce a new scope, but
document.write("<br> e is " + typeof e); // e is undefined

(the jsfiddle)

The Cool Hack

OK, so something a little quirky is going on here. var statements inside of a try {} catch (){} leak to the outside scope, but Exceptions passed to the catch statement don’t exist after that scope is left. So after checking this and some other stuff as well (jsfiddle) it looks like try {} catch(){} totally sets up a new block scope for the Exception you’re throwing to it.

This lets us do interesting things like:

var i;
for (i = 0; i < 3; ++i) {
    try { throw i; } catch (l) {
        // use l without fear of it changing
    }
}

(the cool hack, the typical problem)

in all browsers except ... you guessed it, IE.

Everywhere but here (if "here" is IE6-8)

So, the good news is that this try { throw i; } catch (l) { } hack does introduce a new block level variable without creating a new function (should be cheaper and not affect var statements inside the catch) in many platforms. However, the bad news is that JScript < 9 implements try {} catch(){} in a different way from everybody else because they have a long history of doing things differently and why stop now? (OK, OK - JScript 9+ acts the same as everybody else.)

Anyway, poking around with JScript 5.6-5.8 a little bit more I figured out they're simply creating a functionally scoped local rather than a block scope one. Here's an example:

(function () {
    var i = 5;
    try {
        throw i;
    }
    catch (c) {
        var l = 20;
        c = 50;
    }
    finally {
        var f = 100;
    }
    alert("l: " + typeof l + ", " + // "number"
          "c: " + typeof c + ", " + // "undefined" except
                                    // in IE6-8 it's "number"
          "f: " + typeof f);        // "number"
}());
alert("l: " + typeof l + ", " + // "undefined"
      "c: " + typeof c + ", " + // "undefined"
      "f: " + typeof f);        // "undefined"

(the fiddle)

MSDN does mention that this Exception is a local here, but now how local.

So, can I use this?

This does mean that you can use this hack for sites or libraries not targeting IE, like things on Mobile (for now, unless you think Windows Phone 7 is gonna be big, lol) or solely Linux/UNIX/OSX or Firefox/Chrome/Safari/Opera user bases. If you do want to support IE6-8 (41.61% of current browser market) don't use this hack as it will only work in IE9+ (if they stick to what they're doing right now).

Conclusion

I thought I had figured out how to emulate let in all browsers, but the story just turns out to be the typical one -- all browsers but IE.

Posted in Hacks, JavaScript | 5 Comments

2295 script tags later

What I did

So, building on my “Script: not a type-ical tag” article, I tested a few more ways to inject JavaScript into webpages. 1895 more, to be exact, :) (and I’m not done yet).

How I did it

Firstly, I changed the way I’m injecting scripts to use a PHP file like Mathias Bynens is in his tests (i.e. page.php?type=blah/blah&do=something). Here’s the end result for that PHP file:

<?php

$ct = preg_replace('/[^a-z\/\-]/', '', $_GET['ct']);
header('Content-Type: ' . $ct);

if (empty($ct)) {
    $ct = 'no type';
}

if (isset($_GET['dw'])) {
    echo "document.writeln('Content-Type: $ct works!<br>');";
}
else if (isset($_GET['id'])) {
    $id = preg_replace('/[^a-z_]/', '', $_GET['id']);
    echo <<<EOT
try {
    document.getElementById('{$id}').innerHTML
        = 'Content-Type: {$ct} works!';
}
catch (e) {
    alert("Couldn't find id of {$id}");
}
EOT;
}
else {
    die("// Nothing to do");
}

So this allows me to inject the scripts like so:

<script src="js.php?ct=application/javascript&todo"></script>

And they will set the HTTP Content-Type headers for external <script>s and do some stuff.

I also added data URIs, dynamic script creation and dynamic script injection (subtly different).

Data URIs scripts look like this:

<script src="data:X/Y,document.write('oh%20hai!')%3B"></script>

The dynamic script creation I'm doing looks like this:

window.onload = function () {
    var c = document.createElement('script');
    c.innerHTML = '// do something in JS!';
    document.body.appendChild(s);

And dynamic script injection looks like this:

    var i = document.createElement('script');
    i.src = 'js.php?do=something';
    document.body.appendChild(i);
};

Results

NOTE: I will be switching this to use Browserscope sooner or later, but for now the results of my very manual testing can be seen below.

Conclusions / Findings

So, some interesting things I found out from this were:

  1. Though IE8 supports data URIs in other areas (supposedly), it will not execute a <script> with a data URI as the src attribute (verified by Microsoft here). This has been changed in IE9. Go nuts.
  2. IE9 handles data URI encoding much more strictly than other browsers. Without properly encoding various parts of the inline data (i.e. / -> %2F, ; -> %3B) <scripts> will not execute nor give any particular reason why they didn't (as far as I could find). At first this lead me to believe that data URIs in <script> srcs weren't working in IE8 or IE9 at all!
  3. IE9 accepts many different type attributes on <script> tags like other "good" browsers. This is a large jump above IEs 6-8, :)
  4. A browser made by 90,000 geeks over 30 years is now being rivaled by the browser I have in my pocket on my phone (OK, that's not fair - Apple and Google both made Webkit & Chrome).

As always, let me know if you find problems with my results or if you want to contribute some results yourself.

Posted in Compatibility, Cross-browser, JavaScript, Web | 2 Comments

Script: not a type-ical tag

Problem

I was talking to Paul Irish today regarding some tweaks I was doing to html5boilerplate‘s code when I hit the usual kludge you see when dealing with JavaScript in .htaccess files or <VirtualHost> directives. Something to the effect of

# compress JavaScript
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
# [ ... snip ... ]
FilterProvider  COMPRESS  DEFLATE resp=Content-Type
                         /(text/|(application/(x-)?)javascript)/
# [ ... snip ... ]
# add expires to JavaScript
ExpiresByType application/javascript    "access plus 2 months"
ExpiresByType application/x-javascript  "access plus 2 months"
ExpiresByType text/javascript           "access plus 2 months"

And I wondered why we, as developers, stand for using JavaScript with up to 3 mime/content-types when it was clearly specified 5 years ago in RFC 4329 that we’d at least like to use only one?!

From that spec:

Use of the "text" top-level type for this kind of content is
known to be problematic.  This document thus defines
text/javascript and text/ecmascript but marks them as
"obsolete".  Use of experimental and unregistered media
types, as listed in part above, is discouraged. The media
types,

   * application/javascript
   * application/ecmascript

which are also defined in this document, are intended for
common use and should be used instead.

I know that this is an informational RFC, not a standards track one, but I wanted to see how close modern (and not so modern) browsers were doing at accomplishing this goal of allowing external script files with the Content-Type: application/javascript.

Test

So, I threw together a test page to check how server-side Content-Type HTTP headers and <script type="attributes"> affected their execution. Here’s how I set up the types with Apache on my web server:

AddType application/javascript      .aj
AddType application/x-javascript    .axj
AddType text/javascript             .tj
AddType zomg/totally-fake           .ztf


And then I created a file with each of those extensions with a unique message that would document.write itself to my test page if they were execute correctly. Here’s the basic gist of the test page:

<h1>With type="XXX/YYY":</h1>
<script type="XXX/YYY" src="write.aj"></script>
<script type="XXX/YYY" src="write.axj"></script>
<script type="XXX/YYY" src="write.tj"></script>
<script type="XXX/YYY" src="write.ztf"></script>


And I also added a test with <script> tags with no type attribute at all, as per past advice I remember hearing from Doug Crockford’s site.

Results

Here were my results in a nifty table format (X = it worked!):

<script type>
application
/javascript
application
/x-javascript
text
/javascript
zomg
/totally-fake
(none)
Browser .aj .axj .tj .ztf .aj .axj .tj .ztf .aj .axj .tj .ztf .aj .axj .tj .ztf .aj .axj .tj .ztf
IE 6 (XP) X X X X X X X X
IE 7 (XP) X X X X X X X X
IE 8 (XP) X X X X X X X X
IE 9 (W7) X X X X X X X X X X X X X X X X
Safari 3 (XP) X X X X X X X X X X X X X X X X
Safari 4 (XP) X X X X X X X X X X X X X X X X
Safari 5 (XP) X X X X X X X X X X X X X X X X
Opera 8 (XP) X X X X X X X X X X X X
Opera 9 (XP) X X X X X X X X X X X X X X X X
Opera 10 (XP) X X X X X X X X X X X X X X X X
Opera 11 (W7) X X X X X X X X X X X X X X X X
Opera 11 (Ubuntu) X X X X X X X X X X X X X X X X
FF 2 (XP) X X X X X X X X X X X X X X X X
FF 3 (XP) X X X X X X X X X X X X X X X X
FF 3.6 (XP) X X X X X X X X X X X X X X X X
FF 3.6 (Ubuntu) X X X X X X X X X X X X X X X X
FF 4 (W7) X X X X X X X X X X X X X X X X
FF 4 (Linux) X X X X X X X X X X X X X X X X
Chrome 11 (XP) X X X X X X X X X X X X X X X X
Chrome 11 (W7) X X X X X X X X X X X X X X X X
Chromium 11 (Ubuntu) X X X X X X X X X X X X X X X X

Conclusion

So, given this data, I came to one very clear conclusion:
never use <script type=”zomg/totally-fake”> :P .

In addition to this epiphany, I plan only serving my JavaScript with one Content-Typeapplication/javascript as it had absolutely no impact on anything and is the right thing to do TM for the open web.

I’m also going to simply omit type="XXX/YYY" from my markup when I’m not validating my page as XHTML 1.0, as it saves bytes and works the same way (yup, even in HTML5). I do have a few sites that are valid XHTML 1.0, so it does matter in those cases, so in those cases I’ll use type="text/javascript" so I can continue passing and working in every browser possible.

Will you do the same? :)

Update:

I tested quite a few more combinations and wrote a follow-up article about a couple more things I found out here.

Posted in Compatibility, Cross-browser, JavaScript, Web | Tagged , , , , , , | 15 Comments