diff --git a/common/content/buffer.js b/common/content/buffer.js index 2e8e0731..9760a865 100644 --- a/common/content/buffer.js +++ b/common/content/buffer.js @@ -345,6 +345,18 @@ const Buffer = Module("buffer", { window.content.document.pageIsFullyLoaded = value; }, + /** + * @property {Object} The local state store for the currently selected + * tab. + */ + get localStore() { + if (!content.liberatorStore) + content.liberatorStore = {}; + return content.liberatorStore; + }, + + + /** * @property {Node} The last focused input field in the buffer. Used * by the "gi" key binding. diff --git a/common/content/completion.js b/common/content/completion.js index d1f5b58a..45510430 100644 --- a/common/content/completion.js +++ b/common/content/completion.js @@ -1115,25 +1115,46 @@ const Completion = Module("completion", { context.filters.push(function (item) util.compareIgnoreCase(item.text.substr(0, this.filter.length), this.filter)); if (obj == cache.evalContext) context.regenerate = true; - context.generate = function () self.objectKeys(obj, !recurse); + context.generate = function () { + try { + return self.objectKeys(obj, !recurse) + } + catch(e) { + liberator.reportError(e); + } + }; }; } // TODO: Make this a generic completion helper function. let filter = key + (string || ""); for (let [, obj] in Iterator(objects)) { this.context.fork(obj[1], top[OFFSET], this, fill, - obj[0], obj[1], compl, compl != orig, filter, last, key.length); + obj[0], obj[1], compl, + true, filter, last, key.length); } + if (orig) return; + for (let [, obj] in Iterator(objects)) { let name = obj[1] + " (prototypes)"; this.context.fork(name, top[OFFSET], this, fill, - obj[0], name, function (a, b) compl(a, b, true), compl != orig, - filter, last, key.length); - obj[1] += " (substrings)"; + obj[0], name, function (a, b) compl(a, b, true), + true, filter, last, key.length); + } + + for (let [, obj] in Iterator(objects)) { + let name = obj[1] + " (substrings)"; this.context.fork(obj[1], top[OFFSET], this, fill, - obj[0], obj[1], compl, false, filter, last, key.length); + obj[0], name, compl, + false, filter, last, key.length); + } + + for (let [, obj] in Iterator(objects)) { + let name = obj[1] + " (prototype substrings)"; + this.context.fork(obj[1], top[OFFSET], this, fill, + obj[0], name, function (a, b) compl(a, b, true), + false, filter, last, key.length); } } diff --git a/common/content/finder.js b/common/content/finder.js index 2d6fad7e..9e3c86b0 100644 --- a/common/content/finder.js +++ b/common/content/finder.js @@ -475,6 +475,9 @@ const RangeFinder = Module("rangefinder", { }, bootstrap: function (str, backward) { + if (this.rangeFind && this.rangeFind.stale) + this.rangeFind = null; + let highlighted = this.rangeFind && this.rangeFind.highlighted; let matchCase = !(options["ignorecase"] || options["smartcase"] && !/[A-Z]/.test(str)); let linksOnly = options["linksearch"]; @@ -493,7 +496,11 @@ const RangeFinder = Module("rangefinder", { return n1; return ""; }); - if (!this.rangeFind || linksOnly ^ !!this.rangeFind.elementPath || + + // It's possible, with :tabdetach, for the rangeFind to actually move + // from one window to another, which breaks things. + if (!this.rangeFind || this.rangeFind.window != window || + linksOnly ^ !!this.rangeFind.elementPath || matchCase ^ this.rangeFind.matchCase || backward ^ this.rangeFind.reverse) { if (this.rangeFind) this.rangeFind.cancel(); @@ -562,8 +569,8 @@ const RangeFinder = Module("rangefinder", { this.rangeFind.cancel(); }, - get rangeFind() tabs.localStore.rangeFind, - set rangeFind(val) tabs.localStore.rangeFind = val, + get rangeFind() buffer.localStore.rangeFind, + set rangeFind(val) buffer.localStore.rangeFind = val, /** * Highlights all occurances of str in the buffer. @@ -640,6 +647,7 @@ const RangeFinder = Module("rangefinder", { const RangeFind = Class("RangeFind", { init: function (matchCase, backward, elementPath) { + this.window = window; this.elementPath = elementPath || null; this.matchCase = Boolean(matchCase); this.reverse = Boolean(backward); @@ -898,15 +906,35 @@ const RangeFind = Class("RangeFind", { action(r); this.lastRange = r; } - if (clear) + if (clear) { this.highlighted = null; + this.purgeListeners(); + } else { this.highlighted = this.lastString; + this.addListeners(); this.search(null, false); } }, + addListeners: function () { + for (let range in values(this.ranges)) + range.window.addEventListener("unload", this.closure.onUnload, true); + }, + purgeListeners: function () { + for (let range in values(this.ranges)) + range.window.removeEventListener("unload", this.closure.onUnload, true); + }, + + onUnload: function (event) { + this.purgeListeners(); + if (this.highlighted) + this.highlight(false); + this.stale = true; + }, + cancel: function () { + this.purgeListeners(); this.range.deselect(); this.range.descroll() } @@ -950,9 +978,11 @@ const RangeFind = Class("RangeFind", { }, get docShell() { + if (this._docShell) + return this._docShell; for (let shell in iter(getBrowser().docShell.getDocShellEnumerator(Ci.nsIDocShellTreeItem.typeAll, Ci.nsIDocShell.ENUMERATE_FORWARDS))) if (shell.QueryInterface(nsIWebNavigation).document == this.document) - return shell; + return this._docShell = shell; }, get selectionController() this.docShell .QueryInterface(Ci.nsIInterfaceRequestor) diff --git a/common/content/hints.js b/common/content/hints.js index dd26ad31..4c928e90 100644 --- a/common/content/hints.js +++ b/common/content/hints.js @@ -979,7 +979,7 @@ const Hints = Module("hints", { if (typeof a[2] == "string") a[3] = function (chr) String.fromCharCode(this[2].charCodeAt(0) + chr - this[0]) else - a[3] = function (chr) this[2][(chr - this[0]) % this[3].length]; + a[3] = function (chr) this[2][(chr - this[0]) % this[2].length]; return a; }); diff --git a/common/content/tabs.js b/common/content/tabs.js index b8646373..d1d35bf6 100644 --- a/common/content/tabs.js +++ b/common/content/tabs.js @@ -104,6 +104,10 @@ const Tabs = Module("tabs", { * @returns {Object} */ // FIXME: why not a tab arg? Why this and the property? + // : To the latter question, because this works for any tab, the + // property doesn't. And the property is so oft-used that it's + // convenient. To the former question, because I think this is mainly + // useful for autocommands, and they get index arguments. --Kris getLocalStore: function (tabIndex) { let tab = this.getTab(tabIndex); if (!tab.liberatorStore) @@ -478,7 +482,7 @@ const Tabs = Module("tabs", { tab = this.getBrowser().mTabContainer.selectedItem; services.get("windowWatcher") - .openWindow(window, window.getBrowserURL(), null, "chrome,dialog=no,all", tab); + .openWindow(window, window.getBrowserURL(), null, "chrome,dialog=no,all", tab); }, /**