]> git.donarmstrong.com Git - dactyl.git/blobdiff - common/modules/storage.jsm
Import r6923 from upstream hg supporting Firefox up to 22.0a1
[dactyl.git] / common / modules / storage.jsm
index 664124e883d6df3cb22d7ba826f49a09c0d19322..0f4cc06e7a83a41c0ca3e89dada9b422948c613e 100644 (file)
@@ -1,43 +1,21 @@
-// Copyright (c) 2008-2011 by Kris Maglione <maglione.k at Gmail>
+// Copyright (c) 2008-2012 Kris Maglione <maglione.k at Gmail>
 //
 // 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", {
     exports: ["File", "Storage", "storage"],
     require: ["services", "util"]
-}, this);
+});
 
-this.lazyRequire("config", ["config"]);
-this.lazyRequire("io", ["IO"]);
+lazyRequire("config", ["config"]);
+lazyRequire("io", ["IO"]);
+lazyRequire("overlay", ["overlay"]);
 
 var win32 = /^win(32|nt)$/i.test(services.runtime.OS);
 var myObject = JSON.parse("{}").constructor;
 
-function loadData(name, store, type) {
-    try {
-        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);
-    }
-}
-
-function saveData(obj) {
-    if (obj.privateData && storage.privateMode)
-        return;
-    if (obj.store && storage.infoPath)
-        storage.infoPath.child(obj.name).write(obj.serial);
-}
-
 var StoreBase = Class("StoreBase", {
     OPTIONS: ["privateData", "replacer"],
 
@@ -47,6 +25,7 @@ var StoreBase = Class("StoreBase", {
 
     init: function (name, store, load, options) {
         this._load = load;
+        this._options = options;
 
         this.__defineGetter__("store", function () store);
         this.__defineGetter__("name", function () name);
@@ -56,7 +35,14 @@ var StoreBase = Class("StoreBase", {
         this.reload();
     },
 
-    changed: function () { this.timer.tell(); },
+    clone: function (storage) {
+        let store = storage.privateMode ? false : this.store;
+        let res = this.constructor(this.name, store, this._load, this._options);
+        res.storage = storage;
+        return res;
+    },
+
+    changed: function () { this.timer && this.timer.tell(); },
 
     reload: function reload() {
         this._object = this._load() || this._constructor();
@@ -69,7 +55,7 @@ var StoreBase = Class("StoreBase", {
         storage.infoPath.child(this.name).remove(false);
     },
 
-    save: function () { saveData(this); },
+    save: function () { (self.storage || storage)._saveData(this); },
 
     __iterator__: function () Iterator(this._object)
 });
@@ -176,15 +162,24 @@ var ObjectStore = Class("ObjectStore", StoreBase, {
     }
 });
 
+var sessionGlobal = Cu.import("resource://gre/modules/Services.jsm", {})
+
 var Storage = Module("Storage", {
+    Local: function Local(dactyl, modules, window) ({
+        init: function init() {
+            this.privateMode = PrivateBrowsingUtils.isWindowPrivate(window);
+        }
+    }),
+
     alwaysReload: {},
 
-    init: function () {
+    init: function init() {
         this.cleanup();
 
-        if (services.bootstrap && !services.bootstrap.session)
-            services.bootstrap.session = {};
-        this.session = services.bootstrap ? services.bootstrap.session : {};
+        let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+        if (!Services.dactylSession)
+            Services.dactylSession = Cu.createObjectIn(sessionGlobal);
+        this.session = Services.dactylSession;
     },
 
     cleanup: function () {
@@ -200,6 +195,35 @@ var Storage = Module("Storage", {
         this.observers = {};
     },
 
+    _loadData: function loadData(name, store, type) {
+        try {
+            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);
+        }
+    },
+
+    _saveData: function saveData(obj) {
+        if (obj.privateData && storage.privateMode)
+            return;
+        if (obj.store && storage.infoPath)
+            storage.infoPath.child(obj.name).write(obj.serial);
+    },
+
+    storeForSession: function storeForSession(key, val) {
+        if (val)
+            this.session[key] = sessionGlobal.JSON.parse(JSON.stringify(val));
+        else
+            delete this.dactylSession[key];
+    },
+
     infoPath: Class.Memoize(function ()
         File(IO.runtimePath.replace(/,.*/, ""))
             .child("info").child(config.profileName)),
@@ -217,16 +241,30 @@ var Storage = Module("Storage", {
     },
 
     newObject: function newObject(key, constructor, params) {
+        let self = this;
         if (params == null || !isObject(params))
             throw Error("Invalid argument type");
 
-        if (!(key in this.keys) || params.reload || this.alwaysReload[key]) {
-            if (key in this && !(params.reload || this.alwaysReload[key]))
-                throw Error();
-            let load = function () loadData(key, params.store, params.type || myObject);
+        if (this.isLocalModule) {
+            this.globalInstance.newObject.apply(this.globalInstance, arguments);
+
+            if (!(key in this.keys) && this.privateMode && key in this.globalInstance.keys) {
+                let obj = this.globalInstance.keys[key];
+                this.keys[key] = this._privatize(obj);
+            }
+
+            return this.keys[key];
+        }
+
+        let reload = params.reload || this.alwaysReload[key];
+        if (!(key in this.keys) || reload) {
+            if (key in this && !reload)
+                throw Error("Cannot add storage key with that name.");
+
+            let load = function () self._loadData(key, params.store, params.type || myObject);
 
             this.keys[key] = new constructor(key, params.store, load, params);
-            this.keys[key].timer = new Timer(1000, 10000, function () storage.save(key));
+            this.keys[key].timer = new Timer(1000, 10000, function () self.save(key));
             this.__defineGetter__(key, function () this.keys[key]);
         }
         return this.keys[key];
@@ -249,27 +287,38 @@ var Storage = Module("Storage", {
         else {
             callbackRef = { get: function () callback };
         }
+
         this.removeDeadObservers();
+
         if (!(key in this.observers))
             this.observers[key] = [];
+
         if (!this.observers[key].some(function (o) o.callback.get() == callback))
             this.observers[key].push({ ref: ref && Cu.getWeakReference(ref), callback: callbackRef });
     },
 
     removeObserver: function (key, callback) {
         this.removeDeadObservers();
+
         if (!(key in this.observers))
             return;
+
         this.observers[key] = this.observers[key].filter(function (elem) elem.callback.get() != callback);
         if (this.observers[key].length == 0)
             delete obsevers[key];
     },
 
     removeDeadObservers: function () {
+        function filter(o) {
+            if (!o.callback.get())
+                return false;
+
+            let ref = o.ref && o.ref.get();
+            return ref && !ref.closed && overlay.getData(ref, "storage-refs", null);
+        }
+
         for (let [key, ary] in Iterator(this.observers)) {
-            this.observers[key] = ary = ary.filter(function (o) o.callback.get()
-                                                             && (!o.ref || o.ref.get()
-                                                                        && overlay.getData(o.ref.get(), "storage-refs", null)));
+            this.observers[key] = ary = ary.filter(filter);
             if (!ary.length)
                 delete this.observers[key];
         }
@@ -277,11 +326,13 @@ var Storage = Module("Storage", {
 
     fireEvent: function fireEvent(key, event, arg) {
         this.removeDeadObservers();
+
         if (key in this.observers)
             // Safe, since we have our own Array object here.
             for each (let observer in this.observers[key])
                 observer.callback.get()(key, event, arg);
-        if (key in this.keys)
+
+        if (key in this.keys && this.keys[key].timer)
             this[key].timer.tell();
     },
 
@@ -292,24 +343,39 @@ var Storage = Module("Storage", {
 
     save: function save(key) {
         if (this[key])
-            saveData(this.keys[key]);
+            this._saveData(this.keys[key]);
     },
 
     saveAll: function storeAll() {
         for each (let obj in this.keys)
-            saveData(obj);
+            this._saveData(obj);
     },
 
     _privateMode: false,
     get privateMode() this._privateMode,
-    set privateMode(val) {
-        if (val && !this._privateMode)
+    set privateMode(enabled) {
+        this._privateMode = Boolean(enabled);
+
+        if (this.isLocalModule) {
             this.saveAll();
-        if (!val && this._privateMode)
-            for (let key in this.keys)
-                this.load(key);
-        return this._privateMode = Boolean(val);
-    }
+
+            if (!enabled)
+                delete this.keys;
+            else {
+                let { keys } = this;
+                this.keys = {};
+                for (let [k, v] in Iterator(keys))
+                    this.keys[k] = this._privatize(v);
+            }
+        }
+        return this._privateMode;
+    },
+
+    _privatize: function privatize(obj) {
+        if (obj.privateData && obj.clone)
+            return obj.clone(this);
+        return obj;
+    },
 }, {
     Replacer: {
         skipXpcom: function skipXpcom(key, val) val instanceof Ci.nsISupports ? null : val
@@ -340,7 +406,7 @@ var File = Class("File", {
         if (path instanceof Ci.nsIFileURL)
             path = path.file;
 
-        if (path instanceof Ci.nsIFile)
+        if (path instanceof Ci.nsIFile || path instanceof File)
             file = path.clone();
         else if (/file:\/\//.test(path))
             file = services["file:"].getFileFromURLSpec(path);
@@ -358,9 +424,8 @@ var File = Class("File", {
                 return File.DoesNotExist(path, e);
             }
         }
-        let self = XPCSafeJSObjectWrapper(file.QueryInterface(Ci.nsILocalFile));
-        self.__proto__ = this;
-        return self;
+        this.file = file.QueryInterface(Ci.nsILocalFile);
+        return this;
     },
 
     charset: Class.Memoize(function () File.defaultEncoding),
@@ -369,7 +434,8 @@ var File = Class("File", {
      * @property {nsIFileURL} Returns the nsIFileURL object for this file.
      */
     URI: Class.Memoize(function () {
-        let uri = services.io.newFileURI(this).QueryInterface(Ci.nsIFileURL);
+        let uri = services.io.newFileURI(this.file)
+                          .QueryInterface(Ci.nsIFileURL);
         uri.QueryInterface(Ci.nsIMutable).mutable = false;
         return uri;
     }),
@@ -377,7 +443,7 @@ var File = Class("File", {
     /**
      * Iterates over the objects in this directory.
      */
-    iterDirectory: function () {
+    iterDirectory: function iterDirectory() {
         if (!this.exists())
             throw Error(_("io.noSuchFile"));
         if (!this.isDirectory())
@@ -389,17 +455,18 @@ var File = Class("File", {
     /**
      * Returns a new file for the given child of this directory entry.
      */
-    child: function (name) {
+    child: function child() {
         let f = this.constructor(this);
-        for each (let elem in name.split(File.pathSplit))
-            f.append(elem);
+        for (let [, name] in Iterator(arguments))
+            for each (let elem in name.split(File.pathSplit))
+                f.append(elem);
         return f;
     },
 
     /**
      * Returns an iterator for all lines in a file.
      */
-    get lines() File.readLines(services.FileInStream(this, -1, 0, 0),
+    get lines() File.readLines(services.FileInStream(this.file, -1, 0, 0),
                                this.charset),
 
     /**
@@ -410,8 +477,8 @@ var File = Class("File", {
      *          @default #charset
      * @returns {string}
      */
-    read: function (encoding) {
-        let ifstream = services.FileInStream(this, -1, 0, 0);
+    read: function read(encoding) {
+        let ifstream = services.FileInStream(this.file, -1, 0, 0);
 
         return File.readStream(ifstream, encoding || this.charset);
     },
@@ -423,7 +490,7 @@ var File = Class("File", {
      *     entries.
      * @returns {[nsIFile]}
      */
-    readDirectory: function (sort) {
+    readDirectory: function readDirectory(sort) {
         if (!this.isDirectory())
             throw Error(_("io.eNotDir"));
 
@@ -438,7 +505,7 @@ var File = Class("File", {
      *
      * @returns {nsIFileURL}
      */
-    toURI: function toURI() services.io.newFileURI(this),
+    toURI: function toURI() services.io.newFileURI(this.file),
 
     /**
      * Writes the string *buf* to this file.
@@ -464,7 +531,7 @@ var File = Class("File", {
      * @param {string} encoding The encoding to used to write the file.
      * @default #charset
      */
-    write: function (buf, mode, perms, encoding) {
+    write: function write(buf, mode, perms, encoding) {
         function getStream(defaultChar) {
             return services.ConvOutStream(ofstream, encoding, 0, defaultChar);
         }
@@ -484,7 +551,7 @@ var File = Class("File", {
         if (!this.exists()) // OCREAT won't create the directory
             this.create(this.NORMAL_FILE_TYPE, perms);
 
-        let ofstream = services.FileOutStream(this, mode, perms, 0);
+        let ofstream = services.FileOutStream(this.file, mode, perms, 0);
         try {
             var ocstream = getStream(0);
             ocstream.writeString(buf);
@@ -503,7 +570,34 @@ var File = Class("File", {
             ofstream.close();
         }
         return true;
-    }
+    },
+
+    // Wrapped native methods:
+    copyTo: function copyTo(dir, name)
+        this.file.copyTo(this.constructor(dir).file,
+                         name),
+
+    copyToFollowingLinks: function copyToFollowingLinks(dir, name)
+        this.file.copyToFollowingLinks(this.constructor(dir).file,
+                                       name),
+
+    moveTo: function moveTo(dir, name)
+        this.file.moveTo(this.constructor(dir).file,
+                         name),
+
+    equals: function equals(file)
+        this.file.equals(this.constructor(file).file),
+
+    contains: function contains(dir, recur)
+        this.file.contains(this.constructor(dir).file,
+                           recur),
+
+    getRelativeDescriptor: function getRelativeDescriptor(file)
+        this.file.getRelativeDescriptor(this.constructor(file).file),
+
+    setRelativeDescriptor: function setRelativeDescriptor(file, path)
+        this.file.setRelativeDescriptor(this.constructor(file).file,
+                                        path)
 }, {
     /**
      * @property {number} Open for reading only.
@@ -694,6 +788,28 @@ var File = Class("File", {
     replacePathSep: function (path) path.replace("/", File.PATH_SEP, "g")
 });
 
+let (file = services.directory.get("ProfD", Ci.nsIFile)) {
+    Object.keys(file).forEach(function (prop) {
+        if (!(prop in File.prototype)) {
+            let isFunction;
+            try {
+                isFunction = callable(file[prop])
+            }
+            catch (e) {}
+
+            if (isFunction)
+                File.prototype[prop] = util.wrapCallback(function wrapper() this.file[prop].apply(this.file, arguments));
+            else
+                Object.defineProperty(File.prototype, prop, {
+                    configurable: true,
+                    get: function wrap_get() this.file[prop],
+                    set: function wrap_set(val) { this.file[prop] = val; }
+                });
+        }
+    });
+    file = null;
+}
+
 endModule();
 
 // catch(e){ dump(e + "\n" + (e.stack || Error().stack)); Components.utils.reportError(e) }