diff --git a/common/content/base.js b/common/content/base.js index 07bfc127..16c6d227 100644 --- a/common/content/base.js +++ b/common/content/base.js @@ -8,6 +8,19 @@ const Ci = Components.interfaces; const Cr = Components.results; const Cu = Components.utils; +// TODO: Move to liberator. +function setTimeout(callback, timeout, self) { + function target() { + try { + callback.call(self); + } + catch (e) { + liberator.reportError(e); + } + } + return window.setTimeout(target, timeout); +} + function array(obj) { if (isgenerator(obj)) obj = [k for (k in obj)]; @@ -230,8 +243,9 @@ function Class() { if (callable(args[0])) superclass = args.shift(); - var Constructor = eval("(function " + (name || superclass.name) + + var Constructor = eval("(function " + (name || superclass.name).replace(/\W/g, "_") + String.substr(constructor, 20) + ")"); + Constructor.name = name || superclass.name; if (!('init' in superclass.prototype)) { var superc = superclass; diff --git a/common/content/finder.js b/common/content/finder.js index b658ee1f..6f7db18d 100644 --- a/common/content/finder.js +++ b/common/content/finder.js @@ -430,10 +430,13 @@ const Finder = Module("finder", { "Highlight previous search pattern matches", "boolean", "false", { setter: function (value) { - if (value) - finder.highlight(); - else - finder.clear(); + try { + if (value) + finder.highlight(); + else + finder.clear(); + } + catch (e) {} return value; } @@ -464,45 +467,36 @@ const RangeFinder = Module("rangefinder", { }, openPrompt: function (mode) { - let backwards; - if (mode == modes.FIND_BACKWARD) { - commandline.open("?", "", modes.FIND_BACKWARD); - backwards = true; - } - else { - commandline.open("/", "", modes.FIND_FORWARD); - backwards = false; - } + let backwards = mode == modes.FIND_BACKWARD; + commandline.open(backwards ? "?" : "/", "", mode); this.find("", backwards); - // TODO: focus the top of the currently visible screen }, find: function (str, backwards) { -try { let caseSensitive = false; + if (this.rangeFind) + this.clear(); this.rangeFind = RangeFind(caseSensitive, backwards); 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) { - if (!this.rangeFind || !this.rangeFind.search(null, reverse)) + if (!this.rangeFind) + this.find(this._lastSearchString); + else if (!this.rangeFind.search(null, reverse)) liberator.echoerr("E486: Pattern not found: " + lastSearchPattern); else if (this.rangeFind.wrapped) { // hack needed, because wrapping causes a "scroll" event which clears // our command line setTimeout(function () { - if (rangfinder.rangeFind.backward) - commandline.echo("search hit TOP, continuing at BOTTOM", - commandline.HL_WARNINGMSG, commandline.APPEND_TO_MESSAGES); - else - commandline.echo("search hit BOTTOM, continuing at TOP", - commandline.HL_WARNINGMSG, commandline.APPEND_TO_MESSAGES); + let msg = rangfinder.rangeFind.backward ? "search hit TOP, continuing at BOTTOM" + : "search hit BOTTOM, continuing at TOP"; + commandline.echo(msg, commandline.HL_WARNINGMSG, commandline.APPEND_TO_MESSAGES); }, 0); } else { @@ -515,20 +509,14 @@ try { // 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) { - // use the last pattern if none specified - if (!command) - command = lastSearchPattern; - - if (!options["incsearch"] || !this.rangeFind.found) { + if (!options["incsearch"] || !this.rangeFind || !this.rangeFind.found) { this.clear(); - this.find(command, this.rangeFind.backwards); + this.find(command || this._lastSearchString, this._lastSearchBackwards); } this._lastSearchBackwards = this.rangeFind.backwards; @@ -536,7 +524,7 @@ try { this._lastSearchString = command; if (options["hlsearch"]) - this.highlight(this.rangeFind.searchString); + this.highlight(); modes.reset(); }, @@ -558,15 +546,17 @@ try { * * @param {string} str The string to highlight. */ - highlight: function (str) { - return; + highlight: function () { + if (this.rangeFind) + this.rangeFind.highlight(); }, /** * Clears all search highlighting. */ clear: function () { - return; + if (this.rangeFind) + this.rangeFind.highlight(true); } }, { }, { @@ -632,9 +622,9 @@ const RangeFind = Class("RangeFind", { this._backward = backward; this.ranges = this.makeFrameList(content); - this.range = { document: (tabs.localStore.focusedFrame || content).document }; + this.range = RangeFind.Range(tabs.localStore.focusedFrame || content); - this.startRange = (this.selection.rangeCount ? this.selection.getRangeAt(0) : this.ranges[0].range).cloneRange(); + this.startRange = (this.range.selection.rangeCount ? this.range.selection.getRangeAt(0) : this.ranges[0].range).cloneRange(); this.startRange.collapse(!backward); this.range = this.findRange(this.startRange); this.ranges.first = this.range; @@ -645,17 +635,6 @@ 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) @@ -679,34 +658,25 @@ const RangeFind = Class("RangeFind", { 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 pushRange(start, end) { + frames.push(RangeFind.Range(start, end, frames.length)); } 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); + let pageStart = RangeFind.endpoint(pageRange, true); + let pageEnd = RangeFind.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); + pushRange(pageStart, RangeFind.endpoint(range, true)); + pageStart = RangeFind.endpoint(range, false); rec(frame); } - pushRange(pageStart, pageEnd, win); + pushRange(pageStart, pageEnd); } rec(win); return frames; @@ -749,7 +719,23 @@ const RangeFind = Class("RangeFind", { get searchString() this.lastString, get backward() this.finder.findBackwards, - search: function (word, reverse) { + __iterator__: function () { + let range = this.range; + let lastRange = this.lastRange + try { + this.range = this.ranges[0]; + this.lastRange = null; + var res; + while (res = this.search(null, this._backward, true)) + yield res; + } + finally { + this.range = range; + this.lastRange = lastRange; + } + }, + + search: function (word, reverse, private) { this.finder.findBackwards = reverse ? !this._backward : this._backward; let again = word == null; if (again) @@ -758,9 +744,10 @@ const RangeFind = Class("RangeFind", { word = word.toLowerCase(); if (!again && (word == "" || word.indexOf(this.lastString) != 0 || this.backward)) { - this.unhighlight(); + if (!private) + this.range.deselect(); if (word == "") - this.deScroll(this.range); + this.range.descroll() this.lastRange = this.startRange; this.range = this.ranges.first; } @@ -772,6 +759,8 @@ const RangeFind = Class("RangeFind", { 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; + if (private) + return; this.wrapped = true; for (let i in this.backward ? util.range(this.ranges.length, idx, -1) : util.range(0, idx)) yield i; @@ -779,16 +768,24 @@ const RangeFind = Class("RangeFind", { 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; + RangeFind.endpoint(this.lastRange, this.backward) : + RangeFind.endpoint(this.range.range, !this.backward);; var range = this.finder.Find(word, this.range.range, start, this.range.range); - if (range && this.compareRanges(range, this.range.range) <= 0) + if (range) break; - this.deScroll(this.range); - this.unhighlight(); + if (!private) { + this.range.descroll(); + this.range.deselect(); + } } } + if (range) + this.lastRange = range.cloneRange(); + if (private) + return range; + this.lastString = word; if (range == null) { this.cancel(); @@ -796,159 +793,121 @@ const RangeFind = Class("RangeFind", { return null; } this.wrapped = false; - this.selection.removeAllRanges(); - this.selection.addRange(range); - this.selectionController.scrollSelectionIntoView(this.selectionController.SELECTION_NORMAL, 0, false); - this.lastRange = range.cloneRange(); + this.range.selection.removeAllRanges(); + this.range.selection.addRange(range); + this.range.selectionController.scrollSelectionIntoView( + this.range.selectionController.SELECTION_NORMAL, 0, false); this.found = true; return range; }, + highlight: function (clear) { + if (!this.lastString) + return; + + if (!clear) + for (let range in values(this.ranges)) + if (util.evaluateXPath("//@liberator:highlight[1][.='Search']").snapshotLength) + return; + + let span = util.xmlToDom(, this.range.document); + + function highlight(range) { + let startContainer = range.startContainer; + let startOffset = range.startOffset; + let node = startContainer.ownerDocument.importNode(span, true); + + let docfrag = range.extractContents(); + let before = startContainer.splitText(startOffset); + let parent = before.parentNode; + node.appendChild(docfrag); + parent.insertBefore(node, before); + range.selectNode(node); + } + function unhighlight(range) { + let elem = range.startContainer; + while (!(elem instanceof Element) && elem.parentNode) + elem = elem.parentNode; + if (elem.getAttributeNS(NS.uri, "highlight") != "Search") + return; + + let docfrag = range.extractContents(); + + let parent = elem.parentNode; + parent.replaceChild(docfrag, elem); + parent.normalize(); + } + + this.range.save(); + liberator.dump(String.quote(this.range.initialSelection)); + let action = clear ? unhighlight : highlight; + for (let r in this) { + action(r); + this.lastRange = r; + } + this.range.deselect(); + if (!clear) + this.search(null, false); + }, + cancel: function () { - if (false) // Later. - this.selection.addRange(this.startRange); - this.unhighlight(); - this.deScroll(this.range); + this.range.deselect(); + this.range.descroll() }, +}, { + Range: Class("RangeFind.Range", { + init: function (start, end, index) { + if (start instanceof Ci.nsIDOMWindow) { // Kludge + this.document = start.document; + return; + } - unhighlight: function () { - this.selection.removeAllRanges(); - }, + this.index = index; - deScroll: function (range) { - range.window.scrollTo(range.scroll.x, range.scroll.y); + this.document = start.startContainer.ownerDocument; + this.window = this.document.defaultView; + + this.range = this.document.createRange(); + this.range.setStart(start.startContainer, start.startOffset); + this.range.setEnd(end.startContainer, end.startOffset); + + this.save(); + }, + + save: function () { + this.scroll = Point(this.window.pageXOffset, this.window.pageYOffset); + + this.initialSelection = null; + if (this.selection.rangeCount) + this.initialSelection = this.selection.getRangeAt(0); + }, + + descroll: function (range) { + this.window.scrollTo(this.scroll.x, this.scroll.y); + }, + + deselect: function () { + this.selection.removeAllRanges(); + if (this.initialSelection) + this.selection.addRange(this.initialSelection); + }, + + get 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; + }, + get selectionController() this.docShell + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsISelectionDisplay) + .QueryInterface(Ci.nsISelectionController), + get selection() this.selectionController.getSelection(Ci.nsISelectionController.SELECTION_NORMAL), + }), + endpoint: function (range, before) { + range = range.cloneRange(); + range.collapse(before); + return range; } }); -/* Stolen from toolkit.jar in Firefox, for the time being. The private - * methods were unstable, and changed. The new version is not remotely - * compatible with what we do. - * The following only applies to this object, and may not be - * necessary, or accurate, but, just in case: - * The Original Code is mozilla.org viewsource frontend. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (c) 2003 - * by the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Blake Ross (Original Author) - * Masayuki Nakano - * Ben Basson - * Jason Barnabe - * Asaf Romano - * Ehsan Akhgari - * Graeme McCutcheon - */ -const Highlighter = Class("Highlighter", { - init: function (doc) { - this.doc = doc; - }, - - doc: null, - - spans: [], - - search: function (word, matchCase) { - var finder = services.create("find"); - - var range; - while ((range = finder.Find(word, this.searchRange, this.startPt, this.endPt))) - yield range; - }, - - highlightDoc: function highlightDoc(win, word) { - Array.forEach(win.frames, function (frame) this.highlightDoc(frame, word), this); - - var doc = win.document; - if (!doc || !(doc instanceof HTMLDocument)) - return; - - if (!word) { - let elems = this._highlighter.spans; - for (let i = elems.length; --i >= 0;) { - let elem = elems[i]; - let docfrag = doc.createDocumentFragment(); - let next = elem.nextSibling; - let parent = elem.parentNode; - - let child; - while (child = elem.firstChild) - docfrag.appendChild(child); - - parent.removeChild(elem); - parent.insertBefore(docfrag, next); - parent.normalize(); - } - return; - } - - var baseNode = ; - baseNode = util.xmlToDom(baseNode, window.content.document); - - var body = doc.body; - var count = body.childNodes.length; - this.searchRange = doc.createRange(); - this.startPt = doc.createRange(); - this.endPt = doc.createRange(); - - this.searchRange.setStart(body, 0); - this.searchRange.setEnd(body, count); - - this.startPt.setStart(body, 0); - this.startPt.setEnd(body, 0); - this.endPt.setStart(body, count); - this.endPt.setEnd(body, count); - - liberator.interrupted = false; - let n = 0; - for (let retRange in this.search(word, this._caseSensitive)) { - // Highlight - var nodeSurround = baseNode.cloneNode(true); - var node = this.highlight(retRange, nodeSurround); - this.startPt = node.ownerDocument.createRange(); - this.startPt.setStart(node, node.childNodes.length); - this.startPt.setEnd(node, node.childNodes.length); - if (n++ % 20 == 0) - liberator.threadYield(true); - if (liberator.interrupted) - break; - } - }, - - highlight: function highlight(range, node) { - var startContainer = range.startContainer; - var startOffset = range.startOffset; - var endOffset = range.endOffset; - var docfrag = range.extractContents(); - var before = startContainer.splitText(startOffset); - var parent = before.parentNode; - node.appendChild(docfrag); - parent.insertBefore(node, before); - this.spans.push(node); - return node; - }, - - /** - * Clears all search highlighting. - */ - clear: function () { - this.spans.forEach(function (span) { - if (span.parentNode) { - let el = span.firstChild; - while (el) { - span.removeChild(el); - span.parentNode.insertBefore(el, span); - el = span.firstChild; - } - span.parentNode.removeChild(span); - } - }); - this.spans = []; - }, - - isHighlighted: function (doc) this.doc == doc && this.spans.length > 0 -}); - // vim: set fdm=marker sw=4 ts=4 et: diff --git a/common/content/options.js b/common/content/options.js index 308628ad..7710da29 100644 --- a/common/content/options.js +++ b/common/content/options.js @@ -190,17 +190,17 @@ const Option = Class("Option", { else scope = this.scope; - let aValue; + let value; if (liberator.has("tabs") && (scope & options.OPTION_SCOPE_LOCAL)) - aValue = tabs.options[this.name]; - if ((scope & options.OPTION_SCOPE_GLOBAL) && (aValue == undefined)) - aValue = this.globalValue; + value = tabs.options[this.name]; + if ((scope & options.OPTION_SCOPE_GLOBAL) && (value == undefined)) + value = this.globalValue; if (this.getter) - return liberator.trapErrors(this.getter, this, aValue); + return liberator.trapErrors(this.getter, this, value); - return aValue; + return value; }, /**