summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/webextensions/ChromeManifestParser.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/mozapps/webextensions/ChromeManifestParser.jsm')
-rw-r--r--toolkit/mozapps/webextensions/ChromeManifestParser.jsm157
1 files changed, 157 insertions, 0 deletions
diff --git a/toolkit/mozapps/webextensions/ChromeManifestParser.jsm b/toolkit/mozapps/webextensions/ChromeManifestParser.jsm
new file mode 100644
index 000000000..63f1db785
--- /dev/null
+++ b/toolkit/mozapps/webextensions/ChromeManifestParser.jsm
@@ -0,0 +1,157 @@
+/* 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/. */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = ["ChromeManifestParser"];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+
+const MSG_JAR_FLUSH = "AddonJarFlush";
+
+
+/**
+ * Sends local and remote notifications to flush a JAR file cache entry
+ *
+ * @param aJarFile
+ * The ZIP/XPI/JAR file as a nsIFile
+ */
+function flushJarCache(aJarFile) {
+ Services.obs.notifyObservers(aJarFile, "flush-cache-entry", null);
+ Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageBroadcaster)
+ .broadcastAsyncMessage(MSG_JAR_FLUSH, aJarFile.path);
+}
+
+
+/**
+ * Parses chrome manifest files.
+ */
+this.ChromeManifestParser = {
+
+ /**
+ * Reads and parses a chrome manifest file located at a specified URI, and all
+ * secondary manifests it references.
+ *
+ * @param aURI
+ * A nsIURI pointing to a chrome manifest.
+ * Typically a file: or jar: URI.
+ * @return Array of objects describing each manifest instruction, in the form:
+ * { type: instruction-type, baseURI: string-uri, args: [arguments] }
+ **/
+ parseSync: function(aURI) {
+ function parseLine(aLine) {
+ let line = aLine.trim();
+ if (line.length == 0 || line.charAt(0) == '#')
+ return;
+ let tokens = line.split(/\s+/);
+ let type = tokens.shift();
+ if (type == "manifest") {
+ let uri = NetUtil.newURI(tokens.shift(), null, aURI);
+ data = data.concat(this.parseSync(uri));
+ } else {
+ data.push({type: type, baseURI: baseURI, args: tokens});
+ }
+ }
+
+ let contents = "";
+ try {
+ if (aURI.scheme == "jar")
+ contents = this._readFromJar(aURI);
+ else
+ contents = this._readFromFile(aURI);
+ } catch (e) {
+ // Silently fail.
+ }
+
+ if (!contents)
+ return [];
+
+ let baseURI = NetUtil.newURI(".", null, aURI).spec;
+
+ let data = [];
+ let lines = contents.split("\n");
+ lines.forEach(parseLine.bind(this));
+ return data;
+ },
+
+ _readFromJar: function(aURI) {
+ let data = "";
+ let entries = [];
+ let readers = [];
+
+ try {
+ // Deconstrict URI, which can be nested jar: URIs.
+ let uri = aURI.clone();
+ while (uri instanceof Ci.nsIJARURI) {
+ entries.push(uri.JAREntry);
+ uri = uri.JARFile;
+ }
+
+ // Open the base jar.
+ let reader = Cc["@mozilla.org/libjar/zip-reader;1"].
+ createInstance(Ci.nsIZipReader);
+ reader.open(uri.QueryInterface(Ci.nsIFileURL).file);
+ readers.push(reader);
+
+ // Open the nested jars.
+ for (let i = entries.length - 1; i > 0; i--) {
+ let innerReader = Cc["@mozilla.org/libjar/zip-reader;1"].
+ createInstance(Ci.nsIZipReader);
+ innerReader.openInner(reader, entries[i]);
+ readers.push(innerReader);
+ reader = innerReader;
+ }
+
+ // First entry is the actual file we want to read.
+ let zis = reader.getInputStream(entries[0]);
+ data = NetUtil.readInputStreamToString(zis, zis.available());
+ }
+ finally {
+ // Close readers in reverse order.
+ for (let i = readers.length - 1; i >= 0; i--) {
+ readers[i].close();
+ flushJarCache(readers[i].file);
+ }
+ }
+
+ return data;
+ },
+
+ _readFromFile: function(aURI) {
+ let file = aURI.QueryInterface(Ci.nsIFileURL).file;
+ if (!file.exists() || !file.isFile())
+ return "";
+
+ let data = "";
+ let fis = Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Ci.nsIFileInputStream);
+ try {
+ fis.init(file, -1, -1, false);
+ data = NetUtil.readInputStreamToString(fis, fis.available());
+ } finally {
+ fis.close();
+ }
+ return data;
+ },
+
+ /**
+ * Detects if there were any instructions of a specified type in a given
+ * chrome manifest.
+ *
+ * @param aManifest
+ * Manifest data, as returned by ChromeManifestParser.parseSync().
+ * @param aType
+ * Instruction type to filter by.
+ * @return True if any matching instructions were found in the manifest.
+ */
+ hasType: function(aManifest, aType) {
+ return aManifest.some(entry => entry.type == aType);
+ }
+};