summaryrefslogtreecommitdiff
path: root/data/extensions/spyblock@gnu.org/lib/legacyIO.js
diff options
context:
space:
mode:
Diffstat (limited to 'data/extensions/spyblock@gnu.org/lib/legacyIO.js')
-rw-r--r--data/extensions/spyblock@gnu.org/lib/legacyIO.js335
1 files changed, 335 insertions, 0 deletions
diff --git a/data/extensions/spyblock@gnu.org/lib/legacyIO.js b/data/extensions/spyblock@gnu.org/lib/legacyIO.js
new file mode 100644
index 0000000..5549d96
--- /dev/null
+++ b/data/extensions/spyblock@gnu.org/lib/legacyIO.js
@@ -0,0 +1,335 @@
+/*
+ * This file is part of Adblock Plus <https://adblockplus.org/>,
+ * Copyright (C) 2006-2017 eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @fileOverview Module containing file I/O helpers.
+ */
+
+let {Services} = Cu.import("resource://gre/modules/Services.jsm", null);
+let {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", null);
+let {OS} = Cu.import("resource://gre/modules/osfile.jsm", null);
+let {Task} = Cu.import("resource://gre/modules/Task.jsm", null);
+
+let {Prefs} = require("prefs");
+let {Utils} = require("utils");
+
+let firstRead = true;
+const BUFFER_SIZE = 0x80000; // 512kB
+
+let IO = exports.IO =
+{
+ /**
+ * Retrieves the platform-dependent line break string.
+ */
+ get lineBreak()
+ {
+ let lineBreak = (Services.appinfo.OS == "WINNT" ? "\r\n" : "\n");
+ Object.defineProperty(this, "lineBreak", {value: lineBreak});
+ return lineBreak;
+ },
+
+ /**
+ * Tries to interpret a file path as an absolute path or a path relative to
+ * user's profile. Returns a file or null on failure.
+ */
+ resolveFilePath: function(/**String*/ path) /**nsIFile*/
+ {
+ if (!path)
+ return null;
+
+ try {
+ // Assume an absolute path first
+ return new FileUtils.File(path);
+ } catch (e) {}
+
+ try {
+ // Try relative path now
+ return FileUtils.getFile("ProfD", path.split("/"));
+ } catch (e) {}
+
+ return null;
+ },
+
+ /**
+ * Reads strings from a file asynchronously, calls listener.process() with
+ * each line read and with a null parameter once the read operation is done.
+ * The callback will be called when the operation is done.
+ */
+ readFromFile: function(/**nsIFile*/ file, /**Object*/ listener, /**Function*/ callback)
+ {
+ try
+ {
+ let processing = false;
+ let buffer = "";
+ let loaded = false;
+ let error = null;
+
+ let onProgress = function*(data)
+ {
+ let index = (processing ? -1 : Math.max(data.lastIndexOf("\n"), data.lastIndexOf("\r")));
+ if (index >= 0)
+ {
+ // Protect against reentrance in case the listener processes events.
+ processing = true;
+ try
+ {
+ let oldBuffer = buffer;
+ buffer = data.substr(index + 1);
+ data = data.substr(0, index + 1);
+ let lines = data.split(/[\r\n]+/);
+ lines.pop();
+ lines[0] = oldBuffer + lines[0];
+ for (let i = 0; i < lines.length; i++)
+ listener.process(lines[i]);
+ }
+ finally
+ {
+ processing = false;
+ data = buffer;
+ buffer = "";
+ yield* onProgress(data);
+
+ if (loaded)
+ {
+ loaded = false;
+ onSuccess();
+ }
+
+ if (error)
+ {
+ let param = error;
+ error = null;
+ onError(param);
+ }
+ }
+ }
+ else
+ buffer += data;
+ };
+
+ let onSuccess = function()
+ {
+ if (processing)
+ {
+ // Still processing data, delay processing this event.
+ loaded = true;
+ return;
+ }
+
+ // We are ignoring return value of listener.process() here because
+ // turning this callback into a generator would be complicated, and
+ // delaying isn't really necessary for the last two calls.
+ if (buffer !== "")
+ listener.process(buffer);
+ listener.process(null);
+
+ callback(null);
+ };
+
+ let onError = function(e)
+ {
+ if (processing)
+ {
+ // Still processing data, delay processing this event.
+ error = e;
+ return;
+ }
+
+ callback(e);
+ };
+
+ let decoder = new TextDecoder();
+ Task.spawn(function*()
+ {
+ if (firstRead && Services.vc.compare(Utils.platformVersion, "23.0a1") <= 0)
+ {
+ // See https://issues.adblockplus.org/ticket/530 - the first file
+ // opened cannot be closed due to Gecko bug 858723. Make sure that
+ // our patterns.ini file doesn't stay locked by opening a dummy file
+ // first.
+ try
+ {
+ let dummyPath = IO.resolveFilePath(Prefs.data_directory + "/dummy").path;
+ let dummy = yield OS.File.open(dummyPath, {write: true, truncate: true});
+ yield dummy.close();
+ }
+ catch (e)
+ {
+ // Dummy might be locked already, we don't care
+ }
+ }
+ firstRead = false;
+
+ let f = yield OS.File.open(file.path, {read: true});
+ while (true)
+ {
+ let array = yield f.read(BUFFER_SIZE);
+ if (!array.length)
+ break;
+
+ let data = decoder.decode(array, {stream: true});
+ yield* onProgress(data);
+ }
+ yield f.close();
+ }.bind(this)).then(onSuccess, onError);
+ }
+ catch (e)
+ {
+ callback(e);
+ }
+ },
+
+ /**
+ * Writes string data to a file in UTF-8 format asynchronously. The callback
+ * will be called when the write operation is done.
+ */
+ writeToFile: function(/**nsIFile*/ file, /**Iterator*/ data, /**Function*/ callback)
+ {
+ try
+ {
+ let encoder = new TextEncoder();
+
+ Task.spawn(function*()
+ {
+ // This mimics OS.File.writeAtomic() but writes in chunks.
+ let tmpPath = file.path + ".tmp";
+ let f = yield OS.File.open(tmpPath, {write: true, truncate: true});
+
+ let buf = [];
+ let bufLen = 0;
+ let lineBreak = this.lineBreak;
+
+ function writeChunk()
+ {
+ let array = encoder.encode(buf.join(lineBreak) + lineBreak);
+ buf = [];
+ bufLen = 0;
+ return f.write(array);
+ }
+
+ for (let line of data)
+ {
+ buf.push(line);
+ bufLen += line.length;
+ if (bufLen >= BUFFER_SIZE)
+ yield writeChunk();
+ }
+
+ if (bufLen)
+ yield writeChunk();
+
+ // OS.File.flush() isn't exposed prior to Gecko 27, see bug 912457.
+ if (typeof f.flush == "function")
+ yield f.flush();
+ yield f.close();
+ yield OS.File.move(tmpPath, file.path, {noCopy: true});
+ }.bind(this)).then(callback.bind(null, null), callback);
+ }
+ catch (e)
+ {
+ callback(e);
+ }
+ },
+
+ /**
+ * Copies a file asynchronously. The callback will be called when the copy
+ * operation is done.
+ */
+ copyFile: function(/**nsIFile*/ fromFile, /**nsIFile*/ toFile, /**Function*/ callback)
+ {
+ try
+ {
+ let promise = OS.File.copy(fromFile.path, toFile.path);
+ promise.then(callback.bind(null, null), callback);
+ }
+ catch (e)
+ {
+ callback(e);
+ }
+ },
+
+ /**
+ * Renames a file within the same directory, will call callback when done.
+ */
+ renameFile: function(/**nsIFile*/ fromFile, /**String*/ newName, /**Function*/ callback)
+ {
+ try
+ {
+ let toFile = fromFile.clone();
+ toFile.leafName = newName;
+ let promise = OS.File.move(fromFile.path, toFile.path);
+ promise.then(callback.bind(null, null), callback);
+ }
+ catch(e)
+ {
+ callback(e);
+ }
+ },
+
+ /**
+ * Removes a file, will call callback when done.
+ */
+ removeFile: function(/**nsIFile*/ file, /**Function*/ callback)
+ {
+ try
+ {
+ let promise = OS.File.remove(file.path);
+ promise.then(callback.bind(null, null), callback);
+ }
+ catch(e)
+ {
+ callback(e);
+ }
+ },
+
+ /**
+ * Gets file information such as whether the file exists.
+ */
+ statFile: function(/**nsIFile*/ file, /**Function*/ callback)
+ {
+ try
+ {
+ let promise = OS.File.stat(file.path);
+ promise.then(function onSuccess(info)
+ {
+ callback(null, {
+ exists: true,
+ isDirectory: info.isDir,
+ isFile: !info.isDir,
+ lastModified: info.lastModificationDate.getTime()
+ });
+ }, function onError(e)
+ {
+ if (e.becauseNoSuchFile)
+ {
+ callback(null, {
+ exists: false,
+ isDirectory: false,
+ isFile: false,
+ lastModified: 0
+ });
+ }
+ else
+ callback(e);
+ });
+ }
+ catch(e)
+ {
+ callback(e);
+ }
+ }
+}