diff options
Diffstat (limited to 'common/modules')
-rw-r--r-- | common/modules/base.jsm | 75 | ||||
-rw-r--r-- | common/modules/bootstrap.jsm | 22 | ||||
-rw-r--r-- | common/modules/commands.jsm | 4 | ||||
-rw-r--r-- | common/modules/config.jsm | 38 | ||||
-rw-r--r-- | common/modules/help.jsm | 326 | ||||
-rw-r--r-- | common/modules/io.jsm | 4 | ||||
-rw-r--r-- | common/modules/overlay.jsm | 4 | ||||
-rw-r--r-- | common/modules/protocol.jsm | 228 | ||||
-rw-r--r-- | common/modules/sanitizer.jsm | 2 | ||||
-rw-r--r-- | common/modules/services.jsm | 1 | ||||
-rw-r--r-- | common/modules/template.jsm | 10 |
11 files changed, 672 insertions, 42 deletions
diff --git a/common/modules/base.jsm b/common/modules/base.jsm index b7b7d441..7c72334a 100644 --- a/common/modules/base.jsm +++ b/common/modules/base.jsm @@ -133,7 +133,8 @@ function defineModule(name, params, module) { module.NAME = name; module.EXPORTED_SYMBOLS = params.exports || []; - defineModule.loadLog.push("defineModule " + name); + defineModule.loadLog.push("[Begin " + name + "]"); + defineModule.prefix += " "; for (let [, mod] in Iterator(params.require || [])) require(module, mod); @@ -152,11 +153,13 @@ function defineModule(name, params, module) { defineModule.loadLog = []; Object.defineProperty(defineModule.loadLog, "push", { value: function (val) { + val = defineModule.prefix + val; if (true) defineModule.dump(val + "\n"); this[this.length] = Date.now() + " " + val; } }); +defineModule.prefix = ""; defineModule.dump = function dump_() { let msg = Array.map(arguments, function (msg) { if (loaded.util && typeof msg == "object") @@ -185,7 +188,8 @@ defineModule.time = function time(major, minor, func, self) { } function endModule() { - defineModule.loadLog.push("endModule " + currentModule.NAME); + defineModule.prefix = defineModule.prefix.slice(0, -2); + defineModule.loadLog.push("(End " + currentModule.NAME + ")"); for (let [, mod] in Iterator(use[currentModule.NAME] || [])) require(mod, currentModule.NAME, "use"); @@ -202,7 +206,7 @@ function require(obj, name, from) { let caller = Components.stack.caller; if (!loaded[name]) - defineModule.loadLog.push(" " + (from || "require") + ": loading " + name + " into " + (obj.NAME || caller.filename + ":" + caller.lineNumber)); + defineModule.loadLog.push((from || "require") + ": loading " + name + " into " + (obj.NAME || caller.filename + ":" + caller.lineNumber)); JSMLoader.load(name + ".jsm", obj); return obj; @@ -220,11 +224,12 @@ defineModule("base", { // sed -n 's/^(const|function) ([a-zA-Z0-9_]+).*/ "\2",/p' base.jsm | sort | fmt exports: [ "ErrorBase", "Cc", "Ci", "Class", "Cr", "Cu", "Module", "JSMLoader", "Object", "Runnable", - "Set", "Struct", "StructBase", "Timer", "UTF8", "XPCOM", "XPCOMUtils", "XPCSafeJSObjectWrapper", - "array", "bind", "call", "callable", "ctypes", "curry", "debuggerProperties", "defineModule", - "deprecated", "endModule", "forEach", "isArray", "isGenerator", "isinstance", "isObject", - "isString", "isSubclass", "iter", "iterAll", "iterOwnProperties", "keys", "memoize", "octal", - "properties", "require", "set", "update", "values", "withCallerGlobal" + "Set", "Struct", "StructBase", "Timer", "UTF8", "XPCOM", "XPCOMShim", "XPCOMUtils", + "XPCSafeJSObjectWrapper", "array", "bind", "call", "callable", "ctypes", "curry", + "debuggerProperties", "defineModule", "deprecated", "endModule", "forEach", "isArray", + "isGenerator", "isinstance", "isObject", "isString", "isSubclass", "iter", "iterAll", + "iterOwnProperties", "keys", "memoize", "octal", "properties", "require", "set", "update", + "values", "withCallerGlobal" ], use: ["config", "services", "util"] }, this); @@ -754,6 +759,10 @@ function Class() { constructor: { value: Constructor }, }); self.instance = self; + + if ("_metaInit_" in self && self._metaInit_) + self._metaInit_.apply(self, arguments); + var res = self.init.apply(self, arguments); return res !== undefined ? res : self; })]]>, @@ -1022,7 +1031,7 @@ function XPCOM(interfaces, superClass) { interfaces = Array.concat(interfaces); let shim = interfaces.reduce(function (shim, iface) shim.QueryInterface(iface), - Cc["@dactyl.googlecode.com/base/xpc-interface-shim"].createInstance()); + XPCOMShim()); let res = Class("XPCOM(" + interfaces + ")", superClass || Class, update( iter.toObject([k, v === undefined || callable(v) ? function stub() null : v] @@ -1031,6 +1040,18 @@ function XPCOM(interfaces, superClass) { shim = interfaces = null; return res; } +function XPCOMShim() { + let ip = services.InterfacePointer({ + QueryInterface: function (iid) { + if (iid.equals(Ci.nsISecurityCheckedComponent)) + throw Components.results.NS_ERROR_NO_INTERFACE; + return this; + }, + getHelperForLanguage: function () null, + getInterfaces: function (count) { count.value = 0; } + }); + return ip.data; +}; /** * An abstract base class for classes that wish to inherit from Error. @@ -1065,24 +1086,32 @@ var ErrorBase = Class("ErrorBase", Error, { * @returns {Class} */ function Module(name, prototype) { - let init = callable(prototype) ? 4 : 3; - let proto = arguments[callable(prototype) ? 2 : 1]; + try { + let init = callable(prototype) ? 4 : 3; + let proto = arguments[callable(prototype) ? 2 : 1]; - proto._metaInit_ = function () { - delete module.prototype._metaInit_; - currentModule[name.toLowerCase()] = this; - }; + proto._metaInit_ = function () { + delete module.prototype._metaInit_; + currentModule[name.toLowerCase()] = this; + }; - const module = Class.apply(Class, Array.slice(arguments, 0, init)); - let instance = module(); - module.className = name.toLowerCase(); + const module = Class.apply(Class, Array.slice(arguments, 0, init)); + let instance = module(); + module.className = name.toLowerCase(); - instance.INIT = update(Object.create(Module.INIT), - arguments[init] || {}); + instance.INIT = update(Object.create(Module.INIT), + arguments[init] || {}); - currentModule[module.className] = instance; - defineModule.modules.push(instance); - return module; + currentModule[module.className] = instance; + defineModule.modules.push(instance); + return module; + } + catch (e) { + if (typeof e === "string") + e = Error(e); + + dump(e.fileName + ":" + e.lineNumber + ": " + e + "\n" + (e.stack || Error().stack)); + } } Module.INIT = { init: function Module_INIT_init(dactyl, modules, window) { diff --git a/common/modules/bootstrap.jsm b/common/modules/bootstrap.jsm index d51f8696..fc4eac60 100644 --- a/common/modules/bootstrap.jsm +++ b/common/modules/bootstrap.jsm @@ -19,11 +19,11 @@ if (!JSMLoader && "@mozilla.org/fuel/application;1" in Components.classes) .getService(Components.interfaces.extIApplication) .storage.get("dactyl.JSMLoader", null); -if (JSMLoader && JSMLoader.bump === 5) +if (JSMLoader && JSMLoader.bump === 6) JSMLoader.global = this; else JSMLoader = { - bump: 5, + bump: 6, builtin: Cu.Sandbox(this), @@ -167,6 +167,24 @@ else } }, + Factory: function Factory(clas) ({ + __proto__: clas.prototype, + + createInstance: function (outer, iid) { + try { + if (outer != null) + throw Cr.NS_ERROR_NO_AGGREGATION; + if (!clas.instance) + clas.instance = new clas(); + return clas.instance.QueryInterface(iid); + } + catch (e) { + Cu.reportError(e); + throw e; + } + } + }), + registerFactory: function registerFactory(factory) { this.manager.registerFactory(factory.classID, String(factory.classID), diff --git a/common/modules/commands.jsm b/common/modules/commands.jsm index 95d4b2eb..a837ef33 100644 --- a/common/modules/commands.jsm +++ b/common/modules/commands.jsm @@ -12,7 +12,7 @@ Components.utils.import("resource://dactyl/bootstrap.jsm"); defineModule("commands", { exports: ["ArgType", "Command", "Commands", "CommandOption", "Ex", "commands"], require: ["contexts", "messages", "util"], - use: ["config", "options", "services", "template"] + use: ["config", "help", "options", "services", "template"] }, this); /** @@ -1585,7 +1585,7 @@ var Commands = Module("commands", { cmd.hive == commands.builtin ? "" : <span highlight="Object" style="padding-right: 1em;">{cmd.hive.name}</span> ] })), - iterateIndex: function (args) let (tags = services["dactyl:"].HELP_TAGS) + iterateIndex: function (args) let (tags = help.tags) this.iterate(args).filter(function (cmd) cmd.hive === commands.builtin || Set.has(tags, cmd.helpTag)), format: { headings: ["Command", "Group", "Description"], diff --git a/common/modules/config.jsm b/common/modules/config.jsm index f5d231cb..482216b2 100644 --- a/common/modules/config.jsm +++ b/common/modules/config.jsm @@ -6,16 +6,34 @@ // given in the LICENSE.txt file included with this file. "use strict"; -try { - let global = this; Components.utils.import("resource://dactyl/bootstrap.jsm"); defineModule("config", { exports: ["ConfigBase", "Config", "config"], - require: ["services", "storage", "util", "template"], + require: ["protocol", "services", "storage", "util", "template"], use: ["io", "messages", "prefs", "styles"] }, this); +function AboutHandler() {} +AboutHandler.prototype = { + get classDescription() "About " + config.appName + " Page", + + classID: Components.ID("81495d80-89ee-4c36-a88d-ea7c4e5ac63f"), + + get contractID() "@mozilla.org/network/protocol/about;1?what=" + config.name, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]), + + newChannel: function (uri) { + let channel = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService) + .newChannel("dactyl://content/about.xul", null, null); + channel.originalURI = uri; + return channel; + }, + + getURIFlags: function (uri) Ci.nsIAboutModule.ALLOW_SCRIPT, +}; + var ConfigBase = Class("ConfigBase", { /** * Called on dactyl startup to allow for any arbitrary application-specific @@ -26,9 +44,19 @@ var ConfigBase = Class("ConfigBase", { if (this.haveGecko("2b")) Set.add(this.features, "Gecko2"); + JSMLoader.registerFactory(JSMLoader.Factory(AboutHandler)); + JSMLoader.registerFactory(JSMLoader.Factory( + Protocol("dactyl", "{9c8f2530-51c8-4d41-b356-319e0b155c44}", + "resource://dactyl-content/"))); + this.timeout(function () { services["dactyl:"].pages.dtd = function () [null, util.makeDTD(config.dtd)]; }); + + update(services["dactyl:"].providers, { + "locale": function (uri, path) LocaleChannel("dactyl-locale", config.locale, path, uri), + "locale-local": function (uri, path) LocaleChannel("dactyl-local-locale", config.locale, path, uri) + }); }, modules: { @@ -41,12 +69,14 @@ var ConfigBase = Class("ConfigBase", { "contexts", "downloads", "finder", + "help", "highlight", "javascript", "messages", "options", "overlay", "prefs", + "protocol", "sanitizer", "services", "storage", @@ -1065,6 +1095,6 @@ config.INIT = update(Object.create(config.INIT), config.INIT, { endModule(); -} catch(e){ if (typeof e === "string") e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); } +// catch(e){ if (typeof e === "string") e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); } // vim: set fdm=marker sw=4 sts=4 et ft=javascript: diff --git a/common/modules/help.jsm b/common/modules/help.jsm new file mode 100644 index 00000000..c6dfd445 --- /dev/null +++ b/common/modules/help.jsm @@ -0,0 +1,326 @@ +// Copyright (c) 2008-2011 by 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"; + +Components.utils.import("resource://dactyl/bootstrap.jsm"); +defineModule("help", { + exports: ["help"], + require: ["protocol", "services", "util"], + use: ["config", "highlight", "messages", "template"] +}, this); + +var Help = Module("Help", { + init: function init() { + this.initialized = false; + this.files = {}; + this.overlays = {}; + this.tags = {}; + + function Loop(fn) + function (uri, path) { + if (!help.initialized) + return RedirectChannel(uri.spec, uri, 1); + return fn.apply(this, arguments); + } + + update(services["dactyl:"].providers, { + "help": Loop(function (uri, path) help.files[path]), + "help-overlay": Loop(function (uri, path) help.overlays[path]), + "help-tag": Loop(function (uri, path) { + let tag = decodeURIComponent(path); + if (tag in help.files) + return RedirectChannel("dactyl://help/" + tag, uri); + if (tag in help.tags) + return RedirectChannel("dactyl://help/" + help.tags[tag] + "#" + tag.replace(/#/g, encodeURIComponent), uri); + }) + }); + }, + + Local: function Local(dactyl, modules, window) ({ + init: function init() { + dactyl.commands["dactyl.help"] = function (event) { + let elem = event.originalTarget; + help.help(elem.getAttribute("tag") || elem.textContent); + }; + }, + + /** + * Returns the URL of the specified help *topic* if it exists. + * + * @param {string} topic The help topic to look up. + * @param {boolean} consolidated Whether to search the consolidated help page. + * @returns {string} + */ + findHelp: function (topic, consolidated) { + if (!consolidated && Set.has(help.files, topic)) + return topic; + let items = modules.completion._runCompleter("help", topic, null, !!consolidated).items; + let partialMatch = null; + + function format(item) item.description + "#" + encodeURIComponent(item.text); + + for (let [i, item] in Iterator(items)) { + if (item.text == topic) + return format(item); + else if (!partialMatch && topic) + partialMatch = item; + } + + if (partialMatch) + return format(partialMatch); + return null; + }, + + /** + * Opens the help page containing the specified *topic* if it exists. + * + * @param {string} topic The help topic to open. + * @param {boolean} consolidated Whether to use the consolidated help page. + */ + help: function (topic, consolidated) { + dactyl.initHelp(); + if (!topic) { + let helpFile = consolidated ? "all" : modules.options["helpfile"]; + + if (Set.has(help.files, helpFile)) + dactyl.open("dactyl://help/" + helpFile, { from: "help" }); + else + dactyl.echomsg(_("help.noFile", helpFile.quote())); + return; + } + + let page = this.findHelp(topic, consolidated); + dactyl.assert(page != null, _("help.noTopic", topic)); + + dactyl.open("dactyl://help/" + page, { from: "help" }); + } + + }), + + // Find the tags in the document. + addTags: function addTags(file, doc) { + for (let elem in DOM.XPath("//@tag|//dactyl:tags/text()|//dactyl:tag/text()", doc)) + for (let tag in values((elem.value || elem.textContent).split(/\s+/))) + this.tags[tag] = file; + }, + + namespaces: ["locale-local", "locale"], + + // Find help and overlay files with the given name. + findHelpFile: function findHelpFile(file) { + let result = []; + for (let namespace in values(this.namespaces)) { + let url = ["dactyl://", namespace, "/", file, ".xml"].join(""); + let res = util.httpGet(url); + if (res) { + if (res.responseXML.documentElement.localName == "document") + this.files[file] = url; + if (res.responseXML.documentElement.localName == "overlay") + this.overlays[file] = url; + result.push(res.responseXML); + } + } + return result; + }, + + initialize: function initialize(force) { + // Waits for the add-on to become available, if necessary. + config.addon; + config.version; + + if (force || !this.initialized) { + + this.files["versions"] = function () { + let NEWS = util.httpGet(config.addon.getResourceURI("NEWS").spec, + { mimeType: "text/plain;charset=UTF-8" }) + .responseText; + + let re = util.regexp(<![CDATA[ + ^ (?P<comment> \s* # .*\n) + + | ^ (?P<space> \s*) + (?P<char> [-•*+]) \ // + (?P<content> .*\n + (?: \2\ \ .*\n | \s*\n)* ) + + | (?P<par> + (?: ^ [^\S\n]* + (?:[^-•*+\s] | [-•*+]\S) + .*\n + )+ + ) + + | (?: ^ [^\S\n]* \n) + + ]]>, "gmxy"); + + let betas = util.regexp(/\[(b\d)\]/, "gx"); + + let beta = array(betas.iterate(NEWS)) + .map(function (m) m[1]).uniq().slice(-1)[0]; + + + default xml namespace = NS; + function rec(text, level, li) { + XML.ignoreWhitespace = XML.prettyPrinting = false; + + let res = <></>; + let list, space, i = 0; + + + for (let match in re.iterate(text)) { + if (match.comment) + continue; + else if (match.char) { + if (!list) + res += list = <ul/>; + let li = <li/>; + li.* += rec(match.content.replace(RegExp("^" + match.space, "gm"), ""), level + 1, li); + list.* += li; + } + else if (match.par) { + let [, par, tags] = /([^]*?)\s*((?:\[[^\]]+\])*)\n*$/.exec(match.par); + let t = tags; + tags = array(betas.iterate(tags)).map(function (m) m[1]); + + let group = !tags.length ? "" : + !tags.some(function (t) t == beta) ? "HelpNewsOld" : "HelpNewsNew"; + if (i === 0 && li) { + li.@highlight = group; + group = ""; + } + + list = null; + if (level == 0 && /^.*:\n$/.test(match.par)) { + let text = par.slice(0, -1); + res += <h2 tag={"news-" + text}>{template.linkifyHelp(text, true)}</h2>; + } + else { + let [, a, b] = /^(IMPORTANT:?)?([^]*)/.exec(par); + res += <p highlight={group + " HelpNews"}>{ + !tags.length ? "" : + <hl key="HelpNewsTag">{tags.join(" ")}</hl> + }{ + a ? <hl key="HelpWarning">{a}</hl> : "" + }{ + template.linkifyHelp(b, true) + }</p>; + } + } + i++; + } + for each (let attr in res..@highlight) { + attr.parent().@NS::highlight = attr; + delete attr.parent().@highlight; + } + return res; + } + + XML.ignoreWhitespace = XML.prettyPrinting = false; + let body = rec(NEWS, 0); + for each (let li in body..li) { + let list = li..li.(@NS::highlight == "HelpNewsOld"); + if (list.length() && list.length() == li..li.(@NS::highlight != "").length()) { + for each (let li in list) + li.@NS::highlight = ""; + li.@NS::highlight = "HelpNewsOld"; + } + } + + + return ["application/xml", + '<?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} xmlns:dactyl={NS} + name="versions" title={config.appName + " Versions"}> + <h1 tag="versions news NEWS">{config.appName} Versions</h1> + <toc start="2"/> + + {body} + </document>.toXMLString() + ]; + } + + + + // Scrape the list of help files from all.xml + // Manually process main and overlay files, since XSLTProcessor and + // XMLHttpRequest don't allow access to chrome documents. + this.tags["all"] = this.tags["all.xml"] = "all"; + let files = this.findHelpFile("all").map(function (doc) + [f.value for (f in DOM.XPath("//dactyl:include/@href", doc))]); + + // Scrape the tags from the rest of the help files. + array.flatten(files).forEach(function (file) { + this.tags[file + ".xml"] = file; + this.findHelpFile(file).forEach(function (doc) { + this.addTags(file, doc); + }, this); + }, this); + + this.tags["versions"] = this.tags["versions.xml"] = "versions"; + + this.addTags("versions", util.httpGet("dactyl://help/versions").responseXML); + + help.initialized = true; + } + }, +}, { +}, { + commands: function init_commands(dactyl, modules, window) { + const { commands, completion, help } = modules; + + [ + { + name: "h[elp]", + description: "Open the introductory help page" + }, { + name: "helpa[ll]", + description: "Open the single consolidated help page" + } + ].forEach(function (command) { + let consolidated = command.name == "helpa[ll]"; + + commands.add([command.name], + command.description, + function (args) { + dactyl.assert(!args.bang, _("help.dontPanic")); + help.help(args.literalArg, consolidated); + }, { + argCount: "?", + bang: true, + completer: function (context) completion.help(context, consolidated), + literal: 0 + }); + }); + }, + completion: function init_completion(dactyl, modules, window) { + const { completion } = modules; + + completion.help = function completion_help(context, consolidated) { + dactyl.initHelp(); + context.title = ["Help"]; + context.anchored = false; + context.completions = help.tags; + if (consolidated) + context.keys = { text: 0, description: function () "all" }; + }; + }, + mappings: function init_mappings(dactyl, modules, window) { + const { help, mappings, modes } = modules; + + mappings.add([modes.MAIN], ["<open-help>", "<F1>"], + "Open the introductory help page", + function () { help.help(); }); + + mappings.add([modes.MAIN], ["<open-single-help>", "<A-F1>"], + "Open the single, consolidated help page", + function () { modules.ex.helpall(); }); + } +}); + +endModule(); + +// vim: set fdm=marker sw=4 ts=4 et: diff --git a/common/modules/io.jsm b/common/modules/io.jsm index aeead9b3..20bbc3d9 100644 --- a/common/modules/io.jsm +++ b/common/modules/io.jsm @@ -13,7 +13,7 @@ Components.utils.import("resource://dactyl/bootstrap.jsm"); defineModule("io", { exports: ["IO", "io"], require: ["services"], - use: ["config", "messages", "storage", "styles", "template", "util"] + use: ["config", "help", "messages", "storage", "styles", "template", "util"] }, this); // TODO: why are we passing around strings rather than file objects? @@ -174,7 +174,7 @@ var IO = Module("io", { util.flushCache(); dactyl.loadScript(uri.spec, context); - dactyl.helpInitialized = false; + help.initialized = false; } catch (e) { if (e.fileName) diff --git a/common/modules/overlay.jsm b/common/modules/overlay.jsm index 788c923d..10e20a8a 100644 --- a/common/modules/overlay.jsm +++ b/common/modules/overlay.jsm @@ -9,7 +9,7 @@ try { Components.utils.import("resource://dactyl/bootstrap.jsm"); defineModule("overlay", { exports: ["ModuleBase", "overlay"], - require: ["config", "highlight", "io", "services", "util"] + require: ["config", "help", "highlight", "io", "services", "util"] }, this); /** @@ -41,8 +41,6 @@ var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReferen util.addObserver(this); this.overlays = {}; - services["dactyl:"]; // Hack. Force module initialization. - config.loadStyles(); this.timeout(this.initialize); diff --git a/common/modules/protocol.jsm b/common/modules/protocol.jsm new file mode 100644 index 00000000..23b6801c --- /dev/null +++ b/common/modules/protocol.jsm @@ -0,0 +1,228 @@ +// Copyright (c) 2008-2011 by 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"; + +Components.utils.import("resource://dactyl/bootstrap.jsm"); +defineModule("protocol", { + exports: ["LocaleChannel", "Protocol", "RedirectChannel", "StringChannel", "XMLChannel"], + require: ["services", "util"] +}, this); + +var systemPrincipal = Cc["@mozilla.org/systemprincipal;1"].getService(Ci.nsIPrincipal); + +var DNE = "resource://gre/does/not/exist"; +var _DNE; + +function Channel(url, orig, noFake) { + try { + if (url == null) + return noFake ? null : FakeChannel(orig); + + if (url instanceof Ci.nsIChannel) + return url; + + if (typeof url === "function") + return let ([type, data] = url(orig)) StringChannel(data, type, orig); + + if (isArray(url)) + return let ([type, data] = url) StringChannel(data, type, orig); + + let uri = services.io.newURI(url, null, null); + return (new XMLChannel(uri, null, noFake)).channel; + } + catch (e) { + util.reportError(e); + throw e; + } +} +function FakeChannel(orig) { + let channel = services.io.newChannel(DNE, null, null); + channel.originalURI = orig; + return channel; +} +function RedirectChannel(to, orig, time) { + let html = <html><head><meta http-equiv="Refresh" content={(time || 0) + ";" + to}/></head></html>.toXMLString(); + return StringChannel(html, "text/html", services.io.newURI(to, null, null)); +} + +function Protocol(scheme, classID, contentBase) { + function Protocol() { + ProtocolBase.call(this); + } + Protocol.prototype = { + __proto__: ProtocolBase.prototype, + + classID: Components.ID(classID), + + scheme: scheme, + + contentBase: contentBase, + + _xpcom_factory: JSMLoader.Factory(Protocol), + }; + return Protocol; +} + +function ProtocolBase() { + this.wrappedJSObject = this; + + this.pages = {}; + this.providers = {}; +} +ProtocolBase.prototype = { + get contractID() "@mozilla.org/network/protocol;1?name=" + this.scheme, + get classDescription() this.scheme + " utility protocol", + QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler]), + + defaultPort: -1, + allowPort: function (port, scheme) false, + protocolFlags: 0 + | Ci.nsIProtocolHandler.URI_IS_UI_RESOURCE + | Ci.nsIProtocolHandler.URI_IS_LOCAL_RESOURCE, + + newURI: function newURI(spec, charset, baseURI) { + var uri = Cc["@mozilla.org/network/standard-url;1"] + .createInstance(Ci.nsIStandardURL) + .QueryInterface(Ci.nsIURI); + if (baseURI && baseURI.host === "data") + baseURI = null; + uri.init(uri.URLTYPE_STANDARD, this.defaultPort, spec, charset, baseURI); + return uri; + }, + + newChannel: function newChannel(uri) { + try { + uri.QueryInterface(Ci.nsIURL); + + if (uri.host in this.providers) + return Channel(this.providers[uri.host](uri, uri.filePath.substr(1)), uri); + + let path = decodeURIComponent(uri.path.replace(/^\/|#.*/g, "")); + switch(uri.host) { + case "content": + return Channel(this.pages[path] || this.contentBase + path, uri); + case "data": + try { + var channel = services.io.newChannel(uri.path.replace(/^\/(.*)(?:#.*)?/, "data:$1"), + null, null); + } + catch (e) { + var error = e; + break; + } + channel.contentCharset = "UTF-8"; + channel.owner = systemPrincipal; + channel.originalURI = uri; + return channel; + } + } + catch (e) { + util.reportError(e); + } + if (error) + throw error; + return FakeChannel(uri); + } +}; + +function LocaleChannel(pkg, locale, path, orig) { + for each (let locale in [locale, "en-US"]) + for each (let sep in "-/") { + var channel = Channel(["resource:/", pkg + sep + locale, path].join("/"), orig, true); + if (channel) + return channel; + } + + return FakeChannel(orig); +} + +function StringChannel(data, contentType, uri) { + let channel = services.StreamChannel(uri); + channel.contentStream = services.CharsetConv("UTF-8").convertToInputStream(data); + if (contentType) + channel.contentType = contentType; + channel.contentCharset = "UTF-8"; + channel.owner = systemPrincipal; + if (uri) + channel.originalURI = uri; + return channel; +} + +function XMLChannel(uri, contentType, noFake) { + try { + var channel = services.io.newChannelFromURI(uri); + var channelStream = channel.open(); + } + catch (e) { + this.channel = noFake ? null : FakeChannel(uri); + return; + } + + this.uri = uri; + this.sourceChannel = services.io.newChannelFromURI(uri); + this.pipe = services.Pipe(true, true, 0, 0, null); + this.writes = []; + + this.channel = services.StreamChannel(uri); + this.channel.contentStream = this.pipe.inputStream; + this.channel.contentType = contentType || channel.contentType; + this.channel.contentCharset = "UTF-8"; + this.channel.owner = systemPrincipal; + + let stream = services.InputStream(channelStream); + let [, pre, doctype, url, open, post] = util.regexp(<![CDATA[ + ^ ([^]*?) + (?: + (<!DOCTYPE \s+ \S+ \s+) SYSTEM \s+ "([^"]*)" + (\s+ \[)? + ([^]*) + )? + $ + ]]>, "x").exec(stream.read(4096)); + this.writes.push(pre); + if (doctype) { + this.writes.push(doctype + "[\n"); + try { + this.writes.push(services.io.newChannel(url, null, null).open()); + } + catch (e) {} + if (!open) + this.writes.push("\n]"); + this.writes.push(post); + } + this.writes.push(channelStream); + + this.writeNext(); +} +XMLChannel.prototype = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIRequestObserver]), + writeNext: function () { + try { + if (!this.writes.length) + this.pipe.outputStream.close(); + else { + let stream = this.writes.shift(); + if (isString(stream)) + stream = services.StringStream(stream); + + services.StreamCopier(stream, this.pipe.outputStream, null, + false, true, 4096, true, false) + .asyncCopy(this, null); + } + } + catch (e) { + util.reportError(e); + } + }, + + onStartRequest: function (request, context) {}, + onStopRequest: function (request, context, statusCode) { + this.writeNext(); + } +}; + +endModule(); + +// vim: set fdm=marker sw=4 ts=4 et: diff --git a/common/modules/sanitizer.jsm b/common/modules/sanitizer.jsm index 886121d1..0d602110 100644 --- a/common/modules/sanitizer.jsm +++ b/common/modules/sanitizer.jsm @@ -656,7 +656,7 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef 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", diff --git a/common/modules/services.jsm b/common/modules/services.jsm index 707619f2..d699d699 100644 --- a/common/modules/services.jsm +++ b/common/modules/services.jsm @@ -75,6 +75,7 @@ var Services = Module("Services", { this.addClass("Find", "@mozilla.org/embedcomp/rangefind;1", "nsIFind"); this.addClass("HtmlConverter","@mozilla.org/widget/htmlformatconverter;1", "nsIFormatConverter"); this.addClass("HtmlEncoder", "@mozilla.org/layout/htmlCopyEncoder;1", "nsIDocumentEncoder"); + this.addClass("InterfacePointer", "@mozilla.org/supports-interface-pointer;1", "nsISupportsInterfacePointer", "data"); this.addClass("InputStream", "@mozilla.org/scriptableinputstream;1", "nsIScriptableInputStream", "init"); this.addClass("Persist", "@mozilla.org/embedding/browser/nsWebBrowserPersist;1", "nsIWebBrowserPersist"); this.addClass("Pipe", "@mozilla.org/pipe;1", "nsIPipe", "init"); diff --git a/common/modules/template.jsm b/common/modules/template.jsm index 17ccb9df..9fb99f3e 100644 --- a/common/modules/template.jsm +++ b/common/modules/template.jsm @@ -8,7 +8,7 @@ Components.utils.import("resource://dactyl/bootstrap.jsm"); defineModule("template", { exports: ["Binding", "Template", "template"], require: ["util"], - use: ["messages", "services"] + use: ["help", "messages", "services"] }, this); default xml namespace = XHTML; @@ -204,7 +204,7 @@ var Template = Module("Template", { }, helpLink: function (token, text, type) { - if (!services["dactyl:"].initialized) + if (!help.initialized) util.dactyl.initHelp(); let topic = token; // FIXME: Evil duplication! @@ -213,7 +213,7 @@ var Template = Module("Template", { else if (/^n_/.test(topic)) topic = topic.slice(2); - if (services["dactyl:"].initialized && !Set.has(services["dactyl:"].HELP_TAGS, topic)) + if (help.initialized && !Set.has(help.tags, topic)) return <span highlight={type || ""}>{text || token}</span>; XML.ignoreWhitespace = false; XML.prettyPrinting = false; @@ -224,7 +224,7 @@ var Template = Module("Template", { return <a highlight={"InlineHelpLink " + type} tag={topic} href={"dactyl://help-tag/" + topic} dactyl:command="dactyl.help" xmlns:dactyl={NS}>{text || topic}</a>; }, HelpLink: function (token) { - if (!services["dactyl:"].initialized) + if (!help.initialized) util.dactyl.initHelp(); let topic = token; // FIXME: Evil duplication! @@ -233,7 +233,7 @@ var Template = Module("Template", { else if (/^n_/.test(topic)) topic = topic.slice(2); - if (services["dactyl:"].initialized && !Set.has(services["dactyl:"].HELP_TAGS, topic)) + if (help.initialized && !Set.has(help.tags, topic)) return <>{token}</>; XML.ignoreWhitespace = false; XML.prettyPrinting = false; |