Bug 540030 - Display the error and hide the progress bar if an add-on install fails in the Add-on Manager [r=gavin.sharp]
authorMark Finkle <mfinkle@mozilla.com>
Fri, 15 Jan 2010 17:14:29 -0500
changeset 1311 a905d05db9e8d74ce5e7ac37217de7de3dd3877f
parent 1310 a3c01b40f18453462d7e0212708c76fbaefe836f
child 1312 075ff65da4b7f1e0c1241b229d9701fab2fef195
push id1167
push usermfinkle@mozilla.com
push dateFri, 15 Jan 2010 22:13:58 +0000
reviewersgavin.sharp
bugs540030
Bug 540030 - Display the error and hide the progress bar if an add-on install fails in the Add-on Manager [r=gavin.sharp]
chrome/content/bindings/extensions.xml
chrome/content/browser.css
chrome/content/extensions.js
--- a/chrome/content/bindings/extensions.xml
+++ b/chrome/content/bindings/extensions.xml
@@ -16,22 +16,22 @@
     <content orient="vertical">
       <xul:hbox align="start">
         <xul:image xbl:inherits="src=iconURL"/>
         <xul:vbox flex="1">
           <xul:hbox align="center">
             <xul:label class="title" xbl:inherits="value=name"/>
             <xul:label class="normal" xbl:inherits="value=version"/>
             <xul:spacer flex="1"/>
-            <xul:label class="normal" crop="end" xbl:inherits="value=typeLabel"/>            
+            <xul:label class="normal" crop="end" xbl:inherits="value=typeLabel"/>
           </xul:hbox>
           <xul:vbox>
             <xul:label class="normal hide-on-select" xbl:inherits="value=description" crop="end" flex="1"/>
             <xul:description class="normal show-on-select" xbl:inherits="xbl:text=description" flex="1"/>
-            <xul:label class="normal-bold" xbl:inherits="value=updateStatus"/>            
+            <xul:label class="normal-bold" xbl:inherits="value=updateStatus"/>
           </xul:vbox>
         </xul:vbox>
       </xul:hbox>
       <xul:hbox class="show-on-select buttons-box">
         <xul:button anonid="options-button" type="checkbox" class="addon-options" label="&addonOptions.label;"
                     oncommand="document.getBindingParent(this).toggleOptions();"/>
         <xul:spacer flex="1"/>
         <xul:button anonid="enable-button" class="show-on-disable hide-on-enable hide-on-uninstall addon-enable" label="&addonEnable.label;"
@@ -94,33 +94,33 @@
 
             // retrieve the extensions prefs
             let optionsURL = this.getAttribute("optionsURL");
             let xhr = new XMLHttpRequest();
             xhr.open("GET", optionsURL, false);
             xhr.send();
             if (!xhr.responseXML)
               return;
-            
+
             let currentNode;
             let nodeIterator = xhr.responseXML.createNodeIterator(xhr.responseXML,
                                                               NodeFilter.SHOW_TEXT,
                                                               null,
                                                               false);
             while (currentNode = nodeIterator.nextNode()) {
               let trimmed = currentNode.nodeValue.replace(/^\s\s*/, "").replace(/\s\s*$/, "");
               if (!trimmed.length)
                 currentNode.parentNode.removeChild(currentNode);
             }
 
             // Only allow <setting> for now
             let prefs = xhr.responseXML.querySelectorAll(":root > setting");
             for (let i = 0; i < prefs.length; i++)
               box.appendChild(prefs.item(i));
-            
+
             // Send an event so add-ons can prepopulate any non-preference based
             // settings
             let event = document.createEvent("Events");
             event.initEvent("AddonOptionsLoad", true, false);
             this.dispatchEvent(event);
           ]]>
         </body>
       </method>
@@ -130,17 +130,17 @@
   <binding id="extension-searchplugin" extends="chrome://browser/content/bindings.xml#richlistitem">
     <content orient="vertical">
       <xul:hbox align="start">
         <xul:image width="32" height="32" xbl:inherits="src=iconURL"/>
         <xul:vbox flex="1">
           <xul:hbox align="center">
             <xul:label class="title" xbl:inherits="value=name"/>
             <xul:spacer flex="1"/>
