From 3c0088b1bc041cb92bb8229fd6d3d359fc1f2464 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Wed, 9 Jul 2014 15:34:43 -0400 Subject: [PATCH] Backed out changeset ea4acc8b76dd (bug 903291) for frequent B2G crashtest failures and other regressions. --- dom/apps/src/AppsServiceChild.jsm | 297 +----------------- dom/apps/src/Webapps.js | 305 +++++++++++-------- dom/apps/src/Webapps.jsm | 152 ++++----- dom/apps/tests/test_packaged_app_common.js | 1 - dom/apps/tests/test_packaged_app_update.html | 18 +- dom/apps/tests/test_receipt_operations.html | 2 +- toolkit/devtools/server/actors/webapps.js | 2 +- 7 files changed, 271 insertions(+), 506 deletions(-) diff --git a/dom/apps/src/AppsServiceChild.jsm b/dom/apps/src/AppsServiceChild.jsm index 5bf6a5aad74..c7cb9890f76 100644 --- a/dom/apps/src/AppsServiceChild.jsm +++ b/dom/apps/src/AppsServiceChild.jsm @@ -8,10 +8,10 @@ const Cu = Components.utils; const Cc = Components.classes; const Ci = Components.interfaces; -// This module exposes a subset of the functionalities of the parent DOM -// Registry to content processes, to be used from the AppsService component. +// This module exposes a subset of the functionnalities of the parent DOM +// Registry to content processes, to be be used from the AppsService component. -this.EXPORTED_SYMBOLS = ["DOMApplicationRegistry", "WrappedManifestCache"]; +this.EXPORTED_SYMBOLS = ["DOMApplicationRegistry"]; Cu.import("resource://gre/modules/AppsUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); @@ -20,323 +20,54 @@ function debug(s) { //dump("-*- AppsServiceChild.jsm: " + s + "\n"); } -const APPS_IPC_MSG_NAMES = [ - "Webapps:AddApp", - "Webapps:RemoveApp", - "Webapps:UpdateApp", - "Webapps:CheckForUpdate:Return:KO", - "Webapps:FireEvent", - "Webapps:UpdateState" -]; - -// A simple cache for the wrapped manifests. -this.WrappedManifestCache = { - _cache: { }, - - // Gets an entry from the cache, and populates the cache if needed. - get: function mcache_get(aManifestURL, aManifest, aWindow, aInnerWindowID) { - if (!aManifest) { - return; - } - - if (!(aManifestURL in this._cache)) { - this._cache[aManifestURL] = { }; - } - - let winObjs = this._cache[aManifestURL]; - if (!(aInnerWindowID in winObjs)) { - winObjs[aInnerWindowID] = Cu.cloneInto(aManifest, aWindow); - } - - return winObjs[aInnerWindowID]; - }, - - // Invalidates an entry in the cache. - evict: function mcache_evict(aManifestURL, aInnerWindowID) { - debug("Evicting manifest " + aManifestURL + " window ID " + - aInnerWindowID); - if (aManifestURL in this._cache) { - let winObjs = this._cache[aManifestURL]; - if (aInnerWindowID in winObjs) { - delete winObjs[aInnerWindowID]; - } - - if (Object.keys(winObjs).length == 0) { - delete this._cache[aManifestURL]; - } - } - }, - - observe: function(aSubject, aTopic, aData) { - // Clear the cache on memory pressure. - this._cache = { }; - Cu.forceGC(); - }, - - init: function() { - Services.obs.addObserver(this, "memory-pressure", false); - } -}; - -this.WrappedManifestCache.init(); - - -// DOMApplicationRegistry keeps a cache containing a list of apps in the device. -// This information is updated with the data received from the main process and -// it is queried by the DOM objects to set their state. -// This module handle all the messages broadcasted from the parent process, -// including DOM events, which are dispatched to the corresponding DOM objects. - this.DOMApplicationRegistry = { - // DOMApps will hold a list of arrays of weak references to - // mozIDOMApplication objects indexed by manifest URL. - DOMApps: {}, - - ready: false, - webapps: null, - init: function init() { + debug("init"); this.cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"] .getService(Ci.nsISyncMessageSender); - APPS_IPC_MSG_NAMES.forEach((function(aMsgName) { + ["Webapps:AddApp", "Webapps:RemoveApp"].forEach((function(aMsgName) { this.cpmm.addMessageListener(aMsgName, this); }).bind(this)); - this.cpmm.sendAsyncMessage("Webapps:RegisterForMessages", { - messages: APPS_IPC_MSG_NAMES - }); - // We need to prime the cache with the list of apps. - let list = this.cpmm.sendSyncMessage("Webapps:GetList", { })[0]; - this.webapps = list.webapps; + // XXX shoud we do this async and block callers if it's not yet there? + this.webapps = this.cpmm.sendSyncMessage("Webapps:GetList", { })[0]; + // We need a fast mapping from localId -> app, so we add an index. - // We also add the manifest to the app object. this.localIdIndex = { }; for (let id in this.webapps) { let app = this.webapps[id]; this.localIdIndex[app.localId] = app; - app.manifest = list.manifests[id]; } Services.obs.addObserver(this, "xpcom-shutdown", false); }, observe: function(aSubject, aTopic, aData) { - // cpmm.addMessageListener causes the DOMApplicationRegistry object to - // live forever if we don't clean up properly. + // cpmm.addMessageListener causes the DOMApplicationRegistry object to live + // forever if we don't clean up properly. this.webapps = null; - this.DOMApps = null; - - APPS_IPC_MSG_NAMES.forEach((aMsgName) => { + ["Webapps:AddApp", "Webapps:RemoveApp"].forEach((function(aMsgName) { this.cpmm.removeMessageListener(aMsgName, this); - }); - - this.cpmm.sendAsyncMessage("Webapps:UnregisterForMessages", - APPS_IPC_MSG_NAMES) + }).bind(this)); }, receiveMessage: function receiveMessage(aMessage) { debug("Received " + aMessage.name + " message."); - let msg = aMessage.data; + let msg = aMessage.json; switch (aMessage.name) { case "Webapps:AddApp": this.webapps[msg.id] = msg.app; this.localIdIndex[msg.app.localId] = msg.app; - if (msg.manifest) { - this.webapps[msg.id].manifest = msg.manifest; - } break; case "Webapps:RemoveApp": - delete this.DOMApps[this.webapps[msg.id].manifestURL]; delete this.localIdIndex[this.webapps[msg.id].localId]; delete this.webapps[msg.id]; break; - case "Webapps:UpdateApp": - let app = this.webapps[msg.oldId]; - if (!app) { - return; - } - - if (msg.app) { - for (let prop in msg.app) { - app[prop] = msg.app[prop]; - } - } - - this.webapps[msg.newId] = app; - this.localIdIndex[app.localId] = app; - delete this.webapps[msg.oldId]; - - let apps = this.DOMApps[msg.app.manifestURL]; - if (!apps) { - return; - } - for (let i = 0; i < apps.length; i++) { - let domApp = apps[i].get(); - if (!domApp) { - apps.splice(i); - continue; - } - domApp._proxy = new Proxy(domApp, { - get: function(target, prop) { - if (!DOMApplicationRegistry.webapps[msg.newId]) { - return; - } - return DOMApplicationRegistry.webapps[msg.newId][prop]; - }, - set: function(target, prop, val) { - if (!DOMApplicationRegistry.webapps[msg.newId]) { - return; - } - DOMApplicationRegistry.webapps[msg.newId][prop] = val; - return; - }, - }); - } - break; - case "Webapps:FireEvent": - this._fireEvent(aMessage); - break; - case "Webapps:UpdateState": - this._updateState(msg); - break; - case "Webapps:CheckForUpdate:Return:KO": - let DOMApps = this.DOMApps[msg.manifestURL]; - if (!DOMApps || !msg.requestID) { - return; - } - DOMApps.forEach((DOMApp) => { - let domApp = DOMApp.get(); - if (domApp && msg.requestID) { - domApp._fireRequestResult(aMessage, true /* aIsError */); - } - }); - break; } }, - /** - * mozIDOMApplication management - */ - - // Every time a DOM app is created, we save a weak reference to it that will - // be used to dispatch events and fire request results. - addDOMApp: function(aApp, aManifestURL, aId) { - let weakRef = Cu.getWeakReference(aApp); - - if (!this.DOMApps[aManifestURL]) { - this.DOMApps[aManifestURL] = []; - } - - let apps = this.DOMApps[aManifestURL]; - - // Get rid of dead weak references. - for (let i = 0; i < apps.length; i++) { - if (!apps[i].get()) { - apps.splice(i); - } - } - - apps.push(weakRef); - - // Each DOM app contains a proxy object used to build their state. We - // return the handler for this proxy object with traps to get and set - // app properties kept in the DOMApplicationRegistry app cache. - return { - get: function(target, prop) { - if (!DOMApplicationRegistry.webapps[aId]) { - return; - } - return DOMApplicationRegistry.webapps[aId][prop]; - }, - set: function(target, prop, val) { - if (!DOMApplicationRegistry.webapps[aId]) { - return; - } - DOMApplicationRegistry.webapps[aId][prop] = val; - return; - }, - }; - }, - - _fireEvent: function(aMessage) { - let msg = aMessage.data; - debug("_fireEvent " + JSON.stringify(msg)); - if (!this.DOMApps || !msg.manifestURL || !msg.eventType) { - return; - } - - let DOMApps = this.DOMApps[msg.manifestURL]; - if (!DOMApps) { - return; - } - - // The parent might ask childs to trigger more than one event in one - // shot, so in order to avoid needless IPC we allow an array for the - // 'eventType' IPC message field. - if (!Array.isArray(msg.eventType)) { - msg.eventType = [msg.eventType]; - } - - DOMApps.forEach((DOMApp) => { - let domApp = DOMApp.get(); - if (!domApp) { - return; - } - msg.eventType.forEach((aEventType) => { - if ('on' + aEventType in domApp) { - domApp._fireEvent(aEventType); - } - }); - - if (msg.requestID) { - aMessage.data.result = msg.manifestURL; - domApp._fireRequestResult(aMessage); - } - }); - }, - - _updateState: function(aMessage) { - if (!this.DOMApps || !aMessage.id) { - return; - } - - let app = this.webapps[aMessage.id]; - if (!app) { - return; - } - - if (aMessage.app) { - for (let prop in aMessage.app) { - app[prop] = aMessage.app[prop]; - } - } - - if ("error" in aMessage) { - app.downloadError = aMessage.error; - } - - if (aMessage.manifest) { - app.manifest = aMessage.manifest; - // Evict the wrapped manifest cache for all the affected DOM objects. - let DOMApps = this.DOMApps[app.manifestURL]; - if (!DOMApps) { - return; - } - DOMApps.forEach((DOMApp) => { - let domApp = DOMApp.get(); - if (!domApp) { - return; - } - WrappedManifestCache.evict(app.manifestURL, domApp.innerWindowID); - }); - } - }, - - /** - * nsIAppsService API - */ getAppByManifestURL: function getAppByManifestURL(aManifestURL) { debug("getAppByManifestURL " + aManifestURL); return AppsUtils.getAppByManifestURL(this.webapps, aManifestURL); @@ -358,7 +89,7 @@ this.DOMApplicationRegistry = { }, getAppByLocalId: function getAppByLocalId(aLocalId) { - debug("getAppByLocalId " + aLocalId + " - ready: " + this.ready); + debug("getAppByLocalId " + aLocalId); let app = this.localIdIndex[aLocalId]; if (!app) { debug("Ouch, No app!"); diff --git a/dom/apps/src/Webapps.js b/dom/apps/src/Webapps.js index 35a393907a7..939a0db982d 100644 --- a/dom/apps/src/Webapps.js +++ b/dom/apps/src/Webapps.js @@ -12,7 +12,6 @@ Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); Cu.import("resource://gre/modules/AppsUtils.jsm"); Cu.import("resource://gre/modules/BrowserElementPromptService.jsm"); -Cu.import("resource://gre/modules/AppsServiceChild.jsm"); XPCOMUtils.defineLazyServiceGetter(this, "cpmm", "@mozilla.org/childprocessmessagemanager;1", @@ -279,9 +278,50 @@ WebappsRegistry.prototype = { * mozIDOMApplication object */ +// A simple cache for the wrapped manifests. +let manifestCache = { + _cache: { }, + + // Gets an entry from the cache, and populates the cache if needed. + get: function mcache_get(aManifestURL, aManifest, aWindow, aInnerWindowID) { + if (!(aManifestURL in this._cache)) { + this._cache[aManifestURL] = { }; + } + + let winObjs = this._cache[aManifestURL]; + if (!(aInnerWindowID in winObjs)) { + winObjs[aInnerWindowID] = Cu.cloneInto(aManifest, aWindow); + } + + return winObjs[aInnerWindowID]; + }, + + // Invalidates an entry in the cache. + evict: function mcache_evict(aManifestURL, aInnerWindowID) { + if (aManifestURL in this._cache) { + let winObjs = this._cache[aManifestURL]; + if (aInnerWindowID in winObjs) { + delete winObjs[aInnerWindowID]; + } + + if (Object.keys(winObjs).length == 0) { + delete this._cache[aManifestURL]; + } + } + }, + + observe: function(aSubject, aTopic, aData) { + // Clear the cache on memory pressure. + this._cache = { }; + }, + + init: function() { + Services.obs.addObserver(this, "memory-pressure", false); + } +}; + function createApplicationObject(aWindow, aApp) { - let app = Cc["@mozilla.org/webapps/application;1"] - .createInstance(Ci.mozIDOMApplication); + let app = Cc["@mozilla.org/webapps/application;1"].createInstance(Ci.mozIDOMApplication); app.wrappedJSObject.init(aWindow, aApp); return app; } @@ -294,12 +334,27 @@ WebappsApplication.prototype = { __proto__: DOMRequestIpcHelper.prototype, init: function(aWindow, aApp) { - let proxyHandler = DOMApplicationRegistry.addDOMApp(this, - aApp.manifestURL, - aApp.id); - this._proxy = new Proxy(this, proxyHandler); - this._window = aWindow; + let principal = this._window.document.nodePrincipal; + this._appStatus = principal.appStatus; + this.origin = aApp.origin; + this._manifest = aApp.manifest; + this._updateManifest = aApp.updateManifest; + this.manifestURL = aApp.manifestURL; + this.receipts = aApp.receipts; + this.installOrigin = aApp.installOrigin; + this.installTime = aApp.installTime; + this.installState = aApp.installState || "installed"; + this.removable = aApp.removable; + this.lastUpdateCheck = aApp.lastUpdateCheck ? aApp.lastUpdateCheck + : Date.now(); + this.updateTime = aApp.updateTime ? aApp.updateTime + : aApp.installTime; + this.progress = NaN; + this.downloadAvailable = aApp.downloadAvailable; + this.downloading = aApp.downloading; + this.readyToApplyDownload = aApp.readyToApplyDownload; + this.downloadSize = aApp.downloadSize || 0; this._onprogress = null; this._ondownloadsuccess = null; @@ -307,83 +362,40 @@ WebappsApplication.prototype = { this._ondownloadavailable = null; this._ondownloadapplied = null; - this.initDOMRequestHelper(aWindow); - }, + this._downloadError = null; - get _appStatus() { - return this._proxy.appStatus; - }, + this.initDOMRequestHelper(aWindow, [ + { name: "Webapps:CheckForUpdate:Return:KO", weakRef: true }, + { name: "Webapps:Connect:Return:OK", weakRef: true }, + { name: "Webapps:Connect:Return:KO", weakRef: true }, + { name: "Webapps:FireEvent", weakRef: true }, + { name: "Webapps:GetConnections:Return:OK", weakRef: true }, + { name: "Webapps:UpdateState", weakRef: true } + ]); - get downloadAvailable() { - return this._proxy.downloadAvailable; - }, - - get downloading() { - return this._proxy.downloading; - }, - - get downloadSize() { - return this._proxy.downloadSize; - }, - - get installOrigin() { - return this._proxy.installOrigin; - }, - - get installState() { - return this._proxy.installState; - }, - - get installTime() { - return this._proxy.installTime; - }, - - get lastUpdateCheck() { - return this._proxy.lastUpdateCheck; - }, - - get manifestURL() { - return this._proxy.manifestURL; - }, - - get origin() { - return this._proxy.origin; - }, - - get progress() { - return this._proxy.progress; - }, - - get readyToApplyDownload() { - return this._proxy.readyToApplyDownload; - }, - - get receipts() { - return this._proxy.receipts; - }, - - set receipts(aReceipts) { - this._proxy.receipts = aReceipts; - }, - - get removable() { - return this._proxy.removable; - }, - - get updateTime() { - return this._proxy.updateTime; + cpmm.sendAsyncMessage("Webapps:RegisterForMessages", { + messages: ["Webapps:FireEvent", + "Webapps:UpdateState"], + app: { + id: this.id, + manifestURL: this.manifestURL, + installState: this.installState, + downloading: this.downloading + } + }); }, get manifest() { - return WrappedManifestCache.get(this.manifestURL, - this._proxy.manifest, - this._window, - this.innerWindowID); + return manifestCache.get(this.manifestURL, + this._manifest, + this._window, + this.innerWindowID); }, get updateManifest() { - return this._proxy.updateManifest ? - Cu.cloneInto(this._proxy.updateManifest, this._window) : null; + return this.updateManifest = + this._updateManifest ? Cu.cloneInto(this._updateManifest, this._window) + : null; }, set onprogress(aCallback) { @@ -428,10 +440,10 @@ WebappsApplication.prototype = { get downloadError() { // Only return DOMError when we have an error. - if (!this._proxy.downloadError) { + if (!this._downloadError) { return null; } - return new this._window.DOMError(this._proxy.downloadError); + return new this._window.DOMError(this._downloadError); }, download: function() { @@ -473,11 +485,12 @@ WebappsApplication.prototype = { BrowserElementPromptService.getBrowserElementChildForWindow(this._window); if (browserChild) { this.addMessageListeners("Webapps:ClearBrowserData:Return"); - browserChild.messageManager.sendAsyncMessage("Webapps:ClearBrowserData", { - manifestURL: this.manifestURL, - oid: this._id, - requestID: this.getRequestId(request) - }); + browserChild.messageManager.sendAsyncMessage( + "Webapps:ClearBrowserData", + { manifestURL: this.manifestURL, + oid: this._id, + requestID: this.getRequestId(request) } + ); } else { Services.DOMRequest.fireErrorAsync(request, "NO_CLEARABLE_BROWSER"); } @@ -485,33 +498,28 @@ WebappsApplication.prototype = { }, connect: function(aKeyword, aRules) { - this.addMessageListeners(["Webapps:Connect:Return:OK", - "Webapps:Connect:Return:KO"]); return this.createPromise(function (aResolve, aReject) { - cpmm.sendAsyncMessage("Webapps:Connect", { - keyword: aKeyword, - rules: aRules, - manifestURL: this.manifestURL, - outerWindowID: this._id, - requestID: this.getPromiseResolverId({ - resolve: aResolve, - reject: aReject - }) - }); + cpmm.sendAsyncMessage("Webapps:Connect", + { keyword: aKeyword, + rules: aRules, + manifestURL: this.manifestURL, + outerWindowID: this._id, + requestID: this.getPromiseResolverId({ + resolve: aResolve, + reject: aReject + })}); }.bind(this)); }, getConnections: function() { - this.addMessageListeners("Webapps:GetConnections:Return:OK"); return this.createPromise(function (aResolve, aReject) { - cpmm.sendAsyncMessage("Webapps:GetConnections", { - manifestURL: this.manifestURL, - outerWindowID: this._id, - requestID: this.getPromiseResolverId({ - resolve: aResolve, - reject: aReject - }) - }); + cpmm.sendAsyncMessage("Webapps:GetConnections", + { manifestURL: this.manifestURL, + outerWindowID: this._id, + requestID: this.getPromiseResolverId({ + resolve: aResolve, + reject: aReject + })}); }.bind(this)); }, @@ -560,7 +568,12 @@ WebappsApplication.prototype = { uninit: function() { this._onprogress = null; - WrappedManifestCache.evict(this.manifestURL, this.innerWindowID); + cpmm.sendAsyncMessage("Webapps:UnregisterForMessages", [ + "Webapps:FireEvent", + "Webapps:UpdateState" + ]); + + manifestCache.evict(this.manifestURL, this.innerWindowID); }, _fireEvent: function(aName) { @@ -577,16 +590,22 @@ WebappsApplication.prototype = { } }, - _fireRequestResult: function(aMessage, aIsError) { - let req; - let msg = aMessage.data; - req = this.takeRequest(msg.requestID); - if (!req) { - return; + _updateState: function(aMsg) { + if (aMsg.app) { + for (let prop in aMsg.app) { + this[prop] = aMsg.app[prop]; + } } - aIsError ? Services.DOMRequest.fireError(req, msg.error) - : Services.DOMRequest.fireSuccess(req, msg.result); + // Intentional use of 'in' so we unset the error if this is explicitly null. + if ('error' in aMsg) { + this._downloadError = aMsg.error; + } + + if (aMsg.manifest) { + this._manifest = aMsg.manifest; + manifestCache.evict(this.manifestURL, this.innerWindowID); + } }, receiveMessage: function(aMessage) { @@ -600,7 +619,10 @@ WebappsApplication.prototype = { req = this.takeRequest(msg.requestID); } - if (msg.oid !== this._id || !req) { + // ondownload* callbacks should be triggered on all app instances + if ((msg.oid != this._id || !req) && + aMessage.name !== "Webapps:FireEvent" && + aMessage.name !== "Webapps:UpdateState") { return; } @@ -615,13 +637,51 @@ WebappsApplication.prototype = { "Webapps:Launch:Return:KO"]); Services.DOMRequest.fireSuccess(req, null); break; + case "Webapps:CheckForUpdate:Return:KO": + Services.DOMRequest.fireError(req, msg.error); + break; + case "Webapps:FireEvent": + if (msg.manifestURL != this.manifestURL) { + return; + } + + // The parent might ask childs to trigger more than one event in one + // shot, so in order to avoid needless IPC we allow an array for the + // 'eventType' IPC message field. + if (!Array.isArray(msg.eventType)) { + msg.eventType = [msg.eventType]; + } + + msg.eventType.forEach((aEventType) => { + // If we are in a successful state clear any past errors. + if (aEventType === 'downloadapplied' || + aEventType === 'downloadsuccess') { + this._downloadError = null; + } + + if ("_on" + aEventType in this) { + this._fireEvent(aEventType); + } else { + dump("Unsupported event type " + aEventType + "\n"); + } + }); + + if (req) { + Services.DOMRequest.fireSuccess(req, this.manifestURL); + } + break; + case "Webapps:UpdateState": + if (msg.manifestURL != this.manifestURL) { + return; + } + + this._updateState(msg); + break; case "Webapps:ClearBrowserData:Return": this.removeMessageListeners(aMessage.name); Services.DOMRequest.fireSuccess(req, null); break; case "Webapps:Connect:Return:OK": - this.removeMessageListeners(["Webapps:Connect:Return:OK", - "Webapps:Connect:Return:KO"]); let messagePorts = []; msg.messagePortIDs.forEach((aPortID) => { let port = new this._window.MozInterAppMessagePort(aPortID); @@ -630,12 +690,9 @@ WebappsApplication.prototype = { req.resolve(messagePorts); break; case "Webapps:Connect:Return:KO": - this.removeMessageListeners(["Webapps:Connect:Return:OK", - "Webapps:Connect:Return:KO"]); req.reject("No connections registered"); break; case "Webapps:GetConnections:Return:OK": - this.removeMessageListeners(aMessage.name); let connections = []; msg.connections.forEach((aConnection) => { let connection = @@ -817,8 +874,12 @@ WebappsApplicationMgmt.prototype = { break; case "Webapps:Uninstall:Broadcast:Return:OK": if (this._onuninstall) { + let detail = { + manifestURL: msg.manifestURL, + origin: msg.origin + }; let event = new this._window.MozApplicationEvent("applicationuninstall", - { application : createApplicationObject(this._window, msg) }); + { application : createApplicationObject(this._window, detail) }); this._onuninstall.handleEvent(event); } break; @@ -847,5 +908,7 @@ WebappsApplicationMgmt.prototype = { classDescription: "Webapps Application Mgmt"}) } +manifestCache.init(); + this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsRegistry, WebappsApplication]); diff --git a/dom/apps/src/Webapps.jsm b/dom/apps/src/Webapps.jsm index 37c86bf4cdd..216fef131e5 100755 --- a/dom/apps/src/Webapps.jsm +++ b/dom/apps/src/Webapps.jsm @@ -1141,8 +1141,8 @@ this.DOMApplicationRegistry = { this.removeMessageListener(["Webapps:Internal:AllMessages"], mm); break; case "Webapps:GetList": - return this.doGetList(); - break; + this.addMessageListener(["Webapps:AddApp", "Webapps:RemoveApp"], null, mm); + return this.webapps; case "Webapps:Download": this.startDownload(msg.manifestURL); break; @@ -1245,38 +1245,6 @@ this.DOMApplicationRegistry = { return deferred.promise; }, - /** - * Returns the full list of apps and manifests. - */ - doGetList: function() { - let tmp = []; - - for (let id in this.webapps) { - tmp.push({ id: id }); - } - - let res = {}; - let done = false; - - this._readManifests(tmp).then( - function(manifests) { - manifests.forEach((item) => { - res[item.id] = item.manifest; - }); - done = true; - } - ); - - let thread = Services.tm.currentThread; - while (!done) { - //debug("before processNextEvent"); - thread.processNextEvent(/* mayWait */ true); - //after("before processNextEvent"); - } - return { webapps: this.webapps, manifests: res }; - }, - - doLaunch: function (aData, aMm) { this.launch( aData.manifestURL, @@ -1362,7 +1330,7 @@ this.DOMApplicationRegistry = { downloading: false }, error: error, - id: app.id + manifestURL: app.manifestURL, }) this.broadcastMessage("Webapps:FireEvent", { eventType: "downloaderror", @@ -1393,7 +1361,7 @@ this.DOMApplicationRegistry = { if (!app.downloadAvailable) { this.broadcastMessage("Webapps:UpdateState", { error: "NO_DOWNLOAD_AVAILABLE", - id: app.id + manifestURL: app.manifestURL }); this.broadcastMessage("Webapps:FireEvent", { eventType: "downloaderror", @@ -1441,7 +1409,7 @@ this.DOMApplicationRegistry = { this.broadcastMessage("Webapps:UpdateState", { app: app, manifest: jsonManifest, - id: app.id + manifestURL: aManifestURL }); this.broadcastMessage("Webapps:FireEvent", { eventType: "downloadsuccess", @@ -1485,7 +1453,7 @@ this.DOMApplicationRegistry = { this.broadcastMessage("Webapps:UpdateState", { app: app, - id: app.id + manifestURL: aManifestURL }); this.broadcastMessage("Webapps:FireEvent", { eventType: "downloadsuccess", @@ -1587,7 +1555,7 @@ this.DOMApplicationRegistry = { this.broadcastMessage("Webapps:UpdateState", { app: app, manifest: newManifest, - id: app.id + manifestURL: app.manifestURL }); this.broadcastMessage("Webapps:FireEvent", { eventType: "downloadapplied", @@ -1626,7 +1594,7 @@ this.DOMApplicationRegistry = { installState: aApp.installState, progress: 0 }, - id: aApp.id + manifestURL: aApp.manifestURL }); let cacheUpdate = updateSvc.scheduleAppUpdate( appcacheURI, docURI, aApp.localId, false, aProfileDir); @@ -1676,7 +1644,6 @@ this.DOMApplicationRegistry = { debug("checkForUpdate for " + aData.manifestURL); function sendError(aError) { - debug("checkForUpdate error " + aError); aData.error = aError; aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData); } @@ -1706,7 +1673,8 @@ this.DOMApplicationRegistry = { // then we can't have an update. if (app.origin.startsWith("app://") && app.manifestURL.startsWith("app://")) { - sendError("NOT_UPDATABLE"); + aData.error = "NOT_UPDATABLE"; + aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData); return; } @@ -1723,7 +1691,8 @@ this.DOMApplicationRegistry = { if (onlyCheckAppCache) { // Bail out for packaged apps. if (app.origin.startsWith("app://")) { - sendError("NOT_UPDATABLE"); + aData.error = "NOT_UPDATABLE"; + aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData); return; } @@ -1731,7 +1700,8 @@ this.DOMApplicationRegistry = { this._readManifests([{ id: id }]).then((aResult) => { let manifest = aResult[0].manifest; if (!manifest.appcache_path) { - sendError("NOT_UPDATABLE"); + aData.error = "NOT_UPDATABLE"; + aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData); return; } @@ -1747,7 +1717,7 @@ this.DOMApplicationRegistry = { this._saveApps().then(() => { this.broadcastMessage("Webapps:UpdateState", { app: app, - id: app.id + manifestURL: app.manifestURL }); this.broadcastMessage("Webapps:FireEvent", { eventType: "downloadavailable", @@ -1756,7 +1726,8 @@ this.DOMApplicationRegistry = { }); }); } else { - sendError("NOT_UPDATABLE"); + aData.error = "NOT_UPDATABLE"; + aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData); } } }; @@ -1816,7 +1787,7 @@ this.DOMApplicationRegistry = { : "downloadapplied"; aMm.sendAsyncMessage("Webapps:UpdateState", { app: app, - id: app.id + manifestURL: app.manifestURL }); aMm.sendAsyncMessage("Webapps:FireEvent", { eventType: eventType, @@ -1843,7 +1814,7 @@ this.DOMApplicationRegistry = { : "downloadapplied"; aMm.sendAsyncMessage("Webapps:UpdateState", { app: app, - id: app.id + manifestURL: app.manifestURL }); aMm.sendAsyncMessage("Webapps:FireEvent", { eventType: eventType, @@ -1952,7 +1923,7 @@ this.DOMApplicationRegistry = { this.broadcastMessage("Webapps:UpdateState", { app: aApp, - id: aApp.id + manifestURL: aApp.manifestURL }); this.broadcastMessage("Webapps:FireEvent", { eventType: "downloadavailable", @@ -2018,7 +1989,7 @@ this.DOMApplicationRegistry = { this.broadcastMessage("Webapps:UpdateState", { app: aApp, manifest: aApp.manifest, - id: aApp.id + manifestURL: aApp.manifestURL }); this.broadcastMessage("Webapps:FireEvent", { eventType: "downloadapplied", @@ -2052,7 +2023,7 @@ this.DOMApplicationRegistry = { this.broadcastMessage("Webapps:UpdateState", { app: aApp, manifest: aApp.manifest, - id: aApp.id + manifestURL: aApp.manifestURL }); this.broadcastMessage("Webapps:FireEvent", { eventType: eventType, @@ -2479,8 +2450,7 @@ this.DOMApplicationRegistry = { } this._saveApps().then(() => { - this.broadcastMessage("Webapps:AddApp", - { id: app.id, app: app, manifest: aManifest }); + this.broadcastMessage("Webapps:AddApp", { id: app.id, app: app }); }); }), @@ -2580,8 +2550,6 @@ this.DOMApplicationRegistry = { // saved in the registry. yield this._saveApps(); - aData.isPackage ? appObject.updateManifest = jsonManifest : - appObject.manifest = jsonManifest; this.broadcastMessage("Webapps:AddApp", { id: id, app: appObject }); // The presence of a requestID means that we have a page to update. @@ -2658,8 +2626,7 @@ this.DOMApplicationRegistry = { delete this._manifestCache[aId]; } - this.broadcastMessage("Webapps:AddApp", - { id: aId, app: aNewApp, manifest: aManifest }); + this.broadcastMessage("Webapps:AddApp", { id: aId, app: aNewApp }); Services.obs.notifyObservers(null, "webapps-installed", JSON.stringify({ manifestURL: aNewApp.manifestURL })); @@ -2793,11 +2760,14 @@ this.DOMApplicationRegistry = { oldApp, aNewApp); - AppDownloadManager.add(aNewApp.manifestURL, { - channel: requestChannel, - appId: id, - previousState: aIsUpdate ? "installed" : "pending" - }); + AppDownloadManager.add( + aNewApp.manifestURL, + { + channel: requestChannel, + appId: id, + previousState: aIsUpdate ? "installed" : "pending" + } + ); // We set the 'downloading' flag to true right before starting the fetch. oldApp.downloading = true; @@ -2817,7 +2787,7 @@ this.DOMApplicationRegistry = { // Clear any previous download errors. error: null, app: oldApp, - id: id + manifestURL: aNewApp.manifestURL }); let zipFile = yield this._getPackage(requestChannel, id, oldApp, aNewApp); @@ -2832,7 +2802,7 @@ this.DOMApplicationRegistry = { // We send an "applied" event right away so code awaiting that event // can proceed to access the app. We also throw an error to alert // the caller that the package wasn't downloaded. - this._sendAppliedEvent(oldApp); + this._sendAppliedEvent(aNewApp, oldApp, id); throw new Error("PACKAGE_UNCHANGED"); } @@ -2972,7 +2942,7 @@ this.DOMApplicationRegistry = { app: { progress: aProgress }, - id: aNewApp.id + manifestURL: aNewApp.manifestURL }); this.broadcastMessage("Webapps:FireEvent", { eventType: "progress", @@ -3089,24 +3059,27 @@ this.DOMApplicationRegistry = { * something similar after updating the app, and we could refactor both cases * to use the same code to send the "applied" event. * - * @param aApp {Object} app data + * @param aNewApp {Object} the new app data + * @param aOldApp {Object} the currently stored app data + * @param aId {String} the unique id of the app */ - _sendAppliedEvent: function(aApp) { - aApp.downloading = false; - aApp.downloadAvailable = false; - aApp.downloadSize = 0; - aApp.installState = "installed"; - aApp.readyToApplyDownload = false; - if (aApp.staged && aApp.staged.manifestHash) { + _sendAppliedEvent: function(aNewApp, aOldApp, aId) { + aOldApp.downloading = false; + aOldApp.downloadAvailable = false; + aOldApp.downloadSize = 0; + aOldApp.installState = "installed"; + aOldApp.readyToApplyDownload = false; + if (aOldApp.staged && aOldApp.staged.manifestHash) { // If we're here then the manifest has changed but the package // hasn't. Let's clear this, so we don't keep offering // a bogus update to the user - aApp.manifestHash = aApp.staged.manifestHash; - aApp.etag = aApp.staged.etag || aApp.etag; - aApp.staged = {}; - // Move the staged update manifest to a non staged one. + aOldApp.manifestHash = aOldApp.staged.manifestHash; + aOldApp.etag = aOldApp.staged.etag || aOldApp.etag; + aOldApp.staged = {}; + + // Move the staged update manifest to a non staged one. try { - let staged = this._getAppDir(aApp.id); + let staged = this._getAppDir(aId); staged.append("staged-update.webapp"); staged.moveTo(staged.parent, "update.webapp"); } catch (ex) { @@ -3117,15 +3090,15 @@ this.DOMApplicationRegistry = { // Save the updated registry, and cleanup the tmp directory. this._saveApps().then(() => { this.broadcastMessage("Webapps:UpdateState", { - app: aApp, - id: aApp.id + app: aOldApp, + manifestURL: aNewApp.manifestURL }); this.broadcastMessage("Webapps:FireEvent", { - manifestURL: aApp.manifestURL, + manifestURL: aNewApp.manifestURL, eventType: ["downloadsuccess", "downloadapplied"] }); }); - let file = FileUtils.getFile("TmpD", ["webapps", aApp.id], false); + let file = FileUtils.getFile("TmpD", ["webapps", aId], false); if (file && file.exists()) { file.remove(true); } @@ -3430,10 +3403,9 @@ this.DOMApplicationRegistry = { dir.moveTo(parent, newId); }); // Signals that we need to swap the old id with the new app. - this.broadcastMessage("Webapps:UpdateApp", { oldId: oldId, - newId: newId, - app: aOldApp }); - + this.broadcastMessage("Webapps:RemoveApp", { id: oldId }); + this.broadcastMessage("Webapps:AddApp", { id: newId, + app: aOldApp }); } } }, @@ -3536,7 +3508,7 @@ this.DOMApplicationRegistry = { this.broadcastMessage("Webapps:UpdateState", { app: aOldApp, error: aError, - id: aNewApp.id + manifestURL: aNewApp.manifestURL }); this.broadcastMessage("Webapps:FireEvent", { eventType: "downloaderror", @@ -4088,7 +4060,7 @@ AppcacheObserver.prototype = { let app = this.app; DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", { app: app, - id: app.id + manifestURL: app.manifestURL }); DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", { eventType: "progress", @@ -4120,7 +4092,7 @@ AppcacheObserver.prototype = { app.downloadAvailable = false; DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", { app: app, - id: app.id + manifestURL: app.manifestURL }); DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", { eventType: ["downloadsuccess", "downloadapplied"], @@ -4143,7 +4115,7 @@ AppcacheObserver.prototype = { DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", { app: app, error: aError, - id: app.id + manifestURL: app.manifestURL }); DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", { eventType: "downloaderror", diff --git a/dom/apps/tests/test_packaged_app_common.js b/dom/apps/tests/test_packaged_app_common.js index 7c91021f1f6..9a0bddbff4c 100644 --- a/dom/apps/tests/test_packaged_app_common.js +++ b/dom/apps/tests/test_packaged_app_common.js @@ -98,7 +98,6 @@ var PackagedTestHelper = (function PackagedTestHelper() { var aApp = evt.application; aApp.ondownloaderror = function(evt) { var error = aApp.downloadError.name; - ok(true, "Got downloaderror " + error); if (error == aExpectedError) { ok(true, "Got expected " + aExpectedError); var expected = { diff --git a/dom/apps/tests/test_packaged_app_update.html b/dom/apps/tests/test_packaged_app_update.html index 3b1fc995775..1278e767ca4 100644 --- a/dom/apps/tests/test_packaged_app_update.html +++ b/dom/apps/tests/test_packaged_app_update.html @@ -79,15 +79,15 @@ function updateApp(aExpectedReady, aPreviousVersion, aNextVersion) { checkLastAppState.bind(PackagedTestHelper, miniManifestURL, false, false, aNextVersion, PackagedTestHelper.next); - var ondownloadsuccesshandler = - checkLastAppState.bind(undefined, miniManifestURL, - aExpectedReady, false, aPreviousVersion, - function() { - navigator.mozApps.mgmt.applyDownload(lApp); - }); + var ondownloadsuccesshandler = + checkLastAppState.bind(undefined, miniManifestURL, + aExpectedReady, false, aPreviousVersion, + function() { + navigator.mozApps.mgmt.applyDownload(lApp); + }); - checkForUpdate(true, ondownloadsuccesshandler, ondownloadappliedhandler, - null, true); + checkForUpdate(true, ondownloadsuccesshandler, ondownloadappliedhandler, null, + true); } @@ -254,7 +254,7 @@ var steps = [ "&appName=arandomname" + "&appToFail1"; PackagedTestHelper.checkAppDownloadError(miniManifestURL, - "MANIFEST_MISMATCH", 1, false, true, + "MANIFEST_MISMATCH", 2, false, true, "arandomname", function () { checkForUpdate(false, null, null, null, false, diff --git a/dom/apps/tests/test_receipt_operations.html b/dom/apps/tests/test_receipt_operations.html index 7ebe84ccfae..0907e8d964c 100644 --- a/dom/apps/tests/test_receipt_operations.html +++ b/dom/apps/tests/test_receipt_operations.html @@ -243,4 +243,4 @@ addLoadEvent(go); - + \ No newline at end of file diff --git a/toolkit/devtools/server/actors/webapps.js b/toolkit/devtools/server/actors/webapps.js index c4221cda95a..8c96990ba2e 100644 --- a/toolkit/devtools/server/actors/webapps.js +++ b/toolkit/devtools/server/actors/webapps.js @@ -262,7 +262,7 @@ WebappsActor.prototype = { reg.broadcastMessage("Webapps:UpdateState", { app: aApp, manifest: manifest, - id: aApp.id + manifestURL: aApp.manifestURL }); reg.broadcastMessage("Webapps:FireEvent", { eventType: ["downloadsuccess", "downloadapplied"],