1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/mozapps/extensions/ChromeManifestParser.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,159 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +"use strict"; 1.9 + 1.10 +this.EXPORTED_SYMBOLS = ["ChromeManifestParser"]; 1.11 + 1.12 +const Cc = Components.classes; 1.13 +const Ci = Components.interfaces; 1.14 +const Cr = Components.results; 1.15 +const Cu = Components.utils; 1.16 + 1.17 +Cu.import("resource://gre/modules/Services.jsm"); 1.18 +Cu.import("resource://gre/modules/NetUtil.jsm"); 1.19 + 1.20 +const MSG_JAR_FLUSH = "AddonJarFlush"; 1.21 + 1.22 + 1.23 +/** 1.24 + * Sends local and remote notifications to flush a JAR file cache entry 1.25 + * 1.26 + * @param aJarFile 1.27 + * The ZIP/XPI/JAR file as a nsIFile 1.28 + */ 1.29 +function flushJarCache(aJarFile) { 1.30 + Services.obs.notifyObservers(aJarFile, "flush-cache-entry", null); 1.31 + Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageBroadcaster) 1.32 + .broadcastAsyncMessage(MSG_JAR_FLUSH, aJarFile.path); 1.33 +} 1.34 + 1.35 + 1.36 +/** 1.37 + * Parses chrome manifest files. 1.38 + */ 1.39 +this.ChromeManifestParser = { 1.40 + 1.41 + /** 1.42 + * Reads and parses a chrome manifest file located at a specified URI, and all 1.43 + * secondary manifests it references. 1.44 + * 1.45 + * @param aURI 1.46 + * A nsIURI pointing to a chrome manifest. 1.47 + * Typically a file: or jar: URI. 1.48 + * @return Array of objects describing each manifest instruction, in the form: 1.49 + * { type: instruction-type, baseURI: string-uri, args: [arguments] } 1.50 + **/ 1.51 + parseSync: function CMP_parseSync(aURI) { 1.52 + function parseLine(aLine) { 1.53 + let line = aLine.trim(); 1.54 + if (line.length == 0 || line.charAt(0) == '#') 1.55 + return; 1.56 + let tokens = line.split(/\s+/); 1.57 + let type = tokens.shift(); 1.58 + if (type == "manifest") { 1.59 + let uri = NetUtil.newURI(tokens.shift(), null, aURI); 1.60 + data = data.concat(this.parseSync(uri)); 1.61 + } else { 1.62 + data.push({type: type, baseURI: baseURI, args: tokens}); 1.63 + } 1.64 + } 1.65 + 1.66 + let contents = ""; 1.67 + try { 1.68 + if (aURI.scheme == "jar") 1.69 + contents = this._readFromJar(aURI); 1.70 + else 1.71 + contents = this._readFromFile(aURI); 1.72 + } catch (e) { 1.73 + // Silently fail. 1.74 + } 1.75 + 1.76 + if (!contents) 1.77 + return []; 1.78 + 1.79 + let baseURI = NetUtil.newURI(".", null, aURI).spec; 1.80 + 1.81 + let data = []; 1.82 + let lines = contents.split("\n"); 1.83 + lines.forEach(parseLine.bind(this)); 1.84 + return data; 1.85 + }, 1.86 + 1.87 + _readFromJar: function CMP_readFromJar(aURI) { 1.88 + let data = ""; 1.89 + let entries = []; 1.90 + let readers = []; 1.91 + 1.92 + try { 1.93 + // Deconstrict URI, which can be nested jar: URIs. 1.94 + let uri = aURI.clone(); 1.95 + while (uri instanceof Ci.nsIJARURI) { 1.96 + entries.push(uri.JAREntry); 1.97 + uri = uri.JARFile; 1.98 + } 1.99 + 1.100 + // Open the base jar. 1.101 + let reader = Cc["@mozilla.org/libjar/zip-reader;1"]. 1.102 + createInstance(Ci.nsIZipReader); 1.103 + reader.open(uri.QueryInterface(Ci.nsIFileURL).file); 1.104 + readers.push(reader); 1.105 + 1.106 + // Open the nested jars. 1.107 + for (let i = entries.length - 1; i > 0; i--) { 1.108 + let innerReader = Cc["@mozilla.org/libjar/zip-reader;1"]. 1.109 + createInstance(Ci.nsIZipReader); 1.110 + innerReader.openInner(reader, entries[i]); 1.111 + readers.push(innerReader); 1.112 + reader = innerReader; 1.113 + } 1.114 + 1.115 + // First entry is the actual file we want to read. 1.116 + let zis = reader.getInputStream(entries[0]); 1.117 + data = NetUtil.readInputStreamToString(zis, zis.available()); 1.118 + } 1.119 + finally { 1.120 + // Close readers in reverse order. 1.121 + for (let i = readers.length - 1; i >= 0; i--) { 1.122 + readers[i].close(); 1.123 + flushJarCache(readers[i].file); 1.124 + } 1.125 + } 1.126 + 1.127 + return data; 1.128 + }, 1.129 + 1.130 + _readFromFile: function CMP_readFromFile(aURI) { 1.131 + let file = aURI.QueryInterface(Ci.nsIFileURL).file; 1.132 + if (!file.exists() || !file.isFile()) 1.133 + return ""; 1.134 + 1.135 + let data = ""; 1.136 + let fis = Cc["@mozilla.org/network/file-input-stream;1"]. 1.137 + createInstance(Ci.nsIFileInputStream); 1.138 + try { 1.139 + fis.init(file, -1, -1, false); 1.140 + data = NetUtil.readInputStreamToString(fis, fis.available()); 1.141 + } finally { 1.142 + fis.close(); 1.143 + } 1.144 + return data; 1.145 + }, 1.146 + 1.147 + /** 1.148 + * Detects if there were any instructions of a specified type in a given 1.149 + * chrome manifest. 1.150 + * 1.151 + * @param aManifest 1.152 + * Manifest data, as returned by ChromeManifestParser.parseSync(). 1.153 + * @param aType 1.154 + * Instruction type to filter by. 1.155 + * @return True if any matching instructions were found in the manifest. 1.156 + */ 1.157 + hasType: function CMP_hasType(aManifest, aType) { 1.158 + return aManifest.some(function hasType_matchEntryType(aEntry) { 1.159 + return aEntry.type == aType; 1.160 + }); 1.161 + } 1.162 +};