diff --git a/toolkit/mozapps/extensions/content/extensions.js b/toolkit/mozapps/extensions/content/extensions.js index 7a8c5dffa44..1d98434417a 100644 --- a/toolkit/mozapps/extensions/content/extensions.js +++ b/toolkit/mozapps/extensions/content/extensions.js @@ -164,7 +164,8 @@ function Shutdown() var os = Components.classes["@mozilla.org/observer-service;1"] .getService(Components.interfaces.nsIObserverService); - os.removeObserver(gDownloadManager, "xpinstall-download-started"); + if (gDownloadManager) + os.removeObserver(gDownloadManager, "xpinstall-download-started"); } /////////////////////////////////////////////////////////////////////////////// @@ -196,11 +197,13 @@ XPInstallDownloadManager.prototype = { observe: function (aSubject, aTopic, aData) { - if (aTopic == "xpinstall-download-started") { + switch (aTopic) { + case "xpinstall-download-started": var params = aSubject.QueryInterface(Components.interfaces.nsISupportsArray); var paramBlock = params.GetElementAt(0).QueryInterface(Components.interfaces.nsISupportsInterfacePointer); paramBlock = paramBlock.data.QueryInterface(Components.interfaces.nsIDialogParamBlock); this.addDownloads(paramBlock); + break; } }, @@ -244,6 +247,8 @@ XPInstallDownloadManager.prototype = { { const nsIXPIProgressDialog = Components.interfaces.nsIXPIProgressDialog; var element = document.getElementById(aURL); + dump("*** aURL = " + aURL + "\n"); + if (!element) return; switch (aState) { case nsIXPIProgressDialog.DOWNLOAD_START: element.setAttribute("state", "waiting"); @@ -268,12 +273,10 @@ XPInstallDownloadManager.prototype = { } element.setAttribute("error", msg); } - else { - // Remove the dummy, since we installed successfully - var type = gWindowState == "extensions" ? nsIUpdateItem.TYPE_EXTENSION - : nsIUpdateItem.TYPE_THEME; - gExtensionManager.removeDownload(aURL, type); - } + // Remove the dummy, since we installed successfully + var type = gWindowState == "extensions" ? nsIUpdateItem.TYPE_EXTENSION + : nsIUpdateItem.TYPE_THEME; + gExtensionManager.removeDownload(aURL, type); break; case nsIXPIProgressDialog.DIALOG_CLOSE: break; @@ -284,6 +287,7 @@ XPInstallDownloadManager.prototype = { onProgress: function (aURL, aValue, aMaxValue) { var element = document.getElementById(aURL); + if (!element) return; var percent = Math.round((aValue / aMaxValue) * 100); if (percent > 1 && !(aURL in this._urls)) { this._urls[aURL] = true; diff --git a/toolkit/mozapps/extensions/src/nsExtensionManager.js.in b/toolkit/mozapps/extensions/src/nsExtensionManager.js.in index 867ede373e9..62225760001 100644 --- a/toolkit/mozapps/extensions/src/nsExtensionManager.js.in +++ b/toolkit/mozapps/extensions/src/nsExtensionManager.js.in @@ -1054,8 +1054,87 @@ nsExtensionManager.prototype = { win.close(); } + break; + case "quit-application-requested": + if (this._downloadCount > 0) { + var result; +#ifndef XP_MACOSX + result = this._confirmCancelDownloads(this._downloadCount, + "quitCancelDownloadsAlertTitle", + "quitCancelDownloadsAlertMsgMultiple", + "quitCancelDownloadsAlertMsg", + "dontQuitButtonWin"); +#else + result = this._confirmCancelDownloads(this._downloadCount, + "quitCancelDownloadsAlertTitle", + "quitCancelDownloadsAlertMsgMacMultiple", + "quitCancelDownloadsAlertMsgMac", + "dontQuitButtonMac"); +#endif + if (!result) + this._cancelDownloads(); + var PRBool = aSubject.QueryInterface(Components.interfaces.nsISupportsPRBool); + PRBool.data = result; + } + break; + case "offline-requested": + if (this._downloadCount > 0) { + result = this._confirmCancelDownloads(this._downloadCount, + "offlineCancelDownloadsAlertTitle", + "offlineCancelDownloadsAlertMsgMultiple", + "offlineCancelDownloadsAlertMsg", + "dontGoOfflineButton"); + if (!result) + this._cancelDownloads(); + var PRBool = aSubject.QueryInterface(Components.interfaces.nsISupportsPRBool); + PRBool.data = result; + } + break; } }, + + _cancelDownloads: function () + { + var os = Components.classes["@mozilla.org/observer-service;1"] + .getService(Components.interfaces.nsIObserverService); + for (var i = 0; i < this._transactions.length; ++i) + os.notifyObservers(this._transactions[i], "xpinstall-progress", "cancel"); + os.removeObserver(this, "offline-requested"); + os.removeObserver(this, "quit-application-requested"); + + this._removeAllDownloads(); + }, + + _confirmCancelDownloads: function (aCount, aTitle, aCancelMessageMultiple, + aCancelMessageSingle, aDontCancelButton) + { + var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"] + .getService(Components.interfaces.nsIStringBundleService); + var bundle = sbs.createBundle("chrome://mozapps/locale/downloads/downloads.properties"); + var title = bundle.GetStringFromName(aTitle); + var message, quitButton; + if (aCount > 1) { + message = bundle.formatStringFromName(aCancelMessageMultiple, [aCount], 1); + quitButton = bundle.formatStringFromName("cancelDownloadsOKTextMultiple", [aCount], 1); + } + else { + message = bundle.GetStringFromName(aCancelMessageSingle); + quitButton = bundle.GetStringFromName("cancelDownloadsOKText"); + } + var dontQuitButton = bundle.GetStringFromName(aDontCancelButton); + + var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] + .getService(Components.interfaces.nsIWindowMediator); + var win = wm.getMostRecentWindow("Extension:Manager"); + const nsIPromptService = Components.interfaces.nsIPromptService; + var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] + .getService(nsIPromptService); + var flags = (nsIPromptService.BUTTON_TITLE_IS_STRING * nsIPromptService.BUTTON_POS_0) + + (nsIPromptService.BUTTON_TITLE_IS_STRING * nsIPromptService.BUTTON_POS_1); + var rv = { }; + ps.confirmEx(win, title, message, flags, quitButton, dontQuitButton, null, null, { }, rv); + return rv.value == 0; + }, // This function checks for and disables any "old-style" extensions // from Firefox 0.8 and earlier created using the "chrome:extension=true" flag. @@ -1610,8 +1689,11 @@ nsExtensionManager.prototype = { ///////////////////////////////////////////////////////////////////////////// // Downloads _transactions: [], + _downloadCount: 0, addDownloads: function (aItems, aItemCount) { + this._downloadCount += aItemCount; + var txn = new nsItemDownloadTransaction(this); for (var i = 0; i < aItemCount; ++i) { var currItem = aItems[i]; @@ -1623,6 +1705,8 @@ nsExtensionManager.prototype = { // Kick off the download process for this transaction var os = Components.classes["@mozilla.org/observer-service;1"] .getService(Components.interfaces.nsIObserverService); + os.addObserver(this, "offline-requested", false); + os.addObserver(this, "quit-application-requested", false); os.notifyObservers(txn, "xpinstall-progress", "open"); }, @@ -1636,6 +1720,12 @@ nsExtensionManager.prototype = { } }, + _removeAllDownloads: function () + { + for (var i = 0; i < this._transactions.length; ++i) + this._transactions[i].removeAllDownloads(); + }, + // The nsIXPIProgressDialog implementation in the download transaction object // forwards notifications through these methods which we then pass on to any // front end objects implementing nsIExtensionDownloadProgressListener that @@ -1649,11 +1739,16 @@ nsExtensionManager.prototype = { this._progressData[aURL] = { }; this._progressData[aURL].state = aState; + dump("*** sending state changes for " + aURL + "\n"); for (var i = 0; i < this._downloadObservers.length; ++i) this._downloadObservers[i].onStateChange(aURL, aState, aValue); const nsIXPIProgressDialog = Components.interfaces.nsIXPIProgressDialog; - if (aState == nsIXPIProgressDialog.DIALOG_CLOSE) { + switch (aState) { + case nsIXPIProgressDialog.INSTALL_DONE: + --this._downloadCount; + break; + case nsIXPIProgressDialog.DIALOG_CLOSE: for (var i = 0; i < this._transactions.length; ++i) { if (this._transactions[i].id == aTransaction.id) { this._transactions.splice(i, 1); @@ -1661,6 +1756,7 @@ nsExtensionManager.prototype = { break; } } + break; } }, @@ -1804,6 +1900,12 @@ nsItemDownloadTransaction.prototype = { this._manager._ds.removeDownload(aURL, aItemType); }, + removeAllDownloads: function () + { + for (var i = 0; i < this._downloads.length; ++i) + this.removeDownload(this._downloads[i].url, this._downloads[i].type); + }, + containsURL: function (aURL) { for (var i = 0; i < this._downloads.length; ++i) {