diff options
Diffstat (limited to 'tools/addon-sdk-1.12/lib/sdk/io')
-rw-r--r-- | tools/addon-sdk-1.12/lib/sdk/io/byte-streams.js | 106 | ||||
-rw-r--r-- | tools/addon-sdk-1.12/lib/sdk/io/data.js | 84 | ||||
-rw-r--r-- | tools/addon-sdk-1.12/lib/sdk/io/file.js | 196 | ||||
-rw-r--r-- | tools/addon-sdk-1.12/lib/sdk/io/text-streams.js | 244 |
4 files changed, 630 insertions, 0 deletions
diff --git a/tools/addon-sdk-1.12/lib/sdk/io/byte-streams.js b/tools/addon-sdk-1.12/lib/sdk/io/byte-streams.js new file mode 100644 index 0000000..27b1802 --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/io/byte-streams.js @@ -0,0 +1,106 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim:set ts=2 sw=2 sts=2 et filetype=javascript + * 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/tools/addon-sdk-1.12/lib/sdk/io/data.js b/tools/addon-sdk-1.12/lib/sdk/io/data.js new file mode 100644 index 0000000..9dd7246 --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/io/data.js @@ -0,0 +1,84 @@ +/* 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": "unstable" +}; + +const { Cc, Ci, Cu } = require("chrome"); +const base64 = require("../base64"); + +const IOService = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService); + +const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm"); +const FaviconService = Cc["@mozilla.org/browser/favicon-service;1"]. + getService(Ci.nsIFaviconService); +const { deprecateFunction, deprecatedUsage } = require("../util/deprecate"); + +const PNG_B64 = "data:image/png;base64,"; +const DEF_FAVICON_URI = "chrome://mozapps/skin/places/defaultFavicon.png"; +let DEF_FAVICON = null; + +/** + * Takes URI of the page and returns associated favicon URI. + * If page under passed uri has no favicon then base64 encoded data URI of + * default faveicon is returned. + * @param {String} uri + * @returns {String} + */ +exports.getFaviconURIForLocation = function getFaviconURIForLocation(uri) { + let pageURI = NetUtil.newURI(uri); + try { + return FaviconService.getFaviconDataAsDataURL( + FaviconService.getFaviconForPage(pageURI)); + } + catch(e) { + if (!DEF_FAVICON) { + DEF_FAVICON = PNG_B64 + + base64.encode(getChromeURIContent(DEF_FAVICON_URI)); + } + return DEF_FAVICON; + } +} + +/** + * Takes chrome URI and returns content under that URI. + * @param {String} chromeURI + * @returns {String} + */ +function getChromeURIContent(chromeURI) { + let channel = IOService.newChannel(chromeURI, null, null); + let input = channel.open(); + let stream = Cc["@mozilla.org/binaryinputstream;1"]. + createInstance(Ci.nsIBinaryInputStream); + stream.setInputStream(input); + let content = stream.readBytes(input.available()); + stream.close(); + input.close(); + return content; +} +exports.getChromeURIContent = function deprecated_getChromeURIContent() { + deprecatedUsage( + 'getChromeURIContent is deprecated, ' + + 'please use require("sdk/net/url").readURI instead.' + ); +}; + +/** + * Creates a base-64 encoded ASCII string from a string of binary data. + */ +exports.base64Encode = deprecateFunction(base64.encode, + 'base64Encode is deprecated, ' + + 'please use require("sdk/base64").encode instead.' +); +/** + * Decodes a string of data which has been encoded using base-64 encoding. + */ +exports.base64Decode = deprecateFunction(base64.decode, + 'base64Dencode is deprecated, ' + + 'please use require("sdk/base64").decode instead.' +); diff --git a/tools/addon-sdk-1.12/lib/sdk/io/file.js b/tools/addon-sdk-1.12/lib/sdk/io/file.js new file mode 100644 index 0000000..735edea --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/io/file.js @@ -0,0 +1,196 @@ +/* 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,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 = parseInt("0644"); // 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, parseInt("0755")); // 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/tools/addon-sdk-1.12/lib/sdk/io/text-streams.js b/tools/addon-sdk-1.12/lib/sdk/io/text-streams.js new file mode 100644 index 0000000..0aa469d --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/io/text-streams.js @@ -0,0 +1,244 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim:set ts=2 sw=2 sts=2 et filetype=javascript + * 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"); +var NetUtil = {}; +Cu.import("resource://gre/modules/NetUtil.jsm", NetUtil); +NetUtil = NetUtil.NetUtil; + +// 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"; + +exports.TextReader = TextReader; +exports.TextWriter = TextWriter; + +/** + * 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) { + const self = this; + 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; + }; +} + +/** + * 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) { + const self = this; + 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, function (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(self, err); + } + catch (exc) { + console.exception(exc); + } + } + }); + }; +} + +// 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) { + const self = this; + this.rawStream = rawStream; + this.opened = true; + + /** + * True iff the stream is closed. + */ + stream.__defineGetter__("closed", function stream_closed() { + return !self.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 = 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() { + // 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; +} |