Bug 844227 - Add more functions to the webapps actor. r=fabrice

This commit is contained in:
Alexandre Poirot 2013-05-06 09:51:53 -04:00
Родитель d054001961
Коммит a99c4571dd
8 изменённых файлов: 416 добавлений и 76 удалений

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

@ -8,6 +8,12 @@ let Cu = Components.utils;
let Cc = Components.classes;
let Ci = Components.interfaces;
Cu.import("resource://gre/modules/Webapps.jsm");
Cu.import("resource://gre/modules/AppsUtils.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import('resource://gre/modules/Services.jsm');
Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
function debug(aMsg) {
/*
Cc["@mozilla.org/consoleservice;1"]
@ -32,6 +38,7 @@ WebappsActor.prototype = {
actorPrefix: "webapps",
_registerApp: function wa_actorRegisterApp(aApp, aId, aDir) {
debug("registerApp");
let reg = DOMApplicationRegistry;
let self = this;
@ -49,8 +56,12 @@ WebappsActor.prototype = {
reg._readManifests([{ id: aId }], function(aResult) {
let manifest = aResult[0].manifest;
aApp.name = manifest.name;
reg._registerSystemMessages(manifest, aApp);
reg._registerActivities(manifest, aApp, true);
if ("_registerSystemMessages" in reg) {
reg._registerSystemMessages(manifest, aApp);
}
if ("_registerActivities" in reg) {
reg._registerActivities(manifest, aApp, true);
}
reg._saveApps(function() {
aApp.manifest = manifest;
reg.broadcastMessage("Webapps:AddApp", { id: aId, app: aApp });
@ -237,10 +248,6 @@ WebappsActor.prototype = {
install: function wa_actorInstall(aRequest) {
debug("install");
Cu.import("resource://gre/modules/Webapps.jsm");
Cu.import("resource://gre/modules/AppsUtils.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
let appId = aRequest.appId;
if (!appId) {
return { error: "missingParameter",
@ -289,6 +296,87 @@ WebappsActor.prototype = {
}
return { appId: appId, path: appDir.path }
},
getAll: function wa_actorGetAll(aRequest) {
debug("getAll");
let defer = Promise.defer();
let reg = DOMApplicationRegistry;
reg.getAll(function onsuccess(apps) {
defer.resolve({ apps: apps });
});
return defer.promise;
},
uninstall: function wa_actorUninstall(aRequest) {
debug("uninstall");
let manifestURL = aRequest.manifestURL;
if (!manifestURL) {
return { error: "missingParameter",
message: "missing parameter manifestURL" };
}
let defer = Promise.defer();
let reg = DOMApplicationRegistry;
reg.uninstall(
manifestURL,
function onsuccess() {
defer.resolve({});
},
function onfailure(reason) {
defer.resolve({ error: reason });
}
);
return defer.promise;
},
launch: function wa_actorLaunch(aRequest) {
debug("launch");
let manifestURL = aRequest.manifestURL;
if (!manifestURL) {
return { error: "missingParameter",
message: "missing parameter manifestURL" };
}
let defer = Promise.defer();
let reg = DOMApplicationRegistry;
reg.launch(
aRequest.manifestURL,
aRequest.startPoint || "",
function onsuccess() {
defer.resolve({});
},
function onfailure(reason) {
defer.resolve({ error: reason });
});
return defer.promise;
},
close: function wa_actorLaunch(aRequest) {
debug("close");
let manifestURL = aRequest.manifestURL;
if (!manifestURL) {
return { error: "missingParameter",
message: "missing parameter manifestURL" };
}
let reg = DOMApplicationRegistry;
let app = reg.getAppByManifestURL(manifestURL);
if (!app) {
return { error: "missingParameter",
message: "No application for " + manifestURL };
}
reg.close(app);
return {};
}
};
@ -296,7 +384,12 @@ WebappsActor.prototype = {
* The request types this actor can handle.
*/
WebappsActor.prototype.requestTypes = {
"install": WebappsActor.prototype.install
"install": WebappsActor.prototype.install,
"getAll": WebappsActor.prototype.getAll,
"launch": WebappsActor.prototype.launch,
"close": WebappsActor.prototype.close,
"uninstall": WebappsActor.prototype.uninstall
};
DebuggerServer.addGlobalActor(WebappsActor, "webappsActor");

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

@ -876,6 +876,7 @@ var WebappsHelper = {
init: function webapps_init() {
Services.obs.addObserver(this, "webapps-launch", false);
Services.obs.addObserver(this, "webapps-ask-install", false);
Services.obs.addObserver(this, "webapps-close", false);
},
registerInstaller: function webapps_registerInstaller(data) {
@ -927,6 +928,12 @@ var WebappsHelper = {
app: json.app
});
break;
case "webapps-close":
shell.sendChromeEvent({
"type": "webapps-close",
"manifestURL": json.manifestURL
});
break;
}
}
}

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

