gecko/dom/system/SystemUpdateService.jsm

383 lines
11 KiB
JavaScript

/* 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;
let 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 = Cc[aProvider.contractId].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();