Merge m-c to b2g-inbound. a=merge CLOSED TREE.

This commit is contained in:
Ryan VanderMeulen 2014-07-28 15:04:33 -04:00
Родитель 5183c902b6 dff012e107
Коммит 5c20e8c063
8 изменённых файлов: 338 добавлений и 617 удалений

Просмотреть файл

@ -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,324 +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 || 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);
@ -359,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!");

Просмотреть файл

@ -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 =
@ -748,6 +805,7 @@ 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,
@ -816,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;
@ -846,5 +908,7 @@ WebappsApplicationMgmt.prototype = {
classDescription: "Webapps Application Mgmt"})
}
manifestCache.init();
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsRegistry,
WebappsApplication]);

Просмотреть файл

@ -179,7 +179,6 @@ 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);
@ -276,15 +275,9 @@ 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();
@ -294,10 +287,6 @@ 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) {
@ -973,7 +962,6 @@ this.DOMApplicationRegistry = {
this._registerInterAppConnections(manifest, app);
appsToRegister.push({ manifest: manifest, app: app });
});
this._safeToClone.resolve();
this._registerActivitiesForApps(appsToRegister, aRunUpdate);
});
},
@ -1101,114 +1089,88 @@ 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 "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();
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: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);
}
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:RegisterForMessages":
this.addMessageListener(msg.messages, msg.app, mm);
break;
case "Webapps:UnregisterForMessages":
this.removeMessageListener(msg, mm);
break;
default:
processedImmediately = false;
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;
}
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) {
@ -1283,38 +1245,6 @@ 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,
@ -1400,7 +1330,7 @@ this.DOMApplicationRegistry = {
downloading: false
},
error: error,
id: app.id
manifestURL: app.manifestURL,
})
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloaderror",
@ -1431,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",
@ -1479,7 +1409,7 @@ this.DOMApplicationRegistry = {
this.broadcastMessage("Webapps:UpdateState", {
app: app,
manifest: jsonManifest,
id: app.id
manifestURL: aManifestURL
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadsuccess",
@ -1533,7 +1463,7 @@ this.DOMApplicationRegistry = {
this.broadcastMessage("Webapps:UpdateState", {
app: app,
id: app.id
manifestURL: aManifestURL
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadsuccess",
@ -1635,7 +1565,7 @@ this.DOMApplicationRegistry = {
this.broadcastMessage("Webapps:UpdateState", {
app: app,
manifest: newManifest,
id: app.id
manifestURL: app.manifestURL
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadapplied",
@ -1674,7 +1604,7 @@ this.DOMApplicationRegistry = {
installState: aApp.installState,
progress: 0
},
id: aApp.id
manifestURL: aApp.manifestURL
});
let cacheUpdate = updateSvc.scheduleAppUpdate(
appcacheURI, docURI, aApp.localId, false, aProfileDir);
@ -1724,7 +1654,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);
}
@ -1754,7 +1683,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;
}
@ -1771,7 +1701,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;
}
@ -1779,7 +1710,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;
}
@ -1795,7 +1727,7 @@ this.DOMApplicationRegistry = {
this._saveApps().then(() => {
this.broadcastMessage("Webapps:UpdateState", {
app: app,
id: app.id
manifestURL: app.manifestURL
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadavailable",
@ -1804,7 +1736,8 @@ this.DOMApplicationRegistry = {
});
});
} else {
sendError("NOT_UPDATABLE");
aData.error = "NOT_UPDATABLE";
aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
}
}
};
@ -1864,7 +1797,7 @@ this.DOMApplicationRegistry = {
: "downloadapplied";
aMm.sendAsyncMessage("Webapps:UpdateState", {
app: app,
id: app.id
manifestURL: app.manifestURL
});
aMm.sendAsyncMessage("Webapps:FireEvent", {
eventType: eventType,
@ -1891,7 +1824,7 @@ this.DOMApplicationRegistry = {
: "downloadapplied";
aMm.sendAsyncMessage("Webapps:UpdateState", {
app: app,
id: app.id
manifestURL: app.manifestURL
});
aMm.sendAsyncMessage("Webapps:FireEvent", {
eventType: eventType,
@ -2000,7 +1933,7 @@ this.DOMApplicationRegistry = {
this.broadcastMessage("Webapps:UpdateState", {
app: aApp,
id: aApp.id
manifestURL: aApp.manifestURL
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadavailable",
@ -2066,7 +1999,7 @@ this.DOMApplicationRegistry = {
this.broadcastMessage("Webapps:UpdateState", {
app: aApp,
manifest: aApp.manifest,
id: aApp.id
manifestURL: aApp.manifestURL
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadapplied",
@ -2100,7 +2033,7 @@ this.DOMApplicationRegistry = {
this.broadcastMessage("Webapps:UpdateState", {
app: aApp,
manifest: aApp.manifest,
id: aApp.id
manifestURL: aApp.manifestURL
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: eventType,
@ -2531,8 +2464,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 });
});
}),
@ -2632,8 +2564,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 });
if (!aData.isPackage) {
@ -2716,8 +2646,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 }));
@ -2877,7 +2806,7 @@ this.DOMApplicationRegistry = {
// Clear any previous download errors.
error: null,
app: aOldApp,
id: aId
manifestURL: aNewApp.manifestURL
});
let zipFile = yield this._getPackage(requestChannel, aId, aOldApp, aNewApp);
@ -2892,7 +2821,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(aOldApp);
this._sendAppliedEvent(aNewApp, aOldApp, aId);
throw new Error("PACKAGE_UNCHANGED");
}
@ -3028,7 +2957,7 @@ this.DOMApplicationRegistry = {
app: {
progress: aProgress
},
id: aNewApp.id
manifestURL: aNewApp.manifestURL
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "progress",
@ -3145,24 +3074,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) {
@ -3173,15 +3105,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);
}
@ -3500,10 +3432,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 });
}
}
},
@ -3606,7 +3537,7 @@ this.DOMApplicationRegistry = {
this.broadcastMessage("Webapps:UpdateState", {
app: aOldApp,
error: aError,
id: aNewApp.id
manifestURL: aNewApp.manifestURL
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloaderror",
@ -3786,13 +3717,9 @@ this.DOMApplicationRegistry = {
},
doGetAll: function(aData, aMm) {
// 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);
});
this.getAll(function (apps) {
aData.apps = apps;
aMm.sendAsyncMessage("Webapps:GetAll:Return:OK", aData);
});
},
@ -4160,7 +4087,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",
@ -4192,7 +4119,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"],
@ -4215,7 +4142,7 @@ AppcacheObserver.prototype = {
DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
app: app,
error: aError,
id: app.id
manifestURL: app.manifestURL
});
DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", {
eventType: "downloaderror",

Просмотреть файл

@ -102,7 +102,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 = {

Просмотреть файл

@ -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,

Просмотреть файл

@ -243,4 +243,4 @@ addLoadEvent(go);
</script>
</pre>
</body>
</html>
</html>

Просмотреть файл

@ -186,6 +186,7 @@ disabled = bug 407107
[test_bug764125.html]
[test_bug856472.html]
[test_bug866575.html]
skip-if = (toolkit == 'gonk' && debug) #bug 1045153
[test_bug902651.html]
[test_canvas.html]
skip-if = (toolkit == 'gonk' && debug) #debug-only crash; bug 933541

Просмотреть файл

@ -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"],