SystemUpdateService.jsm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  3. * You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. "use strict";
  5. const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
  6. this.EXPORTED_SYMBOLS = ["SystemUpdateService"];
  7. Cu.import("resource://gre/modules/XPCOMUtils.jsm");
  8. Cu.import("resource://gre/modules/Services.jsm");
  9. const CATEGORY_SYSTEM_UPDATE_PROVIDER = "system-update-provider";
  10. const PROVIDER_ACTIVITY_IDLE = 0;
  11. const PROVIDER_ACTIVITY_CHECKING = 1;
  12. const PROVIDER_ACTIVITY_DOWNLOADING = 1 << 1;
  13. const PROVIDER_ACTIVITY_APPLYING = 1 << 2;
  14. var debug = Services.prefs.getBoolPref("dom.system_update.debug")
  15. ? (aMsg) => dump("-*- SystemUpdateService.jsm : " + aMsg + "\n")
  16. : (aMsg) => {};
  17. XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
  18. "@mozilla.org/parentprocessmessagemanager;1",
  19. "nsIMessageBroadcaster");
  20. function ActiveProvider(aProvider) {
  21. this.id = aProvider.id;
  22. this._instance = Components.classesByID[aProvider.id].getService(Ci.nsISystemUpdateProvider);
  23. this._instance.setListener(this);
  24. }
  25. ActiveProvider.prototype = {
  26. QueryInterface: XPCOMUtils.generateQI([Ci.nsISystemUpdateListener]),
  27. _activity: PROVIDER_ACTIVITY_IDLE,
  28. destroy: function() {
  29. if (this._instance) {
  30. this._instance.unsetListener();
  31. this._instance = null;
  32. }
  33. this.id = null;
  34. },
  35. checkForUpdate: function() {
  36. this._execFuncIfNotInActivity(PROVIDER_ACTIVITY_CHECKING,
  37. this._instance.checkForUpdate);
  38. },
  39. startDownload: function() {
  40. this._execFuncIfNotInActivity(PROVIDER_ACTIVITY_DOWNLOADING,
  41. this._instance.startDownload);
  42. },
  43. stopDownload: function() {
  44. this._execFuncIfNotInActivity(PROVIDER_ACTIVITY_DOWNLOADING,
  45. this._instance.stopDownload);
  46. },
  47. applyUpdate: function() {
  48. this._execFuncIfNotInActivity(PROVIDER_ACTIVITY_APPLYING,
  49. this._instance.applyUpdate);
  50. },
  51. setParameter: function(aName, aValue) {
  52. return this._instance.setParameter(aName, aValue);
  53. },
  54. getParameter: function(aName) {
  55. return this._instance.getParameter(aName);
  56. },
  57. // nsISystemUpdateListener
  58. onUpdateAvailable: function(aType, aVersion, aDescription, aBuildDate, aSize) {
  59. this._execFuncIfActiveAndInAction(PROVIDER_ACTIVITY_CHECKING, function() {
  60. ppmm.broadcastAsyncMessage("SystemUpdate:OnUpdateAvailable", {
  61. uuid: this.id,
  62. packageInfo: {
  63. type: aType,
  64. version: aVersion,
  65. description: aDescription,
  66. buildDate: aBuildDate,
  67. size: aSize,
  68. }
  69. });
  70. this._unsetActivity(PROVIDER_ACTIVITY_CHECKING);
  71. }.bind(this));
  72. },
  73. onProgress: function(aLoaded, aTotal) {
  74. this._execFuncIfActiveAndInAction(PROVIDER_ACTIVITY_DOWNLOADING, function() {
  75. ppmm.broadcastAsyncMessage("SystemUpdate:OnProgress", {
  76. uuid: this.id,
  77. loaded: aLoaded,
  78. total: aTotal,
  79. });
  80. }.bind(this));
  81. },
  82. onUpdateReady: function() {
  83. this._execFuncIfActiveAndInAction(PROVIDER_ACTIVITY_DOWNLOADING, function() {
  84. ppmm.broadcastAsyncMessage("SystemUpdate:OnUpdateReady", {
  85. uuid: this.id,
  86. });
  87. this._unsetActivity(PROVIDER_ACTIVITY_DOWNLOADING);
  88. }.bind(this));
  89. },
  90. onError: function(aErrMsg) {
  91. if (!SystemUpdateService._isActiveProviderId(this.id)) {
  92. return;
  93. }
  94. ppmm.broadcastAsyncMessage("SystemUpdate:OnError", {
  95. uuid: this.id,
  96. message: aErrMsg,
  97. });
  98. this._activity = PROVIDER_ACTIVITY_IDLE;
  99. },
  100. isIdle: function() {
  101. return this._activity === PROVIDER_ACTIVITY_IDLE;
  102. },
  103. _isInActivity: function(aActivity) {
  104. return (this._activity & aActivity) !== PROVIDER_ACTIVITY_IDLE;
  105. },
  106. _setActivity: function(aActivity) {
  107. this._activity |= aActivity;
  108. },
  109. _unsetActivity: function(aActivity) {
  110. this._activity &= ~aActivity;
  111. },
  112. _execFuncIfNotInActivity: function(aActivity, aFunc) {
  113. if (!this._isInActivity(aActivity)) {
  114. this._setActivity(aActivity);
  115. aFunc();
  116. }
  117. },
  118. _execFuncIfActiveAndInAction: function(aActivity, aFunc) {
  119. if (!SystemUpdateService._isActiveProviderId(this.id)) {
  120. return;
  121. }
  122. if (this._isInActivity(aActivity)) {
  123. aFunc();
  124. }
  125. },
  126. };
  127. this.SystemUpdateService = {
  128. _providers: [],
  129. _activeProvider: null,
  130. _updateActiveProvider: function(aProvider) {
  131. if (this._activeProvider) {
  132. this._activeProvider.destroy();
  133. }
  134. this._activeProvider = new ActiveProvider(aProvider);
  135. },
  136. _isActiveProviderId: function(aId) {
  137. return (this._activeProvider && this._activeProvider.id === aId);
  138. },
  139. init: function() {
  140. debug("init");
  141. let messages = ["SystemUpdate:GetProviders",
  142. "SystemUpdate:GetActiveProvider",
  143. "SystemUpdate:SetActiveProvider",
  144. "SystemUpdate:CheckForUpdate",
  145. "SystemUpdate:StartDownload",
  146. "SystemUpdate:StopDownload",
  147. "SystemUpdate:ApplyUpdate",
  148. "SystemUpdate:SetParameter",
  149. "SystemUpdate:GetParameter"];
  150. messages.forEach((function(aMsgName) {
  151. ppmm.addMessageListener(aMsgName, this);
  152. }).bind(this));
  153. // load available provider list
  154. let catMan = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
  155. let entries = catMan.enumerateCategory(CATEGORY_SYSTEM_UPDATE_PROVIDER);
  156. while (entries.hasMoreElements()) {
  157. let name = entries.getNext().QueryInterface(Ci.nsISupportsCString).data;
  158. let [contractId, id] = catMan.getCategoryEntry(CATEGORY_SYSTEM_UPDATE_PROVIDER, name).split(",");
  159. this._providers.push({
  160. id: id,
  161. name: name,
  162. contractId: contractId
  163. });
  164. }
  165. debug("available providers: " + JSON.stringify(this._providers));
  166. // setup default active provider
  167. let defaultActive;
  168. try {
  169. defaultActive = Services.prefs.getCharPref("dom.system_update.active");
  170. } catch (e) {}
  171. if (defaultActive) {
  172. let defaultProvider = this._providers.find(function(aProvider) {
  173. return aProvider.contractId === defaultActive;
  174. });
  175. if (defaultProvider) {
  176. this._updateActiveProvider(defaultProvider);
  177. }
  178. }
  179. },
  180. addProvider: function(aClassId, aContractId, aName) {
  181. debug("addProvider");
  182. //did not allow null or empty string to add.
  183. if(!aClassId || !aContractId || !aName) {
  184. return;
  185. }
  186. let existedProvider = this._providers.find(function(provider) {
  187. return provider.id === aClassId;
  188. });
  189. //skip if adding the existed provider.
  190. if (existedProvider) {
  191. debug("existing providers: " + JSON.stringify(existedProvider));
  192. return;
  193. }
  194. //dynamically add the provider info to list.
  195. this._providers.push({
  196. id: aClassId,
  197. name: aName,
  198. contractId: aContractId
  199. });
  200. debug("available providers: " + JSON.stringify(this._providers));
  201. },
  202. getProviders: function(aData, aMm) {
  203. debug("getProviders");
  204. aData.providers = [];
  205. for (let provider of this._providers) {
  206. aData.providers.push({
  207. name: provider.name,
  208. uuid: provider.id
  209. });
  210. }
  211. aMm.sendAsyncMessage("SystemUpdate:GetProviders:Result:OK", aData);
  212. },
  213. getActiveProvider: function(aData, aMm) {
  214. debug("getActiveProvider");
  215. let self = this;
  216. let providerInfo = this._providers.find(function(provider) {
  217. return self._isActiveProviderId(provider.id);
  218. });
  219. if (!providerInfo) {
  220. aData.error = "NotFoundError";
  221. aMm.sendAsyncMessage("SystemUpdate:GetActiveProvider:Result:Error", aData);
  222. return;
  223. }
  224. aData.provider = {
  225. name: providerInfo.name,
  226. uuid: providerInfo.id
  227. };
  228. aMm.sendAsyncMessage("SystemUpdate:GetActiveProvider:Result:OK", aData);
  229. },
  230. setActiveProvider: function(aData, aMm) {
  231. debug("setActiveProvider");
  232. let self = this;
  233. let selectedProvider = this._providers.find(function(provider) {
  234. return provider.id === aData.uuid;
  235. });
  236. if (!selectedProvider) {
  237. aData.error = "DataError";
  238. aMm.sendAsyncMessage("SystemUpdate:SetActiveProvider:Result:Error", aData);
  239. return;
  240. }
  241. if (!this._isActiveProviderId(selectedProvider.id)) {
  242. // not allow changing active provider while there is an ongoing update activity
  243. if (this.activeProvider && !this._activeProvider.isIdle()) {
  244. aData.error = "DataError";
  245. aMm.sendAsyncMessage("SystemUpdate:SetActiveProvider:Result:Error", aData);
  246. return;
  247. }
  248. this._updateActiveProvider(selectedProvider);
  249. Services.prefs.setCharPref("dom.system_update.active", selectedProvider.contractId);
  250. }
  251. aData.provider = {
  252. name: selectedProvider.name,
  253. uuid: selectedProvider.id
  254. };
  255. aMm.sendAsyncMessage("SystemUpdate:SetActiveProvider:Result:OK", aData);
  256. },
  257. receiveMessage: function(aMessage) {
  258. if (!aMessage.target.assertPermission("system-update")) {
  259. debug("receive message " + aMessage.name +
  260. " from a content process with no 'system-update' privileges.");
  261. return null;
  262. }
  263. let msg = aMessage.data || {};
  264. let mm = aMessage.target;
  265. switch (aMessage.name) {
  266. case "SystemUpdate:GetProviders": {
  267. this.getProviders(msg, mm);
  268. break;
  269. }
  270. case "SystemUpdate:GetActiveProvider": {
  271. this.getActiveProvider(msg, mm);
  272. break;
  273. }
  274. case "SystemUpdate:SetActiveProvider": {
  275. this.setActiveProvider(msg, mm);
  276. break;
  277. }
  278. case "SystemUpdate:CheckForUpdate": {
  279. if (this._isActiveProviderId(msg.uuid)) {
  280. this._activeProvider.checkForUpdate();
  281. }
  282. break;
  283. }
  284. case "SystemUpdate:StartDownload": {
  285. if (this._isActiveProviderId(msg.uuid)) {
  286. this._activeProvider.startDownload();
  287. }
  288. break;
  289. }
  290. case "SystemUpdate:StopDownload": {
  291. if (this._isActiveProviderId(msg.uuid)) {
  292. this._activeProvider.stopDownload();
  293. }
  294. break;
  295. }
  296. case "SystemUpdate:ApplyUpdate": {
  297. if (this._isActiveProviderId(msg.uuid)) {
  298. this._activeProvider.applyUpdate();
  299. }
  300. break;
  301. }
  302. case "SystemUpdate:SetParameter": {
  303. if (this._isActiveProviderId(msg.uuid)) {
  304. return this._activeProvider.setParameter(msg.name, msg.value);
  305. }
  306. break;
  307. }
  308. case "SystemUpdate:GetParameter": {
  309. if (this._isActiveProviderId(msg.uuid)) {
  310. return this._activeProvider.getParameter(msg.name);
  311. }
  312. break;
  313. }
  314. }
  315. },
  316. };
  317. SystemUpdateService.init();