2010/08/08

Compressing a folder with Firefox nsIZipWriter interface

Writing an extension in Firefox is usually done in Javascript, but instead of using just the classic features available to a web page, there are many others that are exclusive to the inner workings.

One of those features introduced in Firefox 3 is nsIZipWriter; with this interface it's possible for an extension to create or modify a zip archive without the need of any external program.

And that looked perfect for some code that I was planning so I looked at it and after reading the docs page and a little testing I created this zipFolder function that takes as arguments a nsFile object that points to the folder that you want to compress, and the second argument is a callback function.

It takes the contents of that folder (including subfolders thanks to the addFolderContentsToZip recursive function) and compress them in a temporary file that you can use afterwards as it's the parameter passed to the callback function.

The only "problem" is that there is no progress notification about the task, I've configured it to do the job asynchronously (that's why it uses a callback to be notified of the end of the task), but that doesn't allow to set any progress listener or use an interval to check for the number of bytes processed.

/* Zipping functions */
const PR_RDONLY      = 0x01;
const PR_WRONLY      = 0x02;
const PR_RDWR        = 0x04;
const PR_CREATE_FILE = 0x08;
const PR_APPEND      = 0x10;
const PR_TRUNCATE    = 0x20;
const PR_SYNC        = 0x40;
const PR_EXCL        = 0x80;

/**
* folder is a nsFile pointing to a folder
* callback is a function that it's called after the zip is created. It has one parameter: the nsFile created
*/
function zipFolder(folder, callback)
{
 // get TMP directory  
 var nsFile = Cc["@mozilla.org/file/directory_service;1"].  
   getService(Ci.nsIProperties).  
   get("TmpD", Ci.nsIFile); 

 // Create a new file
 nsFile.append( folder.leafName  + ".zip");
 nsFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);  
 
 var zipWriter = Components.Constructor("@mozilla.org/zipwriter;1", "nsIZipWriter");
 var zipW = new zipWriter();
 
 zipW.open(nsFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
 
 addFolderContentsToZip(zipW, folder, "");
 
 // We don't want to block the main thread, so the zipping is done asynchronously
 // and here we get the notification that it has finished
 var observer = {
  onStartRequest: function(request, context) {},
  onStopRequest: function(request, context, status)
  {
   zipW.close();
   // Notify that we're done. 
   callback(nsFile);
  }
 }

 zipW.processQueue(observer, null);
}

/**
* function to add the contents of a folder recursively
* zipW a nsIZipWriter object
* folder a nsFile object pointing to a folder
* root a string defining the relative path for this folder in the zip
*/
function addFolderContentsToZip(zipW, folder, root)
{
 var entries = folder.directoryEntries;  
 while(entries.hasMoreElements())  
 {  
  var entry = entries.getNext();  
  entry.QueryInterface(Ci.nsIFile);  
  zipW.addEntryFile(root + entry.leafName, Ci.nsIZipWriter.COMPRESSION_DEFAULT, entry, true);
  if (entry.isDirectory())
   addFolderContentsToZip(zipW, entry, root + entry.leafName + "/");
 } 
}

Hope you find it useful, it's mostly putting together some sample code and doing some tests, but if you are trying to do it and have some problem it's usually nice to find some code that works as it might be the hint to understand your problem.

2010/08/03

SendToPhone now handles market links

Last week Patrick proposed a simple idea to workaround a limitation in the current ChromeToPhone app that doesn't allow to send anything but http links: if we use a proxy server, we can send a link to that server that when called from the phone returns the real link.

He did make some tests and worked so the next step was handling automatically market: links so that instead of Firefox asking what to do with such links, they are redirected to the phone, and thanks to the Torrent Server Handler extension we were able to learn the kind of code required to do the job. This is one of the beautiful things about open source, besides having the official docs that can be daunting for a newcomer, Patrick was able to look at that extension and we had our own market: handler.

Unfortunately it didn't seem to work with the changes in Firefox 4 about that kind of code, but I was able to re-arrange the code and created a new version that after some rewrites works in Firefox 3.x and 4.0b and also takes care of automatically enabling/disabling the handlers as soon as the corresponding preferences are toggled.

So we just had a little pending work: UI to change the protocols (check the last screenshots), some reported bugs and other minor improvements and finally we published version 1.0 yesterday. It might take some days until it's reviewed and approved, but if you are brave and want to have a big smile in your face, go to the version listing and install that 1.0

Of course, some people might be a little worried about the proxy server as it isn't using a secure connection, but as this is meant to be just a temporary workaround and we don't expect you to send military secrets with this kind of links I think that it should be OK. But in any case, you can change the url to any server that you like and you just have to put basic HTML page with just this code:

<script type="text/javascript">
 document.location = unescape( location.search.substr(5) );
</script>

and set the url in about:config for extensions.sendtophone.proxyUrl, for example with a local server you can use this url: http://192.168.0.29:8888/protocolHandler.html?url=
(the substr(5) in the javascript code is meant to remove the "?url=", so if you change that part remember to adjust the value).

If you enjoy this extension, it would be nice if you cold spend some little time to test the new version and report us if you have found any new problem so we can fix it ASAP. On the other hand if it's working fine, please write a nice review to offset the people that are still trying to install it in non-Froyo phones and write negative reviews.