2011-11-28 12:13:26 -08:00
|
|
|
/* ***** 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é <fabrice@mozilla.com>
|
|
|
|
*
|
|
|
|
* 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 WebappsRegistry() {
|
|
|
|
this.messages = ["Webapps:Install:Return:OK", "Webapps:Install:Return:KO",
|
|
|
|
"Webapps:Uninstall:Return:OK", "Webapps:Uninstall:Return:KO",
|
|
|
|
"Webapps:Enumerate:Return:OK", "Webapps:Enumerate:Return:KO"];
|
|
|
|
|
|
|
|
this.mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
|
|
|
|
|
|
|
|
this.messages.forEach((function(msgName) {
|
|
|
|
this.mm.addMessageListener(msgName, this);
|
|
|
|
}).bind(this));
|
|
|
|
|
|
|
|
this._window = null;
|
|
|
|
this._id = this._getRandomId();
|
|
|
|
this._callbacks = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
WebappsRegistry.prototype = {
|
|
|
|
_onerror: null,
|
|
|
|
_oninstall: null,
|
|
|
|
_onuninstall: null,
|
|
|
|
|
|
|
|
/** from https://developer.mozilla.org/en/OpenWebApps/The_Manifest
|
|
|
|
* only the name property is mandatory
|
|
|
|
*/
|
|
|
|
checkManifest: function(aManifest, aInstallOrigin) {
|
|
|
|
// TODO : check for install_allowed_from
|
|
|
|
if (aManifest.name == undefined)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (aManifest.installs_allowed_from) {
|
|
|
|
ok = false;
|
|
|
|
aManifest.installs_allowed_from.forEach(function(aOrigin) {
|
|
|
|
if (aOrigin == "*" || aOrigin == aInstallOrigin)
|
|
|
|
ok = true;
|
|
|
|
});
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
|
|
|
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];
|
|
|
|
apps.push(new WebappsApplication(app.origin, app.manifest, app.receipt, app.installOrigin, app.installTime));
|
|
|
|
}
|
|
|
|
return apps;
|
|
|
|
},
|
|
|
|
|
|
|
|
set oninstall(aCallback) {
|
|
|
|
if (this.hasPrivileges)
|
|
|
|
this._oninstall = aCallback;
|
|
|
|
else
|
|
|
|
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
},
|
|
|
|
|
|
|
|
set onuninstall(aCallback) {
|
|
|
|
if (this.hasPrivileges)
|
|
|
|
this._onuninstall = aCallback;
|
|
|
|
else
|
|
|
|
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
},
|
|
|
|
|
|
|
|
set onerror(aCallback) {
|
|
|
|
this._onerror = aCallback;
|
|
|
|
},
|
|
|
|
|
|
|
|
receiveMessage: function(aMessage) {
|
|
|
|
let msg = aMessage.json;
|
|
|
|
if (!(msg.oid == this._id || aMessage.name == "Webapps:Install:Return:OK" || aMessage.name == "Webapps:Uninstall:Return:OK"))
|
|
|
|
return
|
|
|
|
let app = msg.app;
|
|
|
|
let cb;
|
|
|
|
switch (aMessage.name) {
|
|
|
|
case "Webapps:Install:Return:OK":
|
|
|
|
if (this._oninstall)
|
|
|
|
this._oninstall.handleEvent(new WebappsApplication(app.origin, app.manifest, app.receipt,
|
|
|
|
app.installOrigin, app.installTime));
|
|
|
|
break;
|
|
|
|
case "Webapps:Install:Return:KO":
|
|
|
|
if (this._onerror)
|
|
|
|
this._onerror.handleEvent(new RegistryError(Ci.mozIDOMApplicationRegistryError.DENIED));
|
|
|
|
break;
|
|
|
|
case "Webapps:Uninstall:Return:OK":
|
|
|
|
if (this._onuninstall)
|
|
|
|
this._onuninstall.handleEvent(new WebappsApplication(msg.origin, null, null, null, 0));
|
|
|
|
break;
|
|
|
|
case "Webapps:Uninstall:Return:KO":
|
|
|
|
if (this._onerror)
|
|
|
|
this._onerror.handleEvent(new RegistryError(Ci.mozIDOMApplicationRegistryError.PERMISSION_DENIED));
|
|
|
|
break;
|
|
|
|
case "Webapps:Enumerate:Return:OK":
|
|
|
|
cb = this.getCallback(msg.callbackID);
|
|
|
|
if (cb.success) {
|
|
|
|
let apps = this._convertAppsArray(msg.apps);
|
|
|
|
cb.success.handleEvent(apps, apps.length);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case "Webapps:Enumerate:Return:KO":
|
|
|
|
cb = this.getCallback(msg.callbackID);
|
|
|
|
if (cb.error)
|
|
|
|
cb.error.handleEvent(new RegistryError(Ci.mozIDOMApplicationRegistryError.PERMISSION_DENIED));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
this.removeCallback(msg.callbackID);
|
|
|
|
},
|
|
|
|
|
|
|
|
_fireError: function(aCode) {
|
|
|
|
if (!this._onerror)
|
|
|
|
return;
|
|
|
|
this._onerror.handleEvent(new RegistryError(aCode));
|
|
|
|
},
|
|
|
|
|
|
|
|
_getOrigin: function(aURL) {
|
|
|
|
let uri = Services.io.newURI(aURL, null, null);
|
|
|
|
return uri.prePath;
|
|
|
|
},
|
|
|
|
|
|
|
|
// mozIDOMApplicationRegistry implementation
|
|
|
|
|
|
|
|
install: function(aURL, aReceipt) {
|
|
|
|
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 installOrigin = this._getOrigin(this._window.location.href);
|
|
|
|
let manifest = JSON.parse(xhr.responseText, installOrigin);
|
|
|
|
if (!this.checkManifest(manifest, installOrigin)) {
|
|
|
|
this._fireError(Ci.mozIDOMApplicationRegistryError.INVALID_MANIFEST);
|
|
|
|
} else {
|
|
|
|
this.mm.sendAsyncMessage("Webapps:Install", { app: { installOrigin: installOrigin,
|
|
|
|
origin: this._getOrigin(aURL),
|
|
|
|
manifest: manifest,
|
|
|
|
receipt: aReceipt },
|
|
|
|
from: this._window.location.href,
|
|
|
|
oid: this._id });
|
|
|
|
}
|
|
|
|
} catch(e) {
|
|
|
|
this._fireError(Ci.mozIDOMApplicationRegistryError.MANIFEST_PARSE_ERROR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
this._fireError(Ci.mozIDOMApplicationRegistryError.MANIFEST_URL_ERROR);
|
|
|
|
}
|
|
|
|
}).bind(this), false);
|
|
|
|
|
|
|
|
xhr.addEventListener("error", (function() {
|
|
|
|
this._fireError(Ci.mozIDOMApplicationRegistryError.NETWORK_ERROR);
|
|
|
|
}).bind(this), false);
|
|
|
|
|
|
|
|
xhr.send(null);
|
|
|
|
},
|
|
|
|
|
|
|
|
uninstall: function(aOrigin) {
|
|
|
|
if (this.hasPrivileges)
|
|
|
|
this.mm.sendAsyncMessage("Webapps:Uninstall", { from: this._window.location.href,
|
|
|
|
origin: aOrigin,
|
|
|
|
oid: this._id });
|
|
|
|
else
|
|
|
|
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
},
|
|
|
|
|
|
|
|
launch: function(aOrigin) {
|
|
|
|
this.mm.sendAsyncMessage("Webapps:Launch", { origin: aOrigin,
|
|
|
|
from: this._window.location.href});
|
|
|
|
},
|
|
|
|
|
|
|
|
enumerate: function(aSuccess, aError) {
|
|
|
|
this.mm.sendAsyncMessage("Webapps:Enumerate", { from: this._window.location.href,
|
|
|
|
origin: this._getOrigin(this._window.location.href),
|
|
|
|
oid: this._id,
|
|
|
|
callbackID: this.getCallbackId({ success: aSuccess, error: aError }) });
|
|
|
|
},
|
|
|
|
|
2011-12-07 18:15:42 -08:00
|
|
|
observe: function(aSubject, aTopic, aData) {
|
|
|
|
let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
|
|
|
|
if (wId == this.innerWindowID) {
|
|
|
|
Services.obs.removeObserver(this, "inner-window-destroyed");
|
|
|
|
this._oninstall = null;
|
|
|
|
this._onuninstall = null;
|
|
|
|
this._onerror = null;
|
|
|
|
this._callbacks = [];
|
|
|
|
this._window = null;
|
2011-11-28 12:13:26 -08:00
|
|
|
}
|
|
|
|
},
|
2011-12-07 18:15:42 -08:00
|
|
|
|
2011-11-28 12:13:26 -08:00
|
|
|
// nsIDOMGlobalPropertyInitializer implementation
|
|
|
|
init: function(aWindow) {
|
|
|
|
dump("DOMApplicationRegistry::init() " + aWindow + "\n");
|
|
|
|
this._window = aWindow;
|
|
|
|
this._window.appId = this._id;
|
|
|
|
let from = Services.io.newURI(this._window.location.href, null, null);
|
|
|
|
let perm = Services.perms.testExactPermission(from, "webapps-manage");
|
|
|
|
|
|
|
|
//only pages with perm set and chrome or about pages can uninstall, enumerate all set oninstall an onuninstall
|
|
|
|
this.hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION || from.schemeIs("chrome") || from.schemeIs("about");
|
2011-12-07 18:15:42 -08:00
|
|
|
|
|
|
|
Services.obs.addObserver(this, "inner-window-destroyed", false);
|
|
|
|
let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
|
|
|
this.innerWindowID = util.currentInnerWindowID;
|
2011-11-28 12:13:26 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
classID: Components.ID("{fff440b3-fae2-45c1-bf03-3b5a2e432270}"),
|
|
|
|
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationRegistry, Ci.nsIDOMGlobalPropertyInitializer]),
|
|
|
|
|
|
|
|
classInfo: XPCOMUtils.generateCI({classID: Components.ID("{fff440b3-fae2-45c1-bf03-3b5a2e432270}"),
|
|
|
|
contractID: "@mozilla.org/webapps;1",
|
|
|
|
interfaces: [Ci.mozIDOMApplicationRegistry],
|
|
|
|
flags: Ci.nsIClassInfo.DOM_OBJECT,
|
|
|
|
classDescription: "Webapps Registry"})
|
|
|
|
}
|
|
|
|
|
|
|
|
function WebappsApplication(aOrigin, aManifest, aReceipt, aInstallOrigin, aInstallTime) {
|
|
|
|
this._origin = aOrigin;
|
|
|
|
this._manifest = aManifest;
|
|
|
|
this._receipt = aReceipt;
|
|
|
|
this._installOrigin = aInstallOrigin;
|
|
|
|
this._installTime = aInstallTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
WebappsApplication.prototype = {
|
|
|
|
_origin: null,
|
|
|
|
_manifest: null,
|
|
|
|
_receipt: null,
|
|
|
|
_installOrigin: null,
|
|
|
|
_installTime: 0,
|
|
|
|
|
|
|
|
get origin() {
|
|
|
|
return this._origin;
|
|
|
|
},
|
|
|
|
|
|
|
|
get manifest() {
|
|
|
|
return this._manifest;
|
|
|
|
},
|
|
|
|
|
|
|
|
get receipt() {
|
|
|
|
return this._receipt;
|
|
|
|
},
|
|
|
|
|
|
|
|
get installOrigin() {
|
|
|
|
return this._installOrigin;
|
|
|
|
},
|
|
|
|
|
|
|
|
get installTime() {
|
|
|
|
return this._installTime;
|
|
|
|
},
|
|
|
|
|
|
|
|
classID: Components.ID("{723ed303-7757-4fb0-b261-4f78b1f6bd22}"),
|
|
|
|
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplication]),
|
|
|
|
|
|
|
|
classInfo: XPCOMUtils.generateCI({classID: Components.ID("{723ed303-7757-4fb0-b261-4f78b1f6bd22}"),
|
|
|
|
contractID: "@mozilla.org/webapps/application;1",
|
|
|
|
interfaces: [Ci.mozIDOMApplication],
|
|
|
|
flags: Ci.nsIClassInfo.DOM_OBJECT,
|
|
|
|
classDescription: "Webapps Application"})
|
|
|
|
}
|
|
|
|
|
|
|
|
function RegistryError(aCode) {
|
|
|
|
this._code = aCode;
|
|
|
|
}
|
|
|
|
|
|
|
|
RegistryError.prototype = {
|
|
|
|
_code: null,
|
|
|
|
|
|
|
|
get code() {
|
|
|
|
return this._code;
|
|
|
|
},
|
|
|
|
|
|
|
|
classID: Components.ID("{b4937718-11a3-400b-a69f-ab442a418569}"),
|
|
|
|
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationRegistryError]),
|
|
|
|
|
|
|
|
classInfo: XPCOMUtils.generateCI({classID: Components.ID("{b4937718-11a3-400b-a69f-ab442a418569}"),
|
|
|
|
contractID: "@mozilla.org/webapps/error;1",
|
|
|
|
interfaces: [Ci.mozIDOMApplicationRegistryError],
|
|
|
|
flags: Ci.nsIClassInfo.DOM_OBJECT,
|
|
|
|
classDescription: "Webapps Registry Error"})
|
|
|
|
}
|
|
|
|
|
|
|
|
const NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsRegistry, WebappsApplication, RegistryError]);
|