From: Don Armstrong Date: Fri, 13 Jun 2014 19:55:01 +0000 (-0700) Subject: Imported Upstream version 1.1+hg7904 X-Git-Tag: upstream/1.1+hg7904^0 X-Git-Url: https://git.donarmstrong.com/dactyl.git?p=dactyl.git;a=commitdiff_plain;h=247daf849abc85f4cfb10fa358c62c8daf8db95b Imported Upstream version 1.1+hg7904 --- diff --git a/.hg_archival.txt b/.hg_archival.txt index 3e94fb3..870f741 100644 --- a/.hg_archival.txt +++ b/.hg_archival.txt @@ -1,5 +1,5 @@ repo: 373f1649c80dea9be7b5bc9c57e8395f94f93ab1 -node: a34a77b2caffe9b07ee32821a66342c9ec6e9e09 +node: 025b18cc985df652eb7ec1cb9d8920ba312de985 branch: default -latesttag: pentadactyl-1.0rc1 -latesttagdistance: 247 +latesttag: pentadactyl-1.1 +latesttagdistance: 22 diff --git a/common/Makefile b/common/Makefile index 0251c4a..d098584 100644 --- a/common/Makefile +++ b/common/Makefile @@ -1,11 +1,17 @@ #### configuration +_SH := $(shell if which dash >/dev/null 2>&1; \ + then echo dash; \ + else echo sh; \ + fi) +SH ?= $(_SH) AWK ?= awk B64ENCODE ?= base64 CURL ?= curl SED := $(shell if [ "xoo" = x$$(echo foo | sed -E 's/f(o)/\1/' 2>/dev/null) ]; \ - then echo sed -E; else echo sed -r; \ + then echo sed -E; \ + else echo sed -r; \ fi) TOP = $(shell pwd) @@ -28,7 +34,7 @@ LOCALEDIR = locale DOC_FILES = $(wildcard $(LOCALEDIR)/*/*.xml) export VERSION BUILD_DATE -MAKE_JAR = sh $(BASE)/make_jar.sh +MAKE_JAR = $(SH) $(BASE)/make_jar.sh # TODO: specify source files manually? JAR_BASES = $(TOP) $(BASE) @@ -90,26 +96,6 @@ info: jar: $(JAR) -# This is not for you! -dist: $(XPI) - @echo DIST $(XPI) $(GOOGLE) - set -e; \ - \ - proj=$$(echo -n $(NAME) | sed 's/\(.\).*/\1/' | tr a-z A-Z); \ - proj="$$proj$$(echo $(NAME) | sed 's/.//')"; \ - [ -z "$$summary" ] && summary="$$proj $(VERSION) Release"; \ - labels="Project-$$proj,$(labels)"; \ - [ -n "$(featured)" ] && labels="$$labels,Featured"; \ - \ - IFS=,; for l in $$labels; do \ - set -- "$$@" --form-string "label=$$l"; \ - done; \ - auth=$$(echo -n "$(GOOGLE_USER):$(GOOGLE_PASS)" | $(B64ENCODE)); \ - $(CURL) "$$@" --form-string "summary=$$summary" \ - -F "filename=@$(XPI)" \ - -H "Authorization: Basic $$auth" \ - -i "$(GOOGLE)" | sed -n '/^Location/{p;q;}' - install: export dir; \ for dir in $(PROFILEPATHS); do \ @@ -177,6 +163,10 @@ xpi: $(CHROME) -f $(BASE)/process_manifest.awk \ "$(TOP)/chrome.manifest" >"$(XPI_PATH)/chrome.manifest" + $(AWK) -v 'name=$(NAME)' -v 'suffix=$(MANGLE)' \ + -f $(BASE)/process_config.awk \ + "$(TOP)/config.json" >"$(XPI_PATH)/config.json" + version="$(VERSION)"; \ hg root >/dev/null 2>&1 && \ case "$$version" in \ diff --git a/common/bootstrap.js b/common/bootstrap.js index 4d5dcca..a5b25de 100755 --- a/common/bootstrap.js +++ b/common/bootstrap.js @@ -1,4 +1,4 @@ -// Copyright (c) 2010-2011 by Kris Maglione +// Copyright (c) 2010-2014 by Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -21,6 +21,8 @@ var { AddonManager } = module("resource://gre/modules/AddonManager.jsm"); var { XPCOMUtils } = module("resource://gre/modules/XPCOMUtils.jsm"); var { Services } = module("resource://gre/modules/Services.jsm"); +var Timer = Components.Constructor("@mozilla.org/timer;1", "nsITimer", "initWithCallback"); + const resourceProto = Services.io.getProtocolHandler("resource") .QueryInterface(Ci.nsIResProtocolHandler); const categoryManager = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager); @@ -57,13 +59,11 @@ let addonData = null; let basePath = null; let bootstrap; let bootstrap_jsm; -let categories = []; let components = {}; -let resources = []; let getURI = null; let JSMLoader = { - SANDBOX: Cu.nukeSandbox && false, + SANDBOX: Cu.nukeSandbox, get addon() addon, @@ -134,7 +134,7 @@ let JSMLoader = { return this.modules[name] = this.globals[uri]; this.globals[uri] = this.modules[name]; - bootstrap_jsm.loadSubScript(url, this.modules[name]); + bootstrap_jsm.loadSubScript(url, this.modules[name], "UTF-8"); return; } catch (e) { @@ -172,7 +172,17 @@ let JSMLoader = { let module = this.modules[name]; if (target) for each (let symbol in module.EXPORTED_SYMBOLS) - target[symbol] = module[symbol]; + try { + Object.defineProperty(target, symbol, { + configurable: true, + enumerable: true, + writable: true, + value: module[symbol] + }); + } + catch (e) { + target[symbol] = module[symbol]; + } return module; }, @@ -180,8 +190,8 @@ let JSMLoader = { // Cuts down on stupid, fscking url mangling. get loadSubScript() bootstrap_jsm.loadSubScript, - cleanup: function unregister() { - for each (let factory in this.factories.splice(0)) + cleanup: function cleanup() { + for (let factory of this.factories.splice(0)) manager.unregisterFactory(factory.classID, factory); }, @@ -215,35 +225,29 @@ let JSMLoader = { function init() { debug("bootstrap: init"); - let manifestURI = getURI("chrome.manifest"); - let manifest = httpGet(manifestURI) - .responseText - .replace(/#(resource)#/g, "$1") - .replace(/^\s*|\s*$|#.*/g, "") - .replace(/^\s*\n/gm, ""); - - for each (let line in manifest.split("\n")) { - let fields = line.split(/\s+/); - switch (fields[0]) { - case "category": - categoryManager.addCategoryEntry(fields[1], fields[2], fields[3], false, true); - categories.push([fields[1], fields[2]]); - break; - case "component": - components[fields[1]] = new FactoryProxy(getURI(fields[2]).spec, fields[1]); - break; - case "contract": - components[fields[2]].contractID = fields[1]; - break; - - case "resource": - moduleName = moduleName || fields[1]; - resources.push(fields[1]); - resourceProto.setSubstitution(fields[1], getURI(fields[2])); - } + let manifest = JSON.parse(httpGet(getURI("config.json")) + .responseText); + + if (!manifest.categories) + manifest.categories = []; + + for (let [classID, { contract, path, categories }] of Iterator(manifest.components || {})) { + components[classID] = new FactoryProxy(getURI(path).spec, classID, contract); + if (categories) + for (let [category, id] in Iterator(categories)) + manifest.categories.push([category, id, contract]); + } + + for (let [category, id, value] of manifest.categories) + categoryManager.addCategoryEntry(category, id, value, + false, true); + + for (let [pkg, path] in Iterator(manifest.resources || {})) { + moduleName = moduleName || pkg; + resourceProto.setSubstitution(pkg, getURI(path)); } - JSMLoader.config = JSON.parse(httpGet("resource://dactyl-local/config.json").responseText); + JSMLoader.config = manifest; bootstrap_jsm = module(BOOTSTRAP); if (!JSMLoader.SANDBOX) @@ -255,22 +259,13 @@ function init() { } bootstrap.require = JSMLoader.load("base").require; - // Flush the cache if necessary, just to be paranoid let pref = "extensions.dactyl.cacheFlushCheck"; let val = addon.version; if (!Services.prefs.prefHasUserValue(pref) || Services.prefs.getCharPref(pref) != val) { var cacheFlush = true; - Services.obs.notifyObservers(null, "startupcache-invalidate", ""); Services.prefs.setCharPref(pref, val); } - try { - //JSMLoader.load("disable-acr").init(addon.id); - } - catch (e) { - reportError(e); - } - Services.obs.notifyObservers(null, "dactyl-rehash", null); JSMLoader.bootstrap = global; @@ -326,7 +321,9 @@ function updateVersion() { // Disable automatic updates when switching to nightlies, // restore the default action when switching to stable. if (!config.lastVersion || isDev(config.lastVersion) != isDev(addon.version)) - addon.applyBackgroundUpdates = AddonManager[isDev(addon.version) ? "AUTOUPDATE_DISABLE" : "AUTOUPDATE_DEFAULT"]; + addon.applyBackgroundUpdates = + AddonManager[isDev(addon.version) ? "AUTOUPDATE_DISABLE" + : "AUTOUPDATE_DEFAULT"]; } catch (e) { reportError(e); @@ -381,9 +378,10 @@ function startup(data, reason) { * @param {string} url The URL of the module housing the real factory. * @param {string} classID The CID of the class this factory represents. */ -function FactoryProxy(url, classID) { +function FactoryProxy(url, classID, contractID) { this.url = url; this.classID = Components.ID(classID); + this.contractID = contractID; } FactoryProxy.prototype = { QueryInterface: XPCOMUtils.generateQI(Ci.nsIFactory), @@ -405,18 +403,12 @@ FactoryProxy.prototype = { } } +var timer; function shutdown(data, reason) { let strReason = reasonToString(reason); debug("bootstrap: shutdown " + strReason); if (reason != APP_SHUTDOWN) { - try { - //JSMLoader.load("disable-acr").cleanup(addon.id); - } - catch (e) { - reportError(e); - } - if (~[ADDON_UPGRADE, ADDON_DOWNGRADE, ADDON_UNINSTALL].indexOf(reason)) Services.obs.notifyObservers(null, "dactyl-purge", null); @@ -426,17 +418,20 @@ function shutdown(data, reason) { JSMLoader.atexit(strReason); JSMLoader.cleanup(strReason); - if (JSMLoader.SANDBOX) - Cu.nukeSandbox(bootstrap); - bootstrap_jsm.require = null; - Cu.unload(BOOTSTRAP); - bootstrap = null; - bootstrap_jsm = null; - - for each (let [category, entry] in categories) + for each (let [category, entry] in JSMLoader.config.categories) categoryManager.deleteCategoryEntry(category, entry, false); - for each (let resource in resources) + for (let resource in JSMLoader.config.resources) resourceProto.setSubstitution(resource, null); + + timer = Timer(() => { + bootstrap_jsm.require = null; + if (JSMLoader.SANDBOX) + Cu.nukeSandbox(bootstrap); + else + Cu.unload(BOOTSTRAP); + bootstrap = null; + bootstrap_jsm = null; + }, 5000, Ci.nsITimer.TYPE_ONE_SHOT); } } diff --git a/common/chrome.manifest b/common/chrome.manifest index fbf9096..b24abc9 100644 --- a/common/chrome.manifest +++ b/common/chrome.manifest @@ -1,17 +1 @@ -resource dactyl ../common/modules/ -resource dactyl-common ../common/ -resource dactyl-content ../common/content/ -resource dactyl-skin ../common/skin/ -resource dactyl-locale ../common/locale/ - -resource dactyl-local ./ -resource dactyl-local-content content/ -resource dactyl-local-skin skin/ -resource dactyl-local-locale locale/ - content dactyl ../common/content/ - -component {16dc34f7-6d22-4aa4-a67f-2921fb5dcb69} components/commandline-handler.js -contract @mozilla.org/commandlinehandler/general-startup;1?type=dactyl {16dc34f7-6d22-4aa4-a67f-2921fb5dcb69} -category command-line-handler m-dactyl @mozilla.org/commandlinehandler/general-startup;1?type=dactyl - diff --git a/common/content/abbreviations.js b/common/content/abbreviations.js index f0cd06d..7ceb5e9 100644 --- a/common/content/abbreviations.js +++ b/common/content/abbreviations.js @@ -1,6 +1,6 @@ // Copyright (c) 2006-2009 by Martin Stubenschrott // Copyright (c) 2010 by anekos -// Copyright (c) 2010-2013 Kris Maglione +// Copyright (c) 2010-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -105,7 +105,7 @@ var AbbrevHive = Class("AbbrevHive", Contexts.Hive, { }, /** @property {boolean} True if there are no abbreviations. */ - get empty() !values(this._store).nth(util.identity, 0), + get empty() !values(this._store).find(util.identity), /** * Adds a new abbreviation. @@ -132,7 +132,8 @@ var AbbrevHive = Class("AbbrevHive", Contexts.Hive, { */ get: function (mode, lhs) { let abbrevs = this._store[mode]; - return abbrevs && Set.has(abbrevs, lhs) ? abbrevs[lhs] : null; + return abbrevs && hasOwnProperty(abbrevs, lhs) ? abbrevs[lhs] + : null; }, /** @@ -232,10 +233,10 @@ var Abbreviations = Module("abbreviations", { get userHives() this.allHives.filter(h => h !== this.builtin), - get: deprecated("group.abbrevs.get", { get: function get() this.user.closure.get }), - set: deprecated("group.abbrevs.set", { get: function set() this.user.closure.set }), - remove: deprecated("group.abbrevs.remove", { get: function remove() this.user.closure.remove }), - removeAll: deprecated("group.abbrevs.clear", { get: function removeAll() this.user.closure.clear }), + get: deprecated("group.abbrevs.get", { get: function get() this.user.bound.get }), + set: deprecated("group.abbrevs.set", { get: function set() this.user.bound.set }), + remove: deprecated("group.abbrevs.remove", { get: function remove() this.user.bound.remove }), + removeAll: deprecated("group.abbrevs.clear", { get: function removeAll() this.user.bound.clear }), /** * Returns the abbreviation for the given *mode* if *text* matches the @@ -250,7 +251,7 @@ var Abbreviations = Module("abbreviations", { let match = this._match.exec(text); if (match) return this.hives.map(h => h.get(mode, match[2] || match[4] || match[6])) - .nth(util.identity, 0); + .find(util.identity); return null; }, @@ -266,7 +267,7 @@ var Abbreviations = Module("abbreviations", { let hives = (hives || this.userHives).filter(h => !h.empty); function abbrevs(hive) - hive.merged.filter(ab => (ab.inModes(modes) && ab.lhs.indexOf(lhs) == 0)); + hive.merged.filter(ab => (ab.inModes(modes) && ab.lhs.startsWith(lhs))); let list = ["table", {}, ["tr", { highlight: "Title" }, diff --git a/common/content/autocommands.js b/common/content/autocommands.js index 582337c..7bb2347 100644 --- a/common/content/autocommands.js +++ b/common/content/autocommands.js @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -71,13 +71,15 @@ var AutoCmdHive = Class("AutoCmdHive", Contexts.Hive, { */ var AutoCommands = Module("autocommands", { init: function () { + if (!config.haveGecko("26")) + delete config.autocommands.DownloadPost; // FIXME }, get activeHives() contexts.allGroups.autocmd.filter(h => h._store.length), - add: deprecated("group.autocmd.add", { get: function add() autocommands.user.closure.add }), - get: deprecated("group.autocmd.get", { get: function get() autocommands.user.closure.get }), - remove: deprecated("group.autocmd.remove", { get: function remove() autocommands.user.closure.remove }), + add: deprecated("group.autocmd.add", { get: function add() autocommands.user.bound.add }), + get: deprecated("group.autocmd.get", { get: function get() autocommands.user.bound.get }), + remove: deprecated("group.autocmd.remove", { get: function remove() autocommands.user.bound.remove }), /** * Lists all autocommands with a matching *event*, *regexp* and optionally diff --git a/common/content/bookmarks.js b/common/content/bookmarks.js index 09f088c..dd64e54 100644 --- a/common/content/bookmarks.js +++ b/common/content/bookmarks.js @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -73,7 +73,7 @@ var Bookmarks = Module("bookmarks", { if (id != null) var bmark = bookmarkcache.bookmarks[id]; else if (!force) { - if (keyword && Set.has(bookmarkcache.keywords, keyword)) + if (keyword && hasOwnProperty(bookmarkcache.keywords, keyword)) bmark = bookmarkcache.keywords[keyword]; else if (bookmarkcache.isBookmarked(uri)) for (bmark in bookmarkcache) @@ -172,7 +172,7 @@ var Bookmarks = Module("bookmarks", { } }, - isBookmarked: deprecated("bookmarkcache.isBookmarked", { get: function isBookmarked() bookmarkcache.closure.isBookmarked }), + isBookmarked: deprecated("bookmarkcache.isBookmarked", { get: function isBookmarked() bookmarkcache.bound.isBookmarked }), /** * Remove a bookmark or bookmarks. If *ids* is an array, removes the @@ -189,7 +189,7 @@ var Bookmarks = Module("bookmarks", { let uri = util.newURI(ids); ids = services.bookmarks .getBookmarkIdsForURI(uri, {}) - .filter(bookmarkcache.closure.isRegularBookmark); + .filter(bookmarkcache.bound.isRegularBookmark); } ids.forEach(function (id) { let bmark = bookmarkcache.bookmarks[id]; @@ -223,7 +223,7 @@ var Bookmarks = Module("bookmarks", { if (!alias) alias = "search"; // for search engines which we can't find a suitable alias - if (Set.has(aliases, alias)) + if (hasOwnProperty(aliases, alias)) alias += ++aliases[alias]; else aliases[alias] = 0; @@ -232,6 +232,31 @@ var Bookmarks = Module("bookmarks", { }).toObject(); }, + /** + * Returns true if the given search engine provides suggestions. + * engine based on the given *query*. The results are always in the + * form of an array of strings. If *callback* is provided, the + * request is executed asynchronously and *callback* is called on + * completion. Otherwise, the request is executed synchronously and + * the results are returned. + * + * @param {string} engineName The name of the search engine from + * which to request suggestions. + * @returns {boolean} + */ + hasSuggestions: function hasSuggestions(engineName, query, callback) { + const responseType = "application/x-suggestions+json"; + + if (hasOwnProperty(this.suggestionProviders, engineName)) + return true; + + let engine = hasOwnProperty(this.searchEngines, engineName) && this.searchEngines[engineName]; + if (engine && engine.supportsResponseType(responseType)) + return true; + + return false; + }, + /** * Retrieves a list of search suggestions from the named search * engine based on the given *query*. The results are always in the @@ -251,14 +276,15 @@ var Bookmarks = Module("bookmarks", { getSuggestions: function getSuggestions(engineName, query, callback) { const responseType = "application/x-suggestions+json"; - if (Set.has(this.suggestionProviders, engineName)) + if (hasOwnProperty(this.suggestionProviders, engineName)) return this.suggestionProviders[engineName](query, callback); - let engine = Set.has(this.searchEngines, engineName) && this.searchEngines[engineName]; + let engine = hasOwnProperty(this.searchEngines, engineName) && this.searchEngines[engineName]; if (engine && engine.supportsResponseType(responseType)) var queryURI = engine.getSubmission(query, responseType).uri.spec; + if (!queryURI) - return (callback || util.identity)([]); + return promises.fail(); function parse(req) JSON.parse(req.responseText)[1].filter(isString); return this.makeSuggestions(queryURI, parse, callback); @@ -271,25 +297,25 @@ var Bookmarks = Module("bookmarks", { * @param {string} url The URL to fetch. * @param {function(XMLHttpRequest):[string]} parser The function which * parses the response. + * @returns {Promise} */ - makeSuggestions: function makeSuggestions(url, parser, callback) { - function process(req) { + makeSuggestions: function makeSuggestions(url, parser) { + let deferred = Promise.defer(); + + let req = util.fetchUrl(url); + req.then(function process(req) { let results = []; try { results = parser(req); } catch (e) { - util.reportError(e); + return deferred.reject(e); } - if (callback) - return callback(results); - return results; - } + deferred.resolve(results); + }, Cu.reportError); - let req = util.httpGet(url, callback && process); - if (callback) - return req; - return process(req); + promises.oncancel(deferred, r => promises.cancel(req, reason)); + return deferred.promise; }, suggestionProviders: {}, @@ -320,7 +346,7 @@ var Bookmarks = Module("bookmarks", { param = query.substr(offset + 1); } - var engine = Set.has(bookmarks.searchEngines, keyword) && bookmarks.searchEngines[keyword]; + var engine = hasOwnProperty(bookmarks.searchEngines, keyword) && bookmarks.searchEngines[keyword]; if (engine) { if (engine.searchForm && !param) return engine.searchForm; @@ -536,7 +562,7 @@ var Bookmarks = Module("bookmarks", { "Delete a bookmark", function (args) { if (args.bang) - commandline.input(_("bookmark.prompt.deleteAll") + " ", + commandline.input(_("bookmark.prompt.deleteAll") + " ").then( function (resp) { if (resp && resp.match(/^y(es)?$/i)) { bookmarks.remove(Object.keys(bookmarkcache.bookmarks)); @@ -628,7 +654,7 @@ var Bookmarks = Module("bookmarks", { }, completion: function initCompletion() { - completion.bookmark = function bookmark(context, tags, extra = {}) { + completion.bookmark = function bookmark(context, tags, extra={}) { context.title = ["Bookmark", "Title"]; context.format = bookmarks.format; iter(extra).forEach(function ([k, v]) { @@ -655,7 +681,7 @@ var Bookmarks = Module("bookmarks", { keyword, true); let item = keywords[keyword]; - if (item && item.url.indexOf("%s") > -1) + if (item && item.url.contains("%s")) context.fork("keyword/" + keyword, keyword.length + space.length, null, function (context) { context.format = history.format; context.title = [/*L*/keyword + " Quick Search"]; @@ -668,7 +694,7 @@ var Bookmarks = Module("bookmarks", { return history.get({ uri: util.newURI(begin), uriIsPrefix: true }).map(function (item) { let rest = item.url.length - end.length; let query = item.url.substring(begin.length, rest); - if (item.url.substr(rest) == end && query.indexOf("&") == -1) + if (item.url.substr(rest) == end && query.contains("&")) try { item.url = decodeURIComponent(query.replace(/#.*/, "").replace(/\+/g, " ")); return item; @@ -696,16 +722,19 @@ var Bookmarks = Module("bookmarks", { let engineList = (engineAliases || options["suggestengines"].join(",") || "google").split(","); engineList.forEach(function (name) { + if (!bookmarks.hasSuggestions(name)) + return; + var desc = name; let engine = bookmarks.searchEngines[name]; if (engine) - var desc = engine.description; - else if (!Set.has(bookmarks.suggestionProviders, name)) - return; + desc = engine.description; + let [, word] = /^\s*(\S+)/.exec(context.filter) || []; if (!kludge && word == name) // FIXME: Check for matching keywords return; + let ctxt = context.fork(name, 0); ctxt.title = [/*L*/desc + " Suggestions"]; @@ -717,14 +746,20 @@ var Bookmarks = Module("bookmarks", { return; let words = ctxt.filter.toLowerCase().split(/\s+/g); - ctxt.completions = ctxt.completions.filter(i => words.every(w => i.toLowerCase().indexOf(w) >= 0)); + ctxt.completions = ctxt.completions.filter(i => words.every(w => i.toLowerCase().contains(w))); ctxt.hasItems = ctxt.completions.length; ctxt.incomplete = true; - ctxt.cache.request = bookmarks.getSuggestions(name, ctxt.filter, function (compl) { + ctxt.cache.request = bookmarks.getSuggestions(name, ctxt.filter); + ctxt.cache.request.then(function (compl) { ctxt.incomplete = false; - ctxt.completions = array.uniq(ctxt.completions.filter(c => compl.indexOf(c) >= 0) + ctxt.completions = array.uniq(ctxt.completions.filter(c => compl.contains(c)) .concat(compl), true); + }, function (e) { + ctxt.incomplete = false; + ctxt.completions = []; + if (e) + Cu.reportError(e); }); }); }; diff --git a/common/content/browser.js b/common/content/browser.js index 95c1fd4..7373629 100644 --- a/common/content/browser.js +++ b/common/content/browser.js @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -16,11 +16,17 @@ var Browser = Module("browser", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), this.cleanupProgressListener = overlay.overlayObject(window.XULBrowserWindow, this.progressListener); util.addObserver(this); + + this._unoverlay = overlay.overlayObject(FullZoom, { + get siteSpecific() false, + set siteSpecific(val) {} + }); }, destroy: function () { this.cleanupProgressListener(); this.observe.unregister(); + this._unoverlay(); }, observers: { diff --git a/common/content/commandline.js b/common/content/commandline.js index de8d9c3..9e66e53 100644 --- a/common/content/commandline.js +++ b/common/content/commandline.js @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -263,17 +263,7 @@ var CommandWidgets = Class("CommandWidgets", { } [this.commandbar.container, this.statusbar.container].forEach(check); - // Work around a redrawing bug. - if (changed && config.haveGecko("16", "20")) { - util.delay(function () { - // Urgh. - statusline.statusBar.style.paddingRight = "1px"; - DOM(statusline.statusBar).rect; // Force reflow. - statusline.statusBar.style.paddingRight = ""; - }, 0); - } - - if (this.initialized && loaded.mow && mow.visible) + if (this.initialized && loaded.has("mow") && mow.visible) mow.resize(false); }, @@ -351,7 +341,7 @@ var CommandMode = Class("CommandMode", { false); this.messageCount = commandline.messageCount; - modes.push(this.mode, this.extendedMode, this.closure); + modes.push(this.mode, this.extendedMode, this.bound); this.widgets.active.commandline.collapsed = false; this.widgets.prompt = this.prompt; @@ -694,7 +684,7 @@ var CommandLine = Module("commandline", { * Displays the multi-line output of a command, preceded by the last * executed ex command string. * - * @param {XML} xml The output as an E4X XML object. + * @param {object} xml The output as a JSON XML object. */ commandOutput: function commandOutput(xml) { if (!this.command) @@ -801,7 +791,7 @@ var CommandLine = Module("commandline", { let action = this._echoLine; if ((flags & this.FORCE_MULTILINE) || (/\n/.test(data) || !isinstance(data, [_, "String"])) && !(flags & this.FORCE_SINGLELINE)) - action = mow.closure.echo; + action = mow.bound.echo; let checkSingleLine = () => action == this._echoLine; @@ -813,7 +803,7 @@ var CommandLine = Module("commandline", { // So complicated... if (checkSingleLine() && !this.widgets.mowContainer.collapsed) { highlightGroup += " Message"; - action = mow.closure.echo; + action = mow.bound.echo; } else if (!checkSingleLine() && this.widgets.mowContainer.collapsed) { if (this._lastEcho && this.widgets.message && this.widgets.message[1] == this._lastEcho.msg) { @@ -855,7 +845,6 @@ var CommandLine = Module("commandline", { * pop at any time to close the prompt. * * @param {string} prompt The input prompt to use. - * @param {function(string)} callback * @param {Object} extra * @... {function} onChange - A function to be called with the current * input every time it changes. @@ -866,15 +855,16 @@ var CommandLine = Module("commandline", { * @... {string} default - The initial value that will be returned * if the user presses straightaway. @default "" */ - input: function _input(prompt, callback, extra = {}) { - CommandPromptMode(prompt, update({ onSubmit: callback }, extra)).open(); - }, + input: promises.withCallbacks(function _input([callback, reject], prompt, extra={}, thing={}) { + if (callable(extra)) + // Deprecated. + [callback, extra] = [extra, thing]; + + CommandPromptMode(prompt, update({ onSubmit: callback, onCancel: reject }, extra)).open(); + }), readHeredoc: function readHeredoc(end) { - let args; - commandline.inputMultiline(end, function (res) { args = res; }); - util.waitFor(() => args !== undefined); - return args; + return util.waitFor(commandline.inputMultiline(end)); }, /** @@ -883,10 +873,10 @@ var CommandLine = Module("commandline", { * callback with that string as a parameter. * * @param {string} end - * @param {function(string)} callback + * @returns {Promise} */ // FIXME: Buggy, especially when pasting. - inputMultiline: function inputMultiline(end, callback) { + inputMultiline: promises.withCallbacks(function inputMultiline([callback], end) { let cmd = this.command; let self = { end: "\n" + end + "\n", @@ -912,7 +902,7 @@ var CommandLine = Module("commandline", { this._autosizeMultilineInputWidget(); this.timeout(function () { dactyl.focus(this.widgets.multilineInput); }, 10); - }, + }), get commandMode() this.commandSession && isinstance(modes.main, modes.COMMAND_LINE), @@ -1010,11 +1000,10 @@ var CommandLine = Module("commandline", { if (privateData == "never-save") return; - this.store = this.store.filter(line => (line.value || line) != str); - dactyl.trapErrors(function () { - this.store.push({ value: str, timestamp: Date.now() * 1000, privateData: privateData }); - }, this); - this.store = this.store.slice(Math.max(0, this.store.length - options["history"])); + let store = Array.filter(this.store, line => (line.value || line) != str); + dactyl.trapErrors( + () => store.push({ value: str, timestamp: Date.now() * 1000, privateData: privateData })); + this.store = store.slice(Math.max(0, store.length - options["history"])); }, /** * @property {function} Returns whether a data item should be @@ -1124,7 +1113,7 @@ var CommandLine = Module("commandline", { this.itemList = commandline.completionList; this.itemList.open(this.context); - dactyl.registerObserver("events.doneFeeding", this.closure.onDoneFeeding, true); + dactyl.registerObserver("events.doneFeeding", this.bound.onDoneFeeding, true); this.autocompleteTimer = Timer(200, 500, function autocompleteTell(tabPressed) { if (events.feedingKeys && !tabPressed) @@ -1257,7 +1246,7 @@ var CommandLine = Module("commandline", { * called. */ cleanup: function cleanup() { - dactyl.unregisterObserver("events.doneFeeding", this.closure.onDoneFeeding); + dactyl.unregisterObserver("events.doneFeeding", this.bound.onDoneFeeding); this.previewClear(); this.tabTimer.reset(); @@ -1394,7 +1383,7 @@ var CommandLine = Module("commandline", { * @default {@link #selected} * @returns {object} */ - getItem: function getItem(tuple = this.selected) + getItem: function getItem(tuple=this.selected) tuple && tuple[0] && tuple[0].items[tuple[1]], /** @@ -1509,7 +1498,7 @@ var CommandLine = Module("commandline", { * @default false * @private */ - select: function select(idx, count = 1, fromTab = false) { + select: function select(idx, count=1, fromTab=false) { switch (idx) { case this.UP: case this.DOWN: @@ -1956,7 +1945,7 @@ var ItemList = Class("ItemList", { this.resize(flags); }, this); - DOM(this.win).resize(this._onResize.closure.tell); + DOM(this.win).resize(this._onResize.bound.tell); }, get rootXML() @@ -2029,7 +2018,7 @@ var ItemList = Class("ItemList", { if (start < 0 || start >= this.itemCount) return null; - group = array.nth(groups, g => let (i = start - g.offsets.start) i >= 0 && i < g.itemCount, 0); + group = groups.find(g => let (i = start - g.offsets.start) i >= 0 && i < g.itemCount); return [group.context, start - group.offsets.start]; }, @@ -2146,7 +2135,8 @@ var ItemList = Class("ItemList", { // We need to collect all of the rescrolling functions in // one go, as the height calculation that they need to do - // would force a reflow after each DOM modification. + // would force an expensive reflow after each call due to + // DOM modifications, otherwise. this.activeGroups.filter(g => !g.collapsed) .map(g => g.rescrollFunc) .forEach(call); @@ -2280,7 +2270,7 @@ var ItemList = Class("ItemList", { getGroup: function getGroup(context) context instanceof ItemList.Group ? context : context && context.getCache("itemlist-group", - bind("Group", ItemList, this, context)), + () => ItemList.Group(this, context)), getOffset: function getOffset(tuple) tuple && this.getGroup(tuple[0]).getOffset(tuple[1]) }, { @@ -2407,8 +2397,8 @@ var ItemList = Class("ItemList", { first = row; let container = DOM(this.nodes.items); - let before = first ? DOM(first).closure.before - : DOM(this.nodes.items).closure.append; + let before = first ? DOM(first).bound.before + : DOM(this.nodes.items).bound.append; for (let [i, row] in this.context.getRows(range.start, range.end, this.doc)) { diff --git a/common/content/dactyl.js b/common/content/dactyl.js index 6ad9e55..752b17d 100644 --- a/common/content/dactyl.js +++ b/common/content/dactyl.js @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -136,14 +136,14 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { && !item.hidden && !/rdf:http:/.test(item.getAttribute("label"))) { // FIXME item.dactylPath = parent + item.getAttribute("label"); - if (!targetPath || targetPath.indexOf(item.dactylPath) == 0) + if (!targetPath || targetPath.startsWith(item.dactylPath)) items.push(item); } else { let path = parent; if (item.localName == "menu") path += item.getAttribute("label") + "."; - if (!targetPath || targetPath.indexOf(path) == 0) + if (!targetPath || targetPath.startsWith(path)) addChildren(item, path); } } @@ -206,7 +206,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { registerObservers: function registerObservers(obj, prop) { for (let [signal, func] in Iterator(obj[prop || "signals"])) - this.registerObserver(signal, obj.closure(func), false); + this.registerObserver(signal, func.bind(obj), false); }, unregisterObserver: function unregisterObserver(type, callback) { @@ -216,20 +216,11 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { applyTriggerObserver: function triggerObserver(type, args) { if (type in this._observers) - this._observers[type] = this._observers[type].filter(function (callback) { - if (callback.get()) { - try { - try { - callback.get().apply(null, args); - } - catch (e if e.message == "can't wrap XML objects") { - // Horrible kludge. - callback.get().apply(null, [String(args[0])].concat(args.slice(1))); - } - } - catch (e) { - dactyl.reportError(e); - } + this._observers[type] = this._observers[type] + .filter(callback => { + callback = callback.get(); + if (callback) { + util.trapErrors(() => callback.apply(null, args)); return true; } }); @@ -250,7 +241,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { let filters = args.map(arg => let (re = util.regexp.escape(arg)) util.regexp("\\b" + re + "\\b|(?:^|[()\\s])" + re + "(?:$|[()\\s])", "i")); if (filters.length) - results = results.filter(item => filters.every(re => keys(item).some(re.closure.test))); + results = results.filter(item => filters.every(re => keys(item).some(re.bound.test))); commandline.commandOutput( template.usage(results, params.format)); @@ -278,10 +269,9 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { let results = array((params.iterateIndex || params.iterate).call(params, commands.get(name).newArgs())) .array.sort((a, b) => String.localeCompare(a.name, b.name)); - let haveTag = Set.has(help.tags); for (let obj in values(results)) { let res = dactyl.generateHelp(obj, null, null, true); - if (!haveTag(obj.helpTag)) + if (!hasOwnProperty(help.tags, obj.helpTag)) res[0][1].tag = obj.helpTag; yield res; @@ -387,9 +377,9 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { }, dump: deprecated("util.dump", - { get: function dump() util.closure.dump }), + { get: function dump() util.bound.dump }), dumpStack: deprecated("util.dumpStack", - { get: function dumpStack() util.closure.dumpStack }), + { get: function dumpStack() util.bound.dumpStack }), /** * Outputs a plain message to the command line. @@ -468,16 +458,16 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { * should be loaded. */ loadScript: function loadScript(uri, context) { + let prefix = "literal:" + uri + ":"; + cache.flush(s => s.startsWith(prefix)); + delete literal.files[uri]; JSMLoader.loadSubScript(uri, context, File.defaultEncoding); }, userEval: function userEval(str, context, fileName, lineNumber) { - let ctxt; - if (jsmodules.__proto__ != window && jsmodules.__proto__ != XPCNativeWrapper(window) && - jsmodules.isPrototypeOf(context)) - str = "with (window) { with (modules) { (this.eval || eval)(" + str.quote() + ") } }"; + let ctxt, + info = contexts.context; - let info = contexts.context; if (fileName == null) if (info) ({ file: fileName, line: lineNumber, context: ctxt }) = info; @@ -485,14 +475,14 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { if (fileName && fileName[0] == "[") fileName = "dactyl://command-line/"; else if (!context) - context = ctxt || _userContext; - - if (isinstance(context, ["Sandbox"])) - return Cu.evalInSandbox(str, context, "1.8", fileName, lineNumber); + context = ctxt || userContext; if (!context) context = userContext || ctxt; + if (isinstance(context, ["Sandbox"])) + return Cu.evalInSandbox(str, context, "1.8", fileName, lineNumber); + if (services.has("dactyl") && services.dactyl.evalInContext) return services.dactyl.evalInContext(str, context, fileName, lineNumber); @@ -538,7 +528,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { * @param {boolean} silent Whether the command should be echoed on the * command line. */ - execute: function execute(str, modifiers = {}, silent = false) { + execute: function execute(str, modifiers={}, silent=false) { // skip comments and blank lines if (/^\s*("|$)/.test(str)) return; @@ -620,7 +610,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { * @param {string} feature The feature name. * @returns {boolean} */ - has: function has(feature) Set.has(config.features, feature), + has: function has(feature) config.has(feature), /** * @private @@ -637,8 +627,8 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { } }, - help: deprecated("help.help", { get: function help() modules.help.closure.help }), - findHelp: deprecated("help.findHelp", { get: function findHelp() help.closure.findHelp }), + help: deprecated("help.help", { get: function help() modules.help.bound.help }), + findHelp: deprecated("help.findHelp", { get: function findHelp() help.bound.findHelp }), /** * @private @@ -734,7 +724,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { } if (obj.completer && false) - add(completion._runCompleter(obj.closure.completer, "", null, args).items + add(completion._runCompleter(obj.bound.completer, "", null, args).items .map(i => [i.text, i.description])); if (obj.options && obj.options.some(o => o.description) && false) @@ -832,6 +822,20 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { }, events: { + beforecustomization: function onbeforecustomization(event) { + // Show navigation bar on Australis, where it's not supposed + // to be collapsible, and is therefore not handled by + // builtin code. + if ("CustomizableUI" in window) + this.setNodeVisible(document.getElementById("nav-bar"), + true); + }, + + aftercustomization: function onaftercustomization(event) { + // Restore toolbar states. + options["guioptions"] = options["guioptions"]; + }, + click: function onClick(event) { let elem = event.originalTarget; @@ -890,13 +894,13 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { * tabs. * @returns {boolean} */ - open: function open(urls, params = {}, force = false) { + open: function open(urls, params={}, force=false) { if (typeof urls == "string") urls = dactyl.parseURLs(urls); if (urls.length > prefs.get("browser.tabs.maxOpenBeforeWarn", 20) && !force) - return commandline.input(_("dactyl.prompt.openMany", urls.length) + " ", - function (resp) { + return commandline.input(_("dactyl.prompt.openMany", urls.length) + " ") + .then(function (resp) { if (resp && resp.match(/^y(es)?$/i)) dactyl.open(urls, params, true); }); @@ -1143,7 +1147,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { if (error instanceof FailedAssertion && error.noTrace || error.message === "Interrupted") { let context = contexts.context; let prefix = context ? context.file + ":" + context.line + ": " : ""; - if (error.message && error.message.indexOf(prefix) !== 0 && + if (error.message && !error.message.startsWith(prefix) && prefix != "[Command Line]:1: ") error.message = prefix + error.message; @@ -1184,7 +1188,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { return []; } }, - wrapCallback: function wrapCallback(callback, self = this) { + wrapCallback: function wrapCallback(callback, self=this) { let save = ["forceOpen"]; let saved = save.map(p => dactyl[p]); return function wrappedCallback() { @@ -1205,10 +1209,11 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { * @property {[Window]} Returns an array of all the host application's * open windows. */ - get windows() [win for (win in iter(services.windowMediator.getEnumerator("navigator:browser"))) if (win.dactyl)], + get windows() [w for (w of overlay.windows)] }, { - toolbarHidden: function hidden(elem) (elem.getAttribute("autohide") || elem.getAttribute("collapsed")) == "true" + toolbarHidden: function toolbarHidden(elem) "true" == (elem.getAttribute("autohide") || + elem.getAttribute("collapsed")) }, { cache: function initCache() { cache.register("help/plugins.xml", function () { @@ -1219,19 +1224,23 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { try { let info = contexts.getDocs(context); if (DOM.isJSONXML(info)) { - let langs = info.slice(2).filter(e => isArray(e) && isObject(e[1]) && e[1].lang); + let langs = info.slice(2) + .filter(e => isArray(e) && isObject(e[1]) && e[1].lang); if (langs) { - let lang = config.bestLocale(l[1].lang for each (l in langs)); + let lang = config.bestLocale(langs.map(l => l[1].lang)); info = info.slice(0, 2).concat( info.slice(2).filter(e => !isArray(e) || !isObject(e[1]) || e[1].lang == lang)); - for each (let elem in info.slice(2).filter(e => isArray(e) && e[0] == "info" && isObject(e[1]))) - for (let attr in values(["name", "summary", "href"])) + info.slice(2) + .filter(e => isArray(e) && e[0] == "info" && isObject(e[1])) + .forEach(elem => { + for (let attr of ["name", "summary", "href"]) if (attr in elem[1]) info[attr] = elem[1][attr]; + }); } body.push(["h2", { xmlns: "dactyl", tag: info[1].name + '-plugin' }, String(info[1].summary)]); @@ -1251,7 +1260,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { ["toc", { start: "2" }], body]); - }); + }, true); cache.register("help/index.xml", function () { return '\n' + @@ -1260,7 +1269,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { ["dl", { insertafter: name + "-index" }, template.map(iter(), util.identity)], "\n\n")]); - }); + }, true); cache.register("help/gui.xml", function () { return '\n' + @@ -1272,7 +1281,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { ["dd", {}, val[0]]] : undefined, "\n")]]); - }); + }, true); cache.register("help/privacy.xml", function () { return '\n' + @@ -1285,7 +1294,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { [["dt", {}, name], ["dd", {}, template.linkifyHelp(description, true)]], "\n")]]); - }); + }, true); }, events: function initEvents() { events.listen(window, dactyl, "events", true); @@ -1315,7 +1324,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { M: ["Always show messages outside of the status line"] }, setter: function (opts) { - if (loaded.commandline || ~opts.indexOf("c")) + if (loaded.has("commandline") || ~opts.indexOf("c")) commandline.widgets.updateVisibility(); } }, @@ -1367,10 +1376,6 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { styles.system.add("taboptions", "chrome://*", classes.length ? classes.join(",") + "{ display: none; }" : ""); - if (!dactyl.has("Gecko2")) { - tabs.tabBinding.enabled = Array.some(opts, k => k in this.opts); - tabs.updateTabCount(); - } if (config.tabbrowser.tabContainer._positionPinnedTabs) config.tabbrowser.tabContainer._positionPinnedTabs(); }, @@ -1639,7 +1644,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { if (args["+purgecaches"]) cache.flush(); - util.delay(function () { util.rehash(args) }); + util.delay(() => { util.rehash(args) }); }, { argCount: "0", // FIXME @@ -1889,6 +1894,12 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { }); } + if (config.has("default-theme") && "CustomizableUI" in window) + overlay.overlayWindow(window, { + append: [ + ["window", { id: document.documentElement.id, "dactyl-australis": "true", xmlns: "xul" }]] + }); + dactyl.timeout(function () { try { var args = config.prefs.get("commandline-args") @@ -1927,7 +1938,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { // dactyl.hideGUI(); if (dactyl.userEval("typeof document", null, "test.js") === "undefined") - jsmodules.__proto__ = XPCSafeJSObjectWrapper(window); + jsmodules.__proto__ = window; if (dactyl.commandLineOptions.preCommands) dactyl.commandLineOptions.preCommands.forEach(function (cmd) { diff --git a/common/content/disable-acr.jsm b/common/content/disable-acr.jsm deleted file mode 100644 index 86e55c4..0000000 --- a/common/content/disable-acr.jsm +++ /dev/null @@ -1,76 +0,0 @@ -// By Kris Maglione. Public Domain. -// Please feel free to copy and use at will. - -var ADDON_ID; - -const OVERLAY_URLS = [ - "about:addons", - "chrome://mozapps/content/extensions/extensions.xul" -]; - -let { interfaces: Ci, utils: Cu } = Components; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -const TOPIC = "chrome-document-global-created"; - -function observe(window, topic, url) { - if (topic === TOPIC) - checkDocument(window.document); -} -function init(id) { - if (id) - ADDON_ID = id; - - Services.obs[id ? "addObserver" : "removeObserver"](observe, TOPIC, false); - for (let doc in chromeDocuments()) - checkDocument(doc, !id); -} -function cleanup() { init(null); } - -function checkPopup(event) { - let doc = event.originalTarget.ownerDocument; - let binding = doc.getBindingParent(event.originalTarget); - if (binding && binding.addon && binding.addon.guid == ADDON_ID && !binding.addon.compatible) { - let elem = doc.getAnonymousElementByAttribute(binding, "anonid", "stillworks"); - if (elem && elem.nextSibling) { - elem.nextSibling.disabled = true; - elem.nextSibling.setAttribute("tooltiptext", "Developer has opted out of incompatibility reports\n"+ - "Development versions are available with updated support"); - } - } -} - -function checkDocument(doc, disable, force) { - if (["interactive", "complete"].indexOf(doc.readyState) >= 0 || force && doc.readyState === "uninitialized") { - if (OVERLAY_URLS.indexOf(doc.documentURI) >= 0) - doc[disable ? "removeEventListener" : "addEventListener"]("popupshowing", checkPopup, false); - } - else { - doc.addEventListener("DOMContentLoaded", function listener() { - doc.removeEventListener("DOMContentLoaded", listener, false); - checkDocument(doc, disable, true); - }, false); - } -} - -function chromeDocuments() { - let windows = Services.wm.getXULWindowEnumerator(null); - while (windows.hasMoreElements()) { - let window = windows.getNext().QueryInterface(Ci.nsIXULWindow); - for each (let type in ["typeChrome", "typeContent"]) { - let docShells = window.docShell.getDocShellEnumerator(Ci.nsIDocShellTreeItem[type], - Ci.nsIDocShell.ENUMERATE_FORWARDS); - while (docShells.hasMoreElements()) - try { - yield docShells.getNext().QueryInterface(Ci.nsIDocShell).contentViewer.DOMDocument; - } - catch (e) {} - } - } -} - -var EXPORTED_SYMBOLS = ["cleanup", "init"]; - -// vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript: diff --git a/common/content/editor.js b/common/content/editor.js index d98fa95..38153e4 100644 --- a/common/content/editor.js +++ b/common/content/editor.js @@ -1,4 +1,4 @@ -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // Copyright (c) 2006-2009 by Martin Stubenschrott // // This work is licensed for reuse under an MIT license. Details are @@ -46,7 +46,7 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), { }, this); }, - defaultRegister: "*", + defaultRegister: "*+", selectionRegisters: { "*": "selection", @@ -64,11 +64,12 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), { if (name == null) name = editor.currentRegister || editor.defaultRegister; + name = String(name)[0]; if (name == '"') name = 0; if (name == "_") var res = null; - else if (Set.has(this.selectionRegisters, name)) + else if (hasOwnProperty(this.selectionRegisters, name)) res = { text: dactyl.clipboardRead(this.selectionRegisters[name]) || "" }; else if (!/^[0-9]$/.test(name)) res = this.registers.get(name); @@ -101,17 +102,19 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), { value = DOM.stringify(value); value = { text: value, isLine: modes.extended & modes.LINE, timestamp: Date.now() * 1000 }; - if (name == '"') - name = 0; - if (name == "_") - ; - else if (Set.has(this.selectionRegisters, name)) - dactyl.clipboardWrite(value.text, verbose, this.selectionRegisters[name]); - else if (!/^[0-9]$/.test(name)) - this.registers.set(name, value); - else { - this.registerRing.insert(value, name); - this.registerRing.truncate(10); + for (let n of String(name)) { + if (n == '"') + n = 0; + if (n == "_") + ; + else if (hasOwnProperty(this.selectionRegisters, n)) + dactyl.clipboardWrite(value.text, verbose, this.selectionRegisters[n]); + else if (!/^[0-9]$/.test(n)) + this.registers.set(n, value); + else { + this.registerRing.insert(value, n); + this.registerRing.truncate(10); + } } }, @@ -386,8 +389,8 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), { let keepFocus = modes.stack.some(m => isinstance(m.main, modes.COMMAND_LINE)); if (!forceEditing && textBox && textBox.type == "password") { - commandline.input(_("editor.prompt.editPassword") + " ", - function (resp) { + commandline.input(_("editor.prompt.editPassword") + " ") + .then(function (resp) { if (resp && resp.match(/^y(es)?$/i)) editor.editFieldExternally(true); }); @@ -424,7 +427,7 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), { column = 1 + pre.replace(/[^]*\n/, "").length; let origGroup = DOM(textBox).highlight.toString(); - let cleanup = util.yieldable(function cleanup(error) { + let cleanup = promises.task(function cleanup(error) { if (timer) timer.cancel(); @@ -443,9 +446,11 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), { DOM(textBox).highlight.remove("EditorEditing"); if (!keepFocus) dactyl.focus(textBox); + for (let group in values(blink.concat(blink, ""))) { highlight.highlightNode(textBox, origGroup + " " + group); - yield 100; + + yield promises.sleep(100); } } }); @@ -760,9 +765,9 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), { completion: function initCompletion() { completion.register = function complete_register(context) { context = context.fork("registers"); - context.keys = { text: util.identity, description: editor.closure.getRegister }; + context.keys = { text: util.identity, description: editor.bound.getRegister }; - context.match = function (r) !this.filter || ~this.filter.indexOf(r); + context.match = function (r) !this.filter || this.filter.contains(r); context.fork("clipboard", 0, this, function (ctxt) { ctxt.match = context.match; @@ -1363,11 +1368,12 @@ var Editor = Module("editor", XPCOM(Ci.nsIEditActionListener, ModuleBase), { args.push(obj["file"]); return args; }, - has: function (key) Set.has(util.compileMacro(this.value).seen, key), + has: function (key) util.compileMacro(this.value).seen.has(key), validator: function (value) { this.format({}, value); - return Object.keys(util.compileMacro(value).seen) - .every(k => ["column", "file", "line"].indexOf(k) >= 0); + let allowed = RealSet(["column", "file", "line"]); + return [k for (k of util.compileMacro(value).seen)] + .every(k => allowed.has(k)); } }); diff --git a/common/content/events.js b/common/content/events.js index 8006c4c..844a5b5 100644 --- a/common/content/events.js +++ b/common/content/events.js @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -28,7 +28,7 @@ var EventHive = Class("EventHive", Contexts.Hive, { else [self, events] = [event, event[callback || "events"]]; - if (Set.has(events, "input") && !Set.has(events, "dactyl-input")) + if (hasOwnProperty(events, "input") && !hasOwnProperty(events, "dactyl-input")) events["dactyl-input"] = events.input; return [self, events]; @@ -79,7 +79,7 @@ var EventHive = Class("EventHive", Contexts.Hive, { let elem = args[0].get(); if (target == null || elem == target && self == args[1].get() - && Set.has(events, args[2]) + && hasOwnProperty(events, args[2]) && args[3].wrapped == events[args[2]] && args[4] == capture) { @@ -90,7 +90,7 @@ var EventHive = Class("EventHive", Contexts.Hive, { }); }, - get wrapListener() events.closure.wrapListener + get wrapListener() events.bound.wrapListener }); /** @@ -107,9 +107,9 @@ var Events = Module("events", { ["window", { id: document.documentElement.id, xmlns: "xul" }, // http://developer.mozilla.org/en/docs/XUL_Tutorial:Updating_Commands ["commandset", { id: "dactyl-onfocus", commandupdater: "true", events: "focus", - commandupdate: this.closure.onFocusChange }], + commandupdate: this.bound.onFocusChange }], ["commandset", { id: "dactyl-onselect", commandupdater: "true", events: "select", - commandupdate: this.closure.onSelectionChange }]]] + commandupdate: this.bound.onSelectionChange }]]] }); this._fullscreen = window.fullScreen; @@ -188,6 +188,11 @@ var Events = Module("events", { this.listen(window, this, "events", true); this.listen(window, this.popups, "events", true); + + this.grabFocus = 0; + this.grabFocusTimer = Timer(100, 10000, () => { + this.grabFocus = 0; + }); }, cleanup: function cleanup() { @@ -206,13 +211,13 @@ var Events = Module("events", { } }, - get listen() this.builtin.closure.listen, + get listen() this.builtin.bound.listen, addSessionListener: deprecated("events.listen", { get: function addSessionListener() this.listen }), /** * Wraps an event listener to ensure that errors are reported. */ - wrapListener: function wrapListener(method, self = this) { + wrapListener: function wrapListener(method, self=this) { method.wrapper = wrappedListener; wrappedListener.wrapped = method; function wrappedListener(event) { @@ -419,11 +424,11 @@ var Events = Module("events", { return true; }, - canonicalKeys: deprecated("DOM.Event.canonicalKeys", { get: function canonicalKeys() DOM.Event.closure.canonicalKeys }), + canonicalKeys: deprecated("DOM.Event.canonicalKeys", { get: function canonicalKeys() DOM.Event.bound.canonicalKeys }), create: deprecated("DOM.Event", function create() DOM.Event.apply(null, arguments)), dispatch: deprecated("DOM.Event.dispatch", function dispatch() DOM.Event.dispatch.apply(DOM.Event, arguments)), - fromString: deprecated("DOM.Event.parse", { get: function fromString() DOM.Event.closure.parse }), - iterKeys: deprecated("DOM.Event.iterKeys", { get: function iterKeys() DOM.Event.closure.iterKeys }), + fromString: deprecated("DOM.Event.parse", { get: function fromString() DOM.Event.bound.parse }), + iterKeys: deprecated("DOM.Event.iterKeys", { get: function iterKeys() DOM.Event.bound.iterKeys }), toString: function toString() { if (!arguments.length) @@ -571,7 +576,7 @@ var Events = Module("events", { events: { blur: function onBlur(event) { let elem = event.originalTarget; - if (DOM(elem).isEditable) + if (DOM(elem).editor) util.trapErrors("removeEditActionListener", DOM(elem).editor, editor); @@ -595,7 +600,7 @@ var Events = Module("events", { // TODO: Merge with onFocusChange focus: function onFocus(event) { let elem = event.originalTarget; - if (DOM(elem).isEditable) + if (DOM(elem).editor) util.trapErrors("addEditActionListener", DOM(elem).editor, editor); @@ -616,11 +621,15 @@ var Events = Module("events", { Ci.nsIDOMHTMLSelectElement, Ci.nsIDOMHTMLTextAreaElement, Ci.nsIDOMWindow])) { - - if (elem.frameElement) - dactyl.focusContent(true); - else if (!(elem instanceof Window) || Editor.getEditor(elem)) - dactyl.focus(window); + if (this.grabFocus++ > 5) + ; // Something is fighting us. Give up. + else { + this.grabFocusTimer.tell(); + if (elem.frameElement) + dactyl.focusContent(true); + else if (!(elem instanceof Window) || Editor.getEditor(elem)) + dactyl.focus(window); + } } if (elem instanceof Element) @@ -711,7 +720,10 @@ var Events = Module("events", { return Events.kill(event); } - if (!this.processor) { + if (this.processor) + events.dbg("ON KEYPRESS " + key + " processor: " + this.processor, + event.originalTarget instanceof Element ? event.originalTarget : String(event.originalTarget)); + else { let mode = modes.getStack(0); if (event.dactylMode) mode = Modes.StackElement(event.dactylMode); @@ -875,19 +887,23 @@ var Events = Module("events", { let haveInput = modes.stack.some(m => m.main.input); if (DOM(elem || win).isEditable) { - if (!haveInput) - if (!isinstance(modes.main, [modes.INPUT, modes.TEXT_EDIT, modes.VISUAL])) - if (options["insertmode"]) - modes.push(modes.INSERT); - else { - modes.push(modes.TEXT_EDIT); - if (elem.selectionEnd - elem.selectionStart > 0) - modes.push(modes.VISUAL); - } - - if (hasHTMLDocument(win)) - buffer.lastInputField = elem || win; - return; + let e = elem || win; + if (!(e instanceof Ci.nsIDOMWindow && + DOM(e.document.activeElement).style.MozUserModify != "read-write")) { + if (!haveInput) + if (!isinstance(modes.main, [modes.INPUT, modes.TEXT_EDIT, modes.VISUAL])) + if (options["insertmode"]) + modes.push(modes.INSERT); + else { + modes.push(modes.TEXT_EDIT); + if (elem.selectionEnd - elem.selectionStart > 0) + modes.push(modes.VISUAL); + } + + if (hasHTMLDocument(win)) + buffer.lastInputField = elem || win; + return; + } } if (elem && Events.isInputElement(elem)) { @@ -969,7 +985,7 @@ var Events = Module("events", { }, isInputElement: function isInputElement(elem) { - return DOM(elem).isEditable || + return elem instanceof Ci.nsIDOMElement && DOM(elem).isEditable || isinstance(elem, [Ci.nsIDOMHTMLEmbedElement, Ci.nsIDOMHTMLObjectElement, Ci.nsIDOMHTMLSelectElement]); @@ -1124,12 +1140,12 @@ var Events = Module("events", { "sitemap", "", { flush: function flush() { memoize(this, "filters", function () this.value.filter(function (f) f(buffer.documentURI))); - memoize(this, "pass", function () Set(array.flatten(this.filters.map(function (f) f.keys)))); + memoize(this, "pass", function () RealSet(array.flatten(this.filters.map(function (f) f.keys)))); memoize(this, "commandHive", function hive() Hive(this.filters, "command")); memoize(this, "inputHive", function hive() Hive(this.filters, "input")); }, - has: function (key) Set.has(this.pass, key) || Set.has(this.commandHive.stack.mappings, key), + has: function (key) this.pass.has(key) || hasOwnProperty(this.commandHive.stack.mappings, key), get pass() (this.flush(), this.pass), @@ -1137,9 +1153,9 @@ var Events = Module("events", { let value = parse.superapply(this, arguments); value.forEach(function (filter) { let vals = Option.splitList(filter.result); - filter.keys = DOM.Event.parse(vals[0]).map(DOM.Event.closure.stringify); + filter.keys = DOM.Event.parse(vals[0]).map(DOM.Event.bound.stringify); - filter.commandKeys = vals.slice(1).map(DOM.Event.closure.canonicalKeys); + filter.commandKeys = vals.slice(1).map(DOM.Event.bound.canonicalKeys); filter.inputKeys = filter.commandKeys.filter(bind("test", /^<[ACM]-/)); }); return value; diff --git a/common/content/hints.js b/common/content/hints.js index af09ad4..035f821 100644 --- a/common/content/hints.js +++ b/common/content/hints.js @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -12,7 +12,7 @@ var HintSession = Class("HintSession", CommandMode, { get extendedMode() modes.HINTS, - init: function init(mode, opts = {}) { + init: function init(mode, opts={}) { init.supercall(this); if (!opts.window) @@ -24,7 +24,7 @@ var HintSession = Class("HintSession", CommandMode, { this.activeTimeout = null; // needed for hinttimeout > 0 this.continue = Boolean(opts.continue); this.docs = []; - this.hintKeys = DOM.Event.parse(options["hintkeys"]).map(DOM.Event.closure.stringify); + this.hintKeys = DOM.Event.parse(options["hintkeys"]).map(DOM.Event.bound.stringify); this.hintNumber = 0; this.hintString = opts.filter || ""; this.pageHints = []; @@ -36,8 +36,8 @@ var HintSession = Class("HintSession", CommandMode, { this.open(); this.top = opts.window || content; - this.top.addEventListener("resize", this.closure._onResize, true); - this.top.addEventListener("dactyl-commandupdate", this.closure._onResize, false, true); + this.top.addEventListener("resize", this.bound._onResize, true); + this.top.addEventListener("dactyl-commandupdate", this.bound._onResize, false, true); this.generate(); @@ -101,8 +101,8 @@ var HintSession = Class("HintSession", CommandMode, { if (hints.hintSession == this) hints.hintSession = null; if (this.top) { - this.top.removeEventListener("resize", this.closure._onResize, true); - this.top.removeEventListener("dactyl-commandupdate", this.closure._onResize, true); + this.top.removeEventListener("resize", this.bound._onResize, true); + this.top.removeEventListener("dactyl-commandupdate", this.bound._onResize, true); } this.removeHints(0); @@ -305,6 +305,8 @@ var HintSession = Class("HintSession", CommandMode, { return false; let computedStyle = doc.defaultView.getComputedStyle(elem, null); + if (!computedStyle) + return false; if (computedStyle.visibility != "visible" || computedStyle.display == "none") return false; return true; @@ -332,7 +334,7 @@ var HintSession = Class("HintSession", CommandMode, { __proto__: this.Hint }); - for (let hint in values(_hints)) { + for (let hint of _hints) { let { elem, rect } = hint; if (elem.hasAttributeNS(NS, "hint")) @@ -695,7 +697,7 @@ var HintSession = Class("HintSession", CommandMode, { updateValidNumbers: function updateValidNumbers(always) { let string = this.getHintString(this.hintNumber); for (let hint in values(this.validHints)) - hint.valid = always || hint.span.getAttribute("number").indexOf(string) == 0; + hint.valid = always || hint.span.getAttribute("number").startsWith(string); }, tab: function tab(previous) { @@ -748,19 +750,29 @@ var Hints = Module("hints", { let appContent = document.getElementById("appcontent"); if (appContent) - events.listen(appContent, "scroll", this.resizeTimer.closure.tell, false); + events.listen(appContent, "scroll", this.resizeTimer.bound.tell, false); const Mode = Hints.Mode; Mode.prototype.__defineGetter__("matcher", function () options.get("extendedhinttags").getKey(this.name, options.get("hinttags").matcher)); + function cleanLoc(loc) { + try { + let uri = util.newURI(loc); + if (uri.scheme == "mailto" && !~uri.path.indexOf("?")) + return uri.path; + } + catch (e) {} + return loc; + } + this.modes = {}; - this.addMode(";", "Focus hint", buffer.closure.focusElement); + this.addMode(";", "Focus hint", buffer.bound.focusElement); this.addMode("?", "Show information for hint", elem => buffer.showElementInfo(elem)); // TODO: allow for ! override to overwrite existing paths -- where? --djk this.addMode("s", "Save hint", elem => buffer.saveLink(elem, false)); this.addMode("f", "Focus frame", elem => dactyl.focus(elem.ownerDocument.defaultView)); - this.addMode("F", "Focus frame or pseudo-frame", buffer.closure.focusElement, isScrollable); + this.addMode("F", "Focus frame or pseudo-frame", buffer.bound.focusElement, isScrollable); this.addMode("o", "Follow hint", elem => buffer.followLink(elem, dactyl.CURRENT_TAB)); this.addMode("t", "Follow hint in a new tab", elem => buffer.followLink(elem, dactyl.NEW_TAB)); this.addMode("b", "Follow hint in a background tab", elem => buffer.followLink(elem, dactyl.NEW_BACKGROUND_TAB)); @@ -772,7 +784,7 @@ var Hints = Module("hints", { this.addMode("S", "Add a search keyword", elem => bookmarks.addSearchKeyword(elem)); this.addMode("v", "View hint source", (elem, loc) => buffer.viewSource(loc, false)); this.addMode("V", "View hint source in external editor", (elem, loc) => buffer.viewSource(loc, true)); - this.addMode("y", "Yank hint location", (elem, loc) => editor.setRegister(null, loc, true)); + this.addMode("y", "Yank hint location", (elem, loc) => editor.setRegister(null, cleanLoc(loc), true)); this.addMode("Y", "Yank hint description", elem => editor.setRegister(null, elem.textContent || "", true)); this.addMode("A", "Yank hint anchor url", function (elem) { let uri = elem.ownerDocument.documentURIObject.clone(); @@ -1042,7 +1054,7 @@ var Hints = Module("hints", { let indexOf = String.indexOf; if (options.get("hintmatching").has("transliterated")) - indexOf = Hints.closure.indexOf; + indexOf = Hints.bound.indexOf; switch (options["hintmatching"][0]) { case "contains" : return containsMatcher(hintString); @@ -1054,24 +1066,24 @@ var Hints = Module("hints", { return null; }, //}}} - open: function open(mode, opts = {}) { + open: function open(mode, opts={}) { this._extendedhintCount = opts.count; mappings.pushCommand(); - commandline.input(["Normal", mode], null, { + commandline.input(["Normal", mode], { autocomplete: false, completer: function (context) { context.compare = () => 0; context.completions = [[k, v.prompt] for ([k, v] in Iterator(hints.modes))]; }, - onCancel: mappings.closure.popCommand, + onCancel: mappings.bound.popCommand, onSubmit: function (arg) { if (arg) hints.show(arg, opts); mappings.popCommand(); }, onChange: function (arg) { - if (Object.keys(hints.modes).some(m => m != arg && m.indexOf(arg) == 0)) + if (Object.keys(hints.modes).some(m => m != arg && m.startsWith(arg))) return; this.accepted = true; @@ -1299,23 +1311,29 @@ var Hints = Module("hints", { }, { keepQuotes: true, + getKey: function (val, default_) - let (res = array.nth(this.value, re => let (match = re.exec(val)) match && match[0] == val, 0)) - res ? res.matcher : default_, + let (res = this.value.find(re => let (match = re.exec(val)) match && match[0] == val)) + res ? res.matcher + : default_, + 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(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]),label[for],select,textarea," + + "stringlist", ":-moz-any-link,area,button,iframe,input:not([type=hidden]):not([disabled])," + + "label[for],select,textarea," + "[onclick],[onmouseover],[onmousedown],[onmouseup],[oncommand]," + "[tabindex],[role=link],[role=button],[contenteditable=true]", { @@ -1335,7 +1353,7 @@ var Hints = Module("hints", { "asdfg;lkjh": "Home Row" }, validator: function (value) { - let values = DOM.Event.parse(value).map(DOM.Event.closure.stringify); + let values = DOM.Event.parse(value).map(DOM.Event.bound.stringify); return Option.validIf(array.uniq(values).length === values.length && values.length > 1, _("option.hintkeys.duplicate")); } diff --git a/common/content/history.js b/common/content/history.js index 62aacf1..5b2c6d3 100644 --- a/common/content/history.js +++ b/common/content/history.js @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -13,7 +13,7 @@ var History = Module("history", { get service() services.history, - get: function get(filter, maxItems, sort = this.SORT_DEFAULT) { + get: function get(filter, maxItems, sort=this.SORT_DEFAULT) { if (isString(filter)) filter = { searchTerms: filter }; diff --git a/common/content/key-processors.js b/common/content/key-processors.js index 3059643..d1a51d2 100644 --- a/common/content/key-processors.js +++ b/common/content/key-processors.js @@ -1,4 +1,4 @@ -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -107,8 +107,8 @@ var ProcessorStack = Class("ProcessorStack", { this.timer = services.Timer(this, options["timeoutlen"], services.Timer.TYPE_ONE_SHOT); } else if (result !== Events.KILL && !this.actions.length && - !(this.events[0].isReplay || this.passUnknown - || this.modes.some(function (m) m.passEvent(this), this.events[0]))) { + !(this.events[0].isReplay || this.passUnknown || + this.modes.some(function (m) m.passEvent(this), this.events[0]))) { // No patching processors, this isn't a fake, pass-through // event, we're not in pass-through mode, and we're not // choosing to pass unknown keys. Kill the event and beep. @@ -142,9 +142,9 @@ var ProcessorStack = Class("ProcessorStack", { let list = this.events.filter(e => e.defaultPrevented && !e.dactylDefaultPrevented); if (result === Events.PASS) - events.dbg("PASS THROUGH: " + list.slice(0, length).filter(e => e.type === "keypress").map(DOM.Event.closure.stringify)); + events.dbg("PASS THROUGH: " + list.slice(0, length).filter(e => e.type === "keypress").map(DOM.Event.bound.stringify)); if (list.length > length) - events.dbg("REFEED: " + list.slice(length).filter(e => e.type === "keypress").map(DOM.Event.closure.stringify)); + events.dbg("REFEED: " + list.slice(length).filter(e => e.type === "keypress").map(DOM.Event.bound.stringify)); if (result === Events.PASS) events.feedevents(null, list.slice(0, length), { skipmap: true, isMacro: true, isReplay: true }); diff --git a/common/content/mappings.js b/common/content/mappings.js index fa113b4..5a1d6b2 100644 --- a/common/content/mappings.js +++ b/common/content/mappings.js @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -39,7 +39,7 @@ var Map = Class("Map", { Object.freeze(this.modes); if (info) { - if (Set.has(Map.types, info.type)) + if (hasOwnProperty(Map.types, info.type)) this.update(Map.types[info.type]); this.update(info); } @@ -107,7 +107,7 @@ var Map = Class("Map", { */ hasName: function (name) this.keys.indexOf(name) >= 0, - get keys() array.flatten(this.names.map(mappings.closure.expand)), + get keys() array.flatten(this.names.map(mappings.bound.expand)), /** * Execute the action for this mapping. @@ -171,7 +171,7 @@ var MapHive = Class("MapHive", Contexts.Hive, { * @param {[Modes.Mode]} modes The modes for which to return mappings. */ iterate: function (modes) { - let stacks = Array.concat(modes).map(this.closure.getStack); + let stacks = Array.concat(modes).map(this.bound.getStack); return values(stacks.shift().sort((m1, m2) => String.localeCompare(m1.name, m2.name)) .filter((map) => map.rhs && stacks.every(stack => stack.some(m => m.rhs && m.rhs === map.rhs && m.name === map.name)))); @@ -187,7 +187,7 @@ var MapHive = Class("MapHive", Contexts.Hive, { * @param {Object} extra An optional extra configuration hash. * @optional */ - add: function (modes, keys, description, action, extra = {}) { + add: function (modes, keys, description, action, extra={}) { modes = Array.concat(modes); if (!modes.every(util.identity)) throw TypeError(/*L*/"Invalid modes: " + modes); @@ -380,10 +380,10 @@ var Mappings = Module("mappings", { }, iterate: function (mode) { - let seen = {}; + let seen = RealSet(); for (let hive in this.hives.iterValues()) for (let map in array(hive.getStack(mode)).iterValues()) - if (!Set.add(seen, map.name)) + if (!seen.add(map.name)) yield map; }, @@ -638,10 +638,10 @@ var Mappings = Module("mappings", { } }; function userMappings(hive) { - let seen = {}; + let seen = RealSet(); for (let stack in values(hive.stacks)) for (let map in array.iterValues(stack)) - if (!Set.add(seen, map.id)) + if (!seen.add(map.id)) yield map; } @@ -714,6 +714,7 @@ var Mappings = Module("mappings", { function uniqueModes(modes) { let chars = [k for ([k, v] in Iterator(modules.modes.modeChars)) if (v.every(mode => modes.indexOf(mode) >= 0))]; + return array.uniq(modes.filter(m => chars.indexOf(m.char) < 0) .map(m => m.name.toLowerCase()) .concat(chars)); @@ -748,13 +749,13 @@ var Mappings = Module("mappings", { if (!mainOnly) modes = modes[0].allBases; - let seen = {}; + let seen = RealSet(); // Bloody hell. --Kris for (let [i, mode] in Iterator(modes)) for (let hive in mappings.hives.iterValues()) for (let map in array.iterValues(hive.getStack(mode))) for (let name in values(map.names)) - if (!Set.add(seen, name)) { + if (!seen.add(name)) yield { name: name, columns: [ @@ -765,7 +766,6 @@ var Mappings = Module("mappings", { ], __proto__: map }; - } }, format: { description: function (map) [ @@ -800,7 +800,7 @@ var Mappings = Module("mappings", { name: [mode.char + "listk[eys]", mode.char + "lk"], iterateIndex: function (args) let (self = this, prefix = /^[bCmn]$/.test(mode.char) ? "" : mode.char + "_", - haveTag = Set.has(help.tags)) + haveTag = k => hasOwnProperty(help.tags, k)) ({ helpTag: prefix + map.name, __proto__: map } for (map in self.iterate(args, true)) if (map.hive === mappings.builtin || haveTag(prefix + map.name))), @@ -812,7 +812,7 @@ var Mappings = Module("mappings", { }); }, completion: function initCompletion(dactyl, modules, window) { - completion.userMapping = function userMapping(context, modes_ = [modes.NORMAL], hive = mappings.user) { + completion.userMapping = function userMapping(context, modes_=[modes.NORMAL], hive=mappings.user) { context.keys = { text: function (m) m.names[0], description: function (m) m.description + ": " + m.action }; context.completions = hive.iterate(modes_); diff --git a/common/content/marks.js b/common/content/marks.js index cfa27bf..7f6e256 100644 --- a/common/content/marks.js +++ b/common/content/marks.js @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -34,7 +34,7 @@ var Marks = Module("marks", { get localURI() buffer.focusedFrame.document.documentURI.replace(/#.*/, ""), - Mark: function Mark(params = {}) { + Mark: function Mark(params={}) { let win = buffer.focusedFrame; let doc = win.document; @@ -63,7 +63,8 @@ var Marks = Module("marks", { let mark = this.Mark(); if (Marks.isURLMark(name)) { - mark.tab = util.weakReference(tabs.getTab()); + // FIXME: Disabled due to cross-compartment magic. + // mark.tab = util.weakReference(tabs.getTab()); this._urlMarks.set(name, mark); var message = "mark.addURL"; } @@ -310,7 +311,7 @@ var Marks = Module("marks", { events: function () { let appContent = document.getElementById("appcontent"); if (appContent) - events.listen(appContent, "load", marks.closure._onPageLoad, true); + events.listen(appContent, "load", marks.bound._onPageLoad, true); }, mappings: function () { var myModes = config.browserModes; diff --git a/common/content/modes.js b/common/content/modes.js index 838eab6..d4cd273 100644 --- a/common/content/modes.js +++ b/common/content/modes.js @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -21,13 +21,7 @@ var Modes = Module("modes", { this._recording = false; this._replaying = false; // playing a macro - this._modeStack = update([], { - pop: function pop() { - if (this.length <= 1) - throw Error("Trying to pop last element in mode stack"); - return pop.superapply(this, arguments); - } - }); + this._modeStack = Modes.ModeStack([]); this._modes = []; this._mainModes = []; @@ -262,20 +256,21 @@ var Modes = Module("modes", { // show the current mode string in the command line show: function show() { - if (!loaded.modes) + if (!loaded.has("modes")) return; let msg = this._getModeMessage(); - if (msg || loaded.commandline) + if (msg || loaded.has("commandline")) commandline.widgets.mode = msg || null; }, remove: function remove(mode, covert) { if (covert && this.topOfStack.main != mode) { util.assert(mode != this.NORMAL); - for (let m; m = array.nth(this.modeStack, m => m.main == mode, 0);) - this._modeStack.splice(this._modeStack.indexOf(m)); + + this._modeStack = Modes.ModeStack( + this._modeStack.filter(m => m.main != mode)); } else if (this.stack.some(m => m.main == mode)) { this.pop(mode); @@ -334,10 +329,11 @@ var Modes = Module("modes", { { push: push }, push); for (let [id, { obj, prop, test }] in Iterator(this.boundProperties)) { - if (!obj.get()) + obj = obj.get(); + if (!obj) delete this.boundProperties[id]; else - this.topOfStack.saved[id] = { obj: obj.get(), prop: prop, value: obj.get()[prop], test: test }; + this.topOfStack.saved[id] = { obj: obj, prop: prop, value: obj[prop], test: test }; } } @@ -446,9 +442,11 @@ var Modes = Module("modes", { this.allBases.indexOf(obj) >= 0 || callable(obj) && this instanceof obj, allBases: Class.Memoize(function () { - let seen = {}, res = [], queue = [this].concat(this.bases); + let seen = RealSet(), + res = [], + queue = [this].concat(this.bases); for (let mode in array.iterValues(queue)) - if (!Set.add(seen, mode)) { + if (!seen.add(mode)) { res.push(mode); queue.push.apply(queue, mode.bases); } @@ -485,13 +483,21 @@ var Modes = Module("modes", { }, { _id: 0 }), + ModeStack: function ModeStack(array) + update(array, { + pop: function pop() { + if (this.length <= 1) + throw Error("Trying to pop last element in mode stack"); + return pop.superapply(this, arguments); + } + }), StackElement: (function () { const StackElement = Struct("main", "extended", "params", "saved"); StackElement.className = "Modes.StackElement"; StackElement.defaultValue("params", function () this.main.params); update(StackElement.prototype, { - get toStringParams() !loaded.modes ? this.main.name : [ + get toStringParams() !loaded.has("modes") ? [this.main.name] : [ this.main.name, ["(", modes.all.filter(m => this.extended & m) .map(m => m.name) @@ -502,7 +508,7 @@ var Modes = Module("modes", { return StackElement; })(), cacheId: 0, - boundProperty: function BoundProperty(desc = {}) { + boundProperty: function BoundProperty(desc={}) { let id = this.cacheId++; let value; @@ -560,9 +566,10 @@ var Modes = Module("modes", { return rec(roots); } - cache.register("modes.dtd", () => - util.makeDTD(iter({ "modes.tree": makeTree() }, - config.dtd))); + cache.register("modes.dtd", + () => util.makeDTD(iter({ "modes.tree": makeTree() }, + config.dtd)), + true); }, mappings: function initMappings() { mappings.add([modes.BASE, modes.NORMAL], @@ -601,10 +608,10 @@ var Modes = Module("modes", { getKey: function getKey(val, default_) { if (isArray(val)) - return (array.nth(this.value, v => val.some(m => m.name === v.mode), 0) + return (this.value.find(v => val.some(m => m.name === v.mode)) || { result: default_ }).result; - return Set.has(this.valueMap, val) ? this.valueMap[val] : default_; + return hasOwnProperty(this.valueMap, val) ? this.valueMap[val] : default_; }, setter: function (vals) { @@ -622,9 +629,10 @@ var Modes = Module("modes", { }, validator: function validator(vals) vals.map(v => v.replace(/^!/, "")) - .every(Set.has(this.values)), + .every(k => hasOwnProperty(this.values, k)), - get values() array.toObject([[m.name.toLowerCase(), m.description] for (m in values(modes._modes)) if (!m.hidden)]) + get values() array.toObject([[m.name.toLowerCase(), m.description] + for (m in values(modes._modes)) if (!m.hidden)]) }; options.add(["passunknown", "pu"], diff --git a/common/content/mow.js b/common/content/mow.js index 7149f9c..e2ad98f 100644 --- a/common/content/mow.js +++ b/common/content/mow.js @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -90,14 +90,14 @@ var MOW = Module("mow", { if (modes.main != modes.OUTPUT_MULTILINE) { modes.push(modes.OUTPUT_MULTILINE, null, { - onKeyPress: this.closure.onKeyPress, + onKeyPress: this.bound.onKeyPress, - leave: this.closure(function leave(stack) { + leave: stack => { if (stack.pop) for (let message in values(this.messages)) if (message.leave) message.leave(stack); - }), + }, window: this.window }); diff --git a/common/content/statusline.js b/common/content/statusline.js index 7b0cd48..caf0390 100644 --- a/common/content/statusline.js +++ b/common/content/statusline.js @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -12,32 +12,55 @@ var StatusLine = Module("statusline", { init: function init() { this._statusLine = document.getElementById("status-bar"); this.statusBar = document.getElementById("addon-bar") || this._statusLine; + this.baseGroup = this.statusBar == this._statusLine ? "StatusLine " : ""; + if (this.statusBar.localName == "toolbar" && + this.statusBar.parentNode.id != "browser-bottombox") + overlay.overlayWindow(window, { + objects: this, + append: [ + ["vbox", { id: "browser-bottombox", xmlns: "xul" }, + ["toolbar", { id: "dactyl-addon-bar", + customizable: true, + defaultset: "", + toolboxid: "navigator-toolbox", + toolbarname: /*L*/ "Add-on Bar", + class: "toolbar-primary chromeclass-toolbar", + mode: "icons", + iconsize: "small", defaulticonsize: "small", + key: "statusBar" }, + ["statusbar", { id: "dactyl-status-bar", key: "_statusLine" }]]] + ] + }); + if (config.haveGecko("25")) config.tabbrowser.getStatusPanel().hidden = true; if (this.statusBar.localName == "toolbar") { styles.system.add("addon-bar", config.styleableChrome, literal(/* - #status-bar { margin-top: 0 !important; } - #addon-bar > statusbar { -moz-box-flex: 1 } + #status-bar, #dactyl-status-bar { margin-top: 0 !important; } + #dactyl-status-bar { min-height: 0 !important; } + :-moz-any(#addon-bar, #dactyl-addon-bar) > statusbar { -moz-box-flex: 1 } + :-moz-any(#addon-bar, #dactyl-addon-bar) > xul|toolbarspring { visibility: collapse; } #addon-bar > #addonbar-closebutton { visibility: collapse; } - #addon-bar > xul|toolbarspring { visibility: collapse; } */)); overlay.overlayWindow(window, { append: [ - ["statusbar", { id: "status-bar", ordinal: "0" }]] + ["statusbar", { id: this._statusLine.id, ordinal: "0" }]] }); highlight.loadCSS(util.compileMacro(literal(/* - !AddonBar;#addon-bar { + !AddonBar;#addon-bar,#dactyl-addon-bar { padding-left: 0 !important; + padding-top: 0 !important; + padding-bottom: 0 !important; min-height: 18px !important; -moz-appearance: none !important; } - !AddonButton;#addon-bar xul|toolbarbutton { + !AddonButton;,:-moz-any(#addon-bar, #dactyl-addon-bar) xul|toolbarbutton { -moz-appearance: none !important; padding: 0 !important; border-width: 0px !important; @@ -57,7 +80,7 @@ var StatusLine = Module("statusline", { let prepend = [ ["button", { id: "appmenu-button", label: "", image: "chrome://branding/content/icon16.png", highlight: "AppmenuButton", xmlns: "xul" }], ["toolbarbutton", { id: "appmenu-toolbar-button", label: "", image: "chrome://branding/content/icon16.png" }], - ["statusbar", { id: "status-bar", highlight: "StatusLine", xmlns: "xul" }, + ["statusbar", { id: this._statusLine.id, highlight: "StatusLine", xmlns: "xul" }, // ["hbox", { key: "container", hidden: "false", align: "center", flex: "1" }, ["stack", { orient: "horizontal", align: "stretch", flex: "1", highlight: "CmdLine StatusCmdLine", class: "dactyl-container" }, @@ -99,6 +122,11 @@ var StatusLine = Module("statusline", { catch (e) {} }, + cleanup: function cleanup(reason) { + if (reason != "unload" && "CustomizableUI" in window) + CustomizableUI.unregisterArea(this.statusBar.id, false); + }, + get visible() !this.statusBar.collapsed && !this.statusBar.hidden, signals: { diff --git a/common/content/tabs.js b/common/content/tabs.js index a7f038e..9bf588b 100644 --- a/common/content/tabs.js +++ b/common/content/tabs.js @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -27,7 +27,8 @@ var Tabs = Module("tabs", { config.tabStrip.collapsed = true; this.tabStyle = styles.system.add("tab-strip-hiding", config.styleableChrome, - (config.tabStrip.id ? "#" + config.tabStrip.id : ".tabbrowser-strip") + + (config.tabStrip.id ? "#" + config.tabStrip.id + : ".tabbrowser-strip") + "{ visibility: collapse; }", false, true); @@ -73,28 +74,26 @@ var Tabs = Module("tabs", { updateTabCount: function updateTabCount() { for (let [i, tab] in Iterator(this.visibleTabs)) { - if (dactyl.has("Gecko2")) { - let node = function node(class_) document.getAnonymousElementByAttribute(tab, "class", class_); - if (!node("dactyl-tab-number")) { - let img = node("tab-icon-image"); - if (img) { - let dom = DOM([ - ["xul:hbox", { highlight: "tab-number" }, - ["xul:label", { key: "icon", align: "center", highlight: "TabIconNumber", - class: "dactyl-tab-icon-number" }]], - ["xul:hbox", { highlight: "tab-number" }, - ["html:div", { key: "label", highlight: "TabNumber", - class: "dactyl-tab-number" }]]], - document).appendTo(img.parentNode); - - update(tab, { - get dactylOrdinal() Number(dom.nodes.icon.value), - set dactylOrdinal(i) { - dom.nodes.icon.value = dom.nodes.label.textContent = i; - this.setAttribute("dactylOrdinal", i); - } - }); - } + let node = function node(class_) document.getAnonymousElementByAttribute(tab, "class", class_); + if (!node("dactyl-tab-number")) { + let img = node("tab-icon-image"); + if (img) { + let dom = DOM([ + ["xul:hbox", { highlight: "tab-number" }, + ["xul:label", { key: "icon", align: "center", highlight: "TabIconNumber", + class: "dactyl-tab-icon-number" }]], + ["xul:hbox", { highlight: "tab-number" }, + ["html:div", { key: "label", highlight: "TabNumber", + class: "dactyl-tab-number" }]]], + document).appendTo(img.parentNode); + + update(tab, { + get dactylOrdinal() Number(dom.nodes.icon.value), + set dactylOrdinal(i) { + dom.nodes.icon.value = dom.nodes.label.textContent = i; + this.setAttribute("dactylOrdinal", i); + } + }); } } tab.dactylOrdinal = i + 1; @@ -408,7 +407,7 @@ var Tabs = Module("tabs", { * @param {number} count How many tabs to remove. * @param {boolean} focusLeftTab Focus the tab to the left of the removed tab. */ - remove: function remove(tab, count = 1, focusLeftTab = false) { + remove: function remove(tab, count=1, focusLeftTab=false) { let res = this.count > count; let tabs = this.visibleTabs; @@ -427,11 +426,11 @@ var Tabs = Module("tabs", { if (focusLeftTab) tabs.slice(Math.max(0, index + 1 - count), index + 1) - .forEach(config.closure.removeTab); + .forEach(config.bound.removeTab); else tabs.slice(index, index + count) - .forEach(config.closure.removeTab); + .forEach(config.bound.removeTab); return res; }, @@ -551,7 +550,7 @@ var Tabs = Module("tabs", { if (matches) return tabs.select(this.allTabs[parseInt(matches[1], 10) - 1], false); - matches = array.nth(tabs.allTabs, t => (t.linkedBrowser.lastURI || {}).spec === buffer, 0); + matches = tabs.allTabs.find(t => (t.linkedBrowser.lastURI || {}).spec === buffer); if (matches) return tabs.select(matches, false); @@ -1038,7 +1037,7 @@ var Tabs = Module("tabs", { tabs.getGroups(); tabs[visible ? "visibleTabs" : "allTabs"].forEach(function (tab, i) { let group = (tab.tabItem || tab._tabViewTabItem || defItem).parent || defItem.parent; - if (!Set.has(tabGroups, group.id)) + if (!hasOwnProperty(tabGroups, group.id)) tabGroups[group.id] = [group.getTitle(), []]; group = tabGroups[group.id]; @@ -1114,7 +1113,7 @@ var Tabs = Module("tabs", { } for (let event in values(["TabMove", "TabOpen", "TabClose"])) events.listen(tabContainer, event, callback, false); - events.listen(tabContainer, "TabSelect", tabs.closure._onTabSelect, false); + events.listen(tabContainer, "TabSelect", tabs.bound._onTabSelect, false); }, mappings: function initMappings() { @@ -1224,7 +1223,7 @@ var Tabs = Module("tabs", { tabs.tabStyle.enabled = false; } - if (value !== "multitab" || !dactyl.has("Gecko2")) + if (value !== "multitab") if (tabs.xulTabs) tabs.xulTabs.visible = value !== "never"; else @@ -1263,11 +1262,11 @@ var Tabs = Module("tabs", { values: activateGroups, has: Option.has.toggleAll, setter: function (newValues) { - let valueSet = Set(newValues); + let valueSet = RealSet(newValues); for (let group in values(activateGroups)) if (group[2]) prefs.safeSet("browser.tabs." + group[2], - !(valueSet["all"] ^ valueSet[group[0]]), + !(valueSet.has("all") ^ valueSet.has(group[0])), _("option.safeSet", "activate")); return newValues; } diff --git a/common/locale/en-US/messages.properties b/common/locale/en-US/messages.properties index fc69e8f..da27f0b 100644 --- a/common/locale/en-US/messages.properties +++ b/common/locale/en-US/messages.properties @@ -162,13 +162,11 @@ download.nActive-1 = %S active download.almostDone = ~1 second download.unknown = Unknown -download.action.Cancel = Cancel download.action.Clear = Clear download.action.Delete = Delete -download.action.Pause = Pause +download.action.Stop = Stop download.action.Remove = Remove download.action.Resume = Resume -download.action.Retry = Retry editor.prompt.editPassword = Editing a password field externally will reveal the password. Would you like to continue? (yes/[no]): diff --git a/common/locale/en-US/options.xml b/common/locale/en-US/options.xml index eed009c..64de860 100644 --- a/common/locale/en-US/options.xml +++ b/common/locale/en-US/options.xml @@ -596,6 +596,9 @@
time
Time remaining
url
Source URL
+ + This option is currently unavailable in &dactyl.host; 26 and greater. + diff --git a/common/locale/en-US/print.xml b/common/locale/en-US/print.xml index bd2ec45..f527f95 100644 --- a/common/locale/en-US/print.xml +++ b/common/locale/en-US/print.xml @@ -28,8 +28,6 @@ :hardcopy! >filename

