]> git.donarmstrong.com Git - dactyl.git/blobdiff - common/modules/sanitizer.jsm
Import r6923 from upstream hg supporting Firefox up to 22.0a1
[dactyl.git] / common / modules / sanitizer.jsm
index c7f3ac9ebe5472aa1654ed9a0e6466b42914652f..9b4eb547805e9b2e8a859bf8be683d9dde855715 100644 (file)
@@ -1,5 +1,5 @@
 // Copyright (c) 2009 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2009-2011 by Kris Maglione <maglione.k at Gmail>
+// Copyright (c) 2009-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.
 // FIXME:
 //   - finish 1.9.0 support if we're going to support sanitizing in Melodactyl
 
-try {
-
-Components.utils.import("resource://dactyl/bootstrap.jsm");
 defineModule("sanitizer", {
     exports: ["Range", "Sanitizer", "sanitizer"],
-    use: ["config"],
-    require: ["messages", "prefs", "services", "storage", "template", "util"],
-}, this);
+    require: ["config", "prefs", "services", "util"]
+});
+
+lazyRequire("messages", ["_"]);
+lazyRequire("overlay", ["overlay"]);
+lazyRequire("storage", ["storage"]);
+lazyRequire("template", ["template"]);
 
-let tmp = {};
+let tmp = Object.create(this);
 JSMLoader.loadSubScript("chrome://browser/content/sanitize.js", tmp);
 tmp.Sanitizer.prototype.__proto__ = Class.prototype;
 
@@ -33,7 +34,7 @@ update(Range.prototype, {
     get isEternity() this.max == null && this.min == null,
     get isSession() this.max == null && this.min == sanitizer.sessionStart,
 
-    get native() this.isEternity ? null : [range.min || 0, range.max == null ? Number.MAX_VALUE : range.max]
+    get native() this.isEternity ? null : [this.min || 0, this.max == null ? Number.MAX_VALUE : this.max]
 });
 
 var Item = Class("SanitizeItem", {
@@ -56,7 +57,7 @@ var Item = Class("SanitizeItem", {
     shouldSanitize: function (shutdown) (!shutdown || this.builtin || this.persistent) &&
         prefs.get(shutdown ? this.shutdownPref : this.pref)
 }, {
-    PREFIX: localPrefs.branch.root,
+    PREFIX: config.prefs.branch.root,
     BRANCH: "privacy.cpd.",
     SHUTDOWN_BRANCH: "privacy.clearOnShutdown."
 });
@@ -69,10 +70,10 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
 
         util.addObserver(this);
 
-        services.add("contentprefs", "@mozilla.org/content-pref/service;1", Ci.nsIContentPrefService);
+        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("loginManager", "@mozilla.org/login-manager;1",        Ci.nsILoginManager);
         services.add("permissions",  "@mozilla.org/permissionmanager;1",    Ci.nsIPermissionManager);
 
         this.itemMap = {};
@@ -107,23 +108,29 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
             action: function (range, host) {
                 if (host)
                     services.history.removePagesFromHost(host, true);
-                else
-                    services.history.removeVisitsByTimeframe(this.range.min, this.range.max);
-
-                if (!host)
+                else {
+                    if (range.isEternity)
+                        services.history.removeAllPages();
+                    else
+                        services.history.removeVisitsByTimeframe(range.native[0], Math.min(Date.now() * 1000, range.native[1])); // XXX
                     services.observer.notifyObservers(null, "browser:purge-session-history", "");
+                }
 
                 if (!host || util.isDomainURL(prefs.get("general.open_location.last_url"), host))
                     prefs.reset("general.open_location.last_url");
             },
             override: true
         });
