MathJax

SyntaxHighlighter

Highlight

Custom CSS

Thursday, April 22, 2010

Considerations for using jQuery 1.4.2 in a Firefox 3.5 extension

Due to certain project requirements, I found myself using jQuery within a Firefox 3.5 extension, and boy was it a doozy. The problem is that most Firefox extensions just dump a bunch of code inside the script tag of an overlay on top of browser.xul (see more discussion here). The ramifications of this are that any var you declare is global to the browser and to any other extension the user has installed. Some extensions, like Firebug, get around this by prefixing every global variable with FB_, but this solution will not work with jQuery.

If I decide to include jQuery in my overlay like so:

<script src="chrome://myextension/content/jquery-1.4.2.js"></script>

...this leads to a variety of problems. Firstly, jQuery will automatically set the window.jQuery and window.$ variables in the chrome window, overriding the values that were already there. This is not a terrible problem for $ (which jQuery is nice enough to give it back to you via a call to jQuery.noConflict()), but the jQuery variable is overridden as well. This poses a problem for other not-so-nice extensions that actually use jQuery in this rude manner. For example, if I included jQuery 1.4.2 in my extension and you included jQuery 1.3.2, it would be a crapshoot to determine whose jQuery gets loaded (depends on the overlay loading order).

A second problem for jQuery 1.4.2 is that it actually manipulates the DOM to test for the availability of certain features, which is a big no-no for overlays. The DOM isn't actually available in a XUL document until after all of the overlays have loaded (after all, the overlays themselves dictate elements that should be in the DOM). If you try to access the DOM in an overlay, your overlay will silently not be loaded. Incidentally, all JavaScript in overlays should be executed by an event handler to avoid this problem:

window.addEventListener(
  "load",
  function () {
    // Do stuff after the overlay has loaded
  },
  false
);

One horrific solution to this is to load jQuery in a load handler, and edit the actual jQuery source to stop it from installing itself onto window. For the sadists out there, here's what it would look like:

window.addEventListener(
  "load",     
  function () {
    Components.classes["@mozilla.org/moz/jssubscript-loader;1"].
      getService(Components.interfaces.mozIJSSubScriptLoader).
      loadSubScript("chrome://myextension/content/jquery.js");
  },
  false
);

Of course, chrome://myextension/content/jquery.js would have to be a modified version of jQuery that simply sets the jQuery and $ in the current scope, rather than on window itself. The details of such a hack are omitted (though it's simply a 2 line change within the jQuery source). This solution; however, still allows jQuery itself to run free and wild, doing who-knows-what to the DOM after it has loaded, which is fine and dandy for a webpage but potentially ... explosive for a chrome window.

Another solution is to get jQuery loading within a Firefox code module. Code modules are "sort of" like Google Chrome's content scripts in that any code in a code module will execute in its own context (thus it is unable to stomp on code in overlays unless you explicitly tell it to), but Firefox code modules do not give you access to any sort of DOM, so loading jQuery naively will cause exceptions to be thrown.

So far, I have not found any acceptable solution to this problem. The "cleanest" possible solution may be a jsdom style fake DOM (taking advantage of the Firefox internals to provide implementations such as nsIDOMDocument, and nsIXMLHttpRequest) to fool jQuery, but that's a lot of work.

A poster on the jQuery forums, chewie1024, suggests giving jQuery a real live browser window within a code module by simply giving it a reference to the main browser window. This solution is nice and simple:

var EXPORTED_SYMBOLS = ["jQuery", "$"];

var windowMediator = Components
  .classes["@mozilla.org/appshell/window-mediator;1"]
  .getService(Components.interfaces.nsIWindowMediator);            
var enumerator = windowMediator.getEnumerator("navigator:browser");
if (enumerator.hasMoreElements()) { 
  var window = enumerator.getNext();
  var location = window.location;
  var document = window.document;
  break;
}

// jQuery source follows here...

...however, you'll still have to hack jQuery to make it not install itself on the window object, and you'll have to live with the fact that jQuery will muck around with the DOM on that window. On the flip side, you'll have jQuery in a code module that you can import into any overlay, in any scope you like.

One thing to note is that in the above solution, jQuery will keep a reference to that main window, and anything you do with jQuery will be done on that window by default. That is, make sure that you pass in a context to jQuery or it will be operating on the wrong thing. Another thing to note is that the window that jQuery keeps a reference to can be closed and some of its members could be garbage collected. Unknown behavior will result if that is the case, so keep an eye out if you use this solution (because the symptoms will be seemingly random).