As above, but write the output to filename.

- - Not available on Windows.
diff --git a/common/locale/en-US/repeat.xml b/common/locale/en-US/repeat.xml index 3aaaa4d..b4cf848 100644 --- a/common/locale/en-US/repeat.xml +++ b/common/locale/en-US/repeat.xml @@ -46,11 +46,11 @@ <record-macro> q - q0-9a-zA-Z + qa-zA-Z

Record a key sequence as a macro. Available macros are - 0-9a-zA-Z. If the macro is an uppercase letter, the + a-zA-Z. If the macro is an uppercase letter, the recorded keys are appended to the lowercase macro of the same name. Typing q again stops the recording.

@@ -82,10 +82,10 @@ <play-macro> @ - count@a-z0-9 + count@a-z

- Plays the contents of macro with name a-z0-9 count + Plays the contents of macro with name a-z count times.

diff --git a/common/modules/addons.jsm b/common/modules/addons.jsm index fb1e596..b42db8d 100644 --- a/common/modules/addons.jsm +++ b/common/modules/addons.jsm @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2013 Kris Maglione +// Copyright (c) 2009-2014 Kris Maglione // Copyright (c) 2009-2010 by Doug Kearns // // This work is licensed for reuse under an MIT license. Details are @@ -110,7 +110,6 @@ var actions = { name: "extr[ehash]", description: "Reload an extension", action: function (addon) { - util.assert(config.haveGecko("2b"), _("command.notUseful", config.host)); util.flushCache(); util.timeout(function () { addon.userDisabled = true; @@ -165,7 +164,8 @@ var Addon = Class("Addon", { }, commandAllowed: function commandAllowed(cmd) { - util.assert(Set.has(actions, cmd), _("addon.unknownCommand")); + util.assert(hasOwnProperty(actions, cmd), + _("addon.unknownCommand")); let action = actions[cmd]; if ("perm" in action && !(this.permissions & AddonManager["PERM_CAN_" + action.perm.toUpperCase()])) @@ -258,14 +258,14 @@ var AddonList = Class("AddonList", { this.modules = modules; this.filter = filter && filter.toLowerCase(); this.nodes = {}; - this.addons = []; + this.addons = {}; this.ready = false; - AddonManager.getAddonsByTypes(types, this.closure(function (addons) { + AddonManager.getAddonsByTypes(types, addons => { this._addons = addons; if (this.document) this._init(); - })); + }); AddonManager.addAddonListener(this); }, cleanup: function cleanup() { @@ -273,7 +273,7 @@ var AddonList = Class("AddonList", { }, _init: function _init() { - this._addons.forEach(this.closure.addAddon); + this._addons.forEach(this.bound.addAddon); this.ready = true; this.update(); }, diff --git a/common/modules/base.jsm b/common/modules/base.jsm index e0f0eaa..b581186 100644 --- a/common/modules/base.jsm +++ b/common/modules/base.jsm @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2013 Kris Maglione +// Copyright (c) 2009-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -6,10 +6,19 @@ var { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components; -Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); +var Cs = new Proxy(Components.stack, { + get: function Cs_get(target, prop) Components.stack.caller[prop] +}); + +function module(url) { + let obj = {}; + Cu.import(url, obj); + return obj; +} + +var { XPCOMUtils } = module("resource://gre/modules/XPCOMUtils.jsm"); try { - var ctypes; - Cu.import("resource://gre/modules/ctypes.jsm"); + var ctypes = module("resource://gre/modules/ctypes.jsm"); } catch (e) {} @@ -17,15 +26,41 @@ let objproto = Object.prototype; let { __lookupGetter__, __lookupSetter__, __defineGetter__, __defineSetter__, hasOwnProperty, propertyIsEnumerable } = objproto; -if (typeof XPCSafeJSObjectWrapper === "undefined") - this.XPCSafeJSObjectWrapper = XPCNativeWrapper; +hasOwnProperty = Function.call.bind(hasOwnProperty); +propertyIsEnumerable = Function.call.bind(propertyIsEnumerable); -let getGlobalForObject = Cu.getGlobalForObject || (obj => obj.__parent__); +// Gecko 24. +if (!("find" in Array.prototype)) + Object.defineProperty(Array.prototype, "find", { + configurable: true, + writable: true, + value: function Array_find(pred, self) { + for (let [i, elem] in Iterator(this)) + if (pred.call(self, elem, i, this)) + return elem; + } + }); -function require(module, target) JSMLoader.load(module, target); +if (!("findIndex" in Array.prototype)) + Object.defineProperty(Array.prototype, "findIndex", { + configurable: true, + writable: true, + value: function Array_findIndex(pred, self) { + for (let [i, elem] in Iterator(this)) + if (pred.call(self, elem, i, this)) + return i; + return -1; + } + }); + +function require(module_, target) { + if (/^[A-Za-z0-9]+:/.test(module_)) + return module(module_); + return JSMLoader.load(module_, target); +} function lazyRequire(module, names, target) { - for each (let name in names) + for (let name of names) memoize(target || this, name, name => require(module)[name]); } @@ -133,41 +168,78 @@ function require_(obj, name, from, targetName) { defineModule("base", { // sed -n 's/^(const|var|function) ([a-zA-Z0-9_]+).*/ "\2",/p' base.jsm | sort | fmt exports: [ - "ErrorBase", "Cc", "Ci", "Class", "Cr", "Cu", "Finished", "Module", "JSMLoader", - "Set", "Struct", "StructBase", "Timer", "UTF8", "XPCOM", "XPCOMShim", "XPCOMUtils", - "XPCSafeJSObjectWrapper", "array", "bind", "call", "callable", "ctypes", "curry", - "debuggerProperties", "defineModule", "deprecated", "endModule", "forEach", "isArray", - "isGenerator", "isinstance", "isObject", "isString", "isSubclass", "isXML", "iter", - "iterAll", "iterOwnProperties", "keys", "literal", "memoize", "octal", "properties", - "require", "set", "update", "values", "update_" + "Cc", + "Ci", + "Class", + "Cr", + "Cs", + "Cu", + "ErrorBase", + "Finished", + "JSMLoader", + "Module", + "RealSet", + "Set", + "Struct", + "StructBase", + "Timer", + "UTF8", + "XPCOM", + "XPCOMShim", + "XPCOMUtils", + "array", + "bind", + "call", + "callable", + "ctypes", + "curry", + "defineModule", + "deprecated", + "endModule", + "forEach", + "hasOwnProperty", + "isArray", + "isGenerator", + "isObject", + "isString", + "isSubclass", + "isinstance", + "iter", + "iterAll", + "iterOwnProperties", + "keys", + "literal", + "memoize", + "modujle", + "octal", + "properties", + "require", + "set", + "update", + "values", ] }); this.lazyRequire("cache", ["cache"]); this.lazyRequire("config", ["config"]); this.lazyRequire("messages", ["_", "Messages"]); +this.lazyRequire("promises", ["Task", "promises"]); this.lazyRequire("services", ["services"]); this.lazyRequire("storage", ["File"]); this.lazyRequire("util", ["FailedAssertion", "util"]); +literal.files = {}; +literal.locations = {}; function literal(/* comment */) { let { caller } = Components.stack; while (caller && caller.language != 2) caller = caller.caller; let file = caller.filename.replace(/.* -> /, ""); - let key = "literal:" + file + ":" + caller.line; - - let source = File.readURL(file); - - let match = RegExp("(?:.*\\n){" + (caller.lineNumber - 1) + "}" + - ".*literal\\(/\\*([^]*?)\\*/\\)").exec(source); - return match[1]; - - // Later... - return cache.get(key, function () { - let source = cache.get("literal:" + file, - () => util.httpGet(file).responseText); + let key = "literal:" + file + ":" + caller.lineNumber; + return cache.get(key, function() { + let source = literal.files[file] || File.readURL(file); + literal.files[file] = source; let match = RegExp("(?:.*\\n){" + (caller.lineNumber - 1) + "}" + ".*literal\\(/\\*([^]*?)\\*/\\)").exec(source); @@ -175,25 +247,10 @@ function literal(/* comment */) { }); } -/** - * Returns a list of all of the top-level properties of an object, by - * way of the debugger. - * - * @param {object} obj - * @returns [jsdIProperty] - */ -function debuggerProperties(obj) { - if (loaded.services && services.debugger.isOn) { - let res = {}; - services.debugger.wrapValue(obj).getProperties(res, {}); - return res.value; - } -} - /** * Iterates over the names of all of the top-level properties of an * object or, if prototypes is given, all of the properties in the - * prototype chain below the top. Uses the debugger if possible. + * prototype chain below the top. * * @param {object} obj The object to inspect. * @param {boolean} properties Whether to inspect the prototype chain @@ -205,14 +262,14 @@ function prototype(obj) XPCNativeWrapper.unwrap(obj).__proto__ || Object.getPrototypeOf(XPCNativeWrapper.unwrap(obj)); -function properties(obj, prototypes, debugger_) { +function properties(obj, prototypes) { let orig = obj; - let seen = { dactylPropertyNames: true }; + let seen = RealSet(["dactylPropertyNames"]); try { if ("dactylPropertyNames" in obj && !prototypes) for (let key in values(obj.dactylPropertyNames)) - if (key in obj && !Set.add(seen, key)) + if (key in obj && !seen.add(key)) yield key; } catch (e) {} @@ -250,16 +307,8 @@ function properties(obj, prototypes, debugger_) { } for (; obj; obj = prototypes && prototype(obj)) { - try { - if (!debugger_ || !services.debugger.isOn) - var iter = (v for each (v in props(obj))); - } - catch (e) {} - if (!iter) - iter = (prop.name.stringValue for (prop in values(debuggerProperties(obj)))); - - for (let key in iter) - if (!prototypes || !Set.add(seen, key) && obj != orig) + for (let key of props(obj)) + if (!prototypes || !seen.add(key) && obj != orig) yield key; } } @@ -271,10 +320,14 @@ function iterOwnProperties(obj) { function deprecated(alternative, fn) { if (isObject(fn)) - return Class.Property(iter(fn).map(([k, v]) => [k, callable(v) ? deprecated(alternative, v) : v]) + return Class.Property(iter(fn).map(([k, v]) => [k, + callable(v) ? deprecated(alternative, v) + : v]) .toObject()); - let name, func = callable(fn) ? fn : function () this[fn].apply(this, arguments); + let name, + func = callable(fn) ? fn + : function () this[fn].apply(this, arguments); function deprecatedMethod() { let obj = !this ? "" : @@ -282,9 +335,13 @@ function deprecated(alternative, fn) { this.constructor.className ? this.constructor.className + "#" : ""; - deprecated.warn(func, obj + (fn.name || name), alternative); + deprecated.warn(func, + obj + (fn.realName || fn.name || name || "").replace(/__/g, "."), + alternative); return func.apply(this, arguments); } + if (func.name) + deprecatedMethod.realName = func.name; return callable(fn) ? deprecatedMethod : Class.Property({ get: function () deprecatedMethod, @@ -293,14 +350,20 @@ function deprecated(alternative, fn) { } deprecated.warn = function warn(func, name, alternative, frame) { if (!func.seenCaller) - func.seenCaller = Set([ + func.seenCaller = RealSet([ "resource://dactyl/javascript.jsm", "resource://dactyl/util.jsm" ]); + if (!(loaded.util && util && loaded.config && config.protocolLoaded)) { + dump("DACTYL: deprecated method called too early [" + [name, alternative] + "]:\n" + Error().stack + "\n\n"); + return; + } + frame = frame || Components.stack.caller.caller; + let filename = util.fixURI(frame.filename || "unknown"); - if (!Set.add(func.seenCaller, filename)) + if (!func.seenCaller.add(filename)) util.dactyl(func).warn([util.urlPath(filename), frame.lineNumber, " "].join(":") + _("warn.deprecated", name, alternative)); } @@ -313,9 +376,13 @@ deprecated.warn = function warn(func, name, alternative, frame) { * @returns {Generator} */ function keys(obj) iter(function keys() { - for (var k in obj) - if (hasOwnProperty.call(obj, k)) + if (isinstance(obj, ["Map"])) + for (let [k, v] of obj) yield k; + else + for (var k in obj) + if (hasOwnProperty(obj, k)) + yield k; }()); /** @@ -326,18 +393,47 @@ function keys(obj) iter(function keys() { * @returns {Generator} */ function values(obj) iter(function values() { - if (isinstance(obj, ["Generator", "Iterator", Iter])) + if (isinstance(obj, ["Map"])) + for (let [k, v] of obj) + yield v; + else if (isinstance(obj, ["Generator", "Iterator", Iter])) for (let k in obj) yield k; + else if (iter.iteratorProp in obj) + for (let v of obj) + yield v; else for (var k in obj) - if (hasOwnProperty.call(obj, k)) + if (hasOwnProperty(obj, k)) yield obj[k]; }()); var forEach = deprecated("iter.forEach", function forEach() iter.forEach.apply(iter, arguments)); var iterAll = deprecated("iter", function iterAll() iter.apply(null, arguments)); +var RealSet = Set; +let Set_add = RealSet.prototype.add; +RealSet.prototype.add = function RealSet_add(val) { + let res = this.has(val); + Set_add.apply(this, arguments); + return res; +}; + +RealSet.prototype.difference = function RealSet_difference(set) { + return RealSet(i for (i of this) if (!set.has(i))); +}; + +RealSet.prototype.intersection = function RealSet_intersection(set) { + return RealSet(i for (i of this) if (set.has(i))); +}; + +RealSet.prototype.union = function RealSet_union(set) { + let res = RealSet(this); + for (let item of set) + res.add(item); + return res; +}; + /** * Utility for managing sets of strings. Given an array, returns an * object with one key for each value thereof. @@ -345,13 +441,13 @@ var iterAll = deprecated("iter", function iterAll() iter.apply(null, arguments)) * @param {[string]} ary @optional * @returns {object} */ -function Set(ary) { +this.Set = deprecated("RealSet", function Set(ary) { let obj = {}; if (ary) for (let val in values(ary)) obj[val] = true; return obj; -} +}); /** * Adds an element to a set and returns true if the element was * previously contained. @@ -360,11 +456,18 @@ function Set(ary) { * @param {string} key The key to add. * @returns boolean */ -Set.add = curry(function set_add(set, key) { - let res = this.has(set, key); - set[key] = true; - return res; -}); +Set.add = deprecated("RealSet#add", + curry(function Set__add(set, key) { + if (isinstance(set, ["Set"])) { + let res = set.has(key); + set.add(key); + return res; + } + + let res = this.has(set, key); + set[key] = true; + return res; + })); /** * Returns true if the given set contains the given key. * @@ -372,8 +475,14 @@ Set.add = curry(function set_add(set, key) { * @param {string} key The key to check. * @returns {boolean} */ -Set.has = curry(function set_has(set, key) hasOwnProperty.call(set, key) && - propertyIsEnumerable.call(set, key)); +Set.has = deprecated("hasOwnProperty or Set#has", + curry(function Set__has(set, key) { + if (isinstance(set, ["Set"])) + return set.has(key); + + return hasOwnProperty(set, key) && + propertyIsEnumerable(set, key); + })); /** * Returns a new set containing the members of the first argument which * do not exist in any of the other given arguments. @@ -381,13 +490,15 @@ Set.has = curry(function set_has(set, key) hasOwnProperty.call(set, key) && * @param {object} set The set. * @returns {object} */ -Set.subtract = function set_subtract(set) { - set = update({}, set); - for (let i = 1; i < arguments.length; i++) - for (let k in keys(arguments[i])) - delete set[k]; - return set; -}; +Set.subtract = deprecated("RealSet#difference", + function set_subtract(set) { + set = update({}, set); + for (let i = 1; i < arguments.length; i++) + for (let k in keys(arguments[i])) + delete set[k]; + return set; + }); + /** * Removes an element from a set and returns true if the element was * previously contained. @@ -396,11 +507,15 @@ Set.subtract = function set_subtract(set) { * @param {string} key The key to remove. * @returns boolean */ -Set.remove = curry(function set_remove(set, key) { - let res = set.has(set, key); - delete set[key]; - return res; -}); +Set.remove = deprecated("RealSet#delete", + curry(function Set__remove(set, key) { + if (isinstance(set, ["Set"])) + return set.delete(key); + + let res = set.has(set, key); + delete set[key]; + return res; + })); function set() { deprecated.warn(set, "set", "Set"); @@ -446,7 +561,7 @@ function curry(fn, length, self, acc) { if (acc == null) acc = []; - return function curried(...args) { + function curried(...args) { // The curried result should preserve 'this' if (args.length == 0) return close(self || this, curried); @@ -458,18 +573,13 @@ function curry(fn, length, self, acc) { return curry(fn, length, self || this, args); }; + curried.realName = fn.realName || fn.name; + return curried; } -if (curry.bind) - var bind = function bind(meth, self, ...args) let (func = callable(meth) ? meth : self[meth]) +var bind = function bind(meth, self, ...args) + let (func = callable(meth) ? meth : self[meth]) func.bind.apply(func, [self].concat(args)); -else - var bind = function bind(func, self, ...args) { - if (!callable(func)) - func = self[func]; - - return function bound(...args2) func.apply(self, args.concat(args2)); - }; /** * Returns true if both arguments are functions and @@ -532,12 +642,6 @@ function isinstance(object, interfaces) { */ function isObject(obj) typeof obj === "object" && obj != null || obj instanceof Ci.nsISupports; -/** - * Returns true if obje is an E4X XML object. - * @deprecated - */ -function isXML(obj) typeof obj === "xml"; - /** * Returns true if and only if its sole argument is an * instance of the builtin Array type. The array may come from @@ -545,10 +649,8 @@ function isXML(obj) typeof obj === "xml"; * is not the case when using (obj instanceof Array). */ var isArray = - Array.isArray - // This is bloody stupid. - ? function isArray(val) Array.isArray(val) || val && val.constructor && val.constructor.name === "Array" - : function isArray(val) objproto.toString.call(val) == "[object Array]"; + // This is bloody stupid. + function isArray(val) Array.isArray(val) || val && val.constructor && val.constructor.name === "Array"; /** * Returns true if and only if its sole argument is an @@ -589,7 +691,7 @@ function call(fn, self, ...args) { function memoize(obj, key, getter) { if (arguments.length == 1) { let res = update(Object.create(obj), obj); - for each (let prop in Object.getOwnPropertyNames(obj)) { + for (let prop of Object.getOwnPropertyNames(obj)) { let get = __lookupGetter__.call(obj, prop); if (get) memoize(res, prop, get); @@ -670,33 +772,6 @@ function update(target) { } return target; } -function update_(target) { - for (let i = 1; i < arguments.length; i++) { - let src = arguments[i]; - Object.getOwnPropertyNames(src || {}).forEach(function (k) { - let desc = Object.getOwnPropertyDescriptor(src, k); - if (desc.value instanceof Class.Property) - desc = desc.value.init(k, target) || desc.value; - - try { - if (typeof desc.value === "function" && target.__proto__ && !(desc.value instanceof Ci.nsIDOMElement /* wtf? */)) { - let func = desc.value.wrapped || desc.value; - if (!func.superapply) { - func.__defineGetter__("super", function get_super_() Object.getPrototypeOf(target)[k]); - func.superapply = function super_apply(self, args) - let (meth = Object.getPrototypeOf(target)[k]) - meth && meth.apply(self, args); - func.supercall = function super_call(self, ...args) - func.superapply(self, args); - } - } - Object.defineProperty(target, k, desc); - } - catch (e) {} - }); - } - return target; -} /** * @constructor Class @@ -729,34 +804,23 @@ function Class(...args) { if (callable(args[0])) superclass = args.shift(); - if (loaded.config && (config.haveGecko("5.*", "6.0") || config.haveGecko("6.*"))) // Bug 657418. - var Constructor = function Constructor() { - var self = Object.create(Constructor.prototype); - self.instance = self; - self.globalInstance = self; - - if ("_metaInit_" in self && self._metaInit_) - self._metaInit_.apply(self, arguments); - - var res = self.init.apply(self, arguments); - return res !== undefined ? res : self; - }; - else - var Constructor = eval(String.replace('\n\ - (function constructor(PARAMS) { \n\ - var self = Object.create(Constructor.prototype); \n\ - self.instance = self; \n\ - self.globalInstance = self; \n\ - \n\ - if ("_metaInit_" in self && self._metaInit_) \n\ - self._metaInit_.apply(self, arguments); \n\ - \n\ - var res = self.init.apply(self, arguments); \n\ - return res !== undefined ? res : self; \n\ - })', - "constructor", (name || superclass.className).replace(/\W/g, "_")) - .replace("PARAMS", /^function .*?\((.*?)\)/.exec(args[0] && args[0].init || Class.prototype.init)[1] - .replace(/\b(self|res|Constructor)\b/g, "$1_"))); + var Constructor = eval(String.replace('\n\ + (function constructor(PARAMS) { \n\ + var self = Object.create(Constructor.prototype); \n\ + self.instance = self; \n\ + self.globalInstance = self; \n\ + \n\ + if ("_metaInit_" in self && self._metaInit_) \n\ + self._metaInit_.apply(self, arguments); \n\ + \n\ + var res = self.init.apply(self, arguments); \n\ + return res !== undefined ? res : self; \n\ + })', + "constructor", (name || superclass.className).replace(/\W/g, "_")) + .replace("PARAMS", + /^function .*?\((.*?)\)/ + .exec(args[0] && args[0].init || Class.prototype.init)[1] + .replace(/\b(self|res|Constructor)\b/g, "$1_"))); Constructor.className = name || superclass.className || superclass.name; @@ -772,7 +836,10 @@ function Class(...args) { } Class.extend(Constructor, superclass, args[0]); - memoize(Constructor, "closure", Class.makeClosure); + memoize(Constructor, "bound", Class.makeClosure); + if (Iter && array) // Hack. :/ + Object.defineProperty(Constructor, "closure", + deprecated("bound", { get: function closure() this.bound })); update(Constructor, args[1]); Constructor.__proto__ = superclass; @@ -785,21 +852,14 @@ function Class(...args) { return Constructor; } -if (Cu.getGlobalForObject) - Class.objectGlobal = function (object) { - try { - return Cu.getGlobalForObject(object); - } - catch (e) { - return null; - } - }; -else - Class.objectGlobal = function (object) { - while (object.__parent__) - object = object.__parent__; - return object; - }; +Class.objectGlobal = function (object) { + try { + return Cu.getGlobalForObject(object); + } + catch (e) { + return null; + } +}; /** * @class Class.Property @@ -863,16 +923,16 @@ Class.Memoize = function Memoize(getter, wait) } }); - util.yieldable(function () { + Task.spawn(function () { let wait; for (var res in getter.call(obj)) { if (wait !== undefined) - yield wait; + yield promises.sleep(wait); wait = res; } Class.replaceProperty(obj, key, res); done = true; - })(); + }); return this[key]; }; @@ -956,8 +1016,21 @@ Class.prototype = { util.rehashing && !isinstance(Cu.getGlobalForObject(callback), ["BackstagePass"])) return; this.timeouts.splice(this.timeouts.indexOf(timer), 1); - util.trapErrors(callback, this); + try { + callback.call(this); + } + catch (e) { + try { + util.dump("Error invoking timer callback registered at " + + [frame.filename, frame.lineNumber, ""].join(":")); + util.reportError(e); + } + catch (e) { + Cu.reportError(e); + } + } }; + let frame = Components.stack.caller; let timer = services.Timer(timeout_notify, timeout || 0, services.Timer.TYPE_ONE_SHOT); this.timeouts.push(timer); return timer; @@ -996,7 +1069,7 @@ Class.prototype = { } try { - if ("value" in desc && (k in this.localizedProperties || k in this.magicalProperties)) + if ("value" in desc && (this.localizedProperties.has(k) || this.magicalProperties.has(k))) this[k] = desc.value; else Object.defineProperty(this, k, desc); @@ -1007,8 +1080,8 @@ Class.prototype = { return this; }, - localizedProperties: {}, - magicalProperties: {} + localizedProperties: RealSet(), + magicalProperties: RealSet() }; for (let name in properties(Class.prototype)) { let desc = Object.getOwnPropertyDescriptor(Class.prototype, name); @@ -1016,37 +1089,39 @@ for (let name in properties(Class.prototype)) { Object.defineProperty(Class.prototype, name, desc); } -Class.makeClosure = function makeClosure() { - const self = this; - function closure(fn) { - function _closure() { - try { - return fn.apply(self, arguments); - } - catch (e if !(e instanceof FailedAssertion)) { - util.reportError(e); - throw e.stack ? e : Error(e); - } +var closureHooks = { + get: function closure_get(target, prop) { + if (hasOwnProperty(target._closureCache, prop)) + return target._closureCache[prop]; + + let p = target[prop] + if (callable(p)) + return target._closureCache[prop] = p.bind(target); + return p; + } + + /* + getOwnPropertyNames: function getOwnPropertyNames(target) { + return [k for (k in properties(target, true))]; + }, + + getOwnPropertyDescriptor: function getOwnPropertyDescriptor(target, prop) { + let self = this; + return { + configurable: false, + writable: false, + get value() self.get(target, prop) } - _closure.wrapped = fn; - return _closure; } + */ +}; - iter(properties(this), properties(this, true)).forEach(function (k) { - if (!__lookupGetter__.call(this, k) && callable(this[k])) - closure[k] = closure(this[k]); - else if (!(k in closure)) - Object.defineProperty(closure, k, { - configurable: true, - enumerable: true, - get: function get_proxy() self[k], - set: function set_proxy(val) self[k] = val, - }); - }, this); - - return closure; +Class.makeClosure = function makeClosure() { + this._closureCache = {}; + + return new Proxy(this, closureHooks); }; -memoize(Class.prototype, "closure", Class.makeClosure); +memoize(Class.prototype, "bound", Class.makeClosure); /** * A base class generator for classes which implement XPCOM interfaces. @@ -1094,7 +1169,7 @@ let stub = Class.Property({ */ var ErrorBase = Class("ErrorBase", Error, { level: 2, - init: function EB_init(message, level = 0) { + init: function EB_init(message, level=0) { let error = Error(message); update(this, error); this.stack = error.stack; @@ -1132,7 +1207,7 @@ function Module(name, prototype, ...args) { let proto = callable(prototype) ? args[0] : prototype; proto._metaInit_ = function () { - delete module.prototype._metaInit_; + module.prototype._metaInit_ = null; currentModule[name.toLowerCase()] = this; }; @@ -1223,6 +1298,7 @@ var StructBase = Class("StructBase", Array, { clone: function struct_clone() this.constructor.apply(null, this.slice()), + bound: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "bound")), closure: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "closure")), get: function struct_get(key, val) this[this.members[key]], @@ -1268,7 +1344,7 @@ var StructBase = Class("StructBase", Array, { }); var Timer = Class("Timer", { - init: function init(minInterval, maxInterval, callback, self = this) { + init: function init(minInterval, maxInterval, callback, self=this) { this._timer = services.Timer(); this.callback = callback; this.self = self; @@ -1381,6 +1457,7 @@ function octal(decimal) parseInt(decimal, 8); * @param {nsIJSIID} iface The interface to which to query all elements. * @returns {Generator} */ +iter.iteratorProp = "@@iterator" in [] ? "@@iterator" : "iterator"; function iter(obj, iface) { if (arguments.length == 2 && iface instanceof Ci.nsIJSIID) return iter(obj).map(item => item.QueryInterface(iface)); @@ -1394,8 +1471,12 @@ function iter(obj, iface) { for (let j in iter(args[i])) yield j; })(); - else if (isinstance(obj, ["Iterator", "Generator"])) + else if (isinstance(obj, ["Iterator", "Generator", "Array"])) ; + else if (isinstance(obj, [Ci.nsIDOMHTMLCollection, Ci.nsIDOMNodeList])) + res = array.iterItems(obj); + else if (iter.iteratorProp in obj && callable(obj[iter.iteratorProp]) && !("__iterator__" in obj)) + res = (x for (x of obj)); else if (ctypes && ctypes.CData && obj instanceof ctypes.CData) { while (obj.constructor instanceof ctypes.PointerType) obj = obj.contents; @@ -1409,8 +1490,6 @@ function iter(obj, iface) { else return iter({}); } - else if (isinstance(obj, [Ci.nsIDOMHTMLCollection, Ci.nsIDOMNodeList])) - res = array.iterItems(obj); else if (Ci.nsIDOMNamedNodeMap && obj instanceof Ci.nsIDOMNamedNodeMap || Ci.nsIDOMMozNamedAttrMap && obj instanceof Ci.nsIDOMMozNamedAttrMap) res = (function () { @@ -1460,21 +1539,21 @@ update(iter, { every: function every(iter, pred, self) { pred = pred || util.identity; - for (let elem in iter) + for (let elem of iter) if (!pred.call(self, elem)) return false; return true; }, some: function every(iter, pred, self) { pred = pred || util.identity; - for (let elem in iter) + for (let elem of iter) if (pred.call(self, elem)) return true; return false; }, filter: function filter(iter, pred, self) { - for (let elem in iter) + for (let elem of iter) if (pred.call(self, elem)) yield elem; }, @@ -1488,13 +1567,13 @@ update(iter, { * @param {object} self The this object for *fn*. */ forEach: function forEach(iter, func, self) { - for (let val in iter) + for (let val of iter) func.call(self, val); }, indexOf: function indexOf(iter, elem) { let i = 0; - for (let item in iter) { + for (let item of iter) { if (item == elem) return i; i++; @@ -1510,7 +1589,7 @@ update(iter, { * @returns {Array} */ map: function map(iter, func, self) { - for (let i in iter) + for (let i of iter) yield func.call(self, i); }, @@ -1522,19 +1601,30 @@ update(iter, { if (typeof pred === "number") [pred, n] = [() => true, pred]; // Hack. - for (let elem in iter) + for (let elem of iter) if (pred.call(self, elem) && n-- === 0) return elem; return undefined; }, + /** + * Analog of Array.find method. Returns the first item in the + * iterator for which `pred` returns true. + */ + find: function find(iter, pred, self) { + for (let elem of iter) + if (pred.call(self, elem)) + return elem; + return undefined; + }, + sort: function sort(iter, fn, self) array(this.toArray(iter).sort(fn, self)), uniq: function uniq(iter) { - let seen = {}; - for (let item in iter) - if (!Set.add(seen, item)) + let seen = RealSet(); + for (let item of iter) + if (!seen.add(item)) yield item; }, @@ -1570,6 +1660,20 @@ const Iter = Class("Iter", { __iterator__: function () this.iter }); +iter.Iter = Iter; + +function arrayWrap(fn) { + function wrapper() { + let res = fn.apply(this, arguments); + if (isArray(res)) + return array(res); + if (isinstance(res, ["Iterator", "Generator"])) + return iter(res); + return res; + } + wrapper.wrapped = fn; + return wrapper; +} /** * Array utility methods. @@ -1581,23 +1685,25 @@ var array = Class("array", Array, { else if (ary.length) ary = Array.slice(ary); - return { - __proto__: ary, - __iterator__: function () this.iterItems(), - __noSuchMethod__: function (meth, args) { - var res = array[meth].apply(null, [this.array].concat(args)); - if (isArray(res)) - return array(res); - if (isinstance(res, ["Iterator", "Generator"])) - return iter(res); - return res; - }, - array: ary, - toString: function () this.array.toString(), - concat: function (...args) this.__noSuchMethod__("concat", args), - filter: function (...args) this.__noSuchMethod__("filter", args), - map: function (...args) this.__noSuchMethod__("map", args) - }; + let self = this; + return new Proxy(ary, { + get: function array_get(target, prop) { + if (prop in array && callable(array[prop])) + return arrayWrap(array[prop].bind(array, target)); + + if (prop == "array") + return target; + + let p = target[prop]; + if (!/^\d+$/.test(prop) && + prop != "toString" && + prop != "toSource" && + callable(p)) + return arrayWrap(p); + + return p; + } + }); } }, { /** @@ -1760,6 +1866,9 @@ Object.getOwnPropertyNames(Array.prototype).forEach(function (k) { }; }); +Object.defineProperty(Class.prototype, "closure", + deprecated("bound", { get: function closure() this.bound })); + 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 0316189..66d056c 100644 --- a/common/modules/bookmarkcache.jsm +++ b/common/modules/bookmarkcache.jsm @@ -1,4 +1,4 @@ -// Copyright ©2008-2013 Kris Maglione +// Copyright ©2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -160,7 +160,7 @@ var BookmarkCache = Module("BookmarkCache", XPCOM(Ci.nsINavBookmarkObserver), { try { return services.bookmarks .getBookmarkIdsForURI(uri, {}) - .some(this.closure.isRegularBookmark); + .some(this.bound.isRegularBookmark); } catch (e) { return false; diff --git a/common/modules/bootstrap.jsm b/common/modules/bootstrap.jsm index 4206133..1325f29 100644 --- a/common/modules/bootstrap.jsm +++ b/common/modules/bootstrap.jsm @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2012 Kris Maglione +// Copyright (c) 2011-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -6,11 +6,11 @@ var EXPORTED_SYMBOLS = ["require"]; -// Deal with cross-compartment XML passing issues. function create(proto) Object.create(proto); + this["import"] = function import_(obj) { let res = {}; - for each (let key in Object.getOwnPropertyNames(obj)) + for (let key of Object.getOwnPropertyNames(obj)) Object.defineProperty(res, key, Object.getOwnPropertyDescriptor(obj, key)); return res; } diff --git a/common/modules/buffer.jsm b/common/modules/buffer.jsm index 97b37b2..97398ef 100644 --- a/common/modules/buffer.jsm +++ b/common/modules/buffer.jsm @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -16,6 +16,7 @@ lazyRequire("contexts", ["Group"]); lazyRequire("io", ["io"]); lazyRequire("finder", ["RangeFind"]); lazyRequire("overlay", ["overlay"]); +lazyRequire("promises", ["Promise", "promises"]); lazyRequire("sanitizer", ["sanitizer"]); lazyRequire("storage", ["File", "storage"]); lazyRequire("template", ["template"]); @@ -45,7 +46,7 @@ var Buffer = Module("Buffer", { this.win = win; }, - get addPageInfoSection() Buffer.closure.addPageInfoSection, + get addPageInfoSection() Buffer.bound.addPageInfoSection, get pageInfo() Buffer.pageInfo, @@ -68,6 +69,71 @@ var Buffer = Module("Buffer", { ); }, + /** + * The load context of the window bound to this buffer. + */ + get loadContext() sanitizer.getContext(this.win), + + /** + * Content preference methods. + */ + prefs: Class.Memoize(function () + let (self = this) ({ + /** + * Returns a promise for the given preference name. + * + * @param {string} pref The name of the preference to return. + * @returns {Promise<*>} + */ + get: promises.withCallbacks(function get([resolve, reject], pref) { + let val = services.contentPrefs.getCachedByDomainAndName( + self.uri.spec, pref, self.loadContext); + + let found = false; + if (val) + resolve(val.value); + else + services.contentPrefs.getByDomainAndName( + self.uri.spec, pref, self.loadContext, + { handleCompletion: () => { + if (!found) + resolve(undefined); + }, + handleResult: (pref) => { + found = true; + resolve(pref.value); + }, + handleError: reject }); + }), + + /** + * Sets a content preference for the given buffer. + * + * @param {string} pref The preference to set. + * @param {string} value The value to store. + */ + set: promises.withCallbacks(function set([resolve, reject], pref, value) { + services.contentPrefs.set( + self.uri.spec, pref, value, self.loadContext, + { handleCompletion: () => {}, + handleResult: resolve, + handleError: reject }); + }), + + /** + * Clear a content preference for the given buffer. + * + * @param {string} pref The preference to clear. + */ + clear: promises.withCallbacks(function clear([resolve, reject], pref) { + services.contentPrefs.removeByDomainAndName( + self.uri.spec, pref, self.loadContext, + { handleCompletion: () => {}, + handleResult: resolve, + handleError: reject }); + }) + })), + /** * Gets a content preference for the given buffer. * @@ -77,14 +143,10 @@ var Buffer = Module("Buffer", { * @returns {string|number|boolean} The value of the preference, if * callback is not provided. */ - getPref: function getPref(pref, callback) { - // God damn it. - if (config.haveGecko("19.0a1")) - services.contentPrefs.getPref(this.uri, pref, - sanitizer.getContext(this.win), callback); - else - services.contentPrefs.getPref(this.uri, pref, callback); - }, + getPref: deprecated("prefs.get", function getPref(pref, callback) { + services.contentPrefs.getPref(this.uri, pref, + this.loadContext, callback); + }), /** * Sets a content preference for the given buffer. @@ -92,20 +154,20 @@ var Buffer = Module("Buffer", { * @param {string} pref The preference to set. * @param {string} value The value to store. */ - setPref: function setPref(pref, value) { + setPref: deprecated("prefs.set", function setPref(pref, value) { services.contentPrefs.setPref( - this.uri, pref, value, sanitizer.getContext(this.win)); - }, + this.uri, pref, value, this.loadContext); + }), /** * Clear a content preference for the given buffer. * * @param {string} pref The preference to clear. */ - clearPref: function clearPref(pref) { + clearPref: deprecated("prefs.clear", function clearPref(pref) { services.contentPrefs.removePref( - this.uri, pref, sanitizer.getContext(this.win)); - }, + this.uri, pref, this.loadContext); + }), climbUrlPath: function climbUrlPath(count) { let { dactyl } = this.modules; @@ -525,8 +587,6 @@ var Buffer = Module("Buffer", { }; DOM(elem).mousedown(params).mouseup(params); - if (!config.haveGecko("2b")) - DOM(elem).click(params); let sel = util.selectionController(win); sel.getSelection(sel.SELECTION_FOCUS_REGION).collapseToStart(); @@ -633,7 +693,7 @@ var Buffer = Module("Buffer", { return newURI.spec; } - for each (let shortener in Buffer.uriShorteners) + for (let shortener of Buffer.uriShorteners) try { let shortened = shortener(uri, doc); if (shortened) @@ -644,9 +704,10 @@ var Buffer = Module("Buffer", { } let link = DOM("link[href][rev=canonical], \ - link[href][rel=shortlink]", doc); - if (link.length) - return hashify(link.attr("href")); + link[href][rel=shortlink]", doc) + .attr("href"); + if (link) + return hashify(link); return null; }, @@ -793,7 +854,7 @@ var Buffer = Module("Buffer", { * @param {number} count The multiple of 'scroll' lines to scroll. * @optional */ - scrollByScrollSize: function scrollByScrollSize(direction, count = 1) { + scrollByScrollSize: function scrollByScrollSize(direction, count=1) { let { options } = this.modules; direction = direction ? 1 : -1; @@ -1104,7 +1165,7 @@ var Buffer = Module("Buffer", { else { let url = loc || doc.location.href; const PREFIX = "view-source:"; - if (url.indexOf(PREFIX) == 0) + if (url.startsWith(PREFIX)) url = url.substr(PREFIX.length); else url = PREFIX + url; @@ -1249,12 +1310,12 @@ var Buffer = Module("Buffer", { if (prefs.get("browser.zoom.siteSpecific")) { var privacy = sanitizer.getContext(this.win); if (value == 1) { - this.clearPref("browser.content.full-zoom"); - this.clearPref("dactyl.content.full-zoom"); + this.prefs.clear("browser.content.full-zoom"); + this.prefs.clear("dactyl.content.full-zoom"); } else { - this.setPref("browser.content.full-zoom", value); - this.setPref("dactyl.content.full-zoom", fullZoom); + this.prefs.set("browser.content.full-zoom", value); + this.prefs.set("dactyl.content.full-zoom", fullZoom); } } @@ -1264,15 +1325,15 @@ var Buffer = Module("Buffer", { /** * Updates the zoom level of this buffer from a content preference. */ - updateZoom: util.wrapCallback(function updateZoom() { + updateZoom: promises.task(function updateZoom() { let uri = this.uri; if (prefs.get("browser.zoom.siteSpecific")) { - this.getPref("dactyl.content.full-zoom", (val) => { - if (val != null && uri.equals(this.uri) && val != prefs.get("browser.zoom.full")) - [this.contentViewer.textZoom, this.contentViewer.fullZoom] = - [this.contentViewer.fullZoom, this.contentViewer.textZoom]; - }); + let val = yield this.prefs.get("dactyl.content.full-zoom"); + + if (val != null && uri.equals(this.uri) && val != prefs.get("browser.zoom.full")) + [this.contentViewer.textZoom, this.contentViewer.fullZoom] = + [this.contentViewer.fullZoom, this.contentViewer.textZoom]; } }), @@ -1751,7 +1812,7 @@ var Buffer = Module("Buffer", { let arg = args[0]; // FIXME: arg handling is a bit of a mess, check for filename - dactyl.assert(!arg || arg[0] == ">" && !config.OS.isWindows, + dactyl.assert(!arg || arg[0] == ">", _("error.trailingCharacters")); const PRINTER = "PostScript/default"; @@ -1761,26 +1822,25 @@ var Buffer = Module("Buffer", { BRANCHES.forEach(function (branch) { prefs.set(branch + pref, value); }); } - prefs.withContext(function () { - if (arg) { - prefs.set("print.print_printer", PRINTER); - - let { path } = io.File(arg.substr(1)); - set("print_to_file", true); - set("print_to_filename", path); - prefs.set("print_to_filename", path); + let settings = services.printSettings.newPrintSettings; + settings.printSilent = args.bang; + if (arg) { + settings.printToFile = true; + settings.toFileName = io.File(arg.substr(1)).path; + settings.outputFormat = settings.kOutputFormatPDF; - dactyl.echomsg(_("print.toFile", arg.substr(1))); - } - else - dactyl.echomsg(_("print.sending")); + dactyl.echomsg(_("print.toFile", arg.substr(1))); + } + else { + dactyl.echomsg(_("print.sending")); - prefs.set("print.always_print_silent", args.bang); if (false) prefs.set("print.show_print_progress", !args.bang); + } - config.browser.contentWindow.print(); - }); + config.browser.contentWindow + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebBrowserPrint).print(settings, null); dactyl.echomsg(_("print.sent")); }, @@ -2009,7 +2069,7 @@ var Buffer = Module("Buffer", { events: function initEvents(dactyl, modules, window) { let { buffer, config, events } = modules; - events.listen(config.browser, "scroll", buffer.closure._updateBufferPosition, false); + events.listen(config.browser, "scroll", buffer.bound._updateBufferPosition, false); }, mappings: function initMappings(dactyl, modules, window) { let { Editor, Events, buffer, editor, events, ex, mappings, modes, options, tabs } = modules; @@ -2230,7 +2290,7 @@ var Buffer = Module("Buffer", { elem = DOM(elem); - if (elem[0].readOnly || !DOM(elem).isEditable) + if (elem[0].readOnly || elem[0].disabled || !DOM(elem).isEditable) return false; let style = elem.style; @@ -2428,10 +2488,9 @@ var Buffer = Module("Buffer", { if (/^func:/.test(filter.result)) var res = dactyl.userEval("(" + Option.dequote(filter.result.substr(5)) + ")")(doc, line); else - res = iter.nth(filter.matcher(doc), - elem => ((elem.nodeValue || elem.textContent).trim() == line && - DOM(elem).display != "none"), - 0) + res = iter.find(filter.matcher(doc), + elem => ((elem.nodeValue || elem.textContent).trim() == line && + DOM(elem).display != "none")) || iter.nth(filter.matcher(doc), util.identity, line - 1); if (res) break; diff --git a/common/modules/cache.jsm b/common/modules/cache.jsm index 3f8e2f1..fc04595 100644 --- a/common/modules/cache.jsm +++ b/common/modules/cache.jsm @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2013 Kris Maglione +// Copyright (c) 2011-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -10,33 +10,41 @@ defineModule("cache", { }); lazyRequire("overlay", ["overlay"]); -lazyRequire("storage", ["File"]); +lazyRequire("storage", ["File", "storage"]); var Cache = Module("Cache", XPCOM(Ci.nsIRequestObserver), { init: function init() { this.queue = []; - this.cache = {}; + this.storage = storage.newMap("cache", { store: true }); this.providers = {}; this.globalProviders = this.providers; - this.providing = {}; - this.localProviders = {}; + this.providing = RealSet(); + this.localProviders = RealSet(); if (JSMLoader.cacheFlush) this.flush(); update(services["dactyl:"].providers, { - "cache": function (uri, path) { + "cache": (uri, path) => { let contentType = "text/plain"; try { contentType = services.mime.getTypeFromURI(uri); } catch (e) {} - if (!cache.cacheReader || !cache.cacheReader.hasEntry(path)) - return [contentType, cache.force(path)]; + if (this.storage.has(path) || + !this.cacheReader || + !this.cacheReader.hasEntry(path)) + return [contentType, this.force(path)]; let channel = services.StreamChannel(uri); - channel.contentStream = cache.cacheReader.getInputStream(path); + try { + channel.contentStream = this.cacheReader.getInputStream(path); + } + catch (e if e.result = Cr.NS_ERROR_FILE_CORRUPTED) { + this.flushDiskCache(); + throw e; + } channel.contentType = contentType; channel.contentCharset = "UTF-8"; return channel; @@ -54,7 +62,7 @@ var Cache = Module("Cache", XPCOM(Ci.nsIRequestObserver), { }), parse: function parse(str) { - if (~'{['.indexOf(str[0])) + if ('{['.contains(str[0])) return JSON.parse(str); return str; }, @@ -83,8 +91,7 @@ var Cache = Module("Cache", XPCOM(Ci.nsIRequestObserver), { } catch (e if e.result == Cr.NS_ERROR_FILE_CORRUPTED) { util.reportError(e); - this.closeWriter(); - this.cacheFile.remove(false); + this.flushDiskCache(); } return this._cacheReader; @@ -114,7 +121,7 @@ var Cache = Module("Cache", XPCOM(Ci.nsIRequestObserver), { closeReader: function closeReader() { if (cache._cacheReader) { this.cacheReader.close(); - delete cache._cacheReader; + cache._cacheReader = null; } }, @@ -123,7 +130,7 @@ var Cache = Module("Cache", XPCOM(Ci.nsIRequestObserver), { if (this._cacheWriter) { this._cacheWriter.close(); - delete cache._cacheWriter; + cache._cacheWriter = null; // ZipWriter bug. if (this.cacheFile.fileSize <= 22) @@ -131,10 +138,20 @@ var Cache = Module("Cache", XPCOM(Ci.nsIRequestObserver), { } }), - flush: function flush() { - cache.cache = {}; + flush: function flush(filter) { + if (filter) { + this.storage.keys().filter(filter) + .forEach(bind("remove", this.storage)); + } + else { + this.storage.clear(); + this.flushDiskCache(); + } + }, + + flushDiskCache: function flushDiskCache() { if (this.cacheFile.exists()) { - this.closeReader(); + this.closeWriter(); this.flushJAR(this.cacheFile); this.cacheFile.remove(false); @@ -155,7 +172,7 @@ var Cache = Module("Cache", XPCOM(Ci.nsIRequestObserver), { cache.processQueue(); } - delete this.cache[name]; + this.storage.remove(name); }, flushJAR: function flushJAR(file) { @@ -167,65 +184,80 @@ var Cache = Module("Cache", XPCOM(Ci.nsIRequestObserver), { }, force: function force(name, localOnly) { + if (this.storage.has(name)) + return this.storage.get(name); + util.waitFor(() => !this.inQueue); if (this.cacheReader && this.cacheReader.hasEntry(name)) { - return this.parse(File.readStream( - this.cacheReader.getInputStream(name))); + try { + return this.parse(File.readStream( + this.cacheReader.getInputStream(name))); + } + catch (e if e.result == Cr.NS_ERROR_FILE_CORRUPTED) { + this.flushDiskCache(); + } } - if (Set.has(this.localProviders, name) && !this.isLocal) { - for each (let { cache } in overlay.modules) + if (this.localProviders.has(name) && !this.isLocal) { + for (let { cache } of overlay.modules) if (cache._has(name)) return cache.force(name, true); } - if (Set.has(this.providers, name)) { - util.assert(!Set.add(this.providing, name), + if (hasOwnProperty(this.providers, name)) { + util.assert(!this.providing.add(name), "Already generating cache for " + name, false); + + let [func, long] = this.providers[name]; try { - let [func, self] = this.providers[name]; - this.cache[name] = func.call(self || this, name); + var value = func.call(this, name); } finally { - delete this.providing[name]; + this.providing.delete(name); } - cache.queue.push([Date.now(), name]); - cache.processQueue(); + if (!long) + this.storage.set(name, value); + else { + cache.queue.push([Date.now(), name, value]); + cache.processQueue(); + } - return this.cache[name]; + return value; } if (this.isLocal && !localOnly) return cache.force(name); }, - get: function get(name, callback, self) { - if (!Set.has(this.cache, name)) { - if (callback && !(Set.has(this.providers, name) || - Set.has(this.localProviders, name))) - this.register(name, callback, self); + get: function get(name, callback, long) { + if (this.storage.has(name)) + return this.storage.get(name); - this.cache[name] = this.force(name); - util.assert(this.cache[name] !== undefined, - "No such cache key", false); - } + if (callback && !(hasOwnProperty(this.providers, name) || + this.localProviders.has(name))) + this.register(name, callback, long); - return this.cache[name]; + var result = this.force(name); + util.assert(result !== undefined, "No such cache key", false); + + return result; }, - _has: function _has(name) Set.has(this.providers, name) || set.has(this.cache, name), + _has: function _has(name) hasOwnProperty(this.providers, name) + || this.storage.has(name), - has: function has(name) [this.globalProviders, this.cache, this.localProviders] - .some(obj => Set.has(obj, name)), + has: function has(name) [this.globalProviders, this.localProviders] + .some(obj => isinstance(obj, ["Set"]) ? obj.has(name) + : hasOwnProperty(obj, name)), - register: function register(name, callback, self) { + register: function register(name, callback, long) { if (this.isLocal) - Set.add(this.localProviders, name); + this.localProviders.add(name); - this.providers[name] = [callback, self]; + this.providers[name] = [callback, long]; }, processQueue: function processQueue() { @@ -235,18 +267,20 @@ var Cache = Module("Cache", XPCOM(Ci.nsIRequestObserver), { if (this.queue.length && !this.inQueue) { // removeEntry does not work properly with queues. let removed = 0; - for each (let [, entry] in this.queue) + for (let [, entry] of this.queue) if (this.getCacheWriter().hasEntry(entry)) { this.getCacheWriter().removeEntry(entry, false); removed++; } - if (removed) + if (removed) { this.closeWriter(); + util.flushCache(this.cacheFile); + } - this.queue.splice(0).forEach(function ([time, entry]) { - if (time && Set.has(this.cache, entry)) { + this.queue.splice(0).forEach(function ([time, entry, value]) { + if (time && value != null) { let stream = services.CharsetConv("UTF-8") - .convertToInputStream(this.stringify(this.cache[entry])); + .convertToInputStream(this.stringify(value)); this.getCacheWriter().addEntryStream(entry, time * 1000, this.compression, stream, diff --git a/common/modules/commands.jsm b/common/modules/commands.jsm index 0e0d58f..0fab7bc 100644 --- a/common/modules/commands.jsm +++ b/common/modules/commands.jsm @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -155,7 +155,7 @@ var Command = Class("Command", { * @param {Args} args The Args object passed to {@link #action}. * @param {Object} modifiers Any modifiers to be passed to {@link #action}. */ - execute: function execute(args, modifiers = {}) { + execute: function execute(args, modifiers={}) { const { dactyl } = this.modules; let context = args.context; @@ -210,7 +210,7 @@ var Command = Class("Command", { extra: extra }), - complained: Class.Memoize(function () ({})), + complained: Class.Memoize(function () RealSet()), /** * @property {[string]} All of this command's name specs. e.g., "com[mand]" @@ -325,7 +325,8 @@ var Command = Class("Command", { explicitOpts: Class.Memoize(function () ({})), - has: function AP_has(opt) Set.has(this.explicitOpts, opt) || typeof opt === "number" && Set.has(this, opt), + has: function AP_has(opt) hasOwnProperty(this.explicitOpts, opt) + || typeof opt === "number" && hasOwnProperty(this, opt), get literalArg() this.command.literal != null && this[this.command.literal] || "", @@ -402,7 +403,9 @@ var Command = Class("Command", { warn: function warn(context, type, message) { let loc = !context ? "" : [context.file, context.line, " "].join(":"); - if (!Set.add(this.complained, type + ":" + (context ? context.file : "[Command Line]"))) + let key = type + ":" + (context ? context.file : "[Command Line]"); + + if (!this.complained.add(key)) this.modules.dactyl.warn(loc + message); } }, { @@ -498,22 +501,21 @@ var CommandHive = Class("CommandHive", Contexts.Hive, { */ cache: function cache() { - let self = this; let { cache } = this.modules; this.cached = true; - let cached = cache.get(this.cacheKey, function () { - self.cached = false; + let cached = cache.get(this.cacheKey, () => { + this.cached = false; this.modules.moduleManager.initDependencies("commands"); let map = {}; - for (let [name, cmd] in Iterator(self._map)) + for (let [name, cmd] in Iterator(this._map)) if (cmd.sourceModule) map[name] = { sourceModule: cmd.sourceModule, isPlaceholder: true }; let specs = []; - for (let cmd in values(self._list)) - for each (let spec in cmd.parsedSpecs) + for (let cmd of this._list) + for (let spec of cmd.parsedSpecs) specs.push(spec.concat(cmd.name)); return { map: map, specs: specs }; @@ -553,7 +555,7 @@ var CommandHive = Class("CommandHive", Contexts.Hive, { * @param {boolean} replace Replace an existing command of the same name. * @optional */ - add: function add(specs, description, action, extra = {}, replace = false) { + add: function add(specs, description, action, extra={}, replace=false) { const { commands, contexts } = this.modules; if (!extra.definedAt) @@ -592,7 +594,7 @@ var CommandHive = Class("CommandHive", Contexts.Hive, { return name; }, - _add: function _add(names, description, action, extra = {}, replace = false) { + _add: function _add(names, description, action, extra={}, replace=false) { const { contexts } = this.modules; extra.definedAt = contexts.getCaller(Components.stack.caller.caller); return this.add.apply(this, arguments); @@ -619,11 +621,12 @@ var CommandHive = Class("CommandHive", Contexts.Hive, { */ get: function get(name, full) { let cmd = this._map[name] - || !full && array.nth(this._list, cmd => cmd.hasName(name), 0) + || !full && this._list.find(cmd => cmd.hasName(name)) || null; if (!cmd && full) { - let name = array.nth(this.specs, spec => Command.hasName(spec, name), 0); + // Hrm. This is wrong. -Kris + let name = this._specs.find(spec => Command.hasName(spec, name)); return name && this.get(name); } @@ -756,11 +759,11 @@ var Commands = Module("commands", { const { commandline, completion } = this.modules; function completerToString(completer) { if (completer) - return [k for ([k, v] in Iterator(config.completers)) if (completer == completion.closure[v])][0] || "custom"; + return [k for ([k, v] in Iterator(config.completers)) if (completer == completion.bound[v])][0] || "custom"; return ""; } // TODO: allow matching of aliases? - function cmds(hive) hive._list.filter(cmd => cmd.name.indexOf(filter || "") == 0) + function cmds(hive) hive._list.filter(cmd => cmd.name.startsWith(filter || "")) let hives = (hives || this.userHives).map(h => [h, cmds(h)]) .filter(([h, c]) => c.length); @@ -828,9 +831,9 @@ var Commands = Module("commands", { return group._add.apply(group, arguments); }, - addUserCommand: deprecated("group.commands.add", { get: function addUserCommand() this.user.closure._add }), + addUserCommand: deprecated("group.commands.add", { get: function addUserCommand() this.user.bound._add }), getUserCommands: deprecated("iter(group.commands)", function getUserCommands() iter(this.user).toArray()), - removeUserCommand: deprecated("group.commands.remove", { get: function removeUserCommand() this.user.closure.remove }), + removeUserCommand: deprecated("group.commands.remove", { get: function removeUserCommand() this.user.bound.remove }), /** * Returns the specified command invocation object serialized to @@ -880,7 +883,7 @@ var Commands = Module("commands", { * @returns {Command} */ get: function get(name, full) iter(this.hives).map(([i, hive]) => hive.get(name, full)) - .nth(util.identity, 0), + .find(util.identity), /** * Returns true if a command invocation contains a URL referring to the @@ -964,7 +967,7 @@ var Commands = Module("commands", { * Args object. * @returns {Args} */ - parseArgs: function parseArgs(str, params = {}) { + parseArgs: function parseArgs(str, params={}) { const self = this; function getNextArg(str, _keepQuotes=keepQuotes) { @@ -1011,7 +1014,7 @@ var Commands = Module("commands", { let matchOpts = function matchOpts(arg) { // Push possible option matches into completions if (complete && !onlyArgumentsRemaining) - completeOpts = options.filter(opt => (opt.multiple || !Set.has(args, opt.names[0]))); + completeOpts = options.filter(opt => (opt.multiple || !hasOwnProperty(args, opt.names[0]))); }; let resetCompletions = function resetCompletions() { completeOpts = null; @@ -1063,7 +1066,7 @@ var Commands = Module("commands", { if (!onlyArgumentsRemaining) { for (let [, opt] in Iterator(options)) { for (let [, optname] in Iterator(opt.names)) { - if (sub.indexOf(optname) == 0) { + if (sub.startsWith(optname)) { let count = 0; let invalid = false; let arg, quote, quoted; @@ -1110,7 +1113,7 @@ var Commands = Module("commands", { if (arg == null || (typeof arg == "number" && isNaN(arg))) { if (!complete || orig != "" || args.completeStart != str.length) - fail(_("command.invalidOptTypeArg", opt.type.description, optname, argString)); + fail(_("command.invalidOptTypeArg", opt.type.description, optname, quoted)); if (complete) complete.highlight(args.completeStart, count - 1, "SPELLCHECK"); } @@ -1119,7 +1122,7 @@ var Commands = Module("commands", { // we have a validator function if (typeof opt.validator == "function") { if (opt.validator(arg, quoted) == false && (arg || !complete)) { - fail(_("command.invalidOptArg", optname, argString)); + fail(_("command.invalidOptArg", optname, quoted)); if (complete) // Always true. complete.highlight(args.completeStart, count - 1, "SPELLCHECK"); } @@ -1543,7 +1546,7 @@ var Commands = Module("commands", { function (args) { let cmd = args[0]; - util.assert(!cmd || cmd.split(",").every(commands.validName.closure.test), + util.assert(!cmd || cmd.split(",").every(commands.validName.bound.test), _("command.invalidName", cmd)); if (args.length <= 1) @@ -1573,7 +1576,7 @@ var Commands = Module("commands", { }; } else - completerFunc = context => modules.completion.closure[config.completers[completer]](context); + completerFunc = context => modules.completion.bound[config.completers[completer]](context); } let added = args["-group"].add(cmd.split(","), @@ -1721,7 +1724,7 @@ var Commands = Module("commands", { ] })), iterateIndex: function (args) let (tags = help.tags) - this.iterate(args).filter(cmd => (cmd.hive === commands.builtin || Set.has(tags, cmd.helpTag))), + this.iterate(args).filter(cmd => (cmd.hive === commands.builtin || hasOwnProperty(tags, cmd.helpTag))), format: { headings: ["Command", "Group", "Description"], description: function (cmd) template.linkifyHelp(cmd.description + (cmd.replacementText ? ": " + cmd.action : "")), @@ -1772,7 +1775,7 @@ var Commands = Module("commands", { } }); -let quote = function quote(q, list, map = Commands.quoteMap) { +let quote = function quote(q, list, map=Commands.quoteMap) { let re = RegExp("[" + list + "]", "g"); function quote(str) (q + String.replace(str, re, $0 => ($0 in map ? map[$0] : ("\\" + $0))) + q); diff --git a/common/modules/completion.jsm b/common/modules/completion.jsm index 001b267..7d2b6c1 100644 --- a/common/modules/completion.jsm +++ b/common/modules/completion.jsm @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -33,7 +33,7 @@ lazyRequire("template", ["template"]); * @constructor */ var CompletionContext = Class("CompletionContext", { - init: function cc_init(editor, name = "", offset = 0) { + init: function cc_init(editor, name="", offset=0) { let self = this; if (editor instanceof this.constructor) { let parent = editor; @@ -512,7 +512,7 @@ var CompletionContext = Class("CompletionContext", { filtered.sort(this.compare); if (!this.anchored) { let filter = this.filter; - filtered.sort(function s(a, b) (b.text.indexOf(filter) == 0) - (a.text.indexOf(filter) == 0)); + filtered.sort(function s(a, b) b.text.startsWith(filter) - a.text.startsWith(filter)); } } @@ -549,7 +549,7 @@ var CompletionContext = Class("CompletionContext", { var substrings = [text]; } else { - var compare = function compare(text, s) text.indexOf(s) >= 0; + var compare = function compare(text, s) text.contains(s); var substrings = []; let start = 0; let idx; @@ -733,7 +733,7 @@ var CompletionContext = Class("CompletionContext", { let alias = (prop) => { context.__defineGetter__(prop, () => this[prop]); context.__defineSetter__(prop, (val) => this[prop] = val); - } + }; alias("_cache"); alias("_completions"); alias("_generate"); @@ -843,7 +843,7 @@ var CompletionContext = Class("CompletionContext", { } this.waitingForTab = false; this.runCount++; - for each (let context in this.contextList) + for (let context of this.contextList) context.lastActivated = this.runCount; this.contextList = []; }, @@ -970,7 +970,7 @@ var Completion = Module("completion", { context.generate = function generate_() { return [[k.substr(services.ABOUT.length), ""] for (k in Cc) - if (k.indexOf(services.ABOUT) == 0)]; + if (k.startsWith(services.ABOUT))]; }; }); @@ -1056,7 +1056,7 @@ var Completion = Module("completion", { let contains = String.indexOf; if (context.ignoreCase) { compare = util.compareIgnoreCase; - contains = function contains_(a, b) a && a.toLowerCase().indexOf(b.toLowerCase()) > -1; + contains = function contains_(a, b) a && a.toLowerCase().contains(b.toLowerCase()); } if (tags) @@ -1180,12 +1180,13 @@ var Completion = Module("completion", { .concat([let (name = k.substr(services.AUTOCOMPLETE.length)) ["native:" + name, _("autocomplete.description", name)] for (k in Cc) - if (k.indexOf(services.AUTOCOMPLETE) == 0)]), + if (k.startsWith(services.AUTOCOMPLETE))]), setter: function setter(values) { - if (values.length == 1 && !Set.has(values[0], this.values) - && Array.every(values[0], Set.has(this.valueMap))) - return Array.map(values[0], function m(v) this[v], this.valueMap); + if (values.length == 1 && !hasOwnProperty(values[0], this.values) + && Array.every(values[0], v => hasOwnProperty(this.valueMap, v))) + return Array.map(values[0], v => this.valueMap[v]); + return values; }, diff --git a/common/modules/config.jsm b/common/modules/config.jsm index d009e1a..dfae6a5 100644 --- a/common/modules/config.jsm +++ b/common/modules/config.jsm @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -9,16 +9,19 @@ let global = this; defineModule("config", { exports: ["ConfigBase", "Config", "config"], - require: ["dom", "io", "protocol", "services", "util", "template"] + require: ["io", "protocol", "services"] }); lazyRequire("addons", ["AddonManager"]); lazyRequire("cache", ["cache"]); +lazyRequire("dom", ["DOM"]); lazyRequire("highlight", ["highlight"]); lazyRequire("messages", ["_"]); lazyRequire("prefs", ["localPrefs", "prefs"]); lazyRequire("storage", ["storage", "File"]); lazyRequire("styles", ["Styles"]); +lazyRequire("template", ["template"]); +lazyRequire("util", ["util"]); function AboutHandler() {} AboutHandler.prototype = { @@ -45,19 +48,30 @@ var ConfigBase = Class("ConfigBase", { * initialization code. Must call superclass's init function. */ init: function init() { - this.loadConfig(); + if (!config.haveGecko("26")) + this.modules.global = this.modules.global.filter(m => m != "downloads"); // FIXME - this.features.push = deprecated("Set.add", function push(feature) Set.add(this, feature)); - if (this.haveGecko("2b")) - Set.add(this.features, "Gecko2"); + this.loadConfig(); - JSMLoader.registerFactory(JSMLoader.Factory(AboutHandler)); - JSMLoader.registerFactory(JSMLoader.Factory( - Protocol("dactyl", "{9c8f2530-51c8-4d41-b356-319e0b155c44}", - "resource://dactyl-content/"))); + util.trapErrors(() => { + JSMLoader.registerFactory(JSMLoader.Factory(AboutHandler)); + }); + util.withProperErrors(() => { + JSMLoader.registerFactory(JSMLoader.Factory( + Protocol("dactyl", "{9c8f2530-51c8-4d41-b356-319e0b155c44}", + "resource://dactyl-content/"))); + }); + this.protocolLoaded = true; this.timeout(function () { - cache.register("config.dtd", () => util.makeDTD(config.dtd)); + cache.register("config.dtd", () => util.makeDTD(config.dtd), + true); + }); + + // FIXME: May not be ready before first window opens. + AddonManager.getAddonByID("{972ce4c6-7e08-4474-a285-3208198ce6fd}", a => { + if (!a.isActive) + config.features.delete("default-theme"); }); services["dactyl:"].pages["dtd"] = () => [null, cache.get("config.dtd")]; @@ -70,7 +84,7 @@ var ConfigBase = Class("ConfigBase", { get prefs() localPrefs, - get has() Set.has(this.features), + has: function (feature) this.features.has(feature), configFiles: [ "resource://dactyl-common/config.json", @@ -81,7 +95,7 @@ var ConfigBase = Class("ConfigBase", { loadConfig: function loadConfig(documentURL) { - for each (let config in this.configs) { + for (let config of this.configs) { if (documentURL) config = config.overlays && config.overlays[documentURL] || {}; @@ -90,6 +104,9 @@ var ConfigBase = Class("ConfigBase", { if (isArray(this[prop])) this[prop] = [].concat(this[prop], value); + else if (isinstance(this[prop], ["Set"])) + for (let key of value) + this[prop].add(key); else if (isObject(this[prop])) { if (isArray(value)) value = Set(value); @@ -126,6 +143,7 @@ var ConfigBase = Class("ConfigBase", { "options", "overlay", "prefs", + ["promises", "Promise", "Task", "promises"], "protocol", "sanitizer", "services", @@ -157,17 +175,6 @@ var ConfigBase = Class("ConfigBase", { highlight.loadCSS(this.helpCSS.replace(/__MSG_(.*?)__/g, (m0, m1) => _(m1))); - if (!this.haveGecko("2b")) - highlight.loadCSS(literal(/* - !TabNumber font-weight: bold; margin: 0px; padding-right: .8ex; - !TabIconNumber { - font-weight: bold; - color: white; - text-align: center; - text-shadow: black -1px 0 1px, black 0 1px 1px, black 1px 0 1px, black 0 -1px 1px; - } - */)); - let hl = highlight.set("Find", ""); hl.onChange = function () { function hex(val) ("#" + util.regexp.iterate(/\d+/g, val) @@ -246,7 +253,7 @@ var ConfigBase = Class("ConfigBase", { bestLocale: function (list) { return values([this.appLocale, this.appLocale.replace(/-.*/, ""), "en", "en-US", list[0]]) - .nth(Set.has(Set(list)), 0); + .find(bind("has", RealSet(list))); }, /** @@ -256,7 +263,7 @@ var ConfigBase = Class("ConfigBase", { // Horrible hack. let res = {}; function process(manifest) { - for each (let line in manifest.split(/\n+/)) { + for (let line of manifest.split(/\n+/)) { let match = /^\s*(content|skin|locale|resource)\s+([^\s#]+)\s/.exec(line); if (match) res[match[2]] = true; @@ -274,7 +281,7 @@ var ConfigBase = Class("ConfigBase", { } } - for each (let dir in ["UChrm", "AChrom"]) { + for (let dir of ["UChrm", "AChrom"]) { dir = File(services.directory.get(dir, Ci.nsIFile)); if (dir.exists() && dir.isDirectory()) for (let file in dir.iterDirectory()) @@ -401,7 +408,7 @@ var ConfigBase = Class("ConfigBase", { dtd: Class.Memoize(function () iter(this.dtdExtra, (["dactyl." + k, v] for ([k, v] in iter(config.dtdDactyl))), - (["dactyl." + s, config[s]] for each (s in config.dtdStrings))) + (["dactyl." + s, config[s]] for (s of config.dtdStrings))) .toObject()), dtdDactyl: memoize({ @@ -458,7 +465,7 @@ var ConfigBase = Class("ConfigBase", { ["menupopup", { id: "viewSidebarMenu", xmlns: "xul" }], ["broadcasterset", { id: "mainBroadcasterSet", xmlns: "xul" }]]; - for each (let [id, [name, key, uri]] in Iterator(this.sidebars)) { + for (let [id, [name, key, uri]] in Iterator(this.sidebars)) { append[0].push( ["menuitem", { observes: "pentadactyl-" + id + "Sidebar", label: name, accesskey: key }]); @@ -539,7 +546,7 @@ var ConfigBase = Class("ConfigBase", { * dactyl.has(feature) to check for a feature's presence * in this array. */ - features: {}, + features: RealSet(["default-theme"]), /** * @property {string} The file extension used for command script files. @@ -586,7 +593,7 @@ config.INIT = update(Object.create(config.INIT), config.INIT, { init: function init(dactyl, modules, window) { init.superapply(this, arguments); - let img = window.Image(); + let img = new window.Image; img.src = this.logo || "resource://dactyl-local-content/logo.png"; img.onload = util.wrapCallback(function () { highlight.loadCSS(literal(/* diff --git a/common/modules/contexts.jsm b/common/modules/contexts.jsm index 3c0ca3a..2a6e46d 100644 --- a/common/modules/contexts.jsm +++ b/common/modules/contexts.jsm @@ -1,4 +1,4 @@ -// Copyright (c) 2010-2013 Kris Maglione +// Copyright (c) 2010-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -43,14 +43,14 @@ var Group = Class("Group", { delete this[hive]; if (reason != "shutdown") - this.children.splice(0).forEach(this.contexts.closure.removeGroup); + this.children.splice(0).forEach(this.contexts.bound.removeGroup); }, destroy: function destroy(reason) { for (let hive in values(this.hives)) util.trapErrors("destroy", hive); if (reason != "shutdown") - this.children.splice(0).forEach(this.contexts.closure.removeGroup); + this.children.splice(0).forEach(this.contexts.bound.removeGroup); }, argsExtra: function argsExtra() ({}), @@ -67,8 +67,9 @@ var Group = Class("Group", { }, { compileFilter: function (patterns, default_=false) { function siteFilter(uri) - let (match = array.nth(siteFilter.filters, f => f(uri), 0)) - match ? match.result : default_; + let (match = siteFilter.filters.find(f => f(uri))) + match ? match.result + : default_; return update(siteFilter, { toString: function () this.filters.join(","), @@ -138,22 +139,26 @@ var Contexts = Module("contexts", { completer: function (context) modules.completion.group(context) }); - memoize(modules, "userContext", () => contexts.Context(modules.io.getRCFile("~", true), contexts.user, [modules, true])); - memoize(modules, "_userContext", () => contexts.Context(modules.io.getRCFile("~", true), contexts.user, [modules.userContext])); + memoize(modules, "userContext", () => contexts.Context(modules.io.getRCFile("~", true), contexts.user, [modules, false])); + memoize(modules, "_userContext", () => modules.userContext); }, cleanup: function () { - for each (let hive in this.groupList.slice()) + for (let hive of this.groupList.slice()) util.trapErrors("cleanup", hive, "shutdown"); }, destroy: function () { - for each (let hive in values(this.groupList.slice())) + for (let hive of values(this.groupList.slice())) util.trapErrors("destroy", hive, "shutdown"); - for (let [name, plugin] in iter(this.modules.plugins.contexts)) + for each (let plugin in this.modules.plugins.contexts) { if (plugin && "onUnload" in plugin && callable(plugin.onUnload)) util.trapErrors("onUnload", plugin); + + if (isinstance(plugin, ["Sandbox"])) + util.trapErrors("nukeSandbox", Cu, plugin); + } }, signals: { @@ -189,7 +194,9 @@ var Contexts = Module("contexts", { { _hive: { value: name } }))); memoize(contexts.groupsProto, name, - function () [group[name] for (group in values(this.groups)) if (Set.has(group, name))]); + function () [group[name] + for (group in values(this.groups)) + if (hasOwnProperty(group, name))]); }, get toStringParams() [this.name, this.Hive] @@ -199,25 +206,23 @@ var Contexts = Module("contexts", { Context: function Context(file, group, args) { const { contexts, io, newContext, plugins, userContext } = this.modules; - let isPlugin = array.nth(io.getRuntimeDirectories("plugins"), - dir => dir.contains(file, true), - 0); - let isRuntime = array.nth(io.getRuntimeDirectories(""), - dir => dir.contains(file, true), - 0); + let isPlugin = io.getRuntimeDirectories("plugins") + .find(dir => dir.contains(file, true)); + let isRuntime = io.getRuntimeDirectories("") + .find(dir => dir.contains(file, true)); let name = isPlugin ? file.getRelativeDescriptor(isPlugin).replace(File.PATH_SEP, "-") : file.leafName; let id = util.camelCase(name.replace(/\.[^.]*$/, "")); let contextPath = file.path; - let self = Set.has(plugins, contextPath) && plugins.contexts[contextPath]; + let self = hasOwnProperty(plugins, contextPath) && plugins.contexts[contextPath]; if (!self && isPlugin && false) - self = Set.has(plugins, id) && plugins[id]; + self = hasOwnProperty(plugins, id) && plugins[id]; if (self) { - if (Set.has(self, "onUnload")) + if (hasOwnProperty(self, "onUnload")) util.trapErrors("onUnload", self); } else { @@ -302,15 +307,14 @@ var Contexts = Module("contexts", { if (uri instanceof Ci.nsIFileURL) var file = File(uri.file); - let isPlugin = array.nth(io.getRuntimeDirectories("plugins"), - dir => dir.contains(file, true), - 0); + let isPlugin = io.getRuntimeDirectories("plugins") + .find(dir => dir.contains(file, true)); let name = isPlugin && file && file.getRelativeDescriptor(isPlugin) .replace(File.PATH_SEP, "-"); let id = util.camelCase(name.replace(/\.[^.]*$/, "")); - let self = Set.has(this.pluginModules, canonical) && this.pluginModules[canonical]; + let self = hasOwnProperty(this.pluginModules, canonical) && this.pluginModules[canonical]; if (!self) { self = Object.create(jsmodules); @@ -336,7 +340,7 @@ var Contexts = Module("contexts", { delete contexts.pluginModules[canonical]; } - for each (let { plugins } in overlay.modules) + for (let { plugins } of overlay.modules) if (plugins[this.NAME] == this) delete plugins[this.name]; }) @@ -411,7 +415,7 @@ var Contexts = Module("contexts", { initializedGroups: function (hive) let (need = hive ? [hive] : Object.keys(this.hives)) - this.groupList.filter(group => need.some(Set.has(group))), + this.groupList.filter(group => need.some(hasOwnProperty.bind(null, group))), addGroup: function addGroup(name, description, filter, persist, replace) { let group = this.getGroup(name); @@ -468,7 +472,7 @@ var Contexts = Module("contexts", { getGroup: function getGroup(name, hive) { if (name === "default") var group = this.context && this.context.context && this.context.context.GROUP; - else if (Set.has(this.groupMap, name)) + else if (hasOwnProperty(this.groupMap, name)) group = this.groupMap[name]; if (group && hive) @@ -478,14 +482,8 @@ var Contexts = Module("contexts", { getDocs: function getDocs(context) { try { - if (isinstance(context, ["Sandbox"])) { - let info = "INFO" in context && Cu.evalInSandbox("this.INFO instanceof XML ? INFO.toXMLString() : this.INFO", context); - return /^ -// Copyright (c) 2008-2012 Kris Maglione -// -// This work is licensed for reuse under an MIT license. Details are -// given in the LICENSE.txt file included with this file. -/* use strict */ - -defineModule("dom", { - exports: ["fromXML"] -}); - -lazyRequire("highlight", ["highlight"]); - -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"); -var NS = Namespace("dactyl", "http://vimperator.org/namespaces/liberator"); - -function fromXML(node, doc, nodes) { - XML.ignoreWhitespace = XML.prettyPrinting = false; - if (typeof node === "string") // Sandboxes can't currently pass us XML objects. - node = XML(node); - - if (node.length() != 1) { - let domnode = doc.createDocumentFragment(); - for each (let child in node) - domnode.appendChild(fromXML(child, doc, nodes)); - return domnode; - } - - switch (node.nodeKind()) { - case "text": - return doc.createTextNode(String(node)); - case "element": - let domnode = doc.createElementNS(node.namespace(), node.localName()); - - for each (let attr in node.@*::*) - if (attr.name() != "highlight") - domnode.setAttributeNS(attr.namespace(), attr.localName(), String(attr)); - - for each (let child in node.*::*) - domnode.appendChild(fromXML(child, doc, nodes)); - if (nodes && node.@key) - nodes[node.@key] = domnode; - - if ("@highlight" in node) - highlight.highlightNode(domnode, String(node.@highlight), nodes || true); - return domnode; - default: - return null; - } -} - -// vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript: diff --git a/common/modules/dom.jsm b/common/modules/dom.jsm index 6831bd3..72d8013 100644 --- a/common/modules/dom.jsm +++ b/common/modules/dom.jsm @@ -1,5 +1,5 @@ // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -55,8 +55,6 @@ var DOM = Class("DOM", { if (val == null) ; - else if (typeof val == "xml" && context instanceof Ci.nsIDOMDocument) - this[length++] = DOM.fromXML(val, context, this.nodes); else if (DOM.isJSONXML(val)) { if (context instanceof Ci.nsIDOMDocument) this[length++] = DOM.fromJSON(val, context, this.nodes); @@ -476,7 +474,7 @@ var DOM = Class("DOM", { let charset = doc.characterSet; let converter = services.CharsetConv(charset); - for each (let cs in form.acceptCharset.split(/\s*,\s*|\s+/)) { + for (let cs of form.acceptCharset.split(/\s*,\s*|\s+/)) { let c = services.CharsetConv(cs); if (c) { converter = services.CharsetConv(cs); @@ -627,7 +625,7 @@ var DOM = Class("DOM", { if (callable(v)) v = v.call(this, elem, i); - if (Set.has(hooks, k) && hooks[k].set) + if (hasOwnProperty(hooks, k) && hooks[k].set) hooks[k].set.call(this, elem, v, k); else if (v == null) elem.removeAttributeNS(ns, k); @@ -639,7 +637,7 @@ var DOM = Class("DOM", { if (!this.length) return null; - if (Set.has(hooks, key) && hooks[key].get) + if (hasOwnProperty(hooks, key) && hooks[key].get) return hooks[key].get.call(this, this[0], key); if (!this[0].hasAttributeNS(ns, key)) @@ -1071,7 +1069,7 @@ var DOM = Class("DOM", { keyTable: Class.Memoize(function (prop) this.init()[prop]), key_code: Class.Memoize(function (prop) this.init()[prop]), key_key: Class.Memoize(function (prop) this.init()[prop]), - pseudoKeys: Set(["count", "leader", "nop", "pass"]), + pseudoKeys: RealSet(["count", "leader", "nop", "pass"]), /** * Converts a user-input string of keys into a canonical @@ -1094,7 +1092,7 @@ var DOM = Class("DOM", { * @returns {string} Canonical form. */ canonicalKeys: function canonicalKeys(keys, unknownOk=true) { - return this.parse(keys, unknownOk).map(this.closure.stringify).join(""); + return this.parse(keys, unknownOk).map(this.bound.stringify).join(""); }, iterKeys: function iterKeys(keys) iter(function () { @@ -1141,19 +1139,19 @@ var DOM = Class("DOM", { } else { let [match, modifier, keyname] = evt_str.match(/^<((?:[*12CASM⌘]-)*)(.+?)>$/i) || [false, '', '']; - modifier = Set(modifier.toUpperCase()); + modifier = RealSet(modifier.toUpperCase()); keyname = keyname.toLowerCase(); evt_obj.dactylKeyname = keyname; if (/^u[0-9a-f]+$/.test(keyname)) keyname = String.fromCharCode(parseInt(keyname.substr(1), 16)); if (keyname && (unknownOk || keyname.length == 1 || /mouse$/.test(keyname) || - this.key_code[keyname] || Set.has(this.pseudoKeys, keyname))) { - evt_obj.globKey ="*" in modifier; - evt_obj.ctrlKey ="C" in modifier; - evt_obj.altKey ="A" in modifier; - evt_obj.shiftKey ="S" in modifier; - evt_obj.metaKey ="M" in modifier || "⌘" in modifier; + this.key_code[keyname] || this.pseudoKeys.has(keyname))) { + evt_obj.globKey = modifier.has("*"); + evt_obj.ctrlKey = modifier.has("C"); + evt_obj.altKey = modifier.has("A"); + evt_obj.shiftKey = modifier.has("S"); + evt_obj.metaKey = modifier.has("M") || modifier.has("⌘"); evt_obj.dactylShift = evt_obj.shiftKey; if (keyname.length == 1) { // normal characters @@ -1164,11 +1162,11 @@ var DOM = Class("DOM", { evt_obj.charCode = keyname.charCodeAt(0); evt_obj.keyCode = this.key_code[keyname.toLowerCase()]; } - else if (Set.has(this.pseudoKeys, keyname)) { + else if (this.pseudoKeys.has(keyname)) { evt_obj.dactylString = "<" + this.key_key[keyname] + ">"; } else if (/mouse$/.test(keyname)) { // mouse events - evt_obj.type = (/2-/.test(modifier) ? "dblclick" : "click"); + evt_obj.type = (modifier.has("2") ? "dblclick" : "click"); evt_obj.button = ["leftmouse", "middlemouse", "rightmouse"].indexOf(keyname); delete evt_obj.keyCode; delete evt_obj.charCode; @@ -1354,38 +1352,26 @@ var DOM = Class("DOM", { * @param {Node} target The DOM node to which to dispatch the event. * @param {Event} event The event to dispatch. */ - dispatch: Class.Memoize(function () - config.haveGecko("2b") - ? function dispatch(target, event, extra) { - try { - this.feedingEvent = extra; - - if (target instanceof Ci.nsIDOMElement) - // This causes a crash on Gecko<2.0, it seems. - return (target.ownerDocument || target.document || target).defaultView - .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils) - .dispatchDOMEventViaPresShell(target, event, true); - else { - target.dispatchEvent(event); - return !event.defaultPrevented; - } - } - catch (e) { - util.reportError(e); - } - finally { - this.feedingEvent = null; - } + dispatch: function dispatch(target, event, extra) { + try { + this.feedingEvent = extra; + + if (target instanceof Ci.nsIDOMElement) + return (target.ownerDocument || target.document || target).defaultView + .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils) + .dispatchDOMEventViaPresShell(target, event, true); + else { + target.dispatchEvent(event); + return !event.defaultPrevented; } - : function dispatch(target, event, extra) { - try { - this.feedingEvent = extra; - target.dispatchEvent(update(event, extra)); - } - finally { - this.feedingEvent = null; - } - }) + } + catch (e) { + util.reportError(e); + } + finally { + this.feedingEvent = null; + } + } }), createContents: Class.Memoize(() => services.has("dactyl") && services.dactyl.createContents @@ -1410,9 +1396,9 @@ var DOM = Class("DOM", { * The set of input element type attribute values that mark the element as * an editable field. */ - editableInputs: Set(["date", "datetime", "datetime-local", "email", "file", - "month", "number", "password", "range", "search", - "tel", "text", "time", "url", "week"]), + editableInputs: RealSet(["date", "datetime", "datetime-local", "email", "file", + "month", "number", "password", "range", "search", + "tel", "text", "time", "url", "week"]), /** * Converts a given DOM Node, Range, or Selection to a string. If @@ -1494,7 +1480,7 @@ var DOM = Class("DOM", { * @returns {boolean} True when the patterns are all valid. */ validateMatcher: function validateMatcher(list) { - return this.testValues(list, DOM.closure.testMatcher); + return this.testValues(list, DOM.bound.testMatcher); }, testMatcher: function testMatcher(value) { @@ -1522,21 +1508,6 @@ var DOM = Class("DOM", { return str.replace(regexp, m => map[m]); }, - /** - * Converts an E4X XML literal to a DOM node. Any attribute named - * highlight is present, it is transformed into dactyl:highlight, - * and the named highlight groups are guaranteed to be loaded. - * - * @param {Node} node - * @param {Document} doc - * @param {Object} nodes If present, nodes with the "key" attribute are - * stored here, keyed to the value thereof. - * @returns {Node} - */ - fromXML: deprecated("DOM.fromJSON", { get: function fromXML() - prefs.get("javascript.options.xml.chrome") !== false - && require("dom-e4x").fromXML }), - fromJSON: update(function fromJSON(xml, doc, nodes, namespaces) { if (!doc) doc = document; @@ -1552,8 +1523,6 @@ var DOM = Class("DOM", { if (isinstance(args, ["String", "Number", "Boolean", _])) return doc.createTextNode(args); - if (isXML(args)) - return DOM.fromXML(args, doc, nodes); if (isObject(args) && "toDOM" in args) return args.toDOM(doc, namespaces, nodes); if (args instanceof Ci.nsIDOMNode) @@ -1645,7 +1614,7 @@ var DOM = Class("DOM", { toPrettyXML: function toPrettyXML(xml, asXML, indent, namespaces) { const INDENT = indent || " "; - const EMPTY = Set("area base basefont br col frame hr img input isindex link meta param" + const EMPTY = RealSet("area base basefont br col frame hr img input isindex link meta param" .split(" ")); function namespaced(namespaces, namespace, localName) { @@ -1678,11 +1647,6 @@ var DOM = Class("DOM", { return indent + DOM.escapeHTML(String(args), true); - if (isXML(args)) - return indent + - args.toXMLString() - .replace(/^/m, indent); - if (isObject(args) && "toDOM" in args) return indent + services.XMLSerializer() @@ -1754,7 +1718,7 @@ var DOM = Class("DOM", { let res = [indent, "<", name]; for (let [key, val] in Iterator(attr)) { - if (Set.has(skipAttr, key)) + if (hasOwnProperty(skipAttr, key)) continue; let vals = parseNamespace(key); @@ -1770,7 +1734,7 @@ var DOM = Class("DOM", { '="', DOM.escapeHTML(val), '"'); } - if ((vals[0] || namespaces[""]) == String(XHTML) && Set.has(EMPTY, vals[1]) + if ((vals[0] || namespaces[""]) == String(XHTML) && EMPTY.has(vals[1]) || asXML && !args.length) res.push("/>"); else { @@ -1893,7 +1857,7 @@ var DOM = Class("DOM", { Object.keys(DOM.Event.types).forEach(function (event) { let name = event.replace(/-(.)/g, (m, m1) => m1.toUpperCase()); - if (!Set.has(DOM.prototype, name)) + if (!hasOwnProperty(DOM.prototype, name)) DOM.prototype[name] = function _event(arg, extra) { return this[callable(arg) ? "listen" : "dispatch"](event, arg, extra); diff --git a/common/modules/downloads.jsm b/common/modules/downloads.jsm index cbcd9a6..3f4a350 100644 --- a/common/modules/downloads.jsm +++ b/common/modules/downloads.jsm @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2013 Kris Maglione +// Copyright (c) 2011-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -10,20 +10,22 @@ defineModule("downloads", { }); lazyRequire("overlay", ["overlay"]); +lazyRequire("promises", ["Task", "promises"]); -Cu.import("resource://gre/modules/DownloadUtils.jsm", this); +lazyRequire("resource://gre/modules/Downloads.jsm", ["Downloads"]); +lazyRequire("resource://gre/modules/DownloadUtils.jsm", ["DownloadUtils"]); var MAX_LOAD_TIME = 10 * 1000; let prefix = "DOWNLOAD_"; var states = iter([v, k.slice(prefix.length).toLowerCase()] for ([k, v] in Iterator(Ci.nsIDownloadManager)) - if (k.indexOf(prefix) == 0)) + if (k.startsWith(prefix))) .toObject(); var Download = Class("Download", { - init: function init(id, list) { - this.download = services.downloadManager.getDownload(id); + init: function init(download, list) { + this.download = download; this.list = list; this.nodes = { @@ -39,11 +41,9 @@ var Download = Class("Download", { this.targetFile.path]]], ["td", { highlight: "DownloadState", key: "state" }], ["td", { highlight: "DownloadButtons Buttons" }, - ["a", { highlight: "Button", href: "javascript:0", key: "pause" }, _("download.action.Pause")], + ["a", { highlight: "Button", href: "javascript:0", key: "stop" }, _("download.action.Stop")], ["a", { highlight: "Button", href: "javascript:0", key: "remove" }, _("download.action.Remove")], ["a", { highlight: "Button", href: "javascript:0", key: "resume" }, _("download.action.Resume")], - ["a", { highlight: "Button", href: "javascript:0", key: "retry" }, _("download.action.Retry")], - ["a", { highlight: "Button", href: "javascript:0", key: "cancel" }, _("download.action.Cancel")], ["a", { highlight: "Button", href: "javascript:0", key: "delete" }, _("download.action.Delete")]], ["td", { highlight: "DownloadProgress", key: "progress" }, ["span", { highlight: "DownloadProgressHave", key: "progressHave" }], @@ -53,8 +53,8 @@ var Download = Class("Download", { ["td", { highlight: "DownloadSpeed", key: "speed" }], ["td", { highlight: "DownloadTime", key: "time" }], ["td", {}, - ["a", { highlight: "DownloadSource", key: "source", href: this.source.spec }, - this.source.spec]]], + ["a", { highlight: "DownloadSource", key: "source", href: this.source.url }, + this.source.url]]], this.list.document, this.nodes); this.nodes.launch.addEventListener("click", (event) => { @@ -68,38 +68,40 @@ var Download = Class("Download", { return this; }, + get active() !this.stopped, + + get targetFile() File(this.download.target.path), + + get displayName() this.targetFile.leafName, + get status() states[this.state], inState: function inState(states) states.indexOf(this.status) >= 0, - get alive() this.inState(["downloading", "notstarted", "paused", "queued", "scanning"]), - allowedCommands: Class.Memoize(function () let (self = this) ({ - get cancel() self.cancelable && self.inState(["downloading", "paused", "starting"]), - get delete() !this.cancel && self.targetFile.exists(), - get launch() self.targetFile.exists() && self.inState(["finished"]), - get pause() self.inState(["downloading"]), - get remove() self.inState(["blocked_parental", "blocked_policy", - "canceled", "dirty", "failed", "finished"]), - get resume() self.resumable && self.inState(["paused"]), - get retry() self.inState(["canceled", "failed"]) + get delete() !self.active && (self.targetFile.exists() || self.hasPartialData), + get launch() self.targetFile.exists() && self.succeeded, + get stop() self.active, + get remove() !self.active, + get resume() self.canceled })), command: function command(name) { - util.assert(Set.has(this.allowedCommands, name), _("download.unknownCommand")); + util.assert(hasOwnProperty(this.allowedCommands, name), _("download.unknownCommand")); util.assert(this.allowedCommands[name], _("download.commandNotAllowed")); - if (Set.has(this.commands, name)) + if (hasOwnProperty(this.commands, name)) this.commands[name].call(this); - else - services.downloadManager[name + "Download"](this.id); }, commands: { - delete: function delete_() { - this.targetFile.remove(false); + delete: promises.task(function delete_() { + if (this.hasPartialData) + yield this.removePartialData(); + else if (this.targetFile.exists()) + this.targetFile.remove(false); this.updateStatus(); - }, + }), launch: function launch() { // Behavior mimics that of the builtin Download Manager. function action() { @@ -127,42 +129,52 @@ var Download = Class("Download", { }); else action.call(this); - } + }, + resume: function resume() { + this.download.start(); + }, + remove: promises.task(function remove() { + yield this.list.list.remove(this.download); + yield this.download.finalize(true); + }), + stop: function stop() { + this.download.cancel(); + }, }, _compare: { - active: (a, b) => a.alive - b.alive, + active: (a, b) => a.active - b.active, complete: (a, b) => a.percentComplete - b.percentComplete, - date: (a, b) => a.startTime - b.startTime, + date: (a, b) => a.startTime - b.startTime, filename: (a, b) => String.localeCompare(a.targetFile.leafName, b.targetFile.leafName), - size: (a, b) => a.size - b.size, - speed: (a, b) => a.speed - b.speed, - time: (a, b) => a.timeRemaining - b.timeRemaining, - url: (a, b) => String.localeCompare(a.source.spec, b.source.spec) + size: (a, b) => a.totalBytes - b.totalBytes, + speed: (a, b) => a.speed - b.speed, + time: (a, b) => a.timeRemaining - b.timeRemaining, + url: (a, b) => String.localeCompare(a.source.url, b.source.url) }, compare: function compare(other) values(this.list.sortOrder).map(function (order) { let val = this._compare[order.substr(1)](this, other); return (order[0] == "-") ? -val : val; - }, this).nth(util.identity, 0) || 0, + }, this).find(util.identity) || 0, timeRemaining: Infinity, updateProgress: function updateProgress() { let self = this.__proto__; - if (this.amountTransferred === this.size) { + if (!this.active) { this.nodes.speed.textContent = ""; this.nodes.time.textContent = ""; } else { this.nodes.speed.textContent = util.formatBytes(this.speed, 1, true) + "/s"; - if (this.speed == 0 || this.size == 0) + if (this.speed == 0 || !this.hasProgress) this.nodes.time.textContent = _("download.unknown"); else { - let seconds = (this.size - this.amountTransferred) / this.speed; + let seconds = (this.totalBytes - this.currentBytes) / this.speed; [, self.timeRemaining] = DownloadUtils.getTimeLeft(seconds, this.timeRemaining); if (this.timeRemaining) this.nodes.time.textContent = util.formatSeconds(this.timeRemaining); @@ -171,17 +183,20 @@ var Download = Class("Download", { } } - let total = this.nodes.progressTotal.textContent = this.size || !this.nActive ? util.formatBytes(this.size, 1, true) - : _("download.unknown"); + let total = this.nodes.progressTotal.textContent = + this.hasProgress && (this.totalBytes || !this.nActive) + ? util.formatBytes(this.totalBytes, 1, true) + : _("download.unknown"); + let suffix = RegExp(/( [a-z]+)?$/i.exec(total)[0] + "$"); - this.nodes.progressHave.textContent = util.formatBytes(this.amountTransferred, 1, true).replace(suffix, ""); + this.nodes.progressHave.textContent = util.formatBytes(this.currentBytes, 1, true).replace(suffix, ""); - this.nodes.percent.textContent = this.size ? Math.round(this.amountTransferred * 100 / this.size) + "%" : ""; + this.nodes.percent.textContent = this.hasProgress ? this.progress + "%" : ""; }, updateStatus: function updateStatus() { - this.nodes.row[this.alive ? "setAttribute" : "removeAttribute"]("active", "true"); + this.nodes.row[this.active ? "setAttribute" : "removeAttribute"]("active", "true"); this.nodes.row.setAttribute("status", this.status); this.nodes.state.textContent = util.capitalize(this.status); @@ -193,14 +208,6 @@ var Download = Class("Download", { this.updateProgress(); } }); -Object.keys(XPCOMShim([Ci.nsIDownload])).forEach(function (key) { - if (!(key in Download.prototype)) - Object.defineProperty(Download.prototype, key, { - get: function get() this.download[key], - set: function set(val) this.download[key] = val, - configurable: true - }); -}); var DownloadList = Class("DownloadList", XPCOM([Ci.nsIDownloadProgressListener, @@ -213,12 +220,13 @@ var DownloadList = Class("DownloadList", this.nodes = { commandTarget: this }; - this.downloads = {}; + this.downloads = Map(); }, cleanup: function cleanup() { - this.observe.unregister(); - services.downloadManager.removeListener(this); + if (this.list) + this.list.removeView(this); + this.dead = true; }, message: Class.Memoize(function () { @@ -258,40 +266,44 @@ var DownloadList = Class("DownloadList", this.index = Array.indexOf(this.nodes.list.childNodes, this.nodes.head); - let start = Date.now(); - for (let row in iter(services.downloadManager.DBConnection - .createStatement("SELECT id FROM moz_downloads"))) { - if (Date.now() - start > MAX_LOAD_TIME) { - util.dactyl.warn(_("download.givingUpAfter", (Date.now() - start) / 1000)); - break; + Task.spawn(function () { + this.list = yield Downloads.getList(Downloads.ALL); + + let start = Date.now(); + for (let download of yield this.list.getAll()) { + if (Date.now() - start > MAX_LOAD_TIME) { + util.dactyl.warn(_("download.givingUpAfter", (Date.now() - start) / 1000)); + break; + } + this.addDownload(download); } - this.addDownload(row.id); - } - this.update(); + this.update(); - util.addObserver(this); - services.downloadManager.addListener(this); + if (!this.dead) + this.list.addView(this); + }.bind(this)); return this.nodes.list; }), - addDownload: function addDownload(id) { - if (!(id in this.downloads)) { - let download = Download(id, this); - if (this.filter && download.displayName.indexOf(this.filter) === -1) + addDownload: function addDownload(download) { + if (!this.downloads.has(download)) { + download = Download(download, this); + if (this.filter && !download.displayName.contains(this.filter)) return; - this.downloads[id] = download; - let index = values(this.downloads).sort((a, b) => a.compare(b)) - .indexOf(download); + this.downloads.set(download.download, download); + let index = values(this.downloads).toArray() + .sort((a, b) => a.compare(b)) + .indexOf(download); this.nodes.list.insertBefore(download.nodes.row, this.nodes.list.childNodes[index + this.index + 1]); } }, - removeDownload: function removeDownload(id) { - if (id in this.downloads) { - this.nodes.list.removeChild(this.downloads[id].nodes.row); - delete this.downloads[id]; + removeDownload: function removeDownload(download) { + if (this.downloads.has(download)) { + this.nodes.list.removeChild(this.downloads.get(download).nodes.row); + delete this.downloads.delete(download); } }, @@ -301,17 +313,17 @@ var DownloadList = Class("DownloadList", }, allowedCommands: Class.Memoize(function () let (self = this) ({ - get clear() values(self.downloads).some(dl => dl.allowedCommands.remove) + get clear() iter(self.downloads.values()).some(dl => dl.allowedCommands.remove) })), commands: { clear: function () { - services.downloadManager.cleanUp(); + this.list.removeFinished(); } }, sort: function sort() { - let list = values(this.downloads).sort((a, b) => a.compare(b)); + let list = iter(this.downloads.values()).sort((a, b) => a.compare(b)); for (let [i, download] in iter(list)) if (this.nodes.list.childNodes[i + 1] != download.nodes.row) @@ -335,16 +347,19 @@ var DownloadList = Class("DownloadList", timeRemaining: Infinity, updateProgress: function updateProgress() { - let downloads = values(this.downloads).toArray(); - let active = downloads.filter(d => d.alive); + let downloads = iter(this.downloads.values()).toArray(); + let active = downloads.filter(d => d.active); let self = Object.create(this); - for (let prop in values(["amountTransferred", "size", "speed", "timeRemaining"])) + for (let prop in values(["currentBytes", "totalBytes", "speed", "timeRemaining"])) this[prop] = active.reduce((acc, dl) => dl[prop] + acc, 0); + this.hasProgress = active.every(d => d.hasProgress); + this.progress = Math.round((this.currentBytes / this.totalBytes) * 100); + this.nActive = active.length; + Download.prototype.updateProgress.call(self); - this.nActive = active.length; if (active.length) this.nodes.total.textContent = _("download.nActive", active.length); else for (let key in values(["total", "percent", "speed", "time"])) @@ -354,68 +369,87 @@ var DownloadList = Class("DownloadList", this.sort(); }, - observers: { - "download-manager-remove-download": function (id) { - if (id == null) - id = [k for ([k, dl] in iter(this.downloads)) if (dl.allowedCommands.remove)]; - else - id = [id.QueryInterface(Ci.nsISupportsPRUint32).data]; + onDownloadAdded: function onDownloadAdded(download) { + this.addDownload(download); - Array.concat(id).map(this.closure.removeDownload); - this.update(); - } + this.modules.mow.resize(false); + this.nodes.list.scrollIntoView(false); }, - onDownloadStateChange: function (state, download) { - try { - if (download.id in this.downloads) - this.downloads[download.id].updateStatus(); - else { - this.addDownload(download.id); + onDownloadRemoved: function onDownloadRemoved(download) { + this.removeDownload(download); + }, + + onDownloadChanged: function onDownloadChanged(download) { + if (this.downloads.has(download)) { + download = this.downloads.get(download) + + download.updateStatus(); + download.updateProgress(); - this.modules.mow.resize(false); - this.nodes.list.scrollIntoView(false); - } this.update(); if (this.shouldSort("active")) this.sort(); } - catch (e) { - util.reportError(e); - } - }, - - onProgressChange: function (webProgress, request, - curProgress, maxProgress, - curTotalProgress, maxTotalProgress, - download) { - try { - if (download.id in this.downloads) - this.downloads[download.id].updateProgress(); - this.updateProgress(); - } - catch (e) { - util.reportError(e); - } } }); +["canceled", + "contentType", + "currentBytes", + "error", + "hasPartialData", + "hasProgress", + "launchWhenSucceeded", + "launcherPath", + "progress", + "saver", + "source", + "speed", + "startTime", + "stopped", + "succeeded", + "target", + "totalBytes", + "tryToKeepPartialData"].forEach(key => { + if (!(key in Download.prototype)) + Object.defineProperty(Download.prototype, key, { + get: function get() this.download[key], + set: function set(val) this.download[key] = val, + configurable: true + }); +}); + -var Downloads = Module("downloads", XPCOM(Ci.nsIDownloadProgressListener), { +var Downloads_ = Module("downloads", XPCOM(Ci.nsIDownloadProgressListener), { init: function () { - services.downloadManager.addListener(this); + Downloads.getList(Downloads.ALL).then(list => { + this.list = list; + if (!this.dead) + this.list.addView(this); + }); }, cleanup: function destroy() { - services.downloadManager.removeListener(this); + if (this.list) + this.list.removeView(this); + this.dead = true; + }, + + onDownloadAdded: function onDownloadAdded(download) { }, - 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; + onDownloadRemoved: function onDownloadRemoved(download) { + }, + + onDownloadChanged: function onDownloadChanged(download) { + if (download.succeeded) { + let target = File(download.target.path); + + let url = download.source.url; + let title = target.leafName; + let file = target.path; + let size = download.totalBytes; overlay.modules.forEach(function (modules) { modules.dactyl.echomsg({ domains: [util.getHost(url)], message: _("io.downloadFinished", title, file) }, @@ -451,7 +485,7 @@ var Downloads = Module("downloads", XPCOM(Ci.nsIDownloadProgressListener), { commands.add(["dlc[lear]"], "Clear completed downloads", - function (args) { services.downloadManager.cleanUp(); }); + function (args) { downloads.list.removeFinished(); }); }, options: function initOptions(dactyl, modules, window) { const { options } = modules; @@ -489,9 +523,9 @@ var Downloads = Module("downloads", XPCOM(Ci.nsIDownloadProgressListener), { }, completer: function (context, extra) { - let seen = Set.has(Set(extra.values.map(val => val.substr(1)))); + let seen = RealSet(extra.values.map(val => val.substr(1))); - context.completions = iter(this.values).filter(([k, v]) => !seen(k)) + context.completions = iter(this.values).filter(([k, v]) => !seen.has(k)) .map(([k, v]) => [["+" + k, [v, " (", _("sort.ascending"), ")"].join("")], ["-" + k, [v, " (", _("sort.descending"), ")"].join("")]]) .flatten().array; @@ -500,9 +534,9 @@ var Downloads = Module("downloads", XPCOM(Ci.nsIDownloadProgressListener), { has: function () Array.some(arguments, val => this.value.some(v => v.substr(1) == val)), validator: function (value) { - let seen = {}; - return value.every(val => /^[+-]/.test(val) && Set.has(this.values, val.substr(1)) - && !Set.add(seen, val.substr(1))) + let seen = RealSet(); + return value.every(val => /^[+-]/.test(val) && hasOwnProperty(this.values, val.substr(1)) + && !seen.add(val.substr(1))) && value.length; } }); diff --git a/common/modules/finder.jsm b/common/modules/finder.jsm index 738b177..2f263ba 100644 --- a/common/modules/finder.jsm +++ b/common/modules/finder.jsm @@ -1,4 +1,4 @@ -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -253,10 +253,10 @@ var RangeFinder = Module("rangefinder", { get prompt() this.mode === modules.modes.FIND_BACKWARD ? "?" : "/", - get onCancel() modules.rangefinder.closure.onCancel, - get onChange() modules.rangefinder.closure.onChange, - get onHistory() modules.rangefinder.closure.onHistory, - get onSubmit() modules.rangefinder.closure.onSubmit + get onCancel() modules.rangefinder.bound.onCancel, + get onChange() modules.rangefinder.bound.onChange, + get onHistory() modules.rangefinder.bound.onHistory, + get onSubmit() modules.rangefinder.bound.onSubmit }); }, mappings: function initMappings(dactyl, modules, window) { @@ -626,7 +626,7 @@ var RangeFind = Class("RangeFind", { if (!this.matchCase) pattern = pattern.toLowerCase(); - if (!again && (pattern === "" || pattern.indexOf(this.lastString) !== 0 || this.backward)) { + if (!again && (pattern === "" || !pattern.startsWith(this.lastString) || this.backward)) { if (!private_) this.range.deselect(); if (pattern === "") @@ -707,12 +707,12 @@ var RangeFind = Class("RangeFind", { addListeners: function addListeners() { for (let range in array.iterValues(this.ranges)) - range.window.addEventListener("unload", this.closure.onUnload, true); + range.window.addEventListener("unload", this.bound.onUnload, true); }, purgeListeners: function purgeListeners() { for (let range in array.iterValues(this.ranges)) try { - range.window.removeEventListener("unload", this.closure.onUnload, true); + range.window.removeEventListener("unload", this.bound.onUnload, true); } catch (e if e.result === Cr.NS_ERROR_FAILURE) {} }, diff --git a/common/modules/help.jsm b/common/modules/help.jsm index f477411..cc66a59 100644 --- a/common/modules/help.jsm +++ b/common/modules/help.jsm @@ -1,4 +1,4 @@ -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -201,7 +201,7 @@ var Help = Module("Help", { ["toc", { start: "2" }], body]); - }); + }, true); }, initialize: function initialize() { @@ -237,7 +237,7 @@ var Help = Module("Help", { * @returns {string} */ findHelp: function (topic, consolidated) { - if (!consolidated && Set.has(help.files, topic)) + if (!consolidated && hasOwnProperty(help.files, topic)) return topic; let items = modules.completion._runCompleter("help", topic, null, !!consolidated).items; let partialMatch = null; @@ -268,7 +268,7 @@ var Help = Module("Help", { if (!topic) { let helpFile = consolidated ? "all" : modules.options["helpfile"]; - if (Set.has(help.files, helpFile)) + if (hasOwnProperty(help.files, helpFile)) dactyl.open("dactyl://help/" + helpFile, { from: "help" }); else dactyl.echomsg(_("help.noFile", helpFile.quote())); @@ -304,7 +304,7 @@ var Help = Module("Help", { addURIEntry(file, "data:text/plain;charset=UTF-8," + encodeURI(data)); } - let empty = Set("area base basefont br col frame hr img input isindex link meta param" + let empty = RealSet("area base basefont br col frame hr img input isindex link meta param" .split(" ")); function fix(node) { switch (node.nodeType) { @@ -319,13 +319,13 @@ var Help = Module("Help", { for (let { name, value } in array.iterValues(node.attributes)) { if (name == "dactyl:highlight") { - Set.add(styles, value); + styles.add(value); name = "class"; value = "hl-" + value; } if (name == "href") { value = node.href || value; - if (value.indexOf("dactyl://help-tag/") == 0) { + if (value.startsWith("dactyl://help-tag/")) { try { let uri = services.io.newChannel(value, null, null).originalURI; value = uri.spec == value ? "javascript:;" : uri.path.substr(1); @@ -345,7 +345,7 @@ var Help = Module("Help", { data.push(" ", name, '="', DOM.escapeHTML(value), '"'); } - if (node.localName in empty) + if (empty.has(node.localName)) data.push(" />"); else { data.push(">"); @@ -362,7 +362,7 @@ var Help = Module("Help", { let { buffer, content, events } = modules; let chromeFiles = {}; - let styles = {}; + let styles = RealSet(); for (let [file, ] in Iterator(help.files)) { let url = "dactyl://help/" + file; @@ -380,7 +380,7 @@ var Help = Module("Help", { addDataEntry(file + ".xhtml", data.join("")); } - let data = [h for (h in highlight) if (Set.has(styles, h.class) || /^Help/.test(h.class))] + let data = [h for (h in highlight) if (styles.has(h.class) || /^Help/.test(h.class))] .map(h => h.selector .replace(/^\[.*?=(.*?)\]/, ".hl-$1") .replace(/html\|/g, "") + "\t" + "{" + h.cssText + "}") diff --git a/common/modules/highlight.jsm b/common/modules/highlight.jsm index e25b7cc..501edde 100644 --- a/common/modules/highlight.jsm +++ b/common/modules/highlight.jsm @@ -1,4 +1,4 @@ -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -125,7 +125,7 @@ var Highlights = Module("Highlight", { if (/^[[>+: ]/.test(args[1])) obj.selector = this.selector(obj.class) + args[1]; else if (args[1]) - obj.selector = this.selector(args[1]); + obj.selector = this.selector(args[1].replace(/^,/, "")); if (old && old.value != old.defaultValue) obj.value = old.value; @@ -134,8 +134,12 @@ var Highlights = Module("Highlight", { obj.style.enabled = true; else this.loaded.__defineSetter__(obj.class, function () { - delete this[obj.class]; - this[obj.class] = true; + Object.defineProperty(this, obj.class, { + value: true, + configurable: true, + enumerable: true, + writable: true + }); if (obj.class === obj.baseClass) for (let h in highlight) @@ -200,11 +204,11 @@ var Highlights = Module("Highlight", { node.setAttributeNS(NS, "highlight", group); let groups = group.split(" "); - for each (let group in groups) + for (let group of groups) this.loaded[group] = true; if (applyBindings) - for each (let group in groups) { + for (let group of groups) { if (applyBindings.bindings && group in applyBindings.bindings) applyBindings.bindings[group](node, applyBindings); else if (group in template.bindings) @@ -275,7 +279,7 @@ var Highlights = Module("Highlight", { * If Star is provided, the style is applied as an agent sheet. * * The new styles are lazily activated unless Bang or *eager* is - * provided. See {@link Util#xmlToDom}. + * provided. * * @param {string} css The rules to load. See {@link Highlights#css}. * @param {boolean} eager When true, load all provided rules immediately. diff --git a/common/modules/io.jsm b/common/modules/io.jsm index 4a61d67..9de0455 100644 --- a/common/modules/io.jsm +++ b/common/modules/io.jsm @@ -1,7 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2012 by Doug Kearns -// Copyright (c) 2008-2013 Kris Maglione -// Some code based on Venkman +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -16,6 +15,7 @@ defineModule("io", { lazyRequire("config", ["config"]); lazyRequire("contexts", ["Contexts", "contexts"]); +lazyRequire("promises", ["Promise"]); lazyRequire("storage", ["File", "storage"]); lazyRequire("styles", ["styles"]); lazyRequire("template", ["template"]); @@ -42,7 +42,7 @@ var IO = Module("io", { this._oldcwd = null; this._lastRunCommand = ""; // updated whenever the users runs a command with :! - this._scriptNames = []; + this._scriptNames = RealSet(); }, CommandFileMode: Class("CommandFileMode", modules.CommandMode, { @@ -156,7 +156,7 @@ var IO = Module("io", { else if (/\.js$/.test(filename)) { try { var context = contexts.Script(file, params.group); - if (Set.has(this._scriptNames, file.path)) + if (this._scriptNames.has(file.path)) util.flushCache(); dactyl.loadScript(uri.spec, context); @@ -196,7 +196,7 @@ var IO = Module("io", { dactyl.triggerObserver("io.source", context, file, file.lastModifiedTime); } - Set.add(this._scriptNames, file.path); + this._scriptNames.add(file.path); dactyl.echomsg(_("io.sourcingEnd", filename.quote()), 2); dactyl.log(_("dactyl.sourced", filename), 3); @@ -319,7 +319,7 @@ var IO = Module("io", { * @default "" * @returns {File} */ - createTempFile: function createTempFile(ext = "txt", label = "") { + createTempFile: function createTempFile(ext="txt", label="") { let file = services.directory.get("TmpD", Ci.nsIFile); file.append(config.name + label + "." + ext); file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, octal(666)); @@ -447,26 +447,29 @@ var IO = Module("io", { let process = services.Process(file.file); process.run(false, args.map(String), args.length); - try { - if (callable(blocking)) - var timer = services.Timer( - function () { - if (!process.isRunning) { - timer.cancel(); - util.trapErrors(blocking, self, process.exitValue); - } - }, - 100, services.Timer.TYPE_REPEATING_SLACK); - else if (blocking) - while (process.isRunning) - util.threadYield(false, true); - } - catch (e) { - process.kill(); - throw e; + + let deferred = Promise.defer(); + + if (callable(blocking)) + // Deprecated. + deferred.promise.then(blocking); + else if (blocking) { + // Deprecated? + while (process.isRunning) + util.threadYield(false, true); + return process.exitValue; } - return process.exitValue; + let timer = services.Timer( + function () { + if (!process.isRunning) { + timer.cancel(); + deferred.resolve(process.exitValue); + } + }, + 100, services.Timer.TYPE_REPEATING_SLACK); + + return deferred.promise; }, // TODO: when https://bugzilla.mozilla.org/show_bug.cgi?id=68702 is @@ -486,7 +489,7 @@ var IO = Module("io", { system: function system(command, input, callback) { util.dactyl.echomsg(_("io.callingShell", command), 4); - let { shellEscape } = util.closure; + let { shellEscape } = util.bound; return this.withTempFiles(function (stdin, stdout, cmd) { if (input instanceof File) @@ -865,7 +868,7 @@ unlet s:cpo_save commands.add(["scrip[tnames]"], "List all sourced script names", function () { - let names = Object.keys(io._scriptNames); + let names = [k for (k of io._scriptNames)]; if (!names.length) dactyl.echomsg(_("command.scriptnames.none")); else diff --git a/common/modules/javascript.jsm b/common/modules/javascript.jsm index dcf206b..8555cdf 100644 --- a/common/modules/javascript.jsm +++ b/common/modules/javascript.jsm @@ -1,4 +1,4 @@ -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -55,7 +55,8 @@ var JavaScript = Module("javascript", { lazyInit: true, - newContext: function () this.modules.newContext(this.modules.userContext, true, "Dactyl JS Temp Context"), + newContext: function () this.modules.newContext(this.modules.userContext, false, + "Dactyl JS Temp Context"), completers: Class.Memoize(() => Object.create(JavaScript.completers)), @@ -72,22 +73,23 @@ var JavaScript = Module("javascript", { if (obj == null) return; - let seen = isinstance(obj, ["Sandbox"]) ? Set(JavaScript.magicalNames) : {}; + let seen = RealSet(isinstance(obj, ["Sandbox"]) ? JavaScript.magicalNames : []); let globals = values(toplevel && this.window === obj ? this.globalNames : []); if (toplevel && isObject(obj) && "wrappedJSObject" in obj) - if (!Set.add(seen, "wrappedJSObject")) + if (!seen.add("wrappedJSObject")) yield "wrappedJSObject"; - for (let key in iter(globals, properties(obj, !toplevel, true))) - if (!Set.add(seen, key)) + for (let key in iter(globals, properties(obj, !toplevel))) + if (!seen.add(key)) yield key; // Properties aren't visible in an XPCNativeWrapper until // they're accessed. - for (let key in properties(this.getKey(obj, "wrappedJSObject"), !toplevel, true)) + for (let key in properties(this.getKey(obj, "wrappedJSObject"), + !toplevel)) try { - if (key in obj && !Set.has(seen, key)) + if (key in obj && !seen.has(key)) yield key; } catch (e) {} @@ -336,9 +338,6 @@ var JavaScript = Module("javascript", { _complete: function (objects, key, compl, string, last) { const self = this; - if (!getOwnPropertyNames && !services.debugger.isOn && !this.context.message) - this.context.message = /*L*/"For better completion data, please enable the JavaScript debugger (:set jsdebugger)"; - let base = this.context.fork("js", this._top.offset); base.forceAnchored = true; base.filter = last == null ? key : string; @@ -349,7 +348,7 @@ var JavaScript = Module("javascript", { else { base.quote = [last, text => util.escapeString(text, ""), last]; if (prefix) - base.filters.push(item => item.item.indexOf(prefix) === 0); + base.filters.push(item => item.item.startsWith(prefix)); } if (!compl) { @@ -464,7 +463,7 @@ var JavaScript = Module("javascript", { } this.context.getCache("evalled", Object); - this.context.getCache("evalContext", this.closure.newContext); + this.context.getCache("evalContext", this.bound.newContext); // Okay, have parse stack. Figure out what we're completing. @@ -635,7 +634,7 @@ var JavaScript = Module("javascript", { "ROCSSPrimitiveValue", "RangeError", "ReferenceError", "RegExp", "StopIteration", "String", "SyntaxError", "TypeError", "URIError", "Uint16Array", "Uint32Array", "Uint8Array", "XML", "XMLHttpProgressEvent", - "XMLList", "XMLSerializer", "XPCNativeWrapper", "XPCSafeJSWrapper", + "XMLList", "XMLSerializer", "XPCNativeWrapper", "XULControllers", "constructor", "decodeURI", "decodeURIComponent", "encodeURI", "encodeURIComponent", "escape", "eval", "isFinite", "isNaN", "isXMLName", "parseFloat", "parseInt", "undefined", "unescape", "uneval" @@ -694,7 +693,7 @@ var JavaScript = Module("javascript", { completion: function (dactyl, modules, window) { const { completion } = modules; update(modules.completion, { - get javascript() modules.javascript.closure.complete, + get javascript() modules.javascript.bound.complete, javascriptCompleter: JavaScript // Backwards compatibility }); }, @@ -822,8 +821,10 @@ var JavaScript = Module("javascript", { leave: function leave(params) { leave.superapply(this, arguments); - if (!params.push) + if (!params.push) { modes.delay(function () { modes.pop(); }); + Cu.nukeSandbox(this.context); + } }, updatePrompt: function updatePrompt() { diff --git a/common/modules/main.jsm b/common/modules/main.jsm index 19e36b4..80e64a7 100644 --- a/common/modules/main.jsm +++ b/common/modules/main.jsm @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2013 Kris Maglione +// Copyright (c) 2009-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -13,6 +13,8 @@ defineModule("main", { var BASE = "resource://dactyl-content/"; +var global = this; + /** * @class ModuleBase * The base class for all modules. @@ -89,21 +91,89 @@ var Modules = function Modules(window) { Module.list = []; Module.constructors = {}; - const create = window.Object.create.bind(window.Object); + function newContext(proto, normal, name) { + if (normal) + return create(proto); + + sandbox = Components.utils.Sandbox(window, { sandboxPrototype: proto || modules, + sandboxName: name || ("Dactyl Sandbox " + ++_id), + wantXrays: true }); + + // Hack: + // sandbox.Object = jsmodules.Object; + sandbox.File = global.File; + sandbox.Math = global.Math; + sandbox.Set = global.Set; + return sandbox; + }; + const BASES = [BASE, "resource://dactyl-local-content/"]; - jsmodules = Cu.createObjectIn(window); + let proxyCache = {}; + if (config.haveGecko(29)) + var proxy = new Proxy(window, { + get: function window_get(target, prop) { + // `in`, not `hasOwnProperty`, because we want to return + // unbound methods in `Object.prototype` + if (prop in proxyCache) + return proxyCache[prop]; + + let p = target[prop]; + if (callable(p)) + return proxyCache[prop] = p.bind(target); + + return p; + }, + + set: function window_set(target, prop, val) { + return target[prop] = val; + } + }); + else { + // Bug 814892 + let o = {}; + // Oh, the brokenness... See bug 793210 + Object.preventExtensions(o); + proxy = new Proxy(o, { + get: function window_get(target, prop) { + // `in`, not `hasOwnProperty`, because we want to return + // unbound methods in `Object.prototype` + if (prop in proxyCache) + return proxyCache[prop]; + + let p = window[prop]; + if (callable(p)) + return proxyCache[prop] = p.bind(window); + + return p; + }, + + set: function window_set(target, prop, val) { + return window[prop] = val; + }, + + getOwnPropertyDescriptor: function (target, prop) Object.getOwnPropertyDescriptor(window, prop), + getOwnPropertyNames: function (target, prop) Object.getOwnPropertyNames(window), + defineProperty: function (target, prop, desc) Object.defineProperty(window, prop, desc), + deleteProperty: function (target, prop) { delete window[prop]; }, + has: function (target, prop) prop in window, + hasOwn: function (target, prop) hasOwnProperty(window, prop), + enumerate: function (target) (p for (p in window)), + iterate: function (target) (p for (p of window)) + }); + } + + var jsmodules = newContext(proxy, false, "Dactyl `jsmodules`"); jsmodules.NAME = "jsmodules"; + + const create = bind("create", jsmodules.Object); + const modules = update(create(jsmodules), { yes_i_know_i_should_not_report_errors_in_these_branches_thanks: [], jsmodules: jsmodules, - get content() this.config.browser.contentWindow || window.content, - - window: window, - Module: Module, load: function load(script) { @@ -128,24 +198,7 @@ var Modules = function Modules(window) { } }, - newContext: function newContext(proto, normal, name) { - if (normal) - return create(proto); - - if (services.has("dactyl") && services.dactyl.createGlobal) - var sandbox = services.dactyl.createGlobal(); - else - sandbox = Components.utils.Sandbox(window, { sandboxPrototype: proto || modules, - sandboxName: name || ("Dactyl Sandbox " + ++_id), - wantXrays: false }); - - // Hack: - // sandbox.Object = jsmodules.Object; - sandbox.File = jsmodules.File; - sandbox.Math = jsmodules.Math; - sandbox.__proto__ = proto || modules; - return sandbox; - }, + newContext: newContext, get ownPropertyValues() array.compact( Object.getOwnPropertyNames(this) @@ -167,6 +220,7 @@ overlay.overlayWindow(Object.keys(config.overlays), const modules = Modules(window); modules.moduleManager = this; this.modules = modules; + this.jsmodules = modules.jsmodules; window.dactyl = { modules: modules }; @@ -192,8 +246,8 @@ overlay.overlayWindow(Object.keys(config.overlays), this.startTime = Date.now(); this.deferredInit = { load: {} }; - this.seen = {}; - this.loaded = {}; + this.seen = RealSet(); + this.loaded = RealSet(); modules.loaded = this.loaded; this.modules = modules; @@ -225,11 +279,13 @@ overlay.overlayWindow(Object.keys(config.overlays), }, cleanup: function cleanup(window) { - overlay.windows = overlay.windows.filter(w => w != window); + overlay.windows.delete(window); + + Cu.nukeSandbox(this.jsmodules); }, unload: function unload(window) { - for each (let mod in this.modules.moduleList.reverse()) { + for (let mod of this.modules.moduleList.reverse()) { mod.stale = true; if ("destroy" in mod) @@ -244,7 +300,7 @@ overlay.overlayWindow(Object.keys(config.overlays), defineModule.loadLog.push("Loaded in " + (Date.now() - this.startTime) + "ms"); - overlay.windows = array.uniq(overlay.windows.concat(window), true); + overlay.windows.add(window); }, loadModule: function loadModule(module, prereq, frame) { @@ -258,10 +314,10 @@ overlay.overlayWindow(Object.keys(config.overlays), } try { - if (Set.has(loaded, module.className)) + if (loaded.has(module.className)) return; - if (Set.add(seen, module.className)) + if (seen.add(module.className)) throw Error("Module dependency loop."); for (let dep in values(module.requires)) @@ -277,9 +333,9 @@ overlay.overlayWindow(Object.keys(config.overlays), let obj = defineModule.time(module.className, "init", module); Class.replaceProperty(modules, module.className, obj); - Set.add(loaded, module.className); + loaded.add(module.className); - if (loaded.dactyl && obj.signals) + if (loaded.has("dactyl") && obj.signals) modules.dactyl.registerObservers(obj); if (!module.lazyDepends) @@ -300,7 +356,7 @@ overlay.overlayWindow(Object.keys(config.overlays), let className = mod.className || mod.constructor.className; - if (!Set.has(init, className)) { + if (!hasOwnProperty(init, className)) { init[className] = function callee() { function finish() { this.currentDependency = className; @@ -324,11 +380,12 @@ overlay.overlayWindow(Object.keys(config.overlays), let { Module, modules } = this.modules; defineModule.modules.forEach((mod) => { - let names = Set(Object.keys(mod.INIT)); + let names = RealSet(Object.keys(mod.INIT)); if ("init" in mod.INIT) - Set.add(names, "init"); + names.add("init"); - keys(names).forEach((name) => { this.deferInit(name, mod.INIT, mod); }); + for (let name of names) + this.deferInit(name, mod.INIT, mod); }); Module.list.forEach((mod) => { diff --git a/common/modules/messages.jsm b/common/modules/messages.jsm index 36fead2..82c0f3c 100644 --- a/common/modules/messages.jsm +++ b/common/modules/messages.jsm @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2013 Kris Maglione +// Copyright (c) 2011-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -11,7 +11,7 @@ defineModule("messages", { var Messages = Module("messages", { - init: function init(name = "messages") { + init: function init(name="messages") { let self = this; this.name = name; @@ -45,13 +45,21 @@ var Messages = Module("messages", { "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; } })), + .filter(function (bundle) { + try { + bundle.getSimpleEnumeration(); + return true; + } + catch (e) { + return false; + } + })), iterate: function () { - let seen = {}; + let seen = RealSet(); for (let bundle in values(this.bundles)) for (let { key, value } in iter(bundle.getSimpleEnumeration(), Ci.nsIPropertyElement)) - if (!Set.add(seen, key)) + if (!seen.add(key)) yield [key, value]; }, @@ -97,7 +105,7 @@ var Messages = Module("messages", { let { Buffer, commands, hints, io, mappings, modes, options, sanitizer } = overlay.activeModules; file = io.File(file); - function properties(base, iter_, prop = "description") iter(function _properties() { + function properties(base, iter_, prop="description") iter(function _properties() { function key(...args) [base, obj.identifier || obj.name].concat(args).join(".").replace(/[\\:=]/g, "\\$&"); for (var obj in iter_) { @@ -139,9 +147,9 @@ var Messages = Module("messages", { return { configurable: true, enumerable: true, value: this.default, writable: true }; */ - if (!Set.has(obj, "localizedProperties")) - obj.localizedProperties = { __proto__: obj.localizedProperties }; - obj.localizedProperties[prop] = true; + if (!hasOwnProperty(obj, "localizedProperties")) + obj.localizedProperties = RealSet(obj.localizedProperties); + obj.localizedProperties.add(prop); obj[_prop] = this.default; return { diff --git a/common/modules/options.jsm b/common/modules/options.jsm index 77439af..302a1d3 100644 --- a/common/modules/options.jsm +++ b/common/modules/options.jsm @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2013 by Kris Maglione +// Copyright (c) 2008-2014 by Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -65,7 +65,7 @@ var Option = Class("Option", { this.globalValue = this.defaultValue; }, - magicalProperties: Set(["cleanupValue"]), + magicalProperties: RealSet(["cleanupValue"]), /** * @property {string} This option's description, as shown in :listoptions. @@ -95,7 +95,9 @@ var Option = Class("Option", { return this.globalValue = this.defaultValue; }, set globalValue(val) { - options.store.set(this.name, { value: val, time: Date.now() }); + options.store.set(this.name, + { value: this.parse(this.stringify(val)), + time: Date.now() }); }, /** @@ -107,6 +109,8 @@ var Option = Class("Option", { */ parse: function parse(value) Option.dequote(value), + parseKey: function parseKey(value) value, + /** * Returns *values* packed in the appropriate format for the option type. * @@ -140,6 +144,9 @@ var Option = Class("Option", { if ((scope & Option.SCOPE_GLOBAL) && (values == undefined)) values = this.globalValue; + if (hasOwnProperty(this, "_value")) + values = this._value; + if (this.getter) return util.trapErrors(this.getter, this, values); @@ -169,6 +176,7 @@ var Option = Class("Option", { */ if ((scope & Option.SCOPE_GLOBAL) && !skipGlobal) this.globalValue = newValues; + this._value = newValues; this.hasChanged = true; this.setFrom = null; @@ -329,7 +337,7 @@ var Option = Class("Option", { let defaultValue = this._defaultValue; delete this._defaultValue; - if (Set.has(this.modules.config.optionDefaults, this.name)) + if (hasOwnProperty(this.modules.config.optionDefaults, this.name)) defaultValue = this.modules.config.optionDefaults[this.name]; if (defaultValue == null && this.getter) @@ -558,6 +566,11 @@ var Option = Class("Option", { }, this)) }, + parseKey: { + number: Number, + boolean: function boolean(value) value == "true" || value == true ? true : false, + }, + testValues: { regexpmap: function regexpmap(vals, validator) vals.every(re => validator(re.result)), get sitemap() this.regexpmap, @@ -671,8 +684,8 @@ var Option = Class("Option", { values = Array.concat(values); function uniq(ary) { - let seen = {}; - return ary.filter(elem => !Set.add(seen, elem)); + let seen = RealSet(); + return ary.filter(elem => !seen.add(elem)); } switch (operator) { @@ -682,11 +695,11 @@ var Option = Class("Option", { // NOTE: Vim doesn't prepend if there's a match in the current value return uniq(Array.concat(values, this.value), true); case "-": - return this.value.filter(function (item) !Set.has(this, item), Set(values)); + return this.value.filter(function (item) !this.has(item), RealSet(values)); case "=": if (invert) { - let keepValues = this.value.filter(function (item) !Set.has(this, item), Set(values)); - let addValues = values.filter(function (item) !Set.has(this, item), Set(this.value)); + let keepValues = this.value.filter(function (item) !this.has(item), RealSet(values)); + let addValues = values.filter(function (item) !this.has(item), RealSet(this.value)); return addValues.concat(keepValues); } return values; @@ -723,7 +736,9 @@ var Option = Class("Option", { if (isObject(vals) && !isArray(vals)) { let k = values(completions.call(this, { values: {} })).toObject(); let v = values(completions.call(this, { value: "" })).toObject(); - return Object.keys(vals).every(Set.has(k)) && values(vals).every(Set.has(v)); + + return Object.keys(vals).every(hasOwnProperty.bind(null, k)) && + values(vals).every(hasOwnProperty.bind(null, v)); } if (this.values) @@ -732,12 +747,15 @@ var Option = Class("Option", { acceptable = completions.call(this); if (isArray(acceptable)) - acceptable = Set(acceptable.map(([k]) => (k))); + acceptable = RealSet(acceptable.map(([k]) => k)); + else + acceptable = RealSet(this.parseKey(k) + for (k of Object.keys(acceptable))); if (this.type === "regexpmap" || this.type === "sitemap") - return Array.concat(vals).every(re => Set.has(acceptable, re.result)); + return Array.concat(vals).every(re => acceptable.has(re.result)); - return Array.concat(vals).every(Set.has(acceptable)); + return Array.concat(vals).every(v => acceptable.has(v)); }, types: {} @@ -766,6 +784,9 @@ var Option = Class("Option", { if (type in Option.parse) class_.prototype.parse = Option.parse[type]; + if (type in Option.parseKey) + class_.prototype.parseKey = Option.parse[type]; + if (type in Option.stringify) class_.prototype.stringify = Option.stringify[type]; @@ -789,7 +810,7 @@ var OptionHive = Class("OptionHive", Contexts.Hive, { init: function init(group) { init.supercall(this, group); this.values = {}; - this.has = Set.has(this.values); + this.has = v => hasOwnProperty(this.values, v); }, add: function add(names, description, type, defaultValue, extraInfo) { @@ -822,17 +843,18 @@ var Options = Module("options", { opt.set(opt.globalValue, Option.SCOPE_GLOBAL, true); }, window); - modules.cache.register("options.dtd", () => - util.makeDTD( - iter(([["option", o.name, "default"].join("."), - o.type === "string" ? o.defaultValue.replace(/'/g, "''") : - o.defaultValue === true ? "on" : - o.defaultValue === false ? "off" : o.stringDefaultValue] - for (o in self)), + modules.cache.register("options.dtd", + () => util.makeDTD( + iter(([["option", o.name, "default"].join("."), + o.type === "string" ? o.defaultValue.replace(/'/g, "''") : + o.defaultValue === true ? "on" : + o.defaultValue === false ? "off" : o.stringDefaultValue] + for (o in self)), - ([["option", o.name, "type"].join("."), o.type] for (o in self)), + ([["option", o.name, "type"].join("."), o.type] for (o in self)), - config.dtd))); + config.dtd)), + true); }, signals: { @@ -1098,13 +1120,13 @@ var Options = Module("options", { let list = []; function flushList() { - let names = Set(list.map(opt => opt.option ? opt.option.name : "")); + let names = RealSet(list.map(opt => opt.option ? opt.option.name : "")); if (list.length) if (list.some(opt => opt.all)) options.list(opt => !(list[0].onlyNonDefault && opt.isDefault), list[0].scope); else - options.list(opt => Set.has(names, opt.name), + options.list(opt => names.has(opt.name), list[0].scope); list = []; } @@ -1279,12 +1301,12 @@ var Options = Module("options", { // Fill in the current values if we're removing if (opt.operator == "-" && isArray(opt.values)) { - let have = Set([i.text for (i in values(context.allItems.items))]); + let have = RealSet((i.text for (i in values(context.allItems.items)))); context = context.fork("current-values", 0); context.anchored = optcontext.anchored; context.maxItems = optcontext.maxItems; - context.filters.push(i => !Set.has(have, i.text)); + context.filters.push(i => !have.has(i.text)); modules.completion.optionValue(context, opt.name, opt.operator, null, function (context) { context.generate = () => option.value.map(o => [o, ""]); @@ -1313,7 +1335,7 @@ var Options = Module("options", { util.assert(scope == "g:" || scope == null, _("command.let.illegalVar", scope + name)); - util.assert(Set.has(globalVariables, name) || (expr && !op), + util.assert(hasOwnProperty(globalVariables, name) || (expr && !op), _("command.let.undefinedVar", fullName)); if (!expr) @@ -1411,7 +1433,7 @@ var Options = Module("options", { function (args) { for (let [, name] in args) { name = name.replace(/^g:/, ""); // throw away the scope prefix - if (!Set.has(dactyl._globalVariables, name)) { + if (!hasOwnProperty(dactyl._globalVariables, name)) { if (!args.bang) dactyl.echoerr(_("command.let.noSuch", name)); return; @@ -1493,11 +1515,10 @@ var Options = Module("options", { function val(obj) { if (isArray(opt.defaultValue)) { - let val = array.nth(obj, re => (re.key == extra.key), - 0); + let val = [].find.call(obj, re => (re.key == extra.key)); return val && val.result; } - if (Set.has(opt.defaultValue, extra.key)) + if (hasOwnProperty(opt.defaultValue, extra.key)) return obj[extra.key]; } diff --git a/common/modules/overlay.jsm b/common/modules/overlay.jsm index c52ba4a..f52a23e 100644 --- a/common/modules/overlay.jsm +++ b/common/modules/overlay.jsm @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2013 Kris Maglione +// Copyright (c) 2009-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -103,16 +103,20 @@ var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReferen target = null; this.setData(doc, "listeners", listeners.filter(function (args) { - if (target == null || args[0].get() == target && args[1] == event && args[2].wrapped == callback && args[3] == capture) { - args[0].get().removeEventListener.apply(args[0].get(), args.slice(1)); + let elem = args[0].get(); + if (target == null || elem == target && args[1] == event && args[2].wrapped == callback && args[3] == capture) { + elem.removeEventListener.apply(elem, args.slice(1)); return false; } - return !args[0].get(); + return elem; })); }, cleanup: function cleanup(reason) { for (let doc in util.iterDocuments()) { + for (let callback in values(this.getData(doc, "cleanup"))) + util.trapErrors(callback, doc, reason); + for (let elem in values(this.getData(doc, "overlayElements"))) if (elem.parentNode) elem.parentNode.removeChild(elem); @@ -121,9 +125,6 @@ var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReferen if (getAttr(elem, ns, name) === value) setAttr(elem, ns, name, orig); - for (let callback in values(this.getData(doc, "cleanup"))) - util.trapErrors(callback, doc, reason); - this.unlisten(doc, true); delete doc[this.id]; @@ -185,8 +186,10 @@ var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReferen setData: function setData(obj, key, val) { let data = this.getData(obj); + if (val !== undefined) + return data[key] = val; - return data[key] = val; + delete data[key]; }, overlayWindow: function overlayWindow(url, fn) { @@ -215,7 +218,7 @@ var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReferen _loadOverlays: function _loadOverlays(window) { let overlays = this.getData(window, "overlays"); - for each (let obj in overlay.overlays[window.document.documentURI] || []) { + for (let obj of overlay.overlays[window.document.documentURI] || []) { if (~overlays.indexOf(obj)) continue; overlays.push(obj); @@ -240,16 +243,12 @@ var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReferen if (elem = doc.getElementById(String(elem))) { // Urgh. Hack. let namespaces; - if (attrs && !isXML(attrs)) + if (attrs) namespaces = iter([k.slice(6), DOM.fromJSON.namespaces[v] || v] for ([k, v] in Iterator(attrs)) if (/^xmlns(?:$|:)/.test(k))).toObject(); - let node; - if (isXML(xml)) - node = DOM.fromXML(xml, doc, obj.objects); - else - node = DOM.fromJSON(xml, doc, obj.objects, namespaces); + let node = DOM.fromJSON(xml, doc, obj.objects, namespaces); if (!(node instanceof Ci.nsIDOMDocumentFragment)) savedElems.push(node); @@ -259,12 +258,6 @@ var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReferen fn(elem, node); - if (isXML(attrs)) - // Evilness and such. - let (oldAttrs = attrs) { - attrs = (attr for each (attr in oldAttrs)); - } - for (let attr in attrs || []) { let [ns, localName] = DOM.parseNamespace(attr); let name = attr; @@ -343,18 +336,23 @@ var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReferen overrides = update(Object.create(original), overrides); Object.getOwnPropertyNames(overrides).forEach(function (k) { - let orig, desc = Object.getOwnPropertyDescriptor(overrides, k); + let desc = Object.getOwnPropertyDescriptor(overrides, k); + if (desc.value instanceof Class.Property) desc = desc.value.init(k) || desc.value; if (k in object) { - for (let obj = object; obj && !orig; obj = Object.getPrototypeOf(obj)) - if (orig = Object.getOwnPropertyDescriptor(obj, k)) + for (let obj = object; obj && !orig; obj = Object.getPrototypeOf(obj)) { + var orig = Object.getOwnPropertyDescriptor(obj, k); + if (orig) Object.defineProperty(original, k, orig); + } - if (!orig) - if (orig = Object.getPropertyDescriptor(object, k)) + if (!orig) { + orig = Object.getPropertyDescriptor(object, k); + if (orig) Object.defineProperty(original, k, orig); + } } // Guard against horrible add-ons that use eval-based monkey @@ -366,7 +364,7 @@ var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReferen delete desc.writable; desc.get = function get() value; desc.set = function set(val) { - if (!callable(val) || Function.prototype.toString(val).indexOf(sentinel) < 0) + if (!callable(val) || !Function.prototype.toString(val).contains(sentinel)) Class.replaceProperty(this, k, val); else { let package_ = util.newURI(Components.stack.caller.filename).host; @@ -398,7 +396,7 @@ var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReferen }, this); return function unwrap() { - for each (let k in Object.getOwnPropertyNames(original)) + for (let k of Object.getOwnPropertyNames(original)) if (Object.getOwnPropertyDescriptor(object, k).configurable) Object.defineProperty(object, k, Object.getOwnPropertyDescriptor(original, k)); else { @@ -412,19 +410,22 @@ var Overlay = Module("Overlay", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReferen get activeModules() this.activeWindow && this.activeWindow.dactyl.modules, - get modules() this.windows.map(w => w.dactyl.modules), + get modules() [w.dactyl.modules for (w of this.windows)], /** * The most recently active dactyl window. */ - get activeWindow() this.windows[0], + get activeWindow() { + let win = this._activeWindow && this._activeWindow.get(); + return this.windows.has(win) && win; + }, - set activeWindow(win) this.windows = [win].concat(this.windows.filter(w => w != win)), + set activeWindow(win) this._activeWindow = util.weakReference(win), /** * A list of extant dactyl windows. */ - windows: Class.Memoize(() => []) + windows: Class.Memoize(() => RealSet()) }); endModule(); diff --git a/common/modules/prefs.jsm b/common/modules/prefs.jsm index 47fec50..5cdfc9b 100644 --- a/common/modules/prefs.jsm +++ b/common/modules/prefs.jsm @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -291,7 +291,7 @@ var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]) * @param {string} branch The preference name. @optional */ resetBranch: function resetBranch(branch) { - this.getNames(branch).forEach(this.closure.reset); + this.getNames(branch).forEach(this.bound.reset); }, /** @@ -351,9 +351,10 @@ var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]) if (observers) { let value = this.get(data, false); this._observers[data] = observers.filter(function (callback) { - if (!callback.get()) + callback = callback.get(); + if (!callback) return false; - util.trapErrors(callback.get(), null, value); + util.trapErrors(callback, null, value); return true; }); } @@ -398,7 +399,7 @@ var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]) function prefs() { for (let [, pref] in Iterator(prefArray)) { let userValue = services.pref.prefHasUserValue(pref); - if (onlyNonDefault && !userValue || pref.indexOf(filter) == -1) + if (onlyNonDefault && !userValue || !pref.contains(filter)) continue; let value = this.get(pref); diff --git a/common/modules/promises.jsm b/common/modules/promises.jsm new file mode 100644 index 0000000..a1b832f --- /dev/null +++ b/common/modules/promises.jsm @@ -0,0 +1,148 @@ +// Copyright (c) 2014 Kris Maglione +// +// This work is licensed for reuse under an MIT license. Details are +// given in the LICENSE.txt file included with this file. +"use strict"; + +defineModule("promises", { + exports: ["Promise", "Task", "promises"], + require: [] +}); + +lazyRequire("services", ["services"]); + +lazyRequire("resource://gre/modules/Promise.jsm", ["Promise"]); +lazyRequire("resource://gre/modules/Task.jsm", ["Task"]); + +function withCallbacks(fn) { + return function wrapper(...args) { + let deferred = Promise.defer(); + function resolve(arg) { deferred.resolve(arg); } + function reject(arg) { deferred.reject(arg); } + fn.apply(this, [[resolve, reject, deferred]].concat(args)); + return deferred.promise; + } +} + +var Promises = Module("Promises", { + _cancel: WeakMap(), + + /** + * Allows promises to be canceled.. + * + * @param {Promise} promise The promise to cancel. + * @param {*} arg Argument to be passed to the cancellation + * function. + */ + cancel: function cancel(promise, reason) { + let cleanup = this._cancel.get(promise); + if (cleanup) { + cleanup[0](promise); + cleanup[1].reject(reason); + } + this._cancel.delete(promise); + }, + + /** + * Registers a cleanup function for the given deferred promise. + * + * @param {Deferred} promise The promise to cancel. + * @param {function} fn The cleanup function. + */ + oncancel: function oncancel(deferred, fn) { + this._cancel.set(deferred.promise, [fn, deferred]); + }, + + /** + * Returns a promise which resolves after a brief delay. + */ + delay: withCallbacks(function delay([accept]) { + let { mainThread } = services.threading; + mainThread.dispatch(accept, mainThread.DISPATCH_NORMAL); + }), + + /** + * Returns a promise which resolves with the given argument. + */ + accept: function fail(arg) { + let deferred = Promise.defer(); + deferred.resolve(arg); + return deferred.promise; + }, + + /** + * Returns a promise which fails with the given argument. + */ + fail: function fail(arg) { + let deferred = Promise.defer(); + deferred.reject(arg); + return deferred.promise; + }, + + /** + * Returns a promise which resolves after the given number of + * milliseconds. + * + * @param {number} delay The number of milliseconds to wait. + */ + sleep: withCallbacks(function sleep([callback], delay) { + this.timeout(callback, delay); + }), + + /** + * Wraps the given function so that each call spawns a Task. + * + * @param {function} fn The function to wrap. + * @returns {function} + */ + task: function task(fn) { + return function task_(...args) { + return Task.spawn(fn.bind.apply(fn, [this].concat(args))); + } + }, + + /** + * Returns a promise which resolves when the function *test* returns + * true, or *timeout* milliseconds have expired. + * + * @param {function} test The predicate on which to wait. + * @param {Number} timeout The maximum number of milliseconds to + * wait. + * @optional + * @param {number} pollInterval The poll interval, in milliseconds. + * @default 10 + */ + waitFor: withCallbacks(function waitFor([accept, reject], test, timeout=null, pollInterval=10) { + let end = timeout && Date.now() + timeout, result; + + let timer = services.Timer( + () => { + try { + var result = test(); + } + catch (e) { + timer.cancel(); + reject(e); + } + if (result) { + timer.cancel(); + accept(result); + } + }, + pollInterval, services.Timer.TYPE_REPEATING_SLACK); + }), + + /** + * Wraps the given function so that its first argument is an array + * of success and failure callbacks which, when called, resolve the + * returned promise. + * + * @param {function} fn The function to wrap. + * @returns {Promise} + */ + withCallbacks: withCallbacks, +}); + +endModule(); + +// vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript: diff --git a/common/modules/protocol.jsm b/common/modules/protocol.jsm index f73e209..d5e6d1a 100644 --- a/common/modules/protocol.jsm +++ b/common/modules/protocol.jsm @@ -1,4 +1,4 @@ -// Copyright (c) 2008-2012 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -143,8 +143,8 @@ ProtocolBase.prototype = { }; function LocaleChannel(pkg, locale, path, orig) { - for each (let locale in [locale, "en-US"]) - for each (let sep in "-/") { + for (let locale of [locale, "en-US"]) + for (let sep of "-/") { var channel = Channel(["resource:/", pkg + sep + locale, path].join("/"), orig, true, true); if (channel) return channel; diff --git a/common/modules/sanitizer.jsm b/common/modules/sanitizer.jsm index 2db1dfe..8f08a12 100644 --- a/common/modules/sanitizer.jsm +++ b/common/modules/sanitizer.jsm @@ -1,5 +1,5 @@ // Copyright (c) 2009 by Doug Kearns -// Copyright (c) 2009-2013 Kris Maglione +// Copyright (c) 2009-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -151,11 +151,11 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef // "Allow this site to open popups" ... services.permissions.removeAll(); // Zoom level, ... - services.contentPrefs.removeGroupedPrefs(); + services.contentPrefs.removeAllDomains(null); } // "Never remember passwords" ... - for each (let domain in services.loginManager.getAllDisabledHosts()) + for (let domain of services.loginManager.getAllDisabledHosts()) if (!host || util.isSubdomain(domain, host)) services.loginManager.setLoginSavingEnabled(host, true); }, @@ -247,14 +247,19 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef if (!("value" in prop) || !callable(prop.value) && !(k in item)) Object.defineProperty(item, k, prop); - let names = Set([name].concat(params.contains || []).map(e => "clear-" + e)); + function getWindow(obj) { + obj = Class.objectGlobal(obj); + return obj.window || obj; + } + + let names = RealSet([name].concat(params.contains || []).map(e => "clear-" + e)); if (params.action) storage.addObserver("sanitizer", function (key, event, arg) { - if (event in names) + if (names.has(event)) params.action.apply(params, arg); }, - Class.objectGlobal(params.action)); + getWindow(params.action)); if (params.privateEnter || params.privateLeave) storage.addObserver("private-mode", @@ -263,7 +268,7 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef if (meth) meth.call(params); }, - Class.objectGlobal(params.action)); + getWindow(params.privateEnter || params.privateLeave)); }, observers: { @@ -373,12 +378,12 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef UNPERMS: Class.Memoize(function () iter(this.PERMS).map(Array.reverse).toObject()), COMMANDS: { - unset: /*L*/"Unset", - allow: /*L*/"Allowed", - deny: /*L*/"Denied", - session: /*L*/"Allowed for the current session", - list: /*L*/"List all cookies for domain", - clear: /*L*/"Clear all cookies for domain", + "unset": /*L*/"Unset", + "allow": /*L*/"Allowed", + "deny": /*L*/"Denied", + "session": /*L*/"Allowed for the current session", + "list": /*L*/"List all cookies for domain", + "clear": /*L*/"Clear all cookies for domain", "clear-persistent": /*L*/"Clear all persistent cookies for domain", "clear-session": /*L*/"Clear all session cookies for domain" }, @@ -394,7 +399,7 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef for (let c in iter(services.cookies, Ci.nsICookie2)) if (!host || util.isSubdomain(c.rawHost, host) || c.host[0] == "." && c.host.length < host.length - && host.indexOf(c.host) == host.length - c.host.length) + && host.endsWith(c.host)) yield c; }, @@ -446,11 +451,14 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef if (args.bang) dactyl.assert(args.length == 0, _("error.trailingCharacters")); else { + dactyl.assert(args.length, _("error.argumentRequired")); dactyl.assert(opt.validator(args), _("error.invalidArgument")); opt = { __proto__: opt, value: args.slice() }; } - let items = Object.keys(sanitizer.itemMap).slice(1).filter(opt.has, opt); + let items = Object.keys(sanitizer.itemMap) + .slice(1) + .filter(opt.has, opt); function sanitize(items) { sanitizer.range = range.native; @@ -466,7 +474,10 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef sanitizer.sanitize(items, range); } - if (array.nth(opt.value, i => i == "all" || /^!/.test(i), 0) == "all" && !args["-host"]) + if ("all" == opt.value.find(i => (i == "all" || + /^!/.test(i))) + && !args["-host"]) + modules.commandline.input(_("sanitize.prompt.deleteAll") + " ", function (resp) { if (resp.match(/^y(es)?$/i)) { @@ -586,7 +597,7 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef completion: function initCompletion(dactyl, modules, window) { modules.completion.visibleHosts = function completeHosts(context) { let res = util.visibleHosts(window.content); - if (context.filter && !res.some(host => host.indexOf(context.filter) >= 0)) + if (context.filter && !res.some(host => host.contains(context.filter))) res.push(context.filter); context.title = ["Domain"]; @@ -612,12 +623,11 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef }, has: function has(val) - let (res = array.nth(this.value, v => (v == "all" || v.replace(/^!/, "") == val), - 0)) + let (res = this.value.find(v => (v == "all" || v.replace(/^!/, "") == val))) res && !/^!/.test(res), validator: function (values) values.length && - values.every(val => (val === "all" || Set.has(sanitizer.itemMap, val.replace(/^!/, "")))) + values.every(val => (val === "all" || hasOwnProperty(sanitizer.itemMap, val.replace(/^!/, "")))) }); options.add(["sanitizeshutdown", "ss"], @@ -635,10 +645,10 @@ var Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakRef sanitizer.runAtShutdown = false; else { sanitizer.runAtShutdown = true; - let have = Set(value); + let have = RealSet(value); for (let item in values(sanitizer.itemMap)) prefs.set(item.shutdownPref, - Boolean(Set.has(have, item.name) ^ Set.has(have, "all"))); + Boolean(have.has(item.name) ^ have.has("all"))); } return value; } diff --git a/common/modules/services.jsm b/common/modules/services.jsm index 315f951..b586139 100644 --- a/common/modules/services.jsm +++ b/common/modules/services.jsm @@ -1,4 +1,4 @@ -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -39,7 +39,8 @@ var Services = Module("Services", { this.add("clipboardHelper", "@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper"); this.add("commandLineHandler", "@mozilla.org/commandlinehandler/general-startup;1?type=dactyl"); this.add("console", "@mozilla.org/consoleservice;1", "nsIConsoleService"); - this.add("contentPrefs", "@mozilla.org/content-pref/service;1", "nsIContentPrefService"); + this.add("contentPrefs", "@mozilla.org/content-pref/service;1", ["nsIContentPrefService", + "nsIContentPrefService2"]); this.add("dactyl", "@dactyl.googlecode.com/extra/utils", "dactylIUtils"); this.add("dactyl:", this.PROTOCOL + "dactyl"); this.add("debugger", "@mozilla.org/js/jsd/debugger-service;1", "jsdIDebuggerService"); @@ -62,6 +63,7 @@ var Services = Module("Services", { this.add("mime", "@mozilla.org/mime;1", "nsIMIMEService"); this.add("observer", "@mozilla.org/observer-service;1", "nsIObserverService"); this.add("pref", "@mozilla.org/preferences-service;1", ["nsIPrefBranch2", "nsIPrefService"]); + this.add("printSettings", "@mozilla.org/gfx/printsettings-service;1", "nsIPrintSettingsService"); this.add("privateBrowsing", "@mozilla.org/privatebrowsing;1", "nsIPrivateBrowsingService"); this.add("profile", "@mozilla.org/toolkit/profile-service;1", "nsIToolkitProfileService"); this.add("resource:", this.PROTOCOL + "resource", ["nsIProtocolHandler", "nsIResProtocolHandler"]); @@ -183,7 +185,7 @@ var Services = Module("Services", { function () callable(XPCOMShim(this.interfaces)[this.init])); this[name] = (function Create() this._create(name, arguments)).bind(this); - update.apply(null, [this[name]].concat([Ci[i] for each (i in Array.concat(ifaces))])); + update.apply(null, [this[name]].concat([Ci[i] for (i of Array.concat(ifaces))])); return this[name]; }, @@ -206,7 +208,7 @@ var Services = Module("Services", { * * @param {string} name The service's cache key. */ - has: function has(name) Set.has(this.services, name) && this.services[name].class in Cc && + has: function has(name) hasOwnProperty(this.services, name) && this.services[name].class in Cc && this.services[name].interfaces.every(iface => iface in Ci) }); diff --git a/common/modules/storage.jsm b/common/modules/storage.jsm index 6cc704f..5ff9456 100644 --- a/common/modules/storage.jsm +++ b/common/modules/storage.jsm @@ -1,4 +1,4 @@ -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -6,16 +6,20 @@ defineModule("storage", { exports: ["File", "Storage", "storage"], - require: ["services", "util"] + require: ["promises", "services", "util"] }); lazyRequire("config", ["config"]); lazyRequire("io", ["IO"]); lazyRequire("overlay", ["overlay"]); +lazyRequire("resource://gre/modules/osfile.jsm", ["OS"]); + var win32 = /^win(32|nt)$/i.test(services.runtime.OS); var myObject = JSON.parse("{}").constructor; +var global = Cu.getGlobalForObject(this); + var StoreBase = Class("StoreBase", { OPTIONS: ["privateData", "replacer"], @@ -23,7 +27,7 @@ var StoreBase = Class("StoreBase", { get serial() JSON.stringify(this._object, this.replacer), - init: function (name, store, load, options) { + init: function init(name, store, load, options) { this._load = load; this._options = options; @@ -35,13 +39,21 @@ var StoreBase = Class("StoreBase", { this.reload(); }, - clone: function (storage) { + clone: function clone(storage) { let store = storage.privateMode ? false : this.store; let res = this.constructor(this.name, store, this._load, this._options); res.storage = storage; return res; }, + makeOwn: function makeOwn(val) { + if (typeof val != "object") + return val; + if (Cu.getGlobalForObject(val) == global) + return val; + return JSON.parse(JSON.stringify(val, this.replacer)); + }, + changed: function () { this.timer && this.timer.tell(); }, reload: function reload() { @@ -52,7 +64,8 @@ var StoreBase = Class("StoreBase", { delete: function delete_() { delete storage.keys[this.name]; delete storage[this.name]; - storage.infoPath.child(this.name).remove(false); + return OS.File.remove( + storage.infoPath.child(this.name).path); }, save: function () { (self.storage || storage)._saveData(this); }, @@ -67,7 +80,7 @@ var ArrayStore = Class("ArrayStore", StoreBase, { set: function set(index, value, quiet) { var orig = this._object[index]; - this._object[index] = value; + this._object[index] = this.makeOwn(value); if (!quiet) this.fireEvent("change", index); @@ -75,7 +88,7 @@ var ArrayStore = Class("ArrayStore", StoreBase, { }, push: function push(value) { - this._object.push(value); + this._object.push(this.makeOwn(value)); this.fireEvent("push", this._object.length); }, @@ -96,6 +109,7 @@ var ArrayStore = Class("ArrayStore", StoreBase, { }, insert: function insert(value, ord) { + value = this.makeOwn(value); if (ord == 0) this._object.unshift(value); else @@ -120,7 +134,8 @@ var ArrayStore = Class("ArrayStore", StoreBase, { mutate: function mutate(funcName) { var _funcName = funcName; arguments[0] = this._object; - this._object = Array[_funcName].apply(Array, arguments); + this._object = Array[_funcName].apply(Array, arguments) + .map(this.makeOwn.bind(this)); this.fireEvent("change", null); }, @@ -136,11 +151,13 @@ var ObjectStore = Class("ObjectStore", StoreBase, { }, get: function get(key, default_) { - return key in this._object ? this._object[key] : + return this.has(key) ? this._object[key] : arguments.length > 1 ? this.set(key, default_) : undefined; }, + has: function has(key) hasOwnProperty(this._object, key), + keys: function keys() Object.keys(this._object), remove: function remove(key) { @@ -153,7 +170,7 @@ var ObjectStore = Class("ObjectStore", StoreBase, { set: function set(key, val) { var defined = key in this._object; var orig = this._object[key]; - this._object[key] = val; + this._object[key] = this.makeOwn(val); if (!defined) this.fireEvent("add", key); else if (orig != val) @@ -210,12 +227,18 @@ var Storage = Module("Storage", { } }, - _saveData: function saveData(obj) { + _saveData: promises.task(function saveData(obj) { if (obj.privateData && storage.privateMode) return; - if (obj.store && storage.infoPath) - storage.infoPath.child(obj.name).write(obj.serial); - }, + if (obj.store && storage.infoPath) { + var { path } = storage.infoPath.child(obj.name); + yield OS.File.makeDir(storage.infoPath.path, + { ignoreExisting: true }); + yield OS.File.writeAtomic( + path, obj.serial, + { tmpPath: path + ".part" }); + } + }), storeForSession: function storeForSession(key, val) { if (val) @@ -236,11 +259,12 @@ var Storage = Module("Storage", { this[key].timer.flush(); delete this[key]; delete this.keys[key]; - this.infoPath.child(key).remove(false); + return OS.File.remove( + this.infoPath.child(key).path); } }, - newObject: function newObject(key, constructor, params) { + newObject: function newObject(key, constructor, params={}) { if (params == null || !isObject(params)) throw Error("Invalid argument type"); @@ -269,68 +293,41 @@ var Storage = Module("Storage", { return this.keys[key]; }, - newMap: function newMap(key, options) { + newMap: function newMap(key, options={}) { return this.newObject(key, ObjectStore, options); }, - newArray: function newArray(key, options) { + newArray: function newArray(key, options={}) { return this.newObject(key, ArrayStore, update({ type: Array }, options)); }, - addObserver: function addObserver(key, callback, ref) { - if (ref) { - let refs = overlay.getData(ref, "storage-refs"); - refs.push(callback); - var callbackRef = util.weakReference(callback); - } - else { - callbackRef = { get: function () callback }; - } - - this.removeDeadObservers(); - - if (!(key in this.observers)) - this.observers[key] = []; - - if (!this.observers[key].some(o => o.callback.get() == callback)) - this.observers[key].push({ ref: ref && Cu.getWeakReference(ref), - callback: callbackRef }); + get observerMaps() { + yield this.observers; + for (let window of overlay.windows) + yield overlay.getData(window, "storage-observers", Object); }, - removeObserver: function (key, callback) { - this.removeDeadObservers(); + addObserver: function addObserver(key, callback, window) { + var { observers } = this; + if (window instanceof Ci.nsIDOMWindow) + observers = overlay.getData(window, "storage-observers", Object); - if (!(key in this.observers)) - return; + if (!hasOwnProperty(observers, key)) + observers[key] = RealSet(); - this.observers[key] = this.observers[key].filter(elem => elem.callback.get() != callback); - if (this.observers[key].length == 0) - delete obsevers[key]; + observers[key].add(callback); }, - removeDeadObservers: function () { - function filter(o) { - if (!o.callback.get()) - return false; - - let ref = o.ref && o.ref.get(); - return ref && !ref.closed && overlay.getData(ref, "storage-refs", null); - } - - for (let [key, ary] in Iterator(this.observers)) { - this.observers[key] = ary = ary.filter(filter); - if (!ary.length) - delete this.observers[key]; - } + removeObserver: function (key, callback) { + for (let observers in this.observerMaps) + if (key in observers) + observers[key].remove(callback); }, fireEvent: function fireEvent(key, event, arg) { - this.removeDeadObservers(); - - if (key in this.observers) - // Safe, since we have our own Array object here. - for each (let observer in this.observers[key]) - observer.callback.get()(key, event, arg); + for (let observers in this.observerMaps) + for (let observer of observers[key] || []) + observer(key, event, arg); if (key in this.keys && this.keys[key].timer) this[key].timer.tell(); @@ -382,8 +379,7 @@ var Storage = Module("Storage", { } }, { cleanup: function (dactyl, modules, window) { - overlay.setData(window, "storage-refs", null); - this.removeDeadObservers(); + overlay.setData(window, "storage-callbacks", undefined); } }); @@ -458,7 +454,7 @@ var File = Class("File", { child: function child() { let f = this.constructor(this); for (let [, name] in Iterator(arguments)) - for each (let elem in name.split(File.pathSplit)) + for (let elem of name.split(File.pathSplit)) f.append(elem); return f; }, diff --git a/common/modules/styles.jsm b/common/modules/styles.jsm index 6584a7e..ee635c2 100644 --- a/common/modules/styles.jsm +++ b/common/modules/styles.jsm @@ -1,4 +1,4 @@ -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -111,8 +111,10 @@ var Hive = Class("Hive", { }, cleanup: function cleanup() { - for (let sheet in values(this.sheets)) - sheet.enabled = false; + for (let sheet of this.sheets) + util.trapErrors(() => { + sheet.enabled = false; + }); }, __iterator__: function () Iterator(this.sheets), @@ -137,8 +139,12 @@ var Hive = Class("Hive", { */ add: function add(name, filter, css, agent, lazy) { - if (!isArray(filter)) + if (isArray(filter)) + // Need an array from the same compartment. + filter = Array.slice(filter); + else filter = filter.split(","); + if (name && name in this.names) { var sheet = this.names[name]; sheet.agent = agent; @@ -212,7 +218,7 @@ var Hive = Class("Hive", { name = null; } - if (filter && filter.indexOf(",") > -1) + if (filter && filter.contains(",")) return filter.split(",").reduce( (n, f) => n + this.removeSheet(name, f, index), 0); @@ -261,7 +267,7 @@ var Styles = Module("Styles", { update(services["dactyl:"].providers, { "style": function styleProvider(uri, path) { let id = parseInt(path); - if (Set.has(styles.allSheets, id)) + if (hasOwnProperty(styles.allSheets, id)) return ["text/css", styles.allSheets[id].fullCSS]; return null; } @@ -269,7 +275,7 @@ var Styles = Module("Styles", { }, cleanup: function cleanup() { - for each (let hive in this.hives) + for (let hive of this.hives || []) util.trapErrors("cleanup", hive); this.hives = []; this.user = this.addHive("user", this, true); @@ -277,8 +283,7 @@ var Styles = Module("Styles", { }, addHive: function addHive(name, ref, persist) { - let hive = array.nth(this.hives, h => h.name === name, - 0); + let hive = this.hives.find(h => h.name === name); if (!hive) { hive = Hive(name, persist); this.hives.push(hive); @@ -369,7 +374,7 @@ var Styles = Module("Styles", { }, { append: function (dest, src, sort) { let props = {}; - for each (let str in [dest, src]) + for (let str of [dest, src]) for (let prop in Styles.propertyIter(str)) props[prop.name] = prop.value; @@ -382,7 +387,7 @@ var Styles = Module("Styles", { return val; }, - completeSite: function (context, content, group = styles.user) { + completeSite: function (context, content, group=styles.user) { context.anchored = false; try { context.fork("current", 0, this, function (context) { @@ -553,7 +558,7 @@ var Styles = Module("Styles", { let uris = util.visibleURIs(window.content); context.compare = modules.CompletionContext.Sort.number; context.generate = () => args["-group"].sheets; - context.keys.active = sheet => uris.some(sheet.closure.match); + context.keys.active = sheet => uris.some(sheet.bound.match); context.keys.description = sheet => [sheet.formatSites(uris), ": ", sheet.css.replace("\n", "\\n")]; if (filter) context.filters.push(({ item }) => filter(item)); diff --git a/common/modules/template.jsm b/common/modules/template.jsm index 5b89f18..93b6008 100644 --- a/common/modules/template.jsm +++ b/common/modules/template.jsm @@ -1,4 +1,4 @@ -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -106,7 +106,7 @@ var Template = Module("Template", { "click": function onClick(event) { event.preventDefault(); if (this.commandAllowed) { - if (Set.has(this.target.commands || {}, this.command)) + if (hasOwnProperty(this.target.commands || {}, this.command)) this.target.commands[this.command].call(this.target); else this.target.command(this.command); @@ -115,7 +115,7 @@ var Template = Module("Template", { }, get commandAllowed() { - if (Set.has(this.target.allowedCommands || {}, this.command)) + if (hasOwnProperty(this.target.allowedCommands || {}, this.command)) return this.target.allowedCommands[this.command]; if ("commandAllowed" in this.target) return this.target.commandAllowed(this.command); @@ -140,11 +140,11 @@ var Template = Module("Template", { let obj = params.eventTarget; let events = obj[this.getAttribute("events") || "events"]; - if (Set.has(events, "input")) + if (hasOwnProperty(events, "input")) events["dactyl-input"] = events["input"]; for (let [event, handler] in Iterator(events)) - node.addEventListener(event, util.wrapCallback(obj.closure(handler), true), false); + node.addEventListener(event, util.wrapCallback(handler.bind(obj), true), false); } }) }, @@ -155,7 +155,7 @@ var Template = Module("Template", { let res = []; let n = 0; - for each (let i in Iterator(iter)) { + for (let i in Iterator(iter)) { let val = func(i, n); if (val == undefined) continue; @@ -219,7 +219,7 @@ var Template = Module("Template", { else if (/^n_/.test(topic)) topic = topic.slice(2); - if (help.initialized && !Set.has(help.tags, topic)) + if (help.initialized && !hasOwnProperty(help.tags, topic)) return ["span", { highlight: type || ""}, text || token]; type = type || (/^'.*'$/.test(token) ? "HelpOpt" : @@ -241,7 +241,7 @@ var Template = Module("Template", { else if (/^n_/.test(topic)) topic = topic.slice(2); - if (help.initialized && !Set.has(help.tags, topic)) + if (help.initialized && !hasOwnProperty(help.tags, topic)) return token; let tag = (/^'.*'$/.test(token) ? "o" : @@ -263,30 +263,12 @@ var Template = Module("Template", { })(), this[help ? "HelpLink" : "helpLink"]); }, - // Fixes some strange stack rewinds on NS_ERROR_OUT_OF_MEMORY - // exceptions that we can't catch. - stringify: function stringify(arg) { - if (!callable(arg)) - return String(arg); - - try { - this._sandbox.arg = arg; - return Cu.evalInSandbox("String(arg)", this._sandbox); - } - finally { - this._sandbox.arg = null; - } - }, - - _sandbox: Class.Memoize(() => Cu.Sandbox(Cu.getGlobalForObject(global), - { wantXrays: false })), - // if "processStrings" is true, any passed strings will be surrounded by " and // any line breaks are displayed as \n highlight: function highlight(arg, processStrings, clip, bw) { // some objects like window.JSON or getBrowsers()._browsers need the try/catch try { - let str = this.stringify(arg); + let str = String(arg); if (clip) str = util.clip(str, clip); switch (arg == null ? "undefined" : typeof arg) { @@ -467,7 +449,7 @@ var Template = Module("Template", { ["td", { style: style[i] || "" }, d])])]; }, - usage: function usage(iter, format = {}) { + usage: function usage(iter, format={}) { let desc = format.description || (item => this.linkifyHelp(item.description)); let help = format.help || (item => item.name); let sourceLink = (frame) => { diff --git a/common/modules/util.jsm b/common/modules/util.jsm index d6eaf80..a0d0f09 100644 --- a/common/modules/util.jsm +++ b/common/modules/util.jsm @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2013 Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -10,7 +10,7 @@ try { defineModule("util", { exports: ["DOM", "$", "FailedAssertion", "Math", "NS", "Point", "Util", "XBL", "XHTML", "XUL", "util"], - require: ["dom", "services"] + require: ["dom", "promises", "services"] }); lazyRequire("overlay", ["overlay"]); @@ -72,8 +72,8 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), }, activeWindow: deprecated("overlay.activeWindow", { get: function activeWindow() overlay.activeWindow }), - overlayObject: deprecated("overlay.overlayObject", { get: function overlayObject() overlay.closure.overlayObject }), - overlayWindow: deprecated("overlay.overlayWindow", { get: function overlayWindow() overlay.closure.overlayWindow }), + overlayObject: deprecated("overlay.overlayObject", { get: function overlayObject() overlay.bound.overlayObject }), + overlayWindow: deprecated("overlay.overlayWindow", { get: function overlayWindow() overlay.bound.overlayWindow }), compileMatcher: deprecated("DOM.compileMatcher", { get: function compileMatcher() DOM.compileMatcher }), computedStyle: deprecated("DOM#style", function computedStyle(elem) DOM(elem).style), @@ -89,14 +89,13 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), parseForm: deprecated("DOM#formData", function parseForm(elem) values(DOM(elem).formData).toArray()), scrollIntoView: deprecated("DOM#scrollIntoView", function scrollIntoView(elem, alignWithTop) DOM(elem).scrollIntoView(alignWithTop)), validateMatcher: deprecated("DOM.validateMatcher", { get: function validateMatcher() DOM.validateMatcher }), - xmlToDom: deprecated("DOM.fromJSON", function xmlToDom() DOM.fromXML.apply(DOM, arguments)), map: deprecated("iter.map", function map(obj, fn, self) iter(obj).map(fn, self).toArray()), writeToClipboard: deprecated("dactyl.clipboardWrite", function writeToClipboard(str, verbose) util.dactyl.clipboardWrite(str, verbose)), readFromClipboard: deprecated("dactyl.clipboardRead", function readFromClipboard() util.dactyl.clipboardRead(false)), chromePackages: deprecated("config.chromePackages", { get: function chromePackages() config.chromePackages }), - haveGecko: deprecated("config.haveGecko", { get: function haveGecko() config.closure.haveGecko }), + haveGecko: deprecated("config.haveGecko", { get: function haveGecko() config.bound.haveGecko }), OS: deprecated("config.OS", { get: function OS() config.OS }), dactyl: update(function dactyl(obj) { @@ -138,7 +137,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), let cleanup = ["dactyl-cleanup-modules", "quit-application"]; function register(meth) { - for (let target in Set(cleanup.concat(Object.keys(obj.observers)))) + for (let target of RealSet(cleanup.concat(Object.keys(obj.observers)))) try { services.observer[meth](obj, target, true); } @@ -354,7 +353,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), : "", { elements: [], - seen: {}, + seen: RealSet(), valid: function valid(obj) this.elements.every(e => (!e.test || e.test(obj))) }); @@ -387,15 +386,15 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), } else { let [, flags, name] = /^((?:[a-z]-)*)(.*)/.exec(macro); - flags = Set(flags); + flags = RealSet(flags); let quote = util.identity; - if (flags.q) + if (flags.has("q")) quote = function quote(obj) typeof obj === "number" ? obj : String.quote(obj); - if (flags.e) + if (flags.has("e")) quote = function quote(obj) ""; - if (Set.has(defaults, name)) + if (hasOwnProperty(defaults, name)) stack.top.elements.push(quote(defaults[name])); else { let index = idx; @@ -403,7 +402,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), idx = Number(idx) - 1; stack.top.elements.push(update( obj => obj[name] != null && idx in obj[name] ? quote(obj[name][idx]) - : Set.has(obj, name) ? "" : unknown(full), + : hasOwnProperty(obj, name) ? "" : unknown(full), { test: function test(obj) obj[name] != null && idx in obj[name] && obj[name][idx] !== false @@ -413,7 +412,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), else { stack.top.elements.push(update( obj => obj[name] != null ? quote(obj[name]) - : Set.has(obj, name) ? "" : unknown(full), + : hasOwnProperty(obj, name) ? "" : unknown(full), { test: function test(obj) obj[name] != null && obj[name] !== false @@ -422,7 +421,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), } for (let elem in array.iterValues(stack)) - elem.seen[name] = true; + elem.seen.add(name); } } } @@ -483,7 +482,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), return obj.res; } - if (pattern.indexOf("{") == -1) + if (!pattern.contains("{")) return [pattern]; let res = []; @@ -517,7 +516,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), rec([]); return res; } - catch (e if e.message && ~e.message.indexOf("res is undefined")) { + catch (e if e.message && e.message.contains("res is undefined")) { // prefs.safeSet() would be reset on :rehash prefs.set("javascript.options.methodjit.chrome", false); util.dactyl.warn(_(UTF8("error.damnYouJägermonkey"))); @@ -545,7 +544,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), * @returns {string} */ dequote: function dequote(pattern, chars) - pattern.replace(/\\(.)/, (m0, m1) => chars.indexOf(m1) >= 0 ? m1 : m0), + pattern.replace(/\\(.)/, (m0, m1) => chars.contains(m1) ? m1 : m0), /** * Returns the nsIDocShell for the given window. @@ -750,19 +749,19 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), * * @returns {XMLHttpRequest} */ - httpGet: function httpGet(url, callback, self) { - let params = callback; - if (!isObject(params)) - params = { callback: params && ((...args) => callback.apply(self, args)) }; + httpGet: function httpGet(url, params={}, self) { + if (callable(params)) + // Deprecated. + params = { callback: params.bind(self) }; try { let xmlhttp = services.Xmlhttp(); - xmlhttp.mozBackgroundRequest = Set.has(params, "background") ? params.background : true; + xmlhttp.mozBackgroundRequest = hasOwnProperty(params, "background") ? params.background : true; let async = params.callback || params.onload || params.onerror; if (async) { - xmlhttp.addEventListener("load", function handler(event) { util.trapErrors(params.onload || params.callback, params, xmlhttp, event); }, false); - xmlhttp.addEventListener("error", function handler(event) { util.trapErrors(params.onerror || params.callback, params, xmlhttp, event); }, false); + xmlhttp.addEventListener("load", event => { util.trapErrors(params.onload || params.callback, params, xmlhttp, event); }, false); + xmlhttp.addEventListener("error", event => { util.trapErrors(params.onerror || params.callback, params, xmlhttp, event); }, false); } if (isObject(params.params)) { @@ -810,6 +809,22 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), } }, + /** + * Like #httpGet, but returns a promise rather than accepting + * callbacks. + * + * @param {string} url The URL to fetch. + * @param {object} params Parameter object, as in #httpGet. + */ + fetchUrl: promises.withCallbacks(function fetchUrl([accept, reject, deferred], url, params) { + params = update({}, params); + params.onload = accept; + params.onerror = reject; + + let req = this.httpGet(url, params); + promises.oncancel(deferred, req.cancel); + }), + /** * The identity function. * @@ -880,7 +895,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), let windows = services.windowMediator.getXULWindowEnumerator(null); while (windows.hasMoreElements()) { let window = windows.getNext().QueryInterface(Ci.nsIXULWindow); - for each (let type in types) { + for (let type of types) { let docShells = window.docShell.getDocShellEnumerator(Ci.nsIDocShellTreeItem[type], Ci.nsIDocShell.ENUMERATE_FORWARDS); while (docShells.hasMoreElements()) @@ -1018,12 +1033,18 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), // window.content often does not want to be queried with "var i in object" try { let hasValue = !("__iterator__" in object || isinstance(object, ["Generator", "Iterator"])); + if (object.dactyl && object.modules && object.modules.modules == object.modules) { object = Iterator(object); hasValue = false; } + let keyIter = object; - if ("__iterator__" in object && !callable(object.__iterator__)) + if (iter.iteratorProp in object) { + keyIter = (k for (k of object)); + hasValue = false; + } + else if ("__iterator__" in object && !callable(object.__iterator__)) keyIter = keys(object); for (let i in keyIter) { @@ -1032,6 +1053,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), value = object[i]; } catch (e) {} + if (!hasValue) { if (isArray(i) && i.length == 2) [i, value] = i; @@ -1088,9 +1110,9 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), function rec(data, level, seen) { if (isObject(data)) { - if (~seen.indexOf(data)) + seen = RealSet(seen); + if (seen.add(data)) throw Error("Recursive object passed"); - seen = seen.concat([data]); } let prefix = level + INDENT; @@ -1138,7 +1160,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), } let res = []; - rec(data, "", []); + rec(data, "", RealSet()); return res.join(""); }, @@ -1151,28 +1173,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), util.dump("cleanup: " + module.constructor.className); util.trapErrors(module.cleanup, module, reason); } - - JSMLoader.cleanup(); - - if (!this.rehashing) - services.observer.addObserver(this, "dactyl-rehash", true); - }, - "dactyl-rehash": function dactylRehash() { - services.observer.removeObserver(this, "dactyl-rehash"); - - defineModule.loadLog.push("dactyl: util: observe: dactyl-rehash"); - if (!this.rehashing) - for (let module in values(defineModule.modules)) { - defineModule.loadLog.push("dactyl: util: init(" + module + ")"); - if (module.reinit) - module.reinit(); - else - module.init(); - } - }, - "dactyl-purge": function dactylPurge() { - this.rehashing = 1; - }, + } }, /** @@ -1230,7 +1231,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), * * This is similar to Perl's extended regular expression format. * - * @param {string|XML} expr The expression to compile into a RegExp. + * @param {string} expr The expression to compile into a RegExp. * @param {string} flags Flags to apply to the new RegExp. * @param {object} tokens The tokens to substitute. @optional * @returns {RegExp} A custom regexp object. @@ -1255,10 +1256,10 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), // Replace replacement . if (tokens) expr = String.replace(expr, /(\(?P)?<(\w+)>/g, - (m, n1, n2) => !n1 && Set.has(tokens, n2) ? tokens[n2].dactylSource - || tokens[n2].source - || tokens[n2] - : m); + (m, n1, n2) => !n1 && hasOwnProperty(tokens, n2) ? tokens[n2].dactylSource + || tokens[n2].source + || tokens[n2] + : m); // Strip comments and white space. if (/x/.test(flags)) @@ -1279,7 +1280,8 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), } let res = update(RegExp(expr, flags.replace("x", "")), { - closure: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "closure")), + bound: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "bound")), + closure: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "bound")), dactylPropertyNames: ["exec", "match", "test", "toSource", "toString", "global", "ignoreCase", "lastIndex", "multiLine", "source", "sticky"], iterate: function iterate(str, idx) util.regexp.iterate(this, str, idx) }); @@ -1350,7 +1352,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), storage.storeForSession("commandlineArgs", args); this.timeout(function () { this.flushCache(); - this.rehashing = true; + cache.flush(bind("test", /^literal:/)); let addon = config.addon; addon.userDisabled = true; addon.userDisabled = false; @@ -1555,7 +1557,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), * Waits for the function *test* to return true, or *timeout* * milliseconds to expire. * - * @param {function} test The predicate on which to wait. + * @param {function|Promise} test The predicate on which to wait. * @param {object} self The 'this' object for *test*. * @param {Number} timeout The maximum number of milliseconds to * wait. @@ -1565,6 +1567,15 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), * thrown. */ waitFor: function waitFor(test, self, timeout, interruptable) { + if (!callable(test)) { + let done = false; + var promise = test, + retVal; + promise.then((arg) => { retVal = arg; done = true; }, + (arg) => { retVal = arg; done = true; }); + test = () => done; + } + let end = timeout && Date.now() + timeout, result; let timer = services.Timer(function () {}, 10, services.Timer.TYPE_REPEATING_SLACK); @@ -1575,7 +1586,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), finally { timer.cancel(); } - return result; + return promise ? retVal: result; }, /** @@ -1595,7 +1606,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), * @returns {function} A new function which may not execute * synchronously. */ - yieldable: function yieldable(func) + yieldable: deprecated("Task.spawn", function yieldable(func) function magic() { let gen = func.apply(this, arguments); (function next() { @@ -1604,7 +1615,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), } catch (e if e instanceof StopIteration) {}; })(); - }, + }), /** * Wraps a callback function such that its errors are not lost. This @@ -1673,7 +1684,8 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), * @returns {[string]} The visible domains. */ visibleHosts: function visibleHosts(win) { - let res = [], seen = {}; + let res = [], + seen = RealSet(); (function rec(frame) { try { if (frame.location.hostname) @@ -1682,7 +1694,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), catch (e) {} Array.forEach(frame.frames, rec); })(win); - return res.filter(h => !Set.add(seen, h)); + return res.filter(h => !seen.add(h)); }, /** @@ -1693,7 +1705,8 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), * @returns {[nsIURI]} The visible URIs. */ visibleURIs: function visibleURIs(win) { - let res = [], seen = {}; + let res = [], + seen = RealSet(); (function rec(frame) { try { res = res.concat(util.newURI(frame.location.href)); @@ -1701,7 +1714,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), catch (e) {} Array.forEach(frame.frames, rec); })(win); - return res.filter(h => !Set.add(seen, h.spec)); + return res.filter(h => !seen.add(h.spec)); }, /** diff --git a/common/process_config.awk b/common/process_config.awk new file mode 100644 index 0000000..4582f7b --- /dev/null +++ b/common/process_config.awk @@ -0,0 +1,22 @@ +BEGIN { + chrome = "chrome" + if (suffix) + chrome = suffix +} +/^ \}/ { on = 0 } + +on && $NF ~ /^"([a-z]|\.\/)/ { + $NF = "\"/" name "/" substr($NF, 2) +} +/./ && on { + sub(/^"\.\./, "\"", $NF); + $NF = "\"" chrome substr($NF, 2) +} +/./ && on { + gsub(/\/\.\//, "/") + sub(/^\"\.\.\/common\//, "\"", $NF) + $0 = " " $0 +} +// + +/^ "resources": \{/ { on = 1 } diff --git a/common/process_manifest.awk b/common/process_manifest.awk index 4abfcee..3844834 100644 --- a/common/process_manifest.awk +++ b/common/process_manifest.awk @@ -3,8 +3,12 @@ BEGIN { if (suffix) chrome = suffix } + { content = $1 ~ /^(content|skin|locale|resource)$/ } -content && $NF ~ /^[a-z]|^\.\// { $NF = "/" name "/" $NF } + +content && $NF ~ /^([a-z]|\.\/)/ { + $NF = "/" name "/" $NF +} content { sub(/^\.\./, "", $NF); if (isjar) diff --git a/common/skin/dactyl.css b/common/skin/dactyl.css index 53d3dc2..43aef7f 100644 --- a/common/skin/dactyl.css +++ b/common/skin/dactyl.css @@ -168,6 +168,23 @@ statusbarpanel { visibility: collapse; } +:-moz-any(#addon-bar, #dactyl-addon-bar) .toolbarbutton-1 > xul|dropmarker { + margin-left: 0 !important; + margin-right: 0 !important; +} + +#dactyl-addon-bar .toolbarbutton-1 > xul|dropmarker::after { + content: "▾"; + color: white; + font-size: 18px; + line-height: 18px; +} + +#nav-bar { + padding-top: 0px !important; + padding-bottom: 0px !important; +} + .dactyl-commandline-prompt { /* background-color: inherit; */ margin: 0px; @@ -207,4 +224,60 @@ statusbarpanel { } +@-moz-document url(chrome://browser/content/browser.xul) { + +/* Fix ginormous Australis tabs. */ +[dactyl-australis=true] xul|tab.tabbrowser-tab .tab-background > * { + min-height: 24px !important; + max-height: 24px !important; +} + +[dactyl-australis=true] xul|tab.tabbrowser-tab + .tab-background > :-moz-any(.tab-background-start, .tab-background-end)::after { + background-size: 30px 24px !important; + max-height: 24px !important; + min-height: 24px !important; +} + +[dactyl-australis=true] .tabbrowser-tabs { + min-height: 0 !important; +} + +/* Fix stupid line... */ +[dactyl-australis=true] #navigator-toolbox::after { + height: 0 !important; +} + +#nav-bar-customization-target > .toolbarbutton-1 { + margin-top: -5px !important; + margin-bottom: -5px !important; +} + +/* +#PanelUI-button, +#PanelUI-menu-button, +#nav-bar-customization-target > .toolbarbutton-1, +#nav-bar-customization-target > .toolbarbutton-1 > xul|toolbarbutton { + padding: 0 !important; +} + +#nav-bar-customization-target > xul|toolbaritem > .toolbarbutton-1 { + padding-top: 0 !important; + padding-bottom: 0 !important; +} + +#nav-bar-customization-target > .toolbarbutton-1, +#nav-bar-customization-target > .toolbarbutton-1 > xul|dropmarker > xul|image { + margin-top: 0 !important; + margin-bottom: 0 !important; + max-height: 24px !important; +} + +#nav-bar-customization-target #urlbar { + margin-bottom: 0 !important; +} +*/ + +} + /* vim: set fdm=marker sw=4 ts=4 et: */ diff --git a/pentadactyl/config.json b/pentadactyl/config.json index 5ba3bdd..dc1e8ec 100644 --- a/pentadactyl/config.json +++ b/pentadactyl/config.json @@ -9,6 +9,29 @@ "resource://dactyl/" ], + "resources": { + "dactyl": "../common/modules/", + "dactyl-common": "../common/", + "dactyl-content": "../common/content/", + "dactyl-skin": "../common/skin/", + "dactyl-locale": "../common/locale/", + + "dactyl-local": "./", + "dactyl-local-content": "content/", + "dactyl-local-skin": "skin/", + "dactyl-local-locale": "locale/" + }, + + "components": { + "{16dc34f7-6d22-4aa4-a67f-2921fb5dcb69}": { + "path": "components/commandline-handler.js", + "contract": "@mozilla.org/commandlinehandler/general-startup;1?type=dactyl", + "categories": { + "command-line-handler": "m-dactyl" + } + } + }, + "autocommands": { "BookmarkAdd": "Triggered after a page is bookmarked", "BookmarkChange": "Triggered after a page's bookmark is changed", diff --git a/pentadactyl/install.rdf b/pentadactyl/install.rdf index d706028..bd50bda 100644 --- a/pentadactyl/install.rdf +++ b/pentadactyl/install.rdf @@ -5,7 +5,7 @@ em:id="pentadactyl@dactyl.googlecode.com" em:type="2" em:name="Pentadactyl" - em:version="1.1pre" + em:version="1.2pre" em:description="Firefox for Vim and Links addicts" em:homepageURL="http://5digits.org/pentadactyl" em:bootstrap="true" @@ -31,8 +31,8 @@ + em:minVersion="25.0" + em:maxVersion="31.*"/>