]> git.donarmstrong.com Git - dactyl.git/blobdiff - common/content/dactyl.js
Import r6923 from upstream hg supporting Firefox up to 22.0a1
[dactyl.git] / common / content / dactyl.js
index 2933dcd95d2a35e65ef0a1794d507e6b37188c3c..02ce9bd177eec85c419f7875c35bdd8caac1e2be 100644 (file)
@@ -1,17 +1,13 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2011 by Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2008-2012 Kris Maglione <maglione.k@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 */
+"use strict";
 
 /** @scope modules */
 
-default xml namespace = XHTML;
-XML.ignoreWhitespace = false;
-XML.prettyPrinting = false;
-
 var EVAL_ERROR = "__dactyl_eval_error";
 var EVAL_RESULT = "__dactyl_eval_result";
 var EVAL_STRING = "__dactyl_eval_string";
@@ -20,7 +16,8 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
     init: function () {
         window.dactyl = this;
         // cheap attempt at compatibility
-        let prop = { get: deprecated("dactyl", function liberator() dactyl) };
+        let prop = { get: deprecated("dactyl", function liberator() dactyl),
+                     configurable: true };
         Object.defineProperty(window, "liberator", prop);
         Object.defineProperty(modules, "liberator", prop);
         this.commands = {};
@@ -57,9 +54,9 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
         delete window.liberator;
 
         // Prevents box ordering bugs after our stylesheet is removed.
-        styles.system.add("cleanup-sheet", config.styleableChrome, <![CDATA[
+        styles.system.add("cleanup-sheet", config.styleableChrome, literal(/*
             #TabsToolbar tab { display: none; }
-        ]]>);
+        */));
         styles.unregisterSheet("resource://dactyl-skin/dactyl.css");
         DOM('#TabsToolbar tab', document).style.display;
     },
@@ -112,7 +109,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
 
     signals: {
         "io.source": function ioSource(context, file, modTime) {
-            if (context.INFO)
+            if (contexts.getDocs(context))
                 help.flush("help/plugins.xml", modTime);
         }
     },
@@ -167,6 +164,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
     NEW_WINDOW: "window",
 
     forceBackground: null,
+    forcePrivate: null,
     forceTarget: null,
 
     get forceOpen() ({ background: this.forceBackground,
@@ -285,7 +283,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
                 for (let obj in values(results)) {
                     let res = dactyl.generateHelp(obj, null, null, true);
                     if (!haveTag(obj.helpTag))
-                        res[1].@tag = obj.helpTag;
+                        res[0][1].tag = obj.helpTag;
 
                     yield res;
                 }
@@ -304,20 +302,15 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
                 bell: document.getElementById("dactyl-bell"),
                 strut: document.getElementById("dactyl-bell-strut")
             };
-            XML.ignoreWhitespace = true;
             if (!elems.bell)
                 overlay.overlayWindow(window, {
                     objects: elems,
-                    prepend: <>
-                        <window id={document.documentElement.id} xmlns={XUL}>
-                            <hbox style="display: none" highlight="Bell" id="dactyl-bell" key="bell"/>
-                        </window>
-                    </>,
-                    append: <>
-                        <window id={document.documentElement.id} xmlns={XUL}>
-                            <hbox style="display: none" highlight="Bell" id="dactyl-bell-strut" key="strut"/>
-                        </window>
-                    </>
+                    prepend: [
+                        ["window", { id: document.documentElement.id, xmlns: "xul" },
+                            ["hbox", { style: "display: none",  highlight: "Bell", id: "dactyl-bell", key: "bell" }]]],
+                    append: [
+                        ["window", { id: document.documentElement.id, xmlns: "xul" },
+                            ["hbox", { style: "display: none", highlight: "Bell", id: "dactyl-bell-strut", key: "strut" }]]]
                 }, elems);
 
             elems.bell.style.height = window.innerHeight + "px";
@@ -427,7 +420,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
         if (isObject(str) && "echoerr" in str)
             str = str.echoerr;
         else if (isinstance(str, ["Error", FailedAssertion]) && str.fileName)
-            str = <>{str.fileName.replace(/^.* -> /, "")}: {str.lineNumber}: {str}</>;
+            str = [str.fileName.replace(/^.* -> /, ""), ": ", str.lineNumber, ": ", str].join("");
 
         if (options["errorbells"])
             dactyl.beep();
@@ -475,11 +468,11 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
      * @param {Object} context The context object into which the script
      *     should be loaded.
      */
-    loadScript: function (uri, context) {
+    loadScript: function loadScript(uri, context) {
         JSMLoader.loadSubScript(uri, context, File.defaultEncoding);
     },
 
-    userEval: function (str, context, fileName, lineNumber) {
+    userEval: function userEval(str, context, fileName, lineNumber) {
         let ctxt;
         if (jsmodules.__proto__ != window && jsmodules.__proto__ != XPCNativeWrapper(window) &&
                 jsmodules.isPrototypeOf(context))
@@ -531,7 +524,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
      * Acts like the Function builtin, but the code executes in the
      * userContext global.
      */
-    userFunc: function () {
+    userFunc: function userFunc() {
         return this.userEval(
             "(function userFunction(" + Array.slice(arguments, 0, -1).join(", ") + ")" +
             " { " + arguments[arguments.length - 1] + " })");
@@ -546,7 +539,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
      * @param {boolean} silent Whether the command should be echoed on the
      *     command line.
      */
-    execute: function (str, modifiers, silent) {
+    execute: function execute(str, modifiers, silent) {
         // skip comments and blank lines
         if (/^\s*("|$)/.test(str))
             return;
@@ -630,7 +623,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
      * @param {string} feature The feature name.
      * @returns {boolean}
      */
-    has: function (feature) Set.has(config.features, feature),
+    has: function has(feature) Set.has(config.features, feature),
 
     /**
      * @private
@@ -656,17 +649,11 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
      */
     initHelp: function initHelp() {
         if ("noscriptOverlay" in window)
-            noscriptOverlay.safeAllow("dactyl:", true, false);
+            window.noscriptOverlay.safeAllow("dactyl:", true, false);
 
         help.initialize();
     },
 
-    stringifyXML: function (xml) {
-        XML.prettyPrinting = false;
-        XML.ignoreWhitespace = false;
-        return UTF8(xml.toXMLString());
-    },
-
     /**
      * Generates a help entry and returns it as a string.
      *
@@ -676,107 +663,94 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
      * @returns {string}
      */
     generateHelp: function generateHelp(obj, extraHelp, str, specOnly) {
-        default xml namespace = "";
-
         let link, tag, spec;
         link = tag = spec = util.identity;
         let args = null;
 
         if (obj instanceof Command) {
-            link = function (cmd) <ex>{cmd}</ex>;
+            link = function (cmd) ["ex", {}, cmd];
             args = obj.parseArgs("", CompletionContext(str || ""));
-            tag  = function (cmd) <>:{cmd}</>;
-            spec = function (cmd) <>{
-                    obj.count ? <oa>count</oa> : <></>
-                }{
-                    cmd
-                }{
-                    obj.bang ? <oa>!</oa> : <></>
-                }</>;
+            tag  = function (cmd) DOM.DOMString(":" + cmd);
+            spec = function (cmd) [
+                obj.count ? ["oa", {}, "count"] : [],
+                cmd,
+                obj.bang ? ["oa", {}, "!"] : []
+            ];
         }
         else if (obj instanceof Map) {
-            spec = function (map) obj.count ? <><oa>count</oa>{map}</> : <>{map}</>;
-            tag = function (map) <>{
-                    let (c = obj.modes[0].char) c ? c + "_" : ""
-                }{ map }</>;
+            spec = function (map) obj.count ? [["oa", {}, "count"], map] : DOM.DOMString(map);
+            tag = function (map) [
+                let (c = obj.modes[0].char) c ? c + "_" : "",
+                map
+            ]
             link = function (map) {
                 let [, mode, name, extra] = /^(?:(.)_)?(?:<([^>]+)>)?(.*)$/.exec(map);
-                let k = <k>{extra}</k>;
+                let k = ["k", {}, extra];
                 if (name)
-                    k.@name = name;
+                    k[1].name = name;
                 if (mode)
-                    k.@mode = mode;
+                    k[1].mode = mode;
                 return k;
             };
         }
         else if (obj instanceof Option) {
             spec = function () template.map(obj.names, tag, " ");
-            tag = function (name) <>'{name}'</>;
-            link = function (opt, name) <o>{name}</o>;
+            tag = function (name) DOM.DOMString("'" + name + "'");
+            link = function (opt, name) ["o", {}, name];
             args = { value: "", values: [] };
         }
 
-        XML.prettyPrinting = false;
-        XML.ignoreWhitespace = false;
-        default xml namespace = NS;
-
-        // E4X has its warts.
-        let br = <>
-                    </>;
-
-        let res = <res>
-                <dt>{link(obj.helpTag || tag(obj.name), obj.name)}</dt> <dd>{
-                    template.linkifyHelp(obj.description ? obj.description.replace(/\.$/, "") : "", true)
-                }</dd></res>;
+        let res = [
+                ["dt", {}, link(obj.helpTag || tag(obj.name), obj.name)],
+                ["dd", {},
+                    template.linkifyHelp(obj.description ? obj.description.replace(/\.$/, "") : "", true)]];
         if (specOnly)
-            return res.elements();
+            return res;
 
-        res.* += <>
-            <item>
-                <tags>{template.map(obj.names.slice().reverse(), tag, " ")}</tags>
-                <spec>{let (name = (obj.specs || obj.names)[0])
+        let description = ["description", {},
+            obj.description ? ["p", {}, template.linkifyHelp(obj.description.replace(/\.?$/, "."), true)] : "",
+            extraHelp ? extraHelp : "",
+            !(extraHelp || obj.description) ? ["p", {}, /*L*/ "Sorry, no help available."] : ""]
+
+        res.push(
+            ["item", {},
+                ["tags", {}, template.map(obj.names.slice().reverse(),
+                                          tag,
+                                          " ").join("")],
+                ["spec", {},
+                    let (name = (obj.specs || obj.names)[0])
                           spec(template.highlightRegexp(tag(name),
                                /\[(.*?)\]/g,
-                               function (m, n0) <oa>{n0}</oa>),
-                               name)
-                }</spec>{
-                !obj.type ? "" : <>
-                <type>{obj.type}</type>
-                <default>{obj.stringDefaultValue}</default></>}
-                <description>{
-                    obj.description ? br + <p>{template.linkifyHelp(obj.description.replace(/\.?$/, "."), true)}</p> : "" }{
-                        extraHelp ? br + extraHelp : "" }{
-                        !(extraHelp || obj.description) ? br + <p><!--L-->Sorry, no help available.</p> : "" }
-                </description>
-            </item></>;
+                               function (m, n0) ["oa", {}, n0]),
+                               name)],
+                !obj.type ? "" : [
+                    ["type", {}, obj.type],
+                    ["default", {}, obj.stringDefaultValue]],
+                description]);
 
         function add(ary) {
-            res.item.description.* += br +
-                let (br = br + <>    </>)
-                    <><dl>{ br + template.map(ary, function ([a, b]) <><dt>{a}</dt> <dd>{b}</dd></>, br) }
-                    </dl>
-                </>;
+            description.push(
+                ["dl", {}, template.map(ary,
+                                        function ([a, b]) [["dt", {}, a], " ",
+                                                           ["dd", {}, b]])]);
         }
 
-        if (obj.completer)
+        if (obj.completer && false)
             add(completion._runCompleter(obj.closure.completer, "", null, args).items
                           .map(function (i) [i.text, i.description]));
 
-        if (obj.options && obj.options.some(function (o) o.description))
+        if (obj.options && obj.options.some(function (o) o.description) && false)
             add(obj.options.filter(function (o) o.description)
                    .map(function (o) [
                         o.names[0],
-                        <>{o.description}{
-                            o.names.length == 1 ? "" :
-                                <> (short name: {
-                                    template.map(o.names.slice(1), function (n) <em>{n}</em>, <>, </>)
-                                })</>
-                        }</>
+                        [o.description,
+                         o.names.length == 1 ? "" :
+                             ["", " (short name: ",
+                                 template.map(o.names.slice(1), function (n) ["em", {}, n], ", "),
+                              ")"]]
                     ]));
-        return res.*.toXMLString()
-                  .replace(' xmlns="' + NS + '"', "", "g")
-                  .replace(/^ {12}|[ \t]+$/gm, "")
-                  .replace(/^\s*\n|\n\s*$/g, "") + "\n";
+
+        return DOM.toPrettyXML(res, true, null, { "": String(NS) });
     },
 
     /**
@@ -789,7 +763,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
         get: function globalVariables() this._globalVariables
     }),
 
-    loadPlugins: function (args, force) {
+    loadPlugins: function loadPlugins(args, force) {
         function sourceDirectory(dir) {
             dactyl.assert(dir.isReadable(), _("io.notReadable", dir.path));
 
@@ -846,7 +820,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
      * @param {string|Object} msg The message to print.
      * @param {number} level The logging level 0 - 15.
      */
-    log: function (msg, level) {
+    log: function log(msg, level) {
         let verbose = config.prefs.get("loglevel", 0);
 
         if (!level || level <= verbose) {
@@ -916,7 +890,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
      *     tabs.
      * @returns {boolean}
      */
-    open: function (urls, params, force) {
+    open: function open(urls, params, force) {
         if (typeof urls == "string")
             urls = dactyl.parseURLs(urls);
 
@@ -982,7 +956,11 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
                     });
 
                 case dactyl.NEW_WINDOW:
-                    let win = window.openDialog(document.documentURI, "_blank", "chrome,all,dialog=no");
+                    let options = ["chrome", "all", "dialog=no"];
+                    if (dactyl.forcePrivate)
+                        options.push("private");
+
+                    let win = window.openDialog(document.documentURI, "_blank", options.join(","));
                     util.waitFor(function () win.document.readyState === "complete");
                     browser = win.dactyl && win.dactyl.modules.config.tabbrowser || win.getBrowser();
                     // FALLTHROUGH
@@ -1035,7 +1013,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
                     // Try to find a matching file.
                     let file = io.File(url);
                     if (file.exists() && file.isReadable())
-                        return services.io.newFileURI(file).spec;
+                        return file.URI.spec;
                 }
                 catch (e) {}
             }
@@ -1060,15 +1038,15 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
         }, this);
     },
     stringToURLArray: deprecated("dactyl.parseURLs", "parseURLs"),
-    urlish: Class.Memoize(function () util.regexp(<![CDATA[
+    urlish: Class.Memoize(function () util.regexp(literal(/*
             ^ (
                 <domain>+ (:\d+)? (/ .*) |
                 <domain>+ (:\d+) |
                 <domain>+ \. [a-z0-9]+ |
                 localhost
             ) $
-        ]]>, "ix", {
-        domain: util.regexp(String.replace(<![CDATA[
+        */), "ix", {
+        domain: util.regexp(String.replace(literal(/*
             [^
                 U0000-U002c // U002d-U002e --.
                 U002f       // /
@@ -1077,7 +1055,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
                 U005b-U0060 // U0061-U007a A-Z
                 U007b-U007f
             ]
-        ]]>, /U/g, "\\u"), "x")
+        */), /U/g, "\\u"), "x")
     })),
 
     pluginFiles: {},
@@ -1105,7 +1083,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
      * @param {boolean} force Forcibly quit irrespective of whether all
      *    windows could be closed individually.
      */
-    quit: function (saveSession, force) {
+    quit: function quit(saveSession, force) {
         if (!force && !this.confirmQuit())
             return;
 
@@ -1122,7 +1100,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
     /**
      * Restart the host application.
      */
-    restart: function (args) {
+    restart: function restart(args) {
         if (!this.confirmQuit())
             return;
 
@@ -1198,7 +1176,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
      * @returns {Object}
      * @see Commands#parseArgs
      */
-    parseCommandLine: function (cmdline) {
+    parseCommandLine: function parseCommandLine(cmdline) {
         try {
             return commands.get("rehash").parseArgs(cmdline);
         }
@@ -1207,7 +1185,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
             return [];
         }
     },
-    wrapCallback: function (callback, self) {
+    wrapCallback: function wrapCallback(callback, self) {
         self = self || this;
         let save = ["forceOpen"];
         let saved = save.map(function (p) dactyl[p]);
@@ -1234,28 +1212,31 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
 }, {
     toolbarHidden: function hidden(elem) (elem.getAttribute("autohide") || elem.getAttribute("collapsed")) == "true"
 }, {
-    cache: function () {
+    cache: function initCache() {
         cache.register("help/plugins.xml", function () {
             // Process plugin help entries.
-            XML.ignoreWhiteSpace = XML.prettyPrinting = false;
 
-            let body = XML();
+            let body = [];
             for (let [, context] in Iterator(plugins.contexts))
                 try {
                     let info = contexts.getDocs(context);
-                    if (info instanceof XML) {
-                        if (info.*.@lang.length()) {
-                            let lang = config.bestLocale(String(a) for each (a in info.*.@lang));
-
-                            info.* = info.*.(function::attribute("lang").length() == 0 || @lang == lang);
-
-                            for each (let elem in info.NS::info)
-                                for (let attr in values(["@name", "@summary", "@href"]))
-                                    if (elem[attr].length())
-                                        info[attr] = elem[attr];
+                    if (DOM.isJSONXML(info)) {
+                        let langs = info.slice(2).filter(function (e) isArray(e) && isObject(e[1]) && e[1].lang);
+                        if (langs) {
+                            let lang = config.bestLocale(l[1].lang for each (l in langs));
+
+                            info = info.slice(0, 2).concat(
+                                info.slice(2).filter(function (e) !isArray(e) || !isObject(e[1])
+                                                               || e[1].lang == lang));
+
+                            for each (let elem in info.slice(2).filter(function (e) isArray(e) && e[0] == "info" && isObject(e[1])))
+                                for (let attr in values(["name", "summary", "href"]))
+                                    if (attr in elem[1])
+                                        info[attr] = elem[1][attr];
                         }
-                        body += <h2 xmlns={NS.uri} tag={info.@name + '-plugin'}>{info.@summary}</h2> +
-                            info;
+                        body.push(["h2", { xmlns: "dactyl", tag: info[1].name + '-plugin' },
+                                       String(info[1].summary)]);
+                        body.push(info);
                     }
                 }
                 catch (e) {
@@ -1264,63 +1245,54 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
 
             return '<?xml version="1.0"?>\n' +
                    '<?xml-stylesheet type="text/xsl" href="dactyl://content/help.xsl"?>\n' +
-                   '<!DOCTYPE document SYSTEM "resource://dactyl-content/dactyl.dtd">\n' +
-                   <document xmlns={NS}
-                       name="plugins" title={config.appName + " Plugins"}>
-                       <h1 tag="using-plugins">{_("help.title.Using Plugins")}</h1>
-                       <toc start="2"/>
-
-                       {body}
-                   </document>.toXMLString();
+                   DOM.toXML(
+                       ["document", { xmlns: "dactyl", name: "plugins",
+                                      title: config.appName + ", Plugins" },
+                           ["h1", { tag: "using-plugins" }, _("help.title.Using Plugins")],
+                           ["toc", { start: "2" }],
+
+                           body]);
         });
 
         cache.register("help/index.xml", function () {
-            default xml namespace = NS;
-
             return '<?xml version="1.0"?>\n' +
-                   <overlay xmlns={NS}>{
-                   template.map(dactyl.indices, function ([name, iter])
-                       <dl insertafter={name + "-index"}>{
-                           template.map(iter(), util.identity)
-                       }</dl>, <>{"\n\n"}</>)
-                   }</overlay>;
+                   DOM.toXML(["overlay", { xmlns: "dactyl" },
+                       template.map(dactyl.indices, function ([name, iter])
+                           ["dl", { insertafter: name + "-index" },
+                               template.map(iter(), util.identity)],
+                           "\n\n")]);
         });
 
         cache.register("help/gui.xml", function () {
-            default xml namespace = NS;
-
             return '<?xml version="1.0"?>\n' +
-                   <overlay xmlns={NS}>
-                       <dl insertafter="dialog-list">{
-                       template.map(config.dialogs, function ([name, val])
-                           (!val[2] || val[2]())
-                               ? <><dt>{name}</dt><dd>{val[0]}</dd></>
-                               : undefined,
-                           <>{"\n"}</>)
-                       }</dl>
-                   </overlay>;
+                   DOM.toXML(["overlay", { xmlns: "dactyl" },
+                       ["dl", { insertafter: "dialog-list" },
+                           template.map(config.dialogs, function ([name, val])
+                               (!val[2] || val[2]())
+                                   ? [["dt", {}, name],
+                                      ["dd", {}, val[0]]]
+                                   : undefined,
+                               "\n")]]);
         });
 
         cache.register("help/privacy.xml", function () {
-            default xml namespace = NS;
-
             return '<?xml version="1.0"?>\n' +
-                   <overlay xmlns={NS}>
-                       <dl insertafter="sanitize-items">{
-                       template.map(options.get("sanitizeitems").values
-                           .sort(function (a, b) String.localeCompare(a.name, b.name)),
-                           function ({ name, description })
-                           <><dt>{name}</dt><dd>{template.linkifyHelp(description, true)}</dd></>,
-                           <>{"\n"}</>)
-                       }</dl>
-                   </overlay>;
+                   DOM.toXML(["overlay", { xmlns: "dactyl" },
+                       ["dl", { insertafter: "sanitize-items" },
+                           template.map(options.get("sanitizeitems").values
+                                                .sort(function (a, b) String.localeCompare(a.name,
+                                                                                           b.name)),
+                               function ({ name, description })
+                               [["dt", {}, name],
+                                ["dd", {}, template.linkifyHelp(description, true)]],
+                               "\n")]]);
         });
     },
-    events: function () {
+    events: function initEvents() {
         events.listen(window, dactyl, "events", true);
     },
     // Only general options are added here, which are valid for all Dactyl extensions
-    options: function () {
+    options: function initOptions() {
         options.add(["errorbells", "eb"],
             "Ring the bell when an error message is displayed",
             "boolean", false);
@@ -1453,15 +1425,16 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
                             document.title = document.title.replace(RegExp("(.*)" + util.regexp.escape(old)), "$1" + current);
                     }
 
-                    if (services.has("privateBrowsing")) {
+                    if (win.hasAttribute("titlemodifier_privatebrowsing")) {
                         let oldValue = win.getAttribute("titlemodifier_normal");
                         let suffix = win.getAttribute("titlemodifier_privatebrowsing").substr(oldValue.length);
 
                         win.setAttribute("titlemodifier_normal", value);
                         win.setAttribute("titlemodifier_privatebrowsing", value + suffix);
 
-                        if (services.privateBrowsing.privateBrowsingEnabled) {
+                        if (storage.privateMode) {
                             updateTitle(oldValue + suffix, value + suffix);
+                            win.setAttribute("titlemodifier", value + suffix);
                             return value;
                         }
                     }
@@ -1495,7 +1468,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
             });
     },
 
-    mappings: function () {
+    mappings: function initMappings() {
         if (dactyl.has("session"))
             mappings.add([modes.NORMAL], ["ZQ"],
                 "Quit and don't save the session",
@@ -1506,7 +1479,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
             function () { dactyl.quit(true); });
     },
 
-    commands: function () {
+    commands: function initCommands() {
         commands.add(["dia[log]"],
             "Open a " + config.appName + " dialog",
             function (args) {
@@ -1593,6 +1566,21 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
                 literal: 0
             });
 
+        commands.add(["pr[ivate]", "pr0n", "porn"],
+            "Enable privacy features of a command, when applicable, and do not save the invocation in command history",
+            function (args) {
+                dactyl.withSavedValues(["forcePrivate"], function () {
+                    this.forcePrivate = true;
+                    dactyl.execute(args[0], null, true);
+                });
+            }, {
+                argCount: "1",
+                completer: function (context) completion.ex(context),
+                literal: 0,
+                privateData: "never-save",
+                subCommand: 0
+            });
+
         commands.add(["exit", "x"],
             "Quit " + config.appName,
             function (args) {
@@ -1649,13 +1637,13 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
             "Reload the " + config.appName + " add-on",
             function (args) {
                 if (args.trailing)
-                    storage.session.rehashCmd = args.trailing; // Hack.
+                    storage.storeForSession("rehashCmd", args.trailing); // Hack.
                 args.break = true;
 
                 if (args["+purgecaches"])
                     cache.flush();
 
-                util.rehash(args);
+                util.delay(function () { util.rehash(args) });
             },
             {
                 argCount: "0", // FIXME
@@ -1753,14 +1741,24 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
                             totalUnits = "msec";
 
                         commandline.commandOutput(
-                                <table>
-                                    <tr highlight="Title" align="left">
-                                        <th colspan="3">{_("title.Code execution summary")}</th>
-                                    </tr>
-                                    <tr><td>&#xa0;&#xa0;{_("title.Executed")}:</td><td align="right"><span class="times-executed">{count}</span></td><td><!--L-->times</td></tr>
-                                    <tr><td>&#xa0;&#xa0;{_("title.Average time")}:</td><td align="right"><span class="time-average">{each.toFixed(2)}</span></td><td>{eachUnits}</td></tr>
-                                    <tr><td>&#xa0;&#xa0;{_("title.Total time")}:</td><td align="right"><span class="time-total">{total.toFixed(2)}</span></td><td>{totalUnits}</td></tr>
-                                </table>);
+                                ["table", {}
+                                    ["tr", { highlight: "Title", align: "left" },
+                                        ["th", { colspan: "3" }, _("title.Code execution summary")]],
+                                    ["tr", {},
+                                        ["td", {}, _("title.Executed"), ":"],
+                                        ["td", { align: "right" },
+                                            ["span", { class: "times-executed" }, count]],
+                                        ["td", {}, /*L*/"times"]],
+                                    ["tr", {},
+                                        ["td", {}, _("title.Average time"), ":"],
+                                        ["td", { align: "right" },
+                                            ["span", { class: "time-average" }, each.toFixed(2)]],
+                                        ["td", {}, eachUnits]],
+                                    ["tr", {},
+                                        ["td", {}, _("title.Total time"), ":"],
+                                        ["td", { align: "right" },
+                                            ["span", { class: "time-total" }, total.toFixed(2)]],
+                                        ["td", {}, totalUnits]]]);
                     }
                     else {
                         let beforeTime = Date.now();
@@ -1828,9 +1826,10 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
                     let date = config.buildDate;
                     date = date ? " (" + date + ")" : "";
 
-                    commandline.commandOutput(
-                        <div>{config.appName} {config.version}{date} running on: </div> +
-                        <div>{navigator.userAgent}</div>)
+                    commandline.commandOutput([
+                        ["div", {}, [config.appName, " ", config.version, date, " running on: "].join("")],
+                        ["div", {}, [window.navigator.userAgent].join("")]
+                    ])
                 }
             }, {
                 argCount: "0",
@@ -1839,7 +1838,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
 
     },
 
-    completion: function () {
+    completion: function initCompletion() {
         completion.dialog = function dialog(context) {
             context.title = ["Dialog"];
             context.filters.push(function ({ item }) !item[2] || item[2]());
@@ -1870,7 +1869,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
             context.completions = dactyl.windows;
         };
     },
-    load: function () {
+    load: function initLoad() {
         dactyl.triggerObserver("load");
 
         dactyl.log(_("dactyl.modulesLoaded"), 3);
@@ -1878,6 +1877,22 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
         userContext.DOM = Class("DOM", DOM, { init: function DOM_(sel, ctxt) DOM(sel, ctxt || buffer.focusedFrame.document) });
         userContext.$ = modules.userContext.DOM;
 
+        // Hack: disable disabling of Personas in private windows.
+        let root = document.documentElement;
+
+        if (PrivateBrowsingUtils && PrivateBrowsingUtils.isWindowPrivate(window)
+                && root._lightweightTheme
+                && root._lightweightTheme._lastScreenWidth == null) {
+
+            dactyl.withSavedValues.call(PrivateBrowsingUtils,
+                                      ["isWindowPrivate"], function () {
+                PrivateBrowsingUtils.isWindowPrivate = function () false;
+
+                Cu.import("resource://gre/modules/LightweightThemeConsumer.jsm", {})
+                  .LightweightThemeConsumer.call(root._lightweightTheme, document);
+            });
+        }
+
         dactyl.timeout(function () {
             try {
                 var args = config.prefs.get("commandline-args")