Bug 549969 - Add ability to save xml attributes from update xml that app update doesn't care about. r=dolske
authorRobert Strong <robert.bugzilla@gmail.com>
Wed, 17 Mar 2010 16:12:30 -0700
changeset 39548 c872a88a76aa9c950fb2def3ec0a5ca971dc4a81
parent 39547 6960c848f6e14483d949c128454dd073ebc107ca
child 39549 fd765b970adc49ec6a79c96c20e1fa0d5daadfcf
push id12259
push userrstrong@mozilla.com
push dateWed, 17 Mar 2010 23:13:21 +0000
treeherdermozilla-central@fd765b970adc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdolske
bugs549969
milestone1.9.3a4pre
Bug 549969 - Add ability to save xml attributes from update xml that app update doesn't care about. r=dolske
toolkit/mozapps/update/nsIUpdateService.idl
toolkit/mozapps/update/nsUpdateService.js
toolkit/mozapps/update/test/shared.js
toolkit/mozapps/update/test/unit/test_0020_general.js
toolkit/mozapps/update/test/unit/test_0060_manager.js
--- a/toolkit/mozapps/update/nsIUpdateService.idl
+++ b/toolkit/mozapps/update/nsIUpdateService.idl
@@ -108,17 +108,17 @@ interface nsIUpdatePatch : nsISupports
  * the current application - this update may have several available patches
  * from which one must be selected to download and install, for example we
  * might select a binary difference patch first and attempt to apply that,
  * then if the application process fails fall back to downloading a complete
  * file-replace patch. This object also contains information about the update
  * that the front end and other application services can use to learn more
  * about what is going on.
  */
