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

Unified Diff: lib/io.js

Issue 6726956523454464: Issue 153 - Reimplement io.js based on OS.File API (Closed)
Patch Set: Created March 21, 2014, 1:10 p.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
« no previous file with comments | « lib/filterStorage.js ('k') | metadata.gecko » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/io.js
===================================================================
--- a/lib/io.js
+++ b/lib/io.js
@@ -14,24 +14,26 @@
* 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.
*/
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/FileUtils.jsm");
-Cu.import("resource://gre/modules/NetUtil.jsm");
+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 {TimeLine} = require("timeline");
let {Utils} = require("utils");
+const BUFFER_SIZE = 0x8000; // 32kB
+
let IO = exports.IO =
{
/**
* Retrieves the platform-dependent line break string.
*/
get lineBreak()
{
let lineBreak = (Services.appinfo.OS == "WINNT" ? "\r\n" : "\n");
@@ -62,30 +64,24 @@ let IO = exports.IO =
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|nsIURI*/ file, /**Boolean*/ decode, /**Object*/ listener, /**Function*/ callback, /**String*/ timeLineID)
+ readFromFile: function(/**nsIFile*/ file, /**Boolean*/ decode, /**Object*/ listener, /**Function*/ callback, /**String*/ timeLineID)
{
try
{
let processing = false;
let buffer = "";
let loaded = false;
- let error = Cr.NS_OK;
- let uri = file instanceof Ci.nsIFile ? Services.io.newFileURI(file) : file;
- let request = new XMLHttpRequest();
- request.mozBackgroundRequest = true;
- request.open("GET", uri.spec);
- request.responseType = "moz-chunked-text";
- request.overrideMimeType("text/plain" + (decode ? "? charset=utf-8" : ""));
+ let error = null;
let onProgress = function(data)
{
if (timeLineID)
{
TimeLine.asyncStart(timeLineID);
}
@@ -110,37 +106,37 @@ let IO = exports.IO =
processing = false;
data = buffer;
buffer = "";
onProgress(data);
if (loaded)
{
loaded = false;
- onLoad();
+ onSuccess();
}
- if (error != Cr.NS_OK)
+ if (error)
{
let param = error;
- error = Cr.NS_OK;
+ error = null;
onError(param);
}
}
}
else
buffer += data;
if (timeLineID)
{
TimeLine.asyncEnd(timeLineID);
}
};
- let onLoad = function()
+ let onSuccess = function()
{
if (processing)
{
// Still processing data, delay processing this event.
loaded = true;
return;
}
@@ -157,238 +153,192 @@ let IO = exports.IO =
{
TimeLine.asyncEnd(timeLineID);
TimeLine.asyncDone(timeLineID);
}
callback(null);
};
- let onError = function(status)
+ let onError = function(e)
{
if (processing)
{
// Still processing data, delay processing this event.
- error = status;
+ error = e;
return;
}
- let e = Cc["@mozilla.org/js/xpc/Exception;1"].createInstance(Ci.nsIXPCException);
- e.initialize("File read operation failed", status, null, Components.stack, file, null);
callback(e);
if (timeLineID)
{
TimeLine.asyncDone(timeLineID);
}
};
- request.addEventListener("progress", function(event)
+ let decoder = new TextDecoder();
+ let array = new Uint8Array(BUFFER_SIZE);
+ Task.spawn(function()
{
- Utils.runAsync(onProgress.bind(this, event.target.response));
- }, false);
- request.addEventListener("load", function(event)
- {
- Utils.runAsync(onLoad.bind(this));
- }, false);
- request.addEventListener("error", function(event)
- {
- Utils.runAsync(onError.bind(this, event.target.channel.status));
- }, false);
+ let f = yield OS.File.open(file.path, {read: true});
+ let numBytes;
+ do
+ {
+ numBytes = yield f.readTo(array);
+ if (numBytes)
+ {
+ let data = decoder.decode(numBytes == BUFFER_SIZE ?
+ array :
+ array.subarray(0, numBytes), {stream: true});
+ onProgress(data);
+ }
+ } while (numBytes);
- request.send(null);
+ yield f.close();
+ }.bind(this)).then(onSuccess, onError);
}
catch (e)
{
callback(e);
}
},
+
/**
* Writes string data to a file asynchronously, optionally encodes it into
* UTF-8 first. The callback will be called when the write operation is done.
*/
writeToFile: function(/**nsIFile*/ file, /**Boolean*/ encode, /**Iterator*/ data, /**Function*/ callback, /**String*/ timeLineID)
{
try
{
- let fileStream = FileUtils.openSafeFileOutputStream(file, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE);
+ let encoder = new TextEncoder();
- let pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
- pipe.init(true, true, 0, 0x8000, null);
+ 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 outStream = pipe.outputStream;
- if (encode)
- {
- outStream = Cc["@mozilla.org/intl/converter-output-stream;1"].createInstance(Ci.nsIConverterOutputStream);
- outStream.init(pipe.outputStream, "UTF-8", 0, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
- }
-
- let copier = Cc["@mozilla.org/network/async-stream-copier;1"].createInstance(Ci.nsIAsyncStreamCopier);
- copier.init(pipe.inputStream, fileStream, null, true, false, 0x8000, true, true);
- copier.asyncCopy({
- onStartRequest: function(request, context) {},
- onStopRequest: function(request, context, result)
- {
- if (timeLineID)
- {
- TimeLine.asyncDone(timeLineID);
- }
-
- if (!Components.isSuccessCode(result))
- {
- let e = Cc["@mozilla.org/js/xpc/Exception;1"].createInstance(Ci.nsIXPCException);
- e.initialize("File write operation failed", result, null, Components.stack, file, null);
- callback(e);
- }
- else
- callback(null);
- }
- }, null);
-
- let lineBreak = this.lineBreak;
- let writeNextChunk = function()
- {
let buf = [];
let bufLen = 0;
- while (bufLen < 0x4000)
+ let lineBreak = this.lineBreak;
+
+ function writeChunk()
{
- try
- {
- let str = data.next();
- buf.push(str);
- bufLen += str.length;
- }
- catch (e)
- {
- if (e instanceof StopIteration)
- break;
- else if (typeof e == "number")
- pipe.outputStream.closeWithStatus(e);
- else if (e instanceof Ci.nsIException)
- pipe.outputStream.closeWithStatus(e.result);
- else
- {
- Cu.reportError(e);
- pipe.outputStream.closeWithStatus(Cr.NS_ERROR_FAILURE);
- }
- return;
- }
+ let array = encoder.encode(buf.join(lineBreak) + lineBreak);
+ buf = [];
+ bufLen = 0;
+ return f.write(array);
}
- pipe.outputStream.asyncWait({
- onOutputStreamReady: function()
- {
- if (timeLineID)
- {
- TimeLine.asyncStart(timeLineID);
- }
+ for (let line in data)
+ {
+ buf.push(line);
+ bufLen += line.length;
+ if (bufLen >= BUFFER_SIZE)
+ yield writeChunk();
+ }
- if (buf.length)
- {
- let str = buf.join(lineBreak) + lineBreak;
- if (encode)
- outStream.writeString(str);
- else
- outStream.write(str, str.length);
- writeNextChunk();
- }
- else
- outStream.close();
+ if (bufLen)
+ yield writeChunk();
- if (timeLineID)
- {
- TimeLine.asyncEnd(timeLineID);
- }
- }
- }, 0, 0, Services.tm.currentThread);
- };
- writeNextChunk();
+ // 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 inStream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
- inStream.init(fromFile, FileUtils.MODE_RDONLY, 0, Ci.nsIFileInputStream.DEFER_OPEN);
-
- let outStream = FileUtils.openFileOutputStream(toFile, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE);
-
- NetUtil.asyncCopy(inStream, outStream, function(result)
- {
- if (!Components.isSuccessCode(result))
- {
- let e = Cc["@mozilla.org/js/xpc/Exception;1"].createInstance(Ci.nsIXPCException);
- e.initialize("File write operation failed", result, null, Components.stack, file, null);
- callback(e);
- }
- else
- callback(null);
- });
+ 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
{
- fromFile.moveTo(null, newName);
- callback(null);
+ 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
{
- file.remove(false);
- callback(null);
+ 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 exists = file.exists();
- callback(null, {
- exists: exists,
- isDirectory: exists && file.isDirectory(),
- isFile: exists && file.isFile(),
- lastModified: exists ? file.lastModifiedTime : 0
+ 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
+ })
Felix Dahlke 2014/03/21 15:44:27 Missing semicolon.
+ }
+ else
+ callback(e);
});
}
catch(e)
{
callback(e);
}
}
}
« no previous file with comments | « lib/filterStorage.js ('k') | metadata.gecko » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld