Bug 552444 - Part 3: fix microsummaries code style to Places toolkit, plus minor cleanup. r=dietrich
authorMarco Bonardo <mbonardo@mozilla.com>
Tue, 23 Mar 2010 14:55:22 +0100
changeset 39741 31176eb8917b614db12e2e4deb803064be4496ca
parent 39740 9ad0cee7811bf39fa7d984ccf92bcea8351b56aa
child 39742 a7c62300bbbbf77cc65495593949c867e7a93cf2
push id12372
push usermak77@bonardo.net
push dateTue, 23 Mar 2010 13:56:06 +0000
treeherdermozilla-central@31176eb8917b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdietrich
bugs552444
milestone1.9.3a4pre
Bug 552444 - Part 3: fix microsummaries code style to Places toolkit, plus minor cleanup. r=dietrich
toolkit/components/places/src/nsMicrosummaryService.js
--- a/toolkit/components/places/src/nsMicrosummaryService.js
+++ b/toolkit/components/places/src/nsMicrosummaryService.js
@@ -56,84 +56,77 @@ const MODE_TRUNCATE = 0x20;
 const NS_ERROR_MODULE_DOM = 2152923136;
 const NS_ERROR_DOM_BAD_URI = NS_ERROR_MODULE_DOM + 1012;
 
 // How often to check for microsummaries that need updating, in milliseconds.
 const CHECK_INTERVAL = 15 * 1000; // 15 seconds
 // How often to check for generator updates, in seconds
 const GENERATOR_INTERVAL = 7 * 86400; // 1 week
 
+// The default value of the update interval, used if there is no user's pref.
+const DEFAULT_UPDATE_INTERVAL_MINUTES = 30;
+
 const MICSUM_NS = "http://www.mozilla.org/microsummaries/0.1";
 const XSLT_NS = "http://www.w3.org/1999/XSL/Transform";
 
 const ANNO_MICSUM_GEN_URI    = "microsummary/generatorURI";
 const ANNO_MICSUM_EXPIRATION = "microsummary/expiration";
 const ANNO_STATIC_TITLE      = "bookmarks/staticTitle";
 const ANNO_CONTENT_TYPE      = "bookmarks/contentType";
 
 const MAX_SUMMARY_LENGTH = 4096;