In conclusion, I have no satisfactory solutions to this problem, so if anybody knows of one, please send me a message.

Monday, April 19, 2010

Maven: building a JAR with all the dependencies

One of my colleagues has requested this information of me a few times; in retrospect, it is rather a complicated thing to have to do to achieve the desired effect. Stick this into your pom.xml under the build section; it will create the JAR along bundled with all the dependencies during the package phase:

<build>
  <plugins>
    <plugin>
      <artifactId>maven-assembly-plugin</artifactId>
      <configuration>
        <appendAssemblyId>true</appendAssemblyId>
        <descriptorRefs>
          <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
        <archive>
          <manifest>
            <mainClass>my.package.Main</mainClass>
          </manifest>
        </archive>
      </configuration>
      <executions>
        <execution>
          <id>make-assembly</id>
          <phase>package</phase>
          <goals>
            <goal>assembly</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

Sunday, April 18, 2010

Hiding XUL elements on OSX on Firefox 3.5.8

I was recently confused as to why the following code would not work in a XUL overlay:

var foo = document.getElementById("foo");
foo.style.display = "none";

In this case, the element foo was a XUL element; not HTML. Further investigation found that the above code worked on Windows and Linux builds of Firefox, but not OSX; only the following appeased the Firefox gods:

var foo = document.getElementById("foo");
foo.hidden = true;

This does what you want on all platforms; not especially obvious from the austere MDC article!

Sunday, April 4, 2010

Wireless for Thinkpad X100e

There's a bit of trickery involved in getting the wireless to work on one of those sexy X100es. lspci will give this output:

03:00.0 Network controller: Realtek Semiconductor Co., Ltd. Device 8172 (rev 10)

...but the driver you want is the RTL8192E driver from Realtek's download site. make and make install it the normal way, and stick r8192se_pci into your /etc/modules.

I had a problem with WEP and WPA2 encryption; I could connect fine but after 15 minutes or so, the connection would drop and there would be no way to reconnect. I find that WPA works fine.

Update

This didn't work from me unless I used a 2.6.30 kernel.

Transferring data from one PS3 to another

Recently, I wanted to transfer my 320GB drive from my old first generation PS3 to a PS3 slim. My initial thought was: "Oh, I'll just remove the hard drive from the old one and stick it in the new one". Ha! How misguided that effort was. The data in a hard drive is tied to that system; if not, you could just clone a bunch of drives and play your friends' PSN network games for free.

My second idea was to use the PS3's backup tool to export all my data to an external drive, and then restore from my new PS3. Unfortunately, that doesn't work either because you can't restore copy protected data and games to a different system (for the same reason as above). Here's what I ended up doing:

Deactivate the old system

I had been sharing games with my girlfriend and cousins, and I realized that if I did not deactivate the old system, my games would not work on the new one. This is done through a "hidden" menu under Account Management. This is actually a pretty important step! If I had bought videos and never deactivated the system, then I'd never be able to watch them in my new system =X

In my particular case, the old system had firmware 3.15, which still retains the OtherOS feature. By the time I wanted to deactivate my system though, the new 3.21 firmware had been released, and I didn't really want to upgrade and remove that feature. I ended up spoofing the version number so I could login and deactivate my stuff.

Use the Data Transfer Utility

So Sony realized that people might want to transfer data from an old PS3 to a new one, especially if their old PS3's are getting the YLOD. Unfortunately, their solution is an extremely annoying one: connect the two PS3's via and ethernet cable and have one transfer the data over to the other.

I didn't want to lose my copy protected save data, so I did it :( The Data Transfer Utility is extremely finicky. If you don't follow these instructions, it will not work.
  1. Both PS3's must be firmware version 3.15 or greater
  2. Connect both PS3s to the TV and then connect them via a network cable (can be CAT5 or crossover)
  3. Turn them both on and use the Data Transfer Utility from the source PS3 first; don't touch the destination PS3
  4. Follow the instructions until it tells you to set the destination PS3 to receive, then do so
  5. With about 70GB of data to transfer, this took a few hours

Use the Backup Utility

After following the steps above, my new PS3 had all my old data, but it was on the Slim's 120GB drive instead of the 320GB drive I had in my old PS3. I finally used the Backup Utility to move my data to an external drive, swapped out the Slim's 120GB drive for my larger one, and then restored the backup onto the same machine.

Conclusion

Sony needs to store my savegames in a fucking cloud like Steam.