-        if (services.has("privateBrowsing"))
+        try {
+            var { ForgetAboutSite } = Cu.import("resource://gre/modules/ForgetAboutSite.jsm", {});
+        }
+        catch (e) {}
+        if (ForgetAboutSite)
             this.addItem("host", {
                 description: "All data from the given host",
                 action: function (range, host) {
                     if (host)
-                        services.privateBrowsing.removeDataFromDomain(host);
+                        ForgetAboutSite.removeDataFromDomain(host);
                 }
             });
         this.addItem("sitesettings", {
@@ -138,20 +145,20 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
                         services.permissions.remove(util.createURI(p.host), p.type);
                         services.permissions.add(util.createURI(p.host), p.type, 0);
                     }
-                    for (let p in iter(services.contentprefs.getPrefs(util.createURI(host))))
-                        services.contentprefs.removePref(util.createURI(host), p.QueryInterface(Ci.nsIProperty).name);
+                    for (let p in iter(services.contentPrefs.getPrefs(util.createURI(host))))
+                        services.contentPrefs.removePref(util.createURI(host), p.QueryInterface(Ci.nsIProperty).name);
                 }
                 else {
                     // "Allow this site to open popups" ...
                     services.permissions.removeAll();
                     // Zoom level, ...
-                    services.contentprefs.removeGroupedPrefs();
+                    services.contentPrefs.removeGroupedPrefs();
                 }
 
                 // "Never remember passwords" ...
-                for each (let domain in services.loginmanager.getAllDisabledHosts())
+                for each (let domain in services.loginManager.getAllDisabledHosts())
                     if (!host || util.isSubdomain(domain, host))
-                        services.loginmanager.setLoginSavingEnabled(host, true);
+                        services.loginManager.setLoginSavingEnabled(host, true);
             },
             override: true
         });
@@ -162,13 +169,12 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
         ];
 
         function prefOverlay(branch, persistent, local) update(Object.create(local), {
-            before: array.toObject([
-                [branch.substr(Item.PREFIX.length) + "history",
-                    <preferences xmlns={XUL}>{
-                      template.map(ourItems(persistent), function (item)
-                        <preference type="bool" id={branch + item.name} name={branch + item.name}/>)
-                    }</preferences>.*::*]
-            ]),
+            before: [
+                ["preferences", { id: branch.substr(Item.PREFIX.length) + "history",
+                                  xmlns: "xul" },
+                  template.map(ourItems(persistent), function (item)
+                      ["preference", { type: "bool", id: branch + item.name, name: branch + item.name }])]
+            ],
             init: function init(win) {
                 let pane = win.document.getElementById("SanitizeDialogPane");
                 for (let [, pref] in iter(pane.preferences))
@@ -177,62 +183,63 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
             }
         });
 
