diff options
Diffstat (limited to 'dom/system/SystemUpdateService.jsm')
-rw-r--r-- | dom/system/SystemUpdateService.jsm | 382 |
1 files changed, 382 insertions, 0 deletions
diff --git a/dom/system/SystemUpdateService.jsm b/dom/system/SystemUpdateService.jsm new file mode 100644 index 000000000..41647ea8f --- /dev/null +++ b/dom/system/SystemUpdateService.jsm @@ -0,0 +1,382 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; + +this.EXPORTED_SYMBOLS = ["SystemUpdateService"]; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +const CATEGORY_SYSTEM_UPDATE_PROVIDER = "system-update-provider"; +const PROVIDER_ACTIVITY_IDLE = 0; +const PROVIDER_ACTIVITY_CHECKING = 1; +const PROVIDER_ACTIVITY_DOWNLOADING = 1 << 1; +const PROVIDER_ACTIVITY_APPLYING = 1 << 2; + +var debug = Services.prefs.getBoolPref("dom.system_update.debug") + ? (aMsg) => dump("-*- SystemUpdateService.jsm : " + aMsg + "\n") + : (aMsg) => {}; + +XPCOMUtils.defineLazyServiceGetter(this, "ppmm", + "@mozilla.org/parentprocessmessagemanager;1", + "nsIMessageBroadcaster"); + +function ActiveProvider(aProvider) { + this.id = aProvider.id; + this._instance = Components.classesByID[aProvider.id].getService(Ci.nsISystemUpdateProvider); + this._instance.setListener(this); +} + +ActiveProvider.prototype = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsISystemUpdateListener]), + + _activity: PROVIDER_ACTIVITY_IDLE, + + destroy: function() { + if (this._instance) { + this._instance.unsetListener(); + this._instance = null; + } + + this.id = null; + }, + + checkForUpdate: function() { + this._execFuncIfNotInActivity(PROVIDER_ACTIVITY_CHECKING, + this._instance.checkForUpdate); + }, + + startDownload: function() { + this._execFuncIfNotInActivity(PROVIDER_ACTIVITY_DOWNLOADING, + this._instance.startDownload); + }, + + stopDownload: function() { + this._execFuncIfNotInActivity(PROVIDER_ACTIVITY_DOWNLOADING, + this._instance.stopDownload); + }, + + applyUpdate: function() { + this._execFuncIfNotInActivity(PROVIDER_ACTIVITY_APPLYING, + this._instance.applyUpdate); + }, + + setParameter: function(aName, aValue) { + return this._instance.setParameter(aName, aValue); + }, + + getParameter: function(aName) { + return this._instance.getParameter(aName); + }, + + // nsISystemUpdateListener + onUpdateAvailable: function(aType, aVersion, aDescription, aBuildDate, aSize) { + this._execFuncIfActiveAndInAction(PROVIDER_ACTIVITY_CHECKING, function() { + ppmm.broadcastAsyncMessage("SystemUpdate:OnUpdateAvailable", { + uuid: this.id, + packageInfo: { + type: aType, + version: aVersion, + description: aDescription, + buildDate: aBuildDate, + size: aSize, + } + }); + + this._unsetActivity(PROVIDER_ACTIVITY_CHECKING); + }.bind(this)); + }, + + onProgress: function(aLoaded, aTotal) { + this._execFuncIfActiveAndInAction(PROVIDER_ACTIVITY_DOWNLOADING, function() { + ppmm.broadcastAsyncMessage("SystemUpdate:OnProgress", { + uuid: this.id, + loaded: aLoaded, + total: aTotal, + }); + }.bind(this)); + }, + + onUpdateReady: function() { + this._execFuncIfActiveAndInAction(PROVIDER_ACTIVITY_DOWNLOADING, function() { + ppmm.broadcastAsyncMessage("SystemUpdate:OnUpdateReady", { + uuid: this.id, + }); + + this._unsetActivity(PROVIDER_ACTIVITY_DOWNLOADING); + }.bind(this)); + }, + + onError: function(aErrMsg) { + if (!SystemUpdateService._isActiveProviderId(this.id)) { + return; + } + + ppmm.broadcastAsyncMessage("SystemUpdate:OnError", { + uuid: this.id, + message: aErrMsg, + }); + + this._activity = PROVIDER_ACTIVITY_IDLE; + }, + + isIdle: function() { + return this._activity === PROVIDER_ACTIVITY_IDLE; + }, + + _isInActivity: function(aActivity) { + return (this._activity & aActivity) !== PROVIDER_ACTIVITY_IDLE; + }, + + _setActivity: function(aActivity) { + this._activity |= aActivity; + }, + + _unsetActivity: function(aActivity) { + this._activity &= ~aActivity; + }, + + _execFuncIfNotInActivity: function(aActivity, aFunc) { + if (!this._isInActivity(aActivity)) { + this._setActivity(aActivity); + aFunc(); + } + }, + + _execFuncIfActiveAndInAction: function(aActivity, aFunc) { + if (!SystemUpdateService._isActiveProviderId(this.id)) { + return; + } + if (this._isInActivity(aActivity)) { + aFunc(); + } + }, + +}; + +this.SystemUpdateService = { + _providers: [], + _activeProvider: null, + + _updateActiveProvider: function(aProvider) { + if (this._activeProvider) { + this._activeProvider.destroy(); + } + + this._activeProvider = new ActiveProvider(aProvider); + }, + + _isActiveProviderId: function(aId) { + return (this._activeProvider && this._activeProvider.id === aId); + }, + + init: function() { + debug("init"); + + let messages = ["SystemUpdate:GetProviders", + "SystemUpdate:GetActiveProvider", + "SystemUpdate:SetActiveProvider", + "SystemUpdate:CheckForUpdate", + "SystemUpdate:StartDownload", + "SystemUpdate:StopDownload", + "SystemUpdate:ApplyUpdate", + "SystemUpdate:SetParameter", + "SystemUpdate:GetParameter"]; + messages.forEach((function(aMsgName) { + ppmm.addMessageListener(aMsgName, this); + }).bind(this)); + + // load available provider list + let catMan = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager); + let entries = catMan.enumerateCategory(CATEGORY_SYSTEM_UPDATE_PROVIDER); + while (entries.hasMoreElements()) { + let name = entries.getNext().QueryInterface(Ci.nsISupportsCString).data; + let [contractId, id] = catMan.getCategoryEntry(CATEGORY_SYSTEM_UPDATE_PROVIDER, name).split(","); + this._providers.push({ + id: id, + name: name, + contractId: contractId + }); + } + debug("available providers: " + JSON.stringify(this._providers)); + + // setup default active provider + let defaultActive; + try { + defaultActive = Services.prefs.getCharPref("dom.system_update.active"); + } catch (e) {} + + if (defaultActive) { + let defaultProvider = this._providers.find(function(aProvider) { + return aProvider.contractId === defaultActive; + }); + + if (defaultProvider) { + this._updateActiveProvider(defaultProvider); + } + } + }, + + addProvider: function(aClassId, aContractId, aName) { + debug("addProvider"); + + //did not allow null or empty string to add. + if(!aClassId || !aContractId || !aName) { + return; + } + + let existedProvider = this._providers.find(function(provider) { + return provider.id === aClassId; + }); + + //skip if adding the existed provider. + if (existedProvider) { + debug("existing providers: " + JSON.stringify(existedProvider)); + return; + } + + //dynamically add the provider info to list. + this._providers.push({ + id: aClassId, + name: aName, + contractId: aContractId + }); + debug("available providers: " + JSON.stringify(this._providers)); + }, + + getProviders: function(aData, aMm) { + debug("getProviders"); + + aData.providers = []; + for (let provider of this._providers) { + aData.providers.push({ + name: provider.name, + uuid: provider.id + }); + } + aMm.sendAsyncMessage("SystemUpdate:GetProviders:Result:OK", aData); + }, + + getActiveProvider: function(aData, aMm) { + debug("getActiveProvider"); + + let self = this; + let providerInfo = this._providers.find(function(provider) { + return self._isActiveProviderId(provider.id); + }); + + if (!providerInfo) { + aData.error = "NotFoundError"; + aMm.sendAsyncMessage("SystemUpdate:GetActiveProvider:Result:Error", aData); + return; + } + + aData.provider = { + name: providerInfo.name, + uuid: providerInfo.id + }; + aMm.sendAsyncMessage("SystemUpdate:GetActiveProvider:Result:OK", aData); + }, + + setActiveProvider: function(aData, aMm) { + debug("setActiveProvider"); + + let self = this; + let selectedProvider = this._providers.find(function(provider) { + return provider.id === aData.uuid; + }); + + if (!selectedProvider) { + aData.error = "DataError"; + aMm.sendAsyncMessage("SystemUpdate:SetActiveProvider:Result:Error", aData); + return; + } + + if (!this._isActiveProviderId(selectedProvider.id)) { + // not allow changing active provider while there is an ongoing update activity + if (this.activeProvider && !this._activeProvider.isIdle()) { + aData.error = "DataError"; + aMm.sendAsyncMessage("SystemUpdate:SetActiveProvider:Result:Error", aData); + return; + } + + this._updateActiveProvider(selectedProvider); + Services.prefs.setCharPref("dom.system_update.active", selectedProvider.contractId); + } + + aData.provider = { + name: selectedProvider.name, + uuid: selectedProvider.id + }; + aMm.sendAsyncMessage("SystemUpdate:SetActiveProvider:Result:OK", aData); + }, + + receiveMessage: function(aMessage) { + if (!aMessage.target.assertPermission("system-update")) { + debug("receive message " + aMessage.name + + " from a content process with no 'system-update' privileges."); + return null; + } + + let msg = aMessage.data || {}; + let mm = aMessage.target; + + switch (aMessage.name) { + case "SystemUpdate:GetProviders": { + this.getProviders(msg, mm); + break; + } + case "SystemUpdate:GetActiveProvider": { + this.getActiveProvider(msg, mm); + break; + } + case "SystemUpdate:SetActiveProvider": { + this.setActiveProvider(msg, mm); + break; + } + case "SystemUpdate:CheckForUpdate": { + if (this._isActiveProviderId(msg.uuid)) { + this._activeProvider.checkForUpdate(); + } + break; + } + case "SystemUpdate:StartDownload": { + if (this._isActiveProviderId(msg.uuid)) { + this._activeProvider.startDownload(); + } + break; + } + case "SystemUpdate:StopDownload": { + if (this._isActiveProviderId(msg.uuid)) { + this._activeProvider.stopDownload(); + } + break; + } + case "SystemUpdate:ApplyUpdate": { + if (this._isActiveProviderId(msg.uuid)) { + this._activeProvider.applyUpdate(); + } + break; + } + case "SystemUpdate:SetParameter": { + if (this._isActiveProviderId(msg.uuid)) { + return this._activeProvider.setParameter(msg.name, msg.value); + } + break; + } + case "SystemUpdate:GetParameter": { + if (this._isActiveProviderId(msg.uuid)) { + return this._activeProvider.getParameter(msg.name); + } + break; + } + } + }, + +}; + +SystemUpdateService.init(); |