/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Open Web Apps. * * The Initial Developer of the Original Code is Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2011 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Fabrice Desré * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; const Cr = Components.results; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); function OpenWebapps() { this.messages = ["OpenWebapps:InstallDone", "OpenWebapps:InstallAborted", "OpenWebapps:GetInstalledBy:Return", "OpenWebapps:AmInstalled:Return", "OpenWebapps:MgmtLaunch:Return", "OpenWebapps:MgmtList:Return", "OpenWebapps:MgmtUninstall:Return"]; this.mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsISyncMessageSender); this.messages.forEach((function(msgName) { this.mm.addMessageListener(msgName, this); }).bind(this)); this._callbacks = []; this._window = null; this._watchId = 0; } OpenWebapps.prototype = { /** from https://developer.mozilla.org/en/OpenWebApps/The_Manifest * only the name property is mandatory */ checkManifest: function(aManifest) { return ("name" in aManifest); }, getCallbackId: function(aCallback) { let id = "id" + this._getRandomId(); this._callbacks[id] = aCallback; return id; }, getCallback: function(aId) { return this._callbacks[aId]; }, removeCallback: function(aId) { if (this._callbacks[aId]) delete this._callbacks[aId]; }, _getRandomId: function() { return Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString(); }, _convertAppsArray: function(aApps) { let apps = new Array(); for (let i = 0; i < aApps.length; i++) { let app = aApps[i]; let xapp = Cc["@mozilla.org/openwebapps/application;1"].createInstance(Ci.nsIOpenWebappsApplication); xapp.origin = app.origin; xapp.manifest = app.manifest; xapp.install_data = app.install_data; xapp.install_origin = app.install_origin; xapp.install_time = app.install_time; apps.push(xapp); } return apps; }, receiveMessage: function(aMessage) { let msg = aMessage.json; let callbacks = this.getCallback(msg.callbackID); // if we have no such callback and this is not a broadcast message, bail out if (!callbacks && aMessage.name != "OpenWebapps:InstallDone" && aMessage.name != "OpenWebapps:MgmtUninstall:Return") return; switch(aMessage.name) { case "OpenWebapps:InstallAborted" : if (callbacks.error) callbacks.error.handle({ code: "denied", message: "User denied installation" }); break; case "OpenWebapps:InstallDone" : if (callbacks && callbacks.success) callbacks.success.handle(); this._onInstalled([msg.app]); break; case "OpenWebapps:GetInstalledBy:Return": if (callbacks && callbacks.success) { let apps = this._convertAppsArray(msg.apps); callbacks.success.handle(apps, apps.length); } break; case "OpenWebapps:AmInstalled:Return": if (callbacks.success) callbacks.success.handle(msg.installed ? msg.app : null); break; case "OpenWebapps:MgmtLaunch:Return": if (msg.ok && callbacks && callbacks.success) callbacks.success.handle(); else if (!msg.ok && callbacks.error) callbacks.error.handle({ code: "noSuchApp", message: "Unable to launch application"}); break; case "OpenWebapps:MgmtList:Return": if (msg.ok && callbacks && callbacks.success) { let apps = this._convertAppsArray(msg.apps); callbacks.success.handle(apps, apps.length); } else if (!msg.ok && callbacks && callbacks.error) { callbacks.error.handle({ code: "noAppList", message: "Unable to get application list"}); } break; case "OpenWebapps:MgmtUninstall:Return": if (msg.ok) { if (callbacks && callbacks.success) callbacks.success.handle(); this._onUninstalled([msg.app]); } else if (!msg.ok && callbacks.error) callbacks.error.handle({ code: "noSuchApp", message: "Unable to uninstall application"}); break; } this.removeCallback(msg.callbackID); }, // nsIOpenWebapps implementation install: function(aURL, aInstallData, aSuccess, aError) { let self = this; let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest); xhr.open("GET", aURL, true); xhr.addEventListener("load", function() { if (xhr.status == 200) { try { let manifest = JSON.parse(xhr.responseText); if (!self.checkManifest(manifest)) { if (aError) aError.handle({ code: "invalidManifest", message: "Invalid manifest" }); } else { self.mm.sendAsyncMessage("OpenWebapps:Install", { storeURI: self._window.location.href, manifestURI: aURL, manifest: xhr.responseText, installData: aInstallData, callbackID: self.getCallbackId({ success: aSuccess, error: aError }) }); } } catch(e) { if (aError) aError.handle({ code: "manifestParseError", message: "Unable to parse the manifest" }); } } else if (aError) { aError.handle({ code: "networkError", message: "Unable to retrieve manifest" }); } }, false); xhr.addEventListener("error", function() { if (aError) aError.handle({ code: "networkError", message: "Unable to retrieve manifest" }); }, false); xhr.send(null); }, amInstalled: function(aSuccess, aError) { this.mm.sendAsyncMessage("OpenWebapps:AmInstalled", { appURI: this._window.location.href, callbackID: this.getCallbackId({ success: aSuccess, error: aError }) }); }, getInstalledBy: function(aSuccess, aError) { this.mm.sendAsyncMessage("OpenWebapps:GetInstalledBy", { storeURI: this._window.location.href, callbackID: this.getCallbackId({ success: aSuccess, error: aError }) }); }, // nsIOpenWebappsMgmt implementation launch: function(aOrigin, aSuccess, aError) { this.mm.sendAsyncMessage("OpenWebapps:MgmtLaunch", { origin: aOrigin, callbackID: this.getCallbackId({ success: aSuccess, error: aError }) }); }, list: function(aSuccess, aError) { this.mm.sendAsyncMessage("OpenWebapps:MgmtList", { from: this._window.location.href, callbackID: this.getCallbackId({ success: aSuccess, error: aError }) }); }, uninstall: function(aOrigin, aSuccess, aError) { this.mm.sendAsyncMessage("OpenWebapps:MgmtUninstall", { from: this._window.location.href, origin: aOrigin, callbackID: this.getCallbackId({ success: aSuccess, error: aError }) }); }, _onRepoChange: function(aWhat, aApps) { for (let prop in this._callbacks) { if (this._callbacks[prop].isWatch) { let apps = this._convertAppsArray(aApps); this._callbacks[prop].callback.update(aWhat, apps, apps.length); } } }, _onInstalled: function(aApps) { this._onRepoChange("add", aApps); }, _onUninstalled: function(aApps) { this._onRepoChange("remove", aApps); }, watchUpdates: function(aCallback) { this._watchId++; this._callbacks["_watch" + this._getRandomId()] = { isWatch: true, callback: aCallback }; return this._watchId; }, clearWatch: function(aWatchId) { this.removeCallback("_watch" + aWatchId); }, handleEvent: function(aEvent) { if (aEvent.type == "unload") { // remove all callbacks so we don't call anything on a cleared scope this._callbacks = []; } }, // nsIDOMGlobalPropertyInitializer implementation init: function(aWindow) { this._window = aWindow; this._window.addEventListener("unload", this, false); }, get mgmt() { return this.QueryInterface(Ci.nsIOpenWebappsMgmt); }, classID: Components.ID("{d8fd4d63-27ea-47b9-a931-481214bb8b5b}"), QueryInterface: XPCOMUtils.generateQI([Ci.nsIOpenWebapps, Ci.nsIOpenWebappsMgmt, Ci.nsIDOMGlobalPropertyInitializer]), classInfo: XPCOMUtils.generateCI({classID: Components.ID("{d8fd4d63-27ea-47b9-a931-481214bb8b5b}"), contractID: "@mozilla.org/openwebapps;1", interfaces: [Ci.nsIOpenWebapps], flags: Ci.nsIClassInfo.DOM_OBJECT, classDescription: "OpenWebapps"}) } function OpenWebappsApplication() { } OpenWebappsApplication.prototype = { origin: null, manifest: null, install_data: null, install_origin: null, install_time: 0, classID: Components.ID("{34456347-0792-45a4-8eb1-7b5f94f2d700}"), QueryInterface: XPCOMUtils.generateQI([Ci.nsIOpenWebappsApplication]), classInfo: XPCOMUtils.generateCI({classID: Components.ID("{34456347-0792-45a4-8eb1-7b5f94f2d700}"), contractID: "@mozilla.org/openwebapps/application;1", interfaces: [Ci.nsIOpenWebappsApplication], flags: Ci.nsIClassInfo.DOM_OBJECT, classDescription: "OpenWebapps Application"}) } const NSGetFactory = XPCOMUtils.generateNSGetFactory([OpenWebapps, OpenWebappsApplication]);