changeset 0:dada0ac40a8f

initial import
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Tue, 02 Dec 2008 20:31:01 +0900
parents
children 93e46514f20d
files LICENSE chrome.manifest chrome/nightly.jar components/nightly.xpt components/nttAddonCompatibilityService.js components/nttBreakpadService.js components/nttMultipartFormData.js defaults/preferences/nightlytools.js defaults/preferences/variables.js install.rdf modules/Logging.jsm
diffstat 11 files changed, 1143 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LICENSE	Tue Dec 02 20:31:01 2008 +0900
@@ -0,0 +1,6 @@
+All files in this extension are assumed to be licensed under the 
+tri-license (MPL/GPL/LGPL) unless otherwise specified.
+
+The MPL is available at http://www.mozilla.org/MPL/MPL-1.1.html
+
+The source code is available on request from dtownsend@oxymoronical.com
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/chrome.manifest	Tue Dec 02 20:31:01 2008 +0900
@@ -0,0 +1,25 @@
+content      nightly                                                  jar:chrome/nightly.jar!/content/
+skin         nightly          classic/1.0                             jar:chrome/nightly.jar!/skin/
+
+locale       nightly          en-US                                   jar:chrome/nightly.jar!/locale/en-US/
+
+resource     nightly          modules/
+
+overlay      chrome://mozapps/content/extensions/extensions.xul       chrome://nightly/content/extensions/addons.xul
+override     chrome://nightly/content/platform.js                     chrome://nightly/content/winPlatform.js                  os=winnt
+
+style       chrome://browser/content/browser.xul                      chrome://nightly/skin/browser.css
+style       chrome://messenger/content/messenger.xul                  chrome://nightly/skin/browser.css
+style       chrome://navigator/content/navigator.xul                  chrome://nightly/skin/browser.css
+style       chrome://calendar/content/calendar.xul                    chrome://nightly/skin/browser.css
+style       chrome://global/content/customizeToolbar.xul              chrome://nightly/skin/browser.css
+
+overlay      chrome://browser/content/browser.xul                     chrome://nightly/content/browserOverlay.xul              application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
+
+overlay     chrome://messenger/content/messenger.xul                  chrome://nightly/content/messengerOverlay.xul            application={3550f703-e582-4d05-9a08-453d09bdfdc6}
+
+overlay      chrome://calendar/content/calendar.xul                   chrome://nightly/content/calendarOverlay.xul             application={718e30fb-e89b-41dd-9da7-e25a45638b28}
+
+overlay      chrome://navigator/content/navigator.xul                 chrome://nightly/content/suiteOverlay.xul                application={92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}
+
+overlay      chrome://songbird/content/xul/layoutBaseOverlay.xul      chrome://nightly/content/songbirdOverlay.xul             application=songbird@songbirdnest.com
Binary file chrome/nightly.jar has changed
Binary file components/nightly.xpt has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/nttAddonCompatibilityService.js	Tue Dec 02 20:31:01 2008 +0900
@@ -0,0 +1,506 @@
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const PREFIX_NS_EM                    = "http://www.mozilla.org/2004/em-rdf#";
+const PREFIX_ITEM_URI                 = "urn:mozilla:item:";
+const RDFURI_INSTALL_MANIFEST_ROOT    = "urn:mozilla:install-manifest";
+const FILE_INSTALL_MANIFEST           = "install.rdf";
+const TOOLKIT_ID                      = "toolkit@mozilla.org"
+
+var gEM = null;
+var gRDF = null;
+var gApp = null;
+var gVC = null;
+var gCheckCompatibility = true;
+var gCheckUpdateSecurity = true;
+var gPrefs = null;
+
+function EM_NS(property) {
+  return PREFIX_NS_EM + property;
+}
+
+function EM_R(property) {
+  return gRDF.GetResource(EM_NS(property));
+}
+
+function getRDFProperty(ds, source, property) {
+  var value = ds.GetTarget(source, EM_R(property), true);
+  if (value && value instanceof Ci.nsIRDFLiteral)
+    return value.Value;
+  return null;
+}
+
+function removeRDFProperty(ds, source, property) {
+  var arc = EM_R(property);
+  var targets = ds.GetTargets(source, arc, true);
+  while (targets.hasMoreElements())
+    ds.Unassert(source, arc, targets.getNext());
+}
+
+function extractXPI(xpi) {
+  // XXX For 1.9 final we can switch to just extracting/compressing install.rdf
+  var zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].
+                  createInstance(Ci.nsIZipReader);
+  zipReader.open(xpi);
+  if (!zipReader.hasEntry(FILE_INSTALL_MANIFEST)) {
+    zipReader.close();
+    return null;
+  }
+  var dirs = Cc["@mozilla.org/file/directory_service;1"].
+             getService(Ci.nsIProperties);
+  var file = dirs.get("TmpD", Ci.nsILocalFile);
+  file.append("tmpxpi");
+  file.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0755);
+  var entries = zipReader.findEntries("*");
+  while (entries.hasMore()) {
+    var path = entries.getNext();
+    var entry = zipReader.getEntry(path);
+    if (path.substring(path.length - 1) == "/")
+      path = path.substring(0, entry.length - 1);
+    var parts = path.split("/");
+    var target = file.clone();
+    for (var i = 0; i < parts.length; i++)
+      target.append(parts[i]);
+    if (entry.isDirectory) {
+      if (!target.exists())
+        target.create(Ci.nsIFile.DIRECTORY_TYPE, 0755);
+    }
+    else {
+      var parent = target.parent;
+      if (!parent.exists())
+        parent.create(Ci.nsIFile.DIRECTORY_TYPE, 0755);
+      zipReader.extract(path, target);
+    }
+  }
+  zipReader.close();
+  return file;
+}
+
+function loadManifest(file) {
+  var ioServ = Cc["@mozilla.org/network/io-service;1"].
+               getService(Ci.nsIIOService);
+  var fph = ioServ.getProtocolHandler("file")
+                  .QueryInterface(Ci.nsIFileProtocolHandler);
+  return gRDF.GetDataSourceBlocking(fph.getURLSpecFromFile(file));
+}
+
+function recursiveUpdate(zipWriter, path, dir) {
+  var entries = dir.directoryEntries;
+  while (entries.hasMoreElements()) {
+    var entry = entries.getNext().QueryInterface(Ci.nsIFile);
+    if (entry.isDirectory()) {
+      var newPath = path + entry.leafName + "/";
+      zipWriter.addEntryDirectory(newPath, entry.lastModifiedTime, false);
+      recursiveUpdate(zipWriter, newPath, entry);
+    }
+    else {
+      zipWriter.addEntryFile(path + entry.leafName, Ci.nsIZipWriter.COMPRESSION_NONE,
+                             entry, false);
+    }
+  }
+}
+
+function updateXPI(xpi, file) {
+  // XXX For 1.9 final we can switch to just extracting/compressing install.rdf
+  var zipWriter = Cc["@mozilla.org/zipwriter;1"].
+                  createInstance(Ci.nsIZipWriter);
+  zipWriter.open(xpi, 0x04 | 0x08 | 0x20);
+  recursiveUpdate(zipWriter, "", file);
+  zipWriter.close();
+}
+
+function nttAddonUpdateChecker(addon) {
+  this.addon = addon;
+}
+
+nttAddonUpdateChecker.prototype = {
+  addon: null,
+  busy: null,
+
+  checkForUpdates: function() {
+    this.busy = true;
+    LOG("Searching for compatibility information for " + this.addon.id);
+    gEM.update([this.addon], 1,
+               Ci.nsIExtensionManager.UPDATE_SYNC_COMPATIBILITY, this);
+
+    // Spin an event loop to wait for the update check to complete.
+    var tm = Cc["@mozilla.org/thread-manager;1"].
+             getService(Ci.nsIThreadManager);
+    var thread = tm.currentThread;
+    while (this.busy)
+      thread.processNextEvent(true);
+  },
+
+  // nsIAddonUpdateCheckListener implementation
+  onUpdateStarted: function() {
+  },
+
+  onUpdateEnded: function() {
+    this.busy = false;
+  },
+
+  onAddonUpdateStarted: function(addon) {
+  },
+
+  onAddonUpdateEnded: function(addon, status) {
+    if (status & Ci.nsIAddonUpdateCheckListener.STATUS_DATA_FOUND) {
+      LOG("Found new compatibility information for " + addon.id + ": " + addon.minAppVersion + " " + addon.maxAppVersion);
+      this.addon.minAppVersion = addon.minAppVersion;
+      this.addon.maxAppVersion = addon.maxAppVersion;
+      this.addon.targetAppID = addon.targetAppID;
+      this.addon.overrideVersions();
+    }
+  }
+};
+
+function nttAddonDetail() {
+}
+
+nttAddonDetail.prototype = {
+  datasource: null,
+  root: null,
+
+  xpi: null,
+  file: null,
+
+  id: null,
+  name: null,
+  version: null,
+  type: Ci.nsIUpdateItem.TYPE_EXTENSION,
+  updateRDF: null,
+  updateKey: null,
+  iconURL: "chrome://mozapps/skin/xpinstall/xpinstallItemGeneric.png",
+
+  installLocationKey: null,
+  xpiURL: null,
+  xpiHash: null,
+
+  appResource: null,
+  targetAppID: null,
+  minAppVersion: null,
+  maxAppVersion: null,
+
+  init: function() {
+    if (!this.id)
+      this.id = getRDFProperty(this.datasource, this.root, "id");
+    this.name = getRDFProperty(this.datasource, this.root, "name");
+    this.version = getRDFProperty(this.datasource, this.root, "version");
+    this.updateRDF = getRDFProperty(this.datasource, this.root, "updateURL");
+    this.updateKey = getRDFProperty(this.datasource, this.root, "updateKey");
+
+    var apps = this.datasource.GetTargets(this.root, EM_R("targetApplication"), true);
+    while (apps.hasMoreElements()) {
+      var app = apps.getNext().QueryInterface(Ci.nsIRDFResource);
+      var id = getRDFProperty(this.datasource, app, "id");
+      if (id == gApp.ID || id == TOOLKIT_ID) {
+        this.minAppVersion = getRDFProperty(this.datasource, app, "minVersion");
+        this.maxAppVersion = getRDFProperty(this.datasource, app, "maxVersion");
+        if (this.minAppVersion && this.maxAppVersion) {
+          this.appResource = app;
+          this.targetAppID = id;
+          if (id == gApp.ID)
+            break;
+        }
+      }
+    }
+  },
+
+  initWithXPI: function(xpi) {
+    this.xpi = xpi;
+    this.file = extractXPI(xpi);
+    var rdf = this.file.clone();
+    rdf.append(FILE_INSTALL_MANIFEST);
+    this.datasource = loadManifest(rdf);
+    this.root = gRDF.GetResource(RDFURI_INSTALL_MANIFEST_ROOT);
+    this.init();
+  },
+
+  initWithDataSource: function(ds, root, id) {
+    this.datasource = ds;
+    this.root = root;
+    this.id = id;
+    this.init();
+  },
+
+  cleanup: function() {
+    if (this.file && this.file.exists)
+      this.file.remove(true);
+  },
+
+  overrideVersions: function() {
+    removeRDFProperty(this.datasource, this.appResource, "minVersion");
+    this.datasource.Assert(this.appResource, EM_R("minVersion"), gRDF.GetLiteral(this.minAppVersion), true);
+    removeRDFProperty(this.datasource, this.appResource, "maxVersion");
+    this.datasource.Assert(this.appResource, EM_R("maxVersion"), gRDF.GetLiteral(this.maxAppVersion), true);
+    this.datasource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
+    if (this.xpi && this.file)
+      updateXPI(this.xpi, this.file);
+  },
+
+  overrideCompatibility: function(ignorePrefs) {
+    if (!this.isValid())
+      return;
+
+    var changed = false;
+
+    if (gCheckCompatibility || ignorePrefs) {
+      var version = (gApp.ID == this.targetAppID) ? gApp.version : gApp.platformVersion;
+      if (gVC.compare(version, this.minAppVersion) < 0) {
+        LOG("minVersion " + this.minAppVersion + " is too high, reducing to " + version);
+        if (!this.datasource.GetTarget(this.appResource, EM_R("oldMinVersion"), true))
+          this.datasource.Assert(this.appResource, EM_R("oldMinVersion"), gRDF.GetLiteral(this.minAppVersion), true);
+        removeRDFProperty(this.datasource, this.appResource, "minVersion");
+        this.datasource.Assert(this.appResource, EM_R("minVersion"), gRDF.GetLiteral(version), true);
+        this.minAppVersion = version;
+        changed = true;
+      }
+      else if (gVC.compare(version, this.maxAppVersion) > 0) {
+        LOG("maxVersion " + this.maxAppVersion + " is too low, increasing to " + version);
+        if (!this.datasource.GetTarget(this.appResource, EM_R("oldMaxVersion"), true))
+          this.datasource.Assert(this.appResource, EM_R("oldMaxVersion"), gRDF.GetLiteral(this.maxAppVersion), true);
+        removeRDFProperty(this.datasource, this.appResource, "maxVersion");
+        this.datasource.Assert(this.appResource, EM_R("maxVersion"), gRDF.GetLiteral(version), true);
+        this.maxAppVersion = version;
+        changed = true;
+      }
+
+      if (changed && !this.xpi) {
+        // This updates any UI bound to the datasource
+        var compatprop = EM_R("compatible");
+        var truth = gRDF.GetLiteral("true");
+        this.datasource.Assert(this.root, compatprop, truth, true);
+        this.datasource.Unassert(this.root, compatprop, truth);
+      }
+    }
+
+    if (!this.isUpdateSecure(ignorePrefs)) {
+      LOG("Addon is insecure, removing update URL");
+      removeRDFProperty(this.datasource, this.root, "updateURL");
+      this.updateRDF = null;
+      changed = true;
+
+      // This updates any UI bound to the datasource
+      compatprop = EM_R("providesUpdatesSecurely");
+      truth = gRDF.GetLiteral("true");
+      this.datasource.Assert(this.root, compatprop, truth, true);
+      this.datasource.Unassert(this.root, compatprop, truth);
+    }
+
+    if (changed) {
+      this.datasource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
+      if (this.xpi && this.file)
+        updateXPI(this.xpi, this.file);
+    }
+  },
+
+  isValid: function() {
+    return !!this.appResource;
+  },
+
+  isCompatible: function(ignorePrefs) {
+    if (!gCheckCompatibility && !ignorePrefs)
+      return true;
+
+    var version = (gApp.ID == this.targetAppID) ? gApp.version : gApp.platformVersion;
+    if (gVC.compare(version, this.minAppVersion) < 0)
+      return false;
+    if (gVC.compare(version, this.maxAppVersion) > 0)
+      return false;
+    return true;
+  },
+
+  isUpdateSecure: function(ignorePrefs) {
+    if (!gCheckUpdateSecurity && !ignorePrefs)
+      return true;
+
+    if (!this.updateRDF)
+      return true;
+    if (this.updateKey)
+      return true;
+    return (this.updateRDF.substring(0, 6) == "https:");
+  },
+
+  needsOverride: function(ignorePrefs) {
+    return (!this.isCompatible(ignorePrefs) || !this.isUpdateSecure(ignorePrefs));
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nttIAddon, Ci.nsIUpdateItem]),
+};
+
+function nttAddonCompatibilityService() {
+}
+
+nttAddonCompatibilityService.prototype = {
+  id: null,
+
+  init: function() {
+    Components.utils.import("resource://nightly/Logging.jsm");
+
+    gEM = Cc["@mozilla.org/extensions/manager;1"].
+          getService(Ci.nsIExtensionManager);
+    gRDF = Cc["@mozilla.org/rdf/rdf-service;1"].
+           getService(Ci.nsIRDFService);
+    gApp = Cc["@mozilla.org/xre/app-info;1"].
+           getService(Ci.nsIXULAppInfo).QueryInterface(Ci.nsIXULRuntime);
+    gVC = Cc["@mozilla.org/xpcom/version-comparator;1"].
+          getService(Ci.nsIVersionComparator);
+    if (gVC.compare(gApp.platformVersion, "1.9b5") >= 0)
+      this.id = gEM.addInstallListener(this);
+    gPrefs = Components.classes["@mozilla.org/preferences-service;1"]
+                       .getService(Components.interfaces.nsIPrefService)
+                       .getBranch("extensions.")
+                       .QueryInterface(Components.interfaces.nsIPrefBranch2);
+    try {
+      gCheckCompatibility = gPrefs.getBoolPref("checkCompatibility");
+    }
+    catch (e) { }
+    try {
+      gCheckUpdateSecurity = gPrefs.getBoolPref("checkUpdateSecurity");
+    }
+    catch (e) { }
+    gPrefs.addObserver("", this, false);
+  },
+
+  // nsIAddonCompatibilityService implementation
+  getAddonForID: function(id) {
+    var addon = new nttAddonDetail();
+    addon.initWithDataSource(gEM.datasource, gRDF.GetResource(PREFIX_ITEM_URI + id), id);
+    return addon;
+  },
+
+  confirmOverride: function(addons, count) {
+    var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
+             getService(Ci.nsIWindowMediator);
+    win = wm.getMostRecentWindow("Extension:Manager");
+    if (win && win.top)
+      win = win.top;
+
+    var params = Cc["@mozilla.org/array;1"].
+                 createInstance(Ci.nsIMutableArray);
+    for (var i = 0; i < addons.length; i++)
+      params.appendElement(addons[i], false);
+    var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
+             getService(Ci.nsIWindowWatcher);
+    ww.openWindow(win, "chrome://nightly/content/extensions/incompatible.xul", "",
+                  "chrome,centerscreen,modal,dialog,titlebar", params);
+    return true;
+  },
+
+  // nsIAddonInstallListener implementation
+  onDownloadStarted: function(addon) {
+  },
+
+  onDownloadProgress: function(addon, value, maxValue) {
+  },
+
+  onDownloadEnded: function(addon) {
+  },
+
+  onInstallStarted: function(addon) {
+    LOG("Install Started for " + addon.xpiURL);
+    var ioServ = Cc["@mozilla.org/network/io-service;1"].
+                 getService(Ci.nsIIOService);
+    var fph = ioServ.getProtocolHandler("file")
+                    .QueryInterface(Ci.nsIFileProtocolHandler);
+    var file = fph.getFileFromURLSpec(addon.xpiURL);
+    if (file.exists()) {
+      try {
+        var addon = new nttAddonDetail();
+        addon.initWithXPI(file);
+        if (addon.isValid()) {
+          if (!addon.isCompatible(false)) {
+            // Check if there are remote updates available
+            var checker = new nttAddonUpdateChecker(addon);
+            checker.checkForUpdates();
+          }
+
+          if (addon.needsOverride(false))
+            this.confirmOverride([addon], 1);
+          else
+            LOG("Add-on is already compatible: '" + addon.updateRDF + "' " + addon.minAppVersion + "-" + addon.maxAppVersion);
+        }
+        else {
+          WARN("Add-on seems to be invalid");
+        }
+        addon.cleanup();
+      }
+      catch (e) {
+        ERROR("Exception during compatibility check " + e);
+      }
+    }
+  },
+
+  onCompatibilityCheckStarted: function(addon) {
+  },
+
+  onCompatibilityCheckEnded: function(addon, status) {
+  },
+
+  onInstallEnded: function(addon, status) {
+  },
+
+  onInstallsCompleted: function() {
+  },
+
+  // nsIObserver implementation
+  observe: function(subject, topic, data) {
+    switch (topic) {
+      case "app-startup":
+        var os = Cc["@mozilla.org/observer-service;1"].
+                 getService(Ci.nsIObserverService);
+        os.addObserver(this, "profile-after-change", false);
+        os.addObserver(this, "quit-application", false);
+        break;
+      case "profile-after-change":
+        this.init();
+        break;
+      case "quit-application":
+        if (this.id)
+          gEM.removeInstallListenerAt(this.id);
+        gEM = null;
+        gRDF = null;
+        gApp = null;
+        gVC = null;
+        gPrefs.removeObserver("", this);
+        gPrefs = null;
+        break;
+      case "nsPref:changed":
+        switch (data) {
+          case "checkCompatibility":
+            try {
+              gCheckCompatibility = gPrefs.getBoolPref(data);
+            }
+            catch (e) {
+              gCheckCompatibility = true;
+            }
+            break;
+          case "checkUpdateSecurity":
+            try {
+              gCheckUpdateSecurity = gPrefs.getBoolPref(data);
+            }
+            catch (e) {
+              gCheckUpdateSecurity = true;
+            }
+            break;
+        }
+        break;
+      default:
+        WARN("Unknown event " + topic);
+    }
+  },
+
+  classDescription: "Nightly Tester Install Monitor",
+  contractID: "@oxymoronical.com/nightly/addoncompatibility;1",
+  classID: Components.ID("{801207d5-037c-4565-80ed-ede8f7a7c100}"),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nttIAddonCompatibilityService, Ci.nsIAddonInstallListener, Ci.nsIObserver]),
+  _xpcom_categories: [{
+    category: "app-startup",
+    service: true
+  }]
+}
+
+function NSGetModule(compMgr, fileSpec)
+  XPCOMUtils.generateModule([nttAddonCompatibilityService]);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/nttBreakpadService.js	Tue Dec 02 20:31:01 2008 +0900
@@ -0,0 +1,265 @@
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const LOAD_DELAY = 50;
+
+Cc["@mozilla.org/moz/jssubscript-loader;1"]
+  .getService(Ci.mozIJSSubScriptLoader)
+  .loadSubScript("chrome://nightly/content/includes/tree-utils.js", null);
+
+function BP_CreateArray(source)
+{
+  var result = Cc["@mozilla.org/array;1"]
+                 .createInstance(Ci.nsIMutableArray);
+  
+  for (var key in source)
+    result.appendElement(source[key], false);
+  
+  return result;
+}
+
+function nttBreakpadIncident(file)
+{
+  this.id = file.leafName;
+  this.id = this.id.substring(0, this.id.length - 4);
+  this.date = file.lastModifiedTime;
+  this.file = file;
+}
+
+nttBreakpadIncident.prototype = {
+date: null,
+id: null,
+file: null,
+
+QueryInterface: XPCOMUtils.generateQI([Ci.nttIBreakpadIncident])
+}
+
+function nttBreakpadService() {
+  var obs = Cc["@mozilla.org/observer-service;1"]
+             .getService(Ci.nsIObserverService);
+  obs.addObserver(this, "quit-application", false);
+
+  this._dirs = [];
+  this._databases = [];
+  this._listeners = [];
+  this._incidents = [];
+  this._orderedIncidents = [];
+
+  this._findBreakpad();
+  if (this.reportdir)
+    this._dirs.push(this.reportdir);
+  else
+  {
+    this.loaded = true;
+    this._loading = true;
+  }
+}
+
+nttBreakpadService.prototype = {
+
+reportdir: null,
+
+loaded: false,
+_loading: false,
+_dirs: null,
+_databases: null,
+_loadTimer: null,
+_listeners: null,
+
+incidents: null,
+orderedIncidents: null,
+
+addProgressListener: function(listener)
+{
+  if (!this.loaded)
+    this._listeners.push(listener);
+  else
+    listener.onDatabaseLoaded();
+},
+
+loadDatabase: function()
+{
+  if (this._loading)
+    return;
+
+  this._loading = true;
+
+  if (this.reportdir && this.reportdir.exists())
+  {
+    this.incidents = [];
+    this.orderedIncidents = [];
+
+    this._loadTimer = Cc["@mozilla.org/timer;1"]
+                       .createInstance(Ci.nsITimer);
+    this._loadTimer.init(this, LOAD_DELAY, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
+  }
+  else
+    this.loaded = true;
+},
+
+observe: function(subject, topic, data)
+{
+  switch (topic)
+  {
+    case "quit-application":
+      if (this._loadTimer)
+      {
+        // Shutdown during load, clear references
+        this._loadTimer.cancel();
+        this._loadTimer = null;
+        this._databases = [];
+        this._dirs = [];
+        this._listeners = [];
+      }
+      var obs = Cc["@mozilla.org/observer-service;1"]
+                 .getService(Ci.nsIObserverService);
+      obs.removeObserver(this, "quit-application");
+      break;
+    case "timer-callback":
+      this.run();
+      break;
+  }
+},
+
+run: function()
+{
+  if (this._dirs.length>0)
+    this._scanDir(this._dirs.pop());
+  else if (this._databases.length>0)
+    this._loadDatabase(this._databases.pop());
+  else
+  {
+    this.loaded = true;
+    if (this._listeners.length == 0) {
+      this._loadTimer = null;
+      return;
+    }
+    var listener = this._listeners.pop();
+    listener.onDatabaseLoaded();
+    if (this._listeners.length == 0) {
+      this._loadTimer = null;
+      return;
+    }
+  }
+  this._loadTimer.init(this, LOAD_DELAY, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
+},
+
+_scanDir: function(dir)
+{
+  var entries = dir.directoryEntries;
+  while (entries.hasMoreElements())
+  {
+    var ndir = entries.getNext().QueryInterface(Ci.nsIFile);
+    if (ndir.isDirectory())
+      this._dirs.push(ndir);
+    else
+    {
+      var ext = ndir.leafName;
+      ext = ext.substring(ext.length - 4);
+      if (ext == ".txt")
+        this._databases.push(ndir);
+    }
+  }
+},
+
+_loadDatabase: function(database)
+{
+  var incident = new nttBreakpadIncident(database);
+  this._addIncident(incident);
+},
+
+_findBreakpad: function()
+{
+  var directoryService = Cc["@mozilla.org/file/directory_service;1"]
+                           .getService(Ci.nsIProperties);
+  var dir = directoryService.get("DefProfRt", Ci.nsIFile);
+  if (dir.leafName.toLowerCase() == "profiles")
+    dir = dir.parent;
+  dir.append("Crash Reports");
+  if (dir.exists() && dir.isDirectory())
+    this.reportdir = dir;
+},
+
+_addIncident: function(incident)
+{
+  var pos = 0;
+  while ((pos < this.orderedIncidents.length) && (this.orderedIncidents[pos].date > incident.date))
+    pos++;
+  
+  this.orderedIncidents.splice(pos, 0, incident);
+  this.incidents[incident.id]=incident;
+},
+
+getRecentIncidents: function(date)
+{
+  var result = Cc["@mozilla.org/array;1"]
+                 .createInstance(Ci.nsIMutableArray);
+  
+  for (var i = 0; i < this.orderedIncidents.length; i++)
+  {
+    if (this.orderedIncidents[i].date<date)
+      break;
+      
+    result.appendElement(this.orderedIncidents[i], false);
+  }
+  
+  return result;
+},
+
+getPreviousIncidents: function(count)
+{
+  var result = Cc["@mozilla.org/array;1"]
+                 .createInstance(Ci.nsIMutableArray);
+  
+  count=Math.min(count, this.orderedIncidents.length);
+  
+  for (var i = 0; i < count; i++)
+    result.appendElement(this.orderedIncidents[i], false);
+  
+  return result;
+},
+
+getIncident: function(id)
+{
+  return this.incidents[id];
+},
+
+getIncidents: function()
+{
+  return TB_CreateArray(this.orderedIncidents);
+},
+
+getTreeView: function()
+{
+  var share = {};
+  var tv = new XULTreeView(share);
+  tv.childData.reserveChildren(true);
+  
+  var vparent = tv.childData;
+  
+  for (var i = 0; i < this.orderedIncidents.length; i++)
+  {
+    var incident = this.orderedIncidents[i];
+    record = new XULTreeViewRecord(share);
+    record.setColumnPropertyName("incidentID", "id");
+    record.setColumnPropertyName("incidentDate", "date");
+    record.setColumnProperties("incidentID", "name incident");
+    record.id = incident.id;
+    record.date = (new Date(incident.date)).toLocaleString();
+    tv.childData.appendChild(record);
+  }
+  
+  return tv;
+},
+
+classDescription: "Nightly Tester Breakpad Service",
+contractID: "@blueprintit.co.uk/breakpad;1",
+classID: Components.ID("{b33388ca-71b4-4194-b822-2cbd0e89ffc0}"),
+QueryInterface: XPCOMUtils.generateQI([Ci.nttIBreakpadService, Ci.nsIObserver])
+}
+
+function NSGetModule(compMgr, fileSpec)
+  XPCOMUtils.generateModule([nttBreakpadService]);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/nttMultipartFormData.js	Tue Dec 02 20:31:01 2008 +0900
@@ -0,0 +1,191 @@
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+const Cr = Components.results;
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function NTT_MakeStream(data)
+{
+  var stream = Cc["@mozilla.org/io/string-input-stream;1"]
+                 .createInstance(Ci.nsIStringInputStream);
+  stream.setData(data, data.length);
+  return stream;
+}
+
+const keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+
+function NTT_decode64(input)
+{
+  var output = "";
+  var chr1, chr2, chr3;
+  var enc1, enc2, enc3, enc4;
+  var i = 0;
+
+  // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
+  input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
+
+  do
+  {
+    enc1 = keyStr.indexOf(input.charAt(i++));
+    enc2 = keyStr.indexOf(input.charAt(i++));
+    enc3 = keyStr.indexOf(input.charAt(i++));
+    enc4 = keyStr.indexOf(input.charAt(i++));
+
+    chr1 = (enc1 << 2) | (enc2 >> 4);
+    chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
+    chr3 = ((enc3 & 3) << 6) | enc4;
+
+    output = output + String.fromCharCode(chr1);
+
+    if (enc3 != 64)
+    {
+      output = output + String.fromCharCode(chr2);
+    }
+    if (enc4 != 64)
+    {
+      output = output + String.fromCharCode(chr3);
+    }
+  }
+  while (i < input.length);
+
+  return output;
+}
+
+function MultipartFormData()
+{
+  this.boundary = "hsdluicmwos";
+  this.controls = [];
+  this.files = [];
+  this.postdata = "";
+}
+
+MultipartFormData.prototype = {
+
+boundary: null,
+controls: null,
+files: null,
+length: null,
+postdata: null,
+
+getPostData: function()
+{
+  if (this.postdata)
+    return this.postdata;
+  
+  var data = "";
+  
+  for (var name in this.controls)
+  {
+    data+="\r\n--"+this.boundary+"\r\n";
+    data+="Content-Disposition: form-data; name=\""+name+"\"\r\n\r\n";
+    data+=this.controls[name];
+  }
+  
+  for (var name in this.files)
+  {
+    var filedata = this.files[name];
+    data+="\r\n--"+this.boundary+"\r\n";
+    data+="Content-Disposition: form-data; name=\""+name+"\"; filename=\""+filedata.filename+"\"\r\n";
+    data+="Content-Type: "+filedata.contenttype+"\r\n";
+    if (filedata.source)
+    {
+      data+="Content-Transfer-Encoding: base64\r\n\r\n";
+      
+      var fis = Cc["@mozilla.org/network/file-input-stream;1"]
+                  .createInstance(Ci.nsIFileInputStream);
+      fis.init(filedata.source, 1, 384, Ci.nsIFileInputStream.CLOSE_ON_EOF);
+      
+      var bis = Cc["@mozilla.org/binaryinputstream;1"]
+                  .createInstance(Ci.nsIBinaryInputStream);
+      bis.setInputStream(fis);
+      
+      //TODO this isnt needed as yet
+    }
+    else
+    {
+      data+="Content-Transfer-Encoding: binary\r\n\r\n";
+      if (filedata.encoding == "base64")
+      {
+        data+=NTT_decode64(filedata.data);
+      }
+      else if (filedata.encoding == "binary")
+      {
+        data+=filedata.data;
+      }
+    }
+  }
+  data+="\r\n--"+this.boundary+"--\r\n";
+
+  this.length = data.length-2;
+  this.postdata = data;
+  
+  return data;
+},
+  
+getPostDataStream: function()
+{
+  return NTT_MakeStream(this.getPostData());
+},
+  
+getHeaders: function()
+{
+  if (!this.length)
+    this.getPostData();
+  
+  var headers = "";
+  headers+="Content-Type: "+this.getContentType()+"\r\n";
+  headers+="Content-Length: "+this.length+"\r\n";
+  return headers;
+},
+  
+getHeaderStream: function()
+{
+  return NTT_MakeStream(this.getHeaders());
+},
+  
+getContentType: function()
+{
+  return "multipart/form-data; boundary=\""+this.boundary+"\"";
+},
+  
+addControl: function(name, value)
+{
+  this.controls[name]=value;
+  this.postdata = null;
+  this.length = null;
+},
+  
+addFile: function(name, contenttype, file)
+{
+  throw Components.results.NS_NOT_IMPLEMENTED;
+  var filedata = {
+    filename: file.leafName,
+    contenttype: contenttype,
+    source: file
+  };
+  this.files[name] = filedata;
+  this.postdata = null;
+  this.length = null;
+},
+  
+addFileData: function(name, filename, contenttype, encoding, data)
+{
+  var filedata = {
+    filename: filename,
+    contenttype: contenttype,
+    encoding: encoding,
+    data: data
+  };
+  this.files[name] = filedata;
+  this.postdata = null;
+  this.length = null;
+},
+
+classDescription: "Nightly Tester Multipart Form Data",
+contractID: "@blueprintit.co.uk/multipartformdata;1",
+classID: Components.ID("{46c8b0c6-216c-41e8-ace2-03d61783e278}"),
+QueryInterface: XPCOMUtils.generateQI([Ci.nttIMultipartFormData])
+}
+
+function NSGetModule(compMgr, fileSpec)
+  XPCOMUtils.generateModule([MultipartFormData]);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/defaults/preferences/nightlytools.js	Tue Dec 02 20:31:01 2008 +0900
@@ -0,0 +1,16 @@
+pref("nightly.idtitle", true);
+
+pref("nightly.showEnableAll", true);
+
+pref("nightly.leaks.showleaklog", false);
+pref("nightly.leaks.filterDocshell", true);
+pref("nightly.leaks.filterDocument", true);
+pref("nightly.leaks.filterWindow", true);
+pref("nightly.leaks.filterLeaked", true);
+pref("nightly.leaks.filterCollected", true);
+pref("nightly.leaks.filterIgnored", true);
+
+pref("nightly.crashreports.recentlist.display", true);
+pref("nightly.breakpad.searchurl", "http://crash-stats.mozilla.com/report/index/");
+
+pref("extensions.{8620c15f-30dc-4dba-a131-7c5d20cf4a29}.description", "chrome://nightly/locale/nightly.properties");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/defaults/preferences/variables.js	Tue Dec 02 20:31:01 2008 +0900
@@ -0,0 +1,2 @@
+pref("nightly.templates.buildid", "${UserAgent} ID:${AppBuildID}${Flags}");
+pref("nightly.templates.title", "${DefaultTitle} (Build ${AppBuildID})");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/install.rdf	Tue Dec 02 20:31:01 2008 +0900
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+  <Description about="urn:mozilla:install-manifest">
+    <em:id>{8620c15f-30dc-4dba-a131-7c5d20cf4a29}</em:id>
+    <em:version>2.0.2</em:version>
+    
+    <!-- Firefox version --> 
+    <em:targetApplication>
+      <Description>
+        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
+        <em:minVersion>3.0b5</em:minVersion>
+        <em:maxVersion>3.0pre</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+    
+    <!-- Thunderbird version -->
+    <em:targetApplication>
+      <Description>
+        <em:id>{3550f703-e582-4d05-9a08-453d09bdfdc6}</em:id>
+        <em:minVersion>3.0a1</em:minVersion>
+        <em:maxVersion>3.0a1</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+
+
+    <!-- Suiterunner version -->
+    <em:targetApplication>
+      <Description>
+        <em:id>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</em:id>
+        <em:minVersion>2.0a1pre</em:minVersion>
+        <em:maxVersion>2.0a1pre</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+	
+    <!-- Songbird version -->
+    <em:targetApplication>
+      <Description>
+        <em:id>songbird@songbirdnest.com</em:id>
+        <em:minVersion>0.6pre</em:minVersion>
+        <em:maxVersion>0.6pre</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+
+    <!-- Front End MetaData -->
+    <em:name>Nightly Tester Tools</em:name>
+    <em:description>Useful tools for the nightly tester.</em:description>
+    <em:creator>Mossop</em:creator>
+    <em:contributor>Peter van der Woude (original idea)</em:contributor>
+    <em:contributor>Jesse Ruderman (Mac fixes)</em:contributor>
+    <em:contributor>Steve England (QA)</em:contributor>
+    <em:contributor>Stephen Lau (Songbird port)</em:contributor>
+    <em:contributor>FAMFAMFAM (Some icons)</em:contributor>
+
+    <em:iconURL>chrome://nightly/content/brand/icon.png</em:iconURL>
+    <em:optionsURL>chrome://nightly/content/options/options.xul</em:optionsURL>
+    <em:homepageURL>http://www.oxymoronical.com/web/firefox/nightly</em:homepageURL>
+    
+  </Description>      
+</RDF>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/Logging.jsm	Tue Dec 02 20:31:01 2008 +0900
@@ -0,0 +1,70 @@
+var EXPORTED_SYMBOLS = ["LOG", "WARN", "ERROR"];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+
+var gLoggingEnabled = false;
+
+function logMessage(text, level) {
+  var caller = null;
+  try {
+    caller = Components.stack.caller.caller;
+  }
+  catch (e) { }
+
+  var msg = Cc["@mozilla.org/scripterror;1"].
+            createInstance(Ci.nsIScriptError);
+  if (caller) {
+    var filename = caller.filename;
+    var sourceLine = caller.sourceLine;
+    var lineNumber = caller.lineNumber;
+  }
+  else {
+    var filename = "";
+    var sourceLine = "";
+    var lineNumber = 0;
+  }
+  msg.init(text, filename, sourceLine, lineNumber, 0,
+           level, "XUL JavaScript");
+
+  var console = Cc["@mozilla.org/consoleservice;1"].
+                getService(Ci.nsIConsoleService);
+  console.logMessage(msg);
+}
+
+function ERROR(string) {
+  dump("NTT ERROR: " + string + "\n");
+  logMessage("NTT: " + string, Ci.nsIScriptError.errorFlag);
+}
+
+function WARN(string) {
+  dump("NTT WARN : " + string + "\n");
+  logMessage("NTT: " + string, Ci.nsIScriptError.warningFlag);
+}
+
+function LOG(string) {
+  if (gLoggingEnabled) {
+    dump("NTT LOG  : " + string + "\n");
+    
+    var caller = null;
+    try {
+      caller = Components.stack.caller;
+    }
+    catch (e) { }
+    
+    if (caller)
+      string += " (" + caller.filename + ":" + caller.lineNumber + ")";
+
+    var console = Cc["@mozilla.org/consoleservice;1"].
+                  getService(Ci.nsIConsoleService);
+    console.logStringMessage("NTT: " + string);
+  }
+}
+
+var prefs = Cc["@mozilla.org/preferences-service;1"].
+            getService(Ci.nsIPrefBranch);
+try {
+  gLoggingEnabled = prefs.getBoolPref("nightly.logging");
+}
+catch (e) { }