-            <xul:label class="normal" crop="end" xbl:inherits="value=typeLabel"/>            
+            <xul:label class="normal" crop="end" xbl:inherits="value=typeLabel"/>
           </xul:hbox>
           <xul:vbox>
             <xul:label class="normal hide-on-select" xbl:inherits="value=description" crop="end" flex="1"/>
             <xul:description class="normal show-on-select" xbl:inherits="xbl:text=description" flex="1"/>
           </xul:vbox>
         </xul:vbox>
       </xul:hbox>
       <xul:hbox class="show-on-select">
@@ -150,17 +150,17 @@
         <xul:button class="show-on-enable hide-on-disable hide-on-uninstall addon-disable" label="&addonDisable.label;"
                     oncommand="ExtensionsView.disable(document.getBindingParent(this));"/>
         <xul:button anonid="uninstall-button" class="hide-on-uninstall addon-uninstall" label="&addonUninstall.label;"
                     oncommand="ExtensionsView.uninstall(document.getBindingParent(this));"/>
         <xul:button class="show-on-uninstall addon-cancel" label="&addonCancel.label;"
                     oncommand="ExtensionsView.cancelUninstall(document.getBindingParent(this));"/>
       </xul:hbox>
     </content>
-    
+
     <implementation>
       <constructor>
         <![CDATA[
           let appManaged = this.getAttribute("appManaged");
           if (appManaged == "true")
             document.getAnonymousElementByAttribute(this, "anonid", "uninstall-button").setAttribute("disabled", "true");
         ]]>
       </constructor>
@@ -171,21 +171,22 @@
     <content orient="vertical">
       <xul:hbox align="start">
         <xul:image xbl:inherits="src=iconURL"/>
         <xul:vbox flex="1">
           <xul:hbox align="center">
             <xul:label class="title" xbl:inherits="value=name"/>
             <xul:label class="normal" xbl:inherits="value=version"/>
             <xul:spacer flex="1"/>
-            <xul:hbox class="addon-type-or-rating" align="center" xbl:inherits="rating"/> 
+            <xul:hbox class="addon-type-or-rating" align="center" xbl:inherits="rating"/>
           </xul:hbox>
           <xul:vbox>
             <xul:label class="normal hide-on-select" xbl:inherits="value=description" crop="end" flex="1"/>
             <xul:description class="normal show-on-select" xbl:inherits="xbl:text=description" flex="1"/>
+            <xul:label class="normal-bold show-on-error" xbl:inherits="value=error"/>
           </xul:vbox>
         </xul:vbox>
       </xul:hbox>
       <xul:vbox flex="1">
         <xul:hbox class="show-on-select">
           <xul:button class="addon-install hide-on-install hide-on-restart" label="&addonShowPage.label;"
                       oncommand="ExtensionsView.showPage(document.getBindingParent(this));"/>
           <xul:spacer flex="1"/>
--- a/chrome/content/browser.css
+++ b/chrome/content/browser.css
@@ -144,23 +144,25 @@ richlistitem[typeName="download"][state=
 }
 
 richlistitem[typeName="download"][state="4"] {
   -moz-binding: url("chrome://browser/content/bindings/downloads.xml#download-paused");
 }
 
 /* addons states ----------------------------------------------------------- */
 .hide-on-enable,
+.show-on-error,
 .show-on-uninstall,
 .show-on-install,
 .show-on-restart,
 richlistitem[isDisabled="true"] .hide-on-disable {
   display: none;
 }
 
+richlistitem[error] .show-on-error,
 richlistitem[opType="needs-restart"] .show-on-restart,
 richlistitem[opType="needs-uninstall"] .show-on-uninstall,
 richlistitem[opType="needs-install"] .show-on-install,
 richlistitem[opType="needs-enable"] .show-on-enable,
 richlistitem[opType="needs-disable"] .show-on-disable,
 richlistitem[isDisabled="true"] .show-on-disable {
   display: -moz-box;
 }