-        let (branch = Item.PREFIX + Item.SHUTDOWN_BRANCH) {
-            util.overlayWindow("chrome://browser/content/preferences/sanitize.xul",
-                               function (win) prefOverlay(branch, true, {
-                append: {
-                    SanitizeDialogPane:
-                        <groupbox orient="horizontal" xmlns={XUL}>
-                          <caption label={config.appName + " (see :help privacy)"}/>
-                          <grid flex="1">
-                            <columns><column flex="1"/><column flex="1"/></columns>
-                            <rows>{
-                              let (items = ourItems(true))
-                                 template.map(util.range(0, Math.ceil(items.length / 2)), function (i)
-                                   <row xmlns={XUL}>{
-                                     template.map(items.slice(i * 2, i * 2 + 2), function (item)
-                                       <checkbox xmlns={XUL} label={item.description} preference={branch + item.name}/>)
-                                   }</row>)
-                            }</rows>
-                          </grid>
-                        </groupbox>
-                }
-            }));
-        }
-        let (branch = Item.PREFIX + Item.BRANCH) {
-            util.overlayWindow("chrome://browser/content/sanitize.xul",
-                               function (win) prefOverlay(branch, false, {
-                append: {
-                    itemList: <>
-                        <listitem xmlns={XUL} label="See :help privacy for the following:" disabled="true" style="font-style: italic; font-weight: bold;"/>
-                        {
-                          template.map(ourItems(), function ([item, desc])
-                            <listitem xmlns={XUL} type="checkbox"
-                                      label={config.appName + " " + desc}
-                                      preference={branch + item}
-                                      onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>)
-                        }
-                    </>
-                },
-                init: function (win) {
-                    let elem =  win.document.getElementById("itemList");
-                    elem.setAttribute("rows", elem.itemCount);
-                    win.Sanitizer = Class("Sanitizer", win.Sanitizer, {
-                        sanitize: function sanitize() {
-                            self.withSavedValues(["sanitizing"], function () {
-                                self.sanitizing = true;
-                                sanitize.superapply(this, arguments);
-                                sanitizer.sanitizeItems([item.name for (item in values(self.itemMap))
-                                                         if (item.shouldSanitize(false))],
-                                                        Range.fromArray(this.range || []));
-                            }, this);
-                        }
-                    });
-                }
-            }));
-        }
+        util.timeout(function () { // Load order issue...
+
+            let (branch = Item.PREFIX + Item.SHUTDOWN_BRANCH) {
+                overlay.overlayWindow("chrome://browser/content/preferences/sanitize.xul",
+                                   function (win) prefOverlay(branch, true, {
+                    append: {
+                        SanitizeDialogPane:
+                            ["groupbox", { orient: "horizontal", xmlns: "xul" },
+                              ["caption", { label: config.appName + /*L*/" (see :help privacy)" }],
+                              ["grid", { flex: "1" },
+                                ["columns", {},
+                                    ["column", { flex: "1" }],
+                                    ["column", { flex: "1" }]],
+                                ["rows", {},
+                                  let (items = ourItems(true))
+                                     template.map(util.range(0, Math.ceil(items.length / 2)), function (i)
+                                         ["row", {},
+                                             template.map(items.slice(i * 2, i * 2 + 2), function (item)
+                                                ["checkbox", { xmlns: XUL, label: item.description, preference: branch + item.name }])])]]],
+                    }
+                }));
+            }
+            let (branch = Item.PREFIX + Item.BRANCH) {
+                overlay.overlayWindow("chrome://browser/content/sanitize.xul",
+                                   function (win) prefOverlay(branch, false, {
+                    append: {
+                        itemList: [
+                            ["listitem", { xmlns: "xul", label: /*L*/"See :help privacy for the following:",
+                                           disabled: "true", style: "font-style: italic; font-weight: bold;" }],
+                            template.map(ourItems(), function ([item, desc])
+                                ["listitem", { xmlns: "xul", preference: branch + item,
+                                               type: "checkbox", label: config.appName + ", " + desc,
+                                               onsyncfrompreference: "return gSanitizePromptDialog.onReadGeneric();" }]),
+                        ]
+                    },
+                    ready: function ready(win) {
+                        let elem =  win.document.getElementById("itemList");
+                        elem.setAttribute("rows", elem.itemCount);
+                        win.Sanitizer = Class("Sanitizer", win.Sanitizer, {
+                            sanitize: function sanitize() {
+                                self.withSavedValues(["sanitizing"], function () {
+                                    self.sanitizing = true;
+                                    sanitize.superapply(this, arguments);
+                                    sanitizer.sanitizeItems([item.name for (item in values(self.itemMap))
+                                                             if (item.shouldSanitize(false))],
+                                                            Range.fromArray(this.range || []));
+                                }, this);
+                            }
+                        });
+                    }
+                }));
+            }
+        });
     },
 
