Bug 1196734 - Support packaged WebExtensions in WebIDE r=ochameau

This commit is contained in:
Dan Callahan 2015-08-27 10:13:17 -07:00
parent 905ba07ddd
commit 5a89b3ff45
4 changed files with 91 additions and 57 deletions

View File

@ -37,12 +37,22 @@ AppValidator.prototype._getPackagedManifestFile = function () {
this.error(strings.GetStringFromName("validator.expectProjectFolder"));
return null;
}
manifestFile.append("manifest.webapp");
if (!manifestFile.exists() || !manifestFile.isFile()) {
let appManifestFile = manifestFile.clone();
appManifestFile.append("manifest.webapp");
let jsonManifestFile = manifestFile.clone();
jsonManifestFile.append("manifest.json");
let hasAppManifest = appManifestFile.exists() && appManifestFile.isFile();
let hasJsonManifest = jsonManifestFile.exists() && jsonManifestFile.isFile();
if (!hasAppManifest && !hasJsonManifest) {
this.error(strings.GetStringFromName("validator.wrongManifestFileName"));
return null;
}
return manifestFile;
return hasAppManifest ? appManifestFile : jsonManifestFile;
};
AppValidator.prototype._getPackagedManifestURL = function () {
@ -191,10 +201,6 @@ AppValidator.prototype._getOriginURL = function () {
};
AppValidator.prototype.validateLaunchPath = function (manifest) {
// Addons don't use index page (yet?)
if (manifest.role && manifest.role === "addon") {
return promise.resolve();
}
let deferred = promise.defer();
// The launch_path field has to start with a `/`
if (manifest.launch_path && manifest.launch_path[0] !== "/") {
@ -267,14 +273,20 @@ AppValidator.prototype.validate = function () {
this.errors = [];
this.warnings = [];
return this._getManifest().
then((function (manifest) {
then((manifest) => {
if (manifest) {
this.manifest = manifest;
// Skip validations for add-ons
if (manifest.role === "addon" || manifest.manifest_version) {
return promise.resolve();
}
this.validateManifest(manifest);
this.validateType(manifest);
return this.validateLaunchPath(manifest);
}
}).bind(this));
});
};
exports.AppValidator = AppValidator;

View File

@ -659,7 +659,7 @@ let AppManager = exports.AppManager = {
// Addons don't have any document to load (yet?)
// So that there is no need to run them, installing is enough
if (project.manifest.role && project.manifest.role === "addon") {
if (project.manifest.manifest_version || project.manifest.role === "addon") {
return;
}

View File

@ -18,7 +18,7 @@ project.installing=Installing…
project.installed=Installed!
validator.nonExistingFolder=The project folder doesn't exists
validator.expectProjectFolder=The project folder ends up being a file
validator.wrongManifestFileName=Packaged apps require a manifest file that can only be named 'manifest.webapp' at project root folder
validator.wrongManifestFileName=A manifest file is required at project root folder, named either 'manifest.webapp' for packaged apps or 'manifest.json' for addons.
validator.invalidManifestURL=Invalid manifest URL '%S'
# LOCALIZATION NOTE (validator.invalidManifestJSON, validator.noAccessManifestURL):
# %1$S is the error message, %2$S is the URI of the manifest.

View File

@ -10,6 +10,7 @@ Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/UserCustomizations.jsm");
let promise = require("promise");
let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
@ -479,23 +480,43 @@ WebappsActor.prototype = {
.createInstance(Ci.nsIZipReader);
zipReader.open(zipFile);
// Read app manifest `manifest.webapp` from `application.zip`
let istream = zipReader.getInputStream("manifest.webapp");
// Prefer manifest.webapp when available
let hasWebappManifest = zipReader.hasEntry("manifest.webapp");
let hasJsonManifest = zipReader.hasEntry("manifest.json");
if (!hasWebappManifest && !hasJsonManifest) {
self._sendError(deferred, "Missing manifest.webapp or manifest.json", aId);
return;
}
let manifestName = hasWebappManifest ? "manifest.webapp" : "manifest.json";
// Read app manifest from `application.zip`
let istream = zipReader.getInputStream(manifestName);
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
let jsonString = converter.ConvertToUnicode(
NetUtil.readInputStreamToString(istream, istream.available())
);
zipReader.close();
let manifest;
try {
manifest = JSON.parse(jsonString);
} catch(e) {
self._sendError(deferred, "Error Parsing manifest.webapp: " + e, aId);
self._sendError(deferred, "Error Parsing " + manifestName + ": " + e, aId);
return;
}
if (manifestName === "manifest.json") {
if (!UserCustomizations.checkExtensionManifest(manifest)) {
self._sendError(deferred, "Invalid manifest", aId);
return;
}
manifest = UserCustomizations.convertManifest(manifest);
}
// Completely forbid pushing apps asking for unsafe permissions
if ("permissions" in manifest) {
let list = UNSAFE_PERMISSIONS.split(",");
@ -538,56 +559,57 @@ WebappsActor.prototype = {
// Only after security checks are made and after final app id is computed
// we can move application.zip to the destination directory, and
// extract manifest.webapp there.
// write manifest.webapp there.
let installDir = DOMApplicationRegistry._getAppDir(id);
let manFile = installDir.clone();
manFile.append("manifest.webapp");
zipReader.extract("manifest.webapp", manFile);
zipReader.close();
zipFile.moveTo(installDir, "application.zip");
let origin = "app://" + id;
let manifestURL = origin + "/manifest.webapp";
let manFile = installDir.clone();
manFile.append("manifest.webapp");
DOMApplicationRegistry._writeFile(manFile.path, JSON.stringify(manifest))
.then(() => {
let origin = "app://" + id;
let manifestURL = origin + "/manifest.webapp";
// Refresh application.zip content (e.g. reinstall app), as done here:
// http://hg.mozilla.org/mozilla-central/annotate/aaefec5d34f8/dom/apps/src/Webapps.jsm#l1125
// Do it in parent process for the simulator
let jar = installDir.clone();
jar.append("application.zip");
Services.obs.notifyObservers(jar, "flush-cache-entry", null);
// Refresh application.zip content (e.g. reinstall app), as done here:
// http://hg.mozilla.org/mozilla-central/annotate/aaefec5d34f8/dom/apps/src/Webapps.jsm#l1125
// Do it in parent process for the simulator
let jar = installDir.clone();
jar.append("application.zip");
Services.obs.notifyObservers(jar, "flush-cache-entry", null);
// And then in app content process
// This function will be evaluated in the scope of the content process
// frame script. That will flush the jar cache for this app and allow
// loading fresh updated resources if we reload its document.
let FlushFrameScript = function (path) {
let jar = Cc["@mozilla.org/file/local;1"]
.createInstance(Ci.nsILocalFile);
jar.initWithPath(path);
let obs = Cc["@mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
obs.notifyObservers(jar, "flush-cache-entry", null);
};
for (let frame of self._appFrames()) {
if (frame.getAttribute("mozapp") == manifestURL) {
let mm = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager;
mm.loadFrameScript("data:," +
encodeURIComponent("(" + FlushFrameScript.toString() + ")" +
"('" + jar.path + "')"), false);
}
}
// And then in app content process
// This function will be evaluated in the scope of the content process
// frame script. That will flush the jar cache for this app and allow
// loading fresh updated resources if we reload its document.
let FlushFrameScript = function (path) {
let jar = Cc["@mozilla.org/file/local;1"]
.createInstance(Ci.nsILocalFile);
jar.initWithPath(path);
let obs = Cc["@mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
obs.notifyObservers(jar, "flush-cache-entry", null);
};
for (let frame of self._appFrames()) {
if (frame.getAttribute("mozapp") == manifestURL) {
let mm = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager;
mm.loadFrameScript("data:," +
encodeURIComponent("(" + FlushFrameScript.toString() + ")" +
"('" + jar.path + "')"), false);
}
}
// Create a fake app object with the minimum set of properties we need.
let app = {
origin: origin,
installOrigin: origin,
manifestURL: manifestURL,
appStatus: appType,
receipts: aReceipts,
kind: DOMApplicationRegistry.kPackaged,
}
// Create a fake app object with the minimum set of properties we need.
let app = {
origin: origin,
installOrigin: origin,
manifestURL: manifestURL,
appStatus: appType,
receipts: aReceipts,
kind: DOMApplicationRegistry.kPackaged,
}
self._registerApp(deferred, app, id, aDir);
self._registerApp(deferred, app, id, aDir);
});
} catch(e) {
// If anything goes wrong, just send it back.
self._sendError(deferred, e.toString(), aId);