Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: lib/io.js

Issue 29408710: Issue 5050 - Make legacy extension use WebExtensions I/O (Closed) Base URL: https://hg.adblockplus.org/adblockplus
Patch Set: Created April 10, 2017, 11:23 a.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: lib/io.js
===================================================================
--- a/lib/io.js
+++ b/lib/io.js
@@ -10,330 +10,165 @@
* 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.
- */
+"use strict";
-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 {IO: LegacyIO} = require("legacyIO");
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;
- },
+let webextension = require("webextension");
+let messageID = 0;
+let messageCallbacks = new Map();
- /**
- * 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*/
+webextension.then(port =>
+{
+ port.onMessage.addListener(message =>
{
- if (!path)
- return null;
+ let {id} = message;
+ let callbacks = messageCallbacks.get(id);
+ if (callbacks)
+ {
+ messageCallbacks.delete(id);
- try {
- // Assume an absolute path first
- return new FileUtils.File(path);
- } catch (e) {}
+ if (message.success)
+ callbacks.resolve(message.result);
+ else
+ callbacks.reject(message.result);
+ }
+ });
+});
- try {
- // Try relative path now
- return FileUtils.getFile("ProfD", path.split("/"));
- } catch (e) {}
+function callWebExt(method, ...args)
+{
+ return webextension.then(port =>
+ {
+ return new Promise((resolve, reject) =>
+ {
+ let id = ++messageID;
+ messageCallbacks.set(id, {resolve, reject});
+ port.postMessage({id, method, args});
+ });
+ });
+}
- return null;
- },
+function attachCallback(promise, callback, fallback)
+{
+ promise.then(result =>
+ {
+ callback(null, result);
+ }).catch(error =>
+ {
+ if (fallback && error == "NoSuchFile")
+ fallback();
+ else
+ callback(error);
+ });
+}
+
+exports.IO =
+{
+ resolveFilePath: LegacyIO.resolveFilePath,
/**
* 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)
+ readFromFile(/**nsIFile*/ file, /**Object*/ listener, /**Function*/ callback)
{
- try
- {
- let processing = false;
- let buffer = "";
- let loaded = false;
- let error = null;
+ attachCallback(
+ callWebExt("readFromFile", file.leafName).then(contents =>
+ {
+ return new Promise((resolve, reject) =>
+ {
+ let lineIndex = 0;
- 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
+ function processBatch()
{
- 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++)
+ while (lineIndex < contents.length)
{
- let promise = listener.process(lines[i]);
- if (promise)
- yield promise;
- }
- }
- finally
- {
- processing = false;
- data = buffer;
- buffer = "";
- yield* onProgress(data);
-
- if (loaded)
- {
- loaded = false;
- onSuccess();
+ listener.process(contents[lineIndex++]);
+ if (lineIndex % 1000 == 0)
+ {
+ Utils.runAsync(processBatch);
+ return;
+ }
}
- if (error)
- {
- let param = error;
- error = null;
- onError(param);
- }
+ listener.process(null);
+ resolve();
}
- }
- 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);
- }
+ processBatch();
Wladimir Palant 2017/04/10 11:35:17 The files are processed in batches of 1000 lines i
+ });
+ }),
+ callback,
+ () => LegacyIO.readFromFile(file, listener, callback)
+ );
},
/**
* 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)
+ writeToFile(/**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);
- }
+ attachCallback(
+ callWebExt("writeToFile", file.leafName, Array.from(data)),
+ callback
+ );
},
/**
* Copies a file asynchronously. The callback will be called when the copy
* operation is done.
*/
- copyFile: function(/**nsIFile*/ fromFile, /**nsIFile*/ toFile, /**Function*/ callback)
+ copyFile(/**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);
- }
+ attachCallback(
+ callWebExt("copyFile", fromFile.leafName, toFile.leafName),
+ callback,
+ () => LegacyIO.copyFile(fromFile, toFile, callback)
+ );
},
/**
* Renames a file within the same directory, will call callback when done.
*/
- renameFile: function(/**nsIFile*/ fromFile, /**String*/ newName, /**Function*/ callback)
+ renameFile(/**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);
- }
+ attachCallback(
+ callWebExt("renameFile", fromFile.leafName, newName),
+ callback,
+ () => LegacyIO.renameFile(fromFile, newName, callback)
+ );
},
/**
* Removes a file, will call callback when done.
*/
- removeFile: function(/**nsIFile*/ file, /**Function*/ callback)
+ removeFile(/**nsIFile*/ file, /**Function*/ callback)
{
- try
- {
- let promise = OS.File.remove(file.path);
- promise.then(callback.bind(null, null), callback);
- }
- catch(e)
- {
- callback(e);
- }
+ attachCallback(
+ callWebExt("removeFile", file.leafName),
+ callback,
+ () => LegacyIO.removeFile(file, callback)
+ );
},
/**
* Gets file information such as whether the file exists.
*/
- statFile: function(/**nsIFile*/ file, /**Function*/ callback)
+ statFile(/**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);
- }
+ attachCallback(
+ callWebExt("statFile", file.leafName),
+ callback,
+ () => LegacyIO.statFile(file, callback)
+ );
}
-}
+};

Powered by Google App Engine
This is Rietveld