+    firstRun: 0,
+
     addItem: function addItem(name, params) {
         let item = this.itemMap[name] || Item(name, params);
         this.itemMap[name] = item;
@@ -241,7 +248,7 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
             if (!("value" in prop) || !callable(prop.value) && !(k in item))
                 Object.defineProperty(item, k, prop);
 
-        let names = set([name].concat(params.contains || []).map(function (e) "clear-" + e));
+        let names = Set([name].concat(params.contains || []).map(function (e) "clear-" + e));
         if (params.action)
             storage.addObserver("sanitizer",
                 function (key, event, arg) {
@@ -287,12 +294,32 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
         }
     },
 
-    get ranAtShutdown()    localPrefs.get("didSanitizeOnShutdown"),
-    set ranAtShutdown(val) localPrefs.set("didSanitizeOnShutdown", Boolean(val)),
+    /**
+     * Returns a load context for the given thing, to be used with
+     * interfaces needing one for per-window private browsing support.
+     *
+     * @param {Window|Document|Node} thing The thing for which to return
+     *      a load context.
+     */
+    getContext: function getContext(thing) {
+        if (!Ci.nsILoadContext)
+            return null;
+
+        if (thing instanceof Ci.nsIDOMNode && thing.ownerDocument)
+            thing = thing.ownerDocument;
+        if (thing instanceof Ci.nsIDOMDocument)
+            thing = thing.defaultView;
+        if (thing instanceof Ci.nsIInterfaceRequestor)
+            thing = thing.getInterface(Ci.nsIWebNavigation);
+        return thing.QueryInterface(Ci.nsILoadContext);
+    },
+
+    get ranAtShutdown()    config.prefs.get("didSanitizeOnShutdown"),
+    set ranAtShutdown(val) config.prefs.set("didSanitizeOnShutdown", Boolean(val)),
     get runAtShutdown()    prefs.get("privacy.sanitize.sanitizeOnShutdown"),
     set runAtShutdown(val) prefs.set("privacy.sanitize.sanitizeOnShutdown", Boolean(val)),
 
-    sanitize: function (items, range)
+    sanitize: function sanitize(items, range)
         this.withSavedValues(["sanitizing"], function () {
             this.sanitizing = true;
             let errors = this.sanitizeItems(items, range, null);
@@ -316,7 +343,7 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
             return errors;
         }),
 
-    sanitizeItems: function (items, range, host, key)
+    sanitizeItems: function sanitizeItems(items, range, host, key)
         this.withSavedValues(["sanitizing"], function () {
             this.sanitizing = true;
             if (items == null)
@@ -343,16 +370,18 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
         deny:    2,
         session: 8
     },
-    UNPERMS: Class.memoize(function () iter(this.PERMS).map(Array.reverse).toObject()),
+
+    UNPERMS: Class.Memoize(function () iter(this.PERMS).map(Array.reverse).toObject()),
+
     COMMANDS: {
-        unset:   "Unset",
-        allow:   "Allowed",
-        deny:    "Denied",
-        session: "Allowed for the current session",
-        list:    "List all cookies for domain",
-        clear:   "Clear all cookies for domain",
-        "clear-persistent": "Clear all persistent cookies for domain",
-        "clear-session":    "Clear all session cookies for domain"
+        unset:   /*L*/"Unset",
+        allow:   /*L*/"Allowed",
+        deny:    /*L*/"Denied",
+        session: /*L*/"Allowed for the current session",
+        list:    /*L*/"List all cookies for domain",
+        clear:   /*L*/"Clear all cookies for domain",
+        "clear-persistent": /*L*/"Clear all persistent cookies for domain",
+        "clear-session":    /*L*/"Clear all session cookies for domain"
     },
 
     argPrefMap: {
@@ -363,43 +392,48 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
     prefToArg: function (pref) pref.replace(/.*\./, "").toLowerCase(),
 
     iterCookies: function iterCookies(host) {
-        let iterator = host ? services.cookies.getCookiesFromHost(host)
-                            : services.cookies;
-        for (let c in iter(iterator))
-            yield c.QueryInterface(Ci.nsICookie2);
+        for (let c in iter(services.cookies, Ci.nsICookie2))
+            if (!host || util.isSubdomain(c.rawHost, host) ||
+                    c.host[0] == "." && c.host.length < host.length
+                        && host.indexOf(c.host) == host.length - c.host.length)
+                yield c;
+
     },
     iterPermissions: function iterPermissions(host) {
-        for (let p in iter(services.permissions)) {
-            p.QueryInterface(Ci.nsIPermission);
+        for (let p in iter(services.permissions, Ci.nsIPermission))
             if (!host || util.isSubdomain(p.host, host))
                 yield p;
-        }
     }
 }, {
-    load: function (dactyl, modules, window) {
-        if (sanitizer.runAtShutdown && !sanitizer.ranAtShutdown)
+    load: function initLoad(dactyl, modules, window) {
+        if (!sanitizer.firstRun++ && sanitizer.runAtShutdown && !sanitizer.ranAtShutdown)
             sanitizer.sanitizeItems(null, Range(), null, "shutdown");
         sanitizer.ranAtShutdown = false;
     },
-    autocommands: function (dactyl, modules, window) {
+    autocommands: function initAutocommands(dactyl, modules, window) {
+        const { autocommands } = modules;
+
         storage.addObserver("private-mode",
             function (key, event, value) {
-                modules.autocommands.trigger("PrivateMode", { state: value });
+                autocommands.trigger("PrivateMode", { state: value });
             }, window);
         storage.addObserver("sanitizer",
             function (key, event, value) {
                 if (event == "domain")
-                    modules.autocommands.trigger("SanitizeDomain", { domain: value });
+                    autocommands.trigger("SanitizeDomain", { domain: value });
                 else if (!value[1])
-                    modules.autocommands.trigger("Sanitize", { name: event.substr("clear-".length), domain: value[1] });
+                    autocommands.trigger("Sanitize", { name: event.substr("clear-".length), domain: value[1] });
             }, window);
     },
-    commands: function (dactyl, modules, window) {
+    commands: function initCommands(dactyl, modules, window) {
         const { commands } = modules;
         commands.add(["sa[nitize]"],
             "Clear private data",
             function (args) {
-                dactyl.assert(!modules.options['private'], "Cannot sanitize items in private mode");
+                dactyl.assert(!modules.options['private'], _("command.sanitize.privateMode"));
+
+                if (args["-host"] && !args.length && !args.bang)
+                    args[0] = "all";
 
                 let timespan = args["-timespan"] || modules.options["sanitizetimespan"];
 
@@ -409,32 +443,43 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
                     match ? 1000 * (Date.now() - 1000 * parseInt(num, 10) * { m: 60, h: 3600, d: 3600 * 24, w: 3600 * 24 * 7 }[unit])
                           : (timespan[0] == "s" ? sanitizer.sessionStart : null);
 
-                let items = args.slice();
-                if (args["-host"] && !args.length)
-                    args[0] = "all";
-
-                if (args.bang) {
-                    dactyl.assert(args.length == 0, _("error.trailing"));
-                    items = Object.keys(sanitizer.itemMap).filter(
-                        function (k) modules.options.get("sanitizeitems").has(k));
+                let opt = modules.options.get("sanitizeitems");
+                if (args.bang)
+                    dactyl.assert(args.length == 0, _("error.trailingCharacters"));
+                else {
+                    dactyl.assert(opt.validator(args), _("error.invalidArgument"));
+                    opt = { __proto__: opt, value: args.slice() };
                 }
-                else
-                    dactyl.assert(modules.options.get("sanitizeitems").validator(items), "Valid items required");
-
-                if (items.indexOf("all") >= 0)
-                    items = Object.keys(sanitizer.itemMap).filter(function (k) items.indexOf(k) === -1);
-
-                sanitizer.range = range.native;
-                sanitizer.ignoreTimespan = range.min == null;
-                sanitizer.sanitizing = true;
-                if (args["-host"]) {
-                    args["-host"].forEach(function (host) {
-                        sanitizer.sanitizing = true;
-                        sanitizer.sanitizeItems(items, range, host)
-                    });
+
+                let items = Object.keys(sanitizer.itemMap).slice(1).filter(opt.has, opt);
+
+                function sanitize(items) {
+                    sanitizer.range = range.native;
+                    sanitizer.ignoreTimespan = range.min == null;
+                    sanitizer.sanitizing = true;
+                    if (args["-host"]) {
+                        args["-host"].forEach(function (host) {
+                            sanitizer.sanitizing = true;
+                            sanitizer.sanitizeItems(items, range, host);
+                        });
+                    }
+                    else
+                        sanitizer.sanitize(items, range);
                 }
+
+                if (array.nth(opt.value, function (i) i == "all" || /^!/.test(i), 0) == "all" && !args["-host"])
+                    modules.commandline.input(_("sanitize.prompt.deleteAll") + " ",
+                        function (resp) {
+                            if (resp.match(/^y(es)?$/i)) {
+                                sanitize(items);
+                                dactyl.echomsg(_("command.sanitize.allDeleted"));
+                            }
+                            else
+                                dactyl.echo(_("command.sanitize.noneDeleted"));
+                        });
                 else
-                    sanitizer.sanitize(items, range);
+                    sanitize(items);
+
             },
             {
                 argCount: "*", // FIXME: should be + and 0
@@ -453,7 +498,7 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
                                 !args["-host"].some(function (host) util.isSubdomain(item.text, host)));
                             modules.completion.domain(context);
                         },
-                        type: modules.CommandOption.LIST,
+                        type: modules.CommandOption.LIST
                     }, {
                         names: ["-older", "-o"],
                         description: "Sanitize items older than timespan",
@@ -477,7 +522,7 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
             }
             function setPerms(host, perm) {
                 let uri = util.createURI(host);
-                services.permissions.remove(uri, "cookie");
+                services.permissions.remove(uri.host, "cookie");
                 services.permissions.add(uri, "cookie", Sanitizer.PERMS[perm]);
             }
             commands.add(["cookies", "ck"],
@@ -507,7 +552,7 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
                                 ["Host", "Expiry (UTC)", "Path", "Name", "Value"],
                                 ["padding-right: 1em", "padding-right: 1em", "padding-right: 1em", "max-width: 12em; overflow: hidden;", "padding-left: 1ex;"],
                                 ([c.host,
-                                  c.isSession ? <span highlight="Enabled">session</span>
+                                  c.isSession ? ["span", { highlight: "Enabled" }, "session"]
                                               : (new Date(c.expiry * 1000).toJSON() || "Never").replace(/:\d\d\.000Z/, "").replace("T", " ").replace(/-/g, "/"),
                                   c.path,
                                   c.name,
@@ -529,7 +574,7 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
                                 let count = [0, 0];
                                 for (let c in Sanitizer.iterCookies(host))
                                     count[c.isSession + 0]++;
-                                return <>{Sanitizer.COMMANDS[getPerms(host)]} (session: {count[1]} persistent: {count[0]})</>;
+                                return [Sanitizer.COMMANDS[getPerms(host)], " (session: ", count[1], " persistent: ", count[0], ")"].join("");
                             };
                             break;
                         case 1:
@@ -539,7 +584,7 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
                     },
                 });
     },
-    completion: function (dactyl, modules, window) {
+    completion: function initCompletion(dactyl, modules, window) {
         modules.completion.visibleHosts = function completeHosts(context) {
             let res = util.visibleHosts(window.content);
             if (context.filter && !res.some(function (host) host.indexOf(context.filter) >= 0))
@@ -552,9 +597,9 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
             context.completions = res;
         };
     },
-    options: function (dactyl, modules) {
+    options: function initOptions(dactyl, modules) {
         const options = modules.options;
-        if (services.has("privateBrowsing"))
+        if (services.has("privateBrowsing") && "privateBrowsingEnabled" in services.privateBrowsing)
             options.add(["private", "pornmode"],
                 "Set the 'private browsing' option",
                 "boolean", false,
@@ -573,9 +618,19 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
             "stringlist", "all",
             {
                 get values() values(sanitizer.itemMap).toArray(),
-                has: modules.Option.has.toggleAll,
+
+                completer: function completer(context, extra) {
+                    if (context.filter[0] == "!")
+                        context.advance(1);
+                    return completer.superapply(this, arguments);
+                },
+
+                has: function has(val)
+                    let (res = array.nth(this.value, function (v) v == "all" || v.replace(/^!/, "") == val, 0))
+                        res && !/^!/.test(res),
+
                 validator: function (values) values.length &&
-                    values.every(function (val) val === "all" || set.has(sanitizer.itemMap, val))
+                    values.every(function (val) val === "all" || Set.has(sanitizer.itemMap, val.replace(/^!/, "")))
             });
 
         options.add(["sanitizeshutdown", "ss"],
@@ -593,10 +648,10 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
                         sanitizer.runAtShutdown = false;
                     else {
                         sanitizer.runAtShutdown = true;
-                        let have = set(value);
+                        let have = Set(value);
                         for (let item in values(sanitizer.itemMap))
                             prefs.set(item.shutdownPref,
-                                      Boolean(set.has(have, item.name) ^ set.has(have, "all")));
+                                      Boolean(Set.has(have, item.name) ^ Set.has(have, "all")));
                     }
                     return value;
                 }
@@ -618,13 +673,13 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
                     "1d":      "Past day",
                     "1w":      "Past week"
                 },
-                validator: function (value) /^(a(ll)?|s(ession)|\d+[mhdw])$/.test(value)
+                validator: bind("test", /^(a(ll)?|s(ession)|\d+[mhdw])$/)
             });
 
         options.add(["cookies", "ck"],
             "The default mode for newly added cookie permissions",
             "stringlist", "session",
-            { get values() iter(Sanitizer.COMMANDS) });
+            { get values() Sanitizer.COMMANDS });
 
         options.add(["cookieaccept", "ca"],
             "When to accept cookies",
@@ -667,13 +722,13 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef
                 },
                 initialValue: true,
                 persist: false,
-                validator: function (val) parseInt(val) == val || modules.Option.validateCompleter.call(this, val)
+                validator: function validator(val) parseInt(val) == val || validator.superapply(this, arguments)
             });
     }
 });
 
 endModule();
 
-} catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);}
+// catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);}
 
 // vim: set fdm=marker sw=4 ts=4 et ft=javascript: