diff --git a/b2g/chrome/content/settings.js b/b2g/chrome/content/settings.js
index 98847326d3ef..d0a17ebad37a 100644
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -671,6 +671,44 @@ SettingsListener.observe("accessibility.screenreader", false, function(value) {
});
})();
+// =================== Low-precision buffer ======================
+(function setupLowPrecisionSettings() {
+ // The gaia setting layers.low-precision maps to two gecko prefs
+ SettingsListener.observe('layers.low-precision', null, function(value) {
+ if (value !== null) {
+ // Update gecko from the new Gaia setting
+ Services.prefs.setBoolPref('layers.low-precision-buffer', value);
+ Services.prefs.setBoolPref('layers.progressive-paint', value);
+ } else {
+ // Update gaia setting from gecko value
+ try {
+ let prefValue = Services.prefs.getBoolPref('layers.low-precision-buffer');
+ let setting = { 'layers.low-precision': prefValue };
+ window.navigator.mozSettings.createLock().set(setting);
+ } catch (e) {
+ console.log('Unable to read pref layers.low-precision-buffer: ' + e);
+ }
+ }
+ });
+
+ // The gaia setting layers.low-opacity maps to a string gecko pref (0.5/1.0)
+ SettingsListener.observe('layers.low-opacity', null, function(value) {
+ if (value !== null) {
+ // Update gecko from the new Gaia setting
+ Services.prefs.setCharPref('layers.low-precision-opacity', value ? '0.5' : '1.0');
+ } else {
+ // Update gaia setting from gecko value
+ try {
+ let prefValue = Services.prefs.getCharPref('layers.low-precision-opacity');
+ let setting = { 'layers.low-opacity': (prefValue == '0.5') };
+ window.navigator.mozSettings.createLock().set(setting);
+ } catch (e) {
+ console.log('Unable to read pref layers.low-precision-opacity: ' + e);
+ }
+ }
+ });
+})();
+
// =================== Various simple mapping ======================
let settingsToObserve = {
'app.update.channel': {
diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml
index ce91dfef4d96..9b36032b8935 100644
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -19,8 +19,8 @@
-
-
+
+
diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml
index 7a4a1c2af74e..ca93d0cddc8a 100644
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -17,8 +17,8 @@
-
-
+
+
diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml
index 8bcacc4de2fd..07d59541378c 100644
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -15,9 +15,9 @@
-
+
-
+
diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml
index ce91dfef4d96..9b36032b8935 100644
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -19,8 +19,8 @@
-
-
+
+
diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml
index deb1fd98067d..750025d1f064 100644
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -17,8 +17,8 @@
-
-
+
+
diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json
index c06a6e0e68fd..6ccc08273288 100644
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
- "revision": "d7d92199ea7e8f850ca0c6f0514bf596f178a16f",
+ "revision": "4024e28dbc44b11d3297378484c2474dcee425fa",
"repo_path": "/integration/gaia-central"
}
diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml
index 417594453899..d36dd8c7adae 100644
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -17,8 +17,8 @@
-
-
+
+
diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml
index 57d096e1a735..21f84dc9baed 100644
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -15,8 +15,8 @@
-
-
+
+
diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml
index 1225f594375d..b62ca9587d38 100644
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -17,8 +17,8 @@
-
-
+
+
diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml
index ef0ea35a5d71..7076bfd938d2 100644
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -17,8 +17,8 @@
-
-
+
+
diff --git a/dom/apps/src/AppsServiceChild.jsm b/dom/apps/src/AppsServiceChild.jsm
index c7cb9890f768..58bb4fee21ae 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 functionnalities of the parent DOM
-// Registry to content processes, to be be used from the AppsService component.
+// This module exposes a subset of the functionalities of the parent DOM
+// Registry to content processes, to be used from the AppsService component.
-this.EXPORTED_SYMBOLS = ["DOMApplicationRegistry"];
+this.EXPORTED_SYMBOLS = ["DOMApplicationRegistry", "WrappedManifestCache"];
Cu.import("resource://gre/modules/AppsUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
@@ -20,54 +20,324 @@ 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);
- ["Webapps:AddApp", "Webapps:RemoveApp"].forEach((function(aMsgName) {
+ APPS_IPC_MSG_NAMES.forEach((function(aMsgName) {
this.cpmm.addMessageListener(aMsgName, this);
}).bind(this));
- // We need to prime the cache with the list of apps.
- // XXX shoud we do this async and block callers if it's not yet there?
- this.webapps = this.cpmm.sendSyncMessage("Webapps:GetList", { })[0];
+ 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;
// 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;
- ["Webapps:AddApp", "Webapps:RemoveApp"].forEach((function(aMsgName) {
+ this.DOMApps = null;
+
+ APPS_IPC_MSG_NAMES.forEach((aMsgName) => {
this.cpmm.removeMessageListener(aMsgName, this);
- }).bind(this));
+ });
+
+ this.cpmm.sendAsyncMessage("Webapps:UnregisterForMessages",
+ APPS_IPC_MSG_NAMES)
},
receiveMessage: function receiveMessage(aMessage) {
debug("Received " + aMessage.name + " message.");
- let msg = aMessage.json;
+ let msg = aMessage.data;
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 || domApp._window === null) {
+ apps.splice(i, 1);
+ 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++) {
+ let app = apps[i].get();
+ if (!app || app._window === null) {
+ apps.splice(i, 1);
+ }
+ }
+
+ 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);
@@ -89,7 +359,7 @@ this.DOMApplicationRegistry = {
},
getAppByLocalId: function getAppByLocalId(aLocalId) {
- debug("getAppByLocalId " + aLocalId);
+ debug("getAppByLocalId " + aLocalId + " - ready: " + this.ready);
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 47a3156159f0..ed2c3226ebac 100644
--- a/dom/apps/src/Webapps.js
+++ b/dom/apps/src/Webapps.js
@@ -12,6 +12,7 @@ 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",
@@ -278,50 +279,9 @@ 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;
}
@@ -334,27 +294,12 @@ 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;
@@ -362,40 +307,83 @@ WebappsApplication.prototype = {
this._ondownloadavailable = null;
this._ondownloadapplied = null;
- this._downloadError = null;
+ this.initDOMRequestHelper(aWindow);
+ },
- 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 _appStatus() {
+ return this._proxy.appStatus;
+ },
- cpmm.sendAsyncMessage("Webapps:RegisterForMessages", {
- messages: ["Webapps:FireEvent",
- "Webapps:UpdateState"],
- app: {
- id: this.id,
- manifestURL: this.manifestURL,
- installState: this.installState,
- downloading: this.downloading
- }
- });
+ 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;
},
get manifest() {
- return manifestCache.get(this.manifestURL,
- this._manifest,
- this._window,
- this.innerWindowID);
+ return WrappedManifestCache.get(this.manifestURL,
+ this._proxy.manifest,
+ this._window,
+ this.innerWindowID);
},
get updateManifest() {
- return this.updateManifest =
- this._updateManifest ? Cu.cloneInto(this._updateManifest, this._window)
- : null;
+ return this._proxy.updateManifest ?
+ Cu.cloneInto(this._proxy.updateManifest, this._window) : null;
},
set onprogress(aCallback) {
@@ -440,10 +428,10 @@ WebappsApplication.prototype = {
get downloadError() {
// Only return DOMError when we have an error.
- if (!this._downloadError) {
+ if (!this._proxy.downloadError) {
return null;
}
- return new this._window.DOMError(this._downloadError);
+ return new this._window.DOMError(this._proxy.downloadError);
},
download: function() {
@@ -485,12 +473,11 @@ 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");
}
@@ -498,28 +485,33 @@ 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));
},
@@ -568,12 +560,7 @@ WebappsApplication.prototype = {
uninit: function() {
this._onprogress = null;
- cpmm.sendAsyncMessage("Webapps:UnregisterForMessages", [
- "Webapps:FireEvent",
- "Webapps:UpdateState"
- ]);
-
- manifestCache.evict(this.manifestURL, this.innerWindowID);
+ WrappedManifestCache.evict(this.manifestURL, this.innerWindowID);
},
_fireEvent: function(aName) {
@@ -590,22 +577,16 @@ WebappsApplication.prototype = {
}
},
- _updateState: function(aMsg) {
- if (aMsg.app) {
- for (let prop in aMsg.app) {
- this[prop] = aMsg.app[prop];
- }
+ _fireRequestResult: function(aMessage, aIsError) {
+ let req;
+ let msg = aMessage.data;
+ req = this.takeRequest(msg.requestID);
+ if (!req) {
+ return;
}
- // 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);
- }
+ aIsError ? Services.DOMRequest.fireError(req, msg.error)
+ : Services.DOMRequest.fireSuccess(req, msg.result);
},
receiveMessage: function(aMessage) {
@@ -619,10 +600,7 @@ WebappsApplication.prototype = {
req = this.takeRequest(msg.requestID);
}
- // ondownload* callbacks should be triggered on all app instances
- if ((msg.oid != this._id || !req) &&
- aMessage.name !== "Webapps:FireEvent" &&
- aMessage.name !== "Webapps:UpdateState") {
+ if (msg.oid !== this._id || !req) {
return;
}
@@ -637,51 +615,13 @@ 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);
@@ -690,9 +630,12 @@ 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 =
@@ -805,7 +748,6 @@ WebappsApplicationMgmt.prototype = {
},
uninstall: function(aApp) {
- dump("-- webapps.js uninstall " + aApp.manifestURL + "\n");
let request = this.createRequest();
cpmm.sendAsyncMessage("Webapps:Uninstall", { origin: aApp.origin,
manifestURL: aApp.manifestURL,
@@ -874,12 +816,8 @@ 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, detail) });
+ { application : createApplicationObject(this._window, msg) });
this._onuninstall.handleEvent(event);
}
break;
@@ -908,7 +846,5 @@ 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 fe57310d272a..2fba41293edd 100755
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -179,6 +179,7 @@ this.DOMApplicationRegistry = {
}).bind(this));
cpmm.addMessageListener("Activities:Register:OK", this);
+ cpmm.addMessageListener("Activities:Register:KO", this);
Services.obs.addObserver(this, "xpcom-shutdown", false);
Services.obs.addObserver(this, "memory-pressure", false);
@@ -275,9 +276,15 @@ this.DOMApplicationRegistry = {
return this._registryStarted.promise;
},
+ // The registry will be safe to clone when this promise is resolved.
+ _safeToClone: Promise.defer(),
+
// Notify we are done with registering apps and save a copy of the registry.
_registryReady: Promise.defer(),
notifyAppsRegistryReady: function notifyAppsRegistryReady() {
+ // Usually this promise will be resolved earlier, but just in case,
+ // resolve it here also.
+ this._safeToClone.resolve();
this._registryReady.resolve();
Services.obs.notifyObservers(this, "webapps-registry-ready", null);
this._saveApps();
@@ -287,6 +294,10 @@ this.DOMApplicationRegistry = {
return this._registryReady.promise;
},
+ get safeToClone() {
+ return this._safeToClone.promise;
+ },
+
// Ensure that the .to property in redirects is a relative URL.
sanitizeRedirects: function sanitizeRedirects(aSource) {
if (!aSource) {
@@ -962,6 +973,7 @@ this.DOMApplicationRegistry = {
this._registerInterAppConnections(manifest, app);
appsToRegister.push({ manifest: manifest, app: app });
});
+ this._safeToClone.resolve();
this._registerActivitiesForApps(appsToRegister, aRunUpdate);
});
},
@@ -1089,88 +1101,114 @@ this.DOMApplicationRegistry = {
let mm = aMessage.target;
msg.mm = mm;
+ let processedImmediately = true;
+
+ // There are two kind of messages: the messages that only make sense once the
+ // registry is ready, and those that can (or have to) be treated as soon as
+ // they're received.
switch (aMessage.name) {
- case "Webapps:Install": {
-#ifdef MOZ_WIDGET_ANDROID
- Services.obs.notifyObservers(mm, "webapps-runtime-install", JSON.stringify(msg));
-#else
- this.doInstall(msg, mm);
-#endif
+ case "Activities:Register:KO":
+ dump("Activities didn't register correctly!");
+ case "Activities:Register:OK":
+ // Activities:Register:OK is special because it's one way the registryReady
+ // promise can be resolved.
+ // XXX: What to do when the activities registration failed? At this point
+ // just act as if nothing happened.
+ this.notifyAppsRegistryReady();
break;
- }
- case "Webapps:GetSelf":
- this.getSelf(msg, mm);
+ case "Webapps:GetList":
+ // GetList is special because it's synchronous. So far so well, it's the
+ // only synchronous message, if we get more at some point they should get
+ // this treatment also.
+ return this.doGetList();
+ case "child-process-shutdown":
+ this.removeMessageListener(["Webapps:Internal:AllMessages"], mm);
break;
- case "Webapps:Uninstall":
-#ifdef MOZ_WIDGET_ANDROID
- Services.obs.notifyObservers(mm, "webapps-runtime-uninstall", JSON.stringify(msg));
-#else
- this.doUninstall(msg, mm);
-#endif
- break;
- case "Webapps:Launch":
- this.doLaunch(msg, mm);
- break;
- case "Webapps:CheckInstalled":
- this.checkInstalled(msg, mm);
- break;
- case "Webapps:GetInstalled":
- this.getInstalled(msg, mm);
- break;
- case "Webapps:GetNotInstalled":
- this.getNotInstalled(msg, mm);
- break;
- case "Webapps:GetAll":
- this.doGetAll(msg, mm);
- break;
- case "Webapps:InstallPackage": {
-#ifdef MOZ_WIDGET_ANDROID
- Services.obs.notifyObservers(mm, "webapps-runtime-install-package", JSON.stringify(msg));
-#else
- this.doInstallPackage(msg, mm);
-#endif
- break;
- }
case "Webapps:RegisterForMessages":
this.addMessageListener(msg.messages, msg.app, mm);
break;
case "Webapps:UnregisterForMessages":
this.removeMessageListener(msg, mm);
break;
- case "child-process-shutdown":
- this.removeMessageListener(["Webapps:Internal:AllMessages"], mm);
- break;
- case "Webapps:GetList":
- this.addMessageListener(["Webapps:AddApp", "Webapps:RemoveApp"], null, mm);
- return this.webapps;
- case "Webapps:Download":
- this.startDownload(msg.manifestURL);
- break;
- case "Webapps:CancelDownload":
- this.cancelDownload(msg.manifestURL);
- break;
- case "Webapps:CheckForUpdate":
- this.checkForUpdate(msg, mm);
- break;
- case "Webapps:ApplyDownload":
- this.applyDownload(msg.manifestURL);
- break;
- case "Activities:Register:OK":
- this.notifyAppsRegistryReady();
- break;
- case "Webapps:Install:Return:Ack":
- this.onInstallSuccessAck(msg.manifestURL);
- break;
- case "Webapps:AddReceipt":
- this.addReceipt(msg, mm);
- break;
- case "Webapps:RemoveReceipt":
- this.removeReceipt(msg, mm);
- break;
- case "Webapps:ReplaceReceipt":
- this.replaceReceipt(msg, mm);
- break;
+ default:
+ processedImmediately = false;
}
+
+ if (processedImmediately) {
+ return;
+ }
+
+ // For all the rest (asynchronous), we wait till the registry is ready
+ // before processing the message.
+ this.registryReady.then( () => {
+ switch (aMessage.name) {
+ case "Webapps:Install": {
+#ifdef MOZ_WIDGET_ANDROID
+ Services.obs.notifyObservers(mm, "webapps-runtime-install", JSON.stringify(msg));
+#else
+ this.doInstall(msg, mm);
+#endif
+ break;
+ }
+ case "Webapps:GetSelf":
+ this.getSelf(msg, mm);
+ break;
+ case "Webapps:Uninstall":
+#ifdef MOZ_WIDGET_ANDROID
+ Services.obs.notifyObservers(mm, "webapps-runtime-uninstall", JSON.stringify(msg));
+#else
+ this.doUninstall(msg, mm);
+#endif
+ break;
+ case "Webapps:Launch":
+ this.doLaunch(msg, mm);
+ break;
+ case "Webapps:CheckInstalled":
+ this.checkInstalled(msg, mm);
+ break;
+ case "Webapps:GetInstalled":
+ this.getInstalled(msg, mm);
+ break;
+ case "Webapps:GetNotInstalled":
+ this.getNotInstalled(msg, mm);
+ break;
+ case "Webapps:GetAll":
+ this.doGetAll(msg, mm);
+ break;
+ case "Webapps:InstallPackage": {
+#ifdef MOZ_WIDGET_ANDROID
+ Services.obs.notifyObservers(mm, "webapps-runtime-install-package", JSON.stringify(msg));
+#else
+ this.doInstallPackage(msg, mm);
+#endif
+ break;
+ }
+ case "Webapps:Download":
+ this.startDownload(msg.manifestURL);
+ break;
+ case "Webapps:CancelDownload":
+ this.cancelDownload(msg.manifestURL);
+ break;
+ case "Webapps:CheckForUpdate":
+ this.checkForUpdate(msg, mm);
+ break;
+ case "Webapps:ApplyDownload":
+ this.applyDownload(msg.manifestURL);
+ break;
+ case "Webapps:Install:Return:Ack":
+ this.onInstallSuccessAck(msg.manifestURL);
+ break;
+ case "Webapps:AddReceipt":
+ this.addReceipt(msg, mm);
+ break;
+ case "Webapps:RemoveReceipt":
+ this.removeReceipt(msg, mm);
+ break;
+ case "Webapps:ReplaceReceipt":
+ this.replaceReceipt(msg, mm);
+ break;
+ }
+ });
},
getAppInfo: function getAppInfo(aAppId) {
@@ -1245,6 +1283,38 @@ this.DOMApplicationRegistry = {
return deferred.promise;
},
+ /**
+ * Returns the full list of apps and manifests.
+ */
+ doGetList: function() {
+ let tmp = [];
+
+ let res = {};
+ let done = false;
+
+ // We allow cloning the registry when the local processing has been done.
+ this.safeToClone.then( () => {
+ for (let id in this.webapps) {
+ tmp.push({ id: id });
+ }
+ this._readManifests(tmp).then(
+ function(manifests) {
+ manifests.forEach((item) => {
+ res[item.id] = item.manifest;
+ });
+ done = true;
+ }
+ );
+ });
+
+ let thread = Services.tm.currentThread;
+ while (!done) {
+ thread.processNextEvent(/* mayWait */ true);
+ }
+ return { webapps: this.webapps, manifests: res };
+ },
+
+
doLaunch: function (aData, aMm) {
this.launch(
aData.manifestURL,
@@ -1330,7 +1400,7 @@ this.DOMApplicationRegistry = {
downloading: false
},
error: error,
- manifestURL: app.manifestURL,
+ id: app.id
})
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloaderror",
@@ -1361,7 +1431,7 @@ this.DOMApplicationRegistry = {
if (!app.downloadAvailable) {
this.broadcastMessage("Webapps:UpdateState", {
error: "NO_DOWNLOAD_AVAILABLE",
- manifestURL: app.manifestURL
+ id: app.id
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloaderror",
@@ -1409,7 +1479,7 @@ this.DOMApplicationRegistry = {
this.broadcastMessage("Webapps:UpdateState", {
app: app,
manifest: jsonManifest,
- manifestURL: aManifestURL
+ id: app.id
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadsuccess",
@@ -1463,7 +1533,7 @@ this.DOMApplicationRegistry = {
this.broadcastMessage("Webapps:UpdateState", {
app: app,
- manifestURL: aManifestURL
+ id: app.id
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadsuccess",
@@ -1565,7 +1635,7 @@ this.DOMApplicationRegistry = {
this.broadcastMessage("Webapps:UpdateState", {
app: app,
manifest: newManifest,
- manifestURL: app.manifestURL
+ id: app.id
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadapplied",
@@ -1604,7 +1674,7 @@ this.DOMApplicationRegistry = {
installState: aApp.installState,
progress: 0
},
- manifestURL: aApp.manifestURL
+ id: aApp.id
});
let cacheUpdate = updateSvc.scheduleAppUpdate(
appcacheURI, docURI, aApp.localId, false, aProfileDir);
@@ -1654,6 +1724,7 @@ this.DOMApplicationRegistry = {
debug("checkForUpdate for " + aData.manifestURL);
function sendError(aError) {
+ debug("checkForUpdate error " + aError);
aData.error = aError;
aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
}
@@ -1683,8 +1754,7 @@ this.DOMApplicationRegistry = {
// then we can't have an update.
if (app.origin.startsWith("app://") &&
app.manifestURL.startsWith("app://")) {
- aData.error = "NOT_UPDATABLE";
- aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
+ sendError("NOT_UPDATABLE");
return;
}
@@ -1701,8 +1771,7 @@ this.DOMApplicationRegistry = {
if (onlyCheckAppCache) {
// Bail out for packaged apps.
if (app.origin.startsWith("app://")) {
- aData.error = "NOT_UPDATABLE";
- aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
+ sendError("NOT_UPDATABLE");
return;
}
@@ -1710,8 +1779,7 @@ this.DOMApplicationRegistry = {
this._readManifests([{ id: id }]).then((aResult) => {
let manifest = aResult[0].manifest;
if (!manifest.appcache_path) {
- aData.error = "NOT_UPDATABLE";
- aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
+ sendError("NOT_UPDATABLE");
return;
}
@@ -1727,7 +1795,7 @@ this.DOMApplicationRegistry = {
this._saveApps().then(() => {
this.broadcastMessage("Webapps:UpdateState", {
app: app,
- manifestURL: app.manifestURL
+ id: app.id
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadavailable",
@@ -1736,8 +1804,7 @@ this.DOMApplicationRegistry = {
});
});
} else {
- aData.error = "NOT_UPDATABLE";
- aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
+ sendError("NOT_UPDATABLE");
}
}
};
@@ -1797,7 +1864,7 @@ this.DOMApplicationRegistry = {
: "downloadapplied";
aMm.sendAsyncMessage("Webapps:UpdateState", {
app: app,
- manifestURL: app.manifestURL
+ id: app.id
});
aMm.sendAsyncMessage("Webapps:FireEvent", {
eventType: eventType,
@@ -1824,7 +1891,7 @@ this.DOMApplicationRegistry = {
: "downloadapplied";
aMm.sendAsyncMessage("Webapps:UpdateState", {
app: app,
- manifestURL: app.manifestURL
+ id: app.id
});
aMm.sendAsyncMessage("Webapps:FireEvent", {
eventType: eventType,
@@ -1933,7 +2000,7 @@ this.DOMApplicationRegistry = {
this.broadcastMessage("Webapps:UpdateState", {
app: aApp,
- manifestURL: aApp.manifestURL
+ id: aApp.id
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadavailable",
@@ -1999,7 +2066,7 @@ this.DOMApplicationRegistry = {
this.broadcastMessage("Webapps:UpdateState", {
app: aApp,
manifest: aApp.manifest,
- manifestURL: aApp.manifestURL
+ id: aApp.id
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadapplied",
@@ -2033,7 +2100,7 @@ this.DOMApplicationRegistry = {
this.broadcastMessage("Webapps:UpdateState", {
app: aApp,
manifest: aApp.manifest,
- manifestURL: aApp.manifestURL
+ id: aApp.id
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: eventType,
@@ -2464,7 +2531,8 @@ this.DOMApplicationRegistry = {
}
this._saveApps().then(() => {
- this.broadcastMessage("Webapps:AddApp", { id: app.id, app: app });
+ this.broadcastMessage("Webapps:AddApp",
+ { id: app.id, app: app, manifest: aManifest });
});
}),
@@ -2564,6 +2632,8 @@ 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 });
if (!aData.isPackage) {
@@ -2646,7 +2716,8 @@ this.DOMApplicationRegistry = {
delete this._manifestCache[aId];
}
- this.broadcastMessage("Webapps:AddApp", { id: aId, app: aNewApp });
+ this.broadcastMessage("Webapps:AddApp",
+ { id: aId, app: aNewApp, manifest: aManifest });
Services.obs.notifyObservers(null, "webapps-installed",
JSON.stringify({ manifestURL: aNewApp.manifestURL }));
@@ -2806,7 +2877,7 @@ this.DOMApplicationRegistry = {
// Clear any previous download errors.
error: null,
app: aOldApp,
- manifestURL: aNewApp.manifestURL
+ id: aId
});
let zipFile = yield this._getPackage(requestChannel, aId, aOldApp, aNewApp);
@@ -2821,7 +2892,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(aNewApp, aOldApp, aId);
+ this._sendAppliedEvent(aOldApp);
throw new Error("PACKAGE_UNCHANGED");
}
@@ -2957,7 +3028,7 @@ this.DOMApplicationRegistry = {
app: {
progress: aProgress
},
- manifestURL: aNewApp.manifestURL
+ id: aNewApp.id
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "progress",
@@ -3074,27 +3145,24 @@ 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 aNewApp {Object} the new app data
- * @param aOldApp {Object} the currently stored app data
- * @param aId {String} the unique id of the app
+ * @param aApp {Object} app data
*/
- _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) {
+ _sendAppliedEvent: function(aApp) {
+ aApp.downloading = false;
+ aApp.downloadAvailable = false;
+ aApp.downloadSize = 0;
+ aApp.installState = "installed";
+ aApp.readyToApplyDownload = false;
+ if (aApp.staged && aApp.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
- aOldApp.manifestHash = aOldApp.staged.manifestHash;
- aOldApp.etag = aOldApp.staged.etag || aOldApp.etag;
- aOldApp.staged = {};
-
- // Move the staged update manifest to a non staged one.
+ aApp.manifestHash = aApp.staged.manifestHash;
+ aApp.etag = aApp.staged.etag || aApp.etag;
+ aApp.staged = {};
+ // Move the staged update manifest to a non staged one.
try {
- let staged = this._getAppDir(aId);
+ let staged = this._getAppDir(aApp.id);
staged.append("staged-update.webapp");
staged.moveTo(staged.parent, "update.webapp");
} catch (ex) {
@@ -3105,15 +3173,15 @@ this.DOMApplicationRegistry = {
// Save the updated registry, and cleanup the tmp directory.
this._saveApps().then(() => {
this.broadcastMessage("Webapps:UpdateState", {
- app: aOldApp,
- manifestURL: aNewApp.manifestURL
+ app: aApp,
+ id: aApp.id
});
this.broadcastMessage("Webapps:FireEvent", {
- manifestURL: aNewApp.manifestURL,
+ manifestURL: aApp.manifestURL,
eventType: ["downloadsuccess", "downloadapplied"]
});
});
- let file = FileUtils.getFile("TmpD", ["webapps", aId], false);
+ let file = FileUtils.getFile("TmpD", ["webapps", aApp.id], false);
if (file && file.exists()) {
file.remove(true);
}
@@ -3432,9 +3500,10 @@ this.DOMApplicationRegistry = {
dir.moveTo(parent, newId);
});
// Signals that we need to swap the old id with the new app.
- this.broadcastMessage("Webapps:RemoveApp", { id: oldId });
- this.broadcastMessage("Webapps:AddApp", { id: newId,
- app: aOldApp });
+ this.broadcastMessage("Webapps:UpdateApp", { oldId: oldId,
+ newId: newId,
+ app: aOldApp });
+
}
}
},
@@ -3537,7 +3606,7 @@ this.DOMApplicationRegistry = {
this.broadcastMessage("Webapps:UpdateState", {
app: aOldApp,
error: aError,
- manifestURL: aNewApp.manifestURL
+ id: aNewApp.id
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloaderror",
@@ -3717,9 +3786,13 @@ this.DOMApplicationRegistry = {
},
doGetAll: function(aData, aMm) {
- this.getAll(function (apps) {
- aData.apps = apps;
- aMm.sendAsyncMessage("Webapps:GetAll:Return:OK", aData);
+ // We can't do this until the registry is ready.
+ debug("doGetAll");
+ this.registryReady.then(() => {
+ this.getAll(function (apps) {
+ aData.apps = apps;
+ aMm.sendAsyncMessage("Webapps:GetAll:Return:OK", aData);
+ });
});
},
@@ -4087,7 +4160,7 @@ AppcacheObserver.prototype = {
let app = this.app;
DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
app: app,
- manifestURL: app.manifestURL
+ id: app.id
});
DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", {
eventType: "progress",
@@ -4119,7 +4192,7 @@ AppcacheObserver.prototype = {
app.downloadAvailable = false;
DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
app: app,
- manifestURL: app.manifestURL
+ id: app.id
});
DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", {
eventType: ["downloadsuccess", "downloadapplied"],
@@ -4142,7 +4215,7 @@ AppcacheObserver.prototype = {
DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
app: app,
error: aError,
- manifestURL: app.manifestURL
+ id: app.id
});
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 abcdeacf6008..449aa513d78c 100644
--- a/dom/apps/tests/test_packaged_app_common.js
+++ b/dom/apps/tests/test_packaged_app_common.js
@@ -102,6 +102,7 @@ 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 1278e767ca48..3b1fc9957750 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", 2, false, true,
+ "MANIFEST_MISMATCH", 1, 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 0907e8d964c4..7ebe84ccfae0 100644
--- a/dom/apps/tests/test_receipt_operations.html
+++ b/dom/apps/tests/test_receipt_operations.html
@@ -243,4 +243,4 @@ addLoadEvent(go);