mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2026-02-10 20:35:46 +01:00
Complete :sanitize and private mode overhaul.
--HG-- rename : common/content/sanitizer.js => common/modules/sanitizer.jsm
This commit is contained in:
@@ -66,11 +66,12 @@ defmodule("base", this, {
|
||||
// sed -n 's/^(const|function) ([a-zA-Z0-9_]+).*/ "\2",/p' base.jsm | sort | fmt
|
||||
exports: [
|
||||
"Cc", "Ci", "Class", "Cr", "Cu", "Module", "Object", "Runnable",
|
||||
"Struct", "StructBase", "Timer", "allkeys", "array", "call",
|
||||
"callable", "curry", "debuggerProperties", "defmodule", "dict",
|
||||
"Struct", "StructBase", "Timer", "XPCOMUtils", "allkeys", "array",
|
||||
"call", "callable", "curry", "debuggerProperties", "defmodule", "dict",
|
||||
"endmodule", "extend", "foreach", "isarray", "isgenerator",
|
||||
"isinstance", "isobject", "isstring", "issubclass", "iter", "memoize",
|
||||
"properties", "requiresMainThread", "set", "update", "values",
|
||||
"isinstance", "isobject", "isstring", "issubclass", "iter", "keys",
|
||||
"memoize", "properties", "requiresMainThread", "set", "update",
|
||||
"values",
|
||||
],
|
||||
use: ["services"]
|
||||
});
|
||||
@@ -117,8 +118,9 @@ function debuggerProperties(obj) {
|
||||
}
|
||||
}
|
||||
|
||||
let hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
if (!Object.keys)
|
||||
Object.keys = function keys(obj) [k for (k in obj) if (obj.hasOwnProperty(k))];
|
||||
Object.keys = function keys(obj) [k for (k in obj) if (hasOwnProperty.call(obj, k))];
|
||||
|
||||
if (!Object.getOwnPropertyNames)
|
||||
Object.getOwnPropertyNames = function getOwnPropertyNames(obj) {
|
||||
@@ -144,9 +146,14 @@ function properties(obj, prototypes) {
|
||||
}
|
||||
}
|
||||
|
||||
function keys(obj) {
|
||||
for (var k in obj)
|
||||
if (hasOwnProperty.call(obj, k))
|
||||
yield k;
|
||||
}
|
||||
function values(obj) {
|
||||
for (var k in obj)
|
||||
if (obj.hasOwnProperty(k))
|
||||
if (hasOwnProperty.call(obj, k))
|
||||
yield obj[k];
|
||||
}
|
||||
function foreach(iter, fn, self) {
|
||||
@@ -175,7 +182,7 @@ set.add = function (set, key) {
|
||||
set[key] = true;
|
||||
return res;
|
||||
}
|
||||
set.has = function (set, key) Object.prototype.hasOwnProperty.call(set, key);
|
||||
set.has = function (set, key) hasOwnProperty.call(set, key);
|
||||
set.remove = function (set, key) { delete set[key]; }
|
||||
|
||||
function iter(obj) {
|
||||
@@ -204,6 +211,12 @@ function iter(obj) {
|
||||
for (let i = 0; i < obj.length; i++)
|
||||
yield [obj.name, obj];
|
||||
})();
|
||||
if (obj instanceof Ci.mozIStorageStatement)
|
||||
return (function (obj) {
|
||||
while (obj.executeStep())
|
||||
yield obj.row;
|
||||
obj.reset();
|
||||
})(obj);
|
||||
return Iterator(obj);
|
||||
}
|
||||
|
||||
@@ -542,15 +555,31 @@ Class.prototype = {
|
||||
* @param {Object} classProperties Properties to be applied to the class constructor.
|
||||
* @return {Class}
|
||||
*/
|
||||
function Module(name, prototype, classProperties, init) {
|
||||
const module = Class(name, prototype, classProperties);
|
||||
function Module(name, prototype) {
|
||||
let init = callable(prototype) ? 4 : 3;
|
||||
const module = Class.apply(Class, Array.slice(arguments, 0, init));
|
||||
let instance = module();
|
||||
module.name = name.toLowerCase();
|
||||
instance.INIT = init || {};
|
||||
instance.INIT = arguments[init] || {};
|
||||
currentModule[module.name] = instance;
|
||||
defmodule.modules.push(instance);
|
||||
return module;
|
||||
}
|
||||
if (Cu.getGlobalForObject)
|
||||
Module.callerGlobal = function (caller) {
|
||||
try {
|
||||
return Cu.getGlobalForObject(caller);
|
||||
}
|
||||
catch (e) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
else
|
||||
Module.callerGlobal = function (caller) {
|
||||
while (caller.__parent__)
|
||||
caller = caller.__parent__;
|
||||
return caller;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class Struct
|
||||
@@ -677,7 +706,7 @@ const Timer = Class("Timer", {
|
||||
*/
|
||||
const array = Class("util.Array", Array, {
|
||||
init: function (ary) {
|
||||
if (isgenerator(ary))
|
||||
if (isinstance(ary, ["Iterator", "Generator"]))
|
||||
ary = [k for (k in ary)];
|
||||
else if (ary.length)
|
||||
ary = Array.slice(ary);
|
||||
@@ -688,12 +717,13 @@ const array = Class("util.Array", Array, {
|
||||
__noSuchMethod__: function (meth, args) {
|
||||
var res = array[meth].apply(null, [this.__proto__].concat(args));
|
||||
|
||||
if (array.isinstance(res))
|
||||
if (isarray(res))
|
||||
return array(res);
|
||||
return res;
|
||||
},
|
||||
toString: function () this.__proto__.toString(),
|
||||
concat: function () this.__proto__.concat.apply(this.__proto__, arguments),
|
||||
filter: function () this.__noSuchMethod__("filter", Array.slice(arguments)),
|
||||
map: function () this.__noSuchMethod__("map", Array.slice(arguments))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -52,7 +52,6 @@ const Highlights = Module("Highlight", {
|
||||
return "Unknown highlight keyword: " + class_;
|
||||
|
||||
let style = this.highlight[key] || Highlight(key);
|
||||
styles.removeSheet(true, style.selector);
|
||||
|
||||
if (append)
|
||||
newStyle = (style.value || "").replace(/;?\s*$/, "; " + newStyle);
|
||||
@@ -60,22 +59,23 @@ const Highlights = Module("Highlight", {
|
||||
newStyle = null;
|
||||
if (newStyle == null) {
|
||||
if (style.default == null) {
|
||||
delete this.highlight[style.class];
|
||||
styles.removeSheet(true, style.selector);
|
||||
delete this.highlight[style.class];
|
||||
return null;
|
||||
}
|
||||
newStyle = style.default;
|
||||
force = true;
|
||||
}
|
||||
|
||||
let css = newStyle.replace(/(?:!\s*important\s*)?(?:;?\s*$|;)/g, "!important;")
|
||||
.replace(";!important;", ";", "g"); // Seeming Spidermonkey bug
|
||||
if (!/^\s*(?:!\s*important\s*)?;*\s*$/.test(css)) {
|
||||
css = style.selector + " { " + css + " }";
|
||||
if (!style.loaded || style.value != newStyle) {
|
||||
styles.removeSheet(true, style.selector);
|
||||
let css = newStyle.replace(/(?:!\s*important\s*)?(?:;?\s*$|;)/g, "!important;")
|
||||
.replace(";!important;", ";", "g"); // Seeming Spidermonkey bug
|
||||
if (!/^\s*(?:!\s*important\s*)?;*\s*$/.test(css)) {
|
||||
css = style.selector + " { " + css + " }";
|
||||
|
||||
let error = styles.addSheet(true, "highlight:" + style.class, style.filter, css, true);
|
||||
if (error)
|
||||
return error;
|
||||
styles.addSheet(true, "highlight:" + style.class, style.filter, css, true);
|
||||
style.loaded = true;
|
||||
}
|
||||
}
|
||||
style.value = newStyle;
|
||||
this.highlight[style.class] = style;
|
||||
@@ -120,9 +120,10 @@ const Highlights = Module("Highlight", {
|
||||
style.selector = this.selector(style.class) + style.selector;
|
||||
|
||||
let old = this.highlight[style.class];
|
||||
this.highlight[style.class] = style;
|
||||
if (old && old.value != old.default)
|
||||
style.value = old.value;
|
||||
if (!old)
|
||||
this.highlight[style.class] = style;
|
||||
else if (old.value == old.default)
|
||||
old.value = style.value;
|
||||
}, this);
|
||||
for (let [class_, hl] in Iterator(this.highlight))
|
||||
if (hl.value == hl.default)
|
||||
|
||||
330
common/modules/sanitizer.jsm
Normal file
330
common/modules/sanitizer.jsm
Normal file
@@ -0,0 +1,330 @@
|
||||
// Copyright (c) 2009 by Doug Kearns <dougkearns@gmail.com>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
"use strict";
|
||||
|
||||
// TODO:
|
||||
// - fix Sanitize autocommand
|
||||
// - add warning for TIMESPAN_EVERYTHING?
|
||||
// - respect privacy.clearOnShutdown et al or recommend Leave autocommand?
|
||||
// - integrate with the Clear Private Data dialog?
|
||||
|
||||
// FIXME:
|
||||
// - finish 1.9.0 support if we're going to support sanitizing in Xulmus
|
||||
|
||||
Components.utils.import("resource://dactyl/base.jsm");
|
||||
defmodule("sanitizer", this, {
|
||||
exports: ["Range", "Sanitizer", "sanitizer"],
|
||||
require: ["services", "storage", "util"]
|
||||
});
|
||||
|
||||
let tmp = {};
|
||||
services.get("subscriptLoader").loadSubScript("chrome://browser/content/sanitize.js", tmp);
|
||||
|
||||
const Range = Struct("min", "max");
|
||||
Range.prototype.contains = function (date)
|
||||
date == null || (this.min == null || date >= this.min) && (this.max == null || date <= this.max);
|
||||
Range.prototype.__defineGetter__("isEternity", function () this.max == null && this.min == null);
|
||||
Range.prototype.__defineGetter__("isSession", function () this.max == null && this.min == sanitizer.sessionStart);
|
||||
|
||||
const Sanitizer = Module("sanitizer", tmp.Sanitizer, {
|
||||
sessionStart: Date.now() * 1000,
|
||||
|
||||
init: function () {
|
||||
services.add("contentprefs", "@mozilla.org/content-pref/service;1", Ci.nsIContentPrefService);
|
||||
services.add("cookies", "@mozilla.org/cookiemanager;1", [Ci.nsICookieManager, Ci.nsICookieManager2,
|
||||
Ci.nsICookieService]);
|
||||
services.add("loginmanager", "@mozilla.org/login-manager;1", Ci.nsILoginManager);
|
||||
services.add("permissions", "@mozilla.org/permissionmanager;1", Ci.nsIPermissionManager);
|
||||
|
||||
this.itemOverrides = {};
|
||||
this.itemDescriptions = {
|
||||
all: "Sanitize all items",
|
||||
// Builtin items
|
||||
cache: "Cache",
|
||||
downloads: "Download history",
|
||||
formdata: "Saved form and search history",
|
||||
history: "Browsing history",
|
||||
offlineapps: "Offline website data",
|
||||
passwords: "Saved passwords",
|
||||
sessions: "Authenticated sessions",
|
||||
};
|
||||
// These builtin methods don't support hosts or have
|
||||
// insufficient granularity
|
||||
this.addItem("cookies", {
|
||||
description: "Cookies",
|
||||
action: function (range, host) {
|
||||
for (let c in Sanitizer.iterCookies(host))
|
||||
if (range.contains(c.creationTime) || timespan.isSession && c.isSession)
|
||||
services.get("cookies").remove(c.host, c.name, c.path, false);
|
||||
},
|
||||
override: true
|
||||
});
|
||||
this.addItem("sitesettings", {
|
||||
description: "Site preferences",
|
||||
action: function (range, host) {
|
||||
if (host) {
|
||||
for (let p in Sanitizer.iterPermissions(host)) {
|
||||
services.get("permissions").remove(util.createURI(p.host), p.type);
|
||||
services.get("permissions").add(util.createURI(p.host), p.type, 0);
|
||||
}
|
||||
for (let p in iter(services.get("contentprefs").getPrefs(util.createURI(host)).enumerator))
|
||||
services.get("contentprefs").removePref(util.createURI(host), p.QueryInterface(Ci.nsIProperty).name);
|
||||
}
|
||||
else {
|
||||
// "Allow this site to open popups" ...
|
||||
services.get("permissions").removeAll();
|
||||
// Zoom level, ...
|
||||
services.get("contentprefs").removeGroupedPrefs();
|
||||
}
|
||||
|
||||
// "Never remember passwords" ...
|
||||
for each (let domain in services.get("loginmanager").getAllDisabledHosts())
|
||||
if (!host || util.isSubdomain(domain, host))
|
||||
services.get("loginmanager").setLoginSavingEnabled(host, true);
|
||||
},
|
||||
override: true
|
||||
});
|
||||
util.addObserver(this);
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
|
||||
|
||||
addItem: function addItem(name, params) {
|
||||
if (params.description)
|
||||
this.itemDescriptions[name] = params.description;
|
||||
if (params.override)
|
||||
set.add(this.itemOverrides, name);
|
||||
|
||||
name = "clear-" + name;
|
||||
storage.addObserver("sanitizer",
|
||||
function (key, event, arg) {
|
||||
if (event == name)
|
||||
params.action.apply(params, arg);
|
||||
}, Module.callerGlobal(params.action));
|
||||
|
||||
if (params.privateEnter || params.privateLeave)
|
||||
storage.addObserver("private-mode",
|
||||
function (key, event, arg) {
|
||||
let meth = params[arg ? "privateEnter" : "privateLeave"];
|
||||
if (meth)
|
||||
meth.call(params);
|
||||
}, Module.callerGlobal(params.action));
|
||||
},
|
||||
|
||||
observe: {
|
||||
"browser:purge-domain-data": function (subject, data) {
|
||||
storage.fireEvent("sanitize", "domain", data);
|
||||
// If we're sanitizing, our own sanitization functions will already
|
||||
// be called, and with much greater granularity. Only process this
|
||||
// event if it's triggered externally.
|
||||
if (!this.sanitizing)
|
||||
this.sanitizeItems(null, Range(), data);
|
||||
},
|
||||
"browser:purge-session-history": function (subject, data) {
|
||||
// See above.
|
||||
if (!this.sanitizing)
|
||||
this.sanitizeItems(null, Range(this.sessionStart/1000), null);
|
||||
},
|
||||
"private-browsing": function (subject, data) {
|
||||
if (data == "enter")
|
||||
storage.privateMode = true;
|
||||
else if (data == "exit")
|
||||
storage.privateMode = false;
|
||||
storage.fireEvent("private-mode", "change", storage.privateMode);
|
||||
}
|
||||
},
|
||||
|
||||
sanitize: function (items, range) {
|
||||
this.sanitizing = true;
|
||||
let errors = this.sanitizeItems(items, range, null);
|
||||
|
||||
for (let itemName in values(items)) {
|
||||
try {
|
||||
let item = this.items[Sanitizer.argToPref(itemName)];
|
||||
if (item && !this.itemOverrides[itemName]) {
|
||||
item.range = range;
|
||||
if ("clear" in item && item.canClear)
|
||||
item.clear();
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
errors = errors || {};
|
||||
errors[itemName] = e;
|
||||
dump("Error sanitizing " + itemName + ": " + e + "\n" + e.stack + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
this.sanitizing = false;
|
||||
return errors;
|
||||
},
|
||||
|
||||
sanitizeItems: function (items, range, host) {
|
||||
if (items == null)
|
||||
items = Object.keys(this.itemDescriptions);
|
||||
let errors;
|
||||
for (let itemName in values(items))
|
||||
try {
|
||||
storage.fireEvent("sanitizer", "clear-" + itemName, [range, host]);
|
||||
}
|
||||
catch (e) {
|
||||
errors = errors || {};
|
||||
errors[itemName] = e;
|
||||
dump("Error sanitizing " + itemName + ": " + e + "\n" + e.stack + "\n");
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
}, {
|
||||
argPrefMap: {
|
||||
commandline: "commandLine",
|
||||
offlineapps: "offlineApps",
|
||||
sitesettings: "siteSettings",
|
||||
},
|
||||
argToPref: function (arg) Sanitizer.argPrefMap[arg] || arg,
|
||||
prefToArg: function (pref) pref.replace(/.*\./, "").toLowerCase(),
|
||||
|
||||
iterCookies: function iterCookies(host) {
|
||||
for (let c in iter(services.get("cookies").enumerator))
|
||||
if (!host || util.isSubdomain(c.QueryInterface(Ci.nsICookie2).rawHost, host))
|
||||
yield c;
|
||||
},
|
||||
iterPermissions: function iterPermissions(host) {
|
||||
for (let p in iter(services.get("permissions").enumerator))
|
||||
if (p.QueryInterface(Ci.nsIPermission) && (!host || util.isSubdomain(p.host, host)))
|
||||
yield p;
|
||||
}
|
||||
}, {
|
||||
autocommands: function (dactyl, modules, window) {
|
||||
storage.addObserver("private-mode",
|
||||
function (key, event, value) {
|
||||
modules.autocommands.trigger("PrivateMode", { state: value });
|
||||
}, window);
|
||||
storage.addObserver("sanitizer",
|
||||
function (key, event, value) {
|
||||
if (event == "domain")
|
||||
modules.autocommands.trigger("SanitizeDomain", { domain: value });
|
||||
else if (!value[1])
|
||||
modules.autocommands.trigger("Sanitize", { name: event.substr("clear-".length), domain: value[1] });
|
||||
}, window);
|
||||
},
|
||||
commands: function (dactyl, modules, window) {
|
||||
const commands = modules.commands;
|
||||
commands.add(["sa[nitize]"],
|
||||
"Clear private data",
|
||||
function (args) {
|
||||
dactyl.assert(!modules.options['private'], "Cannot sanitize items in private mode");
|
||||
|
||||
let timespan = args["-timespan"] || modules.options["sanitizetimespan"];
|
||||
|
||||
let range = Range(), match = /^(\d+)([mhdw])$/.exec(timespan);
|
||||
range[args["-older"] ? "max" : "min"] =
|
||||
match ? 1000 * (Date.now() - 1000 * parseInt(match[1], 10) * { m: 60, h: 3600, d: 3600 * 24, w: 3600 * 24 * 7 }[match[2]])
|
||||
: (timespan[0] == "s" ? sanitizer.sessionStart : null);
|
||||
|
||||
let items = args.slice();
|
||||
if (args.bang) {
|
||||
dactyl.assert(args.length == 0, "E488: Trailing characters");
|
||||
items = modules.options.get("sanitizeitems").values;
|
||||
}
|
||||
else
|
||||
dactyl.assert(modules.options.get("sanitizeitems").validator(items), "Valid items required");
|
||||
|
||||
if (items[0] == "all")
|
||||
items = Object.keys(sanitizer.itemDescriptions);
|
||||
|
||||
sanitizer.range = range;
|
||||
sanitizer.ignoreTimespan = range.min == null;
|
||||
sanitizer.sanitizing = true;
|
||||
if (args["-host"]) {
|
||||
args["-host"].forEach(function (host) {
|
||||
sanitizer.sanitizing = true;
|
||||
if (items.indexOf("history") > -1)
|
||||
services.get("privateBrowsing").removeDataFromDomain(host);
|
||||
sanitizer.sanitizeItems(items, range, host)
|
||||
});
|
||||
}
|
||||
else
|
||||
sanitizer.sanitize(items, range);
|
||||
},
|
||||
{
|
||||
argCount: "*", // FIXME: should be + and 0
|
||||
bang: true,
|
||||
completer: function (context) {
|
||||
context.title = ["Privacy Item", "Description"];
|
||||
context.completions = modules.options.get("sanitizeitems").completer();
|
||||
},
|
||||
domains: function (args) args["-host"] || [],
|
||||
options: [
|
||||
{
|
||||
names: ["-host", "-h"],
|
||||
description: "Only sanitize items referring to listed host or hosts",
|
||||
completer: function (context, args) {
|
||||
let hosts = context.filter.split(",");
|
||||
context.advance(context.filter.length - hosts.pop().length);
|
||||
context.filters.push(function (item)
|
||||
!hosts.some(function (host) util.isSubdomain(item.text, host)));
|
||||
modules.completion.domain(context);
|
||||
},
|
||||
type: modules.CommandOption.LIST,
|
||||
}, {
|
||||
names: ["-older", "-o"],
|
||||
description: "Sanitize items older than timespan",
|
||||
type: modules.CommandOption.NOARG
|
||||
}, {
|
||||
names: ["-timespan", "-t"],
|
||||
description: "Timespan for which to sanitize items",
|
||||
completer: function (context) modules.options.get("sanitizetimespan").completer(context),
|
||||
type: modules.CommandOption.STRING,
|
||||
validator: function (arg) modules.options.get("sanitizetimespan").validator(arg),
|
||||
}
|
||||
],
|
||||
privateData: true
|
||||
});
|
||||
},
|
||||
options: function (dactyl, modules) {
|
||||
const options = modules.options;
|
||||
if (services.get("privateBrowsing"))
|
||||
options.add(["private", "pornmode"],
|
||||
"Set the 'private browsing' option",
|
||||
"boolean", false,
|
||||
{
|
||||
setter: function (value) services.get("privateBrowsing").privateBrowsingEnabled = value,
|
||||
getter: function () services.get("privateBrowsing").privateBrowsingEnabled
|
||||
});
|
||||
|
||||
options.add(["sanitizeitems", "si"],
|
||||
"The default list of private items to sanitize",
|
||||
"stringlist", "all",
|
||||
{
|
||||
completer: function (value) Iterator(sanitizer.itemDescriptions),
|
||||
validator: function (values) values.length &&
|
||||
values.every(function (val) set.has(sanitizer.itemDescriptions, val)) &&
|
||||
(values.length == 1 || !values.some(function (val) val == "all"))
|
||||
});
|
||||
|
||||
options.add(["sanitizetimespan", "sts"],
|
||||
"The default sanitizer time span",
|
||||
"string", "all",
|
||||
{
|
||||
completer: function (context) {
|
||||
context.compare = context.constructor.Sort.Unsorted;
|
||||
return [
|
||||
["all", "Everything"],
|
||||
["session", "The current session"],
|
||||
["10m", "Last ten minutes"],
|
||||
["1h", "Past hour"],
|
||||
["1d", "Past day"],
|
||||
["1w", "Past week"],
|
||||
]
|
||||
},
|
||||
validator: function (value) /^(a(ll)?|s(ession)|\d+[mhdw])$/.test(value)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
endmodule();
|
||||
|
||||
// catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);}
|
||||
|
||||
// vim: set fdm=marker sw=4 ts=4 et ft=javascript:
|
||||
@@ -27,12 +27,14 @@ const Services = Module("Services", {
|
||||
this.add("environment", "@mozilla.org/process/environment;1", Ci.nsIEnvironment);
|
||||
this.add("extensionManager", "@mozilla.org/extensions/manager;1", Ci.nsIExtensionManager);
|
||||
this.add("favicon", "@mozilla.org/browser/favicon-service;1", Ci.nsIFaviconService);
|
||||
this.add("history", "@mozilla.org/browser/global-history;2", [Ci.nsIBrowserHistory, Ci.nsIGlobalHistory3, Ci.nsINavHistoryService]);
|
||||
this.add("history", "@mozilla.org/browser/global-history;2", [Ci.nsIBrowserHistory, Ci.nsIGlobalHistory3,
|
||||
Ci.nsINavHistoryService, Ci.nsPIPlacesDatabase]);
|
||||
this.add("io", "@mozilla.org/network/io-service;1", Ci.nsIIOService);
|
||||
this.add("json", "@mozilla.org/dom/json;1", Ci.nsIJSON, "createInstance");
|
||||
this.add("livemark", "@mozilla.org/browser/livemark-service;2", Ci.nsILivemarkService);
|
||||
this.add("observer", "@mozilla.org/observer-service;1", Ci.nsIObserverService);
|
||||
this.add("pref", "@mozilla.org/preferences-service;1", [Ci.nsIPrefBranch, Ci.nsIPrefBranch2, Ci.nsIPrefService]);
|
||||
this.add("privateBrowsing", "@mozilla.org/privatebrowsing;1", Ci.nsIPrivateBrowsingService);
|
||||
this.add("profile", "@mozilla.org/toolkit/profile-service;1", Ci.nsIToolkitProfileService);
|
||||
this.add("runtime", "@mozilla.org/xre/runtime;1", [Ci.nsIXULAppInfo, Ci.nsIXULRuntime]);
|
||||
this.add("rdf", "@mozilla.org/rdf/rdf-service;1", Ci.nsIRDFService);
|
||||
@@ -41,9 +43,9 @@ const Services = Module("Services", {
|
||||
this.add("subscriptLoader", "@mozilla.org/moz/jssubscript-loader;1", Ci.mozIJSSubScriptLoader);
|
||||
this.add("tagging", "@mozilla.org/browser/tagging-service;1", Ci.nsITaggingService);
|
||||
this.add("threadManager", "@mozilla.org/thread-manager;1", Ci.nsIThreadManager);
|
||||
this.add("urifixup", "@mozilla.org/docshell/urifixup;1", Ci.nsIURIFixup);
|
||||
this.add("windowMediator", "@mozilla.org/appshell/window-mediator;1", Ci.nsIWindowMediator);
|
||||
this.add("windowWatcher", "@mozilla.org/embedcomp/window-watcher;1", Ci.nsIWindowWatcher);
|
||||
this.add("xulAppInfo", "@mozilla.org/xre/app-info;1", Ci.nsIXULAppInfo);
|
||||
|
||||
this.addClass("file", "@mozilla.org/file/local;1", Ci.nsILocalFile);
|
||||
this.addClass("file:", "@mozilla.org/network/protocol;1?name=file", Ci.nsIFileProtocolHandler);
|
||||
@@ -128,6 +130,6 @@ const Services = Module("Services", {
|
||||
|
||||
endmodule();
|
||||
|
||||
// catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n");}
|
||||
// catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);}
|
||||
|
||||
// vim: set fdm=marker sw=4 sts=4 et ft=javascript:
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
}}} ***** END LICENSE BLOCK *****/
|
||||
"use strict";
|
||||
|
||||
const myObject = Object;
|
||||
Components.utils.import("resource://dactyl/base.jsm");
|
||||
defmodule("storage", this, {
|
||||
exports: ["File", "storage"],
|
||||
@@ -76,9 +77,9 @@ function savePref(obj) {
|
||||
}
|
||||
|
||||
const StoreBase = Class("StoreBase", {
|
||||
OPTIONS: ["privateData"],
|
||||
OPTIONS: ["privateData", "replacer"],
|
||||
fireEvent: function (event, arg) { storage.fireEvent(this.name, event, arg); },
|
||||
get serial() services.get("json").encode(this._object),
|
||||
get serial() JSON.stringify(this._object, this.replacer),
|
||||
save: function () { savePref(this); },
|
||||
init: function (name, store, load, options) {
|
||||
this._load = load;
|
||||
@@ -97,7 +98,7 @@ const StoreBase = Class("StoreBase", {
|
||||
});
|
||||
|
||||
const ObjectStore = Class("ObjectStore", StoreBase, {
|
||||
_constructor: Object,
|
||||
_constructor: myObject,
|
||||
|
||||
set: function set(key, val) {
|
||||
var defined = key in this._object;
|
||||
@@ -120,6 +121,7 @@ const ObjectStore = Class("ObjectStore", StoreBase, {
|
||||
|
||||
clear: function () {
|
||||
this._object = {};
|
||||
this.fireEvent("clear", key);
|
||||
},
|
||||
|
||||
__iterator__: function () Iterator(this._object),
|
||||
@@ -181,7 +183,7 @@ const Storage = Module("Storage", {
|
||||
if (!(key in keys) || params.reload || this.alwaysReload[key]) {
|
||||
if (key in this && !(params.reload || this.alwaysReload[key]))
|
||||
throw Error();
|
||||
let load = function () loadPref(key, params.store, params.type || Object);
|
||||
let load = function () loadPref(key, params.store, params.type || myObject);
|
||||
keys[key] = new constructor(key, params.store, load, params);
|
||||
timers[key] = new Timer(1000, 10000, function () storage.save(key));
|
||||
this.__defineGetter__(key, function () keys[key]);
|
||||
@@ -234,14 +236,13 @@ const Storage = Module("Storage", {
|
||||
get observers() observers,
|
||||
|
||||
fireEvent: function fireEvent(key, event, arg) {
|
||||
if (!(key in this))
|
||||
return;
|
||||
this.removeDeadObservers();
|
||||
// Safe, since we have our own Array object here.
|
||||
if (key in observers)
|
||||
for each (let observer in observers[key])
|
||||
observer.callback.get()(key, event, arg);
|
||||
timers[key].tell();
|
||||
if (timers[key])
|
||||
timers[key].tell();
|
||||
},
|
||||
|
||||
load: function load(key) {
|
||||
@@ -261,6 +262,8 @@ const Storage = Module("Storage", {
|
||||
_privateMode: false,
|
||||
get privateMode() this._privateMode,
|
||||
set privateMode(val) {
|
||||
if (val && !this._privateMode)
|
||||
this.saveAll();
|
||||
if (!val && this._privateMode)
|
||||
for (let key in keys)
|
||||
this.load(key);
|
||||
|
||||
@@ -31,6 +31,13 @@ Sheet.prototype.__defineGetter__("fullCSS", function wrapCSS() {
|
||||
.join(", ");
|
||||
return "/* Dactyl style #" + this.id + " */ " + namespace + " @-moz-document " + selectors + "{\n" + css + "\n}\n";
|
||||
});
|
||||
Sheet.prototype.__defineGetter__("css", function () this[3]);
|
||||
Sheet.prototype.__defineSetter__("css", function (val) {
|
||||
this.enabled = false;
|
||||
this[3] = val;
|
||||
this.enabled = true;
|
||||
return val;
|
||||
});
|
||||
Sheet.prototype.__defineGetter__("enabled", function () this._enabled);
|
||||
Sheet.prototype.__defineSetter__("enabled", function (on) {
|
||||
this._enabled = Boolean(on);
|
||||
|
||||
@@ -33,6 +33,41 @@ const Util = Module("Util", {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Registers a obj as a new observer with the observer service. obj.observe
|
||||
* must be an object where each key is the name of a target to observe and
|
||||
* each value is a function(subject, data) to be called when the given
|
||||
* target is broadcast. obj.observe will be replaced with a new opaque
|
||||
* function. The observer is automatically unregistered on application
|
||||
* shutdown.
|
||||
*
|
||||
* @param {object} obj
|
||||
*/
|
||||
addObserver: function (obj) {
|
||||
let observers = obj.observe;
|
||||
function register(meth) {
|
||||
services.get("observer")[meth](obj, "quit-application", true);
|
||||
for (let target in keys(observers))
|
||||
services.get("observer")[meth](obj, target, true);
|
||||
}
|
||||
Class.replaceProperty(obj, "observe",
|
||||
function (subject, target, data) {
|
||||
if (target == "quit-application")
|
||||
register("removeObserver");
|
||||
if (observers[target])
|
||||
observers[target].call(obj, subject, data);
|
||||
});
|
||||
register("addObserver");
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls a function synchronously in the main thread. Return values are not
|
||||
* preserved.
|
||||
*
|
||||
* @param {function} callback
|
||||
* @param {object} self The this object for the call.
|
||||
* @returns {function}
|
||||
*/
|
||||
callInMainThread: function (callback, self) {
|
||||
let mainThread = services.get("threadManager").mainThread;
|
||||
if (services.get("threadManager").isMainThread)
|
||||
@@ -123,30 +158,19 @@ const Util = Module("Util", {
|
||||
},
|
||||
|
||||
/**
|
||||
* Copies a string to the system clipboard. If <b>verbose</b> is specified
|
||||
* the copied string is also echoed to the command line.
|
||||
* Converts any arbitrary string into an URI object. Returns null on
|
||||
* failure.
|
||||
*
|
||||
* @param {string} str
|
||||
* @param {boolean} verbose
|
||||
* @returns {nsIURI|null}
|
||||
*/
|
||||
copyToClipboard: function copyToClipboard(str, verbose) {
|
||||
const clipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
|
||||
clipboardHelper.copyString(str);
|
||||
|
||||
if (verbose)
|
||||
util.dactyl.echomsg("Yanked " + str);
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts any arbitrary string into an URI object.
|
||||
*
|
||||
* @param {string} str
|
||||
* @returns {Object}
|
||||
*/
|
||||
// FIXME: newURI needed too?
|
||||
createURI: function createURI(str) {
|
||||
const fixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
|
||||
return fixup.createFixupURI(str, fixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP);
|
||||
try {
|
||||
return services.get("urifixup").createFixupURI(str, services.get("urifixup").FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP);
|
||||
}
|
||||
catch (e) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -336,6 +360,20 @@ const Util = Module("Util", {
|
||||
return strNum[0] + " " + unitVal[unitIndex];
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the host for the given URL, or null if invalid.
|
||||
*
|
||||
* @param {string} url
|
||||
* @returns {string|null}
|
||||
*/
|
||||
getHost: function (url) {
|
||||
try {
|
||||
return util.createURI(url).host;
|
||||
}
|
||||
catch (e) {}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends a synchronous or asynchronous HTTP request to <b>url</b> and
|
||||
* returns the XMLHttpRequest object. If <b>callback</b> is specified the
|
||||
@@ -390,6 +428,29 @@ const Util = Module("Util", {
|
||||
bottom: Math.min(r1.bottom, r2.bottom)
|
||||
}),
|
||||
|
||||
/**
|
||||
* Returns true if 'url' is in the domain 'domain'.
|
||||
*
|
||||
* @param {string} url
|
||||
* @param {string} domain
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isDomainURL: function isDomainURL(url, domain) util.isSubdomain(util.getHost(url), domain),
|
||||
|
||||
/**
|
||||
* Returns true if 'host' is a subdomain of 'domain'.
|
||||
*
|
||||
* @param {string} host The host to check.
|
||||
* @param {string} domain The base domain to check the host agains.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isSubdomain: function isSubdomain(host, domain) {
|
||||
if (host == null)
|
||||
return false;
|
||||
let idx = host.lastIndexOf(domain);
|
||||
return idx > -1 && idx + domain.length == host.length && (idx == 0 || host[idx-1] == ".");
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an XPath union expression constructed from the specified node
|
||||
* tests. An expression is built with node tests for both the null and
|
||||
@@ -589,43 +650,6 @@ const Util = Module("Util", {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Reads a string from the system clipboard.
|
||||
*
|
||||
* This is same as Firefox's readFromClipboard function, but is needed for
|
||||
* apps like Thunderbird which do not provide it.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
readFromClipboard: function readFromClipboard() {
|
||||
let str;
|
||||
|
||||
try {
|
||||
const clipboard = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard);
|
||||
const transferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
|
||||
|
||||
transferable.addDataFlavor("text/unicode");
|
||||
|
||||
if (clipboard.supportsSelectionClipboard())
|
||||
clipboard.getData(transferable, clipboard.kSelectionClipboard);
|
||||
else
|
||||
clipboard.getData(transferable, clipboard.kGlobalClipboard);
|
||||
|
||||
let data = {};
|
||||
let dataLen = {};
|
||||
|
||||
transferable.getTransferData("text/unicode", data, dataLen);
|
||||
|
||||
if (data) {
|
||||
data = data.value.QueryInterface(Ci.nsISupportsString);
|
||||
str = data.data.substring(0, dataLen.value / 2);
|
||||
}
|
||||
}
|
||||
catch (e) {}
|
||||
|
||||
return str;
|
||||
},
|
||||
|
||||
/**
|
||||
* Scrolls an element into view if and only if it's not already
|
||||
* fully visible.
|
||||
|
||||
Reference in New Issue
Block a user