diff --git a/common/components/protocols.js b/common/components/protocols.js index 29cff951..fd6378d1 100644 --- a/common/components/protocols.js +++ b/common/components/protocols.js @@ -27,7 +27,7 @@ let channel = Components.classesByID["{61ba33c0-3031-11d3-8cd0-0060b0fc14a3}"] .QueryInterface(Ci.nsIRequest); const systemPrincipal = channel.owner; channel.cancel(NS_BINDING_ABORTED); -delete channel; +channel = null; function dataURL(type, data) "data:" + (type || "application/xml;encoding=UTF-8") + "," + escape(data); function makeChannel(url, orig) { diff --git a/common/content/buffer.js b/common/content/buffer.js index 7bceb5f1..a656ff6f 100644 --- a/common/content/buffer.js +++ b/common/content/buffer.js @@ -1148,8 +1148,8 @@ const Buffer = Module("buffer", { }, { argCount: "?", - literal: 0, - bang: true + bang: true, + literal: 0 }); commands.add(["pa[geinfo]"], @@ -1188,8 +1188,8 @@ const Buffer = Module("buffer", { "Reload the current web page", function (args) { tabs.reload(config.browser.mCurrentTab, args.bang); }, { - bang: true, - argCount: "0" + argCount: "0", + bang: true }); // TODO: we're prompted if download.useDownloadDir isn't set and no arg specified - intentional? @@ -1483,7 +1483,7 @@ const Buffer = Module("buffer", { let xpath = ["input", "textarea[not(@disabled) and not(@readonly)]"]; let elements = [m for (m in util.evaluateXPath(xpath))].filter(function (elem) { - if (elem.readOnly || elem instanceof HTMLInputElement && ["text", "password", "file"].indexOf(elem.type) < 0) + if (elem.readOnly || elem instanceof HTMLInputElement && ["file", "search", "text", "password"].indexOf(elem.type) < 0) return false; let computedStyle = util.computedStyle(elem); return computedStyle.visibility != "hidden" && computedStyle.display != "none"; diff --git a/common/content/commandline.js b/common/content/commandline.js index f0e057c9..e4c29de2 100644 --- a/common/content/commandline.js +++ b/common/content/commandline.js @@ -762,12 +762,12 @@ const CommandLine = Module("commandline", { case "": case "": case "": - openLink(liberator.NEW_BACKGROUND_TAB); + openLink({ where: liberator.NEW_TAB, background: true }); break; case "": case "": case "": - openLink(liberator.NEW_TAB); + openLink({ where: liberator.NEW_TAB, background: false }); break; case "": openLink(liberator.NEW_WINDOW); diff --git a/common/content/completion.js b/common/content/completion.js index bccd4052..e838734f 100644 --- a/common/content/completion.js +++ b/common/content/completion.js @@ -251,7 +251,7 @@ const CompletionContext = Class("CompletionContext", { get completions() this._completions || [], set completions(items) { // Accept a generator - if ({}.toString.call(items) != '[object Array]') + if (!isarray(items)) items = [x for (x in Iterator(items))]; delete this.cache.filtered; delete this.cache.filter; diff --git a/common/content/events.js b/common/content/events.js index dbfe3c90..a22785eb 100644 --- a/common/content/events.js +++ b/common/content/events.js @@ -671,7 +671,7 @@ const Events = Module("events", { if (elem && elem.readOnly) return; - if ((elem instanceof HTMLInputElement && /^(text|password)$/.test(elem.type)) || + if ((elem instanceof HTMLInputElement && /^(search|text|password)$/.test(elem.type)) || (elem instanceof HTMLSelectElement)) { liberator.mode = modes.INSERT; if (hasHTMLDocument(win)) @@ -1022,7 +1022,9 @@ const Events = Module("events", { if (liberator.mode == modes.COMMAND_LINE) { if (!(modes.extended & modes.INPUT_MULTILINE)) - commandline.onEvent(event); // reroute event in command line mode + liberator.trapErrors(function () { + commandline.onEvent(event); // reroute event in command line mode + }); } else if (!modes.mainMode.input) liberator.beep(); diff --git a/common/content/hints.js b/common/content/hints.js index 977765a9..ed4b4e9c 100644 --- a/common/content/hints.js +++ b/common/content/hints.js @@ -42,7 +42,7 @@ const Hints = Module("hints", { "?": Mode("Show information for hint", function (elem) buffer.showElementInfo(elem), extended), s: Mode("Save hint", function (elem) buffer.saveLink(elem, true)), a: Mode("Save hint with prompt", function (elem) buffer.saveLink(elem, false)), - f: Mode("Focus frame", function (elem) elem.ownerDocument.defaultView.focus(), function () util.makeXPath(["body"])), + f: Mode("Focus frame", function (elem) elem.ownerDocument.defaultView.focus(), function () ["body"]), o: Mode("Follow hint", function (elem) buffer.followLink(elem, liberator.CURRENT_TAB)), t: Mode("Follow hint in a new tab", function (elem) buffer.followLink(elem, liberator.NEW_TAB)), b: Mode("Follow hint in a background tab", function (elem) buffer.followLink(elem, liberator.NEW_BACKGROUND_TAB)), @@ -353,7 +353,7 @@ const Hints = Module("hints", { if (hint.text == "" && hint.elem.firstChild && hint.elem.firstChild instanceof HTMLImageElement) { if (!hint.imgSpan) { - rect = hint.elem.firstChild.getBoundingClientRect(); + var rect = hint.elem.firstChild.getBoundingClientRect(); if (!rect) continue; @@ -381,7 +381,7 @@ const Hints = Module("hints", { let css = []; // FIXME: Broken for imgspans. for (let [, { doc: doc }] in Iterator(this._docs)) { - for (let elem in util.evaluateXPath(" {//*[@liberator:highlight and @number]", doc)) { + for (let elem in util.evaluateXPath("//*[@liberator:highlight and @number]", doc)) { let group = elem.getAttributeNS(NS.uri, "highlight"); css.push(highlight.selector(group) + "[number=" + elem.getAttribute("number").quote() + "] { " + elem.style.cssText + " }"); } @@ -1041,8 +1041,8 @@ const Hints = Module("hints", { }, options: function () { const DEFAULT_HINTTAGS = - util.makeXPath(["input[not(@type='hidden')]", "a", "area", "iframe", "textarea", "button", "select"]) - + " | //*[@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or @role='link']"; + util.makeXPath(["input[not(@type='hidden')]", "a", "area", "iframe", "textarea", "button", "select", + "*[@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or @role='link']"]); function checkXPath(val) { try { diff --git a/common/content/javascript.js b/common/content/javascript.js index 4880e6d0..a5f0f6ce 100644 --- a/common/content/javascript.js +++ b/common/content/javascript.js @@ -34,6 +34,7 @@ const JavaScript = Module("javascript", { }, iter: function iter(obj, toplevel) { + "use strict"; toplevel = !!toplevel; let seen = {}; let ret = {}; @@ -45,9 +46,6 @@ const JavaScript = Module("javascript", { let orig = obj; let top = services.get("debugger").wrapValue(obj); - if (!toplevel) - obj = obj.__proto__; - for (; obj; obj = !toplevel && obj.__proto__) { services.get("debugger").wrapValue(obj).getProperties(ret, {}); for (let prop in values(ret.value)) { @@ -55,20 +53,21 @@ const JavaScript = Module("javascript", { if (name in seen) continue; seen[name] = 1; - yield [prop.name.stringValue, top.getProperty(prop.name.stringValue).value.getWrappedValue()] + if (toplevel || obj !== orig) + yield [prop.name.stringValue, top.getProperty(prop.name.stringValue).value.getWrappedValue()] } } // The debugger doesn't list some properties. I can't guess why. + // This only lists ENUMERABLE properties. for (let k in orig) if (k in orig && !('|' + k in seen) - && callable(orig.hasOwnProperty) - && orig.hasOwnProperty(k) == toplevel) + && Object.hasOwnProperty(orig, k) == toplevel) yield [k, this.getKey(orig, k)] } else { - for (k in allkeys(obj)) + for (let k in allkeys(obj)) try { - if (obj.hasOwnProperty(k) == toplevel) + if (Object.hasOwnProperty(obj, k) == toplevel) yield [k, this.getKey(obj, k)]; } catch (e) {} @@ -508,15 +507,15 @@ const JavaScript = Module("javascript", { // Split up the arguments let prev = this._get(-2).offset; let args = []; - for (let [, idx] in Iterator(this._get(-2).comma)) { + for (let [i, idx] in Iterator(this._get(-2).comma)) { let arg = this._str.substring(prev + 1, idx); prev = idx; - util.memoize(args, this._i, function () self.eval(arg)); + util.memoize(args, i, function () self.eval(arg)); } let key = this._getKey(); args.push(key + string); - compl = function (context, obj) { + let compl = function (context, obj) { let res = completer.call(self, context, func, obj, args); if (res) context.completions = res; @@ -600,7 +599,7 @@ const JavaScript = Module("javascript", { let completer = completers[args.length - 1]; if (!completer) return []; - return completer.call(this, context, obj, args); + return completer.call(obj, context, obj, args); }; } } diff --git a/common/content/liberator.js b/common/content/liberator.js index 02878811..8214b461 100644 --- a/common/content/liberator.js +++ b/common/content/liberator.js @@ -1320,17 +1320,8 @@ const Liberator = Module("liberator", { let arg = args[0]; try { - // TODO: why are these sorts of properties arrays? --djk - let dialogs = config.dialogs; - - for (let [, dialog] in Iterator(dialogs)) { - if (util.compareIgnoreCase(arg, dialog[0]) == 0) { - dialog[2](); - return; - } - } - - liberator.echoerr("E475: Invalid argument: " + arg); + liberator.assert(args[0] in config.dialogs, "E475: Invalid argument: " + arg); + config.dialogs[args[0]][1](); } catch (e) { liberator.echoerr("Error opening " + arg.quote() + ": " + e); @@ -1540,12 +1531,13 @@ const Liberator = Module("liberator", { }); // TODO: maybe indicate pending status too? - commands.add(["extens[ions]"], + commands.add(["extens[ions]", "exts"], "List available extensions", function (args) { AddonManager.getAddonsByTypes(["extension"], function (extensions) { if (args[0]) extensions = extensions.filter(function (extension) extension.name.indexOf(args[0]) >= 0); + extensions.sort(function (a, b) String.localeCompare(a.name, b.name)); if (extensions.length > 0) { let list = template.tabular( @@ -1832,7 +1824,7 @@ const Liberator = Module("liberator", { completion: function () { completion.dialog = function dialog(context) { context.title = ["Dialog"]; - context.completions = config.dialogs; + context.completions = [[k, v[0]] for ([k, v] in Iterator(config.dialogs))]; }; completion.extension = function extension(context) { diff --git a/common/content/style.js b/common/content/style.js index 155416ab..3fc6824a 100644 --- a/common/content/style.js +++ b/common/content/style.js @@ -702,10 +702,10 @@ Module("styles", { JavaScript.setCompleter(["get", "addSheet", "removeSheet", "findSheets"].map(function (m) styles[m]), [ // Prototype: (system, name, filter, css, index) null, - function (context, obj, args) args[0] ? styles.systemNames : styles.userNames, - function (context, obj, args) styles.completeSite(context, content), + function (context, obj, args) args[0] ? this.systemNames : this.userNames, + function (context, obj, args) this.completeSite(context, content), null, - function (context, obj, args) args[0] ? styles.systemSheets : styles.userSheets + function (context, obj, args) args[0] ? this.systemSheets : this.userSheets ]); } }); diff --git a/common/content/util.js b/common/content/util.js index 41c512cd..97e3e888 100644 --- a/common/content/util.js +++ b/common/content/util.js @@ -102,6 +102,55 @@ const Util = Module("util", { return fixup.createFixupURI(str, fixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP); }, + /** + * Expands brace globbing patterns in a string. + * + * Example: + * "a{b,c}d" => ["abd", "acd"] + * + * @param {string} pattern The pattern to deglob. + * @returns [string] The resulting strings. + */ + debrace: function deglobBrace(pattern) { + function split(pattern, re, fn, dequote) { + let end = 0, match, res = []; + while (match = re.exec(pattern)) { + end = match.index + match[0].length; + res.push(match[1]); + if (fn) + fn(match); + } + res.push(pattern.substr(end)); + return res.map(function (s) util.dequote(s, dequote)); + } + let patterns = [], res = []; + let substrings = split(pattern, /((?:[^\\{]|\\.)*)\{((?:[^\\}]|\\.)*)\}/gy, + function (match) { + patterns.push(split(match[2], /((?:[^\\,]|\\.)*),/gy, + null, ",{}")); + }, "{}"); + function rec(acc) { + if (acc.length == patterns.length) + res.push(util.Array.zip(substrings, acc).join("")); + else + for (let [, pattern] in Iterator(patterns[acc.length])) + rec(acc.concat(pattern)); + } + rec([]); + return res; + }, + + /** + * Removes certain backslash-quoted characters while leaving other + * backslash-quoting sequences untouched. + * + * @param {string} pattern The string to unquote. + * @param {string} chars The characters to unquote. + * @returns {string} + */ + dequote: function dequote(pattern, chars) + pattern.replace(/\\(.)/, function (m0, m1) chars.indexOf(m1) >= 0 ? m1 : m0), + /** * Converts HTML special characters in str to the equivalent HTML * entities. @@ -169,7 +218,8 @@ const Util = Module("util", { * @returns {string} */ makeXPath: function makeXPath(nodes) { - return util.Array(nodes).map(function (node) [node, "xhtml:" + node]).flatten() + return util.Array(nodes).map(util.debrace).flatten() + .map(function (node) [node, "xhtml:" + node]).flatten() .map(function (node) "//" + node).join(" | "); }, @@ -832,6 +882,22 @@ const Util = Module("util", { } } return ret; + }, + + /** + * Zips the contents of two arrays. The resulting array is twice the + * length of ary1, with any shortcomings of ary2 replaced with null + * strings. + * + * @param {Array} ary1 + * @param {Array} ary2 + * @returns {Array} + */ + zip: function zip(ary1, ary2) { + let res = [] + for(let [i, item] in Iterator(ary1)) + res.push(item, i in ary2 ? ary2[i] : ""); + return res; } }) }); diff --git a/common/modules/storage.jsm b/common/modules/storage.jsm index 264ce186..d749ecbf 100644 --- a/common/modules/storage.jsm +++ b/common/modules/storage.jsm @@ -103,12 +103,12 @@ function readFile(file) { function writeFile(file, data) { if (!file.exists()) - file.create(file.NORMAL_FILE_TYPE, 0600); + file.create(file.NORMAL_FILE_TYPE, parseInt('0600', 8)); let fileStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream); let stream = Cc["@mozilla.org/intl/converter-output-stream;1"].createInstance(Ci.nsIConverterOutputStream); - fileStream.init(file, 0x20 | 0x08 | 0x02, 0600, 0); // PR_TRUNCATE | PR_CREATE | PR_WRITE + fileStream.init(file, 0x20 | 0x08 | 0x02, parseInt('0600', 8), 0); // PR_TRUNCATE | PR_CREATE | PR_WRITE stream.init(fileStream, "UTF-8", 0, 0); stream.writeString(data); diff --git a/vimperator/chrome.manifest b/vimperator/chrome.manifest index 4890933b..276496d7 100644 --- a/vimperator/chrome.manifest +++ b/vimperator/chrome.manifest @@ -1,26 +1,25 @@ # Firefox content vimperator content/ skin vimperator classic/1.0 skin/ -locale vimperator en-US locale/en-US/ -locale liberator en-US ../common/locale/en-US/ +locale vimperator en-US locale/en-US/ +locale liberator en-US ../common/locale/en-US/ content liberator ../common/content/ resource liberator ../common/modules/ skin liberator classic/1.0 ../common/skin/ -override chrome://liberator/content/liberator.dtd chrome://vimperator/content/liberator.dtd -override chrome://liberator/content/config.js chrome://vimperator/content/config.js +override chrome://liberator/content/liberator.dtd chrome://vimperator/content/liberator.dtd +override chrome://liberator/content/config.js chrome://vimperator/content/config.js overlay chrome://browser/content/browser.xul chrome://liberator/content/liberator.xul overlay chrome://browser/content/browser.xul chrome://vimperator/content/vimperator.xul -component components/ - component {81495d80-89ee-4c36-a88d-ea7c4e5ac63f} components/about-handler.js contract @mozilla.org/network/protocol/about;1?what=vimperator {81495d80-89ee-4c36-a88d-ea7c4e5ac63f} component {16dc34f7-6d22-4aa4-a67f-2921fb5dcb69} components/commandline-handler.js contract @mozilla.org/commandlinehandler/general-startup;1?type=vimperator {16dc34f7-6d22-4aa4-a67f-2921fb5dcb69} +category command-line-handler m-vimperator @mozilla.org/commandlinehandler/general-startup;1?type=vimperator component {c1b67a07-18f7-4e13-b361-2edcc35a5a0d} components/protocols.js contract @mozilla.org/network/protocol;1?name=chrome-data {c1b67a07-18f7-4e13-b361-2edcc35a5a0d} diff --git a/vimperator/content/config.js b/vimperator/content/config.js index 6988d5a6..29446ee4 100644 --- a/vimperator/content/config.js +++ b/vimperator/content/config.js @@ -46,58 +46,58 @@ const Config = Module("config", ConfigBase, { ["VimperatorLeavePre", "Triggered before exiting Firefox, just before destroying each module"], ["VimperatorLeave", "Triggered before exiting Firefox"]], - dialogs: [ - ["about", "About Firefox", + dialogs: { + about: ["About Firefox", function () { window.openDialog("chrome://browser/content/aboutDialog.xul", "_blank", "chrome,dialog,modal,centerscreen"); }], - ["addbookmark", "Add bookmark for the current page", + addbookmark: ["Add bookmark for the current page", function () { PlacesCommandHook.bookmarkCurrentPage(true, PlacesUtils.bookmarksRootId); }], - ["addons", "Manage Add-ons", + addons: ["Manage Add-ons", function () { window.BrowserOpenAddonsMgr(); }], - ["bookmarks", "List your bookmarks", + bookmarks: ["List your bookmarks", function () { window.openDialog("chrome://browser/content/bookmarks/bookmarksPanel.xul", "Bookmarks", "dialog,centerscreen,width=600,height=600"); }], - ["checkupdates", "Check for updates", + checkupdates: ["Check for updates", function () { window.checkForUpdates(); }], - ["cleardata", "Clear private data", + cleardata: ["Clear private data", function () { Cc[GLUE_CID].getService(Ci.nsIBrowserGlue).sanitize(window || null); }], - ["cookies", "List your cookies", + cookies: ["List your cookies", function () { window.toOpenWindowByType("Browser:Cookies", "chrome://browser/content/preferences/cookies.xul", "chrome,dialog=no,resizable"); }], - ["console", "JavaScript console", + console: ["JavaScript console", function () { window.toJavaScriptConsole(); }], - ["customizetoolbar", "Customize the Toolbar", + customizetoolbar: ["Customize the Toolbar", function () { window.BrowserCustomizeToolbar(); }], - ["dominspector", "DOM Inspector", + dominspector: ["DOM Inspector", function () { try { window.inspectDOMDocument(content.document); } catch (e) { liberator.echoerr("DOM Inspector extension not installed"); } }], - ["downloads", "Manage Downloads", + downloads: ["Manage Downloads", function () { window.toOpenWindowByType("Download:Manager", "chrome://mozapps/content/downloads/downloads.xul", "chrome,dialog=no,resizable"); }], - ["history", "List your history", + history: ["List your history", function () { window.openDialog("chrome://browser/content/history/history-panel.xul", "History", "dialog,centerscreen,width=600,height=600"); }], - ["import", "Import Preferences, Bookmarks, History, etc. from other browsers", + import: ["Import Preferences, Bookmarks, History, etc. from other browsers", function () { window.BrowserImport(); }], - ["openfile", "Open the file selector dialog", + openfile: ["Open the file selector dialog", function () { window.BrowserOpenFileWindow(); }], - ["pageinfo", "Show information about the current page", + pageinfo: ["Show information about the current page", function () { window.BrowserPageInfo(); }], - ["pagesource", "View page source", + pagesource: ["View page source", function () { window.BrowserViewSourceOfDocument(content.document); }], - ["places", "Places Organizer: Manage your bookmarks and history", + places: ["Places Organizer: Manage your bookmarks and history", function () { PlacesCommandHook.showPlacesOrganizer(ORGANIZER_ROOT_BOOKMARKS); }], - ["preferences", "Show Firefox preferences dialog", + preferences: ["Show Firefox preferences dialog", function () { window.openPreferences(); }], - ["printpreview", "Preview the page before printing", + printpreview: ["Preview the page before printing", function () { PrintUtils.printPreview(onEnterPrintPreview, onExitPrintPreview); }], - ["printsetup", "Setup the page size and orientation before printing", + printsetup: ["Setup the page size and orientation before printing", function () { PrintUtils.showPageSetup(); }], - ["print", "Show print dialog", + print: ["Show print dialog", function () { PrintUtils.print(); }], - ["saveframe", "Save frame to disk", + saveframe: ["Save frame to disk", function () { window.saveFrameDocument(); }], - ["savepage", "Save page to disk", + savepage: ["Save page to disk", function () { window.saveDocument(window.content.document); }], - ["searchengines", "Manage installed search engines", + searchengines: ["Manage installed search engines", function () { window.openDialog("chrome://browser/content/search/engineManager.xul", "_blank", "chrome,dialog,modal,centerscreen"); }], - ["selectionsource", "View selection source", + selectionsource: ["View selection source", function () { buffer.viewSelectionSource(); }] - ], + }, hasTabbrowser: true, @@ -232,13 +232,20 @@ const Config = Module("config", ConfigBase, { return; context.anchored = false; - context.title = ["Smart Completions"]; - context.keys.icon = 2; - context.incomplete = true; - context.hasItems = context.completions.length > 0; // XXX - context.filterFunc = null; - context.cancel = function () { if (searchRunning) { services.get("autoCompleteSearch").stopSearch(); searchRunning = false; } }; context.compare = CompletionContext.Sort.unsorted; + context.filterFunc = null; + context.hasItems = context.completions.length > 0; // XXX + context.incomplete = true; + context.keys.icon = 2; + context.title = ["Smart Completions"]; + context.cancel = function () { + if (searchRunning) { + services.get("autoCompleteSearch").stopSearch(); + searchRunning = false; + } + }; + if (searchRunning) + services.get("autoCompleteSearch").stopSearch(); let timer = new Timer(50, 100, function (result) { context.incomplete = result.searchResult >= result.RESULT_NOMATCH_ONGOING; context.completions = [ @@ -246,9 +253,6 @@ const Config = Module("config", ConfigBase, { for (i in util.range(0, result.matchCount)) ]; }); - if (searchRunning) - services.get("autoCompleteSearch").stopSearch(); - searchRunning = true; services.get("autoCompleteSearch").startSearch(context.filter, "", context.result, { onSearchResult: function onSearchResult(search, result) { timer.tell(result); @@ -258,6 +262,7 @@ const Config = Module("config", ConfigBase, { } } }); + searchRunning = true; }; completion.sidebar = function sidebar(context) {