From 5ebd29f56d17f62011cdd596b1d351947ee534ff Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9my=20Bobbio?= Date: Sat, 28 Jul 2012 14:27:59 +0200 Subject: [PATCH] Import 1.0 supporting Firefox up to 14.* --- .hg_archival.txt | 6 +- common/Makefile | 15 +- common/bootstrap.js | 4 +- common/components/commandline-handler.js | 6 +- common/content/bookmarks.js | 16 +- common/content/browser.js | 3 +- common/content/commandline.js | 24 ++- common/content/dactyl.js | 28 +++- common/content/editor.js | 18 +- common/content/events.js | 25 +-- common/content/help.xsl | 5 +- common/content/hints.js | 41 +++-- common/content/mappings.js | 24 +-- common/content/modes.js | 4 +- common/content/statusline.js | 4 +- common/locale/en-US/buffer.xml | 6 +- common/locale/en-US/developer.xml | 2 +- common/locale/en-US/hints.xml | 3 +- common/locale/en-US/map.xml | 71 ++++---- common/locale/en-US/marks.xml | 13 +- common/locale/en-US/options.xml | 53 +++--- common/modules/addons.jsm | 19 +-- common/modules/base.jsm | 67 ++++++-- common/modules/bookmarkcache.jsm | 24 ++- common/modules/bootstrap.jsm | 9 +- common/modules/buffer.jsm | 71 ++++---- common/modules/completion.jsm | 36 ++-- common/modules/config.jsm | 10 +- common/modules/contexts.jsm | 5 +- common/modules/dom.jsm | 100 +++++++---- common/modules/downloads.jsm | 38 ++++- common/modules/finder.jsm | 2 +- common/modules/help.jsm | 26 ++- common/modules/io.jsm | 49 ++---- common/modules/javascript.jsm | 13 +- common/modules/main.jsm | 52 +++--- common/modules/messages.jsm | 3 +- common/modules/options.jsm | 12 +- common/modules/prefs.jsm | 201 +++++++++++++---------- common/modules/protocol.jsm | 51 +++--- common/modules/sanitizer.jsm | 114 ++++++------- common/modules/services.jsm | 3 +- common/modules/styles.jsm | 2 + common/modules/template.jsm | 37 +++-- common/modules/util.jsm | 20 ++- common/skin/global-styles.css | 3 +- common/tests/functional/testCommands.js | 4 +- melodactyl/install.rdf | 2 +- pentadactyl/NEWS | 15 +- pentadactyl/install.rdf | 11 +- teledactyl/install.rdf | 2 +- 51 files changed, 814 insertions(+), 558 deletions(-) diff --git a/.hg_archival.txt b/.hg_archival.txt index 50caf3e..8260397 100644 --- a/.hg_archival.txt +++ b/.hg_archival.txt @@ -1,4 +1,4 @@ repo: 373f1649c80dea9be7b5bc9c57e8395f94f93ab1 -node: 91d4f03a135efe65e2e7de46c00ef09c78fdaae3 -branch: default -tag: pentadactyl-1.0rc1 +node: 227d1399fe7c9d8de98fc6ab3222cf9e1d8ad38f +branch: pentadactyl-1.0-branch +tag: pentadactyl-1.0 diff --git a/common/Makefile b/common/Makefile index ef118e8..0251c4a 100644 --- a/common/Makefile +++ b/common/Makefile @@ -16,7 +16,7 @@ GOOGLE_PROJ = dactyl GOOGLE = https://$(GOOGLE_PROJ).googlecode.com/files VERSION ?= $(shell $(SED) -n 's/.*em:version(>|=")(.*)["<].*/\2/p' $(TOP)/install.rdf | sed 1q) UUID := $(shell $(SED) -n 's/.*em:id(>|=")(.*)["<].*/\2/p' $(TOP)/install.rdf | sed 1q) -MANGLE := $(shell date '+%s' | awk '{ printf "%x", $$1 }') +MANGLE := chrome MOZMILL = mozmill HOSTAPP_PATH = $(shell which $(HOSTAPP)) TEST_DIR = $(BASE)/tests/functional @@ -50,9 +50,6 @@ XPI_NAME = $(NAME)-$(VERSION) XPI = ../downloads/$(XPI_NAME).xpi XPI_PATH = $(TOP)/$(XPI:%.xpi=%) -RDF = ../downloads/update.rdf -RDF_IN = $(RDF).in - BUILD_DIR = build.$(VERSION).$(OS) .SILENT: @@ -78,7 +75,6 @@ help: @echo " make install - installs this source tree directly to your $(HOSTAPP) profile" @echo ' set $$PROFILE to select a profile by name and $$PROFILEPATHS' @echo ' to change the directory where profiles are searched' - @echo " make release - updates update.rdf (this is not for you)" @echo " make dist - uploads to Google Code (this is not for you)" @echo " make clean - clean up" @echo " make distclean - clean up more" @@ -94,8 +90,6 @@ info: jar: $(JAR) -release: $(XPI) $(RDF) - # This is not for you! dist: $(XPI) @echo DIST $(XPI) $(GOOGLE) @@ -160,13 +154,6 @@ install: installxpi: xpi $(HOSTAPP) $(XPI) -$(RDF): $(RDF_IN) Makefile - @echo "Preparing release..." - $(SED) -e "s,@VERSION@,$(VERSION),g" \ - -e "s,@DATE@,$(BUILD_DATE),g" \ - < $< > $@ - @echo "SUCCESS: $@" - clean: @echo "General $(NAME) cleanup..." rm -f $(JAR) $(XPI) diff --git a/common/bootstrap.js b/common/bootstrap.js index e029387..d81411e 100755 --- a/common/bootstrap.js +++ b/common/bootstrap.js @@ -195,8 +195,6 @@ function init() { break; case "resource": - var hardSuffix = /^[^\/]*/.exec(fields[2])[0]; - resources.push(fields[1], fields[1] + suffix); resourceProto.setSubstitution(fields[1], getURI(fields[2])); resourceProto.setSubstitution(fields[1] + suffix, getURI(fields[2])); @@ -205,7 +203,7 @@ function init() { // Flush the cache if necessary, just to be paranoid let pref = "extensions.dactyl.cacheFlushCheck"; - let val = addon.version + "-" + hardSuffix; + let val = addon.version; if (!Services.prefs.prefHasUserValue(pref) || Services.prefs.getCharPref(pref) != val) { var cacheFlush = true; Services.obs.notifyObservers(null, "startupcache-invalidate", ""); diff --git a/common/components/commandline-handler.js b/common/components/commandline-handler.js index 0fd1189..738e275 100644 --- a/common/components/commandline-handler.js +++ b/common/components/commandline-handler.js @@ -73,9 +73,9 @@ CommandLineHandler.prototype = { util.dactyl.execute(remote); } } - catch(e) { - util.reportError(e) - }; + catch (e) { + util.reportError(e); + } try { this.optionValue = commandLine.handleFlagWithParam(config.name, false); diff --git a/common/content/bookmarks.js b/common/content/bookmarks.js index ca1bdb0..195bee6 100644 --- a/common/content/bookmarks.js +++ b/common/content/bookmarks.js @@ -15,13 +15,13 @@ var Bookmarks = Module("bookmarks", { storage.addObserver("bookmark-cache", function (key, event, arg) { if (["add", "change", "remove"].indexOf(event) >= 0) - autocommands.trigger("Bookmark" + event[0].toUpperCase() + event.substr(1), + autocommands.trigger("Bookmark" + util.capitalize(event), iter({ bookmark: { toString: function () "bookmarkcache.bookmarks[" + arg.id + "]", valueOf: function () arg } - }, arg)); + }, arg).toObject()); bookmarks.timer.tell(); }, window); }, @@ -345,11 +345,12 @@ var Bookmarks = Module("bookmarks", { catch (e) {} if (charset) - var encodedParam = escape(window.convertFromUnicode(charset, param)); + var encodedParam = escape(window.convertFromUnicode(charset, param)).replace(/\+/g, encodeURIComponent); else - encodedParam = bookmarkcache.keywords[keyword].encodeURIComponent(param); + encodedParam = bookmarkcache.keywords[keyword.toLowerCase()].encodeURIComponent(param); - url = url.replace(/%s/g, encodedParam).replace(/%S/g, param); + url = url.replace(/%s/g, function () encodedParam) + .replace(/%S/g, function () param); if (/%s/i.test(data)) postData = window.getPostDataStream(data, param, encodedParam, "application/x-www-form-urlencoded"); } @@ -472,7 +473,7 @@ var Bookmarks = Module("bookmarks", { let updated = bookmarks.add(opts); let action = updated ? "updated" : "added"; - let extra = (opts.title == opts.url) ? "" : " (" + opts.title + ")"; + let extra = (opts.title && opts.title != opts.url) ? " (" + opts.title + ")" : ""; dactyl.echomsg({ domains: [util.getHost(opts.url)], message: _("bookmark." + action, opts.url + extra) }, 1, commandline.FORCE_SINGLELINE); @@ -707,6 +708,9 @@ var Bookmarks = Module("bookmarks", { ctxt.compare = CompletionContext.Sort.unsorted; ctxt.filterFunc = null; + if (ctxt.waitingForTab) + return; + let words = ctxt.filter.toLowerCase().split(/\s+/g); ctxt.completions = ctxt.completions.filter(function (i) words.every(function (w) i.toLowerCase().indexOf(w) >= 0)); diff --git a/common/content/browser.js b/common/content/browser.js index cee3adb..cc504ff 100644 --- a/common/content/browser.js +++ b/common/content/browser.js @@ -180,7 +180,7 @@ var Browser = Module("browser", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), setOverLink: util.wrapCallback(function setOverLink(link, b) { setOverLink.superapply(this, arguments); dactyl.triggerObserver("browser.overLink", link); - }), + }) } }, { }, { @@ -204,6 +204,7 @@ var Browser = Module("browser", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), function () { window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils) .redraw(); + statusline.overLink = null; statusline.updateStatus(); commandline.clear(); }, diff --git a/common/content/commandline.js b/common/content/commandline.js index 895ae4d..c318759 100644 --- a/common/content/commandline.js +++ b/common/content/commandline.js @@ -282,9 +282,12 @@ var CommandWidgets = Class("CommandWidgets", { contextMenu: Class.Memoize(function () { ["copy", "copylink", "selectall"].forEach(function (tail) { // some host apps use "hostPrefixContext-copy" ids - let xpath = "//xul:menuitem[contains(@id, '" + "ontext-" + tail + "') and not(starts-with(@id, 'dactyl-'))]"; - document.getElementById("dactyl-context-" + tail).style.listStyleImage = - DOM(DOM.XPath(xpath, document).snapshotItem(0)).style.listStyleImage; + let css = "menuitem[id$='ontext-" + tail + "']:not([id^=dactyl-])"; + let style = DOM(css, document).style; + DOM("#dactyl-context-" + tail, document).css({ + listStyleImage: style.listStyleImage, + MozImageRegion: style.MozImageRegion + }); }); return document.getElementById("dactyl-contextmenu"); }), @@ -753,9 +756,7 @@ var CommandLine = Module("commandline", { data = message.message; } - if ((flags & this.ACTIVE_WINDOW) && - window != services.windowWatcher.activeWindow && - services.windowWatcher.activeWindow.dactyl) + if ((flags & this.ACTIVE_WINDOW) && window != overlay.activeWindow) return; if ((flags & this.DISALLOW_MULTILINE) && !this.widgets.mowContainer.collapsed) @@ -1217,6 +1218,7 @@ var CommandLine = Module("commandline", { complete: function complete(show, tabPressed) { this.session.ignoredCount = 0; + this.waiting = null; this.context.reset(); this.context.tabPressed = tabPressed; @@ -1487,8 +1489,12 @@ var CommandLine = Module("commandline", { this.wildIndex = this.wildtypes.length - 1; if (idx && idx[1] >= idx[0].items.length) { - this.waiting = idx; - statusline.progress = _("completion.waitingForResults"); + if (!idx[0].incomplete) + this.waiting = null; + else { + this.waiting = idx; + statusline.progress = _("completion.waitingForResults"); + } return; } @@ -1669,6 +1675,7 @@ var CommandLine = Module("commandline", { }); modes.addMode("INPUT_MULTILINE", { + description: "Active when the command line's multiline input buffer is open", bases: [modes.INSERT] }); }, @@ -1945,6 +1952,7 @@ var ItemList = Class("ItemList", { // Check if we've passed any incomplete contexts let i = groups.indexOf(group); + util.assert(i >= 0, undefined, false); for (; i < groups.length; i++) { let end = groups[i].offsets.start + groups[i].itemCount; if (start >= end && groups[i].context.incomplete) diff --git a/common/content/dactyl.js b/common/content/dactyl.js index dc9df88..2933dcd 100644 --- a/common/content/dactyl.js +++ b/common/content/dactyl.js @@ -887,11 +887,15 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { * Opens one or more URLs. Returns true when load was initiated, or * false on error. * - * @param {string|object|Array} urls A representation of the URLs to open. May be - * either a string, which will be passed to - * {@link Dactyl#parseURLs}, an array in the same format as - * would be returned by the same, or an object as returned by - * {@link DOM#formData}. + * @param {string|Array} urls A representation of the URLs to open. + * A string will be passed to {@link Dactyl#parseURLs}. An array may + * contain elements of the following forms: + * + * • {string} A URL to open. + * • {[string, {string|Array}]} Pair of a URL and POST data. + * • {object} Object compatible with those returned + * by {@link DOM#formData}. + * * @param {object} params A set of parameters specifying how to open the * URLs. The following properties are recognized: * @@ -952,6 +956,18 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { loc = { url: loc[0], postData: loc[1] }; else if (isString(loc)) loc = { url: loc }; + else + loc = Object.create(loc); + + if (isString(loc.postData)) + loc.postData = ["application/x-www-form-urlencoded", loc.postData]; + + if (isArray(loc.postData)) { + let stream = services.MIMEStream(services.StringStream(loc.postData[1])); + stream.addHeader("Content-Type", loc.postData[0]); + stream.addContentLength = true; + loc.postData = stream; + } // decide where to load the first url switch (where) { @@ -1939,7 +1955,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { if (dactyl.commandLineOptions.rcFile == "NONE" || dactyl.commandLineOptions.noPlugins) options["loadplugins"] = []; - if (options["loadplugins"]) + if (options["loadplugins"].length) dactyl.loadPlugins(); } catch (e) { diff --git a/common/content/editor.js b/common/content/editor.js index 262391e..63ec3eb 100644 --- a/common/content/editor.js +++ b/common/content/editor.js @@ -280,7 +280,7 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), { } }, - findChar: function findNumber(key, count, backward, offset) { + findChar: function findChar(key, count, backward, offset) { count = count || 1; // XXX ? offset = (offset || 0) - !!backward; @@ -455,7 +455,7 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), { if (textBox) { textBox.value = val; - if (false) { + if (true) { let elem = DOM(textBox); elem.attrNS(NS, "modifiable", true) .style.MozUserInput; @@ -783,14 +783,16 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), { preExecute: function preExecute(args) { if (editor.editor && !this.editor) { this.editor = editor.editor; - this.editor.beginTransaction(); + if (!this.noTransaction) + this.editor.beginTransaction(); } editor.inEditMap = true; }, postExecute: function preExecute(args) { editor.inEditMap = false; if (this.editor) { - this.editor.endTransaction(); + if (!this.noTransaction) + this.editor.endTransaction(); this.editor = null; } }, @@ -1158,17 +1160,17 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), { // text edit mode bind(["u"], "Undo changes", function (args) { - editor.executeCommand("cmd_undo", Math.max(args.count, 1)); + editor.editor.undo(Math.max(args.count, 1)); editor.deselect(); }, - { count: true }); + { count: true, noTransaction: true }); bind([""], "Redo undone changes", function (args) { - editor.executeCommand("cmd_redo", Math.max(args.count, 1)); + editor.editor.redo(Math.max(args.count, 1)); editor.deselect(); }, - { count: true }); + { count: true, noTransaction: true }); bind(["D"], "Delete characters from the cursor to the end of the line", function () { editor.executeCommand("cmd_deleteToEndOfLine"); }); diff --git a/common/content/events.js b/common/content/events.js index 4b1841d..ac846d8 100644 --- a/common/content/events.js +++ b/common/content/events.js @@ -116,7 +116,7 @@ var Events = Module("events", { }); this._fullscreen = window.fullScreen; - this._lastFocus = null; + this._lastFocus = { get: function () null }; this._macroKeys = []; this._lastMacro = ""; @@ -143,7 +143,7 @@ var Events = Module("events", { this.active.push(elem); } - this.active = this.active.filter(function (e) e.popupBoxObject.popupState != "closed"); + this.active = this.active.filter(function (e) e.popupBoxObject && e.popupBoxObject.popupState != "closed"); if (!this.active.length && !this.activeMenubar) modes.remove(modes.MENU, true); @@ -372,8 +372,6 @@ var Events = Module("events", { if (quiet) commandline.quiet = quiet; - keys = mappings.expandLeader(keys); - for (let [, evt_obj] in Iterator(DOM.Event.parse(keys))) { let now = Date.now(); let key = DOM.Event.stringify(evt_obj); @@ -625,7 +623,7 @@ var Events = Module("events", { } if (elem instanceof Element) - elem.dactylFocusAllowed = undefined; + delete overlay.getData(elem)["focus-allowed"]; }, /* @@ -795,9 +793,10 @@ var Events = Module("events", { !this.processor && event.type === "keydown" && options.get("passunknown").getKey(modes.main.allBases) && let (key = DOM.Event.stringify(event)) - !modes.main.allBases.some( + !(modes.main.count && /^\d$/.test(key) || + modes.main.allBases.some( function (mode) mappings.hives.some( - function (hive) hive.get(mode, key) || hive.getCandidates(mode, key))); + function (hive) hive.get(mode, key) || hive.getCandidates(mode, key)))); events.dbg("ON " + event.type.toUpperCase() + " " + DOM.Event.stringify(event) + " passing: " + this.passing + " " + @@ -902,20 +901,22 @@ var Events = Module("events", { } let urlbar = document.getElementById("urlbar"); - if (elem == null && urlbar && urlbar.inputField == this._lastFocus) + if (elem == null && urlbar && urlbar.inputField == this._lastFocus.get()) util.threadYield(true); // Why? --Kris while (modes.main.ownsFocus - && modes.topOfStack.params.ownsFocus != elem - && modes.topOfStack.params.ownsFocus != win + && let ({ ownsFocus } = modes.topOfStack.params) + (!ownsFocus || + ownsFocus.get() != elem && + ownsFocus.get() != win) && !modes.topOfStack.params.holdFocus) modes.pop(null, { fromFocus: true }); } finally { - this._lastFocus = elem; + this._lastFocus = util.weakReference(elem); if (modes.main.ownsFocus) - modes.topOfStack.params.ownsFocus = elem; + modes.topOfStack.params.ownsFocus = util.weakReference(elem); } }), diff --git a/common/content/help.xsl b/common/content/help.xsl index f1dfce9..0cc3613 100644 --- a/common/content/help.xsl +++ b/common/content/help.xsl @@ -297,8 +297,9 @@ + regexp:replace(regexp:replace(regexp:replace($tag, '%', 'g', '%25'), + '#', 'g', '%23'), + ';', 'g', '%3B')"/> diff --git a/common/content/hints.js b/common/content/hints.js index 004f907..96fe44a 100644 --- a/common/content/hints.js +++ b/common/content/hints.js @@ -301,7 +301,8 @@ var HintSession = Class("HintSession", CommandMode, { if (!rect.width || !rect.height) if (!Array.some(elem.childNodes, function (elem) elem instanceof Element && DOM(elem).style.float != "none" && isVisible(elem))) - return false; + if (elem.textContent || !elem.name) + return false; let computedStyle = doc.defaultView.getComputedStyle(elem, null); if (computedStyle.visibility != "visible" || computedStyle.display == "none") @@ -532,6 +533,7 @@ var HintSession = Class("HintSession", CommandMode, { */ removeHints: function _removeHints(timeout) { for (let { doc, start, end } in values(this.docs)) { + DOM(doc.documentElement).highlight.remove("Hinting"); // Goddamn stupid fucking Gecko 1.x security manager bullshit. try { delete doc.dactylLabels; } catch (e) { doc.dactylLabels = undefined; } @@ -574,6 +576,7 @@ var HintSession = Class("HintSession", CommandMode, { this.validHints = []; for (let { doc, start, end } in values(this.docs)) { + DOM(doc.documentElement).highlight.add("Hinting"); let [offsetX, offsetY] = this.getContainerOffsets(doc); inner: @@ -590,13 +593,13 @@ var HintSession = Class("HintSession", CommandMode, { if (!rect) continue; - hint.imgSpan = util.xmlToDom(, doc); - hint.imgSpan.style.display = "none"; - hint.imgSpan.style.left = (rect.left + offsetX) + "px"; - hint.imgSpan.style.top = (rect.top + offsetY) + "px"; - hint.imgSpan.style.width = (rect.right - rect.left) + "px"; - hint.imgSpan.style.height = (rect.bottom - rect.top) + "px"; - hint.span.parentNode.appendChild(hint.imgSpan); + hint.imgSpan = DOM(, doc).css({ + display: "none", + left: (rect.left + offsetX) + "px", + top: (rect.top + offsetY) + "px", + width: (rect.right - rect.left) + "px", + height: (rect.bottom - rect.top) + "px" + }).appendTo(hint.span.parentNode)[0]; } } @@ -720,7 +723,7 @@ var HintSession = Class("HintSession", CommandMode, { * Display the current status to the user. */ updateStatusline: function _updateStatusline() { - statusline.inputBuffer = (this.escapeNumbers ? options["mapleader"] : "") + + statusline.inputBuffer = (this.escapeNumbers ? "\\" : "") + (this.hintNumber ? this.getHintString(this.hintNumber) : ""); }, }); @@ -759,6 +762,11 @@ var Hints = Module("hints", { this.addMode("V", "View hint source in external editor", function (elem, loc) buffer.viewSource(loc, true)); this.addMode("y", "Yank hint location", function (elem, loc) editor.setRegister(null, loc, true)); this.addMode("Y", "Yank hint description", function (elem) editor.setRegister(null, elem.textContent || "", true)); + this.addMode("A", "Yank hint anchor url", function (elem) { + let uri = elem.ownerDocument.documentURIObject.clone(); + uri.ref = elem.id || elem.name; + dactyl.clipboardWrite(uri.spec, true); + }); this.addMode("c", "Open context menu", function (elem) DOM(elem).contextmenu()); this.addMode("i", "Show image", function (elem) dactyl.open(elem.src)); this.addMode("I", "Show image in a new tab", function (elem) dactyl.open(elem.src, dactyl.NEW_TAB)); @@ -1020,7 +1028,7 @@ var Hints = Module("hints", { let indexOf = String.indexOf; if (options.get("hintmatching").has("transliterated")) - indexOf = Hints.indexOf; + indexOf = Hints.closure.indexOf; switch (options["hintmatching"][0]) { case "contains" : return containsMatcher(hintString); @@ -1169,7 +1177,7 @@ var Hints = Module("hints", { [0x24d0, 0x24e9, "a"], [0xfb00, 0xfb06, ["ff", "fi", "fl", "ffi", "ffl", "st", "st"]], [0xff21, 0xff3a, "A"], [0xff41, 0xff5a, "a"] - ].forEach(function (start, stop, val) { + ].forEach(function ([start, stop, val]) { if (typeof val != "string") for (let i = start; i <= stop; i++) table[String.fromCharCode(i)] = val[(i - start) % val.length]; @@ -1187,7 +1195,7 @@ var Hints = Module("hints", { if (src.length == 0) return 0; outer: - for (var i = 0; i < end; i++) { + for (var i = 0; i <= end; i++) { var j = i; for (var k = 0; k < src.length;) { var s = dest[j++]; @@ -1259,7 +1267,7 @@ var Hints = Module("hints", { "Delete the previous character", function ({ self }) self.backspace()); - bind([""], + bind(["\\"], "Toggle hint filtering", function ({ self }) { self.escapeNumbers = !self.escapeNumbers; }); }, @@ -1267,8 +1275,10 @@ var Hints = Module("hints", { options.add(["extendedhinttags", "eht"], "XPath or CSS selector strings of hintable elements for extended hint modes", "regexpmap", { + // Make sure to update the docs when you change this. "[iI]": "img", "[asOTvVWy]": [":-moz-any-link", "area[href]", "img[src]", "iframe[src]"], + "[A]": ["[id]", "a[name]"], "[f]": "body", "[F]": ["body", "code", "div", "html", "p", "pre", "span"], "[S]": ["input:not([type=hidden])", "textarea", "button", "select"] @@ -1278,16 +1288,19 @@ var Hints = Module("hints", { getKey: function (val, default_) let (res = array.nth(this.value, function (re) let (match = re.exec(val)) match && match[0] == val, 0)) res ? res.matcher : default_, - setter: function (vals) { + parse: function parse(val) { + let vals = parse.supercall(this, val); for (let value in values(vals)) value.matcher = DOM.compileMatcher(Option.splitList(value.result)); return vals; }, + testValues: function testValues(vals, validator) vals.every(function (re) Option.splitList(re).every(validator)), validator: DOM.validateMatcher }); options.add(["hinttags", "ht"], "XPath or CSS selector strings of hintable elements for Hints mode", + // Make sure to update the docs when you change this. "stringlist", ":-moz-any-link,area,button,iframe,input:not([type=hidden]),select,textarea," + "[onclick],[onmouseover],[onmousedown],[onmouseup],[oncommand]," + "[tabindex],[role=link],[role=button],[contenteditable=true]", diff --git a/common/content/mappings.js b/common/content/mappings.js index 149f3fb..f14b3d2 100644 --- a/common/content/mappings.js +++ b/common/content/mappings.js @@ -355,7 +355,7 @@ var Mappings = Module("mappings", { get userHives() this.allHives.filter(function (h) h !== this.builtin, this), - expandLeader: function expandLeader(keyString) keyString.replace(//i, function () options["mapleader"]), + expandLeader: deprecated("your brain", function expandLeader(keyString) keyString), prefixes: Class.Memoize(function () { let list = Array.map("CASM", function (s) s + "-"); @@ -365,8 +365,6 @@ var Mappings = Module("mappings", { }), expand: function expand(keys) { - keys = keys.replace(//i, options["mapleader"]); - if (!/<\*-/.test(keys)) var res = keys; else @@ -541,7 +539,7 @@ var Mappings = Module("mappings", { args["-builtin"] = true; if (!rhs) // list the mapping - mappings.list(mapmodes, mappings.expandLeader(lhs), hives); + mappings.list(mapmodes, lhs, hives); else { util.assert(args["-group"].modifiable, _("map.builtinImmutable")); @@ -659,7 +657,7 @@ var Mappings = Module("mappings", { commands.add([ch + "no[remap]"], "Map a key sequence without remapping keys" + modeDescription, function (args) { map(args, true); }, - update({}, opts)); + update({ deprecated: ":" + ch + "map -builtin" }, opts)); commands.add([ch + "unm[ap]"], "Remove a mapping" + modeDescription, @@ -828,18 +826,10 @@ var Mappings = Module("mappings", { function (context, obj, args) [[m.names, m.description] for (m in this.iterate(args[0]))] ]); }, - options: function initOptions(dactyl, modules, window) { - options.add(["mapleader", "ml"], - "Define the replacement keys for the pseudo-key", - "string", "\\", { - setter: function (value) { - if (this.hasChanged) - for (let hive in values(mappings.allHives)) - for (let stack in values(hive.stacks)) - delete stack.states; - return value; - } - }); + mappings: function initMappings(dactyl, modules, window) { + mappings.add([modes.COMMAND], + ["\\"], "Emits pseudo-key", + function () { events.feedkeys("") }); } }); diff --git a/common/content/modes.js b/common/content/modes.js index 50b2ee5..127fa95 100644 --- a/common/content/modes.js +++ b/common/content/modes.js @@ -119,7 +119,7 @@ var Modes = Module("modes", { onKeyPress: function (events) { if (modes.main == modes.QUOTE) modes.pop(); } }); this.addMode("IGNORE", { hidden: true }, { - onKeyPress: function (events) Events.KILL, + onKeyPress: function (events) false, bases: [], passthrough: true }); @@ -299,7 +299,7 @@ var Modes = Module("modes", { return; } - params = params || this.getMode(mainMode || this.main).params; + params = params || Object.create(this.getMode(mainMode || this.main).params); if (!stack && mainMode != null && this._modeStack.length > 1) this.reset(); diff --git a/common/content/statusline.js b/common/content/statusline.js index 8eefc33..5b7d2a8 100644 --- a/common/content/statusline.js +++ b/common/content/statusline.js @@ -110,9 +110,11 @@ var StatusLine = Module("statusline", { "browser.overLink": function (link) { switch (options["showstatuslinks"]) { case "status": + this.overLink = link ? _("status.link", link) : null; this.status = link ? _("status.link", link) : buffer.uri; break; case "command": + this.overLink = null; if (link) dactyl.echo(_("status.link", link), commandline.FORCE_SINGLELINE); else @@ -250,7 +252,7 @@ var StatusLine = Module("statusline", { updateStatus: function updateStatus() { this.timeout(function () { - this.status = buffer.uri; + this.status = this.overLink || buffer.uri; }); }, diff --git a/common/locale/en-US/buffer.xml b/common/locale/en-US/buffer.xml index 42462c7..38673a5 100644 --- a/common/locale/en-US/buffer.xml +++ b/common/locale/en-US/buffer.xml @@ -533,9 +533,11 @@ <yank-location> y + y - -

Yank current location to the clipboard.

+ +

Yank current location to the clipboard. See also + yankshort.

diff --git a/common/locale/en-US/developer.xml b/common/locale/en-US/developer.xml index 869d4c4..9925d02 100644 --- a/common/locale/en-US/developer.xml +++ b/common/locale/en-US/developer.xml @@ -191,7 +191,7 @@ XML.prettyPrinting = false; <plugin nameflashblock version1.0 - hrefhttp://dactyl.sf.net/pentadactyl/plugins#flashblock-plugin + hrefhttp://5digits.org/pentadactyl/plugins#flashblock-plugin summaryFlash Blocker xmlns{NS}> Kris Maglione diff --git a/common/locale/en-US/hints.xml b/common/locale/en-US/hints.xml index 769f7da..ab5dc32 100644 --- a/common/locale/en-US/hints.xml +++ b/common/locale/en-US/hints.xml @@ -48,7 +48,7 @@
Moves the focus to the next hintable element
-
+
\
Temporarily treats all numbers (or other keys, depending on the value of hintkeys) as ordinary text
@@ -109,6 +109,7 @@
  • V to view its destination source in the external editor
  • y to yank its destination location
  • Y to yank its text description
  • +
  • A to yank its anchor URL
  • c to open its context menu
  • i to open an image
  • I to open an image in a new tab.
  • diff --git a/common/locale/en-US/map.xml b/common/locale/en-US/map.xml index 9ff5c88..d082024 100644 --- a/common/locale/en-US/map.xml +++ b/common/locale/en-US/map.xml @@ -66,13 +66,12 @@

    - The ordinary :map and :noremap commands - add mappings for Normal and Visual mode. In order to map key - bindings in a different mode, any of the mapping commands may be - prefixed with one of the above letters. For instance, + The ordinary :map command adds mappings for Normal and Visual + mode. In order to map key bindings in a different mode, any of the mapping + commands may be prefixed with one of the above letters. For instance, :imap creates a new key mapping in Insert mode, while - :cunmap removes a key mapping from Command Line mode. - Other modes can be specified using the -modes option described below. + :cunmap removes a key mapping from Command Line mode. Other modes + can be specified using the -modes option described below.

    @@ -127,12 +126,12 @@

    -
    :map :noremap :unmap
    Normal and Visual modes
    -
    :nmap :nnoremap :nunmap
    Normal mode
    -
    :vmap :vnoremap :vunmap
    Visual mode
    -
    :imap :inoremap :iunmap
    Insert mode
    -
    :tmap :tnoremap :tunmap
    Text Edit mode
    -
    :cmap :cnoremap :cunmap
    Command Line mode
    +
    :map :unmap
    Normal and Visual modes
    +
    :nmap :nunmap
    Normal mode
    +
    :vmap :vunmap
    Visual mode
    +
    :imap :iunmap
    Insert mode
    +
    :tmap :tunmap
    Text Edit mode
    +
    :cmap :cunmap
    Command Line mode
    @@ -175,6 +174,8 @@ :cno :cnoremap :cnoremap
    lhs rhs + These aliases are deprecated. The -builtin flag + should be used in their stead.

    Map the key-sequence lhs to rhs for the applicable mode(s). The keys in rhs do not @@ -372,6 +373,33 @@ + + ]]> + + ]]> + +

    + An arbitrary and meaningless key that some people seem + attached to. The \ key will, by default, emit + this pseudo-key so that it can be used at the start of + other mappings. Note, however, that there is nothing + special about it and any key can be mapped to emit any + other arbitrary pseudo-key in the same way. +

    + + :map , +:map ; + +:map k -js doStuff() +:map k -js doOtherReallyNiftyStuff() + +

    + Needless to say this behavior is considered childish and + discouraged. +

    +
    + + ]]> ]]> @@ -395,25 +423,6 @@ - - \]]> - - ]]> - -

    - A pseudo-key which expands to the value of the mapleader - option. For example, by default, -

    - :map h :echo Hello -

    works like

    - :map \h :echo Hello -

    but after

    - , -

    it works like

    - :map ,h :echo Hello -
    -
    -

    Mapping examples

    Make do the same as in input modes:

    diff --git a/common/locale/en-US/marks.xml b/common/locale/en-US/marks.xml index 75aaf29..154c429 100644 --- a/common/locale/en-US/marks.xml +++ b/common/locale/en-US/marks.xml @@ -119,11 +119,10 @@ :bmarks! filter

    - List or open multiple bookmarks. Opens the message window - at the bottom of the screen with all bookmarks with - titles or URLs matching filter. The resulting - URLs can be clicked, or accessed via extended hint modes - such as ;o. + List or open multiple bookmarks. Opens the message window at the + bottom of the screen with all bookmarks with URLs matching + filter. The resulting URLs can be clicked, or accessed via + extended hint modes such as ;o.

    @@ -180,7 +179,7 @@

    QuickMarks are bookmarks stripped to the bone for quickly getting to the pages that you visit most. A QuickMark is simply a URL assigned to a letter - or number. They can therefore be saved or opened with only three key + or digit. They can therefore be saved or opened with only three key presses each. QuickMarks are persistent across browser sessions.

    @@ -245,7 +244,7 @@ :qmark a-zA-Z0-9 url

    - Mark url with a letter for quick access. See also + Mark url with a letter or digit for quick access. See also go, gn, and M.

    diff --git a/common/locale/en-US/options.xml b/common/locale/en-US/options.xml index 6de3231..b48c504 100644 --- a/common/locale/en-US/options.xml +++ b/common/locale/en-US/options.xml @@ -18,7 +18,13 @@

    -
    boolean
    Can only be on or off
    +
    +
    boolean
    +
    + Can only be on (:set option) or + off (:set nooption) +
    +
    number
    A numeric value
    string
    A string value
    @@ -688,6 +694,7 @@ &option.extendedhinttags.type; [asOTvVWy]:':-moz-any-link',area[href],img[src],iframe[src], + [A]:[id],a[name], [f]:body, [F]:body,code,div,html,p,pre,span, [iI]:img, @@ -1047,16 +1054,26 @@ 'ln' 'linenumbers' 'linenumbers' 'ln' &option.linenumbers.type; - &option.linenumbers.default; + *, + mxr.mozilla.org:a.l, + pastebin.com:#code_frame>div>ol>li, + addons.mozilla.org:.gutter>.line>a, + bugzilla.mozilla.org:.bz_comment:not(.bz_first_comment):not(.ih_history), + *:'/* Hgweb/Gitweb */ .completecodeline a.codeline, a.linenr']]>

    Patterns used to determine line numbers used by G. May be - either a selector expression as accepted by hinttags, in - which case the first matching element whose text content is equal to - the desired line number is used or the countth element - failing that, or the string func: followed by a - function which, given arguments for the document and desired line - number, must return the target element. + either a selector expression as accepted by hinttags, or the + string func: followed by a JavaScript + expression which evaluates to a function. In the former case, the + first matching element whose text content is equal to the desired + line number is used. If no such element exists, the countth + matching element is used. In the latter case, the provided function + must accept two arguments, the document in question and the desired + line number, and must return the element corresponding to the given + line.

    @@ -1100,16 +1117,6 @@ - - 'ml' 'mapleader' - 'mapleader' 'ml' - &option.mapleader.type; - &option.mapleader.default; - -

    Defines the replacement keys for the pseudo-key.

    -
    -
    - 'maxitems' 'maxitems' @@ -1234,8 +1241,8 @@

    Pass certain keys through directly for the given URLs. - For any page with a URL matching a given regexp, all key - events for keys listed in that regexp's value are passed + For any page with a URL matching a given site-filter, all key + events for keys listed in the corresponding value are passed through directly to &dactyl.host;, and are not processed by &dactyl.appName; in any way. Key names are separated by commas, where the first key name is treated as a list @@ -1352,7 +1359,6 @@

    • colors/
    • -
    • macros/
    • plugins/
    @@ -1632,8 +1638,9 @@

    The regular expression used to split URL lists in commands like :open. When set to the empty string, URL lists - are never split. With the default value, the following will open - three URLs in the current tab and two new background tabs, + are never split. With the default settings, the following will open + three URLs (the first one in the current tab and the latter two in + two new background tabs):

    :open google Linux | wikipedia Arch Linux | imdb Serenity
    diff --git a/common/modules/addons.jsm b/common/modules/addons.jsm index c1e4e33..8ea7f8a 100644 --- a/common/modules/addons.jsm +++ b/common/modules/addons.jsm @@ -83,14 +83,14 @@ var actions = { name: "exte[nable]", description: "Enable an extension", action: function (addon) { addon.userDisabled = false; }, - filter: function ({ item }) item.userDisabled, + filter: function (addon) addon.userDisabled, perm: "enable" }, disable: { name: "extd[isable]", description: "Disable an extension", action: function (addon) { addon.userDisabled = true; }, - filter: function ({ item }) !item.userDisabled, + filter: function (addon) !addon.userDisabled, perm: "disable" }, options: { @@ -103,7 +103,7 @@ var actions = { else this.dactyl.open(addon.optionsURL, { from: "extoptions" }); }, - filter: function ({ item }) item.isActive && item.optionsURL + filter: function (addon) addon.isActive && addon.optionsURL }, rehash: { name: "extr[ehash]", @@ -117,8 +117,8 @@ var actions = { }); }, get filter() { - return function ({ item }) !item.userDisabled && - !(item.operationsRequiringRestart & (AddonManager.OP_NEEDS_RESTART_ENABLE | AddonManager.OP_NEEDS_RESTART_DISABLE)) + return function (addon) !addon.userDisabled && + !(addon.operationsRequiringRestart & (AddonManager.OP_NEEDS_RESTART_ENABLE | AddonManager.OP_NEEDS_RESTART_DISABLE)) }, perm: "disable" }, @@ -170,7 +170,7 @@ var Addon = Class("Addon", { let action = actions[cmd]; if ("perm" in action && !(this.permissions & AddonManager["PERM_CAN_" + action.perm.toUpperCase()])) return false; - if ("filter" in action && !action.filter({ item: this })) + if ("filter" in action && !action.filter(this)) return false; return true; }, @@ -356,7 +356,7 @@ var Addons = Module("addons", { }, { }, { commands: function (dactyl, modules, window) { - const { CommandOption, commands, completion } = modules; + const { CommandOption, commands, completion, io } = modules; commands.add(["addo[ns]", "ao"], "List installed extensions", @@ -412,7 +412,7 @@ var Addons = Module("addons", { // TODO: handle extension dependencies values(actions).forEach(function (command) { let perm = command.perm && AddonManager["PERM_CAN_" + command.perm.toUpperCase()]; - function ok(addon) !perm || addon.permissions & perm; + function ok(addon) (!perm || addon.permissions & perm) && (!command.filter || command.filter(addon)); commands.add(Array.concat(command.name), command.description, @@ -430,6 +430,7 @@ var Addons = Module("addons", { dactyl.assert(list.some(ok), _("error.invalidOperation")); list = list.filter(ok); } + dactyl.assert(list.every(ok)); if (command.actions) command.actions(list, this.modules); else @@ -441,8 +442,6 @@ var Addons = Module("addons", { completer: function (context, args) { completion.addon(context, args["-types"]); context.filters.push(function ({ item }) ok(item)); - if (command.filter) - context.filters.push(command.filter); }, literal: 0, options: [ diff --git a/common/modules/base.jsm b/common/modules/base.jsm index bc767f6..51d2f0d 100644 --- a/common/modules/base.jsm +++ b/common/modules/base.jsm @@ -289,7 +289,7 @@ function properties(obj, prototypes, debugger_) { for (; obj; obj = prototypes && prototype(obj)) { try { if (sandbox.Object.getOwnPropertyNames || !debugger_ || !services.debugger.isOn) - var iter = values(Object.getOwnPropertyNames(obj)); + var iter = (v for each (v in Object.getOwnPropertyNames(obj))); } catch (e) {} if (!iter) @@ -363,7 +363,7 @@ function keys(obj) iter(function keys() { * @returns {Generator} */ function values(obj) iter(function values() { - if (isinstance(obj, ["Generator", "Iterator"])) + if (isinstance(obj, ["Generator", "Iterator", Iter])) for (let k in obj) yield k; else @@ -1452,17 +1452,7 @@ function iter(obj, iface) { return iter(obj.enumerator()); return iter(obj.enumerator); } - res.__noSuchMethod__ = function __noSuchMethod__(meth, args) { - if (meth in iter) - var res = iter[meth].apply(iter, [this].concat(args)); - else - res = let (ary = array(this)) - ary[meth] ? ary[meth].apply(ary, args) : ary.__noSuchMethod__(meth, args); - if (isinstance(res, ["Iterator", "Generator"])) - return iter(res); - return res; - }; - return res; + return Iter(res); } update(iter, { toArray: function toArray(iter) array(iter).array, @@ -1576,6 +1566,23 @@ update(iter, { } }); +const Iter = Class("Iter", { + init: function init(iter) { + this.iter = iter; + if ("__iterator__" in iter) + this.iter = iter.__iterator__(); + + if (this.iter.finalize) + this.finalize = function finalize() this.iter.finalize.apply(this.iter, arguments); + }, + + next: function next() this.iter.next(), + + send: function send() this.iter.send.apply(this.iter, arguments), + + __iterator__: function () this.iter +}); + /** * Array utility methods. */ @@ -1731,6 +1738,40 @@ var array = Class("array", Array, { } }); +/* Make Minefield not explode, because Minefield exploding is not fun. */ +let iterProto = Iter.prototype; +Object.keys(iter).forEach(function (k) { + iterProto[k] = function () { + let res = iter[k].apply(iter, [this].concat(Array.slice(arguments))); + if (isinstance(res, ["Iterator", "Generator"])) + return Iter(res); + return res; + }; +}); + +Object.keys(array).forEach(function (k) { + if (!(k in iterProto)) + iterProto[k] = function () { + let res = array[k].apply(array, [this.toArray()].concat(Array.slice(arguments))); + if (isinstance(res, ["Iterator", "Generator"])) + return Iter(res); + if (isArray(res)) + return array(res); + return res; + }; +}); + +Object.getOwnPropertyNames(Array.prototype).forEach(function (k) { + if (!(k in iterProto) && callable(Array.prototype[k])) + iterProto[k] = function () { + let ary = iter(this).toArray(); + let res = ary[k].apply(ary, arguments); + if (isArray(res)) + return array(res); + return res; + }; +}); + endModule(); // catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);} diff --git a/common/modules/bookmarkcache.jsm b/common/modules/bookmarkcache.jsm index 55328a1..2647848 100644 --- a/common/modules/bookmarkcache.jsm +++ b/common/modules/bookmarkcache.jsm @@ -42,7 +42,19 @@ update(Bookmark.prototype, { if (!this.charset || this.charset === "UTF-8") return encodeURIComponent(str); let conv = services.CharsetConv(this.charset); - return escape(conv.ConvertFromUnicode(str) + conv.Finish()); + return escape(conv.ConvertFromUnicode(str) + conv.Finish()).replace(/\+/g, encodeURIComponent); + }, + + get folder() { + let res = []; + res.toString = function () this.join("/"); + + let id = this.id, parent, title; + while ((id = services.bookmarks.getFolderIdForItem(id)) && + (title = services.bookmarks.getItemTitle(id))) + res.push(title); + + return res.reverse(); } }) Bookmark.prototype.members.uri = Bookmark.prototype.members.url; @@ -94,8 +106,8 @@ var BookmarkCache = Module("BookmarkCache", XPCOM(Ci.nsINavBookmarkObserver), { let uri = newURI(node.uri); let keyword = services.bookmarks.getKeywordForBookmark(node.itemId); - let tags = tags in node ? (node.tags ? node.tags.split(/, /g) : []) - : services.tagging.getTagsForURI(uri, {}) || []; + let tags = "tags" in node ? (node.tags ? node.tags.split(/, /g) : []) + : services.tagging.getTagsForURI(uri, {}) || []; let post = BookmarkCache.getAnnotation(node.itemId, this.POST); let charset = BookmarkCache.getAnnotation(node.itemId, this.CHARSET); @@ -172,7 +184,11 @@ var BookmarkCache = Module("BookmarkCache", XPCOM(Ci.nsINavBookmarkObserver), { let query = services.history.getNewQuery(); let options = services.history.getNewQueryOptions(); options.queryType = options.QUERY_TYPE_BOOKMARKS; - options.excludeItemIfParentHasAnnotation = "livemark/feedURI"; + try { + // https://bugzil.la/702639 + options.excludeItemIfParentHasAnnotation = "livemark/feedURI"; + } + catch (e) {} let { root } = services.history.executeQuery(query, options); root.containerOpen = true; diff --git a/common/modules/bootstrap.jsm b/common/modules/bootstrap.jsm index 4343ec2..7145110 100644 --- a/common/modules/bootstrap.jsm +++ b/common/modules/bootstrap.jsm @@ -123,8 +123,9 @@ else }, cleanup: function unregister() { - for each (let factory in this.factories.splice(0)) + for each (let factory in this.factories) this.manager.unregisterFactory(factory.classID, factory); + this.factories = {}; }, purge: function purge() { @@ -185,11 +186,15 @@ else }), registerFactory: function registerFactory(factory) { + if (Set.has(this.factories, factory.contractID)) + this.manager.unregisterFactory(this.factories[factory.contractID].classID, + this.factories[factory.contractID]); + this.manager.registerFactory(factory.classID, String(factory.classID), factory.contractID, factory); - this.factories.push(factory); + this.factories[factory.contractID] = factory; } }; diff --git a/common/modules/buffer.jsm b/common/modules/buffer.jsm index 94b1692..357beea 100644 --- a/common/modules/buffer.jsm +++ b/common/modules/buffer.jsm @@ -13,8 +13,9 @@ defineModule("buffer", { }, this); this.lazyRequire("finder", ["RangeFind"]); +this.lazyRequire("io", ["io"]); this.lazyRequire("overlay", ["overlay"]); -this.lazyRequire("storage", ["storage"]); +this.lazyRequire("storage", ["File", "storage"]); this.lazyRequire("template", ["template"]); /** @@ -72,7 +73,7 @@ var Buffer = Module("Buffer", { dactyl.assert(url instanceof Ci.nsIURL); while (count-- && url.path != "/") - url.path = url.path.replace(/[^\/]+\/*$/, ""); + url.path = url.path.replace(/[^\/]*\/*$/, ""); dactyl.assert(!url.equals(this.documentURI)); dactyl.open(url.spec); @@ -394,7 +395,7 @@ var Buffer = Module("Buffer", { function a(regexp, elem) regexp.test(elem.textContent) === regexp.result || Array.some(elem.childNodes, function (child) regexp.test(child.alt) === regexp.result); - function b(regexp, elem) regexp.test(elem.title); + function b(regexp, elem) regexp.test(elem.title) === regexp.result; let res = Array.filter(frame.document.querySelectorAll(selector), Hints.isVisible); for (let test in values([a, b])) @@ -456,10 +457,11 @@ var Buffer = Module("Buffer", { let { dactyl } = this.modules; let ctrlKey = false, shiftKey = false; + let button = 0; switch (dactyl.forceTarget || where) { case dactyl.NEW_TAB: case dactyl.NEW_BACKGROUND_TAB: - ctrlKey = true; + button = 1; shiftKey = dactyl.forceBackground != null ? dactyl.forceBackground : where != dactyl.NEW_BACKGROUND_TAB; break; @@ -475,7 +477,7 @@ var Buffer = Module("Buffer", { prefs.withContext(function () { prefs.set("browser.tabs.loadInBackground", true); let params = { - screenX: offsetX, screenY: offsetY, + button: button, screenX: offsetX, screenY: offsetY, ctrlKey: ctrlKey, shiftKey: shiftKey, metaKey: ctrlKey }; @@ -633,18 +635,24 @@ var Buffer = Module("Buffer", { | persist.PERSIST_FLAGS_REPLACE_EXISTING_FILES; let window = this.topWindow; + if (!file.exists()) + file.create(Ci.nsIFile.NORMAL_FILE_TYPE, octal(666)); + let downloadListener = new window.DownloadListener(window, services.Transfer(uri, File(file).URI, "", null, null, null, persist)); - persist.progressListener = update(Object.create(downloadListener), { - onStateChange: util.wrapCallback(function onStateChange(progress, request, flags, status) { - if (callback && (flags & Ci.nsIWebProgressListener.STATE_STOP) && status == 0) - util.trapErrors(callback, self, uri, file, progress, request, flags, status); + if (callback) + persist.progressListener = update(Object.create(downloadListener), { + onStateChange: util.wrapCallback(function onStateChange(progress, request, flags, status) { + if (callback && (flags & Ci.nsIWebProgressListener.STATE_STOP) && status == 0) + util.trapErrors(callback, self, uri, file, progress, request, flags, status); - return onStateChange.superapply(this, arguments); - }) - }); + return onStateChange.superapply(this, arguments); + }) + }); + else + persist.progressListener = downloadListener; persist.saveURI(uri, null, null, null, null, file); }, @@ -1580,18 +1588,19 @@ var Buffer = Module("Buffer", { dactyl.assert(!arg || arg[0] == ">" && !config.OS.isWindows, _("error.trailingCharacters")); - const PRINTER = "PostScript/default"; - const BRANCH = "print.printer_" + PRINTER + "."; + const PRINTER = "PostScript/default"; + const BRANCH = "printer_" + PRINTER + "."; + const BRANCHES = ["print.", BRANCH, "print." + BRANCH]; + function set(pref, value) { + BRANCHES.forEach(function (branch) { prefs.set(branch + pref, value) }); + } prefs.withContext(function () { if (arg) { prefs.set("print.print_printer", PRINTER); - prefs.set( "print.print_to_file", true); - prefs.set(BRANCH + "print_to_file", true); - - prefs.set( "print.print_to_filename", io.File(arg.substr(1)).path); - prefs.set(BRANCH + "print_to_filename", io.File(arg.substr(1)).path); + set("print_to_file", true); + set("print_to_filename", io.File(arg.substr(1)).path); dactyl.echomsg(_("print.toFile", arg.substr(1))); } @@ -1599,19 +1608,21 @@ var Buffer = Module("Buffer", { dactyl.echomsg(_("print.sending")); prefs.set("print.always_print_silent", args.bang); - prefs.set("print.show_print_progress", !args.bang); + if (false) + prefs.set("print.show_print_progress", !args.bang); config.browser.contentWindow.print(); }); - if (arg) - dactyl.echomsg(_("print.printed", arg.substr(1))); - else - dactyl.echomsg(_("print.sent")); + dactyl.echomsg(_("print.sent")); }, { argCount: "?", bang: true, + completer: function (context, args) { + if (args.bang && /^>/.test(context.filter)) + context.fork("file", 1, modules.completion, "file"); + }, literal: 0 }); @@ -1806,7 +1817,8 @@ var Buffer = Module("Buffer", { context.incomplete = false; try { if (/filename="(.*?)"/.test(xhr.getResponseHeader("Content-Disposition"))) - context.completions.push([decodeURIComponent(RegExp.$1), _("buffer.save.suggested")]); + context.completions.push([decodeURIComponent(RegExp.$1), + _("buffer.save.suggested")]); } finally { context.completions = context.completions.slice(); @@ -2020,7 +2032,7 @@ var Buffer = Module("Buffer", { mappings.add([modes.NORMAL], ["[[", ""], "Follow the link labeled 'prev', 'previous' or '<' if it exists", function (args) { - buffer.findLink("previous", options["previouspattern"], (args.count || 1) - 1, true); + buffer.findLink("prev", options["previouspattern"], (args.count || 1) - 1, true); }, { count: true }); @@ -2230,11 +2242,14 @@ var Buffer = Module("Buffer", { options.add(["linenumbers", "ln"], "Patterns used to determine line numbers used by G", "sitemap", { + // Make sure to update the docs when you change this. + "view-source:*": 'body,[id^=line]', "code.google.com": '#nums [id^="nums_table"] a[href^="#"]', "github.com": '.line_numbers>*', "mxr.mozilla.org": 'a.l', "pastebin.com": '#code_frame>div>ol>li', "addons.mozilla.org": '.gutter>.line>a', + "bugzilla.mozilla.org": ".bz_comment:not(.bz_first_comment):not(.ih_history)", "*": '/* Hgweb/Gitweb */ .completecodeline a.codeline, a.linenr' }, { @@ -2277,12 +2292,12 @@ var Buffer = Module("Buffer", { options.add(["nextpattern"], "Patterns to use when guessing the next page in a document sequence", - "regexplist", UTF8(/'\bnext\b',^>$,^(>>|»)$,^(>|»),(>|»)$,'\bmore\b'/.source), + "regexplist", UTF8(/'^Next [>»]','^Next »','\bnext\b',^>$,^(>>|»)$,^(>|»),(>|»)$,'\bmore\b'/.source), { regexpFlags: "i" }); options.add(["previouspattern"], "Patterns to use when guessing the previous page in a document sequence", - "regexplist", UTF8(/'\bprev|previous\b',^<$,^(<<|«)$,^(<|«),(<|«)$/.source), + "regexplist", UTF8(/'[<«] Prev$','« Prev$','\bprev(ious)?\b',^<$,^(<<|«)$,^(<|«),(<|«)$/.source), { regexpFlags: "i" }); options.add(["pageinfo", "pa"], diff --git a/common/modules/completion.jsm b/common/modules/completion.jsm index ff9c091..c44cbb3 100644 --- a/common/modules/completion.jsm +++ b/common/modules/completion.jsm @@ -410,7 +410,7 @@ var CompletionContext = Class("CompletionContext", { this._cache.offset = this.offset; this.lastActivated = this.top.runCount; } - if (!this.itemCache[this.key]) { + if (!this.itemCache[this.key] && !this.waitingForTab) { try { let res = this._generate(); if (res != null) @@ -657,6 +657,7 @@ var CompletionContext = Class("CompletionContext", { } catch (e) { util.reportError(e); + XML.ignoreWhitespace = XML.prettyPrinting = false; cache[idx] = util.xmlToDom(
  • {this.text} 
  • @@ -1014,7 +1015,6 @@ var Completion = Module("completion", { context.hasItems = true; context.completions = context.completions.filter(function ({ url, title }) words.every(function (w) (url + " " + title).toLowerCase().indexOf(w) >= 0)) - context.incomplete = true; context.format = this.modules.bookmarks.format; context.keys.extra = function (item) { @@ -1033,20 +1033,24 @@ var Completion = Module("completion", { running[provider] = false; }; - service.startSearch(context.filter, "", context.result, { - onSearchResult: util.wrapCallback(function onSearchResult(search, result) { - if (result.searchResult <= result.RESULT_SUCCESS) - running[provider] = null; - - context.incomplete = result.searchResult >= result.RESULT_NOMATCH_ONGOING; - context.completions = [ - { url: result.getValueAt(i), title: result.getCommentAt(i), icon: result.getImageAt(i) } - for (i in util.range(0, result.matchCount)) - ]; - }), - get onUpdateSearchResult() this.onSearchResult - }); - running[provider] = true; + if (!context.waitingForTab) { + context.incomplete = true; + + service.startSearch(context.filter, "", context.result, { + onSearchResult: util.wrapCallback(function onSearchResult(search, result) { + if (result.searchResult <= result.RESULT_SUCCESS) + running[provider] = null; + + context.incomplete = result.searchResult >= result.RESULT_NOMATCH_ONGOING; + context.completions = [ + { url: result.getValueAt(i), title: result.getCommentAt(i), icon: result.getImageAt(i) } + for (i in util.range(0, result.matchCount)) + ]; + }), + get onUpdateSearchResult() this.onSearchResult + }); + running[provider] = true; + } }), urls: function (context, tags) { diff --git a/common/modules/config.jsm b/common/modules/config.jsm index c9c1420..f92ea22 100644 --- a/common/modules/config.jsm +++ b/common/modules/config.jsm @@ -19,6 +19,7 @@ this.lazyRequire("highlight", ["highlight"]); this.lazyRequire("messages", ["_"]); this.lazyRequire("prefs", ["localPrefs", "prefs"]); this.lazyRequire("storage", ["storage", "File"]); +this.lazyRequire("styles", ["Styles"]); function AboutHandler() {} AboutHandler.prototype = { @@ -215,7 +216,8 @@ var ConfigBase = Class("ConfigBase", { let jar = io.isJarURL(uri); if (jar) { let prefix = getDir(jar.JAREntry); - var res = iter(s.slice(prefix.length).replace(/\/.*/, "") for (s in io.listJar(jar.JARFile, prefix))) + var res = iter(s.slice(prefix.length).replace(/\/.*/, "") + for (s in io.listJar(jar.JARFile, prefix))) .toArray(); } else { @@ -225,7 +227,7 @@ var ConfigBase = Class("ConfigBase", { if (f.isDirectory())).array; } - function exists(pkg) services["resource:"].hasSubstitution("dactyl-locale-" + pkg); + let exists = function exists(pkg) services["resource:"].hasSubstitution("dactyl-locale-" + pkg); return array.uniq([this.appLocale, this.appLocale.replace(/-.*/, "")] .filter(exists) @@ -402,11 +404,11 @@ var ConfigBase = Class("ConfigBase", { dtdDactyl: memoize({ get name() config.name, - get home() "http://dactyl.sourceforge.net/", + get home() "http://5digits.org/", 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 plugins() "http://5digits.org/" + this.name + "/plugins", get faq() this.home + this.name + "/faq", "list.mailto": Class.Memoize(function () config.name + "@googlegroups.com"), diff --git a/common/modules/contexts.jsm b/common/modules/contexts.jsm index 4681936..6d32fb8 100644 --- a/common/modules/contexts.jsm +++ b/common/modules/contexts.jsm @@ -222,7 +222,10 @@ var Contexts = Module("contexts", { util.trapErrors("onUnload", self); } else { - self = args && !isArray(args) ? args : newContext.apply(null, args || [userContext]); + let params = Array.slice(args || [userContext]); + params[2] = params[2] || File(file).URI.spec; + + self = args && !isArray(args) ? args : newContext.apply(null, params); update(self, { NAME: Const(id), diff --git a/common/modules/dom.jsm b/common/modules/dom.jsm index 981ec9c..9f7f7e5 100644 --- a/common/modules/dom.jsm +++ b/common/modules/dom.jsm @@ -10,6 +10,9 @@ defineModule("dom", { exports: ["$", "DOM", "NS", "XBL", "XHTML", "XUL"] }, this); +this.lazyRequire("highlight", ["highlight"]); +this.lazyRequire("template", ["template"]); + var XBL = Namespace("xbl", "http://www.mozilla.org/xbl"); var XHTML = Namespace("html", "http://www.w3.org/1999/xhtml"); var XUL = Namespace("xul", "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); @@ -55,12 +58,12 @@ var DOM = Class("DOM", { this[length++] = DOM.fromXML(val, context, this.nodes); else if (val instanceof Ci.nsIDOMNode || val instanceof Ci.nsIDOMWindow) this[length++] = val; - else if ("length" in val) - for (let i = 0; i < val.length; i++) - this[length++] = val[i]; else if ("__iterator__" in val || isinstance(val, ["Iterator", "Generator"])) for (let elem in val) this[length++] = elem; + else if ("length" in val) + for (let i = 0; i < val.length; i++) + this[length++] = val[i]; else this[length++] = val; @@ -191,11 +194,11 @@ var DOM = Class("DOM", { this.each(function (elem) { while(true) { elem = fn.call(this, elem) - if (elem instanceof Ci.nsIDOMElement) + if (elem instanceof Ci.nsIDOMNode) res[res.length++] = elem; else if (elem && "length" in elem) - for (let i = 0; i < tmp.length; i++) - res[res.length++] = tmp[j]; + for (let i = 0; i < elem.length; i++) + res[res.length++] = elem[j]; else break; } @@ -256,6 +259,9 @@ var DOM = Class("DOM", { get siblingsBefore() this.all(function (elem) elem.previousElementSibling), get siblingsAfter() this.all(function (elem) elem.nextElementSibling), + get allSiblingsBefore() this.all(function (elem) elem.previousSibling), + get allSiblingsAfter() this.all(function (elem) elem.nextSibling), + get class() let (self = this) ({ toString: function () self[0].className, @@ -318,22 +324,26 @@ var DOM = Class("DOM", { this[0] ? this[0].getBoundingClientRect() : {}, get viewport() { - if (this[0] instanceof Ci.nsIDOMWindow) + let node = this[0]; + if (node instanceof Ci.nsIDOMDocument) + node = node.defaultView; + + if (node instanceof Ci.nsIDOMWindow) return { get width() this.right - this.left, get height() this.bottom - this.top, - bottom: this[0].innerHeight, - right: this[0].innerWidth, + bottom: node.innerHeight, + right: node.innerWidth, top: 0, left: 0 }; let r = this.rect; return { - width: this[0].clientWidth, - height: this[0].clientHeight, - top: r.top + this[0].clientTop, + width: node.clientWidth, + height: node.clientHeight, + top: r.top + node.clientTop, get bottom() this.top + this.height, - left: r.left + this[0].clientLeft, + left: r.left + node.clientLeft, get right() this.left + this.width } }, @@ -406,7 +416,7 @@ var DOM = Class("DOM", { return editor; }, - get isEditable() !!this.editor, + get isEditable() !!this.editor || this[0] instanceof Ci.nsIDOMElement && this.style.MozUserModify == "read-write", get isInput() isinstance(this[0], [Ci.nsIDOMHTMLInputElement, Ci.nsIDOMHTMLTextAreaElement, @@ -784,25 +794,44 @@ var DOM = Class("DOM", { else event = array.toObject([[event, listener]]); - for (let [k, v] in Iterator(event)) - event[k] = util.wrapCallback(v, true); + for (let [evt, callback] in Iterator(event)) + event[evt] = util.wrapCallback(callback, true); return this.each(function (elem) { - for (let [k, v] in Iterator(event)) - elem.addEventListener(k, v, capture); + for (let [evt, callback] in Iterator(event)) + elem.addEventListener(evt, callback, capture); }); }, unlisten: function unlisten(event, listener, capture) { if (isObject(event)) capture = listener; else - event = array.toObject([[key, val]]); + event = array.toObject([[event, listener]]); return this.each(function (elem) { for (let [k, v] in Iterator(event)) elem.removeEventListener(k, v.wrapper || v, capture); }); }, + once: function once(event, listener, capture) { + if (isObject(event)) + capture = listener; + else + event = array.toObject([[event, listener]]); + + for (let pair in Iterator(event)) { + let [evt, callback] = pair; + event[evt] = util.wrapCallback(function wrapper(event) { + this.removeEventListener(evt, wrapper.wrapper, capture); + return callback.apply(this, arguments); + }, true); + } + + return this.each(function (elem) { + for (let [k, v] in Iterator(event)) + elem.addEventListener(k, v, capture); + }); + }, dispatch: function dispatch(event, params, extraProps) { this.canceled = false; @@ -945,21 +974,30 @@ var DOM = Class("DOM", { // want to refer to within dactyl's source code for // comparisons like if (key == "") { ... } this.keyTable = { - add: ["Plus", "Add"], + add: ["+", "Plus", "Add"], + back_quote: ["`"], + back_slash: ["\\"], back_space: ["BS"], + comma: [","], count: ["count"], + close_bracket: ["]"], delete: ["Del"], + equals: ["="], escape: ["Esc", "Escape"], insert: ["Insert", "Ins"], leader: ["Leader"], left_shift: ["LT", "<"], nop: ["Nop"], + open_bracket: ["["], pass: ["Pass"], + period: ["."], + quote: ["'"], return: ["Return", "CR", "Enter"], right_shift: [">"], + semicolon: [";"], slash: ["/"], space: ["Space", " "], - subtract: ["Minus", "Subtract"] + subtract: ["-", "Minus", "Subtract"] }; this.key_key = {}; @@ -1176,7 +1214,9 @@ var DOM = Class("DOM", { if (event.keyCode in this.code_key) { key = this.code_key[event.keyCode]; - if (event.shiftKey && (key.length > 1 || event.ctrlKey || event.altKey || event.metaKey) || event.dactylShift) + if (event.shiftKey && (key.length > 1 || key.toUpperCase() == key.toLowerCase() + || event.ctrlKey || event.altKey || event.metaKey) + || event.dactylShift) modifier += "S-"; else if (!modifier && key.length === 1) if (event.shiftKey) @@ -1530,12 +1570,16 @@ var DOM = Class("DOM", { null ); - return Object.create(result, { - __iterator__: { - value: asIterator ? function () { let elem; while ((elem = this.iterateNext())) yield elem; } - : function () { for (let i = 0; i < this.snapshotLength; i++) yield this.snapshotItem(i); } - } - }); + let res = { + iterateNext: function () result.iterateNext(), + get resultType() result.resultType, + get snapshotLength() result.snapshotLength, + snapshotItem: function (i) result.snapshotItem(i), + __iterator__: + asIterator ? function () { let elem; while ((elem = this.iterateNext())) yield elem; } + : function () { for (let i = 0; i < this.snapshotLength; i++) yield this.snapshotItem(i); } + }; + return res; } catch (e) { throw e.stack ? e : Error(e); diff --git a/common/modules/downloads.jsm b/common/modules/downloads.jsm index 03ab199..e4d2f11 100644 --- a/common/modules/downloads.jsm +++ b/common/modules/downloads.jsm @@ -9,6 +9,8 @@ defineModule("downloads", { exports: ["Download", "Downloads", "downloads"] }, this); +this.lazyRequire("overlay", ["overlay"]); + Cu.import("resource://gre/modules/DownloadUtils.jsm", this); let prefix = "DOWNLOAD_"; @@ -26,6 +28,8 @@ var Download = Class("Download", { this.nodes = { commandTarget: self }; + XML.ignoreWhitespace = true; + XML.prettyPrinting = false; util.xmlToDom( @@ -222,8 +226,10 @@ var DownloadList = Class("DownloadList", message: Class.Memoize(function () { + XML.ignoreWhitespace = true; + XML.prettyPrinting = false; util.xmlToDom( - + {_("title.Title")}{_("title.Status")} @@ -251,6 +257,9 @@ var DownloadList = Class("DownloadList",
    , this.document, this.nodes); + this.index = Array.indexOf(this.nodes.list.childNodes, + this.nodes.head); + for (let row in iter(services.downloadManager.DBConnection .createStatement("SELECT id FROM moz_downloads"))) this.addDownload(row.id); @@ -272,7 +281,7 @@ var DownloadList = Class("DownloadList", .indexOf(download); this.nodes.list.insertBefore(download.nodes.row, - this.nodes.list.childNodes[index + 1]); + this.nodes.list.childNodes[index + this.index + 1]); } }, removeDownload: function removeDownload(id) { @@ -388,7 +397,30 @@ var DownloadList = Class("DownloadList", } }); -var Downloads = Module("downloads", { +var Downloads = Module("downloads", XPCOM(Ci.nsIDownloadProgressListener), { + init: function () { + services.downloadManager.addListener(this); + }, + + destroy: function destroy() { + services.downloadManager.removeListener(this); + }, + + 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; + + + overlay.modules.forEach(function (modules) { + modules.dactyl.echomsg({ domains: [util.getHost(url)], message: _("io.downloadFinished", title, file) }, + 1, modules.commandline.ACTIVE_WINDOW); + modules.autocommands.trigger("DownloadPost", { url: url, title: title, file: file, size: size }); + }); + } + } }, { }, { commands: function initCommands(dactyl, modules, window) { diff --git a/common/modules/finder.jsm b/common/modules/finder.jsm index 2696630..bde3f42 100644 --- a/common/modules/finder.jsm +++ b/common/modules/finder.jsm @@ -44,7 +44,7 @@ var RangeFinder = Module("rangefinder", { init: function init() { prefs.safeSet("accessibility.typeaheadfind.autostart", false); - // The above should be sufficient, but: http://dactyl.sf.net/bmo/348187 + // The above should be sufficient, but: http://bugzil.la/348187 prefs.safeSet("accessibility.typeaheadfind", false); }, diff --git a/common/modules/help.jsm b/common/modules/help.jsm index d12312b..dadadfc 100644 --- a/common/modules/help.jsm +++ b/common/modules/help.jsm @@ -126,7 +126,7 @@ var Help = Module("Help", { | (?: ^ [^\S\n]* \n) + ]]>), "gmxy"); - let betas = util.regexp(/\[(b\d)\]/, "gx"); + let betas = util.regexp(/\[((?:b|rc)\d)\]/, "gx"); let beta = array(betas.iterate(NEWS)) .map(function (m) m[1]).uniq().slice(-1)[0]; @@ -317,12 +317,12 @@ var Help = Module("Help", { .split(" ")); function fix(node) { switch(node.nodeType) { - case Node.ELEMENT_NODE: - if (isinstance(node, [HTMLBaseElement])) + case Ci.nsIDOMNode.ELEMENT_NODE: + if (isinstance(node, [Ci.nsIDOMHTMLBaseElement])) return; data.push("<"); data.push(node.localName); - if (node instanceof HTMLHtmlElement) + if (node instanceof Ci.nsIDOMHTMLHtmlElement) data.push(" xmlns=" + XHTML.uri.quote(), " xmlns:dactyl=" + NS.uri.quote()); @@ -335,8 +335,14 @@ var Help = Module("Help", { if (name == "href") { value = node.href || value; if (value.indexOf("dactyl://help-tag/") == 0) { - let uri = services.io.newChannel(value, null, null).originalURI; - value = uri.spec == value ? "javascript:;" : uri.path.substr(1); + try { + let uri = services.io.newChannel(value, null, null).originalURI; + value = uri.spec == value ? "javascript:;" : uri.path.substr(1); + } + catch (e) { + util.dump("Magical tag thingy failure for: " + value); + dactyl.reportError(e); + } } if (!/^#|[\/](#|$)|^[a-z]+:/.test(value)) value = value.replace(/(#|$)/, ".xhtml$1"); @@ -354,24 +360,26 @@ var Help = Module("Help", { data.push(" />"); else { data.push(">"); - if (node instanceof HTMLHeadElement) + if (node instanceof Ci.nsIDOMHTMLHeadElement) data.push(.toXMLString()); Array.map(node.childNodes, fix); data.push(""); } break; - case Node.TEXT_NODE: + case Ci.nsIDOMNode.TEXT_NODE: data.push(<>{node.textContent}.toXMLString()); } } + let { buffer, content, events } = modules; let chromeFiles = {}; let styles = {}; + for (let [file, ] in Iterator(help.files)) { let url = "dactyl://help/" + file; dactyl.open(url); util.waitFor(function () content.location.href == url && buffer.loaded - && content.document.documentElement instanceof HTMLHtmlElement, + && content.document.documentElement instanceof Ci.nsIDOMHTMLHtmlElement, 15000); events.waitForPageLoad(); var data = [ diff --git a/common/modules/io.jsm b/common/modules/io.jsm index 5eefcc1..0e0a394 100644 --- a/common/modules/io.jsm +++ b/common/modules/io.jsm @@ -41,26 +41,6 @@ var IO = Module("io", { 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: _("io.downloadFinished", title, file) }, - 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); }, CommandFileMode: Class("CommandFileMode", modules.CommandMode, { @@ -84,10 +64,6 @@ var IO = Module("io", { } }), - destroy: function destroy() { - services.downloadManager.removeListener(this.downloadListener); - }, - /** * Returns all directories named *name* in 'runtimepath'. * @@ -331,14 +307,19 @@ var IO = Module("io", { * * @returns {File} */ - createTempFile: function createTempFile(name) { - if (name instanceof Ci.nsIFile) + createTempFile: function createTempFile(name, type) { + if (name instanceof Ci.nsIFile) { var file = name.clone(); + if (!type || type == "file") + file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, octal(666)); + else + file.createUnique(Ci.nsIFile.DIRECTORY_TYPE, octal(777)); + } else { file = services.directory.get("TmpD", Ci.nsIFile); file.append(this.config.tempFile + (name ? "." + name : "")); + file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, octal(666)); } - file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, octal(600)); services.externalApp.deleteTemporaryFileOnExit(file); @@ -414,7 +395,8 @@ var IO = Module("io", { if (bin instanceof File || File.isAbsolutePath(bin)) return this.File(bin); - let dirs = services.environment.get("PATH").split(config.OS.isWindows ? ";" : ":"); + let dirs = services.environment.get("PATH") + .split(config.OS.pathListSep); // Windows tries the CWD first TODO: desirable? if (config.OS.isWindows) dirs = [io.cwd].concat(dirs); @@ -487,10 +469,12 @@ var IO = Module("io", { // TODO: when https://bugzilla.mozilla.org/show_bug.cgi?id=68702 is // fixed use that instead of a tmpfile /** - * Runs *command* in a subshell and returns the output in a string. The - * shell used is that specified by the 'shell' option. + * Runs *command* in a subshell and returns the output. The shell used is + * that specified by the 'shell' option. * - * @param {string} command The command to run. + * @param {string|[string]} command The command to run. This can be a shell + * command string or an array of strings (a command and arguments) + * which will be escaped and concatenated. * @param {string} input Any input to be provided to the command on stdin. * @param {function(object)} callback A callback to be called when * the command completes. @optional @@ -556,7 +540,8 @@ var IO = Module("io", { * otherwise, the return value of *func*. */ withTempFiles: function withTempFiles(func, self, checked, ext) { - let args = array(util.range(0, func.length)).map(bind("createTempFile", this, ext)).array; + let args = array(util.range(0, func.length)) + .map(bind("createTempFile", this, ext)).array; try { if (!args.every(util.identity)) return false; diff --git a/common/modules/javascript.jsm b/common/modules/javascript.jsm index a4d1ab6..c33ca86 100644 --- a/common/modules/javascript.jsm +++ b/common/modules/javascript.jsm @@ -10,7 +10,8 @@ try { Components.utils.import("resource://dactyl/bootstrap.jsm"); defineModule("javascript", { - exports: ["JavaScript", "javascript"] + exports: ["JavaScript", "javascript"], + require: ["util"] }, this); let isPrototypeOf = Object.prototype.isPrototypeOf; @@ -53,9 +54,9 @@ var JavaScript = Module("javascript", { lazyInit: true, - newContext: function () this.modules.newContext(this.modules.userContext, true), + newContext: function () this.modules.newContext(this.modules.userContext, true, "Dactyl JS Temp Context"), - get completers() JavaScript.completers, // For backward compatibility + completers: Class.Memoize(function () Object.create(JavaScript.completers)), // Some object members are only accessible as function calls getKey: function (obj, key) { @@ -550,7 +551,7 @@ var JavaScript = Module("javascript", { } catch (e) {} if (!completer) - completer = JavaScript.completers[funcName]; + completer = this.completers[funcName]; if (!completer) return null; @@ -765,10 +766,10 @@ var JavaScript = Module("javascript", { let self = this; let sandbox = true || isinstance(context, ["Sandbox"]); - this.context = modules.newContext(context, !sandbox); + this.context = modules.newContext(context, !sandbox, "Dactyl REPL Context"); this.js = modules.JavaScript(); this.js.replContext = this.context; - this.js.newContext = function newContext() modules.newContext(self.context, !sandbox); + this.js.newContext = function newContext() modules.newContext(self.context, !sandbox, "Dactyl REPL Temp Context"); this.js.globals = [ [this.context, /*L*/"REPL Variables"], diff --git a/common/modules/main.jsm b/common/modules/main.jsm index 2740b66..ffc89ee 100644 --- a/common/modules/main.jsm +++ b/common/modules/main.jsm @@ -28,6 +28,8 @@ var ModuleBase = Class("ModuleBase", { toString: function () "[module " + this.constructor.className + "]" }); +var _id = 0; + var Modules = function Modules(window) { /** * @constructor Module @@ -136,7 +138,7 @@ var Modules = function Modules(window) { } }, - newContext: function newContext(proto, normal) { + newContext: function newContext(proto, normal, name) { if (normal) return create(proto); @@ -144,6 +146,7 @@ var Modules = function Modules(window) { var sandbox = services.dactyl.createGlobal(); else sandbox = Components.utils.Sandbox(window, { sandboxPrototype: proto || modules, + sandboxName: name || ("Dactyl Sandbox " + ++_id), wantXrays: false }); // Hack: @@ -197,13 +200,6 @@ overlay.overlayWindow(Object.keys(config.overlays), function _overlay(window) ({ this.loaded = {}; modules.loaded = this.loaded; - defineModule.modules.forEach(function defModule(mod) { - let names = Set(Object.keys(mod.INIT)); - if ("init" in mod.INIT) - Set.add(names, "init"); - - keys(names).forEach(function (name) { self.deferInit(name, mod.INIT, mod); }); - }); this.modules = modules; this.scanModules(); @@ -308,28 +304,38 @@ overlay.overlayWindow(Object.keys(config.overlays), function _overlay(window) ({ let className = mod.className || mod.constructor.className; - init[className] = function callee() { - function finish() { - this.currentDependency = className; - defineModule.time(className, name, INIT[name], mod, - modules.dactyl, modules, window); - } - if (!callee.frobbed) { - callee.frobbed = true; - if (modules[name] instanceof Class) - modules[name].withSavedValues(["currentDependency"], finish); - else - finish.call({}); - } - }; + if (!Set.has(init, className)) { + init[className] = function callee() { + function finish() { + this.currentDependency = className; + defineModule.time(className, name, INIT[name], mod, + modules.dactyl, modules, window); + } + if (!callee.frobbed) { + callee.frobbed = true; + if (modules[name] instanceof Class) + modules[name].withSavedValues(["currentDependency"], finish); + else + finish.call({}); + } + }; - INIT[name].require = function (name) { init[name](); }; + INIT[name].require = function (name) { init[name](); }; + } }, scanModules: function scanModules() { let self = this; let { Module, modules } = this.modules; + defineModule.modules.forEach(function defModule(mod) { + let names = Set(Object.keys(mod.INIT)); + if ("init" in mod.INIT) + Set.add(names, "init"); + + keys(names).forEach(function (name) { self.deferInit(name, mod.INIT, mod); }); + }); + Module.list.forEach(function frobModule(mod) { if (!mod.frobbed) { modules.__defineGetter__(mod.className, function () { diff --git a/common/modules/messages.jsm b/common/modules/messages.jsm index bca3669..3e20f27 100644 --- a/common/modules/messages.jsm +++ b/common/modules/messages.jsm @@ -45,7 +45,8 @@ var Messages = Module("messages", { array.uniq([JSMLoader.getTarget("dactyl://locale/" + this.name + ".properties"), JSMLoader.getTarget("dactyl://locale-local/" + this.name + ".properties"), "resource://dactyl-locale/en-US/" + this.name + ".properties", - "resource://dactyl-locale-local/en-US/" + this.name + ".properties"]) + "resource://dactyl-locale-local/en-US/" + this.name + ".properties"], + true) .map(services.stringBundle.createBundle) .filter(function (bundle) { try { bundle.getSimpleEnumeration(); return true; } catch (e) { return false; } })), diff --git a/common/modules/options.jsm b/common/modules/options.jsm index 512e304..dacafbd 100644 --- a/common/modules/options.jsm +++ b/common/modules/options.jsm @@ -339,7 +339,9 @@ var Option = Class("Option", { if (isArray(defaultValue)) defaultValue = defaultValue.map(Option.quote).join(","); else if (isObject(defaultValue)) - defaultValue = iter(defaultValue).map(function (val) val.map(Option.quote).join(":")).join(","); + defaultValue = iter(defaultValue).map(function (val) val.map(function (v) Option.quote(v, /:/)) + .join(":")) + .join(","); if (isArray(defaultValue)) defaultValue = defaultValue.map(Option.quote).join(","); @@ -446,16 +448,16 @@ var Option = Class("Option", { }, unparseRegexp: function unparseRegexp(re, quoted) re.bang + Option.quote(util.regexp.getSource(re), /^!|:/) + - (typeof re.result === "boolean" ? "" : ":" + (quoted ? re.result : Option.quote(re.result))), + (typeof re.result === "boolean" ? "" : ":" + (quoted ? re.result : Option.quote(re.result, /:/))), parseSite: function parseSite(pattern, result, rest) { if (isArray(rest)) // Called by Array.map result = undefined; let [, bang, filter] = /^(!?)(.*)/.exec(pattern); - filter = Option.dequote(filter); + filter = Option.dequote(filter).trim(); - let quote = this.keepQuotes ? util.identity : Option.quote; + let quote = this.keepQuotes ? util.identity : function (v) Option.quote(v, /:/); return update(Styles.matchFilter(filter), { bang: bang, @@ -491,7 +493,7 @@ var Option = Class("Option", { stringlist: function (vals) vals.map(Option.quote).join(","), - stringmap: function (vals) [Option.quote(k, /:/) + ":" + Option.quote(v) for ([k, v] in Iterator(vals))].join(","), + stringmap: function (vals) [Option.quote(k, /:/) + ":" + Option.quote(v, /:/) for ([k, v] in Iterator(vals))].join(","), regexplist: function (vals) vals.join(","), get regexpmap() this.regexplist, diff --git a/common/modules/prefs.jsm b/common/modules/prefs.jsm index f8e7e40..3f865b6 100644 --- a/common/modules/prefs.jsm +++ b/common/modules/prefs.jsm @@ -26,7 +26,8 @@ var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]) this._prefContexts = []; this.branch = services.pref[defaults ? "getDefaultBranch" : "getBranch"](branch || ""); - this.branch instanceof Ci.nsIPrefBranch2; + if ("nsIPrefBranch2" in Ci) + this.branch instanceof Ci.nsIPrefBranch2; this.defaults = defaults ? this : this.constructor(branch, true); @@ -68,103 +69,42 @@ var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]) } }, - /** - * Returns the full name of this object's preference branch. - */ - get root() this.branch.root, - /** * Returns a new Prefs instance for the sub-branch *branch* of this * branch. * - * @param {string} branch The branch to branch to. + * @param {string} branch The sub-branch to branch to. * @returns {Prefs} */ Branch: function Branch(branch) Prefs(this.root + branch), - observe: null, - observers: { - "nsPref:changed": function (subject, data) { - let observers = this._observers[data]; - if (observers) { - let value = this.get(data, false); - this._observers[data] = observers.filter(function (callback) { - if (!callback.get()) - return false; - util.trapErrors(callback.get(), null, value); - return true; - }); - } - } - }, - /** - * Adds a new preference observer for the given preference. + * Clears the entire branch. * - * @param {string} pref The preference to observe. - * @param {function(object)} callback The callback, called with the - * new value of the preference whenever it changes. + * @param {string} name The name of the preference branch to delete. */ - watch: function watch(pref, callback, strong) { - if (!this.observe) { - util.addObserver(this); - this.branch.addObserver("", this, false); - } - - if (!this._observers[pref]) - this._observers[pref] = []; - this._observers[pref].push(!strong ? util.weakReference(callback) : { get: function () callback }); + clear: function clear(branch) { + this.branch.deleteBranch(branch || ""); }, /** - * Lists all preferences matching *filter* or only those with changed - * values if *onlyNonDefault* is specified. - * - * @param {boolean} onlyNonDefault Limit the list to prefs with a - * non-default value. - * @param {string} filter The list filter. A null filter lists all - * prefs. - * @optional + * Returns the full name of this object's preference branch. */ - list: function list(onlyNonDefault, filter) { - if (!filter) - filter = ""; - - let prefArray = this.getNames(); - prefArray.sort(); - function prefs() { - for (let [, pref] in Iterator(prefArray)) { - let userValue = services.pref.prefHasUserValue(pref); - if (onlyNonDefault && !userValue || pref.indexOf(filter) == -1) - continue; - - let value = this.get(pref); - - let option = { - isDefault: !userValue, - default: this.defaults.get(pref, null), - value: <>={template.highlight(value, true, 100)}, - name: pref, - pre: "\u00a0\u00a0" // Unicode nonbreaking space. - }; - - yield option; - } - }; - - return template.options(_("pref.hostPreferences", config.host), prefs.call(this)); - }, + get root() this.branch.root, /** - * Returns the value of a preference. + * Returns the value of the preference *name*, or *defaultValue* if + * the preference does not exist. * - * @param {string} name The preference name. - * @param {value} defaultValue The value to return if the preference - * is unset. + * @param {string} name The name of the preference to return. + * @param {*} defaultValue The value to return if the preference has no value. + * @optional */ get: function get(name, defaultValue) { if (defaultValue == null) defaultValue = null; + if (isArray(name)) + name = name.join("."); let type = this.branch.getPrefType(name); try { @@ -192,20 +132,27 @@ var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]) getDefault: deprecated("Prefs#defaults.get", function getDefault(name, defaultValue) this.defaults.get(name, defaultValue)), /** - * Returns the names of all preferences. + * Returns an array of all preference names in this branch or the + * given sub-branch. * - * @param {string} branch The branch in which to search preferences. - * @default "" + * @param {string} branch The sub-branch for which to return preferences. + * @optional */ getNames: function getNames(branch) this.branch.getChildList(branch || "", { value: 0 }), /** - * Returns true if the current branch has the given preference. + * Returns true if the given preference exists in this branch. * - * @param {string} name The preference name. - * @returns {boolean} + * @param {string} name The name of the preference to check. + */ + has: function has(name) this.branch.getPrefType(name) !== 0, + + /** + * Returns true if the given preference is set to its default value. + * + * @param {string} name The name of the preference to check. */ - has: function get(name) this.branch.getPrefType(name) != 0, + isDefault: function isDefault(name) !this.branch.prefHasUserValue(name), _checkSafe: function _checkSafe(name, message, value) { let curval = this.get(name, null); @@ -256,10 +203,11 @@ var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]) }, /** - * Sets the preference *name* to *value*. + * Sets the preference *name* to *value*. If the preference already + * exists, it must have the same type as the given value. * - * @param {string} name The preference name. - * @param {value} value The new preference value. + * @param {name} name The name of the preference to change. + * @param {string|number|boolean} value The value to set. * @param {boolean} silent Ignore errors. */ set: function set(name, value, silent) { @@ -329,13 +277,11 @@ var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]) /** * Resets the preference *name* to its default value. * - * @param {string} name The preference name. + * @param {string} name The name of the preference to reset. */ reset: function reset(name) { - try { + if (this.branch.prefHasUserValue(name)) this.branch.clearUserPref(name); - } - catch (e) {} // ignore - thrown if not a user set value }, /** @@ -395,7 +341,80 @@ var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]) finally { this.popContext(); } - } + }, + + observe: null, + observers: { + "nsPref:changed": function (subject, data) { + let observers = this._observers[data]; + if (observers) { + let value = this.get(data, false); + this._observers[data] = observers.filter(function (callback) { + if (!callback.get()) + return false; + util.trapErrors(callback.get(), null, value); + return true; + }); + } + } + }, + + /** + * Adds a new preference observer for the given preference. + * + * @param {string} pref The preference to observe. + * @param {function(object)} callback The callback, called with the + * new value of the preference whenever it changes. + */ + watch: function watch(pref, callback, strong) { + if (!this.observe) { + util.addObserver(this); + this.branch.addObserver("", this, false); + } + + if (!this._observers[pref]) + this._observers[pref] = []; + this._observers[pref].push(!strong ? util.weakReference(callback) : { get: function () callback }); + }, + + /** + * Lists all preferences matching *filter* or only those with changed + * values if *onlyNonDefault* is specified. + * + * @param {boolean} onlyNonDefault Limit the list to prefs with a + * non-default value. + * @param {string} filter The list filter. A null filter lists all + * prefs. + * @optional + */ + list: function list(onlyNonDefault, filter) { + if (!filter) + filter = ""; + + let prefArray = this.getNames(); + prefArray.sort(); + function prefs() { + for (let [, pref] in Iterator(prefArray)) { + let userValue = services.pref.prefHasUserValue(pref); + if (onlyNonDefault && !userValue || pref.indexOf(filter) == -1) + continue; + + let value = this.get(pref); + + let option = { + isDefault: !userValue, + default: this.defaults.get(pref, null), + value: <>={template.highlight(value, true, 100)}, + name: pref, + pre: "\u00a0\u00a0" // Unicode nonbreaking space. + }; + + yield option; + } + }; + + return template.options(_("pref.hostPreferences", config.host), prefs.call(this)); + }, }, { }, { completion: function init_completion(dactyl, modules) { diff --git a/common/modules/protocol.jsm b/common/modules/protocol.jsm index 98d8e3b..edb642e 100644 --- a/common/modules/protocol.jsm +++ b/common/modules/protocol.jsm @@ -142,7 +142,7 @@ ProtocolBase.prototype = { 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); + var channel = Channel(["resource:/", pkg + sep + locale, path].join("/"), orig, true, true); if (channel) return channel; } @@ -182,31 +182,34 @@ function XMLChannel(uri, contentType, noErrorChannel, unprivileged) { this.channel.contentType = contentType || channel.contentType; this.channel.contentCharset = "UTF-8"; if (!unprivileged) - this.channel.owner = systemPrincipal; - - let stream = services.InputStream(channelStream); - let [, pre, doctype, url, extra, open, post] = util.regexp(\s]|\s[^[])*)) - (\s+ \[)? - ([^]*) - )? - $ - ]]>, "x").exec(stream.read(4096)); - this.writes.push(pre); - if (doctype) { - this.writes.push(doctype + (extra || "") + " [\n"); - if (url) - this.addChannel(url); - - if (!open) - this.writes.push("\n]"); - - for (let [, pre, url] in util.regexp.iterate(/([^]*?)(?:%include\s+"([^"]*)";|$)/gy, post)) { - this.writes.push(pre); + this.channel.owner = systemPrincipal; + + let type = this.channel.contentType; + if (/^text\/|[\/+]xml$/.test(type)) { + let stream = services.InputStream(channelStream); + let [, pre, doctype, url, extra, open, post] = util.regexp(\s]|\s[^[])*)) + (\s+ \[)? + ([^]*) + )? + $ + ]]>, "x").exec(stream.read(4096)); + this.writes.push(pre); + if (doctype) { + this.writes.push(doctype + (extra || "") + " [\n"); if (url) this.addChannel(url); + + if (!open) + this.writes.push("\n]"); + + for (let [, pre, url] in util.regexp.iterate(/([^]*?)(?:%include\s+"([^"]*)";|$)/gy, post)) { + this.writes.push(pre); + if (url) + this.addChannel(url); + } } } this.writes.push(channelStream); diff --git a/common/modules/sanitizer.jsm b/common/modules/sanitizer.jsm index efc0f2d..bbe36e2 100644 --- a/common/modules/sanitizer.jsm +++ b/common/modules/sanitizer.jsm @@ -19,10 +19,11 @@ defineModule("sanitizer", { }, this); this.lazyRequire("messages", ["_"]); +this.lazyRequire("overlay", ["overlay"]); this.lazyRequire("storage", ["storage"]); this.lazyRequire("template", ["teplate"]); -let tmp = {}; +let tmp = Object.create(this); JSMLoader.loadSubScript("chrome://browser/content/sanitize.js", tmp); tmp.Sanitizer.prototype.__proto__ = Class.prototype; @@ -180,60 +181,63 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef } }); - let (branch = Item.PREFIX + Item.SHUTDOWN_BRANCH) { - util.overlayWindow("chrome://browser/content/preferences/sanitize.xul", - function (win) prefOverlay(branch, true, { - append: { - SanitizeDialogPane: - - - - - { - let (items = ourItems(true)) - template.map(util.range(0, Math.ceil(items.length / 2)), function (i) - { - template.map(items.slice(i * 2, i * 2 + 2), function (item) - ) - }) - } - - - } - })); - } - let (branch = Item.PREFIX + Item.BRANCH) { - util.overlayWindow("chrome://browser/content/sanitize.xul", - function (win) prefOverlay(branch, false, { - append: { - itemList: <> - - { - template.map(ourItems(), function ([item, desc]) - ) - } - - }, - ready: function ready(win) { - let elem = win.document.getElementById("itemList"); - elem.setAttribute("rows", elem.itemCount); - win.Sanitizer = Class("Sanitizer", win.Sanitizer, { - sanitize: function sanitize() { - self.withSavedValues(["sanitizing"], function () { - self.sanitizing = true; - sanitize.superapply(this, arguments); - sanitizer.sanitizeItems([item.name for (item in values(self.itemMap)) - if (item.shouldSanitize(false))], - Range.fromArray(this.range || [])); - }, this); - } - }); - } - })); - } + util.timeout(function () { // Load order issue... + + let (branch = Item.PREFIX + Item.SHUTDOWN_BRANCH) { + overlay.overlayWindow("chrome://browser/content/preferences/sanitize.xul", + function (win) prefOverlay(branch, true, { + append: { + SanitizeDialogPane: + + + + + { + let (items = ourItems(true)) + template.map(util.range(0, Math.ceil(items.length / 2)), function (i) + { + template.map(items.slice(i * 2, i * 2 + 2), function (item) + ) + }) + } + + + } + })); + } + let (branch = Item.PREFIX + Item.BRANCH) { + overlay.overlayWindow("chrome://browser/content/sanitize.xul", + function (win) prefOverlay(branch, false, { + append: { + itemList: <> + + { + template.map(ourItems(), function ([item, desc]) + ) + } + + }, + ready: function ready(win) { + let elem = win.document.getElementById("itemList"); + elem.setAttribute("rows", elem.itemCount); + win.Sanitizer = Class("Sanitizer", win.Sanitizer, { + sanitize: function sanitize() { + self.withSavedValues(["sanitizing"], function () { + self.sanitizing = true; + sanitize.superapply(this, arguments); + sanitizer.sanitizeItems([item.name for (item in values(self.itemMap)) + if (item.shouldSanitize(false))], + Range.fromArray(this.range || [])); + }, this); + } + }); + } + })); + } + }); }, firstRun: 0, diff --git a/common/modules/services.jsm b/common/modules/services.jsm index d47da24..76c4b6b 100644 --- a/common/modules/services.jsm +++ b/common/modules/services.jsm @@ -92,6 +92,7 @@ var Services = Module("Services", { 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("MIMEStream", "@mozilla.org/network/mime-input-stream;1", "nsIMIMEInputStream", "setData"); this.addClass("Persist", "@mozilla.org/embedding/browser/nsWebBrowserPersist;1", "nsIWebBrowserPersist"); this.addClass("Pipe", "@mozilla.org/pipe;1", "nsIPipe", "init"); this.addClass("Process", "@mozilla.org/process/util;1", "nsIProcess", "init"); @@ -105,7 +106,7 @@ var Services = Module("Services", { this.addClass("Transferable", "@mozilla.org/widget/transferable;1", "nsITransferable"); this.addClass("Timer", "@mozilla.org/timer;1", "nsITimer", "initWithCallback"); this.addClass("URL", "@mozilla.org/network/standard-url;1", ["nsIStandardURL", "nsIURL"], "init"); - this.addClass("Xmlhttp", "@mozilla.org/xmlextras/xmlhttprequest;1", "nsIXMLHttpRequest", "open"); + this.addClass("Xmlhttp", "@mozilla.org/xmlextras/xmlhttprequest;1", [], "open"); this.addClass("XPathEvaluator", "@mozilla.org/dom/xpath-evaluator;1", "nsIDOMXPathEvaluator"); this.addClass("XMLDocument", "@mozilla.org/xml/xml-document;1", ["nsIDOMXMLDocument", "nsIDOMNodeSelector"]); this.addClass("ZipReader", "@mozilla.org/libjar/zip-reader;1", "nsIZipReader", "open", false); diff --git a/common/modules/styles.jsm b/common/modules/styles.jsm index 9ee7f62..98d4960 100644 --- a/common/modules/styles.jsm +++ b/common/modules/styles.jsm @@ -420,6 +420,8 @@ var Styles = Module("Styles", { * @returns {nsIURI -> boolean} */ matchFilter: function (filter) { + filter = filter.trim(); + if (filter === "*") var test = function test(uri) true; else if (!/^(?:[a-z-]+:|[a-z-.]+$)/.test(filter)) { diff --git a/common/modules/template.jsm b/common/modules/template.jsm index 61c64cd..09300d0 100644 --- a/common/modules/template.jsm +++ b/common/modules/template.jsm @@ -82,13 +82,22 @@ var Binding = Class("Binding", { }) }); +["appendChild", "getAttribute", "insertBefore", "setAttribute"].forEach(function (key) { + Object.defineProperty(Binding.prototype, key, { + configurable: true, + enumerable: false, + value: function () this.node[key].apply(this.node, arguments), + writable: true + }); +}); + var Template = Module("Template", { add: function add(a, b) a + b, join: function join(c) function (a, b) a + c + b, map: function map(iter, func, sep, interruptable) { - XML.ignoreWhitespace = false; XML.prettyPrinting = false; - if (iter.length) // FIXME: Kludge? + XML.ignoreWhitespace = XML.prettyPrinting = false; + if (typeof iter.length == "number") // FIXME: Kludge? iter = array.iterValues(iter); let res = <>; let n = 0; @@ -192,7 +201,7 @@ var Template = Module("Template", { var desc = this.processor[1].call(this, item, item.description); } - XML.ignoreWhitespace = false; XML.prettyPrinting = false; + XML.ignoreWhitespace = XML.prettyPrinting = false; // return