toolkit/mozapps/extensions/ChromeManifestParser.jsm

changeset 0
6474c204b198
     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 +};

mercurial