@ -687,6 +687,7 @@ WebappsApplicationMgmt.prototype = {
dump("-- webapps.js uninstall " + aApp.manifestURL + "\n");
let request = this.createRequest();
cpmm.sendAsyncMessage("Webapps:Uninstall", { origin: aApp.origin,
manifestURL: aApp.manifestURL,
oid: this._id,
requestID: this.getRequestId(request) });
return request;

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

@ -820,10 +820,10 @@ this.DOMApplicationRegistry = {
this.getSelf(msg, mm);
break;
case "Webapps:Uninstall":
this.uninstall(msg, mm);
this.doUninstall(msg, mm);
break;
case "Webapps:Launch":
this.launchApp(msg, mm);
this.doLaunch(msg, mm);
break;
case "Webapps:CheckInstalled":
this.checkInstalled(msg, mm);
@ -835,7 +835,7 @@ this.DOMApplicationRegistry = {
this.getNotInstalled(msg, mm);
break;
case "Webapps:GetAll":
this.getAll(msg, mm);
this.doGetAll(msg, mm);
break;
case "Webapps:InstallPackage":
this.doInstallPackage(msg, mm);
@ -917,21 +917,48 @@ this.DOMApplicationRegistry = {
});
},
launchApp: function launchApp(aData, aMm) {
let app = this.getAppByManifestURL(aData.manifestURL);
doLaunch: function (aData, aMm) {
this.launch(
aData.manifestURL,
aData.startPoint,
function onsuccess() {
aMm.sendAsyncMessage("Webapps:Launch:Return:OK", aData);
},
function onfailure(reason) {
aMm.sendAsyncMessage("Webapps:Launch:Return:KO", aData);
}
);
},
launch: function launch(aManifestURL, aStartPoint, aOnSuccess, aOnFailure) {
let app = this.getAppByManifestURL(aManifestURL);
if (!app) {
aMm.sendAsyncMessage("Webapps:Launch:Return:KO", aData);
aOnFailure("NO_SUCH_APP");
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);
aOnFailure("PENDING_APP_NOT_LAUNCHABLE");
return;
}
Services.obs.notifyObservers(aMm, "webapps-launch", JSON.stringify(aData));
// We have to clone the app object as nsIDOMApplication objects are
// stringified as an empty object. (see bug 830376)
let appClone = AppsUtils.cloneAppObject(app);
appClone.startPoint = aStartPoint;
Services.obs.notifyObservers(null, "webapps-launch", JSON.stringify(appClone));
aOnSuccess();
},
close: function close(aApp) {
debug("close");
// We have to clone the app object as nsIDOMApplication objects are
// stringified as an empty object. (see bug 830376)
let appClone = AppsUtils.cloneAppObject(aApp);
Services.obs.notifyObservers(null, "webapps-close", JSON.stringify(appClone));
},
cancelDownload: function cancelDownload(aManifestURL, aError) {
@ -2526,67 +2553,79 @@ this.DOMApplicationRegistry = {
}
},
uninstall: function(aData, aMm) {
debug("uninstall " + aData.origin);
for (let id in this.webapps) {
let app = this.webapps[id];
if (app.origin != aData.origin) {
continue;
}
dump("-- webapps.js uninstall " + app.manifestURL + "\n");
if (!app.removable) {
debug("Error: cannot unintall a non-removable app.");
break;
}
// Check if we are downloading something for this app, and cancel the
// download if needed.
this.cancelDownload(app.manifestURL);
// Clean up the deprecated manifest cache if needed.
if (id in this._manifestCache) {
delete this._manifestCache[id];
}
// Clear private data first.
this._clearPrivateData(app.localId, false);
// Then notify observers.
Services.obs.notifyObservers(aMm, "webapps-uninstall", JSON.stringify(aData));
let appNote = JSON.stringify(AppsUtils.cloneAppObject(app));
appNote.id = id;
if (supportSystemMessages()) {
this._readManifests([{ id: id }], (function unregisterManifest(aResult) {
this._unregisterActivities(aResult[0].manifest, app);
}).bind(this));
}
let dir = this._getAppDir(id);
try {
dir.remove(true);
} catch (e) {}
delete this.webapps[id];
this._saveApps((function() {
aData.manifestURL = app.manifestURL;
this.broadcastMessage("Webapps:Uninstall:Broadcast:Return:OK", aData);
doUninstall: function(aData, aMm) {
this.uninstall(aData.manifestURL,
function onsuccess() {
aMm.sendAsyncMessage("Webapps:Uninstall:Return:OK", aData);
Services.obs.notifyObservers(this, "webapps-sync-uninstall", appNote);
this.broadcastMessage("Webapps:RemoveApp", { id: id });
}).bind(this));
},
function onfailure() {
// Fall-through, fails to uninstall the desired app because:
// - we cannot find the app to be uninstalled.
// - the app to be uninstalled is not removable.
aMm.sendAsyncMessage("Webapps:Uninstall:Return:KO", aData);
}
);
},
uninstall: function(aManifestURL, aOnSuccess, aOnFailure) {
debug("uninstall " + aManifestURL);
let app = this.getAppByManifestURL(aManifestURL);
if (!app) {
aOnFailure("NO_SUCH_APP");
return;
}
let id = app.id;
if (!app.removable) {
debug("Error: cannot uninstall a non-removable app.");
aOnFailure("NON_REMOVABLE_APP");
return;
}
// Fall-through, fails to uninstall the desired app because:
// - we cannot find the app to be uninstalled.
// - the app to be uninstalled is not removable.
aMm.sendAsyncMessage("Webapps:Uninstall:Return:KO", aData);
// Check if we are downloading something for this app, and cancel the
// download if needed.
this.cancelDownload(app.manifestURL);
// Clean up the deprecated manifest cache if needed.
if (id in this._manifestCache) {
delete this._manifestCache[id];
}
// Clear private data first.
this._clearPrivateData(app.localId, false);
// Then notify observers.
// We have to clone the app object as nsIDOMApplication objects are
// stringified as an empty object. (see bug 830376)
let appClone = AppsUtils.cloneAppObject(app);
Services.obs.notifyObservers(null, "webapps-uninstall", JSON.stringify(appClone));
if (supportSystemMessages()) {
this._readManifests([{ id: id }], (function unregisterManifest(aResult) {
this._unregisterActivities(aResult[0].manifest, app);
}).bind(this));
}
let dir = this._getAppDir(id);
try {
dir.remove(true);
} catch (e) {}
delete this.webapps[id];
this._saveApps((function() {
this.broadcastMessage("Webapps:Uninstall:Broadcast:Return:OK", appClone);
// Catch exception on callback call to ensure notifying observers after
try {
aOnSuccess();
} catch(e) {
Cu.reportError("DOMApplicationRegistry: Exception on app uninstall: " +
ex + "\n" + ex.stack);
}
Services.obs.notifyObservers(this, "webapps-sync-uninstall", JSON.stringify(appClone));
this.broadcastMessage("Webapps:RemoveApp", { id: id });
}).bind(this));
},
getSelf: function(aData, aMm) {
@ -2681,8 +2720,16 @@ this.DOMApplicationRegistry = {
}).bind(this));
},
getAll: function(aData, aMm) {
aData.apps = [];
doGetAll: function(aData, aMm) {
this.getAll(function (apps) {
aData.apps = apps;
aMm.sendAsyncMessage("Webapps:GetAll:Return:OK", aData);
});
},
getAll: function(aCallback) {
debug("getAll");
let apps = [];
let tmp = [];
for (let id in this.webapps) {
@ -2690,14 +2737,14 @@ this.DOMApplicationRegistry = {
if (!this._isLaunchable(app.origin))
continue;
aData.apps.push(app);
apps.push(app);
tmp.push({ id: id });
}
this._readManifests(tmp, (function(aResult) {
for (let i = 0; i < aResult.length; i++)
aData.apps[i].manifest = aResult[i].manifest;
aMm.sendAsyncMessage("Webapps:GetAll:Return:OK", aData);
apps[i].manifest = aResult[i].manifest;
aCallback(apps);
}).bind(this));
},

Двоичные данные
dom/apps/tests/unit/data/app.zip Normal file

Двоичный файл не отображается.

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

@ -0,0 +1,189 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
Components.utils.import("resource://gre/modules/devtools/dbg-server.jsm");
Components.utils.import("resource://gre/modules/devtools/dbg-client.jsm");
Components.utils.import("resource://gre/modules/FileUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
let gClient, gActor;
let gAppId = "actor-test";
add_test(function testSetup() {
// Initialize a loopback remote protocol connection
DebuggerServer.init(function () { return true; });
// We need to register browser actors to have `listTabs` working
// and also have a root actor
DebuggerServer.addBrowserActors();
DebuggerServer.addActors("chrome://browser/content/dbg-webapps-actors.js");
// Setup client and actor used in all tests
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function onConnect() {
gClient.listTabs(function onListTabs(aResponse) {
gActor = aResponse.webappsActor;
run_next_test();
});
});
});
add_test(function testLaunchInexistantApp() {
let request = {to: gActor, type: "launch", manifestURL: "http://foo.com"};
gClient.request(request, function (aResponse) {
do_check_eq(aResponse.error, "NO_SUCH_APP");
run_next_test();
});
});
add_test(function testCloseInexistantApp() {
let request = {to: gActor, type: "close", manifestURL: "http://foo.com"};
gClient.request(request, function (aResponse) {
do_check_eq(aResponse.error, "missingParameter");
do_check_eq(aResponse.message, "No application for http://foo.com");
run_next_test();
});
});
// Install a test app
add_test(function testInstallPackaged() {
// Copy our test webapp to tmp folder, where the actor retrieves it
let zip = do_get_file("data/app.zip");
let appDir = FileUtils.getDir("TmpD", ["b2g", gAppId], true, true);
zip.copyTo(appDir, "application.zip");
let request = {to: gActor, type: "install", appId: gAppId};
gClient.request(request, function (aResponse) {
do_check_eq(aResponse.appId, gAppId);
});
// The install request is asynchronous and send back an event to tell
// if the installation succeed or failed
gClient.addListener("webappsEvent", function (aState, aType, aPacket) {
do_check_eq(aType.appId, gAppId);
do_check_eq("error" in aType, false);
run_next_test();
});
});
// Now check that the app appear in getAll
add_test(function testGetAll() {
let request = {to: gActor, type: "getAll"};
gClient.request(request, function (aResponse) {
do_check_true("apps" in aResponse);
let apps = aResponse.apps;
do_check_true(apps.length > 0);
for (let i = 0; i < apps.length; i++) {
let app = apps[i];
if (app.id == gAppId) {
do_check_eq(app.name, "Test app");
do_check_eq(app.manifest.description, "Testing webapps actor");
do_check_eq(app.manifest.launch_path, "/index.html");
do_check_eq(app.origin, "app://" + gAppId);
do_check_eq(app.installOrigin, app.origin);
do_check_eq(app.manifestURL, app.origin + "/manifest.webapp");
run_next_test();
return;
}
}
do_throw("Unable to find the test app by its id");
});
});
add_test(function testLaunchApp() {
let manifestURL = "app://" + gAppId + "/manifest.webapp";
let startPoint = "/index.html";
let request = {
to: gActor, type: "launch",
manifestURL: manifestURL,
startPoint: startPoint
};
Services.obs.addObserver(function observer(subject, topic, data) {
Services.obs.removeObserver(observer, topic);
let json = JSON.parse(data);
do_check_eq(json.manifestURL, manifestURL);
do_check_eq(json.startPoint, startPoint);
run_next_test();
}, "webapps-launch", false);
gClient.request(request, function (aResponse) {
do_check_false("error" in aResponse);
});
});
add_test(function testCloseApp() {
let manifestURL = "app://" + gAppId + "/manifest.webapp";
let request = {
to: gActor, type: "close",
manifestURL: manifestURL
};
Services.obs.addObserver(function observer(subject, topic, data) {
Services.obs.removeObserver(observer, topic);
let json = JSON.parse(data);
do_check_eq(json.manifestURL, manifestURL);
run_next_test();
}, "webapps-close", false);
gClient.request(request, function (aResponse) {
do_check_false("error" in aResponse);
});
});
add_test(function testUninstall() {
let origin = "app://" + gAppId;
let manifestURL = origin + "/manifest.webapp";
let request = {
to: gActor, type: "uninstall",
manifestURL: manifestURL
};
let gotFirstUninstallEvent = false;
Services.obs.addObserver(function observer(subject, topic, data) {
Services.obs.removeObserver(observer, topic);
let json = JSON.parse(data);
do_check_eq(json.manifestURL, manifestURL);
do_check_eq(json.origin, origin);
gotFirstUninstallEvent = true;
}, "webapps-uninstall", false);
Services.obs.addObserver(function observer(subject, topic, data) {
Services.obs.removeObserver(observer, topic);
let json = JSON.parse(data);
do_check_eq(json.manifestURL, manifestURL);
do_check_eq(json.origin, origin);
do_check_eq(json.id, gAppId);
do_check_true(gotFirstUninstallEvent);
run_next_test();
}, "webapps-sync-uninstall", false);
gClient.request(request, function (aResponse) {
do_check_false("error" in aResponse);
});
});
// Close the test remote connection before leaving this test
add_test(function testTearDown() {
gClient.close(function () {
run_next_test();
});
});
function run_test() {
// We have to setup a profile, otherwise indexed db used by webapps
// will throw random exception when trying to get profile folder
do_get_profile();
// We also need a valid nsIXulAppInfo service as Webapps.jsm is querying it
Components.utils.import("resource://testing-common/AppInfo.jsm");
updateAppInfo();
// We have to toggle this flag in order to have apps being listed in getAll
// as only launchable apps are returned
Components.utils.import('resource://gre/modules/Webapps.jsm');
DOMApplicationRegistry.allAppsLaunchable = true;
run_next_test();
}

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

@ -3,3 +3,5 @@ head =
tail =
[test_manifestSanitizer.js]
[test_webappsActor.js]
run-if = toolkit == "gonk"

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

@ -2,6 +2,7 @@
; License, v. 2.0. If a copy of the MPL was not distributed with this
; file, You can obtain one at http://mozilla.org/MPL/2.0/.
[include:dom/apps/tests/unit/xpcshell.ini]
[include:dom/mobilemessage/tests/xpcshell.ini]
[include:dom/mms/tests/xpcshell.ini]
[include:dom/system/gonk/tests/xpcshell.ini]