2012-03-06 19:50:58 +00:00
|
|
|
/* 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/. */
|
2011-11-28 20:13:26 +00:00
|
|
|
|
2012-07-03 00:16:55 +00:00
|
|
|
"use strict";
|
|
|
|
|
2012-04-28 07:10:08 +00:00
|
|
|
const Cu = Components.utils;
|
2011-11-28 20:13:26 +00:00
|
|
|
const Cc = Components.classes;
|
|
|
|
const Ci = Components.interfaces;
|
2012-07-11 15:38:33 +00:00
|
|
|
const Cr = Components.results;
|
2011-11-28 20:13:26 +00:00
|
|
|
|
|
|
|
let EXPORTED_SYMBOLS = ["DOMApplicationRegistry", "DOMApplicationManifest"];
|
|
|
|
|
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
2011-12-06 04:22:01 +00:00
|
|
|
Cu.import("resource://gre/modules/FileUtils.jsm");
|
2012-07-20 15:41:30 +00:00
|
|
|
Cu.import('resource://gre/modules/ActivitiesService.jsm');
|
2012-08-28 02:43:57 +00:00
|
|
|
Cu.import("resource://gre/modules/AppsUtils.jsm");
|
2012-09-28 22:16:29 +00:00
|
|
|
Cu.import("resource://gre/modules/PermissionsTable.jsm");
|
2011-11-28 20:13:26 +00:00
|
|
|
|
2012-09-27 01:01:20 +00:00
|
|
|
function debug(aMsg) {
|
|
|
|
//dump("-*-*- Webapps.jsm : " + aMsg + "\n");
|
|
|
|
}
|
|
|
|
|
2012-04-17 14:11:53 +00:00
|
|
|
const WEBAPP_RUNTIME = Services.appinfo.ID == "webapprt@mozilla.org";
|
|
|
|
|
2012-09-28 22:16:29 +00:00
|
|
|
// Permission access flags
|
|
|
|
const READONLY = "readonly";
|
|
|
|
const CREATEONLY = "createonly";
|
|
|
|
const READCREATE = "readcreate";
|
|
|
|
const READWRITE = "readwrite";
|
|
|
|
|
|
|
|
const PERM_TO_STRING = ["unknown", "allow", "deny", "prompt"];
|
|
|
|
|
|
|
|
XPCOMUtils.defineLazyServiceGetter(this,
|
|
|
|
"PermSettings",
|
|
|
|
"@mozilla.org/permissionSettings;1",
|
|
|
|
"nsIDOMPermissionSettings");
|
|
|
|
|
2011-11-28 20:13:26 +00:00
|
|
|
XPCOMUtils.defineLazyGetter(this, "NetUtil", function() {
|
|
|
|
Cu.import("resource://gre/modules/NetUtil.jsm");
|
|
|
|
return NetUtil;
|
|
|
|
});
|
|
|
|
|
2012-09-28 22:16:29 +00:00
|
|
|
XPCOMUtils.defineLazyServiceGetter(this,
|
|
|
|
"permissionManager",
|
|
|
|
"@mozilla.org/permissionmanager;1",
|
|
|
|
"nsIPermissionManager");
|
|
|
|
|
2012-08-27 14:13:02 +00:00
|
|
|
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
|
|
|
|
"@mozilla.org/parentprocessmessagemanager;1",
|
|
|
|
"nsIMessageBroadcaster");
|
2012-03-06 19:50:58 +00:00
|
|
|
|
2012-07-20 15:41:30 +00:00
|
|
|
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
|
|
|
"@mozilla.org/childprocessmessagemanager;1",
|
2012-08-27 14:13:02 +00:00
|
|
|
"nsIMessageSender");
|
2012-07-20 15:41:30 +00:00
|
|
|
|
2012-07-03 00:16:55 +00:00
|
|
|
XPCOMUtils.defineLazyGetter(this, "msgmgr", function() {
|
2012-07-11 15:38:33 +00:00
|
|
|
return Cc["@mozilla.org/system-message-internal;1"]
|
|
|
|
.getService(Ci.nsISystemMessagesInternal);
|
2012-07-03 00:16:55 +00:00
|
|
|
});
|
|
|
|
|
2012-03-23 23:39:15 +00:00
|
|
|
#ifdef MOZ_WIDGET_GONK
|
|
|
|
const DIRECTORY_NAME = "webappsDir";
|
2012-08-15 21:02:15 +00:00
|
|
|
#elifdef ANDROID
|
|
|
|
const DIRECTORY_NAME = "webappsDir";
|
2012-03-23 23:39:15 +00:00
|
|
|
#else
|
2012-04-17 14:11:53 +00:00
|
|
|
// If we're executing in the context of the webapp runtime, the data files
|
|
|
|
// are in a different directory (currently the Firefox profile that installed
|
|
|
|
// the webapp); otherwise, they're in the current profile.
|
|
|
|
const DIRECTORY_NAME = WEBAPP_RUNTIME ? "WebappRegD" : "ProfD";
|
2012-03-23 23:39:15 +00:00
|
|
|
#endif
|
|
|
|
|
2012-09-28 22:16:29 +00:00
|
|
|
/**
|
|
|
|
* Determine the type of app (app, privileged, certified)
|
|
|
|
* that is installed by the manifest
|
|
|
|
* @param object aManifest
|
|
|
|
* @returns integer
|
|
|
|
**/
|
|
|
|
function getAppManifestStatus(aManifest)
|
|
|
|
{
|
|
|
|
let type = aManifest.type || "web";
|
|
|
|
|
|
|
|
switch(type) {
|
|
|
|
case "web":
|
|
|
|
return Ci.nsIPrincipal.APP_STATUS_INSTALLED;
|
|
|
|
case "privileged":
|
|
|
|
return Ci.nsIPrincipal.APP_STATUS_PRIVILEGED;
|
|
|
|
case "certified":
|
|
|
|
return Ci.nsIPrincipal.APP_STATUS_CERTIFIED;
|
|
|
|
default:
|
|
|
|
throw new Error("Webapps.jsm: Undetermined app manifest type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Expand an access string into multiple permission names,
|
|
|
|
* e.g: perm 'contacts' with 'readwrite' =
|
|
|
|
* ['contacts-read', 'contacts-create', contacts-write']
|
|
|
|
* @param string aPermName
|
|
|
|
* @param string aAccess
|
|
|
|
* @returns Array
|
|
|
|
**/
|
|
|
|
function expandPermissions(aPermName, aAccess)
|
|
|
|
{
|
|
|
|
if (!PermissionsTable[aPermName]) {
|
|
|
|
Cu.reportError("Unknown permission: " + aPermName);
|
|
|
|
throw new Error("Webapps.jsm: App install failed, Unknown Permission: " + aPermName);
|
|
|
|
}
|
|
|
|
if (!aAccess && PermissionsTable[aPermName].access ||
|
|
|
|
aAccess && !PermissionsTable[aPermName].access) {
|
|
|
|
Cu.reportError("Webapps.jsm: installPermissions: Invalid Manifest");
|
|
|
|
throw new Error("Webapps.jsm: App install failed, Invalid Manifest");
|
|
|
|
}
|
|
|
|
if (!PermissionsTable[aPermName].access) {
|
|
|
|
return [aPermName];
|
|
|
|
}
|
|
|
|
|
|
|
|
let requestedSuffixes = [];
|
|
|
|
switch(aAccess) {
|
|
|
|
case READONLY:
|
|
|
|
requestedSuffixes.push("read");
|
|
|
|
break;
|
|
|
|
case CREATEONLY:
|
|
|
|
requestedSuffixes.push("create");
|
|
|
|
break;
|
|
|
|
case READCREATE:
|
|
|
|
requestedSuffixes.push("read", "create");
|
|
|
|
break;
|
|
|
|
case READWRITE:
|
|
|
|
requestedSuffixes.push("read", "create", "write");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
let permArr = mapSuffixes(aPermName, requestedSuffixes);
|
|
|
|
|
|
|
|
let expandedPerms = [];
|
|
|
|
for (let idx in permArr) {
|
|
|
|
if (PermissionsTable[aPermName].access.indexOf(requestedSuffixes[idx]) != -1) {
|
|
|
|
expandedPerms.push(permArr[idx]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return expandedPerms;
|
|
|
|
}
|
|
|
|
|
2011-11-28 20:13:26 +00:00
|
|
|
let DOMApplicationRegistry = {
|
|
|
|
appsFile: null,
|
|
|
|
webapps: { },
|
2012-08-28 02:43:57 +00:00
|
|
|
children: [ ],
|
2012-06-29 20:46:21 +00:00
|
|
|
allAppsLaunchable: false,
|
2012-09-27 19:34:41 +00:00
|
|
|
downloads: { },
|
2011-11-28 20:13:26 +00:00
|
|
|
|
|
|
|
init: function() {
|
2012-03-13 00:33:10 +00:00
|
|
|
this.messages = ["Webapps:Install", "Webapps:Uninstall",
|
2012-09-25 15:04:24 +00:00
|
|
|
"Webapps:GetSelf", "Webapps:IsInstalled",
|
2012-08-28 02:43:57 +00:00
|
|
|
"Webapps:GetInstalled", "Webapps:GetNotInstalled",
|
|
|
|
"Webapps:Launch", "Webapps:GetAll",
|
|
|
|
"Webapps:InstallPackage", "Webapps:GetBasePath",
|
2012-09-18 17:34:55 +00:00
|
|
|
"Webapps:GetList", "Webapps:RegisterForMessages",
|
2012-09-27 01:01:20 +00:00
|
|
|
"Webapps:UnregisterForMessages",
|
2012-09-29 20:20:09 +00:00
|
|
|
"Webapps:CancelDownload", "Webapps:CheckForUpdate"];
|
2011-11-28 20:13:26 +00:00
|
|
|
|
2012-09-26 22:03:25 +00:00
|
|
|
this.frameMessages = ["Webapps:ClearBrowserData"];
|
|
|
|
|
2012-03-13 00:33:10 +00:00
|
|
|
this.messages.forEach((function(msgName) {
|
2012-03-06 19:50:58 +00:00
|
|
|
ppmm.addMessageListener(msgName, this);
|
2011-11-28 20:13:26 +00:00
|
|
|
}).bind(this));
|
|
|
|
|
2012-09-19 16:28:55 +00:00
|
|
|
cpmm.addMessageListener("Activities:Register:OK", this);
|
|
|
|
|
2012-03-13 00:33:10 +00:00
|
|
|
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
|
|
|
|
2012-07-11 15:38:33 +00:00
|
|
|
this.appsFile = FileUtils.getFile(DIRECTORY_NAME,
|
|
|
|
["webapps", "webapps.json"], true);
|
2011-12-06 04:22:01 +00:00
|
|
|
|
2012-09-19 16:28:55 +00:00
|
|
|
this.loadAndUpdateApps();
|
|
|
|
},
|
2012-08-29 22:58:31 +00:00
|
|
|
|
2012-09-19 16:28:55 +00:00
|
|
|
// loads the current registry, that could be empty on first run.
|
|
|
|
// aNext() is called after we load the current webapps list.
|
|
|
|
loadCurrentRegistry: function loadCurrentRegistry(aNext) {
|
|
|
|
let file = FileUtils.getFile(DIRECTORY_NAME, ["webapps", "webapps.json"], false);
|
|
|
|
if (file && file.exists) {
|
|
|
|
this._loadJSONAsync(file, (function loadRegistry(aData) {
|
|
|
|
if (aData) {
|
|
|
|
this.webapps = aData;
|
|
|
|
let appDir = FileUtils.getDir(DIRECTORY_NAME, ["webapps"], false);
|
|
|
|
for (let id in this.webapps) {
|
|
|
|
// Make sure we have a localId
|
|
|
|
if (this.webapps[id].localId === undefined) {
|
|
|
|
this.webapps[id].localId = this._nextLocalId();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.webapps[id].basePath === undefined) {
|
|
|
|
this.webapps[id].basePath = appDir.path;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Default to removable apps.
|
|
|
|
if (this.webapps[id].removable === undefined) {
|
|
|
|
this.webapps[id].removable = true;
|
2012-08-29 21:20:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Default to a non privileged status.
|
|
|
|
if (this.webapps[id].appStatus === undefined) {
|
|
|
|
this.webapps[id].appStatus = Ci.nsIPrincipal.APP_STATUS_INSTALLED;
|
|
|
|
}
|
|
|
|
};
|
2012-09-19 16:28:55 +00:00
|
|
|
}
|
|
|
|
aNext();
|
|
|
|
}).bind(this));
|
|
|
|
} else {
|
|
|
|
aNext();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
// We are done with loading and initializing. Notify and
|
|
|
|
// save a copy of the registry.
|
|
|
|
onInitDone: function onInitDone() {
|
|
|
|
Services.obs.notifyObservers(this, "webapps-registry-ready", null);
|
|
|
|
this._saveApps();
|
|
|
|
},
|
|
|
|
|
|
|
|
// registers all the activities and system messages
|
|
|
|
registerAppsHandlers: function registerAppsHandlers() {
|
2012-09-12 01:17:01 +00:00
|
|
|
#ifdef MOZ_SYS_MSG
|
2012-09-19 16:28:55 +00:00
|
|
|
let ids = [];
|
|
|
|
for (let id in this.webapps) {
|
|
|
|
ids.push({ id: id });
|
|
|
|
}
|
|
|
|
this._processManifestForIds(ids);
|
|
|
|
#else
|
|
|
|
// Nothing else to do but notifying we're ready.
|
|
|
|
this.onInitDone();
|
2012-09-12 01:17:01 +00:00
|
|
|
#endif
|
2012-09-19 16:28:55 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
// Implements the core of bug 787439
|
|
|
|
// 1. load the apps from the current registry.
|
|
|
|
// 2. if at first run, go through these steps:
|
|
|
|
// a. load the core apps registry.
|
|
|
|
// b. uninstall any core app from the current registry but not in the
|
|
|
|
// new core apps registry.
|
|
|
|
// c. for all apps in the new core registry, install them if they are not
|
|
|
|
// yet in the current registry, and run installPermissions()
|
|
|
|
loadAndUpdateApps: function loadAndUpdateApps() {
|
|
|
|
let runUpdate = Services.prefs.getBoolPref("dom.mozApps.runUpdate");
|
|
|
|
Services.prefs.setBoolPref("dom.mozApps.runUpdate", false);
|
|
|
|
|
|
|
|
// 1.
|
|
|
|
this.loadCurrentRegistry((function() {
|
|
|
|
#ifdef MOZ_WIDGET_GONK
|
|
|
|
// if first run, merge the system apps.
|
|
|
|
if (runUpdate) {
|
2012-09-22 03:40:03 +00:00
|
|
|
let file;
|
|
|
|
try {
|
|
|
|
file = FileUtils.getFile("coreAppsDir", ["webapps", "webapps.json"], false);
|
|
|
|
} catch(e) { }
|
|
|
|
|
2012-09-19 16:28:55 +00:00
|
|
|
if (file && file.exists) {
|
|
|
|
// 2.a
|
|
|
|
this._loadJSONAsync(file, (function loadCoreRegistry(aData) {
|
|
|
|
if (!aData) {
|
|
|
|
this.registerAppsHandlers();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 2.b : core apps are not removable.
|
|
|
|
for (let id in this.webapps) {
|
|
|
|
if (id in aData || this.webapps[id].removable)
|
|
|
|
continue;
|
|
|
|
let localId = this.webapps[id].localId;
|
|
|
|
delete this.webapps[id];
|
|
|
|
// XXXX once bug 758269 is ready, revoke perms for this app
|
|
|
|
// removePermissions(localId);
|
|
|
|
}
|
|
|
|
|
|
|
|
let appDir = FileUtils.getDir("coreAppsDir", ["webapps"], false);
|
|
|
|
// 2.c
|
|
|
|
for (let id in aData) {
|
|
|
|
// Core apps have ids matching their domain name (eg: dialer.gaiamobile.org)
|
|
|
|
// Use that property to check if they are new or not.
|
|
|
|
if (!(id in this.webapps)) {
|
|
|
|
this.webapps[id] = aData[id];
|
|
|
|
this.webapps[id].basePath = appDir.path;
|
|
|
|
|
|
|
|
// Create a new localId.
|
|
|
|
this.webapps[id].localId = this._nextLocalId();
|
|
|
|
|
|
|
|
// Core apps are not removable.
|
|
|
|
if (this.webapps[id].removable === undefined) {
|
|
|
|
this.webapps[id].removable = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// XXXX once bug 758269 is ready, revoke perms for this app
|
|
|
|
// let localId = this.webapps[id].localId;
|
|
|
|
// installPermissions(localId);
|
|
|
|
}
|
|
|
|
this.registerAppsHandlers();
|
2012-08-29 21:20:03 +00:00
|
|
|
}).bind(this));
|
2012-09-22 03:40:03 +00:00
|
|
|
} else {
|
|
|
|
this.registerAppsHandlers();
|
2012-08-29 21:20:03 +00:00
|
|
|
}
|
2012-09-19 16:28:55 +00:00
|
|
|
} else {
|
|
|
|
this.registerAppsHandlers();
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
this.registerAppsHandlers();
|
|
|
|
#endif
|
2012-08-29 21:20:03 +00:00
|
|
|
}).bind(this));
|
2011-12-05 23:26:18 +00:00
|
|
|
},
|
|
|
|
|
2012-07-03 00:16:55 +00:00
|
|
|
#ifdef MOZ_SYS_MSG
|
2012-09-13 18:47:34 +00:00
|
|
|
|
|
|
|
// aEntryPoint is either the entry_point name or the null, in which case we
|
|
|
|
// use the root of the manifest.
|
|
|
|
_registerSystemMessagesForEntryPoint: function(aManifest, aApp, aEntryPoint) {
|
|
|
|
let root = aManifest;
|
|
|
|
if (aEntryPoint && aManifest.entry_points[aEntryPoint]) {
|
|
|
|
root = aManifest.entry_points[aEntryPoint];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!root.messages || !Array.isArray(root.messages) ||
|
|
|
|
root.messages.length == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let manifest = new DOMApplicationManifest(aManifest, aApp.origin);
|
|
|
|
let launchPath = Services.io.newURI(manifest.fullLaunchPath(aEntryPoint), null, null);
|
|
|
|
let manifestURL = Services.io.newURI(aApp.manifestURL, null, null);
|
|
|
|
root.messages.forEach(function registerPages(aMessage) {
|
|
|
|
let href = launchPath;
|
|
|
|
let messageName;
|
|
|
|
if (typeof(aMessage) === "object" && Object.keys(aMessage).length === 1) {
|
|
|
|
messageName = Object.keys(aMessage)[0];
|
|
|
|
href = Services.io.newURI(manifest.resolveFromOrigin(aMessage[messageName]), null, null);
|
|
|
|
} else {
|
|
|
|
messageName = aMessage;
|
|
|
|
}
|
|
|
|
msgmgr.registerPage(messageName, href, manifestURL);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2012-07-03 00:16:55 +00:00
|
|
|
_registerSystemMessages: function(aManifest, aApp) {
|
2012-09-13 18:47:34 +00:00
|
|
|
this._registerSystemMessagesForEntryPoint(aManifest, aApp, null);
|
|
|
|
|
|
|
|
if (!aManifest.entry_points) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let entryPoint in aManifest.entry_points) {
|
|
|
|
this._registerSystemMessagesForEntryPoint(aManifest, aApp, entryPoint);
|
2012-07-03 00:16:55 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-09-13 18:47:34 +00:00
|
|
|
// aEntryPoint is either the entry_point name or the null, in which case we
|
|
|
|
// use the root of the manifest.
|
|
|
|
_registerActivitiesForEntryPoint: function(aManifest, aApp, aEntryPoint) {
|
|
|
|
let root = aManifest;
|
|
|
|
if (aEntryPoint && aManifest.entry_points[aEntryPoint]) {
|
|
|
|
root = aManifest.entry_points[aEntryPoint];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!root.activities) {
|
2012-07-20 15:41:30 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let manifest = new DOMApplicationManifest(aManifest, aApp.origin);
|
2012-09-13 18:47:34 +00:00
|
|
|
for (let activity in root.activities) {
|
|
|
|
let description = root.activities[activity];
|
2012-08-15 02:40:12 +00:00
|
|
|
if (!description.href) {
|
|
|
|
description.href = manifest.launch_path;
|
|
|
|
}
|
|
|
|
description.href = manifest.resolveFromOrigin(description.href);
|
2012-07-20 15:41:30 +00:00
|
|
|
let json = {
|
|
|
|
"manifest": aApp.manifestURL,
|
|
|
|
"name": activity,
|
|
|
|
"title": manifest.name,
|
|
|
|
"icon": manifest.iconURLForSize(128),
|
|
|
|
"description": description
|
|
|
|
}
|
2012-09-19 16:28:55 +00:00
|
|
|
this.activitiesToRegister++;
|
2012-07-20 15:41:30 +00:00
|
|
|
cpmm.sendAsyncMessage("Activities:Register", json);
|
|
|
|
|
|
|
|
let launchPath =
|
2012-08-09 15:39:52 +00:00
|
|
|
Services.io.newURI(manifest.resolveFromOrigin(description.href), null, null);
|
2012-07-20 15:41:30 +00:00
|
|
|
let manifestURL = Services.io.newURI(aApp.manifestURL, null, null);
|
|
|
|
msgmgr.registerPage("activity", launchPath, manifestURL);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-09-13 18:47:34 +00:00
|
|
|
_registerActivities: function(aManifest, aApp) {
|
|
|
|
this._registerActivitiesForEntryPoint(aManifest, aApp, null);
|
|
|
|
|
|
|
|
if (!aManifest.entry_points) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let entryPoint in aManifest.entry_points) {
|
|
|
|
this._registerActivitiesForEntryPoint(aManifest, aApp, entryPoint);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_unregisterActivitiesForEntryPoint: function(aManifest, aApp, aEntryPoint) {
|
|
|
|
let root = aManifest;
|
|
|
|
if (aEntryPoint && aManifest.entry_points[aEntryPoint]) {
|
|
|
|
root = aManifest.entry_points[aEntryPoint];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!root.activities) {
|
2012-07-20 15:41:30 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-09-13 18:47:34 +00:00
|
|
|
for (let activity in root.activities) {
|
|
|
|
let description = root.activities[activity];
|
2012-07-20 15:41:30 +00:00
|
|
|
let json = {
|
|
|
|
"manifest": aApp.manifestURL,
|
|
|
|
"name": activity
|
|
|
|
}
|
|
|
|
cpmm.sendAsyncMessage("Activities:Unregister", json);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-09-13 18:47:34 +00:00
|
|
|
_unregisterActivities: function(aManifest, aApp) {
|
|
|
|
this._unregisterActivitiesForEntryPoint(aManifest, aApp, null);
|
|
|
|
|
|
|
|
if (!aManifest.entry_points) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let entryPoint in aManifest.entry_points) {
|
|
|
|
this._unregisterActivitiesForEntryPoint(aManifest, aApp, entryPoint);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-09-12 01:17:01 +00:00
|
|
|
_processManifestForIds: function(aIds) {
|
2012-09-19 16:28:55 +00:00
|
|
|
this.activitiesToRegister = 0;
|
|
|
|
this.activitiesRegistered = 0;
|
|
|
|
this.allActivitiesSent = false;
|
2012-09-12 01:17:01 +00:00
|
|
|
this._readManifests(aIds, (function registerManifests(aResults) {
|
|
|
|
aResults.forEach(function registerManifest(aResult) {
|
|
|
|
let app = this.webapps[aResult.id];
|
|
|
|
let manifest = aResult.manifest;
|
|
|
|
app.name = manifest.name;
|
|
|
|
this._registerSystemMessages(manifest, app);
|
|
|
|
this._registerActivities(manifest, app);
|
|
|
|
}, this);
|
2012-09-19 16:28:55 +00:00
|
|
|
this.allActivitiesSent = true;
|
2012-07-03 00:16:55 +00:00
|
|
|
}).bind(this));
|
|
|
|
},
|
|
|
|
#endif
|
|
|
|
|
2012-03-13 00:33:10 +00:00
|
|
|
observe: function(aSubject, aTopic, aData) {
|
|
|
|
if (aTopic == "xpcom-shutdown") {
|
|
|
|
this.messages.forEach((function(msgName) {
|
|
|
|
ppmm.removeMessageListener(msgName, this);
|
|
|
|
}).bind(this));
|
|
|
|
Services.obs.removeObserver(this, "xpcom-shutdown");
|
|
|
|
ppmm = null;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2011-12-05 23:26:18 +00:00
|
|
|
_loadJSONAsync: function(aFile, aCallback) {
|
2011-11-28 20:13:26 +00:00
|
|
|
try {
|
2011-12-05 23:26:18 +00:00
|
|
|
let channel = NetUtil.newChannel(aFile);
|
2011-11-28 20:13:26 +00:00
|
|
|
channel.contentType = "application/json";
|
|
|
|
NetUtil.asyncFetch(channel, function(aStream, aResult) {
|
|
|
|
if (!Components.isSuccessCode(aResult)) {
|
2012-07-11 15:38:33 +00:00
|
|
|
Cu.reportError("DOMApplicationRegistry: Could not read from json file "
|
|
|
|
+ aFile.path);
|
2011-12-05 23:26:18 +00:00
|
|
|
if (aCallback)
|
|
|
|
aCallback(null);
|
2011-11-28 20:13:26 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read json file into a string
|
|
|
|
let data = null;
|
|
|
|
try {
|
2012-07-31 10:06:56 +00:00
|
|
|
// Obtain a converter to read from a UTF-8 encoded input stream.
|
|
|
|
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
|
|
|
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
|
|
|
converter.charset = "UTF-8";
|
|
|
|
|
|
|
|
data = JSON.parse(converter.ConvertToUnicode(NetUtil.readInputStreamToString(aStream,
|
|
|
|
aStream.available()) || ""));
|
2011-11-28 20:13:26 +00:00
|
|
|
aStream.close();
|
2011-12-05 23:26:18 +00:00
|
|
|
if (aCallback)
|
|
|
|
aCallback(data);
|
2011-11-28 20:13:26 +00:00
|
|
|
} catch (ex) {
|
2012-09-22 03:40:03 +00:00
|
|
|
Cu.reportError("DOMApplicationRegistry: Could not parse JSON: " +
|
|
|
|
aFile.path + " " + ex);
|
2011-12-05 23:26:18 +00:00
|
|
|
if (aCallback)
|
|
|
|
aCallback(null);
|
2011-11-28 20:13:26 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
} catch (ex) {
|
2012-07-11 15:38:33 +00:00
|
|
|
Cu.reportError("DOMApplicationRegistry: Could not read from " +
|
|
|
|
aFile.path + " : " + ex);
|
2011-12-05 23:26:18 +00:00
|
|
|
if (aCallback)
|
|
|
|
aCallback(null);
|
2011-11-28 20:13:26 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-09-18 17:34:55 +00:00
|
|
|
addMessageListener: function(aMsgNames, aMm) {
|
|
|
|
aMsgNames.forEach(function (aMsgName) {
|
|
|
|
if (!(aMsgName in this.children)) {
|
|
|
|
this.children[aMsgName] = [];
|
|
|
|
}
|
2012-09-29 20:20:09 +00:00
|
|
|
this.children[aMsgName].push(aMm);
|
2012-09-18 17:34:55 +00:00
|
|
|
}, this);
|
|
|
|
},
|
|
|
|
|
|
|
|
removeMessageListener: function(aMsgNames, aMm) {
|
|
|
|
aMsgNames.forEach(function (aMsgName) {
|
|
|
|
if (!(aMsgName in this.children)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let index;
|
|
|
|
if ((index = this.children[aMsgName].indexOf(aMm)) != -1) {
|
|
|
|
this.children[aMsgName].splice(index, 1);
|
|
|
|
}
|
|
|
|
}, this);
|
|
|
|
},
|
|
|
|
|
2011-11-28 20:13:26 +00:00
|
|
|
receiveMessage: function(aMessage) {
|
2012-06-26 20:12:33 +00:00
|
|
|
// nsIPrefBranch throws if pref does not exist, faster to simply write
|
|
|
|
// the pref instead of first checking if it is false.
|
|
|
|
Services.prefs.setBoolPref("dom.mozApps.used", true);
|
|
|
|
|
2011-11-28 20:13:26 +00:00
|
|
|
let msg = aMessage.json;
|
2012-09-18 17:34:55 +00:00
|
|
|
let mm = aMessage.target;
|
|
|
|
msg.mm = mm;
|
2011-11-28 20:13:26 +00:00
|
|
|
|
|
|
|
switch (aMessage.name) {
|
|
|
|
case "Webapps:Install":
|
|
|
|
// always ask for UI to install
|
2012-09-18 17:34:55 +00:00
|
|
|
Services.obs.notifyObservers(mm, "webapps-ask-install", JSON.stringify(msg));
|
2011-11-28 20:13:26 +00:00
|
|
|
break;
|
2012-03-06 19:50:58 +00:00
|
|
|
case "Webapps:GetSelf":
|
2012-09-18 17:34:55 +00:00
|
|
|
this.getSelf(msg, mm);
|
2012-03-06 19:50:58 +00:00
|
|
|
break;
|
2011-11-28 20:13:26 +00:00
|
|
|
case "Webapps:Uninstall":
|
2012-09-28 16:24:45 +00:00
|
|
|
this.uninstall(msg, mm);
|
2012-09-28 22:16:29 +00:00
|
|
|
debug("Webapps:Uninstall");
|
2011-11-28 20:13:26 +00:00
|
|
|
break;
|
|
|
|
case "Webapps:Launch":
|
2012-09-27 01:01:20 +00:00
|
|
|
this.launchApp(msg, mm);
|
2011-11-28 20:13:26 +00:00
|
|
|
break;
|
2012-09-25 15:04:24 +00:00
|
|
|
case "Webapps:IsInstalled":
|
|
|
|
this.isInstalled(msg, mm);
|
|
|
|
break;
|
2012-03-06 19:50:58 +00:00
|
|
|
case "Webapps:GetInstalled":
|
2012-09-18 17:34:55 +00:00
|
|
|
this.getInstalled(msg, mm);
|
2011-12-15 17:20:57 +00:00
|
|
|
break;
|
2012-06-29 20:46:21 +00:00
|
|
|
case "Webapps:GetNotInstalled":
|
2012-09-18 17:34:55 +00:00
|
|
|
this.getNotInstalled(msg, mm);
|
2012-06-29 20:46:21 +00:00
|
|
|
break;
|
2012-03-06 19:50:58 +00:00
|
|
|
case "Webapps:GetAll":
|
|
|
|
if (msg.hasPrivileges)
|
2012-09-18 17:34:55 +00:00
|
|
|
this.getAll(msg, mm);
|
2012-03-06 19:50:58 +00:00
|
|
|
else
|
2012-09-18 17:34:55 +00:00
|
|
|
mm.sendAsyncMessage("Webapps:GetAll:Return:KO", msg);
|
2011-11-28 20:13:26 +00:00
|
|
|
break;
|
2012-07-11 15:38:33 +00:00
|
|
|
case "Webapps:InstallPackage":
|
2012-09-18 17:34:55 +00:00
|
|
|
this.installPackage(msg, mm);
|
2012-07-11 15:38:33 +00:00
|
|
|
break;
|
2012-07-11 15:38:36 +00:00
|
|
|
case "Webapps:GetBasePath":
|
2012-08-29 21:20:03 +00:00
|
|
|
return this.webapps[msg.id].basePath;
|
2012-07-11 15:38:36 +00:00
|
|
|
break;
|
2012-09-18 17:34:55 +00:00
|
|
|
case "Webapps:RegisterForMessages":
|
|
|
|
this.addMessageListener(msg, mm);
|
|
|
|
break;
|
|
|
|
case "Webapps:UnregisterForMessages":
|
|
|
|
this.removeMessageListener(msg, mm);
|
|
|
|
break;
|
2012-08-28 02:43:57 +00:00
|
|
|
case "Webapps:GetList":
|
2012-09-18 17:34:55 +00:00
|
|
|
this.addMessageListener(["Webapps:AddApp", "Webapps:RemoveApp"], mm);
|
2012-08-28 02:43:57 +00:00
|
|
|
return this.webapps;
|
2012-09-27 01:01:20 +00:00
|
|
|
case "Webapps:CancelDownload":
|
2012-09-29 20:20:09 +00:00
|
|
|
this.cancelDowload(msg.manifestURL);
|
2012-09-27 01:01:20 +00:00
|
|
|
break;
|
|
|
|
case "Webapps:CheckForUpdate":
|
|
|
|
this.checkForUpdate(msg, mm);
|
|
|
|
break;
|
2012-09-19 16:28:55 +00:00
|
|
|
case "Activities:Register:OK":
|
|
|
|
this.activitiesRegistered++;
|
|
|
|
if (this.allActivitiesSent &&
|
|
|
|
this.activitiesRegistered === this.activitiesToRegister) {
|
|
|
|
this.onInitDone();
|
|
|
|
}
|
|
|
|
break;
|
2011-11-28 20:13:26 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-09-18 17:34:55 +00:00
|
|
|
// Some messages can be listened by several content processes:
|
|
|
|
// Webapps:AddApp
|
|
|
|
// Webapps:RemoveApp
|
|
|
|
// Webapps:Install:Return:OK
|
|
|
|
// Webapps:Uninstall:Return:OK
|
|
|
|
// Webapps:OfflineCache
|
|
|
|
broadcastMessage: function broadcastMessage(aMsgName, aContent) {
|
|
|
|
if (!(aMsgName in this.children)) {
|
|
|
|
return;
|
|
|
|
}
|
2012-09-26 18:06:25 +00:00
|
|
|
let i;
|
|
|
|
for (i = this.children[aMsgName].length - 1; i >= 0; i -= 1) {
|
|
|
|
let msgMgr = this.children[aMsgName][i];
|
|
|
|
try {
|
|
|
|
msgMgr.sendAsyncMessage(aMsgName, aContent);
|
|
|
|
} catch (e) {
|
|
|
|
// Remove once 777508 lands.
|
|
|
|
let index;
|
|
|
|
if ((index = this.children[aMsgName].indexOf(msgMgr)) != -1) {
|
|
|
|
this.children[aMsgName].splice(index, 1);
|
|
|
|
dump("Remove dead MessageManager!\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2012-09-18 17:34:55 +00:00
|
|
|
},
|
|
|
|
|
2012-08-29 21:20:03 +00:00
|
|
|
_getAppDir: function(aId) {
|
|
|
|
return FileUtils.getDir(DIRECTORY_NAME, ["webapps", aId], true, true);
|
|
|
|
},
|
|
|
|
|
2011-11-28 20:13:26 +00:00
|
|
|
_writeFile: function ss_writeFile(aFile, aData, aCallbak) {
|
|
|
|
// Initialize the file output stream.
|
2011-12-06 04:22:01 +00:00
|
|
|
let ostream = FileUtils.openSafeFileOutputStream(aFile);
|
2011-11-28 20:13:26 +00:00
|
|
|
|
|
|
|
// Obtain a converter to convert our data to a UTF-8 encoded input stream.
|
2012-07-11 15:38:33 +00:00
|
|
|
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
|
|
|
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
2011-11-28 20:13:26 +00:00
|
|
|
converter.charset = "UTF-8";
|
|
|
|
|
|
|
|
// Asynchronously copy the data to the file.
|
|
|
|
let istream = converter.convertToInputStream(aData);
|
|
|
|
NetUtil.asyncCopy(istream, ostream, function(rc) {
|
|
|
|
if (aCallbak)
|
|
|
|
aCallbak();
|
|
|
|
});
|
|
|
|
},
|
2011-12-08 13:32:54 +00:00
|
|
|
|
2012-09-27 01:01:20 +00:00
|
|
|
launchApp: function launchApp(aData, aMm) {
|
|
|
|
let app = this.getAppByManifestURL(aData.manifestURL);
|
|
|
|
if (!app) {
|
|
|
|
aMm.sendAsyncMessage("Webapps:Launch:Return:KO", aData);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fire an error when trying to launch an app that is not
|
|
|
|
// yet fully installed.
|
|
|
|
if (app.installState == "pending") {
|
|
|
|
aMm.sendAsyncMessage("Webapps:Launch:Return:KO", aData);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Services.obs.notifyObservers(aMm, "webapps-launch", JSON.stringify(aData));
|
|
|
|
},
|
|
|
|
|
2012-09-29 20:20:09 +00:00
|
|
|
cancelDownload: function cancelDowload(aManifestURL) {
|
|
|
|
// We can't cancel appcache dowloads for now.
|
2012-09-27 19:34:41 +00:00
|
|
|
if (!this.downloads[aManifestURL]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// This is a HTTP channel.
|
|
|
|
let download = this.downloads[aManifestURL]
|
|
|
|
download.channel.cancel(Cr.NS_BINDING_ABORTED);
|
2012-09-29 20:20:09 +00:00
|
|
|
let app = this.webapps[dowload.appId];
|
2012-09-27 19:34:41 +00:00
|
|
|
|
|
|
|
app.progress = 0;
|
|
|
|
app.installState = app.previousState;
|
2012-09-29 20:20:09 +00:00
|
|
|
app.dowloading = false;
|
|
|
|
app.dowloadavailable = false;
|
2012-09-27 19:34:41 +00:00
|
|
|
app.downloadSize = 0;
|
|
|
|
this._saveApps((function() {
|
|
|
|
this.broadcastMessage("Webapps:PackageEvent",
|
|
|
|
{ type: "canceled",
|
|
|
|
manifestURL: aApp.manifestURL,
|
|
|
|
app: app,
|
|
|
|
error: "DOWNLOAD_CANCELED" });
|
|
|
|
}).bind(this));
|
2012-09-27 01:01:20 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
startOfflineCacheDownload: function startOfflineCacheDownload(aManifest, aApp, aProfileDir) {
|
|
|
|
// if the manifest has an appcache_path property, use it to populate the appcache
|
|
|
|
if (aManifest.appcache_path) {
|
|
|
|
let appcacheURI = Services.io.newURI(aManifest.fullAppcachePath(), null, null);
|
|
|
|
let updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"]
|
|
|
|
.getService(Ci.nsIOfflineCacheUpdateService);
|
|
|
|
let docURI = Services.io.newURI(aManifest.fullLaunchPath(), null, null);
|
|
|
|
let cacheUpdate = aProfileDir ? updateService.scheduleCustomProfileUpdate(appcacheURI, docURI, aProfileDir)
|
|
|
|
: updateService.scheduleUpdate(appcacheURI, docURI, null);
|
|
|
|
cacheUpdate.addObserver(new AppcacheObserver(aApp), false);
|
|
|
|
if (aOfflineCacheObserver) {
|
|
|
|
cacheUpdate.addObserver(aOfflineCacheObserver, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-09-28 22:16:29 +00:00
|
|
|
/**
|
|
|
|
* Install permissisions or remove deprecated permissions upon re-install
|
|
|
|
* @param object aAppObject
|
|
|
|
* The just installed AppUtils cloned appObject
|
|
|
|
* @param object aData
|
|
|
|
* The just-installed app configuration
|
|
|
|
* @param boolean aIsReinstall
|
|
|
|
* Indicates the app was just re-installed
|
|
|
|
* @returns void
|
|
|
|
**/
|
|
|
|
installPermissions:
|
|
|
|
function installPermissions(aAppObject, aData, aIsReinstall)
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
let newManifest = new DOMApplicationManifest(aData.app.manifest,
|
|
|
|
aData.app.origin);
|
|
|
|
if (!newManifest.permissions && !aIsReinstall) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aIsReinstall) {
|
|
|
|
// Compare the original permissions against the new permissions
|
|
|
|
// Remove any deprecated Permissions
|
|
|
|
|
|
|
|
if (newManifest.permissions) {
|
|
|
|
// Expand perms
|
|
|
|
let newPerms = [];
|
|
|
|
for (let perm in newManifest.permissions) {
|
|
|
|
let _perms = expandPermissions(perm,
|
|
|
|
newManifest.permissions[perm].access);
|
|
|
|
newPerms = newPerms.concat(_perms);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let idx in AllPossiblePermissions) {
|
|
|
|
let index = newPerms.indexOf(AllPossiblePermissions[idx]);
|
|
|
|
if (index == -1) {
|
|
|
|
// See if the permission was installed previously
|
|
|
|
let _perm = PermSettings.get(AllPossiblePermissions[idx],
|
|
|
|
aData.app.manifestURL,
|
|
|
|
aData.app.origin,
|
|
|
|
false);
|
|
|
|
if (_perm == "unknown" || _perm == "deny") {
|
|
|
|
// All 'deny' permissions should be preserved
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Remove the deprecated permission
|
|
|
|
// TODO: use PermSettings.remove, see bug 793204
|
|
|
|
PermSettings.set(AllPossiblePermissions[idx],
|
|
|
|
"unknown",
|
|
|
|
aData.app.manifestURL,
|
|
|
|
aData.app.origin,
|
|
|
|
false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let installPermType;
|
|
|
|
// Check to see if the 'webapp' is app/priv/certified
|
|
|
|
switch (getAppManifestStatus(newManifest)) {
|
|
|
|
case Ci.nsIPrincipal.APP_STATUS_CERTIFIED:
|
|
|
|
installPermType = "certified";
|
|
|
|
break;
|
|
|
|
case Ci.nsIPrincipal.APP_STATUS_PRIVILEGED:
|
|
|
|
installPermType = "privileged";
|
|
|
|
break;
|
|
|
|
case Ci.nsIPrincipal.APP_STATUS_INSTALLED:
|
|
|
|
installPermType = "app";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// Cannot determine app type, abort install by throwing an error
|
|
|
|
throw new Error("Webapps.jsm: Cannot determine app type, install cancelled");
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let permName in newManifest.permissions) {
|
|
|
|
if (!PermissionsTable[permName]) {
|
|
|
|
throw new Error("Webapps.jsm: '" + permName + "'" +
|
|
|
|
" is not a valid Webapps permission type. Aborting Webapp installation");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let perms = expandPermissions(permName,
|
|
|
|
newManifest.permissions[permName].access);
|
|
|
|
for (let idx in perms) {
|
|
|
|
let perm = PermissionsTable[permName][installPermType];
|
|
|
|
let permValue = PERM_TO_STRING[perm];
|
|
|
|
PermSettings.set(perms[idx],
|
|
|
|
permValue,
|
|
|
|
aData.app.manifestURL,
|
|
|
|
aData.app.origin,
|
|
|
|
false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (ex) {
|
|
|
|
debug("Caught webapps install permissions error");
|
|
|
|
Cu.reportError(ex);
|
|
|
|
this.uninstall(aData);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-09-27 01:01:20 +00:00
|
|
|
checkForUpdate: function(aData, aMm) {
|
|
|
|
let app = this.getAppByManifestURL(aData.manifestURL);
|
|
|
|
if (!app) {
|
|
|
|
aData.error = "NO_SUCH_APP";
|
|
|
|
aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
function sendError(aError) {
|
|
|
|
aData.error = aError;
|
|
|
|
aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
|
|
|
|
}
|
|
|
|
|
|
|
|
function updatePackagedApp(aManifest) {
|
|
|
|
debug("updatePackagedApp");
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateHostedApp(aManifest) {
|
|
|
|
debug("updateHostedApp");
|
|
|
|
let id = this._appId(app.origin);
|
|
|
|
|
|
|
|
#ifdef MOZ_SYS_MSG
|
|
|
|
// Update the Web Activities
|
|
|
|
this._readManifests([{ id: id }], (function unregisterManifest(aResult) {
|
|
|
|
this._unregisterActivities(aResult[0].manifest, app);
|
|
|
|
this._registerSystemMessages(aManifest, app);
|
|
|
|
this._registerActivities(aManifest, app);
|
|
|
|
}).bind(this));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Store the new manifest.
|
|
|
|
let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true);
|
|
|
|
let manFile = dir.clone();
|
|
|
|
manFile.append("manifest.webapp");
|
|
|
|
this._writeFile(manFile, JSON.stringify(aManifest), function() { });
|
|
|
|
|
|
|
|
let manifest = new DOMApplicationManifest(aManifest, app.origin);
|
|
|
|
|
|
|
|
if (manifest.appcache_path) {
|
|
|
|
app.installState = "updating";
|
|
|
|
app.downloadAvailable = true;
|
|
|
|
app.downloading = true;
|
|
|
|
app.downloadsize = 0;
|
|
|
|
app.readyToApplyDownload = false;
|
|
|
|
} else {
|
|
|
|
app.installState = "installed";
|
|
|
|
app.downloadAvailable = false;
|
|
|
|
app.downloading = false;
|
|
|
|
app.readyToApplyDownload = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
app.name = aManifest.name;
|
|
|
|
|
|
|
|
// Update the registry.
|
|
|
|
this.webapps[id] = app;
|
|
|
|
|
|
|
|
this._saveApps((function() {
|
|
|
|
// XXX Should we fire notifications ?
|
|
|
|
}).bind(this));
|
|
|
|
|
|
|
|
// Preload the appcache if needed.
|
|
|
|
this.startOfflineCacheDownload(manifest, app);
|
|
|
|
}
|
|
|
|
|
|
|
|
// First, we download the manifest.
|
|
|
|
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
|
|
|
.createInstance(Ci.nsIXMLHttpRequest);
|
|
|
|
xhr.open("GET", aData.manifestURL, true);
|
|
|
|
if (aData.etag) {
|
|
|
|
xhr.setRequestHeader("If-None-Match", aData.etag);
|
|
|
|
}
|
|
|
|
|
|
|
|
xhr.addEventListener("load", (function() {
|
|
|
|
if (xhr.status == 200) {
|
|
|
|
let manifest;
|
|
|
|
try {
|
|
|
|
JSON.parse(xhr.responseText, installOrigin);
|
|
|
|
} catch(e) {
|
|
|
|
sendError("MANIFEST_PARSE_ERROR");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!AppsUtils.checkManifest(manifest, installOrigin)) {
|
|
|
|
sendError("INVALID_MANIFEST");
|
|
|
|
} else {
|
|
|
|
app.etag = xhr.getResponseHeader("Etag");
|
|
|
|
app.lastCheckedUpdate = Date.now();
|
2012-09-29 20:20:09 +00:00
|
|
|
if (package_path in manifest) {
|
2012-09-27 01:01:20 +00:00
|
|
|
updatePackagedApp(manifest);
|
|
|
|
} else {
|
|
|
|
updateHostedApp(manifest);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this._saveApps();
|
|
|
|
} else if (xhr.status == 304) {
|
|
|
|
// The manifest has not changed. We just update lastCheckedUpdate.
|
|
|
|
app.lastCheckedUpdate = Date.now();
|
|
|
|
aData.event = "downloadapplied";
|
|
|
|
aData.app = {
|
|
|
|
lastCheckedUpdate: app.lastCheckedUpdate
|
|
|
|
}
|
|
|
|
aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:OK", aData);
|
|
|
|
this._saveApps();
|
|
|
|
} else {
|
|
|
|
sendError("MANIFEST_URL_ERROR");
|
|
|
|
}
|
|
|
|
}).bind(this), false);
|
|
|
|
|
|
|
|
xhr.addEventListener("error", (function() {
|
|
|
|
sendError(request, "NETWORK_ERROR");
|
|
|
|
}).bind(this), false);
|
|
|
|
|
|
|
|
xhr.send(null);
|
|
|
|
},
|
|
|
|
|
2011-11-28 20:13:26 +00:00
|
|
|
denyInstall: function(aData) {
|
2012-07-11 15:38:33 +00:00
|
|
|
let packageId = aData.app.packageId;
|
|
|
|
if (packageId) {
|
|
|
|
let dir = FileUtils.getDir("TmpD", ["webapps", packageId],
|
|
|
|
true, true);
|
|
|
|
try {
|
|
|
|
dir.remove(true);
|
|
|
|
} catch(e) {
|
|
|
|
}
|
|
|
|
}
|
2012-09-18 17:34:55 +00:00
|
|
|
aData.mm.sendAsyncMessage("Webapps:Install:Return:KO", aData);
|
2011-11-28 20:13:26 +00:00
|
|
|
},
|
2011-12-08 13:32:54 +00:00
|
|
|
|
2012-06-11 18:41:46 +00:00
|
|
|
confirmInstall: function(aData, aFromSync, aProfileDir, aOfflineCacheObserver) {
|
2012-09-28 22:16:29 +00:00
|
|
|
let isReinstall = false;
|
2011-11-28 20:13:26 +00:00
|
|
|
let app = aData.app;
|
2012-08-29 21:20:03 +00:00
|
|
|
app.removable = true;
|
2012-09-27 19:34:41 +00:00
|
|
|
|
|
|
|
let origin = Services.io.newURI(app.origin, null, null);
|
|
|
|
let manifestURL = origin.resolve(app.manifestURL);
|
|
|
|
|
2012-09-27 21:37:39 +00:00
|
|
|
let id = app.syncId || this._appId(app.origin);
|
2012-09-27 19:34:41 +00:00
|
|
|
let localId = this.getAppLocalIdByManifestURL(manifestURL);
|
2011-11-28 20:13:26 +00:00
|
|
|
|
2012-07-11 15:38:33 +00:00
|
|
|
// Installing an application again is considered as an update.
|
2011-11-28 20:13:26 +00:00
|
|
|
if (id) {
|
2012-09-28 22:16:29 +00:00
|
|
|
isReinstall = true;
|
2012-08-29 21:20:03 +00:00
|
|
|
let dir = this._getAppDir(id);
|
2011-11-28 20:13:26 +00:00
|
|
|
try {
|
|
|
|
dir.remove(true);
|
|
|
|
} catch(e) {
|
|
|
|
}
|
2012-04-28 07:10:08 +00:00
|
|
|
} else {
|
|
|
|
id = this.makeAppId();
|
2012-09-28 22:16:29 +00:00
|
|
|
app.id = id;
|
2012-07-09 10:25:41 +00:00
|
|
|
localId = this._nextLocalId();
|
2011-11-28 20:13:26 +00:00
|
|
|
}
|
|
|
|
|
2012-09-27 19:34:41 +00:00
|
|
|
let manifestName = "manifest.webapp";
|
|
|
|
if (aData.isPackage) {
|
2012-07-11 15:38:33 +00:00
|
|
|
// Override the origin with the correct id.
|
|
|
|
app.origin = "app://" + id;
|
2012-09-27 19:34:41 +00:00
|
|
|
|
|
|
|
// For packaged apps, keep the update manifest distinct from the app
|
|
|
|
// manifest.
|
|
|
|
manifestName = "update.webapp";
|
2012-07-11 15:38:33 +00:00
|
|
|
}
|
|
|
|
|
2012-08-28 02:43:57 +00:00
|
|
|
let appObject = AppsUtils.cloneAppObject(app);
|
2012-08-28 02:43:57 +00:00
|
|
|
appObject.appStatus = app.appStatus || Ci.nsIPrincipal.APP_STATUS_INSTALLED;
|
2012-06-18 19:49:35 +00:00
|
|
|
appObject.installTime = app.installTime = Date.now();
|
2012-09-27 01:01:20 +00:00
|
|
|
appObject.lastUpdateCheck = app.lastUpdateCheck = Date.now();
|
2012-04-28 07:10:08 +00:00
|
|
|
let appNote = JSON.stringify(appObject);
|
|
|
|
appNote.id = id;
|
2011-12-06 04:22:01 +00:00
|
|
|
|
2012-07-09 10:25:41 +00:00
|
|
|
appObject.localId = localId;
|
2012-09-06 00:37:41 +00:00
|
|
|
appObject.basePath = FileUtils.getDir(DIRECTORY_NAME, ["webapps"], true, true).path;
|
2012-04-28 07:10:08 +00:00
|
|
|
let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true);
|
2011-11-28 20:13:26 +00:00
|
|
|
let manFile = dir.clone();
|
2012-09-27 19:34:41 +00:00
|
|
|
manFile.append(manifestName);
|
|
|
|
let jsonManifest = aData.isPackage ? app.updateManifest : app.manifest;
|
|
|
|
this._writeFile(manFile, JSON.stringify(jsonManifest), function() { });
|
2011-11-28 20:13:26 +00:00
|
|
|
|
2012-09-27 19:34:41 +00:00
|
|
|
let manifest = new DOMApplicationManifest(jsonManifest, app.origin);
|
2012-09-27 01:01:20 +00:00
|
|
|
|
|
|
|
if (manifest.appcache_path) {
|
|
|
|
appObject.installState = "pending";
|
|
|
|
appObject.downloadAvailable = true;
|
|
|
|
appObject.downloading = true;
|
2012-09-27 19:34:41 +00:00
|
|
|
appObject.downloadSize = 0;
|
|
|
|
appObject.readyToApplyDownload = false;
|
|
|
|
} else if (manifest.package_path) {
|
|
|
|
appObject.installState = "pending";
|
|
|
|
appObject.downloadAvailable = true;
|
|
|
|
appObject.downloading = true;
|
|
|
|
appObject.downloadSize = manifest.size;
|
2012-09-27 01:01:20 +00:00
|
|
|
appObject.readyToApplyDownload = false;
|
|
|
|
} else {
|
|
|
|
appObject.installState = "installed";
|
|
|
|
appObject.downloadAvailable = false;
|
|
|
|
appObject.downloading = false;
|
|
|
|
appObject.readyToApplyDownload = false;
|
|
|
|
}
|
|
|
|
|
2012-09-27 19:34:41 +00:00
|
|
|
appObject.name = manifest.name;
|
2012-09-26 23:49:43 +00:00
|
|
|
|
2012-09-27 01:01:20 +00:00
|
|
|
this.webapps[id] = appObject;
|
2012-09-28 22:16:29 +00:00
|
|
|
this.installPermissions(appObject, aData, isReinstall);
|
2012-09-27 19:34:41 +00:00
|
|
|
["installState", "downloadAvailable",
|
|
|
|
"downloading", "downloadSize", "readyToApplyDownload"].forEach(function(aProp) {
|
|
|
|
aData.app[aProp] = appObject[aProp];
|
|
|
|
});
|
2012-06-11 18:41:46 +00:00
|
|
|
|
2011-12-08 13:32:54 +00:00
|
|
|
if (!aFromSync)
|
|
|
|
this._saveApps((function() {
|
2012-09-18 17:34:55 +00:00
|
|
|
this.broadcastMessage("Webapps:Install:Return:OK", aData);
|
2012-04-28 07:10:08 +00:00
|
|
|
Services.obs.notifyObservers(this, "webapps-sync-install", appNote);
|
2012-09-18 17:34:55 +00:00
|
|
|
this.broadcastMessage("Webapps:AddApp", { id: id, app: appObject });
|
2011-12-08 13:32:54 +00:00
|
|
|
}).bind(this));
|
2012-06-11 18:41:46 +00:00
|
|
|
|
2012-07-03 00:16:55 +00:00
|
|
|
#ifdef MOZ_SYS_MSG
|
2012-09-27 19:34:41 +00:00
|
|
|
if (!aData.isPackage) {
|
|
|
|
this._registerSystemMessages(app.manifest, app);
|
|
|
|
this._registerActivities(app.manifest, app);
|
|
|
|
}
|
2012-07-03 00:16:55 +00:00
|
|
|
#endif
|
|
|
|
|
2012-09-27 01:01:20 +00:00
|
|
|
this.startOfflineCacheDownload(manifest, appObject, aProfileDir);
|
2012-09-27 19:34:41 +00:00
|
|
|
if (manifest.package_path) {
|
2012-09-29 20:20:09 +00:00
|
|
|
this.downloadPackage(manifest, appObject);
|
2012-09-27 19:34:41 +00:00
|
|
|
}
|
2011-11-28 20:13:26 +00:00
|
|
|
},
|
2011-12-08 13:32:54 +00:00
|
|
|
|
2012-07-09 10:25:41 +00:00
|
|
|
_nextLocalId: function() {
|
2012-09-06 01:07:21 +00:00
|
|
|
let id = Services.prefs.getIntPref("dom.mozApps.maxLocalId") + 1;
|
|
|
|
Services.prefs.setIntPref("dom.mozApps.maxLocalId", id);
|
|
|
|
return id;
|
2012-07-09 10:25:41 +00:00
|
|
|
},
|
|
|
|
|
2011-11-28 20:13:26 +00:00
|
|
|
_appId: function(aURI) {
|
|
|
|
for (let id in this.webapps) {
|
|
|
|
if (this.webapps[id].origin == aURI)
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
|
2012-09-27 19:34:41 +00:00
|
|
|
_appIdForManifestURL: function(aURI) {
|
|
|
|
for (let id in this.webapps) {
|
|
|
|
if (this.webapps[id].manifestURL == aURI)
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
|
2012-04-28 07:10:08 +00:00
|
|
|
makeAppId: function() {
|
|
|
|
let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
|
|
|
|
return uuidGenerator.generateUUID().toString();
|
|
|
|
},
|
|
|
|
|
2011-12-08 13:32:54 +00:00
|
|
|
_saveApps: function(aCallback) {
|
2012-09-27 01:01:20 +00:00
|
|
|
this._writeFile(this.appsFile, JSON.stringify(this.webapps, null, 2), function() {
|
2011-12-08 13:32:54 +00:00
|
|
|
if (aCallback)
|
|
|
|
aCallback();
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2011-12-05 23:26:18 +00:00
|
|
|
/**
|
|
|
|
* Asynchronously reads a list of manifests
|
|
|
|
*/
|
|
|
|
_readManifests: function(aData, aFinalCallback, aIndex) {
|
2011-12-15 17:20:57 +00:00
|
|
|
if (!aData.length) {
|
|
|
|
aFinalCallback(aData);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-12-05 23:26:18 +00:00
|
|
|
let index = aIndex || 0;
|
|
|
|
let id = aData[index].id;
|
2012-05-22 21:38:34 +00:00
|
|
|
|
|
|
|
// the manifest file used to be named manifest.json, so fallback on this.
|
2012-08-29 21:20:03 +00:00
|
|
|
let baseDir = (this.webapps[id].removable ? DIRECTORY_NAME : "coreAppsDir");
|
|
|
|
let file = FileUtils.getFile(baseDir, ["webapps", id, "manifest.webapp"], true);
|
2012-09-27 19:34:41 +00:00
|
|
|
if (!file.exists()) {
|
|
|
|
file = FileUtils.getFile(baseDir, ["webapps", id, "update.webapp"], true);
|
|
|
|
}
|
2012-05-22 21:38:34 +00:00
|
|
|
if (!file.exists()) {
|
2012-08-29 21:20:03 +00:00
|
|
|
file = FileUtils.getFile(baseDir, ["webapps", id, "manifest.json"], true);
|
2012-05-22 21:38:34 +00:00
|
|
|
}
|
|
|
|
|
2011-12-05 23:26:18 +00:00
|
|
|
this._loadJSONAsync(file, (function(aJSON) {
|
|
|
|
aData[index].manifest = aJSON;
|
|
|
|
if (index == aData.length - 1)
|
|
|
|
aFinalCallback(aData);
|
|
|
|
else
|
|
|
|
this._readManifests(aData, aFinalCallback, index + 1);
|
2012-04-28 07:10:08 +00:00
|
|
|
}).bind(this));
|
2011-11-28 20:13:26 +00:00
|
|
|
},
|
2011-12-08 13:32:54 +00:00
|
|
|
|
2012-09-29 20:20:09 +00:00
|
|
|
downloadPackage: function(aManifest, aApp) {
|
2012-07-11 15:38:33 +00:00
|
|
|
// Here are the steps when installing a package:
|
|
|
|
// - create a temp directory where to store the app.
|
|
|
|
// - download the zip in this directory.
|
|
|
|
// - extract the manifest from the zip and check it.
|
|
|
|
// - ask confirmation to the user.
|
|
|
|
// - add the new app to the registry.
|
|
|
|
// If we fail at any step, we backout the previous ones and return an error.
|
|
|
|
|
2012-09-27 19:34:41 +00:00
|
|
|
debug(JSON.stringify(aApp));
|
2012-07-11 15:38:33 +00:00
|
|
|
|
2012-09-27 19:34:41 +00:00
|
|
|
let id = this._appIdForManifestURL(aApp.manifestURL);
|
|
|
|
let app = this.webapps[id];
|
2012-07-11 15:38:33 +00:00
|
|
|
|
|
|
|
// Removes the directory we created, and sends an error to the DOM side.
|
|
|
|
function cleanup(aError) {
|
2012-09-27 19:34:41 +00:00
|
|
|
debug("Cleanup: " + aError);
|
|
|
|
let dir = FileUtils.getDir("TmpD", ["webapps", id], true, true);
|
2012-07-11 15:38:33 +00:00
|
|
|
try {
|
|
|
|
dir.remove(true);
|
|
|
|
} catch (e) { }
|
2012-09-27 19:34:41 +00:00
|
|
|
this.broadcastMessage("Webapps:PackageEvent",
|
|
|
|
{ type: "error",
|
|
|
|
manifestURL: aApp.manifestURL,
|
|
|
|
error: aError});
|
2012-07-11 15:38:33 +00:00
|
|
|
}
|
|
|
|
|
2012-08-28 02:43:57 +00:00
|
|
|
function getInferedStatus() {
|
|
|
|
// XXX Update once we have digital signatures (bug 772365)
|
|
|
|
return Ci.nsIPrincipal.APP_STATUS_INSTALLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getAppStatus(aManifest) {
|
|
|
|
let manifestStatus = getAppManifestStatus(aManifest);
|
|
|
|
let inferedStatus = getInferedStatus();
|
|
|
|
|
|
|
|
return (Services.prefs.getBoolPref("dom.mozApps.dev_mode") ? manifestStatus
|
|
|
|
: inferedStatus);
|
|
|
|
}
|
|
|
|
// Returns true if the privilege level from the manifest
|
|
|
|
// is lower or equal to the one we infered for the app.
|
|
|
|
function checkAppStatus(aManifest) {
|
|
|
|
if (Services.prefs.getBoolPref("dom.mozApps.dev_mode")) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (getAppManifestStatus(aManifest) <= getInferedStatus());
|
|
|
|
}
|
|
|
|
|
2012-09-27 19:34:41 +00:00
|
|
|
debug("About to download " + aManifest.fullPackagePath());
|
|
|
|
|
|
|
|
let requestChannel = NetUtil.newChannel(aManifest.fullPackagePath())
|
|
|
|
.QueryInterface(Ci.nsIHttpChannel);
|
|
|
|
this.downloads[aApp.manifestURL] =
|
|
|
|
{ channel:requestChannel,
|
|
|
|
appId: id,
|
2012-09-29 20:20:09 +00:00
|
|
|
previousState: "pending"
|
2012-09-27 19:34:41 +00:00
|
|
|
};
|
|
|
|
requestChannel.notificationCallbacks = {
|
|
|
|
QueryInterface: function notifQI(aIID) {
|
|
|
|
if (aIID.equals(Ci.nsISupports) ||
|
|
|
|
aIID.equals(Ci.nsIProgressEventSink))
|
|
|
|
return this;
|
|
|
|
|
|
|
|
throw Cr.NS_ERROR_NO_INTERFACE;
|
|
|
|
},
|
|
|
|
getInterface: function notifGI(aIID) {
|
|
|
|
return this.QueryInterface(aIID);
|
|
|
|
},
|
|
|
|
onProgress: function notifProgress(aRequest, aContext,
|
|
|
|
aProgress, aProgressMax) {
|
|
|
|
debug("onProgress: " + aProgress + "/" + aProgressMax);
|
|
|
|
app.progress = aProgress;
|
|
|
|
DOMApplicationRegistry.broadcastMessage("Webapps:PackageEvent",
|
|
|
|
{ type: "progress",
|
|
|
|
manifestURL: aApp.manifestURL,
|
|
|
|
progress: aProgress });
|
|
|
|
},
|
|
|
|
onStatus: function notifStatus(aRequest, aContext, aStatus, aStatusArg) { }
|
|
|
|
}
|
|
|
|
|
|
|
|
NetUtil.asyncFetch(requestChannel,
|
|
|
|
function(aInput, aResult, aRequest) {
|
2012-07-11 15:38:33 +00:00
|
|
|
if (!Components.isSuccessCode(aResult)) {
|
|
|
|
// We failed to fetch the zip.
|
|
|
|
cleanup("NETWORK_ERROR");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Copy the zip on disk.
|
|
|
|
let zipFile = FileUtils.getFile("TmpD",
|
|
|
|
["webapps", id, "application.zip"], true);
|
|
|
|
let ostream = FileUtils.openSafeFileOutputStream(zipFile);
|
|
|
|
NetUtil.asyncCopy(aInput, ostream, function (aResult) {
|
|
|
|
if (!Components.isSuccessCode(aResult)) {
|
|
|
|
// We failed to save the zip.
|
|
|
|
cleanup("DOWNLOAD_ERROR");
|
|
|
|
return;
|
|
|
|
}
|
2012-09-27 19:34:41 +00:00
|
|
|
|
2012-07-11 15:38:33 +00:00
|
|
|
let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"]
|
|
|
|
.createInstance(Ci.nsIZipReader);
|
|
|
|
try {
|
|
|
|
zipReader.open(zipFile);
|
|
|
|
if (!zipReader.hasEntry("manifest.webapp")) {
|
|
|
|
throw "No manifest.webapp found.";
|
|
|
|
}
|
|
|
|
|
|
|
|
let istream = zipReader.getInputStream("manifest.webapp");
|
2012-09-27 19:34:41 +00:00
|
|
|
|
|
|
|
// Obtain a converter to read from a UTF-8 encoded input stream.
|
|
|
|
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
|
|
|
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
|
|
|
converter.charset = "UTF-8";
|
|
|
|
|
|
|
|
let manifest = JSON.parse(converter.ConvertToUnicode(NetUtil.readInputStreamToString(istream,
|
|
|
|
istream.available()) || ""));
|
|
|
|
|
|
|
|
if (!AppsUtils.checkManifest(manifest, aApp.installOrigin)) {
|
2012-08-28 02:43:57 +00:00
|
|
|
throw "INVALID_MANIFEST";
|
|
|
|
}
|
|
|
|
|
2012-09-27 19:34:41 +00:00
|
|
|
if (!checkAppStatus(manifest)) {
|
2012-08-28 02:43:57 +00:00
|
|
|
throw "INVALID_SECURITY_LEVEL";
|
2012-07-11 15:38:33 +00:00
|
|
|
}
|
|
|
|
|
2012-09-29 20:20:09 +00:00
|
|
|
// Success! Move the zip out of TmpD.
|
|
|
|
let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true);
|
|
|
|
zipFile.moveTo(dir, "application.zip");
|
|
|
|
let tmpDir = FileUtils.getDir("TmpD", ["webapps", id], true, true);
|
|
|
|
try {
|
|
|
|
tmpDir.remove(true);
|
|
|
|
} catch(e) { }
|
|
|
|
|
|
|
|
// Save the manifest
|
|
|
|
let manFile = dir.clone();
|
|
|
|
manFile.append("manifest.webapp");
|
|
|
|
DOMApplicationRegistry._writeFile(manFile,
|
|
|
|
JSON.stringify(manifest),
|
|
|
|
function() { });
|
|
|
|
// Set state and fire events.
|
|
|
|
app.installState = "installed";
|
|
|
|
app.dowloading = false;
|
|
|
|
app.dowloadavailable = false;
|
|
|
|
DOMApplicationRegistry._saveApps(function() {
|
|
|
|
debug("About to fire Webapps:PackageEvent");
|
|
|
|
DOMApplicationRegistry.broadcastMessage("Webapps:PackageEvent",
|
|
|
|
{ type: "installed",
|
|
|
|
manifestURL: aApp.manifestURL,
|
|
|
|
app: app,
|
|
|
|
manifest: manifest });
|
|
|
|
delete DOMApplicationRegistry.downloads[aApp.manifestURL]
|
|
|
|
});
|
2012-07-11 15:38:33 +00:00
|
|
|
} catch (e) {
|
|
|
|
// XXX we may need new error messages.
|
2012-08-28 02:43:57 +00:00
|
|
|
cleanup(e);
|
2012-07-11 15:38:33 +00:00
|
|
|
} finally {
|
|
|
|
zipReader.close();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2012-09-28 16:24:45 +00:00
|
|
|
uninstall: function(aData, aMm) {
|
2011-11-28 20:13:26 +00:00
|
|
|
for (let id in this.webapps) {
|
|
|
|
let app = this.webapps[id];
|
2012-07-20 15:41:30 +00:00
|
|
|
if (app.origin != aData.origin) {
|
|
|
|
continue;
|
|
|
|
}
|
2012-04-28 07:10:08 +00:00
|
|
|
|
2012-08-29 21:20:03 +00:00
|
|
|
if (!this.webapps[id].removable)
|
|
|
|
return;
|
|
|
|
|
2012-09-28 16:24:45 +00:00
|
|
|
// Clear private data first.
|
|
|
|
this._clearPrivateData(app.localId, false);
|
|
|
|
|
|
|
|
// Then notify observers.
|
|
|
|
Services.obs.notifyObservers(aMm, "webapps-uninstall", JSON.stringify(aData));
|
|
|
|
|
2012-08-28 02:43:57 +00:00
|
|
|
let appNote = JSON.stringify(AppsUtils.cloneAppObject(app));
|
2012-07-20 15:41:30 +00:00
|
|
|
appNote.id = id;
|
2012-04-28 07:10:08 +00:00
|
|
|
|
2012-07-20 15:41:30 +00:00
|
|
|
#ifdef MOZ_SYS_MSG
|
2012-09-27 01:01:20 +00:00
|
|
|
this._readManifests([{ id: id }], (function unregisterManifest(aResult) {
|
2012-07-20 15:41:30 +00:00
|
|
|
this._unregisterActivities(aResult[0].manifest, app);
|
|
|
|
}).bind(this));
|
2012-09-27 01:01:20 +00:00
|
|
|
#endif
|
2012-07-20 15:41:30 +00:00
|
|
|
|
2012-08-29 21:20:03 +00:00
|
|
|
let dir = this._getAppDir(id);
|
2012-07-20 15:41:30 +00:00
|
|
|
try {
|
|
|
|
dir.remove(true);
|
|
|
|
} catch (e) {}
|
|
|
|
|
|
|
|
delete this.webapps[id];
|
|
|
|
|
|
|
|
this._saveApps((function() {
|
2012-09-18 17:34:55 +00:00
|
|
|
this.broadcastMessage("Webapps:Uninstall:Return:OK", aData);
|
2012-07-20 15:41:30 +00:00
|
|
|
Services.obs.notifyObservers(this, "webapps-sync-uninstall", appNote);
|
2012-09-18 17:34:55 +00:00
|
|
|
this.broadcastMessage("Webapps:RemoveApp", { id: id });
|
2012-07-20 15:41:30 +00:00
|
|
|
}).bind(this));
|
|
|
|
|
2012-09-28 16:24:45 +00:00
|
|
|
return;
|
2012-07-20 15:41:30 +00:00
|
|
|
}
|
2012-09-28 16:24:45 +00:00
|
|
|
|
|
|
|
aMm.sendAsyncMessage("Webapps:Uninstall:Return:KO", aData);
|
2011-11-28 20:13:26 +00:00
|
|
|
},
|
2011-12-08 13:32:54 +00:00
|
|
|
|
2012-09-18 17:34:55 +00:00
|
|
|
getSelf: function(aData, aMm) {
|
2011-11-28 20:13:26 +00:00
|
|
|
aData.apps = [];
|
2012-09-25 15:04:24 +00:00
|
|
|
|
|
|
|
if (aData.appId == Ci.nsIScriptSecurityManager.NO_APP_ID ||
|
|
|
|
aData.appId == Ci.nsIScriptSecurityManager.UNKNOWN_APP_ID) {
|
|
|
|
aMm.sendAsyncMessage("Webapps:GetSelf:Return:OK", aData);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-12-05 23:26:18 +00:00
|
|
|
let tmp = [];
|
2012-03-06 19:50:58 +00:00
|
|
|
|
2012-09-25 15:04:24 +00:00
|
|
|
for (let id in this.webapps) {
|
|
|
|
if (this.webapps[id].origin == aData.origin &&
|
|
|
|
this.webapps[id].localId == aData.appId &&
|
|
|
|
this._isLaunchable(this.webapps[id].origin)) {
|
|
|
|
let app = AppsUtils.cloneAppObject(this.webapps[id]);
|
|
|
|
aData.apps.push(app);
|
|
|
|
tmp.push({ id: id });
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!aData.apps.length) {
|
|
|
|
aMm.sendAsyncMessage("Webapps:GetSelf:Return:OK", aData);
|
|
|
|
return;
|
2011-11-28 20:13:26 +00:00
|
|
|
}
|
|
|
|
|
2012-03-06 19:50:58 +00:00
|
|
|
this._readManifests(tmp, (function(aResult) {
|
|
|
|
for (let i = 0; i < aResult.length; i++)
|
|
|
|
aData.apps[i].manifest = aResult[i].manifest;
|
2012-09-18 17:34:55 +00:00
|
|
|
aMm.sendAsyncMessage("Webapps:GetSelf:Return:OK", aData);
|
2012-03-06 19:50:58 +00:00
|
|
|
}).bind(this));
|
|
|
|
},
|
2011-11-28 20:13:26 +00:00
|
|
|
|
2012-09-25 15:04:24 +00:00
|
|
|
isInstalled: function(aData, aMm) {
|
|
|
|
aData.installed = false;
|
|
|
|
|
|
|
|
for (let appId in this.webapps) {
|
|
|
|
if (this.webapps[appId].manifestURL == aData.manifestURL) {
|
|
|
|
aData.installed = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
aMm.sendAsyncMessage("Webapps:IsInstalled:Return:OK", aData);
|
|
|
|
},
|
|
|
|
|
2012-09-18 17:34:55 +00:00
|
|
|
getInstalled: function(aData, aMm) {
|
2012-03-06 19:50:58 +00:00
|
|
|
aData.apps = [];
|
|
|
|
let tmp = [];
|
|
|
|
|
2012-06-29 20:46:21 +00:00
|
|
|
for (let id in this.webapps) {
|
|
|
|
if (this.webapps[id].installOrigin == aData.origin &&
|
2012-07-03 03:45:00 +00:00
|
|
|
this._isLaunchable(this.webapps[id].origin)) {
|
2012-08-28 02:43:57 +00:00
|
|
|
aData.apps.push(AppsUtils.cloneAppObject(this.webapps[id]));
|
2012-03-06 19:50:58 +00:00
|
|
|
tmp.push({ id: id });
|
2011-11-28 20:13:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-05 23:26:18 +00:00
|
|
|
this._readManifests(tmp, (function(aResult) {
|
|
|
|
for (let i = 0; i < aResult.length; i++)
|
|
|
|
aData.apps[i].manifest = aResult[i].manifest;
|
2012-09-18 17:34:55 +00:00
|
|
|
aMm.sendAsyncMessage("Webapps:GetInstalled:Return:OK", aData);
|
2011-12-05 23:26:18 +00:00
|
|
|
}).bind(this));
|
2011-11-28 20:13:26 +00:00
|
|
|
},
|
|
|
|
|
2012-09-18 17:34:55 +00:00
|
|
|
getNotInstalled: function(aData, aMm) {
|
2012-06-29 20:46:21 +00:00
|
|
|
aData.apps = [];
|
|
|
|
let tmp = [];
|
|
|
|
|
|
|
|
for (let id in this.webapps) {
|
2012-08-20 17:19:56 +00:00
|
|
|
if (!this._isLaunchable(this.webapps[id].origin)) {
|
2012-08-28 02:43:57 +00:00
|
|
|
aData.apps.push(AppsUtils.cloneAppObject(this.webapps[id]));
|
2012-06-29 20:46:21 +00:00
|
|
|
tmp.push({ id: id });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this._readManifests(tmp, (function(aResult) {
|
|
|
|
for (let i = 0; i < aResult.length; i++)
|
|
|
|
aData.apps[i].manifest = aResult[i].manifest;
|
2012-09-18 17:34:55 +00:00
|
|
|
aMm.sendAsyncMessage("Webapps:GetNotInstalled:Return:OK", aData);
|
2012-06-29 20:46:21 +00:00
|
|
|
}).bind(this));
|
|
|
|
},
|
|
|
|
|
2012-09-18 17:34:55 +00:00
|
|
|
getAll: function(aData, aMm) {
|
2011-11-28 20:13:26 +00:00
|
|
|
aData.apps = [];
|
2011-12-05 23:26:18 +00:00
|
|
|
let tmp = [];
|
2011-11-28 20:13:26 +00:00
|
|
|
|
2012-04-28 07:10:08 +00:00
|
|
|
for (let id in this.webapps) {
|
2012-08-28 02:43:57 +00:00
|
|
|
let app = AppsUtils.cloneAppObject(this.webapps[id]);
|
2012-07-03 03:45:00 +00:00
|
|
|
if (!this._isLaunchable(app.origin))
|
2012-06-29 20:46:21 +00:00
|
|
|
continue;
|
|
|
|
|
2011-11-28 20:13:26 +00:00
|
|
|
aData.apps.push(app);
|
2011-12-05 23:26:18 +00:00
|
|
|
tmp.push({ id: id });
|
2011-11-28 20:13:26 +00:00
|
|
|
}
|
|
|
|
|
2011-12-05 23:26:18 +00:00
|
|
|
this._readManifests(tmp, (function(aResult) {
|
|
|
|
for (let i = 0; i < aResult.length; i++)
|
|
|
|
aData.apps[i].manifest = aResult[i].manifest;
|
2012-09-18 17:34:55 +00:00
|
|
|
aMm.sendAsyncMessage("Webapps:GetAll:Return:OK", aData);
|
2011-12-05 23:26:18 +00:00
|
|
|
}).bind(this));
|
2011-11-28 20:13:26 +00:00
|
|
|
},
|
|
|
|
|
2011-12-05 23:26:18 +00:00
|
|
|
getManifestFor: function(aOrigin, aCallback) {
|
|
|
|
if (!aCallback)
|
|
|
|
return;
|
|
|
|
|
2011-11-28 20:13:26 +00:00
|
|
|
let id = this._appId(aOrigin);
|
2012-09-27 19:34:41 +00:00
|
|
|
if (!id || this.webapps[id].installState == "pending") {
|
2011-12-05 23:26:18 +00:00
|
|
|
aCallback(null);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._readManifests([{ id: id }], function(aResult) {
|
|
|
|
aCallback(aResult[0].manifest);
|
|
|
|
});
|
2011-12-08 13:32:54 +00:00
|
|
|
},
|
|
|
|
|
2012-04-28 07:10:08 +00:00
|
|
|
/** Added to support AITC and classic sync */
|
|
|
|
itemExists: function(aId) {
|
|
|
|
return !!this.webapps[aId];
|
|
|
|
},
|
2011-12-08 13:32:54 +00:00
|
|
|
|
|
|
|
getAppById: function(aId) {
|
|
|
|
if (!this.webapps[aId])
|
|
|
|
return null;
|
2012-07-14 02:52:30 +00:00
|
|
|
|
2012-08-28 02:43:57 +00:00
|
|
|
let app = AppsUtils.cloneAppObject(this.webapps[aId]);
|
2011-12-08 13:32:54 +00:00
|
|
|
return app;
|
|
|
|
},
|
2012-05-16 10:40:47 +00:00
|
|
|
|
|
|
|
getAppByManifestURL: function(aManifestURL) {
|
2012-08-28 02:43:57 +00:00
|
|
|
return AppsUtils.getAppByManifestURL(this.webapps, aManifestURL);
|
2012-05-16 10:40:47 +00:00
|
|
|
},
|
2012-07-09 10:25:41 +00:00
|
|
|
|
2012-08-08 16:41:47 +00:00
|
|
|
getAppByLocalId: function(aLocalId) {
|
2012-08-28 02:43:57 +00:00
|
|
|
return AppsUtils.getAppByLocalId(this.webapps, aLocalId);
|
2012-08-08 16:41:47 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
getManifestURLByLocalId: function(aLocalId) {
|
2012-08-28 02:43:57 +00:00
|
|
|
return AppsUtils.getManifestURLByLocalId(this.webapps, aLocalId);
|
2012-08-08 16:41:47 +00:00
|
|
|
},
|
|
|
|
|
2012-07-09 10:25:41 +00:00
|
|
|
getAppLocalIdByManifestURL: function(aManifestURL) {
|
2012-08-28 02:43:57 +00:00
|
|
|
return AppsUtils.getAppLocalIdByManifestURL(this.webapps, aManifestURL);
|
2012-07-09 10:25:41 +00:00
|
|
|
},
|
|
|
|
|
2012-08-31 14:34:28 +00:00
|
|
|
getAppFromObserverMessage: function(aMessage) {
|
|
|
|
return AppsUtils.getAppFromObserverMessage(this.webapps, aMessage);
|
|
|
|
},
|
|
|
|
|
2012-04-28 07:10:08 +00:00
|
|
|
getAllWithoutManifests: function(aCallback) {
|
|
|
|
let result = {};
|
|
|
|
for (let id in this.webapps) {
|
2012-08-28 02:43:57 +00:00
|
|
|
let app = AppsUtils.cloneAppObject(this.webapps[id]);
|
2012-04-28 07:10:08 +00:00
|
|
|
result[id] = app;
|
|
|
|
}
|
|
|
|
aCallback(result);
|
2011-12-08 13:32:54 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
updateApps: function(aRecords, aCallback) {
|
|
|
|
for (let i = 0; i < aRecords.length; i++) {
|
|
|
|
let record = aRecords[i];
|
2012-06-23 17:22:53 +00:00
|
|
|
if (record.hidden) {
|
2012-08-29 21:20:03 +00:00
|
|
|
if (!this.webapps[record.id] || !this.webapps[record.id].removable)
|
2011-12-08 13:32:54 +00:00
|
|
|
continue;
|
2012-09-28 22:16:29 +00:00
|
|
|
|
2011-12-08 13:32:54 +00:00
|
|
|
let origin = this.webapps[record.id].origin;
|
|
|
|
delete this.webapps[record.id];
|
2012-08-29 21:20:03 +00:00
|
|
|
let dir = this._getAppDir(record.id);
|
2011-12-08 13:32:54 +00:00
|
|
|
try {
|
|
|
|
dir.remove(true);
|
|
|
|
} catch (e) {
|
|
|
|
}
|
2012-09-18 17:34:55 +00:00
|
|
|
this.broadcastMessage("Webapps:Uninstall:Return:OK", { origin: origin });
|
2011-12-08 13:32:54 +00:00
|
|
|
} else {
|
2012-04-28 07:10:08 +00:00
|
|
|
if (this.webapps[record.id]) {
|
2011-12-08 13:32:54 +00:00
|
|
|
this.webapps[record.id] = record.value;
|
|
|
|
delete this.webapps[record.id].manifest;
|
2012-04-28 07:10:08 +00:00
|
|
|
} else {
|
2011-12-08 13:32:54 +00:00
|
|
|
let data = { app: record.value };
|
|
|
|
this.confirmInstall(data, true);
|
2012-09-18 17:34:55 +00:00
|
|
|
this.broadcastMessage("Webapps:Install:Return:OK", data);
|
2011-12-08 13:32:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this._saveApps(aCallback);
|
|
|
|
},
|
|
|
|
|
|
|
|
getAllIDs: function() {
|
|
|
|
let apps = {};
|
|
|
|
for (let id in this.webapps) {
|
|
|
|
// only sync http and https apps
|
|
|
|
if (this.webapps[id].origin.indexOf("http") == 0)
|
|
|
|
apps[id] = true;
|
|
|
|
}
|
|
|
|
return apps;
|
|
|
|
},
|
|
|
|
|
|
|
|
wipe: function(aCallback) {
|
|
|
|
let ids = this.getAllIDs();
|
|
|
|
for (let id in ids) {
|
2012-08-29 21:20:03 +00:00
|
|
|
if (!this.webapps[id].removable) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2011-12-08 13:32:54 +00:00
|
|
|
delete this.webapps[id];
|
2012-08-29 21:20:03 +00:00
|
|
|
let dir = this._getAppDir(id);
|
2011-12-08 13:32:54 +00:00
|
|
|
try {
|
|
|
|
dir.remove(true);
|
|
|
|
} catch (e) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this._saveApps(aCallback);
|
2012-06-29 20:46:21 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
_isLaunchable: function(aOrigin) {
|
|
|
|
if (this.allAppsLaunchable)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
#ifdef XP_WIN
|
|
|
|
let uninstallKey = Cc["@mozilla.org/windows-registry-key;1"]
|
|
|
|
.createInstance(Ci.nsIWindowsRegKey);
|
|
|
|
try {
|
|
|
|
uninstallKey.open(uninstallKey.ROOT_KEY_CURRENT_USER,
|
|
|
|
"SOFTWARE\\Microsoft\\Windows\\" +
|
|
|
|
"CurrentVersion\\Uninstall\\" +
|
|
|
|
aOrigin,
|
|
|
|
uninstallKey.ACCESS_READ);
|
|
|
|
uninstallKey.close();
|
|
|
|
return true;
|
|
|
|
} catch (ex) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#elifdef XP_MACOSX
|
|
|
|
let mwaUtils = Cc["@mozilla.org/widget/mac-web-app-utils;1"]
|
|
|
|
.createInstance(Ci.nsIMacWebAppUtils);
|
|
|
|
|
|
|
|
return !!mwaUtils.pathForAppWithIdentifier(aOrigin);
|
|
|
|
#elifdef XP_UNIX
|
|
|
|
let env = Cc["@mozilla.org/process/environment;1"]
|
|
|
|
.getService(Ci.nsIEnvironment);
|
|
|
|
let xdg_data_home_env = env.get("XDG_DATA_HOME");
|
|
|
|
|
|
|
|
let desktopINI;
|
|
|
|
if (xdg_data_home_env != "") {
|
|
|
|
desktopINI = Cc["@mozilla.org/file/local;1"]
|
|
|
|
.createInstance(Ci.nsIFile);
|
|
|
|
desktopINI.initWithPath(xdg_data_home_env);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
desktopINI = Services.dirsvc.get("Home", Ci.nsIFile);
|
|
|
|
desktopINI.append(".local");
|
|
|
|
desktopINI.append("share");
|
|
|
|
}
|
|
|
|
desktopINI.append("applications");
|
|
|
|
|
|
|
|
let origin = Services.io.newURI(aOrigin, null, null);
|
|
|
|
let uniqueName = origin.scheme + ";" +
|
|
|
|
origin.host +
|
|
|
|
(origin.port != -1 ? ";" + origin.port : "");
|
|
|
|
|
|
|
|
desktopINI.append("owa-" + uniqueName + ".desktop");
|
|
|
|
|
|
|
|
return desktopINI.exists();
|
|
|
|
#else
|
|
|
|
return true;
|
|
|
|
#endif
|
|
|
|
|
2012-09-26 22:03:25 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
_notifyCategoryAndObservers: function(subject, topic, data) {
|
|
|
|
const serviceMarker = "service,";
|
|
|
|
|
|
|
|
// First create observers from the category manager.
|
|
|
|
let cm =
|
|
|
|
Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
|
|
|
|
let enumerator = cm.enumerateCategory(topic);
|
|
|
|
|
|
|
|
let observers = [];
|
|
|
|
|
|
|
|
while (enumerator.hasMoreElements()) {
|
|
|
|
let entry =
|
|
|
|
enumerator.getNext().QueryInterface(Ci.nsISupportsCString).data;
|
|
|
|
let contractID = cm.getCategoryEntry(topic, entry);
|
|
|
|
|
|
|
|
let factoryFunction;
|
|
|
|
if (contractID.substring(0, serviceMarker.length) == serviceMarker) {
|
|
|
|
contractID = contractID.substring(serviceMarker.length);
|
|
|
|
factoryFunction = "getService";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
factoryFunction = "createInstance";
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
let handler = Cc[contractID][factoryFunction]();
|
|
|
|
if (handler) {
|
|
|
|
let observer = handler.QueryInterface(Ci.nsIObserver);
|
|
|
|
observers.push(observer);
|
|
|
|
}
|
|
|
|
} catch(e) { }
|
|
|
|
}
|
|
|
|
|
|
|
|
// Next enumerate the registered observers.
|
|
|
|
enumerator = Services.obs.enumerateObservers(topic);
|
|
|
|
while (enumerator.hasMoreElements()) {
|
2012-09-28 16:24:45 +00:00
|
|
|
try {
|
|
|
|
let observer = enumerator.getNext().QueryInterface(Ci.nsIObserver);
|
|
|
|
if (observers.indexOf(observer) == -1) {
|
|
|
|
observers.push(observer);
|
|
|
|
}
|
|
|
|
} catch (e) { }
|
2012-09-26 22:03:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
observers.forEach(function (observer) {
|
|
|
|
try {
|
|
|
|
observer.observe(subject, topic, data);
|
|
|
|
} catch(e) { }
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
registerBrowserElementParentForApp: function(bep, appId) {
|
|
|
|
let mm = bep._mm;
|
|
|
|
|
|
|
|
// Make a listener function that holds on to this appId.
|
|
|
|
let listener = this.receiveAppMessage.bind(this, appId);
|
|
|
|
|
|
|
|
this.frameMessages.forEach(function(msgName) {
|
|
|
|
mm.addMessageListener(msgName, listener);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
receiveAppMessage: function(appId, message) {
|
|
|
|
switch (message.name) {
|
|
|
|
case "Webapps:ClearBrowserData":
|
2012-09-28 16:24:45 +00:00
|
|
|
this._clearPrivateData(appId, true);
|
2012-09-26 22:03:25 +00:00
|
|
|
break;
|
|
|
|
}
|
2012-09-28 16:24:45 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
_clearPrivateData: function(appId, browserOnly) {
|
|
|
|
let subject = {
|
|
|
|
appId: appId,
|
|
|
|
browserOnly: browserOnly,
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.mozIApplicationClearPrivateDataParams])
|
|
|
|
};
|
|
|
|
this._notifyCategoryAndObservers(subject, "webapps-clear-data", null);
|
2012-04-28 07:10:08 +00:00
|
|
|
}
|
2011-11-28 20:13:26 +00:00
|
|
|
};
|
|
|
|
|
2012-06-11 18:41:46 +00:00
|
|
|
/**
|
|
|
|
* Appcache download observer
|
|
|
|
*/
|
2012-07-03 00:16:55 +00:00
|
|
|
let AppcacheObserver = function(aApp) {
|
2012-06-11 18:41:46 +00:00
|
|
|
this.app = aApp;
|
2012-09-27 01:01:20 +00:00
|
|
|
this.startStatus = aApp.installState;
|
2012-06-11 18:41:46 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
AppcacheObserver.prototype = {
|
|
|
|
// nsIOfflineCacheUpdateObserver implementation
|
|
|
|
updateStateChanged: function appObs_Update(aUpdate, aState) {
|
|
|
|
let mustSave = false;
|
|
|
|
let app = this.app;
|
|
|
|
|
|
|
|
let setStatus = function appObs_setStatus(aStatus) {
|
2012-09-27 01:01:20 +00:00
|
|
|
mustSave = (app.installState != aStatus);
|
|
|
|
app.installState = aStatus;
|
|
|
|
DOMApplicationRegistry.broadcastMessage("Webapps:OfflineCache",
|
|
|
|
{ manifest: app.manifestURL,
|
|
|
|
installState: app.installState });
|
|
|
|
}
|
|
|
|
|
|
|
|
let setError = function appObs_setError(aError) {
|
2012-09-18 17:34:55 +00:00
|
|
|
DOMApplicationRegistry.broadcastMessage("Webapps:OfflineCache",
|
|
|
|
{ manifest: app.manifestURL,
|
2012-09-27 01:01:20 +00:00
|
|
|
error: aError });
|
2012-06-11 18:41:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (aState) {
|
|
|
|
case Ci.nsIOfflineCacheUpdateObserver.STATE_ERROR:
|
|
|
|
aUpdate.removeObserver(this);
|
2012-09-27 01:01:20 +00:00
|
|
|
setError("APP_CACHE_DOWNLOAD_ERROR");
|
2012-06-11 18:41:46 +00:00
|
|
|
break;
|
|
|
|
case Ci.nsIOfflineCacheUpdateObserver.STATE_NOUPDATE:
|
|
|
|
case Ci.nsIOfflineCacheUpdateObserver.STATE_FINISHED:
|
|
|
|
aUpdate.removeObserver(this);
|
2012-09-27 01:01:20 +00:00
|
|
|
setStatus("installed");
|
2012-06-11 18:41:46 +00:00
|
|
|
break;
|
|
|
|
case Ci.nsIOfflineCacheUpdateObserver.STATE_DOWNLOADING:
|
|
|
|
case Ci.nsIOfflineCacheUpdateObserver.STATE_ITEMSTARTED:
|
|
|
|
case Ci.nsIOfflineCacheUpdateObserver.STATE_ITEMPROGRESS:
|
2012-09-27 01:01:20 +00:00
|
|
|
setStatus(this.startStatus);
|
2012-06-11 18:41:46 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Status changed, update the stored version.
|
|
|
|
if (mustSave) {
|
|
|
|
DOMApplicationRegistry._saveApps();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
applicationCacheAvailable: function appObs_CacheAvail(aApplicationCache) {
|
|
|
|
// Nothing to do.
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-11-28 20:13:26 +00:00
|
|
|
/**
|
|
|
|
* Helper object to access manifest information with locale support
|
|
|
|
*/
|
2012-07-03 00:16:55 +00:00
|
|
|
let DOMApplicationManifest = function(aManifest, aOrigin) {
|
2011-11-28 20:13:26 +00:00
|
|
|
this._origin = Services.io.newURI(aOrigin, null, null);
|
|
|
|
this._manifest = aManifest;
|
|
|
|
let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry)
|
|
|
|
.QueryInterface(Ci.nsIToolkitChromeRegistry);
|
|
|
|
let locale = chrome.getSelectedLocale("browser").toLowerCase();
|
|
|
|
this._localeRoot = this._manifest;
|
2012-04-28 07:10:08 +00:00
|
|
|
|
2011-11-28 20:13:26 +00:00
|
|
|
if (this._manifest.locales && this._manifest.locales[locale]) {
|
|
|
|
this._localeRoot = this._manifest.locales[locale];
|
|
|
|
}
|
|
|
|
else if (this._manifest.locales) {
|
|
|
|
// try with the language part of the locale ("en" for en-GB) only
|
|
|
|
let lang = locale.split('-')[0];
|
2012-03-22 21:26:36 +00:00
|
|
|
if (lang != locale && this._manifest.locales[lang])
|
2011-11-28 20:13:26 +00:00
|
|
|
this._localeRoot = this._manifest.locales[lang];
|
|
|
|
}
|
2012-04-28 07:10:08 +00:00
|
|
|
};
|
2011-11-28 20:13:26 +00:00
|
|
|
|
|
|
|
DOMApplicationManifest.prototype = {
|
|
|
|
_localeProp: function(aProp) {
|
|
|
|
if (this._localeRoot[aProp] != undefined)
|
|
|
|
return this._localeRoot[aProp];
|
|
|
|
return this._manifest[aProp];
|
|
|
|
},
|
|
|
|
|
|
|
|
get name() {
|
|
|
|
return this._localeProp("name");
|
|
|
|
},
|
2012-04-28 07:10:08 +00:00
|
|
|
|
2011-11-28 20:13:26 +00:00
|
|
|
get description() {
|
|
|
|
return this._localeProp("description");
|
|
|
|
},
|
2012-04-28 07:10:08 +00:00
|
|
|
|
2011-11-28 20:13:26 +00:00
|
|
|
get version() {
|
|
|
|
return this._localeProp("version");
|
|
|
|
},
|
2012-04-28 07:10:08 +00:00
|
|
|
|
2011-11-28 20:13:26 +00:00
|
|
|
get launch_path() {
|
|
|
|
return this._localeProp("launch_path");
|
|
|
|
},
|
2012-04-28 07:10:08 +00:00
|
|
|
|
2011-11-28 20:13:26 +00:00
|
|
|
get developer() {
|
|
|
|
return this._localeProp("developer");
|
|
|
|
},
|
2012-04-28 07:10:08 +00:00
|
|
|
|
2011-11-28 20:13:26 +00:00
|
|
|
get icons() {
|
|
|
|
return this._localeProp("icons");
|
|
|
|
},
|
2012-04-28 07:10:08 +00:00
|
|
|
|
2012-06-11 18:41:46 +00:00
|
|
|
get appcache_path() {
|
|
|
|
return this._localeProp("appcache_path");
|
|
|
|
},
|
|
|
|
|
2012-08-08 19:35:15 +00:00
|
|
|
get orientation() {
|
|
|
|
return this._localeProp("orientation");
|
|
|
|
},
|
|
|
|
|
2012-09-27 19:34:41 +00:00
|
|
|
get package_path() {
|
|
|
|
return this._localeProp("package_path");
|
|
|
|
},
|
|
|
|
|
|
|
|
get size() {
|
|
|
|
return this._manifest["size"] || 0;
|
|
|
|
},
|
|
|
|
|
2012-09-28 22:16:29 +00:00
|
|
|
get permissions() {
|
|
|
|
if (this._manifest.permissions) {
|
|
|
|
return this._manifest.permissions;
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
},
|
|
|
|
|
2011-11-28 20:13:26 +00:00
|
|
|
iconURLForSize: function(aSize) {
|
|
|
|
let icons = this._localeProp("icons");
|
|
|
|
if (!icons)
|
|
|
|
return null;
|
|
|
|
let dist = 100000;
|
|
|
|
let icon = null;
|
|
|
|
for (let size in icons) {
|
|
|
|
let iSize = parseInt(size);
|
|
|
|
if (Math.abs(iSize - aSize) < dist) {
|
|
|
|
icon = this._origin.resolve(icons[size]);
|
|
|
|
dist = Math.abs(iSize - aSize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return icon;
|
|
|
|
},
|
2012-04-28 07:10:08 +00:00
|
|
|
|
2012-05-04 18:02:05 +00:00
|
|
|
fullLaunchPath: function(aStartPoint) {
|
2012-08-30 22:02:00 +00:00
|
|
|
// If no start point is specified, we use the root launch path.
|
|
|
|
// In all error cases, we just return null.
|
|
|
|
if ((aStartPoint || "") === "") {
|
|
|
|
return this._origin.resolve(this._localeProp("launch_path") || "");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Search for the l10n entry_points property.
|
|
|
|
let entryPoints = this._localeProp("entry_points");
|
|
|
|
if (!entryPoints) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entryPoints[aStartPoint]) {
|
|
|
|
return this._origin.resolve(entryPoints[aStartPoint].launch_path || "");
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
2012-06-11 18:41:46 +00:00
|
|
|
},
|
|
|
|
|
2012-08-09 15:39:52 +00:00
|
|
|
resolveFromOrigin: function(aURI) {
|
|
|
|
return this._origin.resolve(aURI);
|
|
|
|
},
|
|
|
|
|
2012-06-11 18:41:46 +00:00
|
|
|
fullAppcachePath: function() {
|
|
|
|
let appcachePath = this._localeProp("appcache_path");
|
|
|
|
return this._origin.resolve(appcachePath ? appcachePath : "");
|
2012-09-27 19:34:41 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
fullPackagePath: function() {
|
|
|
|
let packagePath = this._localeProp("package_path");
|
|
|
|
return this._origin.resolve(packagePath ? packagePath : "");
|
2011-11-28 20:13:26 +00:00
|
|
|
}
|
2012-04-28 07:10:08 +00:00
|
|
|
};
|
2011-11-28 20:13:26 +00:00
|
|
|
|
|
|
|
DOMApplicationRegistry.init();
|