diff options
author | Matt A. Tobin <email@mattatobin.com> | 2018-02-09 06:46:43 -0500 |
---|---|---|
committer | Matt A. Tobin <email@mattatobin.com> | 2018-02-09 06:46:43 -0500 |
commit | ac46df8daea09899ce30dc8fd70986e258c746bf (patch) | |
tree | 2750d3125fc253fd5b0671e4bd268eff1fd97296 /addon-sdk/source/lib/sdk/io | |
parent | 8cecf8d5208f3945b35f879bba3015bb1a11bec6 (diff) | |
download | UXP-ac46df8daea09899ce30dc8fd70986e258c746bf.tar UXP-ac46df8daea09899ce30dc8fd70986e258c746bf.tar.gz UXP-ac46df8daea09899ce30dc8fd70986e258c746bf.tar.lz UXP-ac46df8daea09899ce30dc8fd70986e258c746bf.tar.xz UXP-ac46df8daea09899ce30dc8fd70986e258c746bf.zip |
Move Add-on SDK source to toolkit/jetpack
Diffstat (limited to 'addon-sdk/source/lib/sdk/io')
-rw-r--r-- | addon-sdk/source/lib/sdk/io/buffer.js | 351 | ||||
-rw-r--r-- | addon-sdk/source/lib/sdk/io/byte-streams.js | 104 | ||||
-rw-r--r-- | addon-sdk/source/lib/sdk/io/file.js | 196 | ||||
-rw-r--r-- | addon-sdk/source/lib/sdk/io/fs.js | 984 | ||||
-rw-r--r-- | addon-sdk/source/lib/sdk/io/stream.js | 440 | ||||
-rw-r--r-- | addon-sdk/source/lib/sdk/io/text-streams.js | 235 |
6 files changed, 0 insertions, 2310 deletions
diff --git a/addon-sdk/source/lib/sdk/io/buffer.js b/addon-sdk/source/lib/sdk/io/buffer.js deleted file mode 100644 index 5ea169402..000000000 --- a/addon-sdk/source/lib/sdk/io/buffer.js +++ /dev/null @@ -1,351 +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' -}; - -/* - * Encodings supported by TextEncoder/Decoder: - * utf-8, utf-16le, utf-16be - * http://encoding.spec.whatwg.org/#interface-textencoder - * - * Node however supports the following encodings: - * ascii, utf-8, utf-16le, usc2, base64, hex - */ - -const { Cu } = require('chrome'); -const { isNumber } = require('sdk/lang/type'); -const { TextEncoder, TextDecoder } = Cu.import('resource://gre/modules/commonjs/toolkit/loader.js', {}); - -exports.TextEncoder = TextEncoder; -exports.TextDecoder = TextDecoder; - -/** - * Use WeakMaps to work around Bug 929146, which prevents us from adding - * getters or values to typed arrays - * https://bugzilla.mozilla.org/show_bug.cgi?id=929146 - */ -const parents = new WeakMap(); -const views = new WeakMap(); - -function Buffer(subject, encoding /*, bufferLength */) { - - // Allow invocation without `new` constructor - if (!(this instanceof Buffer)) - return new Buffer(subject, encoding, arguments[2]); - - var type = typeof(subject); - - switch (type) { - case 'number': - // Create typed array of the given size if number. - try { - let buffer = new Uint8Array(subject > 0 ? Math.floor(subject) : 0); - return buffer; - } catch (e) { - if (/size and count too large/.test(e.message) || - /invalid arguments/.test(e.message)) - throw new RangeError('Could not instantiate buffer: size of buffer may be too large'); - else - throw new Error('Could not instantiate buffer'); - } - break; - case 'string': - // If string encode it and use buffer for the returned Uint8Array - // to create a local patched version that acts like node buffer. - encoding = encoding || 'utf8'; - return new Uint8Array(new TextEncoder(encoding).encode(subject).buffer); - case 'object': - // This form of the constructor uses the form of - // new Uint8Array(buffer, offset, length); - // So we can instantiate a typed array within the constructor - // to inherit the appropriate properties, where both the - // `subject` and newly instantiated buffer share the same underlying - // data structure. - if (arguments.length === 3) - return new Uint8Array(subject, encoding, arguments[2]); - // If array or alike just make a copy with a local patched prototype. - else - return new Uint8Array(subject); - default: - throw new TypeError('must start with number, buffer, array or string'); - } -} -exports.Buffer = Buffer; - -// Tests if `value` is a Buffer. -Buffer.isBuffer = value => value instanceof Buffer - -// Returns true if the encoding is a valid encoding argument & false otherwise -Buffer.isEncoding = function (encoding) { - if (!encoding) return false; - try { - new TextDecoder(encoding); - } catch(e) { - return false; - } - return true; -} - -// Gives the actual byte length of a string. encoding defaults to 'utf8'. -// This is not the same as String.prototype.length since that returns the -// number of characters in a string. -Buffer.byteLength = (value, encoding = 'utf8') => - new TextEncoder(encoding).encode(value).byteLength - -// Direct copy of the nodejs's buffer implementation: -// https://github.com/joyent/node/blob/b255f4c10a80343f9ce1cee56d0288361429e214/lib/buffer.js#L146-L177 -Buffer.concat = function(list, length) { - if (!Array.isArray(list)) - throw new TypeError('Usage: Buffer.concat(list[, length])'); - - if (typeof length === 'undefined') { - length = 0; - for (var i = 0; i < list.length; i++) - length += list[i].length; - } else { - length = ~~length; - } - - if (length < 0) - length = 0; - - if (list.length === 0) - return new Buffer(0); - else if (list.length === 1) - return list[0]; - - if (length < 0) - throw new RangeError('length is not a positive number'); - - var buffer = new Buffer(length); - var pos = 0; - for (var i = 0; i < list.length; i++) { - var buf = list[i]; - buf.copy(buffer, pos); - pos += buf.length; - } - - return buffer; -}; - -// Node buffer is very much like Uint8Array although it has bunch of methods -// that typically can be used in combination with `DataView` while preserving -// access by index. Since in SDK each module has it's own set of bult-ins it -// ok to patch ours to make it nodejs Buffer compatible. -const Uint8ArraySet = Uint8Array.prototype.set -Buffer.prototype = Uint8Array.prototype; -Object.defineProperties(Buffer.prototype, { - parent: { - get: function() { return parents.get(this, undefined); } - }, - view: { - get: function () { - let view = views.get(this, undefined); - if (view) return view; - view = new DataView(this.buffer); - views.set(this, view); - return view; - } - }, - toString: { - value: function(encoding, start, end) { - encoding = !!encoding ? (encoding + '').toLowerCase() : 'utf8'; - start = Math.max(0, ~~start); - end = Math.min(this.length, end === void(0) ? this.length : ~~end); - return new TextDecoder(encoding).decode(this.subarray(start, end)); - } - }, - toJSON: { - value: function() { - return { type: 'Buffer', data: Array.slice(this, 0) }; - } - }, - get: { - value: function(offset) { - return this[offset]; - } - }, - set: { - value: function(offset, value) { this[offset] = value; } - }, - copy: { - value: function(target, offset, start, end) { - let length = this.length; - let targetLength = target.length; - offset = isNumber(offset) ? offset : 0; - start = isNumber(start) ? start : 0; - - if (start < 0) - throw new RangeError('sourceStart is outside of valid range'); - if (end < 0) - throw new RangeError('sourceEnd is outside of valid range'); - - // If sourceStart > sourceEnd, or targetStart > targetLength, - // zero bytes copied - if (start > end || - offset > targetLength - ) - return 0; - - // If `end` is not defined, or if it is defined - // but would overflow `target`, redefine `end` - // so we can copy as much as we can - if (end - start > targetLength - offset || - end == null) { - let remainingTarget = targetLength - offset; - let remainingSource = length - start; - if (remainingSource <= remainingTarget) - end = length; - else - end = start + remainingTarget; - } - - Uint8ArraySet.call(target, this.subarray(start, end), offset); - return end - start; - } - }, - slice: { - value: function(start, end) { - let length = this.length; - start = ~~start; - end = end != null ? end : length; - - if (start < 0) { - start += length; - if (start < 0) start = 0; - } else if (start > length) - start = length; - - if (end < 0) { - end += length; - if (end < 0) end = 0; - } else if (end > length) - end = length; - - if (end < start) - end = start; - - // This instantiation uses the new Uint8Array(buffer, offset, length) version - // of construction to share the same underling data structure - let buffer = new Buffer(this.buffer, start, end - start); - - // If buffer has a value, assign its parent value to the - // buffer it shares its underlying structure with. If a slice of - // a slice, then use the root structure - if (buffer.length > 0) - parents.set(buffer, this.parent || this); - - return buffer; - } - }, - write: { - value: function(string, offset, length, encoding = 'utf8') { - // write(string, encoding); - if (typeof(offset) === 'string' && Number.isNaN(parseInt(offset))) { - [offset, length, encoding] = [0, null, offset]; - } - // write(string, offset, encoding); - else if (typeof(length) === 'string') - [length, encoding] = [null, length]; - - if (offset < 0 || offset > this.length) - throw new RangeError('offset is outside of valid range'); - - offset = ~~offset; - - // Clamp length if it would overflow buffer, or if its - // undefined - if (length == null || length + offset > this.length) - length = this.length - offset; - - let buffer = new TextEncoder(encoding).encode(string); - let result = Math.min(buffer.length, length); - if (buffer.length !== length) - buffer = buffer.subarray(0, length); - - Uint8ArraySet.call(this, buffer, offset); - return result; - } - }, - fill: { - value: function fill(value, start, end) { - let length = this.length; - value = value || 0; - start = start || 0; - end = end || length; - - if (typeof(value) === 'string') - value = value.charCodeAt(0); - if (typeof(value) !== 'number' || isNaN(value)) - throw TypeError('value is not a number'); - if (end < start) - throw new RangeError('end < start'); - - // Fill 0 bytes; we're done - if (end === start) - return 0; - if (length == 0) - return 0; - - if (start < 0 || start >= length) - throw RangeError('start out of bounds'); - - if (end < 0 || end > length) - throw RangeError('end out of bounds'); - - let index = start; - while (index < end) this[index++] = value; - } - } -}); - -// Define nodejs Buffer's getter and setter functions that just proxy -// to internal DataView's equivalent methods. - -// TODO do we need to check architecture to see if it's default big/little endian? -[['readUInt16LE', 'getUint16', true], - ['readUInt16BE', 'getUint16', false], - ['readInt16LE', 'getInt16', true], - ['readInt16BE', 'getInt16', false], - ['readUInt32LE', 'getUint32', true], - ['readUInt32BE', 'getUint32', false], - ['readInt32LE', 'getInt32', true], - ['readInt32BE', 'getInt32', false], - ['readFloatLE', 'getFloat32', true], - ['readFloatBE', 'getFloat32', false], - ['readDoubleLE', 'getFloat64', true], - ['readDoubleBE', 'getFloat64', false], - ['readUInt8', 'getUint8'], - ['readInt8', 'getInt8']].forEach(([alias, name, littleEndian]) => { - Object.defineProperty(Buffer.prototype, alias, { - value: function(offset) { - return this.view[name](offset, littleEndian); - } - }); -}); - -[['writeUInt16LE', 'setUint16', true], - ['writeUInt16BE', 'setUint16', false], - ['writeInt16LE', 'setInt16', true], - ['writeInt16BE', 'setInt16', false], - ['writeUInt32LE', 'setUint32', true], - ['writeUInt32BE', 'setUint32', false], - ['writeInt32LE', 'setInt32', true], - ['writeInt32BE', 'setInt32', false], - ['writeFloatLE', 'setFloat32', true], - ['writeFloatBE', 'setFloat32', false], - ['writeDoubleLE', 'setFloat64', true], - ['writeDoubleBE', 'setFloat64', false], - ['writeUInt8', 'setUint8'], - ['writeInt8', 'setInt8']].forEach(([alias, name, littleEndian]) => { - Object.defineProperty(Buffer.prototype, alias, { - value: function(value, offset) { - return this.view[name](offset, value, littleEndian); - } - }); -}); diff --git a/addon-sdk/source/lib/sdk/io/byte-streams.js b/addon-sdk/source/lib/sdk/io/byte-streams.js deleted file mode 100644 index 6afab4369..000000000 --- a/addon-sdk/source/lib/sdk/io/byte-streams.js +++ /dev/null @@ -1,104 +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" -}; - -exports.ByteReader = ByteReader; -exports.ByteWriter = ByteWriter; - -const {Cc, Ci} = require("chrome"); - -// This just controls the maximum number of bytes we read in at one time. -const BUFFER_BYTE_LEN = 0x8000; - -function ByteReader(inputStream) { - const self = this; - - let stream = Cc["@mozilla.org/binaryinputstream;1"]. - createInstance(Ci.nsIBinaryInputStream); - stream.setInputStream(inputStream); - - let manager = new StreamManager(this, stream); - - this.read = function ByteReader_read(numBytes) { - manager.ensureOpened(); - if (typeof(numBytes) !== "number") - numBytes = Infinity; - - let data = ""; - let read = 0; - try { - while (true) { - let avail = stream.available(); - let toRead = Math.min(numBytes - read, avail, BUFFER_BYTE_LEN); - if (toRead <= 0) - break; - data += stream.readBytes(toRead); - read += toRead; - } - } - catch (err) { - throw new Error("Error reading from stream: " + err); - } - - return data; - }; -} - -function ByteWriter(outputStream) { - const self = this; - - let stream = Cc["@mozilla.org/binaryoutputstream;1"]. - createInstance(Ci.nsIBinaryOutputStream); - stream.setOutputStream(outputStream); - - let manager = new StreamManager(this, stream); - - this.write = function ByteWriter_write(str) { - manager.ensureOpened(); - try { - stream.writeBytes(str, str.length); - } - catch (err) { - throw new Error("Error writing to stream: " + err); - } - }; -} - - -// This manages the lifetime of stream, a ByteReader or ByteWriter. It defines -// closed and close() on stream and registers an unload listener that closes -// rawStream if it's still opened. It also provides ensureOpened(), which -// throws an exception if the stream is closed. -function StreamManager(stream, rawStream) { - const self = this; - this.rawStream = rawStream; - this.opened = true; - - stream.__defineGetter__("closed", function stream_closed() { - return !self.opened; - }); - - stream.close = function stream_close() { - self.ensureOpened(); - self.unload(); - }; - - require("../system/unload").ensure(this); -} - -StreamManager.prototype = { - ensureOpened: function StreamManager_ensureOpened() { - if (!this.opened) - throw new Error("The stream is closed and cannot be used."); - }, - unload: function StreamManager_unload() { - this.rawStream.close(); - this.opened = false; - } -}; diff --git a/addon-sdk/source/lib/sdk/io/file.js b/addon-sdk/source/lib/sdk/io/file.js deleted file mode 100644 index 47467df87..000000000 --- a/addon-sdk/source/lib/sdk/io/file.js +++ /dev/null @@ -1,196 +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": "deprecated" -}; - -const {Cc,Ci,Cr} = require("chrome"); -const byteStreams = require("./byte-streams"); -const textStreams = require("./text-streams"); - -// Flags passed when opening a file. See nsprpub/pr/include/prio.h. -const OPEN_FLAGS = { - RDONLY: parseInt("0x01"), - WRONLY: parseInt("0x02"), - CREATE_FILE: parseInt("0x08"), - APPEND: parseInt("0x10"), - TRUNCATE: parseInt("0x20"), - EXCL: parseInt("0x80") -}; - -var dirsvc = Cc["@mozilla.org/file/directory_service;1"] - .getService(Ci.nsIProperties); - -function MozFile(path) { - var file = Cc['@mozilla.org/file/local;1'] - .createInstance(Ci.nsILocalFile); - file.initWithPath(path); - return file; -} - -function ensureReadable(file) { - if (!file.isReadable()) - throw new Error("path is not readable: " + file.path); -} - -function ensureDir(file) { - ensureExists(file); - if (!file.isDirectory()) - throw new Error("path is not a directory: " + file.path); -} - -function ensureFile(file) { - ensureExists(file); - if (!file.isFile()) - throw new Error("path is not a file: " + file.path); -} - -function ensureExists(file) { - if (!file.exists()) - throw friendlyError(Cr.NS_ERROR_FILE_NOT_FOUND, file.path); -} - -function friendlyError(errOrResult, filename) { - var isResult = typeof(errOrResult) === "number"; - var result = isResult ? errOrResult : errOrResult.result; - switch (result) { - case Cr.NS_ERROR_FILE_NOT_FOUND: - return new Error("path does not exist: " + filename); - } - return isResult ? new Error("XPCOM error code: " + errOrResult) : errOrResult; -} - -exports.exists = function exists(filename) { - return MozFile(filename).exists(); -}; - -exports.isFile = function isFile(filename) { - return MozFile(filename).isFile(); -}; - -exports.read = function read(filename, mode) { - if (typeof(mode) !== "string") - mode = ""; - - // Ensure mode is read-only. - mode = /b/.test(mode) ? "b" : ""; - - var stream = exports.open(filename, mode); - try { - var str = stream.read(); - } - finally { - stream.close(); - } - - return str; -}; - -exports.join = function join(base) { - if (arguments.length < 2) - throw new Error("need at least 2 args"); - base = MozFile(base); - for (var i = 1; i < arguments.length; i++) - base.append(arguments[i]); - return base.path; -}; - -exports.dirname = function dirname(path) { - var parent = MozFile(path).parent; - return parent ? parent.path : ""; -}; - -exports.basename = function basename(path) { - var leafName = MozFile(path).leafName; - - // On Windows, leafName when the path is a volume letter and colon ("c:") is - // the path itself. But such a path has no basename, so we want the empty - // string. - return leafName == path ? "" : leafName; -}; - -exports.list = function list(path) { - var file = MozFile(path); - ensureDir(file); - ensureReadable(file); - - var entries = file.directoryEntries; - var entryNames = []; - while(entries.hasMoreElements()) { - var entry = entries.getNext(); - entry.QueryInterface(Ci.nsIFile); - entryNames.push(entry.leafName); - } - return entryNames; -}; - -exports.open = function open(filename, mode) { - var file = MozFile(filename); - if (typeof(mode) !== "string") - mode = ""; - - // File opened for write only. - if (/w/.test(mode)) { - if (file.exists()) - ensureFile(file); - var stream = Cc['@mozilla.org/network/file-output-stream;1']. - createInstance(Ci.nsIFileOutputStream); - var openFlags = OPEN_FLAGS.WRONLY | - OPEN_FLAGS.CREATE_FILE | - OPEN_FLAGS.TRUNCATE; - var permFlags = 0o644; // u+rw go+r - try { - stream.init(file, openFlags, permFlags, 0); - } - catch (err) { - throw friendlyError(err, filename); - } - return /b/.test(mode) ? - new byteStreams.ByteWriter(stream) : - new textStreams.TextWriter(stream); - } - - // File opened for read only, the default. - ensureFile(file); - stream = Cc['@mozilla.org/network/file-input-stream;1']. - createInstance(Ci.nsIFileInputStream); - try { - stream.init(file, OPEN_FLAGS.RDONLY, 0, 0); - } - catch (err) { - throw friendlyError(err, filename); - } - return /b/.test(mode) ? - new byteStreams.ByteReader(stream) : - new textStreams.TextReader(stream); -}; - -exports.remove = function remove(path) { - var file = MozFile(path); - ensureFile(file); - file.remove(false); -}; - -exports.mkpath = function mkpath(path) { - var file = MozFile(path); - if (!file.exists()) - file.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755); // u+rwx go+rx - else if (!file.isDirectory()) - throw new Error("The path already exists and is not a directory: " + path); -}; - -exports.rmdir = function rmdir(path) { - var file = MozFile(path); - ensureDir(file); - try { - file.remove(false); - } - catch (err) { - // Bug 566950 explains why we're not catching a specific exception here. - throw new Error("The directory is not empty: " + path); - } -}; diff --git a/addon-sdk/source/lib/sdk/io/fs.js b/addon-sdk/source/lib/sdk/io/fs.js deleted file mode 100644 index 860a884a5..000000000 --- a/addon-sdk/source/lib/sdk/io/fs.js +++ /dev/null @@ -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; diff --git a/addon-sdk/source/lib/sdk/io/stream.js b/addon-sdk/source/lib/sdk/io/stream.js deleted file mode 100644 index 0698b8e32..000000000 --- a/addon-sdk/source/lib/sdk/io/stream.js +++ /dev/null @@ -1,440 +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, Cc, Ci, Cu, Cr, components } = require("chrome"); -const { EventTarget } = require("../event/target"); -const { emit } = require("../event/core"); -const { Buffer } = require("./buffer"); -const { Class } = require("../core/heritage"); -const { setTimeout } = require("../timers"); - - -const MultiplexInputStream = CC("@mozilla.org/io/multiplex-input-stream;1", - "nsIMultiplexInputStream"); -const AsyncStreamCopier = CC("@mozilla.org/network/async-stream-copier;1", - "nsIAsyncStreamCopier", "init"); -const StringInputStream = CC("@mozilla.org/io/string-input-stream;1", - "nsIStringInputStream"); -const ArrayBufferInputStream = CC("@mozilla.org/io/arraybuffer-input-stream;1", - "nsIArrayBufferInputStream"); - -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", "setInputStream"); -const InputStreamPump = CC("@mozilla.org/network/input-stream-pump;1", - "nsIInputStreamPump", "init"); - -const threadManager = Cc["@mozilla.org/thread-manager;1"]. - getService(Ci.nsIThreadManager); - -const eventTarget = Cc["@mozilla.org/network/stream-transport-service;1"]. - getService(Ci.nsIEventTarget); - -var isFunction = value => typeof(value) === "function" - -function accessor() { - let map = new WeakMap(); - return function(target, value) { - if (value) - map.set(target, value); - return map.get(target); - } -} - -const Stream = Class({ - extends: EventTarget, - initialize: function() { - this.readable = false; - this.writable = false; - this.encoding = null; - }, - setEncoding: function setEncoding(encoding) { - this.encoding = String(encoding).toUpperCase(); - }, - pipe: function pipe(target, options) { - let source = this; - function onData(chunk) { - if (target.writable) { - if (false === target.write(chunk)) - source.pause(); - } - } - function onDrain() { - if (source.readable) - source.resume(); - } - function onEnd() { - target.end(); - } - function onPause() { - source.pause(); - } - function onResume() { - if (source.readable) - source.resume(); - } - - function cleanup() { - source.removeListener("data", onData); - target.removeListener("drain", onDrain); - source.removeListener("end", onEnd); - - target.removeListener("pause", onPause); - target.removeListener("resume", onResume); - - source.removeListener("end", cleanup); - source.removeListener("close", cleanup); - - target.removeListener("end", cleanup); - target.removeListener("close", cleanup); - } - - if (!options || options.end !== false) - target.on("end", onEnd); - - source.on("data", onData); - target.on("drain", onDrain); - target.on("resume", onResume); - target.on("pause", onPause); - - source.on("end", cleanup); - source.on("close", cleanup); - - target.on("end", cleanup); - target.on("close", cleanup); - - emit(target, "pipe", source); - }, - pause: function pause() { - emit(this, "pause"); - }, - resume: function resume() { - emit(this, "resume"); - }, - destroySoon: function destroySoon() { - this.destroy(); - } -}); -exports.Stream = Stream; - - -var nsIStreamListener = accessor(); -var nsIInputStreamPump = accessor(); -var nsIAsyncInputStream = accessor(); -var nsIBinaryInputStream = accessor(); - -const StreamListener = Class({ - initialize: function(stream) { - this.stream = stream; - }, - - // Next three methods are part of `nsIStreamListener` interface and are - // invoked by `nsIInputStreamPump.asyncRead`. - onDataAvailable: function(request, context, input, offset, count) { - let stream = this.stream; - let buffer = new ArrayBuffer(count); - nsIBinaryInputStream(stream).readArrayBuffer(count, buffer); - emit(stream, "data", new Buffer(buffer)); - }, - - // Next two methods implement `nsIRequestObserver` interface and are invoked - // by `nsIInputStreamPump.asyncRead`. - onStartRequest: function() {}, - // Called to signify the end of an asynchronous request. We only care to - // discover errors. - onStopRequest: function(request, context, status) { - let stream = this.stream; - stream.readable = false; - if (!components.isSuccessCode(status)) - emit(stream, "error", status); - else - emit(stream, "end"); - } -}); - - -const InputStream = Class({ - extends: Stream, - readable: false, - paused: false, - initialize: function initialize(options) { - let { asyncInputStream } = options; - - this.readable = true; - - let binaryInputStream = new BinaryInputStream(asyncInputStream); - let inputStreamPump = new InputStreamPump(asyncInputStream, - -1, -1, 0, 0, false); - let streamListener = new StreamListener(this); - - nsIAsyncInputStream(this, asyncInputStream); - nsIInputStreamPump(this, inputStreamPump); - nsIBinaryInputStream(this, binaryInputStream); - nsIStreamListener(this, streamListener); - - this.asyncInputStream = asyncInputStream; - this.inputStreamPump = inputStreamPump; - this.binaryInputStream = binaryInputStream; - }, - get status() { - return nsIInputStreamPump(this).status; - }, - read: function() { - nsIInputStreamPump(this).asyncRead(nsIStreamListener(this), null); - }, - pause: function pause() { - this.paused = true; - nsIInputStreamPump(this).suspend(); - emit(this, "paused"); - }, - resume: function resume() { - this.paused = false; - if (nsIInputStreamPump(this).isPending()) { - nsIInputStreamPump(this).resume(); - emit(this, "resume"); - } - }, - close: function close() { - this.readable = false; - nsIInputStreamPump(this).cancel(Cr.NS_OK); - nsIBinaryInputStream(this).close(); - nsIAsyncInputStream(this).close(); - }, - destroy: function destroy() { - this.close(); - - nsIInputStreamPump(this); - nsIAsyncInputStream(this); - nsIBinaryInputStream(this); - nsIStreamListener(this); - } -}); -exports.InputStream = InputStream; - - - -var nsIRequestObserver = accessor(); -var nsIAsyncOutputStream = accessor(); -var nsIAsyncStreamCopier = accessor(); -var nsIMultiplexInputStream = accessor(); - -const RequestObserver = Class({ - initialize: function(stream) { - this.stream = stream; - }, - // Method is part of `nsIRequestObserver` interface that is - // invoked by `nsIAsyncStreamCopier.asyncCopy`. - onStartRequest: function() {}, - // Method is part of `nsIRequestObserver` interface that is - // invoked by `nsIAsyncStreamCopier.asyncCopy`. - onStopRequest: function(request, context, status) { - let stream = this.stream; - stream.drained = true; - - // Remove copied chunk. - let multiplexInputStream = nsIMultiplexInputStream(stream); - multiplexInputStream.removeStream(0); - - // If there was an error report. - if (!components.isSuccessCode(status)) - emit(stream, "error", status); - - // If there more chunks in queue then flush them. - else if (multiplexInputStream.count) - stream.flush(); - - // If stream is still writable notify that queue has drained. - else if (stream.writable) - emit(stream, "drain"); - - // If stream is no longer writable close it. - else { - nsIAsyncStreamCopier(stream).cancel(Cr.NS_OK); - nsIMultiplexInputStream(stream).close(); - nsIAsyncOutputStream(stream).close(); - nsIAsyncOutputStream(stream).flush(); - } - } -}); - -const OutputStreamCallback = Class({ - initialize: function(stream) { - this.stream = stream; - }, - // Method is part of `nsIOutputStreamCallback` interface that - // is invoked by `nsIAsyncOutputStream.asyncWait`. It is registered - // with `WAIT_CLOSURE_ONLY` flag that overrides the default behavior, - // causing the `onOutputStreamReady` notification to be suppressed until - // the stream becomes closed. - onOutputStreamReady: function(nsIAsyncOutputStream) { - emit(this.stream, "finish"); - } -}); - -const OutputStream = Class({ - extends: Stream, - writable: false, - drained: true, - get bufferSize() { - let multiplexInputStream = nsIMultiplexInputStream(this); - return multiplexInputStream && multiplexInputStream.available(); - }, - initialize: function initialize(options) { - let { asyncOutputStream, output } = options; - this.writable = true; - - // Ensure that `nsIAsyncOutputStream` was provided. - asyncOutputStream.QueryInterface(Ci.nsIAsyncOutputStream); - - // Create a `nsIMultiplexInputStream` and `nsIAsyncStreamCopier`. Former - // is used to queue written data chunks that `asyncStreamCopier` will - // asynchronously drain into `asyncOutputStream`. - let multiplexInputStream = MultiplexInputStream(); - let asyncStreamCopier = AsyncStreamCopier(multiplexInputStream, - output || asyncOutputStream, - eventTarget, - // nsIMultiplexInputStream - // implemnts .readSegments() - true, - // nsIOutputStream may or - // may not implemnet - // .writeSegments(). - false, - // Use default buffer size. - null, - // Should not close an input. - false, - // Should not close an output. - false); - - // Create `requestObserver` implementing `nsIRequestObserver` interface - // in the constructor that's gonna be reused across several flushes. - let requestObserver = RequestObserver(this); - - - // Create observer that implements `nsIOutputStreamCallback` and register - // using `WAIT_CLOSURE_ONLY` flag. That way it will be notfied once - // `nsIAsyncOutputStream` is closed. - asyncOutputStream.asyncWait(OutputStreamCallback(this), - asyncOutputStream.WAIT_CLOSURE_ONLY, - 0, - threadManager.currentThread); - - nsIRequestObserver(this, requestObserver); - nsIAsyncOutputStream(this, asyncOutputStream); - nsIMultiplexInputStream(this, multiplexInputStream); - nsIAsyncStreamCopier(this, asyncStreamCopier); - - this.asyncOutputStream = asyncOutputStream; - this.multiplexInputStream = multiplexInputStream; - this.asyncStreamCopier = asyncStreamCopier; - }, - write: function write(content, encoding, callback) { - if (isFunction(encoding)) { - callback = encoding; - encoding = callback; - } - - // If stream is not writable we throw an error. - if (!this.writable) throw Error("stream is not writable"); - - let chunk = null; - - // If content is not a buffer then we create one out of it. - if (Buffer.isBuffer(content)) { - chunk = new ArrayBufferInputStream(); - chunk.setData(content.buffer, 0, content.length); - } - else { - chunk = new StringInputStream(); - chunk.setData(content, content.length); - } - - if (callback) - this.once("drain", callback); - - // Queue up chunk to be copied to output sync. - nsIMultiplexInputStream(this).appendStream(chunk); - this.flush(); - - return this.drained; - }, - flush: function() { - if (this.drained) { - this.drained = false; - nsIAsyncStreamCopier(this).asyncCopy(nsIRequestObserver(this), null); - } - }, - end: function end(content, encoding, callback) { - if (isFunction(content)) { - callback = content - content = callback - } - if (isFunction(encoding)) { - callback = encoding - encoding = callback - } - - // Setting a listener to "finish" event if passed. - if (isFunction(callback)) - this.once("finish", callback); - - - if (content) - this.write(content, encoding); - this.writable = false; - - // Close `asyncOutputStream` only if output has drained. If it's - // not drained than `asyncStreamCopier` is busy writing, so let - // it finish. Note that since `this.writable` is false copier will - // close `asyncOutputStream` once output drains. - if (this.drained) - nsIAsyncOutputStream(this).close(); - }, - destroy: function destroy() { - nsIAsyncOutputStream(this).close(); - nsIAsyncOutputStream(this); - nsIMultiplexInputStream(this); - nsIAsyncStreamCopier(this); - nsIRequestObserver(this); - } -}); -exports.OutputStream = OutputStream; - -const DuplexStream = Class({ - extends: Stream, - implements: [InputStream, OutputStream], - allowHalfOpen: true, - initialize: function initialize(options) { - options = options || {}; - let { readable, writable, allowHalfOpen } = options; - - InputStream.prototype.initialize.call(this, options); - OutputStream.prototype.initialize.call(this, options); - - if (readable === false) - this.readable = false; - - if (writable === false) - this.writable = false; - - if (allowHalfOpen === false) - this.allowHalfOpen = false; - - // If in a half open state and it's disabled enforce end. - this.once("end", () => { - if (!this.allowHalfOpen && (!this.readable || !this.writable)) - this.end(); - }); - }, - destroy: function destroy(error) { - InputStream.prototype.destroy.call(this); - OutputStream.prototype.destroy.call(this); - } -}); -exports.DuplexStream = DuplexStream; diff --git a/addon-sdk/source/lib/sdk/io/text-streams.js b/addon-sdk/source/lib/sdk/io/text-streams.js deleted file mode 100644 index ed4ec4972..000000000 --- a/addon-sdk/source/lib/sdk/io/text-streams.js +++ /dev/null @@ -1,235 +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, Cu, components } = require("chrome"); -const { ensure } = require("../system/unload"); -const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {}); - -// NetUtil.asyncCopy() uses this buffer length, and since we call it, for best -// performance we use it, too. -const BUFFER_BYTE_LEN = 0x8000; -const PR_UINT32_MAX = 0xffffffff; -const DEFAULT_CHARSET = "UTF-8"; - - -/** - * An input stream that reads text from a backing stream using a given text - * encoding. - * - * @param inputStream - * The stream is backed by this nsIInputStream. It must already be - * opened. - * @param charset - * Text in inputStream is expected to be in this character encoding. If - * not given, "UTF-8" is assumed. See nsICharsetConverterManager.idl for - * documentation on how to determine other valid values for this. - */ -function TextReader(inputStream, charset) { - charset = checkCharset(charset); - - let stream = Cc["@mozilla.org/intl/converter-input-stream;1"]. - createInstance(Ci.nsIConverterInputStream); - stream.init(inputStream, charset, BUFFER_BYTE_LEN, - Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); - - let manager = new StreamManager(this, stream); - - /** - * Reads a string from the stream. If the stream is closed, an exception is - * thrown. - * - * @param numChars - * The number of characters to read. If not given, the remainder of - * the stream is read. - * @return The string read. If the stream is already at EOS, returns the - * empty string. - */ - this.read = function TextReader_read(numChars) { - manager.ensureOpened(); - - let readAll = false; - if (typeof(numChars) === "number") - numChars = Math.max(numChars, 0); - else - readAll = true; - - let str = ""; - let totalRead = 0; - let chunkRead = 1; - - // Read in numChars or until EOS, whichever comes first. Note that the - // units here are characters, not bytes. - while (true) { - let chunk = {}; - let toRead = readAll ? - PR_UINT32_MAX : - Math.min(numChars - totalRead, PR_UINT32_MAX); - if (toRead <= 0 || chunkRead <= 0) - break; - - // The converter stream reads in at most BUFFER_BYTE_LEN bytes in a call - // to readString, enough to fill its byte buffer. chunkRead will be the - // number of characters encoded by the bytes in that buffer. - chunkRead = stream.readString(toRead, chunk); - str += chunk.value; - totalRead += chunkRead; - } - - return str; - }; -} -exports.TextReader = TextReader; - -/** - * A buffered output stream that writes text to a backing stream using a given - * text encoding. - * - * @param outputStream - * The stream is backed by this nsIOutputStream. It must already be - * opened. - * @param charset - * Text will be written to outputStream using this character encoding. - * If not given, "UTF-8" is assumed. See nsICharsetConverterManager.idl - * for documentation on how to determine other valid values for this. - */ -function TextWriter(outputStream, charset) { - charset = checkCharset(charset); - - let stream = outputStream; - - // Buffer outputStream if it's not already. - let ioUtils = Cc["@mozilla.org/io-util;1"].getService(Ci.nsIIOUtil); - if (!ioUtils.outputStreamIsBuffered(outputStream)) { - stream = Cc["@mozilla.org/network/buffered-output-stream;1"]. - createInstance(Ci.nsIBufferedOutputStream); - stream.init(outputStream, BUFFER_BYTE_LEN); - } - - // I'd like to use nsIConverterOutputStream. But NetUtil.asyncCopy(), which - // we use below in writeAsync(), naturally expects its sink to be an instance - // of nsIOutputStream, which nsIConverterOutputStream's only implementation is - // not. So we use uconv and manually convert all strings before writing to - // outputStream. - let uconv = Cc["@mozilla.org/intl/scriptableunicodeconverter"]. - createInstance(Ci.nsIScriptableUnicodeConverter); - uconv.charset = charset; - - let manager = new StreamManager(this, stream); - - /** - * Flushes the backing stream's buffer. - */ - this.flush = function TextWriter_flush() { - manager.ensureOpened(); - stream.flush(); - }; - - /** - * Writes a string to the stream. If the stream is closed, an exception is - * thrown. - * - * @param str - * The string to write. - */ - this.write = function TextWriter_write(str) { - manager.ensureOpened(); - let istream = uconv.convertToInputStream(str); - let len = istream.available(); - while (len > 0) { - stream.writeFrom(istream, len); - len = istream.available(); - } - istream.close(); - }; - - /** - * Writes a string on a background thread. After the write completes, the - * backing stream's buffer is flushed, and both the stream and the backing - * stream are closed, also on the background thread. If the stream is already - * closed, an exception is thrown immediately. - * - * @param str - * The string to write. - * @param callback - * An optional function. If given, it's called as callback(error) when - * the write completes. error is an Error object or undefined if there - * was no error. Inside callback, |this| is the stream object. - */ - this.writeAsync = function TextWriter_writeAsync(str, callback) { - manager.ensureOpened(); - let istream = uconv.convertToInputStream(str); - NetUtil.asyncCopy(istream, stream, (result) => { - let err = components.isSuccessCode(result) ? undefined : - new Error("An error occured while writing to the stream: " + result); - if (err) - console.error(err); - - // asyncCopy() closes its output (and input) stream. - manager.opened = false; - - if (typeof(callback) === "function") { - try { - callback.call(this, err); - } - catch (exc) { - console.exception(exc); - } - } - }); - }; -} -exports.TextWriter = TextWriter; - -// This manages the lifetime of stream, a TextReader or TextWriter. It defines -// closed and close() on stream and registers an unload listener that closes -// rawStream if it's still opened. It also provides ensureOpened(), which -// throws an exception if the stream is closed. -function StreamManager(stream, rawStream) { - this.rawStream = rawStream; - this.opened = true; - - /** - * True iff the stream is closed. - */ - stream.__defineGetter__("closed", () => !this.opened); - - /** - * Closes both the stream and its backing stream. If the stream is already - * closed, an exception is thrown. For TextWriters, this first flushes the - * backing stream's buffer. - */ - stream.close = () => { - this.ensureOpened(); - this.unload(); - }; - - ensure(this); -} - -StreamManager.prototype = { - ensureOpened: function StreamManager_ensureOpened() { - if (!this.opened) - throw new Error("The stream is closed and cannot be used."); - }, - unload: function StreamManager_unload() { - // TextWriter.writeAsync() causes rawStream to close and therefore sets - // opened to false, so check that we're still opened. - if (this.opened) { - // Calling close() on both an nsIUnicharInputStream and - // nsIBufferedOutputStream closes their backing streams. It also forces - // nsIOutputStreams to flush first. - this.rawStream.close(); - this.opened = false; - } - } -}; - -function checkCharset(charset) { - return typeof(charset) === "string" ? charset : DEFAULT_CHARSET; -} |