toolkit/mozapps/extensions/ChromeManifestParser.jsm

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 "use strict";
     7 this.EXPORTED_SYMBOLS = ["ChromeManifestParser"];
     9 const Cc = Components.classes;
    10 const Ci = Components.interfaces;
    11 const Cr = Components.results;
    12 const Cu = Components.utils;
    14 Cu.import("resource://gre/modules/Services.jsm");
    15 Cu.import("resource://gre/modules/NetUtil.jsm");
    17 const MSG_JAR_FLUSH = "AddonJarFlush";
    20 /**
    21  * Sends local and remote notifications to flush a JAR file cache entry
    22  *
    23  * @param aJarFile
    24  *        The ZIP/XPI/JAR file as a nsIFile
    25  */
    26 function flushJarCache(aJarFile) {
    27   Services.obs.notifyObservers(aJarFile, "flush-cache-entry", null);
    28   Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageBroadcaster)
    29     .broadcastAsyncMessage(MSG_JAR_FLUSH, aJarFile.path);
    30 }
    33 /**
    34  * Parses chrome manifest files.
    35  */
    36 this.ChromeManifestParser = {
    38   /**
    39    * Reads and parses a chrome manifest file located at a specified URI, and all
    40    * secondary manifests it references.
    41    *
    42    * @param  aURI
    43    *         A nsIURI pointing to a chrome manifest.
    44    *         Typically a file: or jar: URI.
    45    * @return Array of objects describing each manifest instruction, in the form:
    46    *         { type: instruction-type, baseURI: string-uri, args: [arguments] }
    47    **/
    48   parseSync: function CMP_parseSync(aURI) {
    49     function parseLine(aLine) {
    50       let line = aLine.trim();
    51       if (line.length == 0 || line.charAt(0) == '#')
    52         return;
    53       let tokens = line.split(/\s+/);
    54       let type = tokens.shift();
    55       if (type == "manifest") {
    56         let uri = NetUtil.newURI(tokens.shift(), null, aURI);
    57         data = data.concat(this.parseSync(uri));
    58       } else {
    59         data.push({type: type, baseURI: baseURI, args: tokens});
    60       }
    61     }
    63     let contents = "";
    64     try {
    65       if (aURI.scheme == "jar")
    66         contents = this._readFromJar(aURI);
    67       else
    68         contents = this._readFromFile(aURI);
    69     } catch (e) {
    70       // Silently fail.
    71     }
    73     if (!contents)
    74       return [];
    76     let baseURI = NetUtil.newURI(".", null, aURI).spec;
    78     let data = [];
    79     let lines = contents.split("\n");
    80     lines.forEach(parseLine.bind(this));
    81     return data;
    82   },
    84   _readFromJar: function CMP_readFromJar(aURI) {
    85     let data = "";
    86     let entries = [];
    87     let readers = [];
    89     try {
    90       // Deconstrict URI, which can be nested jar: URIs. 
    91       let uri = aURI.clone();
    92       while (uri instanceof Ci.nsIJARURI) {
    93         entries.push(uri.JAREntry);
    94         uri = uri.JARFile;
    95       }
    97       // Open the base jar.
    98       let reader = Cc["@mozilla.org/libjar/zip-reader;1"].
    99                    createInstance(Ci.nsIZipReader);
   100       reader.open(uri.QueryInterface(Ci.nsIFileURL).file);
   101       readers.push(reader);
   103       // Open the nested jars.
   104       for (let i = entries.length - 1; i > 0; i--) {
   105         let innerReader = Cc["@mozilla.org/libjar/zip-reader;1"].
   106                           createInstance(Ci.nsIZipReader);
   107         innerReader.openInner(reader, entries[i]);
   108         readers.push(innerReader);
   109         reader = innerReader;
   110       }
   112       // First entry is the actual file we want to read.
   113       let zis = reader.getInputStream(entries[0]);
   114       data = NetUtil.readInputStreamToString(zis, zis.available());
   115     }
   116     finally {
   117       // Close readers in reverse order.
   118       for (let i = readers.length - 1; i >= 0; i--) {
   119         readers[i].close();
   120         flushJarCache(readers[i].file);
   121       }
   122     }
   124     return data;
   125   },
   127   _readFromFile: function CMP_readFromFile(aURI) {
   128     let file = aURI.QueryInterface(Ci.nsIFileURL).file;
   129     if (!file.exists() || !file.isFile())
   130       return "";
   132     let data = "";
   133     let fis = Cc["@mozilla.org/network/file-input-stream;1"].
   134               createInstance(Ci.nsIFileInputStream);
   135     try {
   136       fis.init(file, -1, -1, false);
   137       data = NetUtil.readInputStreamToString(fis, fis.available());
   138     } finally {
   139       fis.close();
   140     }
   141     return data;
   142   },
   144   /**
   145   * Detects if there were any instructions of a specified type in a given
   146   * chrome manifest.
   147   *
   148   * @param  aManifest
   149   *         Manifest data, as returned by ChromeManifestParser.parseSync().
   150   * @param  aType
   151   *         Instruction type to filter by.
   152   * @return True if any matching instructions were found in the manifest.
   153   */
   154   hasType: function CMP_hasType(aManifest, aType) {
   155     return aManifest.some(function hasType_matchEntryType(aEntry) {
   156       return aEntry.type == aType;
   157     });
   158   }
   159 };

mercurial