From d19bb08b57b67e461a32b01988a4fbbb859c5a5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabrice=20Desr=C3=A9?= Date: Fri, 20 Jul 2012 17:41:30 +0200 Subject: [PATCH] Bug 715814 - Implement Web Activities: DOM Part [r=mounir] --- b2g/installer/package-manifest.in | 5 +- dom/activities/interfaces/Makefile.in | 1 + .../interfaces/nsIActivityUIGlue.idl | 25 ++ dom/activities/src/Activities.manifest | 9 + dom/activities/src/ActivitiesService.jsm | 292 ++++++++++++++++++ dom/activities/src/ActivityOptions.js | 56 ++++ dom/activities/src/ActivityRequestHandler.js | 76 +++++ dom/activities/src/ActivityWrapper.js | 45 +++ dom/activities/src/Makefile.in | 7 + dom/apps/src/Webapps.jsm | 96 ++++-- dom/messages/SystemMessageManager.js | 26 +- .../interfaces/nsISystemMessagesInternal.idl | 10 + 12 files changed, 625 insertions(+), 23 deletions(-) create mode 100644 dom/activities/interfaces/nsIActivityUIGlue.idl create mode 100644 dom/activities/src/ActivitiesService.jsm create mode 100644 dom/activities/src/ActivityOptions.js create mode 100644 dom/activities/src/ActivityRequestHandler.js create mode 100644 dom/activities/src/ActivityWrapper.js diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in index fff19f083e3d..47b70c233c07 100644 --- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -477,8 +477,11 @@ @BINPATH@/components/SystemMessageManager.js @BINPATH@/components/SystemMessageManager.manifest -@BINPATH@/components/ActivityProxy.js @BINPATH@/components/Activities.manifest +@BINPATH@/components/ActivityOptions.js +@BINPATH@/components/ActivityProxy.js +@BINPATH@/components/ActivityRequestHandler.js +@BINPATH@/components/ActivityWrapper.js @BINPATH@/components/AppProtocolHandler.js @BINPATH@/components/AppProtocolHandler.manifest diff --git a/dom/activities/interfaces/Makefile.in b/dom/activities/interfaces/Makefile.in index e3093a68997f..f39d57d42488 100644 --- a/dom/activities/interfaces/Makefile.in +++ b/dom/activities/interfaces/Makefile.in @@ -17,6 +17,7 @@ XPIDLSRCS = nsIDOMActivity.idl \ nsIDOMActivityRequestHandler.idl \ nsIDOMNavigatorActivities.idl \ nsIActivityProxy.idl \ + nsIActivityUIGlue.idl \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/dom/activities/interfaces/nsIActivityUIGlue.idl b/dom/activities/interfaces/nsIActivityUIGlue.idl new file mode 100644 index 000000000000..3bc15e45bb0c --- /dev/null +++ b/dom/activities/interfaces/nsIActivityUIGlue.idl @@ -0,0 +1,25 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +[scriptable, function, uuid(7a16feb4-5a78-4589-9174-b728f26942e2)] +interface nsIActivityUIGlueCallback : nsISupports +{ + void handleEvent(in long choice); +}; + +/** + * To be implemented by @mozilla.org/dom/activities/ui-glue;1 + */ +[scriptable, uuid(8624ad73-937a-400f-9d93-39ab5449b867)] +interface nsIActivityUIGlue : nsISupports +{ + /** + * @param name The name of the activity to handle (eg. "share", "pick"). + * @param activities A json blob which is an array of { "title":"...", "icon":"..." }. + * @param onresult The callback to send the index of the choosen activity. Send -1 if no choice is made. + */ + void chooseActivity(in DOMString title, in jsval activities, in nsIActivityUIGlueCallback onresult); +}; diff --git a/dom/activities/src/Activities.manifest b/dom/activities/src/Activities.manifest index b1ac720e839f..67a17ae1e463 100644 --- a/dom/activities/src/Activities.manifest +++ b/dom/activities/src/Activities.manifest @@ -1,2 +1,11 @@ component {ba9bd5cb-76a0-4ecf-a7b3-d2f7c43c5949} ActivityProxy.js contract @mozilla.org/dom/activities/proxy;1 {ba9bd5cb-76a0-4ecf-a7b3-d2f7c43c5949} + +component {5430d6f9-32d6-4924-ba39-6b6d1b093cd6} ActivityWrapper.js +contract @mozilla.org/dom/system-messages/wrapper/activity;1 {5430d6f9-32d6-4924-ba39-6b6d1b093cd6} + +component {9326952a-dbe3-4d81-a51f-d9c160d96d6b} ActivityRequestHandler.js +contract @mozilla.org/dom/activities/request-handler;1 {9326952a-dbe3-4d81-a51f-d9c160d96d6b} + +component {ee983dbb-d5ea-4c5b-be98-10a13cac9f9d} ActivityOptions.js +contract @mozilla.org/dom/activities/options;1 {ee983dbb-d5ea-4c5b-be98-10a13cac9f9d} diff --git a/dom/activities/src/ActivitiesService.jsm b/dom/activities/src/ActivitiesService.jsm new file mode 100644 index 000000000000..4f00f98dba47 --- /dev/null +++ b/dom/activities/src/ActivitiesService.jsm @@ -0,0 +1,292 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict" + +const Cu = Components.utils; +const Cc = Components.classes; +const Ci = Components.interfaces; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/IndexedDBHelper.jsm"); + +XPCOMUtils.defineLazyGetter(this, "ppmm", function() { + return Cc["@mozilla.org/parentprocessmessagemanager;1"] + .getService(Ci.nsIFrameMessageManager); +}); + +const EXPORTED_SYMBOLS = []; + +let idbGlobal = this; + +function debug(aMsg) { + //dump("-- ActivitiesService.jsm " + Date.now() + " " + aMsg + "\n"); +} + +const DB_NAME = "activities"; +const DB_VERSION = 1; +const STORE_NAME = "activities"; + +function ActivitiesDb() { + +} + +ActivitiesDb.prototype = { + __proto__: IndexedDBHelper.prototype, + + init: function actdb_init() { + let idbManager = Cc["@mozilla.org/dom/indexeddb/manager;1"] + .getService(Ci.nsIIndexedDatabaseManager); + idbManager.initWindowless(idbGlobal); + this.initDBHelper(DB_NAME, DB_VERSION, STORE_NAME, idbGlobal); + }, + + /** + * Create the initial database schema. + * + * The schema of records stored is as follows: + * + * { + * id: String + * manifest: String + * name: String + * title: String + * icon: String + * description: jsval + * } + */ + upgradeSchema: function actdb_upgradeSchema(aTransaction, aDb, aOldVersion, aNewVersion) { + debug("Upgrade schema " + aOldVersion + " -> " + aNewVersion); + let objectStore = aDb.createObjectStore(STORE_NAME, { keyPath: "id" }); + + // indexes + objectStore.createIndex("name", "name", { unique: false }); + objectStore.createIndex("manifest", "manifest", { unique: false }); + + debug("Created object stores and indexes"); + }, + + // unique ids made of (uri, action) + createId: function actdb_createId(aObject) { + let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"] + .createInstance(Ci.nsIScriptableUnicodeConverter); + converter.charset = "UTF-8"; + + let hasher = Cc["@mozilla.org/security/hash;1"] + .createInstance(Ci.nsICryptoHash); + hasher.init(hasher.SHA1); + + // add uri and action to the hash + ["manifest", "name"].forEach(function(aProp) { + let data = converter.convertToByteArray(aObject[aProp], {}); + hasher.update(data, data.length); + }); + + return hasher.finish(true); + }, + + add: function actdb_add(aObject, aSuccess, aError) { + this.newTxn("readwrite", function (txn, store) { + let object = { + manifest: aObject.manifest, + name: aObject.name, + title: aObject.title || "", + icon: aObject.icon || "", + description: aObject.description + }; + object.id = this.createId(object); + debug("Going to add " + JSON.stringify(object)); + + store.put(object); + }.bind(this), aSuccess, aError); + }, + + // we want to remove all activities for (manifest, name) + remove: function actdb_remove(aObject) { + this.newTxn("readwrite", function (txn, store) { + let object = { + manifest: aObject.manifest, + name: aObject.name + }; + debug("Going to remove " + JSON.stringify(object)); + store.delete(this.createId(object)); + }.bind(this), function() {}, function() {}); + }, + + find: function actdb_find(aObject, aSuccess, aError, aMatch) { + debug("Looking for " + aObject.options.name); + + this.newTxn("readonly", function (txn, store) { + let index = store.index("name"); + let request = index.mozGetAll(aObject.options.name); + request.onsuccess = function findSuccess(aEvent) { + debug("Request successful. Record count: " + aEvent.target.result.length); + if (!txn.result) { + txn.result = { + name: aObject.options.name, + options: [] + }; + } + + aEvent.target.result.forEach(function(result) { + if (!aMatch(result)) + return; + + txn.result.options.push({ + manifest: result.manifest, + title: result.title, + icon: result.icon, + description: result.description + }); + }); + } + }.bind(this), aSuccess, aError); + } +} + +let Activities = { + messages: [ + // ActivityProxy.js + "Activity:Start", + + // ActivityRequestHandler.js + "Activity:PostResult", + "Activity:PostError", + + "Activities:Register", + "Activities:Unregister", + ], + + init: function activities_init() { + this.messages.forEach(function(msgName) { + ppmm.addMessageListener(msgName, this); + }, this); + + Services.obs.addObserver(this, "xpcom-shutdown", false); + + this.db = new ActivitiesDb(); + this.db.init(); + }, + + observe: function activities_observe(aSubject, aTopic, aData) { + this.messages.forEach(function(msgName) { + ppmm.removeMessageListener(msgName, this); + }, this); + ppmm = null; + + Services.obs.removeObserver(this, "xpcom-shutdown"); + }, + + /** + * Starts an activity by doing: + * - finds a list of matching activities. + * - calls the UI glue to get the user choice. + * - fire an system message of type "activity" to this app, sending the + * activity data as a payload. + */ + startActivity: function activities_startActivity(aMsg) { + debug("StartActivity: " + JSON.stringify(aMsg)); + + let successCb = function successCb(aResults) { + debug(JSON.stringify(aResults)); + + // We have no matching activity registered, let's fire an error. + if (aResults.length === 0) { + ppmm.sendAsyncMessage("Activity:FireError", { + "id": aMsg.id, + "error": "NO_PROVIDER" + }); + return; + } + + function getActivityChoice(aChoice) { + debug("Activity choice: " + aChoice); + + // The user has cancelled the choice, fire an error. + if (aChoice === -1) { + ppmm.sendAsyncMessage("Activity:FireError", { + "id": aMsg.id, + "error": "USER_ABORT" + }); + return; + } + + let sysmm = Cc["@mozilla.org/system-message-internal;1"] + .getService(Ci.nsISystemMessagesInternal); + if (!sysmm) { + // System message is not present, what should we do? + return; + } + + debug("Sending system message..."); + let result = aResults.options[aChoice]; + sysmm.sendMessage("activity", { + "id": aMsg.id, + "payload": aMsg.options + }, Services.io.newURI(result.manifest, null, null)); + + if (!result.description.returnValue) { + ppmm.sendAsyncMessage("Activity:FireSuccess", { + "id": aMsg.id, + "result": null + }); + } + }; + + let glue = Cc["@mozilla.org/dom/activities/ui-glue;1"] + .createInstance(Ci.nsIActivityUIGlue); + glue.chooseActivity(aResults.name, aResults.options, getActivityChoice); + }; + + let errorCb = function errorCb(aError) { + // Something unexpected happened. Should we send an error back? + debug("Error in startActivity: " + aError + "\n"); + }; + + let matchFunc = function matchFunc(aResult) { + // Bug 773383: arrays of strings / regexp. + for (let prop in aResult.description.filters) { + if (aMsg.options.data[prop] !== aResult.description.filters[prop]) { + return false; + } + } + return true; + }; + + this.db.find(aMsg, successCb, errorCb, matchFunc); + }, + + receiveMessage: function activities_receiveMessage(aMessage) { + let mm = aMessage.target.QueryInterface(Ci.nsIFrameMessageManager); + let msg = aMessage.json; + switch(aMessage.name) { + case "Activity:Start": + this.startActivity(msg); + break; + + case "Activity:PostResult": + ppmm.sendAsyncMessage("Activity:FireSuccess", msg); + break; + case "Activity:PostError": + ppmm.sendAsyncMessage("Activity:FireError", msg); + break; + + case "Activities:Register": + this.db.add(msg, function onSuccess(aEvent) { + mm.sendAsyncMessage("Activities:Register:OK", msg); + }, + function onError(aEvent) { + msg.error = "REGISTER_ERROR"; + mm.sendAsyncMessage("Activities:Register:KO", msg); + }); + break; + case "Activities:Unregister": + this.db.remove(msg); + break; + } + } +} + +Activities.init(); diff --git a/dom/activities/src/ActivityOptions.js b/dom/activities/src/ActivityOptions.js new file mode 100644 index 000000000000..cd5f4fdf32b3 --- /dev/null +++ b/dom/activities/src/ActivityOptions.js @@ -0,0 +1,56 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +function debug(aMsg) { + //dump("-- ActivityOptions.js " + Date.now() + " : " + aMsg + "\n"); +} + +/** + * nsIDOMMozActivityOptions implementation. + */ + +function ActivityOptions() { + debug("ActivityOptions"); + this.wrappedJSObject = this; + + // When a system message of type 'activity' is emitted, it forces the + // creation of an ActivityWrapper which in turns replace the default + // system message callback. The newly created wrapper then create a + // nsIDOMActivityRequestHandler object and fills up the properties of + // this object as well as the properties of the nsIDOMActivityOptions + // object contains by the request handler. + this._name = null; + this._data = null; +} + +ActivityOptions.prototype = { + get name() { + return this._name; + }, + + get data() { + return this._data; + }, + + classID: Components.ID("{ee983dbb-d5ea-4c5b-be98-10a13cac9f9d}"), + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozActivityOptions]), + + classInfo: XPCOMUtils.generateCI({ + classID: Components.ID("{ee983dbb-d5ea-4c5b-be98-10a13cac9f9d}"), + contractID: "@mozilla.org/dom/activities/options;1", + interfaces: [Ci.nsIDOMMozActivityOptions], + flags: Ci.nsIClassInfo.DOM_OBJECT, + classDescription: "Activity Options" + }) +} + +const NSGetFactory = XPCOMUtils.generateNSGetFactory([ActivityOptions]); diff --git a/dom/activities/src/ActivityRequestHandler.js b/dom/activities/src/ActivityRequestHandler.js new file mode 100644 index 000000000000..de6da1c3ac46 --- /dev/null +++ b/dom/activities/src/ActivityRequestHandler.js @@ -0,0 +1,76 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyGetter(this, "cpmm", function() { + return Cc["@mozilla.org/childprocessmessagemanager;1"] + .getService(Ci.nsIFrameMessageManager) + .QueryInterface(Ci.nsISyncMessageSender); +}); + +function debug(aMsg) { + //dump("-- ActivityRequestHandler.js " + Date.now() + " : " + aMsg + "\n"); +} + +/** + * nsIDOMMozActivityRequestHandler implementation. + */ + +function ActivityRequestHandler() { + debug("ActivityRequestHandler"); + this.wrappedJSObject = this; + + // When a system message of type 'activity' is emitted, it forces the + // creation of an ActivityWrapper which in turns replace the default + // system message callback. The newly created wrapper then create a + // nsIDOMActivityRequestHandler object and fills up the properties of + // this object as well as the properties of the nsIDOMActivityOptions + // object contains by the request handler. + this._id = null; + this._options = Cc["@mozilla.org/dom/activities/options;1"] + .createInstance(Ci.nsIDOMMozActivityOptions); +} + +ActivityRequestHandler.prototype = { + get source() { + return this._options; + }, + + postResult: function arh_postResult(aResult) { + cpmm.sendAsyncMessage("Activity:PostResult", { + "id": this._id, + "result": aResult + }); + }, + + postError: function arh_postError(aError) { + cpmm.sendAsyncMessage("Activity:PostError", { + "id": this._id, + "error": aError + }); + }, + + classID: Components.ID("{9326952a-dbe3-4d81-a51f-d9c160d96d6b}"), + + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsIDOMMozActivityRequestHandler + ]), + + classInfo: XPCOMUtils.generateCI({ + classID: Components.ID("{9326952a-dbe3-4d81-a51f-d9c160d96d6b}"), + contractID: "@mozilla.org/dom/activities/request-handler;1", + interfaces: [Ci.nsIDOMMozActivityRequestHandler], + flags: Ci.nsIClassInfo.DOM_OBJECT, + classDescription: "Activity Request Handler" + }) +} + +const NSGetFactory = XPCOMUtils.generateNSGetFactory([ActivityRequestHandler]); diff --git a/dom/activities/src/ActivityWrapper.js b/dom/activities/src/ActivityWrapper.js new file mode 100644 index 000000000000..3f431f2f2121 --- /dev/null +++ b/dom/activities/src/ActivityWrapper.js @@ -0,0 +1,45 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +function debug(aMsg) { + //dump("-- ActivityWrapper.js " + Date.now() + " : " + aMsg + "\n"); +} + +/** + * nsISystemMessagesWrapper implementation. Will return a + * nsIDOMMozActivityRequestHandler + */ +function ActivityWrapper() { + debug("ActivityWrapper"); +} + +ActivityWrapper.prototype = { + wrapMessage: function wrapMessage(aMessage) { + debug("Wrapping " + JSON.stringify(aMessage)); + let handler = Cc["@mozilla.org/dom/activities/request-handler;1"] + .createInstance(Ci.nsIDOMMozActivityRequestHandler); + handler.wrappedJSObject._id = aMessage.id; + + // options is an nsIDOMActivityOptions object. + var options = handler.wrappedJSObject._options; + options.wrappedJSObject._name = aMessage.payload.name; + options.wrappedJSObject._data = aMessage.payload.data; + + return handler; + }, + + classID: Components.ID("{5430d6f9-32d6-4924-ba39-6b6d1b093cd6}"), + QueryInterface: XPCOMUtils.generateQI([Ci.nsISystemMessagesWrapper]) +} + +const NSGetFactory = XPCOMUtils.generateNSGetFactory([ActivityWrapper]); + diff --git a/dom/activities/src/Makefile.in b/dom/activities/src/Makefile.in index fb3d19c028bd..25e21e3e9bca 100644 --- a/dom/activities/src/Makefile.in +++ b/dom/activities/src/Makefile.in @@ -26,9 +26,16 @@ EXPORTS_mozilla/dom = \ $(NULL) EXTRA_COMPONENTS = \ + ActivityOptions.js \ ActivityProxy.js \ + ActivityRequestHandler.js \ + ActivityWrapper.js \ Activities.manifest \ $(NULL) +EXTRA_JS_MODULES = \ + ActivitiesService.jsm \ + $(NULL) + include $(topsrcdir)/config/config.mk include $(topsrcdir)/config/rules.mk diff --git a/dom/apps/src/Webapps.jsm b/dom/apps/src/Webapps.jsm index 8d9a8c379a05..c006fa72b45f 100644 --- a/dom/apps/src/Webapps.jsm +++ b/dom/apps/src/Webapps.jsm @@ -14,6 +14,7 @@ let EXPORTED_SYMBOLS = ["DOMApplicationRegistry", "DOMApplicationManifest"]; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/FileUtils.jsm"); +Cu.import('resource://gre/modules/ActivitiesService.jsm'); const WEBAPP_RUNTIME = Services.appinfo.ID == "webapprt@mozilla.org"; @@ -27,6 +28,10 @@ XPCOMUtils.defineLazyGetter(this, "ppmm", function() { .getService(Ci.nsIFrameMessageManager); }); +XPCOMUtils.defineLazyServiceGetter(this, "cpmm", + "@mozilla.org/childprocessmessagemanager;1", + "nsIFrameMessageManager"); + XPCOMUtils.defineLazyGetter(this, "msgmgr", function() { return Cc["@mozilla.org/system-message-internal;1"] .getService(Ci.nsISystemMessagesInternal); @@ -67,7 +72,7 @@ let DOMApplicationRegistry = { this.webapps = aData; for (let id in this.webapps) { #ifdef MOZ_SYS_MSG - this._registerSystemMessagesForId(id); + this._processManifestForId(id); #endif if (!this.webapps[id].localId) { this.webapps[id].localId = this._nextLocalId(); @@ -99,10 +104,51 @@ let DOMApplicationRegistry = { } }, - _registerSystemMessagesForId: function(aId) { + _registerActivities: function(aManifest, aApp) { + if (!aManifest.activities) { + return; + } + + let manifest = new DOMApplicationManifest(aManifest, aApp.origin); + for (let activity in aManifest.activities) { + let description = aManifest.activities[activity]; + let json = { + "manifest": aApp.manifestURL, + "name": activity, + "title": manifest.name, + "icon": manifest.iconURLForSize(128), + "description": description + } + cpmm.sendAsyncMessage("Activities:Register", json); + + let launchPath = + Services.io.newURI(manifest.fullLaunchPath(description.href), null, null); + let manifestURL = Services.io.newURI(aApp.manifestURL, null, null); + msgmgr.registerPage("activity", launchPath, manifestURL); + } + }, + + _unregisterActivities: function(aManifest, aApp) { + if (!aManifest.activities) { + return; + } + + for (let activity in aManifest.activities) { + let description = aManifest.activities[activity]; + let json = { + "manifest": aApp.manifestURL, + "name": activity + } + cpmm.sendAsyncMessage("Activities:Unregister", json); + } + }, + + _processManifestForId: function(aId) { let app = this.webapps[aId]; this._readManifests([{ id: aId }], (function registerManifest(aResult) { - this._registerSystemMessages(aResult[0].manifest, app); + let manifest = aResult[0].manifest; + this._registerSystemMessages(manifest, app); + this._registerActivities(manifest, app); }).bind(this)); }, #endif @@ -489,26 +535,36 @@ let DOMApplicationRegistry = { let found = false; for (let id in this.webapps) { let app = this.webapps[id]; - if (app.origin == aData.origin) { - found = true; - let appNote = JSON.stringify(this._cloneAppObject(app)); - appNote.id = id; - - delete this.webapps[id]; - let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true); - try { - dir.remove(true); - } catch (e) { - } - - this._saveApps((function() { - ppmm.sendAsyncMessage("Webapps:Uninstall:Return:OK", aData); - Services.obs.notifyObservers(this, "webapps-sync-uninstall", appNote); - }).bind(this)); + if (app.origin != aData.origin) { + continue; } + + found = true; + let appNote = JSON.stringify(this._cloneAppObject(app)); + appNote.id = id; + + this._readManifests([{ id: id }], (function unregisterManifest(aResult) { +#ifdef MOZ_SYS_MSG + this._unregisterActivities(aResult[0].manifest, app); +#endif + }).bind(this)); + + let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true); + try { + dir.remove(true); + } catch (e) {} + + delete this.webapps[id]; + + this._saveApps((function() { + ppmm.sendAsyncMessage("Webapps:Uninstall:Return:OK", aData); + Services.obs.notifyObservers(this, "webapps-sync-uninstall", appNote); + }).bind(this)); } - if (!found) + + if (!found) { ppmm.sendAsyncMessage("Webapps:Uninstall:Return:KO", aData); + } }, getSelf: function(aData) { diff --git a/dom/messages/SystemMessageManager.js b/dom/messages/SystemMessageManager.js index 60d62861c781..30ed97b215e5 100644 --- a/dom/messages/SystemMessageManager.js +++ b/dom/messages/SystemMessageManager.js @@ -45,6 +45,27 @@ function SystemMessageManager() { SystemMessageManager.prototype = { __proto__: DOMRequestIpcHelper.prototype, + _dispatchMessage: function sysMessMgr_dispatchMessage(aType, aHandler, aMessage) { + // We get a json blob, but in some cases we want another kind of object + // to be dispatched. + // To do so, we check if we have a with a contract ID of + // "@mozilla.org/dom/system-messages/wrapper/TYPE;1" component implementing + // nsISystemMessageWrapper. + debug("Dispatching " + JSON.stringify(aMessage) + "\n"); + let contractID = "@mozilla.org/dom/system-messages/wrapper/" + aType + ";1"; + + if (contractID in Cc) { + debug(contractID + " is registered, creating an instance"); + let wrapper = Cc[contractID].createInstance(Ci.nsISystemMessagesWrapper); + if (wrapper) { + aMessage = wrapper.wrapMessage(aMessage); + debug("wrapped = " + aMessage); + } + } + + aHandler.handleMessage(aMessage); + }, + mozSetMessageHandler: function sysMessMgr_setMessageHandler(aType, aHandler) { debug("setMessage handler for [" + aType + "] " + aHandler); if (!aType) { @@ -68,10 +89,11 @@ SystemMessageManager.prototype = { let thread = Services.tm.mainThread; let pending = this._pendings[aType]; this._pendings[aType] = []; + let self = this; pending.forEach(function dispatch_pending(aPending) { thread.dispatch({ run: function run() { - aHandler.handleMessage(aPending); + self._dispatchMessage(aType, aHandler, aPending); } }, Ci.nsIEventTarget.DISPATCH_NORMAL); }); @@ -139,7 +161,7 @@ SystemMessageManager.prototype = { return; } - this._handlers[msg.type].handleMessage(msg.msg); + this._dispatchMessage(msg.type, this._handlers[msg.type], msg.msg); }, // nsIDOMGlobalPropertyInitializer implementation. diff --git a/dom/messages/interfaces/nsISystemMessagesInternal.idl b/dom/messages/interfaces/nsISystemMessagesInternal.idl index 01316daf6b47..fba5d9bad17a 100644 --- a/dom/messages/interfaces/nsISystemMessagesInternal.idl +++ b/dom/messages/interfaces/nsISystemMessagesInternal.idl @@ -27,3 +27,13 @@ interface nsISystemMessagesInternal : nsISupports */ void registerPage(in DOMString type, in nsIURI pageURI, in nsIURI manifestURI); }; + +[scriptable, uuid(b43c74ec-1b64-49fb-b552-aadd9d827eec)] +interface nsISystemMessagesWrapper: nsISupports +{ + /* + * Wrap a message and gives back any kind of object. + * @param message The json blob to wrap. + */ + jsval wrapMessage(in jsval message); +};