summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/webextensions
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2018-06-26 14:43:25 -0400
committerMatt A. Tobin <email@mattatobin.com>2018-06-26 14:43:25 -0400
commit431fe926e002cfcf8996bc17201ee885a3860c8b (patch)
tree84809b585c2547ff056d3f1c332031f9d13f5409 /toolkit/mozapps/webextensions
parent2ec54f1e95870e95e278b66b621b30a584041d72 (diff)
downloadUXP-431fe926e002cfcf8996bc17201ee885a3860c8b.tar
UXP-431fe926e002cfcf8996bc17201ee885a3860c8b.tar.gz
UXP-431fe926e002cfcf8996bc17201ee885a3860c8b.tar.lz
UXP-431fe926e002cfcf8996bc17201ee885a3860c8b.tar.xz
UXP-431fe926e002cfcf8996bc17201ee885a3860c8b.zip
[AllAM] De-duplicate ChromeManifestParser.jsm and DeferedSave.jsm
Diffstat (limited to 'toolkit/mozapps/webextensions')
-rw-r--r--toolkit/mozapps/webextensions/ChromeManifestParser.jsm157
-rw-r--r--toolkit/mozapps/webextensions/DeferredSave.jsm275
-rw-r--r--toolkit/mozapps/webextensions/moz.build4
3 files changed, 2 insertions, 434 deletions
diff --git a/toolkit/mozapps/webextensions/ChromeManifestParser.jsm b/toolkit/mozapps/webextensions/ChromeManifestParser.jsm
deleted file mode 100644
index 63f1db785..000000000
--- a/toolkit/mozapps/webextensions/ChromeManifestParser.jsm
+++ /dev/null
@@ -1,157 +0,0 @@
-/* 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);
- }
-};
diff --git a/toolkit/mozapps/webextensions/DeferredSave.jsm b/toolkit/mozapps/webextensions/DeferredSave.jsm
deleted file mode 100644
index 89f82b265..000000000
--- a/toolkit/mozapps/webextensions/DeferredSave.jsm
+++ /dev/null
@@ -1,275 +0,0 @@
-/* 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";
-
-const Cu = Components.utils;
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-
-Cu.import("resource://gre/modules/osfile.jsm");
-/* globals OS*/
-Cu.import("resource://gre/modules/Promise.jsm");
-
-// Make it possible to mock out timers for testing
-var MakeTimer = () => Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-
-this.EXPORTED_SYMBOLS = ["DeferredSave"];
-
-// If delay parameter is not provided, default is 50 milliseconds.
-const DEFAULT_SAVE_DELAY_MS = 50;
-
-Cu.import("resource://gre/modules/Log.jsm");
-// Configure a logger at the parent 'DeferredSave' level to format
-// messages for all the modules under DeferredSave.*
-const DEFERREDSAVE_PARENT_LOGGER_ID = "DeferredSave";
-var parentLogger = Log.repository.getLogger(DEFERREDSAVE_PARENT_LOGGER_ID);
-parentLogger.level = Log.Level.Warn;
-var formatter = new Log.BasicFormatter();
-// Set parent logger (and its children) to append to
-// the Javascript section of the Browser Console
-parentLogger.addAppender(new Log.ConsoleAppender(formatter));
-// Set parent logger (and its children) to
-// also append to standard out
-parentLogger.addAppender(new Log.DumpAppender(formatter));
-
-// Provide the ability to enable/disable logging
-// messages at runtime.
-// If the "extensions.logging.enabled" preference is
-// missing or 'false', messages at the WARNING and higher
-// severity should be logged to the JS console and standard error.
-// If "extensions.logging.enabled" is set to 'true', messages
-// at DEBUG and higher should go to JS console and standard error.
-Cu.import("resource://gre/modules/Services.jsm");
-
-const PREF_LOGGING_ENABLED = "extensions.logging.enabled";
-const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
-
-/**
-* Preference listener which listens for a change in the
-* "extensions.logging.enabled" preference and changes the logging level of the
-* parent 'addons' level logger accordingly.
-*/
-var PrefObserver = {
- init: function() {
- Services.prefs.addObserver(PREF_LOGGING_ENABLED, this, false);
- Services.obs.addObserver(this, "xpcom-shutdown", false);
- this.observe(null, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, PREF_LOGGING_ENABLED);
- },
-
- observe: function(aSubject, aTopic, aData) {
- if (aTopic == "xpcom-shutdown") {
- Services.prefs.removeObserver(PREF_LOGGING_ENABLED, this);
- Services.obs.removeObserver(this, "xpcom-shutdown");
- }
- else if (aTopic == NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) {
- let debugLogEnabled = false;
- try {
- debugLogEnabled = Services.prefs.getBoolPref(PREF_LOGGING_ENABLED);
- }
- catch (e) {
- }
- if (debugLogEnabled) {
- parentLogger.level = Log.Level.Debug;
- }
- else {
- parentLogger.level = Log.Level.Warn;
- }
- }
- }
-};
-
-PrefObserver.init();
-
-/**
- * A module to manage deferred, asynchronous writing of data files
- * to disk. Writing is deferred by waiting for a specified delay after
- * a request to save the data, before beginning to write. If more than
- * one save request is received during the delay, all requests are
- * fulfilled by a single write.
- *
- * @constructor
- * @param aPath
- * String representing the full path of the file where the data
- * is to be written.
- * @param aDataProvider
- * Callback function that takes no argument and returns the data to
- * be written. If aDataProvider returns an ArrayBufferView, the
- * bytes it contains are written to the file as is.
- * If aDataProvider returns a String the data are UTF-8 encoded
- * and then written to the file.
- * @param [optional] aDelay
- * The delay in milliseconds between the first saveChanges() call
- * that marks the data as needing to be saved, and when the DeferredSave
- * begins writing the data to disk. Default 50 milliseconds.
- */
-this.DeferredSave = function(aPath, aDataProvider, aDelay) {
- // Create a new logger (child of 'DeferredSave' logger)
- // for use by this particular instance of DeferredSave object
- let leafName = OS.Path.basename(aPath);
- let logger_id = DEFERREDSAVE_PARENT_LOGGER_ID + "." + leafName;
- this.logger = Log.repository.getLogger(logger_id);
-
- // @type {Deferred|null}, null when no data needs to be written
- // @resolves with the result of OS.File.writeAtomic when all writes complete
- // @rejects with the error from OS.File.writeAtomic if the write fails,
- // or with the error from aDataProvider() if that throws.
- this._pending = null;
-
- // @type {Promise}, completes when the in-progress write (if any) completes,
- // kept as a resolved promise at other times to simplify logic.
- // Because _deferredSave() always uses _writing.then() to execute
- // its next action, we don't need a special case for whether a write
- // is in progress - if the previous write is complete (and the _writing
- // promise is already resolved/rejected), _writing.then() starts
- // the next action immediately.
- //
- // @resolves with the result of OS.File.writeAtomic
- // @rejects with the error from OS.File.writeAtomic
- this._writing = Promise.resolve(0);
-
- // Are we currently waiting for a write to complete
- this.writeInProgress = false;
-
- this._path = aPath;
- this._dataProvider = aDataProvider;
-
- this._timer = null;
-
- // Some counters for telemetry
- // The total number of times the file was written
- this.totalSaves = 0;
-
- // The number of times the data became dirty while
- // another save was in progress
- this.overlappedSaves = 0;
-
- // Error returned by the most recent write (if any)
- this._lastError = null;
-
- if (aDelay && (aDelay > 0))
- this._delay = aDelay;
- else
- this._delay = DEFAULT_SAVE_DELAY_MS;
-}
-
-this.DeferredSave.prototype = {
- get dirty() {
- return this._pending || this.writeInProgress;
- },
-
- get lastError() {
- return this._lastError;
- },
-
- // Start the pending timer if data is dirty
- _startTimer: function() {
- if (!this._pending) {
- return;
- }
-
- this.logger.debug("Starting timer");
- if (!this._timer)
- this._timer = MakeTimer();
- this._timer.initWithCallback(() => this._deferredSave(),
- this._delay, Ci.nsITimer.TYPE_ONE_SHOT);
- },
-
- /**
- * Mark the current stored data dirty, and schedule a flush to disk
- * @return A Promise<integer> that will be resolved after the data is written to disk;
- * the promise is resolved with the number of bytes written.
- */
- saveChanges: function() {
- this.logger.debug("Save changes");
- if (!this._pending) {
- if (this.writeInProgress) {
- this.logger.debug("Data changed while write in progress");
- this.overlappedSaves++;
- }
- this._pending = Promise.defer();
- // Wait until the most recent write completes or fails (if it hasn't already)
- // and then restart our timer
- this._writing.then(count => this._startTimer(), error => this._startTimer());
- }
- return this._pending.promise;
- },
-
- _deferredSave: function() {
- let pending = this._pending;
- this._pending = null;
- let writing = this._writing;
- this._writing = pending.promise;
-
- // In either the success or the exception handling case, we don't need to handle
- // the error from _writing here; it's already being handled in another then()
- let toSave = null;
- try {
- toSave = this._dataProvider();
- }
- catch (e) {
- this.logger.error("Deferred save dataProvider failed", e);
- writing.then(null, error => {})
- .then(count => {
- pending.reject(e);
- });
- return;
- }
-
- writing.then(null, error => { return 0; })
- .then(count => {
- this.logger.debug("Starting write");
- this.totalSaves++;
- this.writeInProgress = true;
-
- OS.File.writeAtomic(this._path, toSave, {tmpPath: this._path + ".tmp"})
- .then(
- result => {
- this._lastError = null;
- this.writeInProgress = false;
- this.logger.debug("Write succeeded");
- pending.resolve(result);
- },
- error => {
- this._lastError = error;
- this.writeInProgress = false;
- this.logger.warn("Write failed", error);
- pending.reject(error);
- });
- });
- },
-
- /**
- * Immediately save the dirty data to disk, skipping
- * the delay of normal operation. Note that the write
- * still happens asynchronously in the worker
- * thread from OS.File.
- *
- * There are four possible situations:
- * 1) Nothing to flush
- * 2) Data is not currently being written, in-memory copy is dirty
- * 3) Data is currently being written, in-memory copy is clean
- * 4) Data is being written and in-memory copy is dirty
- *
- * @return Promise<integer> that will resolve when all in-memory data
- * has finished being flushed, returning the number of bytes
- * written. If all in-memory data is clean, completes with the
- * result of the most recent write.
- */
- flush: function() {
- // If we have pending changes, cancel our timer and set up the write
- // immediately (_deferredSave queues the write for after the most
- // recent write completes, if it hasn't already)
- if (this._pending) {
- this.logger.debug("Flush called while data is dirty");
- if (this._timer) {
- this._timer.cancel();
- this._timer = null;
- }
- this._deferredSave();
- }
-
- return this._writing;
- }
-};
diff --git a/toolkit/mozapps/webextensions/moz.build b/toolkit/mozapps/webextensions/moz.build
index d039ac68c..d437bf46d 100644
--- a/toolkit/mozapps/webextensions/moz.build
+++ b/toolkit/mozapps/webextensions/moz.build
@@ -31,9 +31,9 @@ EXTRA_PP_COMPONENTS += [
]
EXTRA_JS_MODULES += [
+ '../extensions/ChromeManifestParser.jsm',
+ '../extensions/DeferredSave.jsm',
'AddonManager.jsm',
- 'ChromeManifestParser.jsm',
- 'DeferredSave.jsm',
'LightweightThemeManager.jsm',
]