From 0f4598fcd6ed8deba068bc66486f60ad94fd609b Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Wed, 11 Nov 2009 02:35:51 -0500 Subject: [PATCH] Resurrect my range finder, Part II: Make things works sensibly with frames. --- common/content/base.js | 12 +-- common/content/events.js | 2 +- common/content/finder.js | 164 +++++++++++++++++++++++++++++---------- 3 files changed, 130 insertions(+), 48 deletions(-) diff --git a/common/content/base.js b/common/content/base.js index db7d2dd9..07bfc127 100644 --- a/common/content/base.js +++ b/common/content/base.js @@ -115,11 +115,11 @@ function isobject(obj) { } function isarray(obj) { - return Object.prototype.toString(obj) == "[object Array]"; + return Object.prototype.toString.call(obj) == "[object Array]"; } function isgenerator(val) { - return Object.prototype.toString(obj) == "[object Generator]"; + return Object.prototype.toString.call(val) == "[object Generator]"; } function isstring(val) { @@ -212,9 +212,11 @@ function Class() { constructor: Constructor, get closure() { delete this.closure; - const self = this; - return this.closure = dict([k for (k in this) if (!self.__lookupGetter__(k) && callable(self[k]))].map( - function (k) [k, function () self[k].apply(self, arguments)])); + function closure(fn) function () fn.apply(self, arguments); + for (let k in this) + if (!this.__lookupGetter__(k) && callable(this[k])) + closure[k] = closure(self[k]); + return this.closure = closure; } }; var res = self.init.apply(self, arguments); diff --git a/common/content/events.js b/common/content/events.js index 3cbf85df..b5ff96b1 100644 --- a/common/content/events.js +++ b/common/content/events.js @@ -523,7 +523,7 @@ const Events = Module("events", { */ toString: function (event) { if (!event) - return "[object Mappings]"; + return "[instance events]"; if (event.liberatorString) return event.liberatorString; diff --git a/common/content/finder.js b/common/content/finder.js index 57d184f6..b658ee1f 100644 --- a/common/content/finder.js +++ b/common/content/finder.js @@ -479,13 +479,15 @@ const RangeFinder = Module("rangefinder", { }, find: function (str, backwards) { +try { let caseSensitive = false; this.rangeFind = RangeFind(caseSensitive, backwards); - if (!this.rangeFind.search(searchString)) - setTimeout(function () { liberator.echoerr("E486: Pattern not found: " + searchPattern); }, 0); + if (!this.rangeFind.search(str)) + setTimeout(function () { liberator.echoerr("E486: Pattern not found: " + str); }, 0); return this.rangeFind.found; +} catch(e) { liberator.reportError(e) } }, findAgain: function (reverse) { @@ -513,8 +515,10 @@ const RangeFinder = Module("rangefinder", { // Called when the user types a key in the search dialog. Triggers a find attempt if 'incsearch' is set onKeyPress: function (command) { +try { if (options["incsearch"] && this.rangeFind) this.rangeFind.search(command); +} catch(e) { liberator.reportError(e); } }, onSubmit: function (command) { @@ -622,28 +626,18 @@ const RangeFinder = Module("rangefinder", { const RangeFind = Class("RangeFind", { init: function (matchCase, backward) { - this.finder = Components.classes["@mozilla.org/embedcomp/rangefind;1"] - .createInstance() - .QueryInterface(Components.interfaces.nsIFind); + this.finder = services.create("find"); this.finder.caseSensitive = matchCase; this.matchCase = matchCase; this._backward = backward; - this.sel = buffer.selectionController; - this.win = content; - this.doc = content.document; - this.pageRange = this.doc.createRange(); - this.pageRange.setStartBefore(this.doc.body); - this.pageRange.setEndAfter(this.doc.body); - this.pageStart = this.pageRange.cloneRange(); - this.pageEnd = this.pageRange.cloneRange(); - this.pageStart.collapse(true); - this.pageEnd.collapse(false); + this.ranges = this.makeFrameList(content); + this.range = { document: (tabs.localStore.focusedFrame || content).document }; - this.start = Point(this.win.pageXOffset, this.win.pageYOffset); - this.selection = this.sel.getSelection(this.sel.SELECTION_NORMAL); - this.startRange = this.selection.rangeCount ? this.selection.getRangeAt(0) : this.pageStart; - this.startRange.collapse(true); + this.startRange = (this.selection.rangeCount ? this.selection.getRangeAt(0) : this.ranges[0].range).cloneRange(); + this.startRange.collapse(!backward); + this.range = this.findRange(this.startRange); + this.ranges.first = this.range; this.lastString = ""; this.lastRange = null; @@ -651,9 +645,76 @@ const RangeFind = Class("RangeFind", { this.found = false; }, + get docShell() { + for (let shell in iter(getBrowser().docShell.getDocShellEnumerator(Ci.nsIDocShellTreeItem.typeAll, Ci.nsIDocShell.ENUMERATE_FORWARDS))) + if (shell.QueryInterface(nsIWebNavigation).document == this.range.document) + return shell; + }, + get selectionController() this.docShell + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsISelectionDisplay) + .QueryInterface(Ci.nsISelectionController), + get selection() this.selectionController.getSelection(Ci.nsISelectionController.SELECTION_NORMAL), + + sameDocument: function (r1, r2) r1 && r2 && r1.endContainer.ownerDocument == r2.endContainer.ownerDocument, + + compareRanges: function (r1, r2) + this.backward ? r1.compareBoundaryPoints(Range.END_TO_START, r2) + : -r1.compareBoundaryPoints(Range.START_TO_END, r2), + + findRange: function (range) { + let doc = range.startContainer.ownerDocument; + let win = doc.defaultView; + let ranges = this.ranges.filter(function (r) + r.window == win && + r.range.compareBoundaryPoints(Range.START_TO_END, range) >= 0 && + r.range.compareBoundaryPoints(Range.END_TO_START, range) <= 0); + + if (this.backward) + return ranges[ranges.length - 1]; + return ranges[0]; + }, + + makeFrameList: function (win) { + win = win.top; + let frames = []; + + function endpoint(range, before) { + range = range.cloneRange(); + range.collapse(before); + return range; + } + function pushRange(start, end, win) { + let scroll = Point(win.pageXOffset, win.pageYOffset); + let range = win.document.createRange(); + range.setStart(start.startContainer, start.startOffset); + range.setEnd(end.startContainer, end.startOffset); + frames.push({ range: range, index: frames.length, window: win, document: win.document, scroll: scroll }); + } + function rec(win) { + let doc = win.document; + let pageRange = doc.createRange(); + pageRange.setStartBefore(doc.body || doc.documentElement.lastChild); + pageRange.setEndAfter(doc.body || doc.documentElement.lastChild); + let pageStart = endpoint(pageRange, true); + let pageEnd = endpoint(pageRange, false); + + for (let frame in util.Array.itervalues(win.frames)) { + let range = doc.createRange(); + range.selectNode(frame.frameElement); + pushRange(pageStart, endpoint(range, true), win); + pageStart = endpoint(range, false); + rec(frame); + } + pushRange(pageStart, pageEnd, win); + } + rec(win); + return frames; + }, + // This doesn't work yet. resetCaret: function () { - let equal = function (r1, r2) !r1.compareToRange(Range.START_TO_START, r2) && !r1.compareToRange(Range.END_TO_END, r2); + let equal = function (r1, r2) !r1.compareBoundaryPoints(Range.START_TO_START, r2) && !r1.compareBoundaryPoints(Range.END_TO_END, r2); letselection = this.win.getSelection(); if (selection.rangeCount == 0) selection.addRange(this.pageStart); @@ -696,48 +757,67 @@ const RangeFind = Class("RangeFind", { if (!this.matchCase) word = word.toLowerCase(); + if (!again && (word == "" || word.indexOf(this.lastString) != 0 || this.backward)) { + this.unhighlight(); + if (word == "") + this.deScroll(this.range); + this.lastRange = this.startRange; + this.range = this.ranges.first; + } + if (word == "") var range = this.startRange; else { - if (this.lastRange) { - if (again) - this.lastRange.collapse(this.backward); - else if (word.indexOf(this.lastString) != 0 || this.backward) - this.lastRange = null; - else - this.lastRange.collapse(true); + function indices() { + let idx = this.range.index; + for (let i in this.backward ? util.range(idx + 1, -1, -1) : util.range(idx, this.ranges.length)) + yield i; + this.wrapped = true; + for (let i in this.backward ? util.range(this.ranges.length, idx, -1) : util.range(0, idx)) + yield i; } + for (let i in indices.call(this)) { + this.range = this.ranges[i]; + let start = this.sameDocument(this.lastRange, this.range.range) ? + this.lastRange : this.range.range; - var range = this.finder.Find(word, this.pageRange, - this.lastRange || this.startRange, - this.pageEnd); + var range = this.finder.Find(word, this.range.range, start, this.range.range); + if (range && this.compareRanges(range, this.range.range) <= 0) + break; + this.deScroll(this.range); + this.unhighlight(); + } } this.lastString = word; if (range == null) { - if (this.wrapped) { - this.cancel(); - this.found = false; - return null; - } - this.wrapped = true; - this.lastRange = this.backward ? this.pageEnd : this.pageStart; - return this.search(again ? null : word, reverse); + this.cancel(); + this.found = false; + return null; } this.wrapped = false; this.selection.removeAllRanges(); this.selection.addRange(range); - this.sel.scrollSelectionIntoView(this.sel.SELECTION_NORMAL, 0, false); + this.selectionController.scrollSelectionIntoView(this.selectionController.SELECTION_NORMAL, 0, false); this.lastRange = range.cloneRange(); this.found = true; return range; }, cancel: function () { - this.selection.removeAllRanges(); - this.selection.addRange(this.startRange); - this.win.scrollTo(this.start.x, this.start.y); + if (false) // Later. + this.selection.addRange(this.startRange); + this.unhighlight(); + this.deScroll(this.range); }, + + unhighlight: function () { + this.selection.removeAllRanges(); + }, + + deScroll: function (range) { + range.window.scrollTo(range.scroll.x, range.scroll.y); + } }); /* Stolen from toolkit.jar in Firefox, for the time being. The private