-[scriptable, uuid(31eced2b-8adb-46f9-992b-26858ab3d558)]
+[scriptable, uuid(2379e2e1-8eab-4084-8d8c-94ffeee56804)]
 interface nsIUpdate : nsISupports
 {
   /**
    * The type of update:
    *   "major"  A major new version of the Application
    *   "minor"  A minor update to the Application (e.g. security update)
    */
   attribute AString type;
@@ -185,27 +185,16 @@ interface nsIUpdate : nsISupports
   attribute AString serviceURL;
 
   /**
    * The channel used to retrieve this update from the Update Service.
    */
   attribute AString channel;
 
   /**
-   * Stores custom string data provided by the update xml for use by the
-   * application.
-   *
-   * Implementation Note: After an update has been successfully applied this
-   * value will be added to the app.update.extra preference and if an
-   * application uses this preference to determine that an update has been
-   * applied it should also delete the preference.
-   */
-  attribute AString extra1;
-
-  /**
    * Whether to show the update prompt which requires user confirmation when an
    * update is found during a background update check. This overrides the
    * default setting to download the update in the background.
    */
   attribute boolean showPrompt;
 
   /**
    * Whether to show the "No Thanks" button in the update prompt. This allows
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -48,23 +48,23 @@ Components.utils.import("resource://gre/
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
 const PREF_APP_UPDATE_AUTO                = "app.update.auto";
 const PREF_APP_UPDATE_BACKGROUND_INTERVAL = "app.update.download.backgroundInterval";
 const PREF_APP_UPDATE_CHANNEL             = "app.update.channel";
 const PREF_APP_UPDATE_ENABLED             = "app.update.enabled";
-const PREF_APP_UPDATE_EXTRA1              = "app.update.extra1";
 const PREF_APP_UPDATE_IDLETIME            = "app.update.idletime";
 const PREF_APP_UPDATE_INCOMPATIBLE_MODE   = "app.update.incompatible.mode";
 const PREF_APP_UPDATE_INTERVAL            = "app.update.interval";
 const PREF_APP_UPDATE_LOG                 = "app.update.log";
 const PREF_APP_UPDATE_MODE                = "app.update.mode";
 const PREF_APP_UPDATE_NEVER_BRANCH        = "app.update.never.";
+const PREF_APP_UPDATE_POSTUPDATE          = "app.update.postupdate";
 const PREF_APP_UPDATE_PROMPTWAITTIME      = "app.update.promptWaitTime";
 const PREF_APP_UPDATE_SHOW_INSTALLED_UI   = "app.update.showInstalledUI";
 const PREF_APP_UPDATE_SILENT              = "app.update.silent";
 const PREF_APP_UPDATE_URL                 = "app.update.url";
 const PREF_APP_UPDATE_URL_DETAILS         = "app.update.url.details";
 const PREF_APP_UPDATE_URL_OVERRIDE        = "app.update.url.override";
 
 const PREF_PARTNER_BRANCH                 = "app.partner.";
@@ -855,46 +855,67 @@ function Update(update) {
   if (0 == this._patches.length)
     throw Cr.NS_ERROR_ILLEGAL_VALUE;
 
   for (var i = 0; i < update.attributes.length; ++i) {
     var attr = update.attributes.item(i);
     attr.QueryInterface(Ci.nsIDOMAttr);
     if (attr.value == "undefined")
       continue;
+    else if (attr.name == "detailsURL")
+      this._detailsURL = attr.value;
+    else if (attr.name == "extensionVersion") {
+      // Prevent extensionVersion from replacing appVersion if appVersion is
+      // present in the update xml.
+      if (!this.appVersion)
+        this.appVersion = attr.value;
+    }
     else if (attr.name == "installDate" && attr.value)
       this.installDate = parseInt(attr.value);
     else if (attr.name == "isCompleteUpdate")
       this.isCompleteUpdate = attr.value == "true";
     else if (attr.name == "isSecurityUpdate")
       this.isSecurityUpdate = attr.value == "true";
+    else if (attr.name == "showNeverForVersion")
+      this.showNeverForVersion = attr.value == "true";
     else if (attr.name == "showPrompt")
       this.showPrompt = attr.value == "true";
-    else if (attr.name == "showNeverForVersion")
-      this.showNeverForVersion = attr.value == "true";
     else if (attr.name == "showSurvey")
       this.showSurvey = attr.value == "true";
-    else if (attr.name == "detailsURL")
-      this._detailsURL = attr.value;
-    else if (attr.name == "channel")
-      this.channel = attr.value;
-    else if (attr.name == "extensionVersion") {
-      // Prevent extensionVersion from replacing appVersion if appVersion is
-      // present in the update xml.
-      if (!this.appVersion)
-        this.appVersion = attr.value;
-    }
     else if (attr.name == "version") {
       // Prevent version from replacing displayVersion if displayVersion is
       // present in the update xml.
       if (!this.displayVersion)
         this.displayVersion = attr.value;
     }
-    else
+    else {
       this[attr.name] = attr.value;
+
+      switch (attr.name) {
+      case "appVersion":
+      case "billboardURL":
+      case "buildID":
+      case "channel":
+      case "displayVersion":
+      case "licenseURL":
+      case "name":
+      case "platformVersion":
+      case "previousAppVersion":
+      case "serviceURL":
+      case "statusText":
+      case "type":
+        break;
+      default:
+        // Save custom attributes when serializing to the local xml file but
+        // don't use this method for the expected attributes which are already
+        // handled in serialize.
+        this.setProperty(attr.name, attr.value);
+        break;
+      };
+    }
   }
 
   // Set the initial value with the current time when it doesn't already have a
   // value or the value is already set to 0 (bug 316328).
   if (!this.installDate && this.installDate != 0)
     this.installDate = (new Date()).getTime();
 
   // The Update Name is either the string provided by the <update> element, or
@@ -982,46 +1003,46 @@ Update.prototype = {
     return this._detailsURL || "";
   },
 
   /**
    * See nsIUpdateService.idl
    */
   serialize: function Update_serialize(updates) {
     var update = updates.createElementNS(URI_UPDATE_NS, "update");
-    update.setAttribute("type", this.type);
-    update.setAttribute("name", this.name);
+    update.setAttribute("appVersion", this.appVersion);
+    update.setAttribute("buildID", this.buildID);
+    update.setAttribute("channel", this.channel);
     update.setAttribute("displayVersion", this.displayVersion);
     // for backwards compatibility in case the user downgrades
+    update.setAttribute("extensionVersion", this.appVersion);
+    update.setAttribute("installDate", this.installDate);
+    update.setAttribute("isCompleteUpdate", this.isCompleteUpdate);
+    update.setAttribute("name", this.name);
+    update.setAttribute("serviceURL", this.serviceURL);
+    update.setAttribute("showNeverForVersion", this.showNeverForVersion);
+    update.setAttribute("showPrompt", this.showPrompt);
+    update.setAttribute("showSurvey", this.showSurvey);
+    update.setAttribute("type", this.type);
+    // for backwards compatibility in case the user downgrades
+
+    // Optional attributes
     update.setAttribute("version", this.displayVersion);
-    update.setAttribute("appVersion", this.appVersion);
-    update.setAttribute("buildID", this.buildID);
-    // for backwards compatibility in case the user downgrades
-    update.setAttribute("extensionVersion", this.appVersion);
+    if (this.billboardURL)
+      update.setAttribute("billboardURL", this.billboardURL);
+    if (this.detailsURL)
+      update.setAttribute("detailsURL", this.detailsURL);
+    if (this.licenseURL)
+      update.setAttribute("licenseURL", this.licenseURL);
     if (this.platformVersion)
       update.setAttribute("platformVersion", this.platformVersion);
     if (this.previousAppVersion)
       update.setAttribute("previousAppVersion", this.previousAppVersion);
-    if (this.detailsURL)
-      update.setAttribute("detailsURL", this.detailsURL);
-    if (this.billboardURL)
-      update.setAttribute("billboardURL", this.billboardURL);
-    if (this.licenseURL)
-      update.setAttribute("licenseURL", this.licenseURL);
-    update.setAttribute("serviceURL", this.serviceURL);
-    update.setAttribute("installDate", this.installDate);
     if (this.statusText)
       update.setAttribute("statusText", this.statusText);
-    update.setAttribute("isCompleteUpdate", this.isCompleteUpdate);
-    update.setAttribute("channel", this.channel);
-    update.setAttribute("showPrompt", this.showPrompt);
-    update.setAttribute("showSurvey", this.showSurvey);
-    update.setAttribute("showNeverForVersion", this.showNeverForVersion);
-    if (this.extra1)
-      update.setAttribute("extra1", this.extra1);
     updates.documentElement.appendChild(update);
 
     for (var p in this._properties) {
       if (this._properties[p].present)
         update.setAttribute(p, this._properties[p].data);
     }
 
     for (var i = 0; i < this.patchCount; ++i)
@@ -1192,18 +1213,17 @@ UpdateService.prototype = {
                    createInstance(Ci.nsIUpdatePrompt);
 
     update.state = status;
     if (status == STATE_SUCCEEDED) {
       update.statusText = gUpdateBundle.GetStringFromName("installSuccess");
 
       // Update the patch's metadata.
       um.activeUpdate = update;
-      gPref.setCharPref(PREF_APP_UPDATE_EXTRA1,
-                        update.extra1 ? update.extra1 : "undefined");
+      gPref.setBoolPref(PREF_APP_UPDATE_POSTUPDATE, true);
       prompter.showUpdateInstalled();
 
       // Done with this update. Clean it up.
       cleanupActiveUpdate();
     }
     else {
       // If we hit an error, then the error code will be included in the
       // status string following a colon.  If we had an I/O error, then we
--- a/toolkit/mozapps/update/test/shared.js
+++ b/toolkit/mozapps/update/test/shared.js
@@ -185,22 +185,23 @@ function getRemoteUpdatesXMLString(aUpda
  * file.
  * See getUpdateString
  * @returns The string representing an update element for an update xml file.
  */
 function getRemoteUpdateString(aPatches, aType, aName, aDisplayVersion,
                                aAppVersion, aPlatformVersion, aBuildID,
                                aDetailsURL, aBillboardURL, aLicenseURL,
                                aShowPrompt, aShowNeverForVersion, aShowSurvey,
-                               aExtra1, aVersion, aExtensionVersion) {
+                               aVersion, aExtensionVersion, aCustom1,
+                               aCustom2) {
   return  getUpdateString(aType, aName, aDisplayVersion, aAppVersion,
                           aPlatformVersion, aBuildID, aDetailsURL,
                           aBillboardURL, aLicenseURL, aShowPrompt,
-                          aShowNeverForVersion, aShowSurvey, aExtra1, aVersion,
-                          aExtensionVersion) + ">\n" +
+                          aShowNeverForVersion, aShowSurvey, aVersion,
+                          aExtensionVersion, aCustom1, aCustom2) + ">\n" +
               aPatches + 
          "  </update>\n";
 }
 
 /**
  * Constructs a string representing a patch element for a remote update xml
  * file
  * See getPatchString
@@ -250,30 +251,31 @@ function getLocalUpdatesXMLString(aUpdat
  * @returns The string representing an update element for an update xml file.
  */
 function getLocalUpdateString(aPatches, aType, aName, aDisplayVersion,
                               aAppVersion, aPlatformVersion, aBuildID,
                               aDetailsURL, aBillboardURL, aLicenseURL,
                               aServiceURL, aInstallDate, aStatusText,
                               aIsCompleteUpdate, aChannel, aForegroundDownload,
                               aShowPrompt, aShowNeverForVersion, aShowSurvey,
-                              aExtra1, aVersion, aExtensionVersion,
-                              aPreviousAppVersion) {
+                              aVersion, aExtensionVersion, aPreviousAppVersion,
+                              aCustom1, aCustom2) {
   var serviceURL = aServiceURL ? aServiceURL : "http://test_service/";
   var installDate = aInstallDate ? aInstallDate : "1238441400314";
   var statusText = aStatusText ? aStatusText : "Install Pending";
   var isCompleteUpdate = typeof(aIsCompleteUpdate) == "string" ? aIsCompleteUpdate : "true";
   var channel = aChannel ? aChannel : "test_channel";
   var foregroundDownload =
     typeof(aForegroundDownload) == "string" ? aForegroundDownload : "true";
   var previousAppVersion = aPreviousAppVersion ? "previousAppVersion=\"" + aPreviousAppVersion + "\" " : "";
   return getUpdateString(aType, aName, aDisplayVersion, aAppVersion,
                          aPlatformVersion, aBuildID, aDetailsURL, aBillboardURL,
                          aLicenseURL, aShowPrompt, aShowNeverForVersion,
-                         aShowSurvey, aExtra1, aVersion, aExtensionVersion) +
+                         aShowSurvey, aVersion, aExtensionVersion, aCustom1,
+                         aCustom2) +
                    " " +
                    previousAppVersion +
                    "serviceURL=\"" + serviceURL + "\" " +
                    "installDate=\"" + installDate + "\" " +
                    "statusText=\"" + statusText + "\" " +
                    "isCompleteUpdate=\"" + isCompleteUpdate + "\" " +
                    "channel=\"" + channel + "\" " +
                    "foregroundDownload=\"" + foregroundDownload + "\">"  +
@@ -338,32 +340,37 @@ function getLocalPatchString(aType, aURL
  *          enabled.
  *          If null will not be added and the backend will default to false.
  * @param   aShowNeverForVersion
  *          Whether to show the 'No Thanks' button in the update prompt.
  *          If null will not be added and the backend will default to false.
  * @param   aShowSurvey
  *          Whether to show the 'No Thanks' button in the update prompt.
  *          If null will not be added and the backend will default to false.
- * @param   aExtra1
- *          A custom string provided by the update xml for use by the
- *          application.
- *          If null will not be added.
  * @param   aVersion
  *          The update's application version from 1.9.2.
  *          If null will not be present.
  * @param   aExtensionVersion
  *          The update's application version from 1.9.2.
  *          If null will not be present.
+ * @param   aCustom1
+ *          A custom attribute name AND attribute value to add to the xml.
+ *          Example: custom1_attribute="custom1 value"
+ *          If null will not be present.
+ * @param   aCustom2
+ *          A custom attribute name AND attribute value to add to the xml.
+ *          Example: custom2_attribute="custom2 value"
+ *          If null will not be present.
  * @returns The string representing an update element for an update xml file.
  */
 function getUpdateString(aType, aName, aDisplayVersion, aAppVersion,
                          aPlatformVersion, aBuildID, aDetailsURL, aBillboardURL,
                          aLicenseURL, aShowPrompt, aShowNeverForVersion,
-                         aShowSurvey, aExtra1, aVersion, aExtensionVersion) {
+                         aShowSurvey, aVersion, aExtensionVersion, aCustom1,
+                         aCustom2) {
   var type = aType ? aType : "major";
   var name = aName ? aName : "App Update Test";
   var displayVersion = "";
   if (aDisplayVersion || !aVersion) {
     displayVersion = "displayVersion=\"" +
                      (aDisplayVersion ? aDisplayVersion
                                       : "version 99.0") + "\" ";
   }
@@ -385,32 +392,34 @@ function getUpdateString(aType, aName, a
   // XXXrstrong - not specifying a detailsURL will cause a leak due to bug 470244
 //   var detailsURL = aDetailsURL ? "detailsURL=\"" + aDetailsURL + "\" " : "";
   var detailsURL = "detailsURL=\"" + (aDetailsURL ? aDetailsURL : "http://test_details/") + "\" ";
   var billboardURL = aBillboardURL ? "billboardURL=\"" + aBillboardURL + "\" " : "";
   var licenseURL = aLicenseURL ? "licenseURL=\"" + aLicenseURL + "\" " : "";
   var showPrompt = aShowPrompt ? "showPrompt=\"" + aShowPrompt + "\" " : "";
   var showNeverForVersion = aShowNeverForVersion ? "showNeverForVersion=\"" + aShowNeverForVersion + "\" " : "";
   var showSurvey = aShowSurvey ? "showSurvey=\"" + aShowSurvey + "\" " : "";
-  var extra1 = aExtra1 ? "extra1=\"" + aExtra1 + "\" " : "";
+  var custom1 = aCustom1 ? aCustom1 + " " : "";
+  var custom2 = aCustom2 ? aCustom2 + " " : "";
   return "  <update type=\"" + type + "\" " +
                    "name=\"" + name + "\" " +
                    displayVersion +
                    version +
                    appVersion +
                    extensionVersion +
                    platformVersion +
-                   "buildID=\"" + buildID + "\" " +
                    detailsURL +
                    billboardURL +
                    licenseURL +
                    showPrompt +
                    showNeverForVersion +
                    showSurvey +
-                   extra1;
+                   custom1 +
+                   custom2 +
+                   "buildID=\"" + buildID + "\"";
 }
 
 /**
  * Constructs a string representing a patch element for an update xml file.
  * @param   aType
  *          The patch's type which should be complete or partial.
  *          If null will default to 'complete'.
  * @param   aURL
--- a/toolkit/mozapps/update/test/unit/test_0020_general.js
+++ b/toolkit/mozapps/update/test/unit/test_0020_general.js
@@ -110,18 +110,19 @@ function run_test_pt02() {
                                   "e6678ca40ae7582316acdeddf3c133c9c8577de4",
                                   "1316138");
   var updates = getRemoteUpdateString(patches, "minor", "Minor Test",
                                       "version 2.1a1pre", "2.1a1pre",
                                       "3.1a1pre", "20080811053724",
                                       "http://details/",
                                       "http://billboard/",
                                       "http://license/", "true",
-                                      "true", "true", "test extra1",
-                                      "4.1a1pre", "5.1a1pre");
+                                      "true", "true", "4.1a1pre", "5.1a1pre",
+                                      "custom1_attr=\"custom1 value\"",
+                                      "custom2_attr=\"custom2 value\"");
   gResponseBody = getRemoteUpdatesXMLString(updates);
   gUpdateChecker.checkForUpdates(updateCheckListener, true);
 }
 
 function check_test_pt02() {
   // XXXrstrong - not specifying a detailsURL will cause a leak due to bug 470244
   // and until this is fixed this will not test the value for detailsURL when it
   // isn't specified in the update xml.
@@ -133,44 +134,46 @@ function check_test_pt02() {
 //                    getService(AUS_Ci.nsIURLFormatter);
 //    defaultDetailsURL = formatter.formatURLPref(PREF_APP_UPDATE_URL_DETAILS);
 //  }
 //  catch (e) {
 //    defaultDetailsURL = "";
 //  }
 
   do_check_eq(gUpdateCount, 1);
-  var bestUpdate = gAUS.selectUpdate(gUpdates, gUpdateCount);
+  var bestUpdate = gAUS.selectUpdate(gUpdates, gUpdateCount).QueryInterface(AUS_Ci.nsIPropertyBag);
   do_check_eq(bestUpdate.type, "minor");
   do_check_eq(bestUpdate.name, "Minor Test");
   do_check_eq(bestUpdate.displayVersion, "version 2.1a1pre");
   do_check_eq(bestUpdate.appVersion, "2.1a1pre");
   do_check_eq(bestUpdate.platformVersion, "3.1a1pre");
   do_check_eq(bestUpdate.buildID, "20080811053724");
   do_check_eq(bestUpdate.detailsURL, "http://details/");
   do_check_eq(bestUpdate.billboardURL, "http://billboard/");
   do_check_eq(bestUpdate.licenseURL, "http://license/");
   do_check_true(bestUpdate.showPrompt);
   do_check_true(bestUpdate.showNeverForVersion);
   do_check_true(bestUpdate.showSurvey);
-  do_check_eq(bestUpdate.extra1, "test extra1");
   do_check_eq(bestUpdate.serviceURL, URL_HOST + "update.xml?force=1");
   do_check_eq(bestUpdate.channel, "test_channel");
   do_check_false(bestUpdate.isCompleteUpdate);
   do_check_false(bestUpdate.isSecurityUpdate);
   // Check the date is approximately equal
   do_check_neq(bestUpdate.installDate, 0);
   do_check_eq(bestUpdate.statusText, null);
   // nsIUpdate:state returns an empty string when no action has been performed
   // on an available update
   do_check_eq(bestUpdate.state, "");
   do_check_eq(bestUpdate.errorCode, 0);
   do_check_eq(bestUpdate.patchCount, 2);
   //XXX TODO - test nsIUpdate:serialize
 
+  do_check_eq(bestUpdate.getProperty("custom1_attr"), "custom1 value");
+  do_check_eq(bestUpdate.getProperty("custom2_attr"), "custom2 value");
+
   var patch = bestUpdate.getPatchAt(0);
   do_check_eq(patch.type, "complete");
   do_check_eq(patch.URL, "http://complete/");
   do_check_eq(patch.hashFunction, "SHA1");
   do_check_eq(patch.hashValue, "98db9dad8e1d80eda7e1170d0187d6f53e477059");
   do_check_eq(patch.size, 9856459);
   // The value for patch.state can be the string 'null' as a valid value. This
   // is confusing if it returns null which is an invalid value since the test
@@ -203,19 +206,17 @@ function run_test_pt03() {
   gCheckFunc = check_test_pt03;
   var patches = getRemotePatchString("complete", "http://complete/", "SHA1",
                                      "98db9dad8e1d80eda7e1170d0187d6f53e477059",
                                      "9856459");
   var updates = getRemoteUpdateString(patches, "major", "Major Test",
                                       null, null,
                                       "5.1a1pre", "20080811053724",
                                       "http://details/",
-                                      null,
-                                      null, null,
-                                      null, null, null,
+                                      null, null, null, null, null,
                                       "version 4.1a1pre", "4.1a1pre");
   gResponseBody = getRemoteUpdatesXMLString(updates);
   gUpdateChecker.checkForUpdates(updateCheckListener, true);
 }
 
 function check_test_pt03() {
   do_check_eq(gUpdateCount, 1);
   var bestUpdate = gAUS.selectUpdate(gUpdates, gUpdateCount);
@@ -226,17 +227,16 @@ function check_test_pt03() {
   do_check_eq(bestUpdate.platformVersion, "5.1a1pre");
   do_check_eq(bestUpdate.buildID, "20080811053724");
   do_check_eq(bestUpdate.detailsURL, "http://details/");
   do_check_eq(bestUpdate.billboardURL, null);
   do_check_eq(bestUpdate.licenseURL, null);
   do_check_false(bestUpdate.showPrompt);
   do_check_false(bestUpdate.showNeverForVersion);
   do_check_false(bestUpdate.showSurvey);
-  do_check_eq(bestUpdate.extra1, null);
   do_check_eq(bestUpdate.serviceURL, URL_HOST + "update.xml?force=1");
   do_check_eq(bestUpdate.channel, "test_channel");
   do_check_false(bestUpdate.isCompleteUpdate);
   do_check_false(bestUpdate.isSecurityUpdate);
   // Check the date is approximately equal
   do_check_neq(bestUpdate.installDate, 0);
   do_check_eq(bestUpdate.statusText, null);
   // nsIUpdate:state returns an empty string when no action has been performed
--- a/toolkit/mozapps/update/test/unit/test_0060_manager.js
+++ b/toolkit/mozapps/update/test/unit/test_0060_manager.js
@@ -50,38 +50,42 @@ function run_test() {
   // when it isn't specified in the update xml.
   patches = getLocalPatchString("partial", "http://partial/", "SHA256", "cd43",
                                 "86", "true", STATE_PENDING);
   updates = getLocalUpdateString(patches, "major", "New", "version 4", "4.0",
                                  "4.0", "20070811053724", "http://details1/",
                                  "http://billboard1/", "http://license1/",
                                  "http://service1/", "1238441300314",
                                  "test status text", "false", "test_channel",
-                                 "true", "true", "true", "true", "test extra1",
-                                 "test version", "3.0", "3.0");
+                                 "true", "true", "true", "true",
+                                 "test version", "3.0", "3.0",
+                                 "custom1_attr=\"custom1 value\"",
+                                 "custom2_attr=\"custom2 value\"");
 
   writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
   writeStatusFile(STATE_SUCCEEDED);
 
   patches = getLocalPatchString("complete", "http://complete/", "SHA1", "6232",
                                 "75", "true", STATE_FAILED);
   updates = getLocalUpdateString(patches, "major", "Existing", null, null,
                                  "3.0", null, "http://details2/", null, null,
                                  "http://service2/", null,
                                  getString("patchApplyFailure"), "true",
                                  "test_channel", "false", null, null, null,
-                                 null, "version 3", "3.0", null);
+                                 "version 3", "3.0", null,
+                                 "custom3_attr=\"custom3 value\"",
+                                 "custom4_attr=\"custom4 value\"");
   writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), false);
 
   standardInit();
 
   do_check_eq(gUpdateManager.activeUpdate, null);
   do_check_eq(gUpdateManager.updateCount, 2);
 
-  update = gUpdateManager.getUpdateAt(0);
+  update = gUpdateManager.getUpdateAt(0).QueryInterface(AUS_Ci.nsIPropertyBag);
   do_check_eq(update.state, STATE_SUCCEEDED);
   do_check_eq(update.type, "major");
   do_check_eq(update.name, "New");
   do_check_eq(update.displayVersion, "version 4");
   do_check_eq(update.appVersion, "4.0");
   do_check_eq(update.platformVersion, "4.0");
   do_check_eq(update.buildID, "20070811053724");
   do_check_eq(update.detailsURL, "http://details1/");
@@ -91,29 +95,31 @@ function run_test() {
   do_check_eq(update.installDate, "1238441300314");
   // statusText is updated
   do_check_eq(update.statusText, getString("installSuccess"));
   do_check_false(update.isCompleteUpdate);
   do_check_eq(update.channel, "test_channel");
   do_check_true(update.showPrompt);
   do_check_true(update.showNeverForVersion);
   do_check_true(update.showSurvey);
-  do_check_eq(update.extra1, "test extra1");
   do_check_eq(update.previousAppVersion, "3.0");
+  // Custom attributes
+  do_check_eq(update.getProperty("custom1_attr"), "custom1 value");
+  do_check_eq(update.getProperty("custom2_attr"), "custom2 value");
 
   patch = update.selectedPatch;
   do_check_eq(patch.type, "partial");
   do_check_eq(patch.URL, "http://partial/");
   do_check_eq(patch.hashFunction, "SHA256");
   do_check_eq(patch.hashValue, "cd43");
   do_check_eq(patch.size, "86");
   do_check_true(patch.selected);
   do_check_eq(patch.state, STATE_SUCCEEDED);
 
-  update = gUpdateManager.getUpdateAt(1);
+  update = gUpdateManager.getUpdateAt(1).QueryInterface(AUS_Ci.nsIPropertyBag);
   do_check_eq(update.state, STATE_FAILED);
   do_check_eq(update.name, "Existing");
   do_check_eq(update.type, "major");
   do_check_eq(update.displayVersion, "version 3");
   do_check_eq(update.appVersion, "3.0");
   do_check_eq(update.platformVersion, "3.0");
   do_check_eq(update.detailsURL, "http://details2/");
   do_check_eq(update.billboardURL, null);
@@ -125,18 +131,20 @@ function run_test() {
   do_check_eq(update.installDate, "1238441400314");
   do_check_eq(update.statusText, getString("patchApplyFailure"));
   do_check_eq(update.buildID, "20080811053724");
   do_check_true(update.isCompleteUpdate);
   do_check_eq(update.channel, "test_channel");
   do_check_false(update.showPrompt);
   do_check_false(update.showNeverForVersion);
   do_check_false(update.showSurvey);
-  do_check_eq(update.extra1, null);
   do_check_eq(update.previousAppVersion, null);
+  // Custom attributes
+  do_check_eq(update.getProperty("custom3_attr"), "custom3 value");
+  do_check_eq(update.getProperty("custom4_attr"), "custom4 value");
 
   patch = update.selectedPatch;
   do_check_eq(patch.type, "complete");
   do_check_eq(patch.URL, "http://complete/");
   do_check_eq(patch.hashFunction, "SHA1");
   do_check_eq(patch.hashValue, "6232");
   do_check_eq(patch.size, "75");
   do_check_true(patch.selected);
@@ -149,29 +157,29 @@ function run_test() {
   // isn't specified in the update xml.
   patches = getLocalPatchString(null, null, null, null, null, null,
                                 STATE_PENDING);
   updates = getLocalUpdateString(patches, "major", "New", null, null, "4.0",
                                  null, "http://details/", "http://billboard/",
                                  "http://license/", "http://service/",
                                  "1238441400314", "test status text", null,
                                  "test_channel", "true", "true", "true", "true",
-                                 "test extra1", "version 4.0", "4.0", "3.0");
+                                 "version 4.0", "4.0", "3.0");
 
   writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
   writeStatusFile(STATE_SUCCEEDED);
 
   patches = getLocalPatchString(null, null, null, null, null, null,
                                 STATE_FAILED);
   updates = getLocalUpdateString(patches, "major", "Existing", "version 3.0",
                                  "3.0", "3.0", null, "http://details/", null,
                                  null, "http://service/", null,
                                  getString("patchApplyFailure"), null,
                                  "test_channel", "false", null, null, null,
-                                 null, "version 3", null, null);
+                                 "version 3", null, null);
   writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), false);
 
   reloadUpdateManagerData();
   initUpdateServiceStub();
 
   do_check_eq(gUpdateManager.activeUpdate, null);
   do_check_eq(gUpdateManager.updateCount, 2);