mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-19 23:07:33 +09:00
Move Add-on SDK source to toolkit/jetpack
This commit is contained in:
parent
0a03067ee3
commit
23b67db438
993 changed files with 484 additions and 100663 deletions
|
|
@ -1,984 +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";
|
||||
|
||||
module.metadata = {
|
||||
"stability": "experimental"
|
||||
};
|
||||
|
||||
const { Cc, Ci, CC } = require("chrome");
|
||||
|
||||
const { setTimeout } = require("../timers");
|
||||
const { Stream, InputStream, OutputStream } = require("./stream");
|
||||
const { emit, on } = require("../event/core");
|
||||
const { Buffer } = require("./buffer");
|
||||
const { ns } = require("../core/namespace");
|
||||
const { Class } = require("../core/heritage");
|
||||
|
||||
|
||||
const nsILocalFile = CC("@mozilla.org/file/local;1", "nsILocalFile",
|
||||
"initWithPath");
|
||||
const FileOutputStream = CC("@mozilla.org/network/file-output-stream;1",
|
||||
"nsIFileOutputStream", "init");
|
||||
const FileInputStream = CC("@mozilla.org/network/file-input-stream;1",
|
||||
"nsIFileInputStream", "init");
|
||||
const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
|
||||
"nsIBinaryInputStream", "setInputStream");
|
||||
const BinaryOutputStream = CC("@mozilla.org/binaryoutputstream;1",
|
||||
"nsIBinaryOutputStream", "setOutputStream");
|
||||
const StreamPump = CC("@mozilla.org/network/input-stream-pump;1",
|
||||
"nsIInputStreamPump", "init");
|
||||
|
||||
const { createOutputTransport, createInputTransport } =
|
||||
Cc["@mozilla.org/network/stream-transport-service;1"].
|
||||
getService(Ci.nsIStreamTransportService);
|
||||
|
||||
const { OPEN_UNBUFFERED } = Ci.nsITransport;
|
||||
|
||||
|
||||
const { REOPEN_ON_REWIND, DEFER_OPEN } = Ci.nsIFileInputStream;
|
||||
const { DIRECTORY_TYPE, NORMAL_FILE_TYPE } = Ci.nsIFile;
|
||||
const { NS_SEEK_SET, NS_SEEK_CUR, NS_SEEK_END } = Ci.nsISeekableStream;
|
||||
|
||||
const FILE_PERMISSION = 0o666;
|
||||
const PR_UINT32_MAX = 0xfffffff;
|
||||
// Values taken from:
|
||||
// http://mxr.mozilla.org/mozilla-central/source/nsprpub/pr/include/prio.h#615
|
||||
const PR_RDONLY = 0x01;
|
||||
const PR_WRONLY = 0x02;
|
||||
const PR_RDWR = 0x04;
|
||||
const PR_CREATE_FILE = 0x08;
|
||||
const PR_APPEND = 0x10;
|
||||
const PR_TRUNCATE = 0x20;
|
||||
const PR_SYNC = 0x40;
|
||||
const PR_EXCL = 0x80;
|
||||
|
||||
const FLAGS = {
|
||||
"r": PR_RDONLY,
|
||||
"r+": PR_RDWR,
|
||||
"w": PR_CREATE_FILE | PR_TRUNCATE | PR_WRONLY,
|
||||
"w+": PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR,
|
||||
"a": PR_APPEND | PR_CREATE_FILE | PR_WRONLY,
|
||||
"a+": PR_APPEND | PR_CREATE_FILE | PR_RDWR
|
||||
};
|
||||
|
||||
function accessor() {
|
||||
let map = new WeakMap();
|
||||
return function(fd, value) {
|
||||
if (value === null) map.delete(fd);
|
||||
if (value !== undefined) map.set(fd, value);
|
||||
return map.get(fd);
|
||||
}
|
||||
}
|
||||
|
||||
var nsIFile = accessor();
|
||||
var nsIFileInputStream = accessor();
|
||||
var nsIFileOutputStream = accessor();
|
||||
var nsIBinaryInputStream = accessor();
|
||||
var nsIBinaryOutputStream = accessor();
|
||||
|
||||
// Just a contstant object used to signal that all of the file
|
||||
// needs to be read.
|
||||
const ALL = new String("Read all of the file");
|
||||
|
||||
function isWritable(mode) {
|
||||
return !!(mode & PR_WRONLY || mode & PR_RDWR);
|
||||
}
|
||||
function isReadable(mode) {
|
||||
return !!(mode & PR_RDONLY || mode & PR_RDWR);
|
||||
}
|
||||
|
||||
function isString(value) {
|
||||
return typeof(value) === "string";
|
||||
}
|
||||
function isFunction(value) {
|
||||
return typeof(value) === "function";
|
||||
}
|
||||
|
||||
function toArray(enumerator) {
|
||||
let value = [];
|
||||
while(enumerator.hasMoreElements())
|
||||
value.push(enumerator.getNext())
|
||||
return value
|
||||
}
|
||||
|
||||
function getFileName(file) {
|
||||
return file.QueryInterface(Ci.nsIFile).leafName;
|
||||
}
|
||||
|
||||
|
||||
function remove(path, recursive) {
|
||||
let fd = new nsILocalFile(path)
|
||||
if (fd.exists()) {
|
||||
fd.remove(recursive || false);
|
||||
}
|
||||
else {
|
||||
throw FSError("remove", "ENOENT", 34, path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to convert either an octal number or string
|
||||
* into an octal number
|
||||
* 0777 => 0o777
|
||||
* "0644" => 0o644
|
||||
*/
|
||||
function Mode(mode, fallback) {
|
||||
return isString(mode) ? parseInt(mode, 8) : mode || fallback;
|
||||
}
|
||||
function Flags(flag) {
|
||||
return !isString(flag) ? flag :
|
||||
FLAGS[flag] || Error("Unknown file open flag: " + flag);
|
||||
}
|
||||
|
||||
|
||||
function FSError(op, code, errno, path, file, line) {
|
||||
let error = Error(code + ", " + op + " " + path, file, line);
|
||||
error.code = code;
|
||||
error.path = path;
|
||||
error.errno = errno;
|
||||
return error;
|
||||
}
|
||||
|
||||
const ReadStream = Class({
|
||||
extends: InputStream,
|
||||
initialize: function initialize(path, options) {
|
||||
this.position = -1;
|
||||
this.length = -1;
|
||||
this.flags = "r";
|
||||
this.mode = FILE_PERMISSION;
|
||||
this.bufferSize = 64 * 1024;
|
||||
|
||||
options = options || {};
|
||||
|
||||
if ("flags" in options && options.flags)
|
||||
this.flags = options.flags;
|
||||
if ("bufferSize" in options && options.bufferSize)
|
||||
this.bufferSize = options.bufferSize;
|
||||
if ("length" in options && options.length)
|
||||
this.length = options.length;
|
||||
if ("position" in options && options.position !== undefined)
|
||||
this.position = options.position;
|
||||
|
||||
let { flags, mode, position, length } = this;
|
||||
let fd = isString(path) ? openSync(path, flags, mode) : path;
|
||||
this.fd = fd;
|
||||
|
||||
let input = nsIFileInputStream(fd);
|
||||
// Setting a stream position, unless it"s `-1` which means current position.
|
||||
if (position >= 0)
|
||||
input.QueryInterface(Ci.nsISeekableStream).seek(NS_SEEK_SET, position);
|
||||
// We use `nsIStreamTransportService` service to transform blocking
|
||||
// file input stream into a fully asynchronous stream that can be written
|
||||
// without blocking the main thread.
|
||||
let transport = createInputTransport(input, position, length, false);
|
||||
// Open an input stream on a transport. We don"t pass flags to guarantee
|
||||
// non-blocking stream semantics. Also we use defaults for segment size &
|
||||
// count.
|
||||
InputStream.prototype.initialize.call(this, {
|
||||
asyncInputStream: transport.openInputStream(null, 0, 0)
|
||||
});
|
||||
|
||||
// Close file descriptor on end and destroy the stream.
|
||||
on(this, "end", _ => {
|
||||
this.destroy();
|
||||
emit(this, "close");
|
||||
});
|
||||
|
||||
this.read();
|
||||
},
|
||||
destroy: function() {
|
||||
closeSync(this.fd);
|
||||
InputStream.prototype.destroy.call(this);
|
||||
}
|
||||
});
|
||||
exports.ReadStream = ReadStream;
|
||||
exports.createReadStream = function createReadStream(path, options) {
|
||||
return new ReadStream(path, options);
|
||||
};
|
||||
|
||||
const WriteStream = Class({
|
||||
extends: OutputStream,
|
||||
initialize: function initialize(path, options) {
|
||||
this.drainable = true;
|
||||
this.flags = "w";
|
||||
this.position = -1;
|
||||
this.mode = FILE_PERMISSION;
|
||||
|
||||
options = options || {};
|
||||
|
||||
if ("flags" in options && options.flags)
|
||||
this.flags = options.flags;
|
||||
if ("mode" in options && options.mode)
|
||||
this.mode = options.mode;
|
||||
if ("position" in options && options.position !== undefined)
|
||||
this.position = options.position;
|
||||
|
||||
let { position, flags, mode } = this;
|
||||
// If pass was passed we create a file descriptor out of it. Otherwise
|
||||
// we just use given file descriptor.
|
||||
let fd = isString(path) ? openSync(path, flags, mode) : path;
|
||||
this.fd = fd;
|
||||
|
||||
let output = nsIFileOutputStream(fd);
|
||||
// Setting a stream position, unless it"s `-1` which means current position.
|
||||
if (position >= 0)
|
||||
output.QueryInterface(Ci.nsISeekableStream).seek(NS_SEEK_SET, position);
|
||||
// We use `nsIStreamTransportService` service to transform blocking
|
||||
// file output stream into a fully asynchronous stream that can be written
|
||||
// without blocking the main thread.
|
||||
let transport = createOutputTransport(output, position, -1, false);
|
||||
// Open an output stream on a transport. We don"t pass flags to guarantee
|
||||
// non-blocking stream semantics. Also we use defaults for segment size &
|
||||
// count.
|
||||
OutputStream.prototype.initialize.call(this, {
|
||||
asyncOutputStream: transport.openOutputStream(OPEN_UNBUFFERED, 0, 0),
|
||||
output: output
|
||||
});
|
||||
|
||||
// For write streams "finish" basically means close.
|
||||
on(this, "finish", _ => {
|
||||
this.destroy();
|
||||
emit(this, "close");
|
||||
});
|
||||
},
|
||||
destroy: function() {
|
||||
OutputStream.prototype.destroy.call(this);
|
||||
closeSync(this.fd);
|
||||
}
|
||||
});
|
||||
exports.WriteStream = WriteStream;
|
||||
exports.createWriteStream = function createWriteStream(path, options) {
|
||||
return new WriteStream(path, options);
|
||||
};
|
||||
|
||||
const Stats = Class({
|
||||
initialize: function initialize(path) {
|
||||
let file = new nsILocalFile(path);
|
||||
if (!file.exists()) throw FSError("stat", "ENOENT", 34, path);
|
||||
nsIFile(this, file);
|
||||
},
|
||||
isDirectory: function() {
|
||||
return nsIFile(this).isDirectory();
|
||||
},
|
||||
isFile: function() {
|
||||
return nsIFile(this).isFile();
|
||||
},
|
||||
isSymbolicLink: function() {
|
||||
return nsIFile(this).isSymlink();
|
||||
},
|
||||
get mode() {
|
||||
return nsIFile(this).permissions;
|
||||
},
|
||||
get size() {
|
||||
return nsIFile(this).fileSize;
|
||||
},
|
||||
get mtime() {
|
||||
return nsIFile(this).lastModifiedTime;
|
||||
},
|
||||
isBlockDevice: function() {
|
||||
return nsIFile(this).isSpecial();
|
||||
},
|
||||
isCharacterDevice: function() {
|
||||
return nsIFile(this).isSpecial();
|
||||
},
|
||||
isFIFO: function() {
|
||||
return nsIFile(this).isSpecial();
|
||||
},
|
||||
isSocket: function() {
|
||||
return nsIFile(this).isSpecial();
|
||||
},
|
||||
// non standard
|
||||
get exists() {
|
||||
return nsIFile(this).exists();
|
||||
},
|
||||
get hidden() {
|
||||
return nsIFile(this).isHidden();
|
||||
},
|
||||
get writable() {
|
||||
return nsIFile(this).isWritable();
|
||||
},
|
||||
get readable() {
|
||||
return nsIFile(this).isReadable();
|
||||
}
|
||||
});
|
||||
exports.Stats = Stats;
|
||||
|
||||
const LStats = Class({
|
||||
extends: Stats,
|
||||
get size() {
|
||||
return this.isSymbolicLink() ? nsIFile(this).fileSizeOfLink :
|
||||
nsIFile(this).fileSize;
|
||||
},
|
||||
get mtime() {
|
||||
return this.isSymbolicLink() ? nsIFile(this).lastModifiedTimeOfLink :
|
||||
nsIFile(this).lastModifiedTime;
|
||||
},
|
||||
// non standard
|
||||
get permissions() {
|
||||
return this.isSymbolicLink() ? nsIFile(this).permissionsOfLink :
|
||||
nsIFile(this).permissions;
|
||||
}
|
||||
});
|
||||
|
||||
const FStat = Class({
|
||||
extends: Stats,
|
||||
initialize: function initialize(fd) {
|
||||
nsIFile(this, nsIFile(fd));
|
||||
}
|
||||
});
|
||||
|
||||
function noop() {}
|
||||
function Async(wrapped) {
|
||||
return function (path, callback) {
|
||||
let args = Array.slice(arguments);
|
||||
callback = args.pop();
|
||||
// If node is not given a callback argument
|
||||
// it just does not calls it.
|
||||
if (typeof(callback) !== "function") {
|
||||
args.push(callback);
|
||||
callback = noop;
|
||||
}
|
||||
setTimeout(function() {
|
||||
try {
|
||||
var result = wrapped.apply(this, args);
|
||||
if (result === undefined) callback(null);
|
||||
else callback(null, result);
|
||||
} catch (error) {
|
||||
callback(error);
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Synchronous rename(2)
|
||||
*/
|
||||
function renameSync(oldPath, newPath) {
|
||||
let source = new nsILocalFile(oldPath);
|
||||
let target = new nsILocalFile(newPath);
|
||||
if (!source.exists()) throw FSError("rename", "ENOENT", 34, oldPath);
|
||||
return source.moveTo(target.parent, target.leafName);
|
||||
};
|
||||
exports.renameSync = renameSync;
|
||||
|
||||
/**
|
||||
* Asynchronous rename(2). No arguments other than a possible exception are
|
||||
* given to the completion callback.
|
||||
*/
|
||||
var rename = Async(renameSync);
|
||||
exports.rename = rename;
|
||||
|
||||
/**
|
||||
* Test whether or not the given path exists by checking with the file system.
|
||||
*/
|
||||
function existsSync(path) {
|
||||
return new nsILocalFile(path).exists();
|
||||
}
|
||||
exports.existsSync = existsSync;
|
||||
|
||||
var exists = Async(existsSync);
|
||||
exports.exists = exists;
|
||||
|
||||
/**
|
||||
* Synchronous ftruncate(2).
|
||||
*/
|
||||
function truncateSync(path, length) {
|
||||
let fd = openSync(path, "w");
|
||||
ftruncateSync(fd, length);
|
||||
closeSync(fd);
|
||||
}
|
||||
exports.truncateSync = truncateSync;
|
||||
|
||||
/**
|
||||
* Asynchronous ftruncate(2). No arguments other than a possible exception are
|
||||
* given to the completion callback.
|
||||
*/
|
||||
function truncate(path, length, callback) {
|
||||
open(path, "w", function(error, fd) {
|
||||
if (error) return callback(error);
|
||||
ftruncate(fd, length, function(error) {
|
||||
if (error) {
|
||||
closeSync(fd);
|
||||
callback(error);
|
||||
}
|
||||
else {
|
||||
close(fd, callback);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
exports.truncate = truncate;
|
||||
|
||||
function ftruncate(fd, length, callback) {
|
||||
write(fd, new Buffer(length), 0, length, 0, function(error) {
|
||||
callback(error);
|
||||
});
|
||||
}
|
||||
exports.ftruncate = ftruncate;
|
||||
|
||||
function ftruncateSync(fd, length = 0) {
|
||||
writeSync(fd, new Buffer(length), 0, length, 0);
|
||||
}
|
||||
exports.ftruncateSync = ftruncateSync;
|
||||
|
||||
function chownSync(path, uid, gid) {
|
||||
throw Error("Not implemented yet!!");
|
||||
}
|
||||
exports.chownSync = chownSync;
|
||||
|
||||
var chown = Async(chownSync);
|
||||
exports.chown = chown;
|
||||
|
||||
function lchownSync(path, uid, gid) {
|
||||
throw Error("Not implemented yet!!");
|
||||
}
|
||||
exports.lchownSync = chownSync;
|
||||
|
||||
var lchown = Async(lchown);
|
||||
exports.lchown = lchown;
|
||||
|
||||
/**
|
||||
* Synchronous chmod(2).
|
||||
*/
|
||||
function chmodSync (path, mode) {
|
||||
let file;
|
||||
try {
|
||||
file = new nsILocalFile(path);
|
||||
} catch(e) {
|
||||
throw FSError("chmod", "ENOENT", 34, path);
|
||||
}
|
||||
|
||||
file.permissions = Mode(mode);
|
||||
}
|
||||
exports.chmodSync = chmodSync;
|
||||
/**
|
||||
* Asynchronous chmod(2). No arguments other than a possible exception are
|
||||
* given to the completion callback.
|
||||
*/
|
||||
var chmod = Async(chmodSync);
|
||||
exports.chmod = chmod;
|
||||
|
||||
/**
|
||||
* Synchronous chmod(2).
|
||||
*/
|
||||
function fchmodSync(fd, mode) {
|
||||
throw Error("Not implemented yet!!");
|
||||
};
|
||||
exports.fchmodSync = fchmodSync;
|
||||
/**
|
||||
* Asynchronous chmod(2). No arguments other than a possible exception are
|
||||
* given to the completion callback.
|
||||
*/
|
||||
var fchmod = Async(fchmodSync);
|
||||
exports.fchmod = fchmod;
|
||||
|
||||
|
||||
/**
|
||||
* Synchronous stat(2). Returns an instance of `fs.Stats`
|
||||
*/
|
||||
function statSync(path) {
|
||||
return new Stats(path);
|
||||
};
|
||||
exports.statSync = statSync;
|
||||
|
||||
/**
|
||||
* Asynchronous stat(2). The callback gets two arguments (err, stats) where
|
||||
* stats is a `fs.Stats` object. It looks like this:
|
||||
*/
|
||||
var stat = Async(statSync);
|
||||
exports.stat = stat;
|
||||
|
||||
/**
|
||||
* Synchronous lstat(2). Returns an instance of `fs.Stats`.
|
||||
*/
|
||||
function lstatSync(path) {
|
||||
return new LStats(path);
|
||||
};
|
||||
exports.lstatSync = lstatSync;
|
||||
|
||||
/**
|
||||
* Asynchronous lstat(2). The callback gets two arguments (err, stats) where
|
||||
* stats is a fs.Stats object. lstat() is identical to stat(), except that if
|
||||
* path is a symbolic link, then the link itself is stat-ed, not the file that
|
||||
* it refers to.
|
||||
*/
|
||||
var lstat = Async(lstatSync);
|
||||
exports.lstat = lstat;
|
||||
|
||||
/**
|
||||
* Synchronous fstat(2). Returns an instance of `fs.Stats`.
|
||||
*/
|
||||
function fstatSync(fd) {
|
||||
return new FStat(fd);
|
||||
};
|
||||
exports.fstatSync = fstatSync;
|
||||
|
||||
/**
|
||||
* Asynchronous fstat(2). The callback gets two arguments (err, stats) where
|
||||
* stats is a fs.Stats object.
|
||||
*/
|
||||
var fstat = Async(fstatSync);
|
||||
exports.fstat = fstat;
|
||||
|
||||
/**
|
||||
* Synchronous link(2).
|
||||
*/
|
||||
function linkSync(source, target) {
|
||||
throw Error("Not implemented yet!!");
|
||||
};
|
||||
exports.linkSync = linkSync;
|
||||
|
||||
/**
|
||||
* Asynchronous link(2). No arguments other than a possible exception are given
|
||||
* to the completion callback.
|
||||
*/
|
||||
var link = Async(linkSync);
|
||||
exports.link = link;
|
||||
|
||||
/**
|
||||
* Synchronous symlink(2).
|
||||
*/
|
||||
function symlinkSync(source, target) {
|
||||
throw Error("Not implemented yet!!");
|
||||
};
|
||||
exports.symlinkSync = symlinkSync;
|
||||
|
||||
/**
|
||||
* Asynchronous symlink(2). No arguments other than a possible exception are
|
||||
* given to the completion callback.
|
||||
*/
|
||||
var symlink = Async(symlinkSync);
|
||||
exports.symlink = symlink;
|
||||
|
||||
/**
|
||||
* Synchronous readlink(2). Returns the resolved path.
|
||||
*/
|
||||
function readlinkSync(path) {
|
||||
return new nsILocalFile(path).target;
|
||||
};
|
||||
exports.readlinkSync = readlinkSync;
|
||||
|
||||
/**
|
||||
* Asynchronous readlink(2). The callback gets two arguments
|
||||
* `(error, resolvedPath)`.
|
||||
*/
|
||||
var readlink = Async(readlinkSync);
|
||||
exports.readlink = readlink;
|
||||
|
||||
/**
|
||||
* Synchronous realpath(2). Returns the resolved path.
|
||||
*/
|
||||
function realpathSync(path) {
|
||||
return new nsILocalFile(path).path;
|
||||
};
|
||||
exports.realpathSync = realpathSync;
|
||||
|
||||
/**
|
||||
* Asynchronous realpath(2). The callback gets two arguments
|
||||
* `(err, resolvedPath)`.
|
||||
*/
|
||||
var realpath = Async(realpathSync);
|
||||
exports.realpath = realpath;
|
||||
|
||||
/**
|
||||
* Synchronous unlink(2).
|
||||
*/
|
||||
var unlinkSync = remove;
|
||||
exports.unlinkSync = unlinkSync;
|
||||
|
||||
/**
|
||||
* Asynchronous unlink(2). No arguments other than a possible exception are
|
||||
* given to the completion callback.
|
||||
*/
|
||||
var unlink = Async(remove);
|
||||
exports.unlink = unlink;
|
||||
|
||||
/**
|
||||
* Synchronous rmdir(2).
|
||||
*/
|
||||
var rmdirSync = remove;
|
||||
exports.rmdirSync = rmdirSync;
|
||||
|
||||
/**
|
||||
* Asynchronous rmdir(2). No arguments other than a possible exception are
|
||||
* given to the completion callback.
|
||||
*/
|
||||
var rmdir = Async(rmdirSync);
|
||||
exports.rmdir = rmdir;
|
||||
|
||||
/**
|
||||
* Synchronous mkdir(2).
|
||||
*/
|
||||
function mkdirSync(path, mode) {
|
||||
try {
|
||||
return nsILocalFile(path).create(DIRECTORY_TYPE, Mode(mode));
|
||||
} catch (error) {
|
||||
// Adjust exception thorw to match ones thrown by node.
|
||||
if (error.name === "NS_ERROR_FILE_ALREADY_EXISTS") {
|
||||
let { fileName, lineNumber } = error;
|
||||
error = FSError("mkdir", "EEXIST", 47, path, fileName, lineNumber);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
exports.mkdirSync = mkdirSync;
|
||||
|
||||
/**
|
||||
* Asynchronous mkdir(2). No arguments other than a possible exception are
|
||||
* given to the completion callback.
|
||||
*/
|
||||
var mkdir = Async(mkdirSync);
|
||||
exports.mkdir = mkdir;
|
||||
|
||||
/**
|
||||
* Synchronous readdir(3). Returns an array of filenames excluding `"."` and
|
||||
* `".."`.
|
||||
*/
|
||||
function readdirSync(path) {
|
||||
try {
|
||||
return toArray(new nsILocalFile(path).directoryEntries).map(getFileName);
|
||||
}
|
||||
catch (error) {
|
||||
// Adjust exception thorw to match ones thrown by node.
|
||||
if (error.name === "NS_ERROR_FILE_TARGET_DOES_NOT_EXIST" ||
|
||||
error.name === "NS_ERROR_FILE_NOT_FOUND")
|
||||
{
|
||||
let { fileName, lineNumber } = error;
|
||||
error = FSError("readdir", "ENOENT", 34, path, fileName, lineNumber);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
exports.readdirSync = readdirSync;
|
||||
|
||||
/**
|
||||
* Asynchronous readdir(3). Reads the contents of a directory. The callback
|
||||
* gets two arguments `(error, files)` where `files` is an array of the names
|
||||
* of the files in the directory excluding `"."` and `".."`.
|
||||
*/
|
||||
var readdir = Async(readdirSync);
|
||||
exports.readdir = readdir;
|
||||
|
||||
/**
|
||||
* Synchronous close(2).
|
||||
*/
|
||||
function closeSync(fd) {
|
||||
let input = nsIFileInputStream(fd);
|
||||
let output = nsIFileOutputStream(fd);
|
||||
|
||||
// Closing input stream and removing reference.
|
||||
if (input) input.close();
|
||||
// Closing output stream and removing reference.
|
||||
if (output) output.close();
|
||||
|
||||
nsIFile(fd, null);
|
||||
nsIFileInputStream(fd, null);
|
||||
nsIFileOutputStream(fd, null);
|
||||
nsIBinaryInputStream(fd, null);
|
||||
nsIBinaryOutputStream(fd, null);
|
||||
};
|
||||
exports.closeSync = closeSync;
|
||||
/**
|
||||
* Asynchronous close(2). No arguments other than a possible exception are
|
||||
* given to the completion callback.
|
||||
*/
|
||||
var close = Async(closeSync);
|
||||
exports.close = close;
|
||||
|
||||
/**
|
||||
* Synchronous open(2).
|
||||
*/
|
||||
function openSync(aPath, aFlag, aMode) {
|
||||
let [ fd, flags, mode, file ] =
|
||||
[ { path: aPath }, Flags(aFlag), Mode(aMode), nsILocalFile(aPath) ];
|
||||
|
||||
nsIFile(fd, file);
|
||||
|
||||
// If trying to open file for just read that does not exists
|
||||
// need to throw exception as node does.
|
||||
if (!file.exists() && !isWritable(flags))
|
||||
throw FSError("open", "ENOENT", 34, aPath);
|
||||
|
||||
// If we want to open file in read mode we initialize input stream.
|
||||
if (isReadable(flags)) {
|
||||
let input = FileInputStream(file, flags, mode, DEFER_OPEN);
|
||||
nsIFileInputStream(fd, input);
|
||||
}
|
||||
|
||||
// If we want to open file in write mode we initialize output stream for it.
|
||||
if (isWritable(flags)) {
|
||||
let output = FileOutputStream(file, flags, mode, DEFER_OPEN);
|
||||
nsIFileOutputStream(fd, output);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
exports.openSync = openSync;
|
||||
/**
|
||||
* Asynchronous file open. See open(2). Flags can be
|
||||
* `"r", "r+", "w", "w+", "a"`, or `"a+"`. mode defaults to `0666`.
|
||||
* The callback gets two arguments `(error, fd).
|
||||
*/
|
||||
var open = Async(openSync);
|
||||
exports.open = open;
|
||||
|
||||
/**
|
||||
* Synchronous version of buffer-based fs.write(). Returns the number of bytes
|
||||
* written.
|
||||
*/
|
||||
function writeSync(fd, buffer, offset, length, position) {
|
||||
if (length + offset > buffer.length) {
|
||||
throw Error("Length is extends beyond buffer");
|
||||
}
|
||||
else if (length + offset !== buffer.length) {
|
||||
buffer = buffer.slice(offset, offset + length);
|
||||
}
|
||||
|
||||
let output = BinaryOutputStream(nsIFileOutputStream(fd));
|
||||
nsIBinaryOutputStream(fd, output);
|
||||
// We write content as a byte array as this will avoid any transcoding
|
||||
// if content was a buffer.
|
||||
output.writeByteArray(buffer.valueOf(), buffer.length);
|
||||
output.flush();
|
||||
};
|
||||
exports.writeSync = writeSync;
|
||||
|
||||
/**
|
||||
* Write buffer to the file specified by fd.
|
||||
*
|
||||
* `offset` and `length` determine the part of the buffer to be written.
|
||||
*
|
||||
* `position` refers to the offset from the beginning of the file where this
|
||||
* data should be written. If `position` is `null`, the data will be written
|
||||
* at the current position. See pwrite(2).
|
||||
*
|
||||
* The callback will be given three arguments `(error, written, buffer)` where
|
||||
* written specifies how many bytes were written into buffer.
|
||||
*
|
||||
* Note that it is unsafe to use `fs.write` multiple times on the same file
|
||||
* without waiting for the callback.
|
||||
*/
|
||||
function write(fd, buffer, offset, length, position, callback) {
|
||||
if (!Buffer.isBuffer(buffer)) {
|
||||
// (fd, data, position, encoding, callback)
|
||||
let encoding = null;
|
||||
[ position, encoding, callback ] = Array.slice(arguments, 1);
|
||||
buffer = new Buffer(String(buffer), encoding);
|
||||
offset = 0;
|
||||
} else if (length + offset > buffer.length) {
|
||||
throw Error("Length is extends beyond buffer");
|
||||
} else if (length + offset !== buffer.length) {
|
||||
buffer = buffer.slice(offset, offset + length);
|
||||
}
|
||||
|
||||
let writeStream = new WriteStream(fd, { position: position,
|
||||
length: length });
|
||||
writeStream.on("error", callback);
|
||||
writeStream.write(buffer, function onEnd() {
|
||||
writeStream.destroy();
|
||||
if (callback)
|
||||
callback(null, buffer.length, buffer);
|
||||
});
|
||||
};
|
||||
exports.write = write;
|
||||
|
||||
/**
|
||||
* Synchronous version of string-based fs.read. Returns the number of
|
||||
* bytes read.
|
||||
*/
|
||||
function readSync(fd, buffer, offset, length, position) {
|
||||
let input = nsIFileInputStream(fd);
|
||||
// Setting a stream position, unless it"s `-1` which means current position.
|
||||
if (position >= 0)
|
||||
input.QueryInterface(Ci.nsISeekableStream).seek(NS_SEEK_SET, position);
|
||||
// We use `nsIStreamTransportService` service to transform blocking
|
||||
// file input stream into a fully asynchronous stream that can be written
|
||||
// without blocking the main thread.
|
||||
let binaryInputStream = BinaryInputStream(input);
|
||||
let count = length === ALL ? binaryInputStream.available() : length;
|
||||
if (offset === 0) binaryInputStream.readArrayBuffer(count, buffer.buffer);
|
||||
else {
|
||||
let chunk = new Buffer(count);
|
||||
binaryInputStream.readArrayBuffer(count, chunk.buffer);
|
||||
chunk.copy(buffer, offset);
|
||||
}
|
||||
|
||||
return buffer.slice(offset, offset + count);
|
||||
};
|
||||
exports.readSync = readSync;
|
||||
|
||||
/**
|
||||
* Read data from the file specified by `fd`.
|
||||
*
|
||||
* `buffer` is the buffer that the data will be written to.
|
||||
* `offset` is offset within the buffer where writing will start.
|
||||
*
|
||||
* `length` is an integer specifying the number of bytes to read.
|
||||
*
|
||||
* `position` is an integer specifying where to begin reading from in the file.
|
||||
* If `position` is `null`, data will be read from the current file position.
|
||||
*
|
||||
* The callback is given the three arguments, `(error, bytesRead, buffer)`.
|
||||
*/
|
||||
function read(fd, buffer, offset, length, position, callback) {
|
||||
let bytesRead = 0;
|
||||
let readStream = new ReadStream(fd, { position: position, length: length });
|
||||
readStream.on("data", function onData(data) {
|
||||
data.copy(buffer, offset + bytesRead);
|
||||
bytesRead += data.length;
|
||||
});
|
||||
readStream.on("end", function onEnd() {
|
||||
callback(null, bytesRead, buffer);
|
||||
readStream.destroy();
|
||||
});
|
||||
};
|
||||
exports.read = read;
|
||||
|
||||
/**
|
||||
* Asynchronously reads the entire contents of a file.
|
||||
* The callback is passed two arguments `(error, data)`, where data is the
|
||||
* contents of the file.
|
||||
*/
|
||||
function readFile(path, encoding, callback) {
|
||||
if (isFunction(encoding)) {
|
||||
callback = encoding
|
||||
encoding = null
|
||||
}
|
||||
|
||||
let buffer = null;
|
||||
try {
|
||||
let readStream = new ReadStream(path);
|
||||
readStream.on("data", function(data) {
|
||||
if (!buffer) buffer = data;
|
||||
else buffer = Buffer.concat([buffer, data], 2);
|
||||
});
|
||||
readStream.on("error", function onError(error) {
|
||||
callback(error);
|
||||
});
|
||||
readStream.on("end", function onEnd() {
|
||||
// Note: Need to destroy before invoking a callback
|
||||
// so that file descriptor is released.
|
||||
readStream.destroy();
|
||||
callback(null, buffer);
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
setTimeout(callback, 0, error);
|
||||
}
|
||||
};
|
||||
exports.readFile = readFile;
|
||||
|
||||
/**
|
||||
* Synchronous version of `fs.readFile`. Returns the contents of the path.
|
||||
* If encoding is specified then this function returns a string.
|
||||
* Otherwise it returns a buffer.
|
||||
*/
|
||||
function readFileSync(path, encoding) {
|
||||
let fd = openSync(path, "r");
|
||||
let size = fstatSync(fd).size;
|
||||
let buffer = new Buffer(size);
|
||||
try {
|
||||
readSync(fd, buffer, 0, ALL, 0);
|
||||
}
|
||||
finally {
|
||||
closeSync(fd);
|
||||
}
|
||||
return buffer;
|
||||
};
|
||||
exports.readFileSync = readFileSync;
|
||||
|
||||
/**
|
||||
* Asynchronously writes data to a file, replacing the file if it already
|
||||
* exists. data can be a string or a buffer.
|
||||
*/
|
||||
function writeFile(path, content, encoding, callback) {
|
||||
if (!isString(path))
|
||||
throw new TypeError('path must be a string');
|
||||
|
||||
try {
|
||||
if (isFunction(encoding)) {
|
||||
callback = encoding
|
||||
encoding = null
|
||||
}
|
||||
if (isString(content))
|
||||
content = new Buffer(content, encoding);
|
||||
|
||||
let writeStream = new WriteStream(path);
|
||||
let error = null;
|
||||
|
||||
writeStream.end(content, function() {
|
||||
writeStream.destroy();
|
||||
callback(error);
|
||||
});
|
||||
|
||||
writeStream.on("error", function onError(reason) {
|
||||
error = reason;
|
||||
writeStream.destroy();
|
||||
});
|
||||
} catch (error) {
|
||||
callback(error);
|
||||
}
|
||||
};
|
||||
exports.writeFile = writeFile;
|
||||
|
||||
/**
|
||||
* The synchronous version of `fs.writeFile`.
|
||||
*/
|
||||
function writeFileSync(filename, data, encoding) {
|
||||
// TODO: Implement this in bug 1148209 https://bugzilla.mozilla.org/show_bug.cgi?id=1148209
|
||||
throw Error("Not implemented");
|
||||
};
|
||||
exports.writeFileSync = writeFileSync;
|
||||
|
||||
|
||||
function utimesSync(path, atime, mtime) {
|
||||
throw Error("Not implemented");
|
||||
}
|
||||
exports.utimesSync = utimesSync;
|
||||
|
||||
var utimes = Async(utimesSync);
|
||||
exports.utimes = utimes;
|
||||
|
||||
function futimesSync(fd, atime, mtime, callback) {
|
||||
throw Error("Not implemented");
|
||||
}
|
||||
exports.futimesSync = futimesSync;
|
||||
|
||||
var futimes = Async(futimesSync);
|
||||
exports.futimes = futimes;
|
||||
|
||||
function fsyncSync(fd, atime, mtime, callback) {
|
||||
throw Error("Not implemented");
|
||||
}
|
||||
exports.fsyncSync = fsyncSync;
|
||||
|
||||
var fsync = Async(fsyncSync);
|
||||
exports.fsync = fsync;
|
||||
|
||||
|
||||
/**
|
||||
* Watch for changes on filename. The callback listener will be called each
|
||||
* time the file is accessed.
|
||||
*
|
||||
* The second argument is optional. The options if provided should be an object
|
||||
* containing two members a boolean, persistent, and interval, a polling value
|
||||
* in milliseconds. The default is { persistent: true, interval: 0 }.
|
||||
*/
|
||||
function watchFile(path, options, listener) {
|
||||
throw Error("Not implemented");
|
||||
};
|
||||
exports.watchFile = watchFile;
|
||||
|
||||
|
||||
function unwatchFile(path, listener) {
|
||||
throw Error("Not implemented");
|
||||
}
|
||||
exports.unwatchFile = unwatchFile;
|
||||
|
||||
function watch(path, options, listener) {
|
||||
throw Error("Not implemented");
|
||||
}
|
||||
exports.watch = watch;
|
||||
Loading…
Add table
Add a link
Reference in a new issue