+const MAX_GENERATOR_NAME_LENGTH = 60;
+const MIN_GENERATOR_NAME_LENGTH = 6;
 
 const USER_MICROSUMMARY_GENS_DIR = "microsummary-generators";
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-__defineGetter__("NetUtil", function() {
-  delete this.NetUtil;
+XPCOMUtils.defineLazyGetter(this, "NetUtil", function() {
   Cu.import("resource://gre/modules/NetUtil.jsm");
   return NetUtil;
 });
 
 XPCOMUtils.defineLazyServiceGetter(this, "gObsSvc",
                                    "@mozilla.org/observer-service;1",
                                    "nsIObserverService");
+XPCOMUtils.defineLazyServiceGetter(this, "gPrefBranch",
+                                   "@mozilla.org/preferences-service;1",
+                                   "nsIPrefBranch");
 
 function MicrosummaryService() {
   gObsSvc.addObserver(this, "xpcom-shutdown", true);
+
+  this._ans = Cc["@mozilla.org/browser/annotation-service;1"].
+              getService(Ci.nsIAnnotationService);
   this._ans.addObserver(this, false);
-
-  Cc["@mozilla.org/preferences-service;1"].
-    getService(Ci.nsIPrefService).
-    getBranch("browser.microsummary.").
-    QueryInterface(Ci.nsIPrefBranch2).
-    addObserver("", this, true);
-
+  Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).
+                                           getBranch("browser.microsummary.").
+                                           QueryInterface(Ci.nsIPrefBranch2).
+                                           addObserver("", this, true);
   this._initTimers();
   this._cacheLocalGenerators();
 }
-
 MicrosummaryService.prototype = {
-  // Bookmarks Service
   get _bms() {
     var svc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
               getService(Ci.nsINavBookmarksService);
     this.__defineGetter__("_bms", function() svc);
     return this._bms;
   },
-
-  // Annotation Service
-  get _ans() {
-    var svc = Cc["@mozilla.org/browser/annotation-service;1"].
-              getService(Ci.nsIAnnotationService);
-    this.__defineGetter__("_ans", function() svc);
-    return this._ans;
+  get _dirs() {
+    var svc = Cc["@mozilla.org/file/directory_service;1"].
+              getService(Ci.nsIProperties);
+    this.__defineGetter__("_dirs", function() svc);
+    return this._dirs;
   },
-
-  // Directory Locator
-  __dirs: null,
-  get _dirs() {
-    if (!this.__dirs)
-      this.__dirs = Cc["@mozilla.org/file/directory_service;1"].
-                    getService(Ci.nsIProperties);
-    return this.__dirs;
-  },
-
-  // The update interval as specified by the user (defaults to 30 minutes)
+  // The update interval as specified by the user.
   get _updateInterval() {
-    var updateInterval =
-      getPref("browser.microsummary.updateInterval", 30);
+    var updateInterval = getPref("browser.microsummary.updateInterval",
+                                 DEFAULT_UPDATE_INTERVAL_MINUTES);
     // the minimum update interval is 1 minute
     return Math.max(updateInterval, 1) * 60 * 1000;
   },
 
   // A cache of local microsummary generators.  This gets built on startup
   // by the _cacheLocalGenerators() method.
   _localGenerators: {},
 
@@ -162,54 +155,53 @@ MicrosummaryService.prototype = {
         this._destroy();
         break;
       case "nsPref:changed":
         if (data == "enabled")
           this._initTimers();
         break;
     }
   },
-
-  // cross-session timer used to periodically check for generator updates.
+  // cross-session timer used to periodically check for generator or
+  // microsummaries updates.
   notify: function MSS_notify(timer) {
-    this._updateGenerators();
+    if (timer == this._timer)
+      this._updateMicrosummaries();
+    else
+      this._updateGenerators();
   },
-
   _initTimers: function MSS__initTimers() {
     if (this._timer)
       this._timer.cancel();
 
     if (!getPref("browser.microsummary.enabled", true))
       return;
 
     // Periodically update microsummaries that need updating.
     this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-    var callback = {
-      _svc: this,
-      notify: function(timer) { this._svc._updateMicrosummaries() }
-    };
-    this._timer.initWithCallback(callback,
-                                 CHECK_INTERVAL,
+    this._timer.initWithCallback(this, CHECK_INTERVAL,
                                  this._timer.TYPE_REPEATING_SLACK);
   },
-  
+
   _destroy: function MSS__destroy() {
     gObsSvc.removeObserver(this, "xpcom-shutdown", true);
     this._ans.removeObserver(this);
+    Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).
+                                             getBranch("browser.microsummary.").
+                                             QueryInterface(Ci.nsIPrefBranch2).
+                                             removeObserver("", this);
     this._timer.cancel();
     this._timer = null;
   },
 
   _updateMicrosummaries: function MSS__updateMicrosummaries() {
-    var bookmarks = this._bookmarks;
-
     var now = Date.now();
     var updateInterval = this._updateInterval;
-    for ( var i = 0; i < bookmarks.length; i++ ) {
-      var bookmarkID = bookmarks[i];
+    for (let i = 0; i < this._bookmarks.length; i++) {
+      var bookmarkID = this._bookmarks[i];
 
       // Skip this page if its microsummary hasn't expired yet.
       if (this._ans.itemHasAnnotation(bookmarkID, ANNO_MICSUM_EXPIRATION) &&
           this._ans.getItemAnnotation(bookmarkID, ANNO_MICSUM_EXPIRATION) > now)
         continue;
 
       // Reset the expiration time immediately, so if the refresh is failing
       // we don't try it every 15 seconds, potentially overloading the server.
@@ -226,21 +218,20 @@ MicrosummaryService.prototype = {
     }
   },
 
   _updateGenerators: function MSS__updateGenerators() {
     var generators = this._localGenerators;
     var update = getPref("browser.microsummary.updateGenerators", true);
     if (!generators || !update)
       return;
-
-    for (let uri in generators)
+    for (let uri in generators) {
       generators[uri].update();
+    }
   },
-
   _updateMicrosummary: function MSS__updateMicrosummary(bookmarkID, microsummary) {
     var title = this._bms.getItemTitle(bookmarkID);
 
     // Ensure the user-given title is cached
     if (!this._ans.itemHasAnnotation(bookmarkID, ANNO_STATIC_TITLE))
       this._setAnnotation(bookmarkID, ANNO_STATIC_TITLE, title);
 
     // A string identifying the bookmark to use when logging the update.
@@ -264,32 +255,30 @@ MicrosummaryService.prototype = {
     // before checking again for updates, and that can vary across updates,
     // even when the title itself hasn't changed.
     this._setAnnotation(bookmarkID, ANNO_MICSUM_EXPIRATION,
                   Date.now() + (microsummary.updateInterval || this._updateInterval));
   },
 
   /**
    * Load local generators into the cache.
-   * 
    */
   _cacheLocalGenerators: function MSS__cacheLocalGenerators() {
     // Load generators from the user's profile.
     var msDir = this._dirs.get("ProfDS", Ci.nsIFile);
     msDir.append(USER_MICROSUMMARY_GENS_DIR);
     if (msDir.exists())
       this._cacheLocalGeneratorDir(msDir);
   },
 
   /**
    * Load local generators from a directory into the cache.
    *
    * @param   dir
    *          nsIFile object pointing to directory containing generator files
-   * 
    */
   _cacheLocalGeneratorDir: function MSS__cacheLocalGeneratorDir(dir) {
     var files = dir.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
     var file = files.nextFile;
 
     while (file) {
       // Recursively load generators so support packs containing
       // lots of generators can organize them into multiple directories.
@@ -303,28 +292,30 @@ MicrosummaryService.prototype = {
     files.close();
   },
 
   /**
    * Load a local generator from a file into the cache.
    * 
    * @param   file
    *          nsIFile object pointing to file from which to load generator
-   * 
    */
   _cacheLocalGeneratorFile: function MSS__cacheLocalGeneratorFile(file) {
     var uri = NetUtil.ioService.newFileURI(file);
 
     var t = this;
     var callback =
       function MSS_cacheLocalGeneratorCallback(resource) {
-        try     { t._handleLocalGenerator(resource) }
-        finally { resource.destroy() }
+        try {
+          t._handleLocalGenerator(resource);
+        }
+        finally {
+          resource.destroy();
+        }
       };
-
     var resource = new MicrosummaryResource(uri);
     resource.load(callback);
   },
 
   _handleLocalGenerator: function MSS__handleLocalGenerator(resource) {
     if (!resource.isXML)
       throw(resource.uri.spec + " microsummary generator loaded, but not XML");
 
@@ -347,35 +338,36 @@ MicrosummaryService.prototype = {
   /**
    * Return a microsummary generator for the given URI.
    *
    * @param   generatorURI
    *          the URI of the generator
    */
   getGenerator: function MSS_getGenerator(generatorURI) {
     return this._localGenerators[generatorURI.spec] ||
-      new MicrosummaryGenerator(generatorURI);
+           new MicrosummaryGenerator(generatorURI);
   },
-
   /**
    * Install the microsummary generator from the resource at the supplied URI.
    * Callable by content via the addMicrosummaryGenerator() sidebar method.
    *
    * @param   generatorURI
    *          the URI of the resource providing the generator
-   *
    */
   addGenerator: function MSS_addGenerator(generatorURI) {
     var t = this;
     var callback =
       function MSS_addGeneratorCallback(resource) {
-        try     { t._handleNewGenerator(resource) }
-        finally { resource.destroy() }
+        try {
+          t._handleNewGenerator(resource);
+        }
+        finally {
+          resource.destroy();
+        }
       };
-
     var resource = new MicrosummaryResource(generatorURI);
     resource.load(callback);
   },
 
   _handleNewGenerator: function MSS__handleNewGenerator(resource) {
     if (!resource.isXML)
       throw(resource.uri.spec + " microsummary generator loaded, but not XML");
 
@@ -393,17 +385,16 @@ MicrosummaryService.prototype = {
  
   /**
    * Install a microsummary generator from the given XML definition.
    *
    * @param   xmlDefinition
    *          an nsIDOMDocument XML document defining the generator
    *
    * @returns the newly-installed nsIMicrosummaryGenerator generator
-   *
    */
   installGenerator: function MSS_installGenerator(xmlDefinition) {
     var rootNode = xmlDefinition.getElementsByTagNameNS(MICSUM_NS, "generator")[0];
  
     var generatorID = rootNode.getAttribute("uri");
  
     // The existing cache entry for this generator, if it is already installed.
     var generator = this._localGenerators[generatorID];
@@ -456,28 +447,25 @@ MicrosummaryService.prototype = {
    *
    * @param   pageURI
    *          the URI of the page for which to retrieve available microsummaries
    *
    * @param   bookmarkID (optional)
    *          the ID of the bookmark for which this method is being called
    *
    * @returns an nsIMicrosummarySet of nsIMicrosummaries for the given page
-   *
    */
   getMicrosummaries: function MSS_getMicrosummaries(pageURI, bookmarkID) {
     var microsummaries = new MicrosummarySet();
 
     if (!getPref("browser.microsummary.enabled", true))
       return microsummaries;
-
     // Get microsummaries defined by local generators.
-    for (var genURISpec in this._localGenerators) {
+    for (let genURISpec in this._localGenerators) {
       var generator = this._localGenerators[genURISpec];
-
       if (generator.appliesToURI(pageURI)) {
         var microsummary = new Microsummary(pageURI, generator);
 
         // If this is the current microsummary for this bookmark, load the content
         // from the datastore so it shows up immediately in microsummary picking UI.
         if (bookmarkID != -1 && this.isMicrosummary(bookmarkID, microsummary))
           microsummary._content = this._bms.getItemTitle(bookmarkID);
 
@@ -492,27 +480,34 @@ MicrosummaryService.prototype = {
       if (!microsummaries.hasItemForMicrosummary(currentMicrosummary))
         microsummaries.AppendElement(currentMicrosummary);
     }
 
     // Get microsummaries defined by the page.  If we don't have the page,
     // download it asynchronously, and then finish populating the set.
     var resource = getLoadedMicrosummaryResource(pageURI);
     if (resource) {
-      try     { microsummaries.extractFromPage(resource) }
-      finally { resource.destroy() }
+      try {
+        microsummaries.extractFromPage(resource);
+      }
+      finally {
+        resource.destroy();
+      }
     }
     else {
       // Load the page with a callback that will add the page's microsummaries
       // to the set once the page has loaded.
       var callback = function MSS_extractFromPageCallback(resource) {
-        try     { microsummaries.extractFromPage(resource) }
-        finally { resource.destroy() }
+        try {
+          microsummaries.extractFromPage(resource);
+        }
+        finally {
+          resource.destroy();
+        }
       };
-
       try {
         resource = new MicrosummaryResource(pageURI);
         resource.load(callback);
       }
       catch(e) {
         // We don't have to do anything special if the call fails besides
         // destroying the Resource object.  We can just return the list
         // of microsummaries without including page-defined microsummaries.
@@ -529,39 +524,34 @@ MicrosummaryService.prototype = {
    * Change all occurrences of a specific value in a given field to a new value.
    *
    * @param   fieldName
    *          the name of the field whose values should be changed
    * @param   oldValue
    *          the value that should be changed
    * @param   newValue
    *          the value to which it should be changed
-   *
    */
   _changeField: function MSS__changeField(fieldName, oldValue, newValue) {
-    var bookmarks = this._bookmarks;
-
-    for ( var i = 0; i < bookmarks.length; i++ ) {
-      var bookmarkID = bookmarks[i];
-
+    for (let i = 0; i < this._bookmarks.length; i++) {
+      var bookmarkID = this._bookmarks[i];
       if (this._ans.itemHasAnnotation(bookmarkID, fieldName) &&
           this._ans.getItemAnnotation(bookmarkID, fieldName) == oldValue)
         this._setAnnotation(bookmarkID, fieldName, newValue);
     }
   },
 
   /**
    * Get the set of bookmarks that have microsummaries.
    *
    * This caches the list of microsummarized bookmarks. The cache is
    * managed by observing the annotation service, and updating 
    * when a microsummary annotation is added or removed.
    *
    * @returns an array of item ids for microsummarized bookmarks
-   *
    */
   get _bookmarks() {
     var bookmarks = this._ans.getItemsWithAnnotation(ANNO_MICSUM_GEN_URI);
     this.__defineGetter__("_bookmarks", function() bookmarks);
     return this._bookmarks;
   },
 
   _setAnnotation: function MSS__setAnnotation(aBookmarkId, aFieldName, aFieldValue) {
@@ -575,31 +565,29 @@ MicrosummaryService.prototype = {
   /**
    * Get the set of bookmarks with microsummaries.
    *
    * This is the external version of this method and is accessible via XPCOM.
    * Use it outside this component. Inside the component, use _bookmarks
    * (with underscore prefix) instead for performance.
    *
    * @returns an nsISimpleEnumerator enumeration of bookmark IDs
-   *
    */
   getBookmarks: function MSS_getBookmarks() {
     return new ArrayEnumerator(this._bookmarks);
   },
 
   /**
    * Get the current microsummary for the given bookmark.
    *
    * @param   bookmarkID
    *          the bookmark for which to get the current microsummary
    *
    * @returns the current microsummary for the bookmark, or null
    *          if the bookmark does not have a current microsummary
-   *
    */
   getMicrosummary: function MSS_getMicrosummary(bookmarkID) {
     if (!this.hasMicrosummary(bookmarkID))
       return null;
 
     var pageURI = this._bms.getBookmarkURI(bookmarkID);
     var generatorURI = NetUtil.newURI(this._ans.getItemAnnotation(bookmarkID,
                                                                   ANNO_MICSUM_GEN_URI));
@@ -613,48 +601,45 @@ MicrosummaryService.prototype = {
    *
    * @param   pageURI
    *          the URI of the page to be summarized
    *
    * @param   generatorURI
    *          the URI of the microsummary generator
    *
    * @returns an nsIMicrosummary for the given page and generator URIs.
-   *
    */
   createMicrosummary: function MSS_createMicrosummary(pageURI, generatorURI) {
     var generator = this.getGenerator(generatorURI);
     return new Microsummary(pageURI, generator);
   },
 
   /**
    * Set the current microsummary for the given bookmark.
    *
    * @param   bookmarkID
    *          the bookmark for which to set the current microsummary
    *
    * @param   microsummary
    *          the microsummary to set as the current one
-   *
    */
   setMicrosummary: function MSS_setMicrosummary(bookmarkID, microsummary) {
     this._setAnnotation(bookmarkID, ANNO_MICSUM_GEN_URI, microsummary.generator.uri.spec);
 
     if (microsummary.content)
       this._updateMicrosummary(bookmarkID, microsummary);
     else
       this.refreshMicrosummary(bookmarkID);
   },
 
   /**
    * Remove the current microsummary for the given bookmark.
    *
    * @param   bookmarkID
    *          the bookmark for which to remove the current microsummary
-   *
    */
   removeMicrosummary: function MSS_removeMicrosummary(bookmarkID) {
     // Restore the user's title
     if (this._ans.itemHasAnnotation(bookmarkID, ANNO_STATIC_TITLE))
       this._bms.setItemTitle(bookmarkID, this._ans.getItemAnnotation(bookmarkID, ANNO_STATIC_TITLE));
 
     var fields = [ANNO_MICSUM_GEN_URI,
                   ANNO_MICSUM_EXPIRATION,
@@ -671,17 +656,16 @@ MicrosummaryService.prototype = {
   /**
    * Whether or not the given bookmark has a current microsummary.
    *
    * @param   bookmarkId
    *          the bookmark id to check
    *
    * @returns a boolean representing whether or not the given bookmark
    *          currently has a microsummary
-   *
    */
   hasMicrosummary: function MSS_hasMicrosummary(aBookmarkId) {
     return (this._bookmarks.indexOf(aBookmarkId) != -1);
   },
 
   /**
    * Whether or not the given microsummary is the current microsummary
    * for the given bookmark.
@@ -689,17 +673,16 @@ MicrosummaryService.prototype = {
    * @param   bookmarkID
    *          the bookmark to check
    *
    * @param   microsummary
    *          the microsummary to check
    *
    * @returns whether or not the microsummary is the current one
    *          for the bookmark
-   *
    */
   isMicrosummary: function MSS_isMicrosummary(aBookmarkID, aMicrosummary) {
     if (!aMicrosummary || !aBookmarkID)
       throw Cr.NS_ERROR_INVALID_ARG;
 
     if (this.hasMicrosummary(aBookmarkID)) {
       var currentMicrosummarry = this.getMicrosummary(aBookmarkID);
       if (aMicrosummary.equals(currentMicrosummarry))
@@ -719,17 +702,16 @@ MicrosummaryService.prototype = {
    * is "null", then it's an async refresh, and the caller should register
    * itself as an nsIMicrosummaryObserver via nsIMicrosummary.addObserver()
    * to find out when the refresh completes.
    *
    * @param   bookmarkID
    *          the bookmark whose microsummary is being refreshed
    *
    * @returns the microsummary being refreshed
-   *
    */
   refreshMicrosummary: function MSS_refreshMicrosummary(bookmarkID) {
     if (!this.hasMicrosummary(bookmarkID))
       throw "bookmark " + bookmarkID + " does not have a microsummary";
 
     var pageURI = this._bms.getBookmarkURI(bookmarkID);
     if (!pageURI)
       throw("can't get URL for bookmark with ID " + bookmarkID);
@@ -816,125 +798,100 @@ function Microsummary(aPageURI, aGenerat
   this._observers = [];
   this._pageURI = aPageURI || null;
   this._generator = aGenerator || null;
   this._content = null;
   this._pageContent = null;
   this._updateInterval = null;
   this._needsRemoval = false;
 }
-
 Microsummary.prototype = {
-  // The microsummary service.
-  __mss: null,
   get _mss() {
-    if (!this.__mss)
-      this.__mss = Cc["@mozilla.org/microsummary/service;1"].
-                   getService(Ci.nsIMicrosummaryService);
-    return this.__mss;
+    var svc = Cc["@mozilla.org/microsummary/service;1"].
+              getService(Ci.nsIMicrosummaryService);
+    this.__defineGetter__("_mss", function() svc);
+    return this._mss;
   },
-
   // nsISupports
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIMicrosummary]),
 
   // nsIMicrosummary
   get content() {
     // If we have everything we need to generate the content, generate it.
-    if (!this._content &&
-        this.generator.loaded &&
+    if (!this._content && this.generator.loaded &&
         (this.pageContent || !this.generator.needsPageContent)) {
       this._content = this.generator.generateMicrosummary(this.pageContent);
       this._updateInterval = this.generator.calculateUpdateInterval(this.pageContent);
     }
 
     // Note: we return "null" if the content wasn't already generated and we
     // couldn't retrieve it from the generated title annotation or generate it
     // ourselves.  So callers shouldn't count on getting content; instead,
     // they should call update if the return value of this getter is "null",
     // setting an observer to tell them when content generation is done.
     return this._content;
   },
-
-  get generator()            { return this._generator },
-  set generator(newValue)    { return this._generator = newValue },
-
-  get pageURI() { return this._pageURI },
-
+  get generator() this._generator,
+  set generator(newValue) this._generator = newValue,
+  get pageURI() this._pageURI,
   equals: function(aOther) {
-    if (this._generator &&
-        this._pageURI.equals(aOther.pageURI) &&
-        this._generator.equals(aOther.generator))
-      return true;
-
-    return false;
+    return this._generator &&
+           this._pageURI.equals(aOther.pageURI) &&
+           this._generator.equals(aOther.generator);
   },
-
   get pageContent() {
     if (!this._pageContent) {
       // If the page is currently loaded into a browser window, use that.
       var resource = getLoadedMicrosummaryResource(this.pageURI);
       if (resource) {
         this._pageContent = resource.content;
         resource.destroy();
       }
     }
-
     return this._pageContent;
   },
-  set pageContent(newValue) { return this._pageContent = newValue },
-
-  get updateInterval()         { return this._updateInterval; },
-  set updateInterval(newValue) { return this._updateInterval = newValue; },
-
-  get needsRemoval() { return this._needsRemoval; },
-
+  set pageContent(newValue) this._pageContent = newValue,
+  get updateInterval() this._updateInterval,
+  set updateInterval(newValue) this._updateInterval = newValue,
+  get needsRemoval() this._needsRemoval,
   // nsIMicrosummary
-
   addObserver: function MS_addObserver(observer) {
     // Register the observer, but only if it isn't already registered,
     // so that we don't call the same observer twice for any given change.
     if (this._observers.indexOf(observer) == -1)
       this._observers.push(observer);
   },
   
   removeObserver: function MS_removeObserver(observer) {
-    //NS_ASSERT(this._observers.indexOf(observer) != -1,
-    //          "can't remove microsummary observer " + observer + ": not registered");
-  
-    //this._observers =
-    //  this._observers.filter(function(i) { observer != i });
     if (this._observers.indexOf(observer) != -1)
       this._observers.splice(this._observers.indexOf(observer), 1);
   },
 
   /**
    * Regenerates the microsummary, asynchronously downloading its generator
    * and content as needed.
-   *
    */
   update: function MS_update() {
     LOG("microsummary.update called for page:\n  " + this.pageURI.spec +
         "\nwith generator:\n  " + this.generator.uri.spec);
 
     var t = this;
 
     // We use a common error callback here to flag this microsummary for removal
     // if either the generator or page content have gone permanently missing.
     var errorCallback = function MS_errorCallback(resource) {
       if (resource.status == 410) {
         t._needsRemoval = true;
         LOG("server indicated " + resource.uri.spec + " is gone. flagging for removal");
       }
-
       resource.destroy();
-
-      for (let i = 0; i < t._observers.length; i++)
+      for (let i = 0; i < t._observers.length; i++) {
         t._observers[i].onError(t);
+      }
     };
-
     // If we don't have the generator, download it now.  After it downloads,
     // we'll re-call this method to continue updating the microsummary.
     if (!this.generator.loaded) {
       // If this generator is identified by a URN, then it's a local generator
       // that should have been cached on application start, so it's missing.
       if (this.generator.uri.scheme == "urn") {
         // If it was installed via nsSidebar::addMicrosummaryGenerator (i.e. it
         // has a URN that identifies the source URL from which we installed it),
@@ -945,48 +902,56 @@ Microsummary.prototype = {
         }
         else
           throw "missing local generator: " + this.generator.uri.spec;
       }
 
       LOG("generator not yet loaded; downloading it");
       var generatorCallback =
         function MS_generatorCallback(resource) {
-          try     { t._handleGeneratorLoad(resource) }
-          finally { resource.destroy() }
+          try {
+            t._handleGeneratorLoad(resource);
+          }
+          finally {
+            resource.destroy();
+          }
         };
       var resource = new MicrosummaryResource(this.generator.uri);
       resource.load(generatorCallback, errorCallback);
       return;
     }
 
     // If we need the page content, and we don't have it, download it now.
     // Afterwards we'll re-call this method to continue updating the microsummary.
     if (this.generator.needsPageContent && !this.pageContent) {
       LOG("page content not yet loaded; downloading it");
       var pageCallback =
         function MS_pageCallback(resource) {
-          try     { t._handlePageLoad(resource) }
-          finally { resource.destroy() }
+          try {
+            t._handlePageLoad(resource);
+          }
+          finally {
+            resource.destroy();
+          }
         };
       var resource = new MicrosummaryResource(this.pageURI);
       resource.load(pageCallback, errorCallback);
       return;
     }
 
     LOG("generator (and page, if needed) both loaded; generating microsummary");
 
     // Now that we have both the generator and (if needed) the page content,
     // generate the microsummary, then let the observers know about it.
     this._content = this.generator.generateMicrosummary(this.pageContent);
     this._updateInterval = this.generator.calculateUpdateInterval(this.pageContent);
     this.pageContent = null;
-    for ( var i = 0; i < this._observers.length; i++ )
+    for (let i = 0; i < this._observers.length; i++) {
       this._observers[i].onContentLoaded(this);
-
+    }
     LOG("generated microsummary: " + this.content);
   },
 
   _handleGeneratorLoad: function MS__handleGeneratorLoad(resource) {
     LOG(this.generator.uri.spec + " microsummary generator downloaded");
     if (resource.isXML)
       this.generator.initFromXML(resource.content);
     else if (resource.contentType == "text/plain")
@@ -1007,35 +972,39 @@ Microsummary.prototype = {
 
     this.pageContent = resource.content;
     this.update();
   },
 
   /**
    * Try to reinstall a missing local generator that was originally installed
    * from a URL using nsSidebar::addMicrosummaryGenerator.
-   *
    */
   _reinstallMissingGenerator: function MS__reinstallMissingGenerator() {
     LOG("attempting to reinstall missing generator " + this.generator.uri.spec);
 
     var t = this;
-
     var loadCallback =
       function MS_missingGeneratorLoadCallback(resource) {
-        try     { t._handleMissingGeneratorLoad(resource) }
-        finally { resource.destroy() }
+        try {
+          t._handleMissingGeneratorLoad(resource);
+        }
+        finally {
+          resource.destroy();
+        }
       };
-
     var errorCallback =
       function MS_missingGeneratorErrorCallback(resource) {
-        try     { t._handleMissingGeneratorError(resource) }
-        finally { resource.destroy() }
+        try {
+          t._handleMissingGeneratorError(resource);
+        }
+        finally {
+          resource.destroy();
+        }
       };
-
     try {
       // Extract the URI from which the generator was originally installed.
       var sourceURL = this.generator.uri.path.replace(/^source:/, "");
       var sourceURI = NetUtil.newURI(sourceURL);
 
       var resource = new MicrosummaryResource(sourceURI);
       resource.load(loadCallback, errorCallback);
     }
@@ -1049,17 +1018,16 @@ Microsummary.prototype = {
    * Handle a load event for a missing local generator by trying to reinstall
    * the generator.  If this fails, call _handleMissingGeneratorError to unset
    * microsummaries for bookmarks using this generator so we don't repeatedly
    * try to reinstall the generator, creating too much traffic to the website
    * from which we downloaded it.
    *
    * @param resource
    *        the nsIMicrosummaryResource representing the downloaded generator
-   *
    */
   _handleMissingGeneratorLoad: function MS__handleMissingGeneratorLoad(resource) {
     try {
       // Make sure the generator is XML, since local generators have to be.
       if (!resource.isXML)
         throw("downloaded, but not XML " + this.generator.uri.spec);
 
       // Store the generator's ID in its XML definition.
@@ -1070,36 +1038,34 @@ Microsummary.prototype = {
       // with the newly installed generator.
       this.generator = this._mss.installGenerator(resource.content);
 
       // A reinstalled generator should always be loaded.  But just in case
       // it isn't, throw an error so we don't get into an infinite loop
       // (otherwise this._update would try again to reinstall it).
       if (!this.generator.loaded)
         throw("supposedly installed, but not in cache " + this.generator.uri.spec);
+
+      LOG("reinstall succeeded; resuming update " + this.generator.uri.spec);
+      this.update();
     }
     catch(ex) {
       Cu.reportError(ex);
       this._handleMissingGeneratorError(resource);
-      return;
     }
-  
-    LOG("reinstall succeeded; resuming update " + this.generator.uri.spec);
-    this.update();
   },
 
   /**
    * Handle an error event for a missing local generator load by unsetting
    * the microsummaries for bookmarks using this generator so we don't
    * repeatedly try to reinstall the generator, creating too much traffic
    * to the website from which we downloaded it.
    *
    * @param resource
    *        the nsIMicrosummaryResource representing the downloaded generator
-   *
    */
   _handleMissingGeneratorError: function MS__handleMissingGeneratorError(resource) {
     LOG("reinstall failed; removing microsummaries " + this.generator.uri.spec);
     var bookmarks = this._mss.getBookmarks();
     while (bookmarks.hasMoreElements()) {
       var bookmarkID = bookmarks.getNext();
       var microsummary = this._mss.getMicrosummary(bookmarkID);
       if (microsummary.generator.uri.equals(this.generator.uri)) {
@@ -1130,48 +1096,43 @@ MicrosummaryGenerator.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIMicrosummaryGenerator]),
 
   // nsIMicrosummaryGenerator
 
   // Normally this is just the URL from which we download the generator,
   // but for generators stored in the app or profile generators directory
   // it's the value of the generator tag's "uri" attribute (or its local URI
   // should the "uri" attribute be missing).
-  get uri() { return this._uri || this.localURI },
-
+  get uri() this._uri || this.localURI,
   // For generators bundled with the browser or installed by the user,
   // the local URI is the URI of the local file containing the generator XML.
-  get localURI() { return this._localURI },
-  get name() { return this._name },
-  get loaded() { return this._loaded },
-
+  get localURI() this._localURI,
+  get name() this._name,
+  get loaded() this._loaded,
   equals: function(aOther) {
     // XXX: could the uri attribute for an exposed generator ever be null?
     return aOther.uri.equals(this.uri);
   },
 
   /**
    * Determines whether or not the generator applies to a given URI.
    * By default, the generator does not apply to any URI.  In order for it
    * to apply to a URI, the URI must match one or more of the generator's
    * "include" rules and not match any of the generator's "exclude" rules.
    *
    * @param   uri
    *          the URI to test to see if this generator applies to it
    *
    * @returns boolean
    *          whether or not the generator applies to the given URI
-   *
    */
   appliesToURI: function(uri) {
     var applies = false;
-
-    for ( var i = 0 ; i < this._rules.length ; i++ ) {
+    for (let i = 0; i < this._rules.length; i++) {
       var rule = this._rules[i];
-
       switch (rule.type) {
       case "include":
         if (rule.regexp.test(uri.spec))
           applies = true;
         break;
       case "exclude":
         if (rule.regexp.test(uri.spec))
           return false;
@@ -1226,35 +1187,34 @@ MicrosummaryGenerator.prototype = {
       throw Cr.NS_ERROR_FAILURE;
 
     this._name = generatorNode.getAttribute("name");
 
     // We have to retrieve the URI from local generators via the "uri" attribute
     // of its generator tag.
     if (this.localURI && generatorNode.hasAttribute("uri"))
       this._uri = NetUtil.newURI(generatorNode.getAttribute("uri"), null, null);
-
     function getFirstChildByTagName(tagName, parentNode, namespace) {
       var nodeList = parentNode.getElementsByTagNameNS(namespace, tagName);
-      for (var i = 0; i < nodeList.length; i++) {
+      for (let i = 0; i < nodeList.length; i++) {
         // Make sure that the node is a direct descendent of the generator node
         if (nodeList[i].parentNode == parentNode)
           return nodeList[i];
       }
       return null;
     }
 
     // Slurp the include/exclude rules that determine the pages to which
     // this generator applies.  Order is important, so we add the rules
     // in the order in which they appear in the XML.
     this._rules.splice(0);
     var pages = getFirstChildByTagName("pages", generatorNode, MICSUM_NS);
     if (pages) {
       // XXX Make sure the pages tag exists.
-      for ( var i = 0; i < pages.childNodes.length ; i++ ) {
+      for (let i = 0; i < pages.childNodes.length ; i++) {
         var node = pages.childNodes[i];
         if (node.nodeType != node.ELEMENT_NODE ||
             node.namespaceURI != MICSUM_NS ||
             (node.nodeName != "include" && node.nodeName != "exclude"))
           continue;
         var urlRegexp = node.textContent.replace(/^\s+|\s+$/g, "");
         this._rules.push({ type: node.nodeName, regexp: new RegExp(urlRegexp) });
       }
@@ -1268,20 +1228,19 @@ MicrosummaryGenerator.prototype = {
         // convert from minute fractions to milliseconds
         // and ensure a minimum value of 1 minute
         return Math.round(Math.max(parseFloat(string) || 0, 1) * 60 * 1000);
       }
 
       this._unconditionalUpdateInterval =
         update.hasAttribute("interval") ?
         _parseInterval(update.getAttribute("interval")) : null;
-
       // collect the <condition expression="XPath Expression" interval="time"/> clauses
       this._updateIntervals = new Array();
-      for (i = 0; i < update.childNodes.length; i++) {
+      for (let i = 0; i < update.childNodes.length; i++) {
         node = update.childNodes[i];
         if (node.nodeType != node.ELEMENT_NODE || node.namespaceURI != MICSUM_NS ||
             node.nodeName != "condition")
           continue;
         if (!node.getAttribute("expression") || !node.getAttribute("interval")) {
           LOG("ignoring incomplete conditional update interval for " + this.uri.spec);
           continue;
         }
@@ -1296,21 +1255,18 @@ MicrosummaryGenerator.prototype = {
     if (templateNode) {
       this._template = getFirstChildByTagName("transform", templateNode, XSLT_NS) ||
                        getFirstChildByTagName("stylesheet", templateNode, XSLT_NS);
     }
     // XXX Make sure the template is a valid XSL transform sheet.
 
     this._loaded = true;
   },
-
   generateMicrosummary: function MSD_generateMicrosummary(pageContent) {
-
     var content;
-
     if (this._content)
       content = this._content;
     else if (this._template)
       content = this._processTemplate(pageContent);
     else
       throw("generateMicrosummary called on uninitialized microsummary generator");
 
     // Clean up the output
@@ -1319,18 +1275,17 @@ MicrosummaryGenerator.prototype = {
       content = content.substring(0, MAX_SUMMARY_LENGTH);
 
     return content;
   },
 
   calculateUpdateInterval: function MSD_calculateUpdateInterval(doc) {
     if (this._content || !this._updateIntervals || !doc)
       return null;
-
-    for (var i = 0; i < this._updateIntervals.length; i++) {
+    for (let i = 0; i < this._updateIntervals.length; i++) {
       try {
         if (doc.evaluate(this._updateIntervals[i].expression, doc, null,
                          Ci.nsIDOMXPathResult.BOOLEAN_TYPE, null).booleanValue)
           return this._updateIntervals[i].interval;
       }
       catch (ex) {
         Cu.reportError(ex);
         // remove the offending conditional update interval
@@ -1372,18 +1327,22 @@ MicrosummaryGenerator.prototype = {
                        createInstance(Ci.nsIFileOutputStream);
     var localFile = file.QueryInterface(Ci.nsILocalFile);
     outputStream.init(localFile, (MODE_WRONLY | MODE_TRUNCATE | MODE_CREATE),
                       PERMS_FILE, 0);
     var serializer = Cc["@mozilla.org/xmlextras/xmlserializer;1"].
                      createInstance(Ci.nsIDOMSerializer);
     serializer.serializeToStream(xmlDefinition, outputStream, null);
     if (outputStream instanceof Ci.nsISafeOutputStream) {
-      try       { outputStream.finish() }
-      catch (e) { outputStream.close()  }
+      try {
+        outputStream.finish();
+      }
+      catch (e) {
+        outputStream.close();
+      }
     }
     else
       outputStream.close();
   },
 
   update: function MSD_update() {
     // Update this generator if it was downloaded from a remote source and has
     // been modified since we last downloaded it.
@@ -1421,18 +1380,22 @@ MicrosummaryGenerator.prototype = {
     resource.lastMod = lastmod.toUTCString();
     resource.method = "HEAD";
     resource.load(loadCallback, errorCallback);
   },
 
   _performUpdate: function MSD__performUpdate(uri) {
     var t = this;
     var loadCallback = function(resource) {
-      try     { t._handleUpdateLoad(resource) }
-      finally { resource.destroy() }
+      try {
+        t._handleUpdateLoad(resource);
+      }
+      finally {
+        resource.destroy();
+      }
     };
     var errorCallback = function(resource) {
       resource.destroy();
     };
 
     var resource = new MicrosummaryResource(uri);
     resource.load(loadCallback, errorCallback);
   },
@@ -1451,97 +1414,83 @@ MicrosummaryGenerator.prototype = {
     this.saveXMLToFile(resource.content);
 
     // Let observers know we've updated this generator
     gObsSvc.notifyObservers(this, "microsummary-generator-updated", null);
   }
 };
 
 
-
-
-
-// Microsummary sets are collections of microsummaries.  They allow callers
-// to register themselves as observers of the set, and when any microsummary
-// in the set changes, the observers get notified.  Thus a caller can observe
-// the set instead of each individual microsummary.
-
+/**
+ * Microsummary sets are collections of microsummaries.  They allow callers
+ * to register themselves as observers of the set, and when any microsummary
+ * in the set changes, the observers get notified.  Thus a caller can observe
+ * the set instead of each individual microsummary.
+ */
 function MicrosummarySet() {
   this._observers = [];
   this._elements = [];
 }
 
 MicrosummarySet.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIMicrosummarySet,
                                          Ci.nsIMicrosummaryObserver]),
-
   // nsIMicrosummaryObserver
-
   onContentLoaded: function MSSet_onContentLoaded(microsummary) {
-    for ( var i = 0; i < this._observers.length; i++ )
+    for (let i = 0; i < this._observers.length; i++) {
       this._observers[i].onContentLoaded(microsummary);
+    }
   },
-
   onError: function MSSet_onError(microsummary) {
-    for ( var i = 0; i < this._observers.length; i++ )
+    for (let i = 0; i < this._observers.length; i++) {
       this._observers[i].onError(microsummary);
+    }
   },
-
   // nsIMicrosummarySet
-
   addObserver: function MSSet_addObserver(observer) {
     if (this._observers.length == 0) {
-      for ( var i = 0 ; i < this._elements.length ; i++ )
+      for (let i = 0; i < this._elements.length; i++) {
         this._elements[i].addObserver(this);
+      }
     }
-
     // Register the observer, but only if it isn't already registered,
     // so that we don't call the same observer twice for any given change.
     if (this._observers.indexOf(observer) == -1)
       this._observers.push(observer);
   },
-  
+
   removeObserver: function MSSet_removeObserver(observer) {
-    //NS_ASSERT(this._observers.indexOf(observer) != -1,
-    //          "can't remove microsummary observer " + observer + ": not registered");
-  
-    //this._observers =
-    //  this._observers.filter(function(i) { observer != i });
     if (this._observers.indexOf(observer) != -1)
       this._observers.splice(this._observers.indexOf(observer), 1);
-    
+
     if (this._observers.length == 0) {
-      for ( var i = 0 ; i < this._elements.length ; i++ )
+      for (let i = 0; i < this._elements.length; i++) {
         this._elements[i].removeObserver(this);
+      }
     }
   },
 
   extractFromPage: function MSSet_extractFromPage(resource) {
     if (!resource.isXML && resource.contentType != "text/html")
       throw("page is neither HTML nor XML");
 
     // XXX Handle XML documents, whose microsummaries are specified
     // via processing instructions.
-
     var links = resource.content.getElementsByTagName("link");
-    for ( var i = 0; i < links.length; i++ ) {
+    for (let i = 0; i < links.length; i++) {
       var link = links[i];
-
-      if(!link.hasAttribute("rel"))
+      if (!link.hasAttribute("rel"))
         continue;
-
       var relAttr = link.getAttribute("rel");
 
       // The attribute's value can be a space-separated list of link types,
       // check to see if "microsummary" is one of them.
       var linkTypes = relAttr.split(/\s+/);
-      if (!linkTypes.some( function(v) { return v.toLowerCase() == "microsummary"; }))
+      if (!linkTypes.some(function(v) {return v.toLowerCase() == "microsummary";}))
         continue;
-
-
       // Look for a TITLE attribute to give the generator a nice name in the UI.
       var linkTitle = link.getAttribute("title");
 
 
       // Unlike the "href" attribute, the "href" property contains
       // an absolute URI spec, so we use it here to create the URI.
       var generatorURI = NetUtil.newURI(link.href, resource.content.characterSet,
                                         null);
@@ -1559,55 +1508,54 @@ MicrosummarySet.prototype = {
     }
   },
 
   /**
    * Determines whether the given microsummary is already represented in the
    * set.
    */
   hasItemForMicrosummary: function MSSet_hasItemForMicrosummary(aMicrosummary) {
-    for (var i = 0; i < this._elements.length; i++) {
+    for (let i = 0; i < this._elements.length; i++) {
       if (this._elements[i].equals(aMicrosummary))
         return true;
     }
     return false;
   },
 
   // XXX Turn this into a complete implementation of nsICollection?
   AppendElement: function MSSet_AppendElement(element) {
     // Query the element to a microsummary.
     // XXX Should we NS_ASSERT if this fails?
     element = element.QueryInterface(Ci.nsIMicrosummary);
 
     if (this._elements.indexOf(element) == -1) {
       this._elements.push(element);
       element.addObserver(this);
     }
-
     // Notify observers that an element has been appended.
-    for ( var i = 0; i < this._observers.length; i++ )
+    for (let i = 0; i < this._observers.length; i++) {
       this._observers[i].onElementAppended(element);
+    }
   },
-
   Enumerate: function MSSet_Enumerate() {
     return new ArrayEnumerator(this._elements);
   }
 };
 
 
 
 
 
 /**
  * An enumeration of items in a JS array.
  * @constructor
  */
 function ArrayEnumerator(aItems) {
   if (aItems) {
-    for (var i = 0; i < aItems.length; ++i) {
+    for (let i = 0; i < aItems.length; ++i) {
       if (!aItems[i])
         aItems.splice(i--, 1);
     }
     this._contents = aItems;
   } else {
     this._contents = [];
   }
 }
@@ -1657,17 +1605,16 @@ function LOG(aText) {
  * A resource (page, microsummary, generator, etc.) identifiable by URI.
  * This object abstracts away much of the code for loading resources
  * and parsing their content if they are XML or HTML.
  * 
  * @constructor
  * 
  * @param   uri
  *          the location of the resource
- *
  */
 function MicrosummaryResource(uri) {
   // Make sure we're not loading javascript: or data: URLs, which could
   // take advantage of the load to run code with chrome: privileges.
   // XXX Perhaps use nsIScriptSecurityManager.checkLoadURI instead.
   if (!(uri.schemeIs("http") || uri.schemeIs("https") || uri.schemeIs("file")))
     throw NS_ERROR_DOM_BAD_URI;
 
@@ -1682,77 +1629,57 @@ function MicrosummaryResource(uri) {
 
   // A function to call when we finish loading/parsing the resource.
   this._loadCallback = null;
   // A function to call if we get an error while loading/parsing the resource.
   this._errorCallback = null;
   // A hidden iframe to parse HTML content.
   this._iframe = null;
 }
-
 MicrosummaryResource.prototype = {
-  get uri() {
-    return this._uri;
-  },
-
-  get content() {
-    return this._content;
-  },
-
-  get contentType() {
-    return this._contentType;
-  },
-
-  get isXML() {
-    return this._isXML;
-  },
-
-  get status()        { return this._status },
-  set status(aStatus) { this._status = aStatus },
-
-  get method()        { return this._method },
-  set method(aMethod) { this._method = aMethod },
-
-  get lastMod()     { return this._lastMod },
-  set lastMod(aMod) { this._lastMod = aMod },
-
+  get uri() this._uri,
+  get content() this._content,
+  get contentType() this._contentType,
+  get isXML() this._isXML,
+  get status() this._status,
+  set status(aStatus) this._status = aStatus,
+  get method() this._method,
+  set method(aMethod) this._method = aMethod,
+  get lastMod() this._lastMod,
+  set lastMod(aMod) this._lastMod = aMod,
   // Implement notification callback interfaces so we can suppress UI
   // and abort loads for bad SSL certs and HTTP authorization requests.
   
   // Interfaces this component implements.
   interfaces: [Ci.nsIAuthPromptProvider,
                Ci.nsIAuthPrompt,
                Ci.nsIBadCertListener2,
                Ci.nsISSLErrorListener,
                Ci.nsIPrompt,
                Ci.nsIProgressEventSink,
                Ci.nsIInterfaceRequestor,
                Ci.nsISupports],
-
   // nsISupports
-
   QueryInterface: function MSR_QueryInterface(iid) {
-    if (!this.interfaces.some( function(v) { return iid.equals(v) } ))
+    if (!this.interfaces.some(function(v) {return iid.equals(v);}))
       throw Cr.NS_ERROR_NO_INTERFACE;
-
     // nsIAuthPrompt and nsIPrompt need separate implementations because
     // their method signatures conflict.  The other interfaces we implement
     // within MicrosummaryResource itself.
     switch (iid) {
     case Ci.nsIAuthPrompt:
       return this.authPrompt;
     case Ci.nsIPrompt:
       return this.prompt;
     default:
       return this;
     }
   },
 
   // nsIInterfaceRequestor
-  
   getInterface: function MSR_getInterface(iid) {
     return this.QueryInterface(iid);
   },
 
   // nsIBadCertListener2
   // Suppress any certificate errors
   notifyCertProblem: function MSR_certProblem(socketInfo, status, targetSite) {
     return true;
@@ -1765,32 +1692,29 @@ MicrosummaryResource.prototype = {
   },
 
   
   // Suppress UI and abort loads for files secured by authentication.
 
   // Auth requests appear to succeed when we cancel them (since the server
   // redirects us to a "you're not authorized" page), so we have to set a flag
   // to let the load handler know to treat the load as a failure.
-  get _authFailed()         { return this.__authFailed; },
-  set _authFailed(newValue) { return this.__authFailed = newValue },
+  get _authFailed() this.__authFailed,
+  set _authFailed(newValue) this.__authFailed = newValue,
 
   // nsIAuthPromptProvider
-  
   getAuthPrompt: function(aPromptReason, aIID) {
     this._authFailed = true;
     throw Cr.NS_ERROR_NOT_AVAILABLE;
   },
 
   // HTTP always requests nsIAuthPromptProvider first, so it never needs
   // nsIAuthPrompt, but not all channels use nsIAuthPromptProvider, so we
   // implement nsIAuthPrompt too.
-
   // nsIAuthPrompt
-
   get authPrompt() {
     var resource = this;
     return {
       QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt]),
       prompt: function(dialogTitle, text, passwordRealm, savePassword, defaultText, result) {
         resource._authFailed = true;
         return false;
       },
@@ -1799,19 +1723,17 @@ MicrosummaryResource.prototype = {
         return false;
       },
       promptPassword: function(dialogTitle, text, passwordRealm, savePassword, pwd) {
         resource._authFailed = true;
         return false;
       }
     };
   },
-
   // nsIPrompt
-
   get prompt() {
     var resource = this;
     return {
       QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt]),
       alert: function(dialogTitle, text) {
         throw Cr.NS_ERROR_NOT_IMPLEMENTED;
       },
       alertCheck: function(dialogTitle, text, checkMessage, checkValue) {
@@ -1843,28 +1765,24 @@ MicrosummaryResource.prototype = {
     };
   },
 
   // XXX We implement nsIProgressEventSink because otherwise bug 253127
   // would cause too many extraneous errors to get reported to the console.
   // Fortunately this doesn't screw up XMLHttpRequest, because it ensures
   // that its implementation of nsIProgressEventSink will always get called
   // in addition to whatever notification callbacks we set on the channel.
-
   // nsIProgressEventSink
-
   onProgress: function(aRequest, aContext, aProgress, aProgressMax) {},
   onStatus: function(aRequest, aContext, aStatus, aStatusArg) {},
-
   /**
    * Initialize the resource from an existing DOM document object.
    * 
    * @param   document
    *          a DOM document object
-   *
    */
   initFromDocument: function MSR_initFromDocument(document) {
     this._content = document;
     this._contentType = document.contentType;
 
     // Normally we set this property based on whether or not
     // XMLHttpRequest parsed the content into an XML document object,
     // but since we already have the content, we have to analyze
@@ -1872,17 +1790,16 @@ MicrosummaryResource.prototype = {
     this._isXML = (this.contentType == "text/xml" ||
                    this.contentType == "application/xml" ||
                    /^.+\/.+\+xml$/.test(this.contentType));
   },
 
   /**
    * Destroy references to avoid leak-causing cycles.  Instantiators must call
    * this method on all instances they instantiate once they're done with them.
-   *
    */
   destroy: function MSR_destroy() {
     this._uri = null;
     this._content = null;
     this._loadCallback = null;
     this._errorCallback = null;
     this._loadTimer = null;
     this._authFailed = false;
@@ -1895,17 +1812,16 @@ MicrosummaryResource.prototype = {
 
   /**
    * Load the resource.
    * 
    * @param   loadCallback
    *          a function to invoke when the resource finishes loading
    * @param   errorCallback
    *          a function to invoke when an error occurs during the load
-   *
    */
   load: function MSR_load(loadCallback, errorCallback) {
     LOG(this.uri.spec + " loading");
   
     this._loadCallback = loadCallback;
     this._errorCallback = errorCallback;
 
     var request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
@@ -1921,57 +1837,75 @@ MicrosummaryResource.prototype = {
         if (this._self._authFailed || this._self.status >= 400) {
           // Technically the request succeeded, but we treat it as a failure,
           // since we won't be able to extract anything relevant from the result.
 
           // XXX For now HTTP is the only protocol we handle that might fail
           // auth. This message will need to change once we support FTP, which
           // returns 0 for all statuses.
           LOG(this._self.uri.spec + " load failed; HTTP status: " + this._self.status);
-          try     { this._self._handleError(event) }
-          finally { this._self = null }
+          try {
+            this._self._handleError(event);
+          }
+          finally {
+            this._self = null;
+          }
         }
         else if (event.target.channel.contentType == "multipart/x-mixed-replace") {
           // Technically the request succeeded, but we treat it as a failure,
           // since we aren't able to handle multipart content.
           LOG(this._self.uri.spec + " load failed; contains multipart content");
-          try     { this._self._handleError(event) }
-          finally { this._self = null }
+          try {
+            this._self._handleError(event);
+          }
+          finally {
+            this._self = null;
+          }
         }
         else {
           LOG(this._self.uri.spec + " load succeeded; invoking callback");
-          try     { this._self._handleLoad(event) }
-          finally { this._self = null }
+          try {
+            this._self._handleLoad(event);
+          }
+          finally {
+            this._self = null;
+          }
         }
       }
     };
 
     var errorHandler = {
       _self: this,
       handleEvent: function MSR_errorHandler_handleEvent(event) {
         if (this._self._loadTimer)
           this._self._loadTimer.cancel();
-
         LOG(this._self.uri.spec + " load failed");
-        try     { this._self._handleError(event) }
-        finally { this._self = null }
+        try {
+          this._self._handleError(event);
+        }
+        finally {
+          this._self = null;
+        }
       }
     };
-
     // cancel loads that take too long
     // timeout specified in seconds at browser.microsummary.requestTimeout,
     // or 300 seconds (five minutes)
     var timeout = getPref("browser.microsummary.requestTimeout", 300) * 1000;
     var timerObserver = {
       _self: this,
       observe: function MSR_timerObserver_observe() {
         LOG("timeout loading microsummary resource " + this._self.uri.spec + ", aborting request");
         request.abort();
-        try     { this._self.destroy() }
-        finally { this._self = null }
+        try {
+          this._self.destroy();
+        }
+        finally {
+          this._self = null;
+        }
       }
     };
     this._loadTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
     this._loadTimer.init(timerObserver, timeout, Ci.nsITimer.TYPE_ONE_SHOT);
 
     request = request.QueryInterface(Ci.nsIDOMEventTarget);
     request.addEventListener("load", loadHandler, false);
     request.addEventListener("error", errorHandler, false);
@@ -2030,28 +1964,32 @@ MicrosummaryResource.prototype = {
       this._content = request.responseText;
       this._contentType = request.channel.contentType;
       this._loadCallback(this);
     }
   },
 
   _handleError: function MSR__handleError(event) {
     // Call the error callback, then destroy ourselves to prevent memory leaks.
-    try     { if (this._errorCallback) this._errorCallback(this) } 
-    finally { this.destroy() }
+    try {
+      if (this._errorCallback)
+        this._errorCallback(this);
+    }
+    finally {
+      this.destroy();
+    }
   },
 
   /**
    * Parse a string of HTML text.  Used by _load() when it retrieves HTML.
    * We do this via hidden XUL iframes, which according to bz is the best way
    * to do it currently, since bug 102699 is hard to fix.
    * 
    * @param   htmlText
    *          a string containing the HTML content
-   *
    */
   _parse: function MSR__parse(htmlText) {
     // Find a window to stick our hidden iframe into.
     var windowMediator = Cc['@mozilla.org/appshell/window-mediator;1'].
                          getService(Ci.nsIWindowMediator);
     var window = windowMediator.getMostRecentWindow("navigator:browser");
     // XXX We can use other windows, too, so perhaps we should try to get
     // some other window if there's no browser window open.  Perhaps we should
@@ -2089,18 +2027,22 @@ MicrosummaryResource.prototype = {
     this._iframe.docShell.allowSubframes = false;
     this._iframe.docShell.allowImages = false;
     this._iframe.docShell.allowDNSPrefetch = false;
   
     var parseHandler = {
       _self: this,
       handleEvent: function MSR_parseHandler_handleEvent(event) {
         event.target.removeEventListener("DOMContentLoaded", this, false);
-        try     { this._self._handleParse(event) }
-        finally { this._self = null }
+        try {
+          this._self._handleParse(event);
+        }
+        finally {
+          this._self = null;
+        }
       }
     };
  
     // Convert the HTML text into an input stream.
     var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
                     createInstance(Ci.nsIScriptableUnicodeConverter);
     converter.charset = "UTF-8";
     var stream = converter.convertToInputStream(htmlText);
@@ -2134,51 +2076,50 @@ MicrosummaryResource.prototype = {
     uriLoader.openURI(channel, true, this._iframe.docShell);
   },
 
   /**
    * Handle a load event for the iframe-based parser.
    * 
    * @param   event
    *          the event object representing the load event
-   *
    */
   _handleParse: function MSR__handleParse(event) {
     // XXX Make sure the parse was successful?
 
     this._content = this._iframe.contentDocument;
     this._contentType = this._iframe.contentDocument.contentType;
     this._loadCallback(this);
   }
 
 };
 
 /**
  * Get a resource currently loaded into a browser window.  Checks windows
  * one at a time, starting with the frontmost (a.k.a. most recent) one.
- * 
+ *
  * @param   uri
  *          the URI of the resource
  *
  * @returns a Resource object, if the resource is currently loaded
  *          into a browser window; otherwise null
- *
  */
 function getLoadedMicrosummaryResource(uri) {
   var mediator = Cc["@mozilla.org/appshell/window-mediator;1"].
                  getService(Ci.nsIWindowMediator);
 
   // Apparently the Z order enumerator is broken on Linux per bug 156333.
   //var windows = mediator.getZOrderDOMWindowEnumerator("navigator:browser", true);
   var windows = mediator.getEnumerator("navigator:browser");
-
   while (windows.hasMoreElements()) {
     var win = windows.getNext();
+    if (win.closed)
+      continue;
     var tabBrowser = win.document.getElementById("content");
-    for ( var i = 0; i < tabBrowser.browsers.length; i++ ) {
+    for (let i = 0; i < tabBrowser.browsers.length; i++) {
       var browser = tabBrowser.browsers[i];
       if (uri.equals(browser.currentURI)) {
         var resource = new MicrosummaryResource(uri);
         resource.initFromDocument(browser.contentDocument);
         return resource;
       }
     }
   }
@@ -2190,63 +2131,42 @@ function getLoadedMicrosummaryResource(u
  * Get a value from a pref or a default value if the pref doesn't exist.
  *
  * @param   prefName
  * @param   defaultValue
  * @returns the pref's value or the default (if it is missing)
  */
 function getPref(prefName, defaultValue) {
   try {
-    var prefBranch = Cc["@mozilla.org/preferences-service;1"].
-                     getService(Ci.nsIPrefBranch);
-    var type = prefBranch.getPrefType(prefName);
+    var type = gPrefBranch.getPrefType(prefName);
     switch (type) {
-      case prefBranch.PREF_BOOL:
-        return prefBranch.getBoolPref(prefName);
-      case prefBranch.PREF_INT:
-        return prefBranch.getIntPref(prefName);
+      case gPrefBranch.PREF_BOOL:
+        return gPrefBranch.getBoolPref(prefName);
+      case gPrefBranch.PREF_INT:
+        return gPrefBranch.getIntPref(prefName);
     }
   }
-  catch (ex) { /* return the default value */ }
-  
+  catch (ex) {}
+
   return defaultValue;
 }
 
 
-// From http://lxr.mozilla.org/mozilla/source/browser/components/search/nsSearchService.js
-
 /**
- * Removes all characters not in the "chars" string from aName.
- *
- * @returns a sanitized name to be used as a filename, or a random name
- *          if a sanitized name cannot be obtained (if aName contains
- *          no valid characters).
+ * Generates a sanitized name to be used as a filename, or a random name if a
+ * sanitized name cannot be obtained (if aName contains no valid characters).
  */
 function sanitizeName(aName) {
-  const chars = "-abcdefghijklmnopqrstuvwxyz0123456789";
-  const maxLength = 60;
+  var name = aName.toLowerCase();
+  name = name.replace(/\s+/g, "-");
+  name = name.replace(/[^-a-z0-9]/g, "");
 
-  var name = aName.toLowerCase();
-  name = name.replace(/ /g, "-");
-  //name = name.split("").filter(function (el) {
-  //                               return chars.indexOf(el) != -1;
-  //                             }).join("");
-  var filteredName = "";
-  for ( var i = 0 ; i < name.length ; i++ )
-    if (chars.indexOf(name[i]) != -1)
-      filteredName += name[i];
-  name = filteredName;
+  // If our input had not enough valid characters, use a random name.
+  if (name.length < MIN_GENERATOR_NAME_LENGTH)
+    name = Math.random().toString(36).substr(2);
 
-  if (!name) {
-    // Our input had no valid characters - use a random name
-    for (var i = 0; i < 8; ++i)
-      name += chars.charAt(Math.round(Math.random() * (chars.length - 1)));
-  }
-
-  if (name.length > maxLength)
-    name = name.substring(0, maxLength);
-
-  return name;
+  // Force max length.
+  return name.substring(0, MAX_GENERATOR_NAME_LENGTH);
 }
 
 function NSGetModule(compMgr, fileSpec) {
   return XPCOMUtils.generateModule([MicrosummaryService]);
 }