diff --git a/common/components/protocols.js b/common/components/protocols.js index 4cb99a95..3719858c 100644 --- a/common/components/protocols.js +++ b/common/components/protocols.js @@ -24,6 +24,7 @@ var global = this; var Cc = Components.classes; var Ci = Components.interfaces; var Cu = Components.utils; +var DNE = "chrome://dactyl/content/does/not/exist"; Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); @@ -34,21 +35,23 @@ function makeChannel(url, orig) { try { if (url == null) return fakeChannel(orig); + if (typeof url === "function") return let ([type, data] = url()) StringChannel(data, type, orig); - let channel = ioService.newChannel(url, null, null); - channel.contentCharset = "UTF-8"; - channel.owner = systemPrincipal; - channel.originalURI = orig; - return channel; + let uri = ioService.newURI(url, null, null); + return (new XMLChannel(uri)).channel; } catch (e) { util.reportError(e); throw e; } } -function fakeChannel(orig) makeChannel("chrome://dactyl/content/does/not/exist", orig); +function fakeChannel(orig) { + let channel = ioService.newChannel(DNE, null, null); + channel.originalURI = orig; + return channel; +} function redirect(to, orig, time) { let html = .toXMLString(); return StringChannel(html, "text/html", ioService.newURI(to, null, null)); @@ -148,13 +151,13 @@ Dactyl.prototype = { newChannel: function newChannel(uri) { try { - if (uri.host != "content" && !("all" in this.FILE_MAP)) + if (/^help/.test(uri.host) && !("all" in this.FILE_MAP)) return redirect(uri.spec, uri, 1); let path = decodeURIComponent(uri.path.replace(/^\/|#.*/g, "")); switch(uri.host) { case "content": - return makeChannel(this.pages[path], uri); + return makeChannel(this.pages[path] || "chrome://dactyl/content/" + path, uri); case "help": return makeChannel(this.FILE_MAP[path], uri); case "help-overlay": @@ -165,6 +168,10 @@ Dactyl.prototype = { return redirect("dactyl://help/" + tag, uri); if (tag in this.HELP_TAGS) return redirect("dactyl://help/" + this.HELP_TAGS[tag] + "#" + tag, uri); + case "locale": + return makeChannel("chrome://dactyl/locale/" + path, uri); + case "locale-local": + return makeChannel("chrome://" + config.name + "/locale/" + path, uri); } } catch (e) {} @@ -188,7 +195,8 @@ Dactyl.prototype = { function StringChannel(data, contentType, uri) { let channel = services.StreamChannel(uri); channel.contentStream = services.StringStream(data); - channel.contentType = contentType; + if (contentType) + channel.contentType = contentType; channel.contentCharset = "UTF-8"; channel.owner = systemPrincipal; if (uri) @@ -197,17 +205,76 @@ function StringChannel(data, contentType, uri) { } function XMLChannel(uri, contentType) { + let channel = services.io.newChannelFromURI(uri); + try { + var channelStream = channel.open(); + } + catch (e) { + this.channel = 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.outputStream; - this.channel.contentType = contentType; + 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().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(); + } }; function AboutHandler() {} diff --git a/common/content/base.dtd b/common/content/base.dtd deleted file mode 100644 index aed97f89..00000000 --- a/common/content/base.dtd +++ /dev/null @@ -1,19 +0,0 @@ - - -%dactylBranding; - - - - - - - - - - - - - - - - diff --git a/common/content/dactyl.js b/common/content/dactyl.js index b0723506..7f6905b9 100644 --- a/common/content/dactyl.js +++ b/common/content/dactyl.js @@ -92,9 +92,9 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { * @property {number} The current main mode. * @see modes#mainModes */ - mode: Class.Property({ - get: deprecated("Please use modes.main instead", function mode() modes.main), - set: deprecated("Please use modes.main instead", function mode(val) modes.main = val), + mode: deprecated("Please use modes.main instead", { + get: function mode() modes.main, + set: function mode(val) modes.main = val }), get menuItems() Dactyl.getMenuItems(), @@ -108,23 +108,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { forceNewTab: false, forceNewWindow: false, - /** @property {string} The Dactyl version string. */ - version: Class.memoize(function () { - if (/pre$/.test(util.addon.version)) { - let uri = util.addon.getResourceURI("../.hg"); - if (uri instanceof Ci.nsIFileURL && - uri.QueryInterface(Ci.nsIFileURL).file.exists() && - io.pathSearch("hg")) { - return io.system(["hg", "-R", uri.file.parent.path, - "log", "-r.", - "--template=hg{rev} ({date|isodate})"]); - } - } - let version = util.addon.version; - if ("@DATE" !== "@" + "DATE@") - version += " (created: @DATE@)"; - return version; - }), + version: deprecated("Please use config.version instead", { get: function version() config.version }), /** * @property {Object} The map of command-line options. These are @@ -304,9 +288,9 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { }, dump: deprecated("Use util.dump instead", - function dump() util.dump.apply(util, arguments)), + { get: function dump() util.closure.dump }), dumpStack: deprecated("Use util.dumpStack instead", - function dumpStack() util.dumpStack.apply(util, arguments)), + { get: function dumpStack() util.closure.dumpStack }), /** * Outputs a plain message to the command line. @@ -546,14 +530,14 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { * @private * Initialize the help system. */ - initHelp: function () { - if (!this.helpInitialized) { + initHelp: function (force) { + if (!force && !this.helpInitialized) { if ("noscriptOverlay" in window) { noscriptOverlay.safeAllow("chrome-data:", true, false); noscriptOverlay.safeAllow("dactyl:", true, false); } - let namespaces = [config.name, "dactyl"]; + let namespaces = ["locale-local", "locale"]; services["dactyl:"].init({}); let tagMap = services["dactyl:"].HELP_TAGS; @@ -564,7 +548,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { function findHelpFile(file) { let result = []; for (let [, namespace] in Iterator(namespaces)) { - let url = ["chrome://", namespace, "/locale/", file, ".xml"].join(""); + let url = ["dactyl://", namespace, "/", file, ".xml"].join(""); let res = util.httpGet(url); if (res) { if (res.responseXML.documentElement.localName == "document") @@ -894,9 +878,8 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { * These are set and accessed with the "g:" prefix. */ _globalVariables: {}, - globalVariables: Class.Property({ - get: deprecated("Please use the options system instead", - function globalVariables() this._globalVariables) + globalVariables: deprecated("Please use the options system instead", { + get: function globalVariables() this._globalVariables }), loadPlugins: function (args) { @@ -2051,7 +2034,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { dactyl.open("about:"); else commandline.commandOutput(<> - {config.appName} {dactyl.version} running on:
{navigator.userAgent} + {config.appName} {config.version} running on:
{navigator.userAgent} ); }, { argCount: "0", diff --git a/common/content/help.dtd b/common/content/help.dtd deleted file mode 100644 index 4bfcf5b9..00000000 --- a/common/content/help.dtd +++ /dev/null @@ -1,6 +0,0 @@ - -%dactylMain; - -command line'> -status line'> - diff --git a/common/content/help.xsl b/common/content/help.xsl index 143a815b..288bef12 100644 --- a/common/content/help.xsl +++ b/common/content/help.xsl @@ -1,5 +1,5 @@ - + - + - + - + - + - + - + - + - + - + - + - + - ]> diff --git a/common/locale/en-US/eval.xml b/common/locale/en-US/eval.xml index 66918643..507ee5a7 100644 --- a/common/locale/en-US/eval.xml +++ b/common/locale/en-US/eval.xml @@ -1,7 +1,7 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + "].join("")) + .join("\n")] + }); }, get addonID() this.name + "@dactyl.googlecode.com", + addon: Class.memoize(function () { + let addon = services.fuel.storage.get("dactyl.bootstrap", {}).addon; + if (!addon) + addon = AddonManager.getAddonByID(this.addonID); + return addon; + }), + + /** @property {string} The Dactyl version string. */ + version: Class.memoize(function () { + if (/pre$/.test(this.addon.version)) { + let uri = this.addon.getResourceURI("../.hg"); + if (uri instanceof Ci.nsIFileURL && + uri.QueryInterface(Ci.nsIFileURL).file.exists() && + io.pathSearch("hg")) { + return io.system(["hg", "-R", uri.file.parent.path, + "log", "-r.", + "--template=hg{rev} ({date|isodate})"]); + } + } + let version = this.addon.version; + if ("@DATE" !== "@" + "DATE@") + version += " (created: @DATE@)"; + return version; + }), + + // TODO: DTD properties. Cleanup. + get home() "http://dactyl.sourceforge.net/", + get apphome() this.home + this.name, + code: "http://code.google.com/p/dactyl/", + get issues() this.home + "bug/" + this.name, + get plugins() "http://dactyl.sf.net/" + this.name + "/plugins", + get faq() this.home + this.name + "/faq", + "list.mailto": Class.memoize(function () config.name + "@googlegroups.com"), + "list.href": Class.memoize(function () "http://groups.google.com/group/" + config.name), + + dtdExtra: { + "xmlns.dactyl": "http://vimperator.org/namespaces/liberator", + "xmlns.html": "http://www.w3.org/1999/xhtml", + "xmlns.xul": "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", + + "tag.command-line": 'command line', + "tag.status-line": 'status line', + }, + + dtdStrings: [ + "appName", + "apphome", + "code", + "faq", + "fileExt", + "home", + "host", + "hostbin", + "idName", + "issues", + "list.href", + "list.mailto", + "name", + "plugins", + "version", + ], styleHelp: function styleHelp() { if (!this.helpStyled) @@ -519,15 +588,6 @@ config.INIT = update(Object.create(config.INIT), config.INIT, { init: function init(dactyl, modules, window) { init.superapply(this, arguments); - // Hmm... - let config1 = Object.create(config); - let config2 = Object.create(config1); - config2.instance = config2; - update(config1, config.Local.superapply(config2, arguments)); - update(config2, config.Local.apply(config2, arguments)); - modules.config = config2; - modules.config.init(); - let img = window.Image(); img.src = this.logo || "chrome://" + this.name + "/content/logo.png"; img.onload = function () { diff --git a/common/content/io.js b/common/modules/io.jsm similarity index 74% rename from common/content/io.js rename to common/modules/io.jsm index 096b5f65..c657fe9f 100644 --- a/common/content/io.js +++ b/common/modules/io.jsm @@ -7,31 +7,14 @@ // given in the LICENSE.txt file included with this file. "use strict"; -/** @scope modules */ +try { -plugins.contexts = {}; -function Script(file) { - let self = set.has(plugins, file.path) && plugins[file.path]; - if (self) { - if (set.has(self, "onUnload")) - self.onUnload(); - } - else { - self = update({ __proto__: plugins }, { - NAME: file.leafName.replace(/\..*/, "").replace(/-([a-z])/g, function (m, n1) n1.toUpperCase()), - PATH: file.path, - CONTEXT: self - }); - Class.replaceProperty(plugins, file.path, self); - - // This belongs elsewhere - if (io.getRuntimeDirectories("plugins").some( - function (dir) dir.contains(file, false))) - Class.replaceProperty(plugins, self.NAME, self); - } - plugins.contexts[file.path] = self; - return self; -} +Components.utils.import("resource://dactyl/base.jsm"); +defineModule("io", { + exports: ["IO", "io"], + require: ["services"], + use: ["config", "storage", "template", "util"] +}); // TODO: why are we passing around strings rather than file objects? /** @@ -43,37 +26,168 @@ var IO = Module("io", { this._processDir = services.directory.get("CurWorkD", Ci.nsIFile); this._cwd = this._processDir.path; this._oldcwd = null; + }, - this._lastRunCommand = ""; // updated whenever the users runs a command with :! - this._scriptNames = []; + Local: function (dactyl, modules, window) let ({ Script, plugins } = modules) ({ - this.downloadListener = { - onDownloadStateChange: function (state, download) { - if (download.state == services.downloadManager.DOWNLOAD_FINISHED) { - let url = download.source.spec; - let title = download.displayName; - let file = download.targetFile.path; - let size = download.size; + init: function init() { + this._processDir = services.directory.get("CurWorkD", Ci.nsIFile); + this._cwd = this._processDir.path; + this._oldcwd = null; - dactyl.echomsg({ domains: [util.getHost(url)], message: "Download of " + title + " to " + file + " finished" }, - 1, commandline.ACTIVE_WINDOW); - autocommands.trigger("DownloadPost", { url: url, title: title, file: file, size: size }); + this._lastRunCommand = ""; // updated whenever the users runs a command with :! + this._scriptNames = []; + + this.downloadListener = { + onDownloadStateChange: function (state, download) { + if (download.state == services.downloadManager.DOWNLOAD_FINISHED) { + let url = download.source.spec; + let title = download.displayName; + let file = download.targetFile.path; + let size = download.size; + + dactyl.echomsg({ domains: [util.getHost(url)], message: "Download of " + title + " to " + file + " finished" }, + 1, modules.commandline.ACTIVE_WINDOW); + modules.autocommands.trigger("DownloadPost", { url: url, title: title, file: file, size: size }); + } + }, + onStateChange: function () {}, + onProgressChange: function () {}, + onSecurityChange: function () {} + }; + + services.downloadManager.addListener(this.downloadListener); + }, + + destroy: function destroy() { + services.downloadManager.removeListener(this.downloadListener); + for (let [, plugin] in Iterator(plugins.contexts)) + if (plugin.onUnload) + plugin.onUnload(); + }, + + /** + * Returns all directories named *name* in 'runtimepath'. + * + * @param {string} name + * @returns {nsIFile[]) + */ + getRuntimeDirectories: function getRuntimeDirectories(name) { + let dirs = modules.options["runtimepath"]; + + dirs = dirs.map(function (dir) File.joinPaths(dir, name, this.cwd), this) + .filter(function (dir) dir.exists() && dir.isDirectory() && dir.isReadable()); + return dirs; + }, + + // FIXME: multiple paths? + /** + * Sources files found in 'runtimepath'. For each relative path in *paths* + * each directory in 'runtimepath' is searched and if a matching file is + * found it is sourced. Only the first file found (per specified path) is + * sourced unless *all* is specified, then all found files are sourced. + * + * @param {string[]} paths An array of relative paths to source. + * @param {boolean} all Whether all found files should be sourced. + */ + sourceFromRuntimePath: function sourceFromRuntimePath(paths, all) { + let dirs = options["runtimepath"]; + let found = false; + + dactyl.echomsg("Searching for " + paths.join(" ").quote() + " in " + options.get("runtimepath").stringValue, 2); + + outer: + for (let [, dir] in Iterator(dirs)) { + for (let [, path] in Iterator(paths)) { + let file = File.joinPaths(dir, path, this.cwd); + + dactyl.echomsg("Searching for " + file.path.quote(), 3); + + if (file.exists() && file.isFile() && file.isReadable()) { + io.source(file.path, false); + found = true; + + if (!all) + break outer; + } } - }, - onStateChange: function () {}, - onProgressChange: function () {}, - onSecurityChange: function () {} - }; + } - services.downloadManager.addListener(this.downloadListener); - }, + if (!found) + dactyl.echomsg("not found in 'runtimepath': " + paths.join(" ").quote(), 1); - destroy: function () { - services.downloadManager.removeListener(this.downloadListener); - for (let [, plugin] in Iterator(plugins.contexts)) - if (plugin.onUnload) - plugin.onUnload(); - }, + return found; + }, + + /** + * Reads Ex commands, JavaScript or CSS from *filename*. + * + * @param {string} filename The name of the file to source. + * @param {boolean} silent Whether errors should be reported. + */ + source: function source(filename, silent) { + defineModule.loadLog.push("sourcing " + filename); + let time = Date.now(); + this.withSavedValues(["sourcing"], function _source() { + this.sourcing = null; + try { + var file = util.getFile(filename) || io.File(filename); + + if (!file.exists() || !file.isReadable() || file.isDirectory()) { + if (!silent) + dactyl.echoerr("E484: Can't open file " + filename.quote()); + return; + } + + dactyl.echomsg("sourcing " + filename.quote(), 2); + + let uri = services.io.newFileURI(file); + + // handle pure JavaScript files specially + if (/\.js$/.test(filename)) { + try { + dactyl.loadScript(uri.spec, Script(file)); + dactyl.helpInitialized = false; + } + catch (e) { + if (e.fileName) + try { + e.fileName = e.fileName.replace(/^(chrome|resource):.*? -> /, ""); + if (e.fileName == uri.spec) + e.fileName = filename; + e.echoerr = <>{e.fileName}:{e.lineNumber}: {e}; + } + catch (e) {} + throw e; + } + } + else if (/\.css$/.test(filename)) + styles.registerSheet(uri.spec, false, true); + else { + modules.commands.execute(file.read(), null, silent || "loud", null, + { file: file.path, line: 1 }); + } + + if (this._scriptNames.indexOf(file.path) == -1) + this._scriptNames.push(file.path); + + dactyl.echomsg("finished sourcing " + filename.quote(), 2); + + dactyl.log("Sourced: " + filename, 3); + } + catch (e) { + if (!(e instanceof FailedAssertion)) + dactyl.reportError(e); + let message = "Sourcing file: " + (e.echoerr || file.path + ": " + e); + if (!silent) + dactyl.echoerr(message); + } + finally { + defineModule.loadLog.push("done sourcing " + filename + ": " + (Date.now() - time) + "ms"); + } + }); + }, + }), // TODO: there seems to be no way, short of a new component, to change // the process's CWD - see https://bugzilla.mozilla.org/show_bug.cgi?id=280953 @@ -123,10 +237,11 @@ var IO = Module("io", { * @property {function} File class. * @final */ - File: Class("File", File, { - init: function init(path, checkCWD) - init.supercall(this, path, (arguments.length < 2 || checkCWD) && io.cwd) - }), + File: Class.memoize(function () let (io = this) + Class("File", File, { + init: function init(path, checkCWD) + init.supercall(this, path, (arguments.length < 2 || checkCWD) && io.cwd) + })), /** * @property {Object} The current file sourcing context. As a file is @@ -135,36 +250,7 @@ var IO = Module("io", { */ sourcing: null, - /** - * Expands "~" and environment variables in *path*. - * - * "~" is expanded to to the value of $HOME. On Windows if this is not - * set then the following are tried in order: - * $USERPROFILE - * ${HOMDRIVE}$HOMEPATH - * - * The variable notation is $VAR (terminated by a non-word character) - * or ${VAR}. %VAR% is also supported on Windows. - * - * @param {string} path The unexpanded path string. - * @param {boolean} relative Whether the path is relative or absolute. - * @returns {string} - */ - expandPath: File.expandPath, - - /** - * Returns all directories named *name* in 'runtimepath'. - * - * @param {string} name - * @returns {nsIFile[]) - */ - getRuntimeDirectories: function (name) { - let dirs = options["runtimepath"]; - - dirs = dirs.map(function (dir) File.joinPaths(dir, name, this.cwd), this) - .filter(function (dir) dir.exists() && dir.isDirectory() && dir.isReadable()); - return dirs; - }, + expandPath: deprecated("Please use File.expandPath instead", function expandPath() File.expandPath.apply(File, arguments)), /** * Returns the first user RC file found in *dir*. @@ -207,7 +293,7 @@ var IO = Module("io", { Cc["@mozilla.org/uriloader/external-helper-app-service;1"] .getService(Ci.nsPIExternalAppLauncher).deleteTemporaryFileOnExit(file); - return io.File(file); + return File(file); }, isJarURL: function (url) { @@ -266,9 +352,9 @@ var IO = Module("io", { let file; if (File.isAbsolutePath(program)) - file = io.File(program, true); + file = this.File(program, true); else - file = io.pathSearch(program); + file = this.pathSearch(program); if (!file || !file.exists()) { dactyl.echoerr("Command not found: " + program); @@ -283,7 +369,7 @@ var IO = Module("io", { function () { if (!process.isRunning) { timer.cancel(); - dactyl.trapErrors(blocking); + util.trapErrors(blocking); } }, 100, services.Timer.TYPE_REPEATING_SLACK); @@ -299,114 +385,6 @@ var IO = Module("io", { return process.exitValue; }, - // FIXME: multiple paths? - /** - * Sources files found in 'runtimepath'. For each relative path in *paths* - * each directory in 'runtimepath' is searched and if a matching file is - * found it is sourced. Only the first file found (per specified path) is - * sourced unless *all* is specified, then all found files are sourced. - * - * @param {string[]} paths An array of relative paths to source. - * @param {boolean} all Whether all found files should be sourced. - */ - sourceFromRuntimePath: function (paths, all) { - let dirs = options["runtimepath"]; - let found = false; - - dactyl.echomsg("Searching for " + paths.join(" ").quote() + " in " + options.get("runtimepath").stringValue, 2); - - outer: - for (let [, dir] in Iterator(dirs)) { - for (let [, path] in Iterator(paths)) { - let file = File.joinPaths(dir, path, this.cwd); - - dactyl.echomsg("Searching for " + file.path.quote(), 3); - - if (file.exists() && file.isFile() && file.isReadable()) { - io.source(file.path, false); - found = true; - - if (!all) - break outer; - } - } - } - - if (!found) - dactyl.echomsg("not found in 'runtimepath': " + paths.join(" ").quote(), 1); - - return found; - }, - - /** - * Reads Ex commands, JavaScript or CSS from *filename*. - * - * @param {string} filename The name of the file to source. - * @param {boolean} silent Whether errors should be reported. - */ - source: function (filename, silent) { - defineModule.loadLog.push("sourcing " + filename); - let time = Date.now(); - this.withSavedValues(["sourcing"], function () { - this.sourcing = null; - try { - var file = util.getFile(filename) || io.File(filename); - - if (!file.exists() || !file.isReadable() || file.isDirectory()) { - if (!silent) - dactyl.echoerr("E484: Can't open file " + filename.quote()); - return; - } - - dactyl.echomsg("sourcing " + filename.quote(), 2); - - let uri = services.io.newFileURI(file); - - // handle pure JavaScript files specially - if (/\.js$/.test(filename)) { - try { - dactyl.loadScript(uri.spec, Script(file)); - dactyl.helpInitialized = false; - } - catch (e) { - if (e.fileName) - try { - e.fileName = e.fileName.replace(/^(chrome|resource):.*? -> /, ""); - if (e.fileName == uri.spec) - e.fileName = filename; - e.echoerr = <>{e.fileName}:{e.lineNumber}: {e}; - } - catch (e) {} - throw e; - } - } - else if (/\.css$/.test(filename)) - styles.registerSheet(uri.spec, false, true); - else { - commands.execute(file.read(), null, silent || "loud", null, - { file: file.path, line: 1 }); - } - - if (this._scriptNames.indexOf(file.path) == -1) - this._scriptNames.push(file.path); - - dactyl.echomsg("finished sourcing " + filename.quote(), 2); - - dactyl.log("Sourced: " + filename, 3); - } - catch (e) { - if (!(e instanceof FailedAssertion)) - dactyl.reportError(e); - let message = "Sourcing file: " + (e.echoerr || file.path + ": " + e); - if (!silent) - dactyl.echoerr(message); - } - finally { - defineModule.loadLog.push("done sourcing " + filename + ": " + (Date.now() - time) + "ms"); - } - }); - }, - // TODO: when https://bugzilla.mozilla.org/show_bug.cgi?id=68702 is // fixed use that instead of a tmpfile /** @@ -418,7 +396,7 @@ var IO = Module("io", { * @returns {string} */ system: function (command, input) { - dactyl.echomsg("Calling shell to execute: " + command, 4); + util.dactyl.echomsg("Calling shell to execute: " + command, 4); function escape(str) '"' + str.replace(/[\\"$]/g, "\\$&") + '"'; @@ -428,7 +406,8 @@ var IO = Module("io", { else if (input) stdin.write(input); - let shell = File.expandPath(options["shell"]); + let shell = File.expandPath(storage["options"].get("shell").value); + let shcf = storage["options"].get("shellcmdflag").value; if (isArray(command)) command = command.map(escape).join(" "); @@ -436,12 +415,12 @@ var IO = Module("io", { // TODO: implement 'shellredir' if (util.OS.isWindows && !/sh/.test(options["shell"])) { command = "cd /D " + this.cwd + " && " + command + " > " + stdout.path + " 2>&1" + " < " + stdin.path; - var res = this.run(shell, options["shellcmdflag"].split(/\s+/).concat(command), true); + var res = this.run(shell, shcf.split(/\s+/).concat(command), true); } else { cmd.write("cd " + escape(this.cwd) + "\n" + ["exec", ">" + escape(stdout.path), "2>&1", "<" + escape(stdin.path), - escape(shell), options["shellcmdflag"], escape(command)].join(" ")); + escape(shell), shcf, escape(command)].join(" ")); res = this.run("/bin/sh", ["-e", cmd.path], true); } @@ -496,9 +475,40 @@ var IO = Module("io", { /** * @property {string} The current platform's path separator. */ - PATH_SEP: File.PATH_SEP + PATH_SEP: deprecated("Please use File.PATH_SEP", { get: function PATH_SEP() File.PATH_SEP }) }, { - commands: function () { + init: function init(dactyl, modules, window) { + modules.plugins.contexts = {}; + modules.Script = function Script(file) { + const { io, plugins } = modules; + + let self = set.has(plugins, file.path) && plugins[file.path]; + if (self) { + if (set.has(self, "onUnload")) + self.onUnload(); + } + else { + self = update(modules.newContext(plugins, true), { + NAME: file.leafName.replace(/\..*/, "").replace(/-([a-z])/g, function (m, n1) n1.toUpperCase()), + PATH: file.path, + CONTEXT: self + }); + Class.replaceProperty(plugins, file.path, self); + + // This belongs elsewhere + if (io.getRuntimeDirectories("plugins").some( + function (dir) dir.contains(file, false))) + Class.replaceProperty(plugins, self.NAME, self); + } + plugins.contexts[file.path] = self; + return self; + } + + init.superapply(this, arguments); + }, + commands: function (dactyl, modules, window) { + const { commands, completion, io } = modules; + commands.add(["cd", "chd[ir]"], "Change the current directory", function (args) { @@ -517,7 +527,7 @@ var IO = Module("io", { dactyl.echomsg(io.cwd); } else { - let dirs = options["cdpath"]; + let dirs = modules.options["cdpath"]; for (let [, dir] in Iterator(dirs)) { dir = File.joinPaths(dir, arg, io.cwd); @@ -708,9 +718,9 @@ unlet s:cpo_save commands: wrap("syn keyword " + config.name + "Command ", array(c.specs for (c in commands)).flatten()), options: wrap("syn keyword " + config.name + "Option ", - array(o.names for (o in options) if (o.type != "boolean")).flatten()), + array(o.names for (o in modules.options) if (o.type != "boolean")).flatten()), toggleoptions: wrap("let s:toggleOptions = [", - array(o.realNames for (o in options) if (o.type == "boolean")) + array(o.realNames for (o in modules.options) if (o.type == "boolean")) .flatten().map(String.quote), ", ") + "]" })); @@ -734,7 +744,7 @@ unlet s:cpo_save commands.add(["scrip[tnames]"], "List all sourced script names", function () { - commandline.commandOutput( + modules.commandline.commandOutput( template.tabular(["", "Filename"], ["text-align: right; padding-right: 1em;"], ([i + 1, file] for ([i, file] in Iterator(io._scriptNames))))); // TODO: add colon and remove column titles for pedantic Vim compatibility? }, @@ -768,7 +778,7 @@ unlet s:cpo_save // This is an asinine and irritating feature when we have searchable // command-line history. --Kris - if (options["banghist"]) { + if (modules.options["banghist"]) { // replaceable bang and no previous command? dactyl.assert(!/((^|[^\\])(\\\\)*)!/.test(arg) || io._lastRunCommand, "E34: No previous command"); @@ -782,10 +792,10 @@ unlet s:cpo_save let output = io.system(arg); - commandline.command = "!" + arg; - commandline.commandOutput({output}); + modules.commandline.command = "!" + arg; + modules.commandline.commandOutput({output}); - autocommands.trigger("ShellCmdPost", {}); + modules.autocommands.trigger("ShellCmdPost", {}); }, { argCount: "?", // TODO: "1" - probably not worth supporting weird Vim edge cases. The dream is dead. --djk bang: true, @@ -794,7 +804,9 @@ unlet s:cpo_save literal: 0 }); }, - completion: function () { + completion: function (dactyl, modules, window) { + const { completion, io } = modules; + completion.charset = function (context) { context.anchored = false; context.keys = { @@ -840,8 +852,8 @@ unlet s:cpo_save }; context.compare = function (a, b) b.isdir - a.isdir || String.localeCompare(a.text, b.text); - if (options["wildignore"]) { - let wig = options.get("wildignore"); + if (modules.options["wildignore"]) { + let wig = modules.options.get("wildignore"); context.filters.push(function (item) item.isdir || !wig.getKey(this.name)); } @@ -881,7 +893,7 @@ unlet s:cpo_save }; completion.runtime = function (context) { - for (let [, dir] in Iterator(options["runtimepath"])) + for (let [, dir] in Iterator(modules.options["runtimepath"])) context.fork(dir, 0, this, function (context) { dir = dir.replace("/+$", "") + "/"; completion.file(context, true, dir + context.filter); @@ -938,15 +950,17 @@ unlet s:cpo_save completion.file(context, full); }); }, - javascript: function () { - JavaScript.setCompleter([File, File.expandPath], + javascript: function (dactyl, modules, window) { + modules.JavaScript.setCompleter([File, File.expandPath], [function (context, obj, args) { context.quote[2] = ""; completion.file(context, true); }]); }, - options: function () { + options: function (dactyl, modules, window) { + const { options } = modules; + var shell, shellcmdflag; if (util.OS.isWindows) { shell = "cmd.exe"; @@ -991,6 +1005,8 @@ unlet s:cpo_save return /sh/.test(options["shell"]) ? "-c" : "/c"; } }); + options["shell"]; // Make sure it's loaded into global storage. + options["shellcmdflag"]; options.add(["wildignore", "wig"], "List of file patterns to ignore when completing file names", @@ -998,4 +1014,8 @@ unlet s:cpo_save } }); -// vim: set fdm=marker sw=4 ts=4 et: +endModule(); + +} catch(e){ if (isString(e)) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); } + +// vim: set fdm=marker sw=4 ts=4 et ft=javascript: diff --git a/common/modules/overlay.jsm b/common/modules/overlay.jsm index 9ce2bb93..3056438e 100644 --- a/common/modules/overlay.jsm +++ b/common/modules/overlay.jsm @@ -129,7 +129,9 @@ var Overlay = Module("Overlay", { } }, - newContext: function newContext(proto) { + newContext: function newContext(proto, normal) { + if (normal) + return create(proto); let sandbox = Components.utils.Sandbox(window, { sandboxPrototype: proto || modules, wantXrays: false }); // Hack: sandbox.Object = jsmodules.Object; diff --git a/common/modules/services.jsm b/common/modules/services.jsm index 343b89ea..7d4138f4 100644 --- a/common/modules/services.jsm +++ b/common/modules/services.jsm @@ -69,11 +69,12 @@ var Services = Module("Services", { this.addClass("Find", "@mozilla.org/embedcomp/rangefind;1", Ci.nsIFind); this.addClass("HtmlConverter","@mozilla.org/widget/htmlformatconverter;1", Ci.nsIFormatConverter); this.addClass("HtmlEncoder", "@mozilla.org/layout/htmlCopyEncoder;1", Ci.nsIDocumentEncoder); - this.addClass("StreamChannel","@mozilla.org/network/input-stream-channel;1", - [Ci.nsIChannel, Ci.nsIInputStreamChannel, Ci.nsIRequest], "setURI"); + this.addClass("InputStream", "@mozilla.org/scriptableinputstream;1", Ci.nsIScriptableInputStream, "init"); this.addClass("Persist", "@mozilla.org/embedding/browser/nsWebBrowserPersist;1", Ci.nsIWebBrowserPersist); this.addClass("Pipe", "@mozilla.org/pipe;1", Ci.nsIPipe, "init"); this.addClass("Process", "@mozilla.org/process/util;1", Ci.nsIProcess, "init"); + this.addClass("StreamChannel","@mozilla.org/network/input-stream-channel;1", + [Ci.nsIChannel, Ci.nsIInputStreamChannel, Ci.nsIRequest], "setURI"); this.addClass("String", "@mozilla.org/supports-string;1", Ci.nsISupportsString, "data"); this.addClass("StringStream", "@mozilla.org/io/string-input-stream;1", Ci.nsIStringInputStream, "data"); this.addClass("Timer", "@mozilla.org/timer;1", Ci.nsITimer, "initWithCallback"); diff --git a/common/modules/storage.jsm b/common/modules/storage.jsm index 7748d644..e9c3cf12 100644 --- a/common/modules/storage.jsm +++ b/common/modules/storage.jsm @@ -490,6 +490,21 @@ var File = Class("File", { defaultEncoding: "UTF-8", + /** + * Expands "~" and environment variables in *path*. + * + * "~" is expanded to to the value of $HOME. On Windows if this is not + * set then the following are tried in order: + * $USERPROFILE + * ${HOMDRIVE}$HOMEPATH + * + * The variable notation is $VAR (terminated by a non-word character) + * or ${VAR}. %VAR% is also supported on Windows. + * + * @param {string} path The unexpanded path string. + * @param {boolean} relative Whether the path is relative or absolute. + * @returns {string} + */ expandPath: function (path, relative) { function getenv(name) services.environment.get(name); diff --git a/common/modules/util.jsm b/common/modules/util.jsm index df758275..2c361142 100644 --- a/common/modules/util.jsm +++ b/common/modules/util.jsm @@ -59,13 +59,6 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), } }, - addon: Class.memoize(function () { - let addon = services.fuel.storage.get("dactyl.bootstrap", {}).addon; - if (!addon) - addon = AddonManager.getAddonByID(config.addonID); - return addon; - }), - // FIXME: Only works for Pentadactyl get activeWindow() services.windowMediator.getMostRecentWindow("navigator:browser"), dactyl: update(function dactyl(obj) { @@ -1203,8 +1196,8 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), services.fuel.storage.set("dactyl.commandlineArgs", args); this.timeout(function () { this.rehashing = true; - this.addon.userDisabled = true; - this.addon.userDisabled = false; + config.addon.userDisabled = true; + config.addon.userDisabled = false; }); }, @@ -1508,6 +1501,6 @@ var Math = update(Object.create(GlobalMath), { endModule(); -} catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);} +} catch(e){ if (isString(e)) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); } // vim: set fdm=marker sw=4 ts=4 et ft=javascript: diff --git a/melodactyl/content/dactyl.dtd b/melodactyl/content/dactyl.dtd deleted file mode 100644 index 23ba95cb..00000000 --- a/melodactyl/content/dactyl.dtd +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - -%dactylBase; - diff --git a/melodactyl/locale/en-US/all.xml b/melodactyl/locale/en-US/all.xml index 6af00427..7d838b98 100644 --- a/melodactyl/locale/en-US/all.xml +++ b/melodactyl/locale/en-US/all.xml @@ -1,7 +1,7 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - - - - - - - -%dactylBase; - diff --git a/pentadactyl/locale/en-US/all.xml b/pentadactyl/locale/en-US/all.xml index b0335c24..859eef45 100644 --- a/pentadactyl/locale/en-US/all.xml +++ b/pentadactyl/locale/en-US/all.xml @@ -1,7 +1,7 @@ - + - + - + - + - + - + - + - + - + - + - + - + - - - - - - - - diff --git a/teledactyl/content/dactyl.dtd b/teledactyl/content/dactyl.dtd deleted file mode 100644 index 0fc1a9e2..00000000 --- a/teledactyl/content/dactyl.dtd +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - -%dactylBase; - diff --git a/teledactyl/locale/en-US/all.xml b/teledactyl/locale/en-US/all.xml index cd195af1..59d250a5 100644 --- a/teledactyl/locale/en-US/all.xml +++ b/teledactyl/locale/en-US/all.xml @@ -1,7 +1,7 @@ - + - + - + - + - + - + - + - + - + - +