--- a/chrome/content/extensions.js
+++ b/chrome/content/extensions.js
@@ -189,24 +189,24 @@ var ExtensionsView = {
 
     let item = this.getElementForAddon(aID);
     if (!item)
       return;
 
     // if the element is not the selected element, select it
     if (item != this._list.selectedItem)
       this._list.selectedItem = item;
-    
+
     item.showOptions();
   },
 
   hideOptions: function ev_hideOptions() {
     if (!this._list)
       return;
-    
+
     let items = this._list.childNodes;
     for (let i = 0; i < items.length; i++) {
       let item = items[i];
       if (item.hideOptions)
         item.hideOptions();
     }
     this._list.ensureSelectedElementIsVisible();
   },
@@ -284,17 +284,17 @@ var ExtensionsView = {
   },
 
   uninit: function ev_uninit() {
     var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
     os.removeObserver(this._dloadmgr, "xpinstall-download-started");
 
     this._extmgr.removeInstallListenerAt(this._observerIndex);
   },
-  
+
   hideOnSelect: function ev_handleEvent(aEvent) {
     // When list selection changes, be sure to close up any open options sections
     if (aEvent.target == this._list)
       this.hideOptions();
   },
 
   getAddonsFromLocal: function ev_getAddonsFromLocal() {
     this.clearSection("local");
@@ -318,17 +318,17 @@ var ExtensionsView = {
       listitem.setAttribute("appDisabled", appDisabled);
       listitem.setAttribute("appManaged", appManaged);
       listitem.setAttribute("description", desc);
       listitem.setAttribute("optionsURL", optionsURL);
       listitem.setAttribute("opType", opType);
       listitem.setAttribute("updateable", updateable);
       this._list.insertBefore(listitem, this._repoItem);
     }
-    
+
     // Load the search engines
     let defaults = this._search.getDefaultEngines({ }).map(function (e) e.name);
     function isDefault(aEngine)
       defaults.indexOf(aEngine.name) != -1
 
     let strings = Elements.browserBundle;
     let defaultDescription = strings.getString("addonsSearchEngine.description");
 
@@ -389,17 +389,17 @@ var ExtensionsView = {
       this._extmgr.disableItem(id);
       opType = this._getRDFProperty(id, "opType");
 
       if (opType == "needs-disable")
         this.showRestart();
       else
         this.hideRestart();
     }
-    
+
     aItem.setAttribute("opType", opType);
   },
 
   uninstall: function ev_uninstall(aItem) {
     let opType;
     if (aItem.getAttribute("type") == "1024") {
       // Make sure the engine isn't hidden before removing it, to make sure it's
       // visible if the user later re-adds it (works around bug 341833)
@@ -425,20 +425,31 @@ var ExtensionsView = {
     this.hideRestart();
 
     let opType = this._getRDFProperty(id, "opType");
     aItem.setAttribute("opType", opType);
   },
 
   _installCallback: function ev__installCallback(aItem, aStatus) {
     if (aStatus == -210) {
-      // TODO: User cancelled
+      // User cancelled
+      aItem.removeAttribute("opType");
     }
     else if (aStatus < 0) {
-      // TODO: Some other error
+      // Some other error
+      aItem.removeAttribute("opType");
+      let bundles = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);
+      let strings = bundles.createBundle("chrome://global/locale/xpinstall/xpinstall.properties");
+
+      try {
+        var msg = strings.GetStringFromName("error" + aStatus);
+      } catch (ex) {
+        msg = strings.formatStringFromName("unknown.error", [aStatus]);
+      }
+      aItem.setAttribute("error", msg);
     }
     else {
       // Success
       aItem.setAttribute("opType", "needs-restart");
     }
   },
 
   installFromRepo: function ev_installFromRepo(aItem) {
@@ -536,17 +547,17 @@ var ExtensionsView = {
 
     let strings = Elements.browserBundle;
     if (aAddons.length == 0) {
       let msg = aIsRecommended ? strings.getString("addonsSearchNone.recommended") :
                                  strings.getString("addonsSearchNone.search");
       let button = aIsRecommended ? strings.getString("addonsSearchNone.button") :
                                     strings.getString("addonsSearchSuccess2.button");
       let item = this.displaySectionMessage("repo", msg, button, true);
-      
+
       if (aSelectFirstResult)
         this._list.scrollBoxObject.scrollToElement(item);
       return;
     }
 
     if (aIsRecommended) {
       // Locale sensitive sort
       function compare(a, b) {
@@ -600,17 +611,17 @@ var ExtensionsView = {
     this.searchBox.value = aTerms;
     this.getAddonsFromRepo(aTerms, true);
   },
 
   resetSearch: function ev_resetSearch() {
     this.searchBox.value = "";
     this.getAddonsFromRepo("");
   },
-  
+
   updateAll: function ev_updateAll() {
     if (!this._isXPInstallEnabled())
       return;
 
     // Make sure we're online before attempting to load
     Util.forceOnline();
 
     // To support custom views we check the add-ons displayed in the list
@@ -618,25 +629,25 @@ var ExtensionsView = {
     let start = this._localItem.nextSibling;
     let end = this._repoItem;
 
     while (start != end) {
       if (start.getAttribute("updateable") == "true")
         items.push(this._extmgr.getItemForID(start.getAttribute("addonID")));
       start = start.nextSibling;
     }
-  
+
     if (items.length > 0) {
       let listener = new UpdateCheckListener();
       this._extmgr.update(items, items.length, Ci.nsIExtensionManager.UPDATE_CHECK_NEWVERSION, listener);
     }
-    
+
     if (this._list.selectedItem)
       this._list.selectedItem.focus();
-  
+
     this._pref.setBoolPref("extensions.update.notifyUser", false);
   }
 };
 
 
 function searchFailed() {
   ExtensionsView.clearSection("repo");
 
@@ -752,17 +763,17 @@ XPInstallDownloadManager.prototype = {
     if (!ExtensionsView.visible)
       return;
 
     var element = ExtensionsView.getElementForAddon(aAddon.id);
     if (!element)
       return;
 
     element.setAttribute("status", (Components.isSuccessCode(aStatus) ? "success" : "fail"));
-    
+
     // If we are updating an add-on, change the status
     if (element.hasAttribute("updating")) {
       let strings = Elements.browserBundle;
       element.setAttribute("updateStatus", strings.getFormattedString("addonUpdate.updated", [aAddon.version]));
       element.removeAttribute("updating");
     }
   },
 
@@ -848,17 +859,17 @@ UpdateCheckListener.prototype = {
     let strings = Elements.browserBundle;
     let element = document.getElementById(PREFIX_ITEM_URI + aAddon.id);
     element.setAttribute("updateStatus", strings.getString("addonUpdate.checking"));
   },
 
   onAddonUpdateEnded: function ucl_onAddonUpdateEnded(aAddon, aStatus) {
     if (!document)
       return;
-    
+
     let strings = Elements.browserBundle;
     let element = document.getElementById(PREFIX_ITEM_URI + aAddon.id);
     let updateable = false;
     const nsIAUCL = Ci.nsIAddonUpdateCheckListener;
     switch (aStatus) {
       case nsIAUCL.STATUS_UPDATE:
         var statusMsg = strings.getFormattedString("addonUpdate.updating", [aAddon.version]);
         updateable = true;
@@ -896,9 +907,8 @@ UpdateCheckListener.prototype = {
 
   QueryInterface: function ucl_QueryInterface(aIID) {
     if (!aIID.equals(Ci.nsIAddonUpdateCheckListener) &&
         !aIID.equals(Ci.nsISupports))
       throw Components.results.NS_ERROR_NO_INTERFACE;
     return this;
   }
 };
-