X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=common%2Fmodules%2Fstorage.jsm;fp=common%2Fmodules%2Fstorage.jsm;h=664124e883d6df3cb22d7ba826f49a09c0d19322;hb=9044153cb63835e39b9de8ec4ade237c03e3888a;hp=896daf4461189867811231b5c916dba25887f93b;hpb=70740024f9c028c1fd63e1a1850ab062ff956054;p=dactyl.git diff --git a/common/modules/storage.jsm b/common/modules/storage.jsm index 896daf4..664124e 100644 --- a/common/modules/storage.jsm +++ b/common/modules/storage.jsm @@ -2,7 +2,7 @@ // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. -"use strict"; +/* use strict */ Components.utils.import("resource://dactyl/bootstrap.jsm"); defineModule("storage", { @@ -10,17 +10,25 @@ defineModule("storage", { require: ["services", "util"] }, this); +this.lazyRequire("config", ["config"]); +this.lazyRequire("io", ["IO"]); + var win32 = /^win(32|nt)$/i.test(services.runtime.OS); var myObject = JSON.parse("{}").constructor; function loadData(name, store, type) { try { - let data = storage.infoPath.child(name).read(); - let result = JSON.parse(data); - if (result instanceof type) - return result; + let file = storage.infoPath.child(name); + if (file.exists()) { + let data = file.read(); + let result = JSON.parse(data); + if (result instanceof type) + return result; + } + } + catch (e) { + util.reportError(e); } - catch (e) {} } function saveData(obj) { @@ -71,10 +79,13 @@ var ArrayStore = Class("ArrayStore", StoreBase, { get length() this._object.length, - set: function set(index, value) { + set: function set(index, value, quiet) { var orig = this._object[index]; this._object[index] = value; - this.fireEvent("change", index); + if (!quiet) + this.fireEvent("change", index); + + return orig; }, push: function push(value) { @@ -82,12 +93,32 @@ var ArrayStore = Class("ArrayStore", StoreBase, { this.fireEvent("push", this._object.length); }, - pop: function pop(value) { - var res = this._object.pop(); - this.fireEvent("pop", this._object.length); + pop: function pop(value, ord) { + if (ord == null) + var res = this._object.pop(); + else + res = this._object.splice(ord, 1)[0]; + + this.fireEvent("pop", this._object.length, ord); + return res; + }, + + shift: function shift(value) { + var res = this._object.shift(); + this.fireEvent("shift", this._object.length); return res; }, + insert: function insert(value, ord) { + if (ord == 0) + this._object.unshift(value); + else + this._object = this._object.slice(0, ord) + .concat([value]) + .concat(this._object.slice(ord)); + this.fireEvent("insert", this._object.length, ord); + }, + truncate: function truncate(length, fromEnd) { var res = this._object.length; if (this._object.length > length) { @@ -164,16 +195,26 @@ var Storage = Module("Storage", { this[key].timer.flush(); delete this[key]; } - for (let ary in values(this.observers)) - for (let obj in values(ary)) - if (obj.ref && obj.ref.get()) - delete obj.ref.get().dactylStorageRefs; this.keys = {}; this.observers = {}; }, - exists: function exists(name) this.infoPath.child(name).exists(), + infoPath: Class.Memoize(function () + File(IO.runtimePath.replace(/,.*/, "")) + .child("info").child(config.profileName)), + + exists: function exists(key) this.infoPath.child(key).exists(), + + remove: function remove(key) { + if (this.exists(key)) { + if (this[key] && this[key].timer) + this[key].timer.flush(); + delete this[key]; + delete this.keys[key]; + this.infoPath.child(key).remove(false); + } + }, newObject: function newObject(key, constructor, params) { if (params == null || !isObject(params)) @@ -201,10 +242,9 @@ var Storage = Module("Storage", { addObserver: function addObserver(key, callback, ref) { if (ref) { - if (!ref.dactylStorageRefs) - ref.dactylStorageRefs = []; - ref.dactylStorageRefs.push(callback); - var callbackRef = Cu.getWeakReference(callback); + let refs = overlay.getData(ref, "storage-refs"); + refs.push(callback); + var callbackRef = util.weakReference(callback); } else { callbackRef = { get: function () callback }; @@ -227,7 +267,9 @@ var Storage = Module("Storage", { removeDeadObservers: function () { for (let [key, ary] in Iterator(this.observers)) { - this.observers[key] = ary = ary.filter(function (o) o.callback.get() && (!o.ref || o.ref.get() && o.ref.get().dactylStorageRefs)); + this.observers[key] = ary = ary.filter(function (o) o.callback.get() + && (!o.ref || o.ref.get() + && overlay.getData(o.ref.get(), "storage-refs", null))); if (!ary.length) delete this.observers[key]; } @@ -273,14 +315,8 @@ var Storage = Module("Storage", { skipXpcom: function skipXpcom(key, val) val instanceof Ci.nsISupports ? null : val } }, { - init: function init(dactyl, modules) { - init.superapply(this, arguments); - storage.infoPath = File(modules.IO.runtimePath.replace(/,.*/, "")) - .child("info").child(dactyl.profileName); - }, - cleanup: function (dactyl, modules, window) { - delete window.dactylStorageRefs; + overlay.setData(window, "storage-refs", null); this.removeDeadObservers(); } }); @@ -292,11 +328,18 @@ var Storage = Module("Storage", { * @param {nsIFile|string} path Expanded according to {@link IO#expandPath} * @param {boolean} checkPWD Whether to allow expansion relative to the * current directory. @default true + * @param {string} charset The charset of the file. @default File.defaultEncoding */ var File = Class("File", { - init: function (path, checkPWD) { + init: function (path, checkPWD, charset) { let file = services.File(); + if (charset) + this.charset = charset; + + if (path instanceof Ci.nsIFileURL) + path = path.file; + if (path instanceof Ci.nsIFile) file = path.clone(); else if (/file:\/\//.test(path)) @@ -320,10 +363,16 @@ var File = Class("File", { return self; }, + charset: Class.Memoize(function () File.defaultEncoding), + /** * @property {nsIFileURL} Returns the nsIFileURL object for this file. */ - get URI() services.io.newFileURI(this), + URI: Class.Memoize(function () { + let uri = services.io.newFileURI(this).QueryInterface(Ci.nsIFileURL); + uri.QueryInterface(Ci.nsIMutable).mutable = false; + return uri; + }), /** * Iterates over the objects in this directory. @@ -347,19 +396,24 @@ var File = Class("File", { return f; }, + /** + * Returns an iterator for all lines in a file. + */ + get lines() File.readLines(services.FileInStream(this, -1, 0, 0), + this.charset), + /** * Reads this file's entire contents in "text" mode and returns the * content as a string. * * @param {string} encoding The encoding from which to decode the file. - * @default options["fileencoding"] + * @default #charset * @returns {string} */ read: function (encoding) { - let ifstream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream); - ifstream.init(this, -1, 0, 0); + let ifstream = services.FileInStream(this, -1, 0, 0); - return File.readStream(ifstream, encoding); + return File.readStream(ifstream, encoding || this.charset); }, /** @@ -408,20 +462,17 @@ var File = Class("File", { * permissions if the file exists. * @default 0644 * @param {string} encoding The encoding to used to write the file. - * @default options["fileencoding"] + * @default #charset */ write: function (buf, mode, perms, encoding) { - let ofstream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream); function getStream(defaultChar) { - let stream = Cc["@mozilla.org/intl/converter-output-stream;1"].createInstance(Ci.nsIConverterOutputStream); - stream.init(ofstream, encoding, 0, defaultChar); - return stream; + return services.ConvOutStream(ofstream, encoding, 0, defaultChar); } if (buf instanceof File) buf = buf.read(); if (!encoding) - encoding = File.defaultEncoding; + encoding = this.charset; if (mode == ">>") mode = File.MODE_WRONLY | File.MODE_CREATE | File.MODE_APPEND; @@ -433,7 +484,7 @@ var File = Class("File", { if (!this.exists()) // OCREAT won't create the directory this.create(this.NORMAL_FILE_TYPE, perms); - ofstream.init(this, mode, perms, 0); + let ofstream = services.FileOutStream(this, mode, perms, 0); try { var ocstream = getStream(0); ocstream.writeString(buf); @@ -510,15 +561,15 @@ var File = Class("File", { /** * @property {string} The current platform's path separator. */ - PATH_SEP: Class.memoize(function () { + PATH_SEP: Class.Memoize(function () { let f = services.directory.get("CurProcD", Ci.nsIFile); f.append("foo"); return f.path.substr(f.parent.path.length, 1); }), - pathSplit: Class.memoize(function () util.regexp("(?:/|" + util.regexp.escape(this.PATH_SEP) + ")", "g")), + pathSplit: Class.Memoize(function () util.regexp("(?:/|" + util.regexp.escape(this.PATH_SEP) + ")", "g")), - DoesNotExist: function (path, error) ({ + DoesNotExist: function DoesNotExist(path, error) ({ path: path, exists: function () false, __noSuchMethod__: function () { throw error || Error("Does not exist"); } @@ -541,7 +592,7 @@ var File = Class("File", { * @param {boolean} relative Whether the path is relative or absolute. * @returns {string} */ - expandPath: function (path, relative) { + expandPath: function expandPath(path, relative) { function getenv(name) services.environment.get(name); // expand any $ENV vars - this is naive but so is Vim and we like to be compatible @@ -577,11 +628,18 @@ var File = Class("File", { expandPathList: function (list) list.map(this.expandPath), - readStream: function (ifstream, encoding) { + readURL: function readURL(url, encoding) { + let channel = services.io.newChannel(url, null, null); + channel.contentType = "text/plain"; + return this.readStream(channel.open(), encoding); + }, + + readStream: function readStream(ifstream, encoding) { try { - var icstream = Cc["@mozilla.org/intl/converter-input-stream;1"].createInstance(Ci.nsIConverterInputStream); - icstream.init(ifstream, encoding || File.defaultEncoding, 4096, // 4096 bytes buffering - Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); + var icstream = services.CharsetStream( + ifstream, encoding || File.defaultEncoding, 4096, // buffer size + services.CharsetStream.DEFAULT_REPLACEMENT_CHARACTER); + let buffer = []; let str = {}; while (icstream.readString(4096, str) != 0) @@ -594,7 +652,24 @@ var File = Class("File", { } }, - isAbsolutePath: function (path) { + readLines: function readLines(ifstream, encoding) { + try { + var icstream = services.CharsetStream( + ifstream, encoding || File.defaultEncoding, 4096, // buffer size + services.CharsetStream.DEFAULT_REPLACEMENT_CHARACTER); + + var value = {}; + while (icstream.readLine(value)) + yield value.value; + } + finally { + icstream.close(); + ifstream.close(); + } + }, + + + isAbsolutePath: function isAbsolutePath(path) { try { services.File().initWithPath(path); return true; @@ -604,7 +679,7 @@ var File = Class("File", { } }, - joinPaths: function (head, tail, cwd) { + joinPaths: function joinPaths(head, tail, cwd) { let path = this(head, cwd); try { // FIXME: should only expand environment vars and normalize path separators @@ -621,6 +696,6 @@ var File = Class("File", { endModule(); -// catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);} +// catch(e){ dump(e + "\n" + (e.stack || Error().stack)); Components.utils.reportError(e) } // vim: set fdm=marker sw=4 sts=4 et ft=javascript: