diff --git a/content/buffer.js b/content/buffer.js index 36758b40..a72bd78d 100644 --- a/content/buffer.js +++ b/content/buffer.js @@ -28,6 +28,8 @@ the terms of any one of the MPL, the GPL or the LGPL. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); +const Point = new Struct("x", "y"); + function Buffer() //{{{ { //////////////////////////////////////////////////////////////////////////////// @@ -1278,17 +1280,13 @@ function Buffer() //{{{ if (!selection) { - var selectionController = getBrowser().docShell - .QueryInterface(Components.interfaces.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsISelectionDisplay) - .QueryInterface(Components.interfaces.nsISelectionController); - - var caretmode = selectionController.getCaretEnabled(); - selectionController.setCaretEnabled(true); - selectionController.wordMove(false, false); - selectionController.wordMove(true, true); + let selController = this.selectionController; + let caretmode = selController.getCaretEnabled(); + selController.setCaretEnabled(true); + selController.wordMove(false, false); + selController.wordMove(true, true); selection = window.content.getSelection().toString(); - selectionController.setCaretEnabled(caretmode); + selController.setCaretEnabled(caretmode); } return selection; @@ -1481,6 +1479,11 @@ function Buffer() //{{{ }) }, + get selectionController() getBrowser().docShell + .QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsISelectionDisplay) + .QueryInterface(Components.interfaces.nsISelectionController), + saveLink: function (elem, skipPrompt) { var doc = elem.ownerDocument; diff --git a/content/completion.js b/content/completion.js index 5c813574..9c34cc12 100644 --- a/content/completion.js +++ b/content/completion.js @@ -149,8 +149,16 @@ function Completion() //{{{ // Things we can dereference if (["object", "string", "function"].indexOf(typeof obj) == -1) continue; + /* Try harder. if (/^\[XPCNativeWrapper /.test(obj)) obj = obj.wrappedJSObject; + */ + try + { + if (obj.wrappedJSObject) + obj = obj.wrappedJSObject; + } + catch (e) {} for (let [k, v] in this.iter(obj)) compl.push([k, v]); @@ -381,7 +389,7 @@ function Completion() //{{{ let statement = get(frame, 0, STATEMENTS) || 0; // Current statement. let prev = statement; let obj; - for (let [i, dot] in Iterator(get(frame)[DOTS])) + for (let [i, dot] in Iterator(get(frame)[DOTS].concat(stop))) { if (dot < statement) continue; diff --git a/content/find.js b/content/find.js index 42421a96..1606f9b2 100644 --- a/content/find.js +++ b/content/find.js @@ -1,4 +1,4 @@ -/***** BEGIN LICENSE BLOCK ***** {{{ +/***** B/GIN LICENSE BLOCK ***** {{{ Version: MPL 1.1/GPL 2.0/LGPL 2.1 The contents of this file are subject to the Mozilla Public License Version @@ -54,6 +54,7 @@ function Search() //{{{ var lastSearchBackwards = false; // like "backwards", but for the last search, so if you cancel a search with this is not set var caseSensitive = false; // search string is case sensitive var linksOnly = false; // search is limited to link text only + var rangeFind; // Event handlers for search - closure is needed liberator.registerCallback("change", modes.SEARCH_FORWARD, function (command) { search.searchKeyPressed(command); }); @@ -109,6 +110,129 @@ function Search() //{{{ searchString = pattern; } + function RangeFind(matchCase, backward) + { + var finder = Components.classes["@mozilla.org/embedcomp/rangefind;1"] + .createInstance() + .QueryInterface(Components.interfaces.nsIFind); + finder.caseSensitive = matchCase; + var sel = buffer.selectionController; + var doc = content.document; + var win = content; + + var pageRange = doc.createRange(); + pageRange.setStartBefore(doc.body); + pageRange.setEndAfter(doc.body); + var pageStart = pageRange.cloneRange(); + var pageEnd = pageRange.cloneRange(); + pageStart.collapse(true); + pageEnd.collapse(false); + + // This doesn't work yet. + this.resetCaret = function () + { + let equal = function (r1, r2) !r1.compareToRange(Range.START_TO_START, r2) && !r1.compareToRange(Range.END_TO_END, r2); + let selection = win.getSelection(); + if (selection.rangeCount == 0) + selection.addRange(pageStart); + function getLines() + { + let orig = selection.getRangeAt(0); + function getRanges(forward) + { + selection.removeAllRanges(); + selection.addRange(orig); + let cur = orig; + while (true) + { + var last = cur; + sel.lineMove(forward, false); + cur = selection.getRangeAt(0); + if(equal(cur, last)) + break; + yield cur; + } + } + yield orig; + for(let range in getRanges(true)) + yield range; + for(let range in getRanges(false)) + yield range; + } + for (let range in getLines()) + { + if (sel.checkVisibility(range.startContainer, range.startOffset, range.startOffset)) + return range; + } + return null; + } + + var start = new Point(win.pageXOffset, win.pageYOffset); + let selection = sel.getSelection(sel.SELECTION_NORMAL); + var startRange = selection.rangeCount ? selection.getRangeAt(0) : pageStart; + startRange.collapse(true); + + var lastString = ""; + var lastRange; + var forward; + + this.__defineGetter__("searchString", function () lastString); + this.__defineGetter__("backward", function () finder.findBackwards); + + this.search = function (word, reverse) + { + finder.findBackwards = reverse ? !backward : backward; + let again = word == null; + if (again) + word = lastString; + if (!matchCase) + word = word.toLowerCase(); + + if (word == "") + var range = startRange; + else + { + if (lastRange) + { + if (again) + lastRange.collapse(this.backward); + else if (word.indexOf(lastString) != 0 || this.backward) + lastRange = null; + else + lastRange.collapse(true); + } + + var range = finder.Find(word, pageRange, + lastRange || startRange, + pageEnd); + } + + lastString = word; + if (range == null) + { + liberator.dump(this.wrapped); + if (this.wrapped) + return null; + this.wrapped = true; + lastRange = this.backward ? pageEnd : pageStart; + return this.search(again ? null : word, reverse); + } + this.wrapped = false; + selection.removeAllRanges(); + selection.addRange(range); + sel.scrollSelectionIntoView(sel.SELECTION_NORMAL, 0, false); + lastRange = range.cloneRange(); + return range; + } + + this.cancel = function () + { + selection.removeAllRanges(); + selection.addRange(startRange); + win.scrollTo(start.x, start.y); + } + } + /* 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. @@ -328,19 +452,17 @@ function Search() //{{{ backwards = false; } + this.find("", backwards); // TODO: focus the top of the currently visible screen }, // Finds text in a page - // TODO: backwards seems impossible i fear :( find: function (str, backwards) { - var fastFind = getBrowser().fastFind; - processUserPattern(str); - fastFind.caseSensitive = caseSensitive; - found = fastFind.find(searchString, linksOnly) != Components.interfaces.nsITypeAheadFind.FIND_NOTFOUND; + rangeFind = new RangeFind(caseSensitive, backwards); + found = rangeFind.search(searchString); if (!found) setTimeout(function () { liberator.echoerr("E486: Pattern not found: " + searchPattern); }, 0); @@ -351,25 +473,14 @@ function Search() //{{{ // Called when the current search needs to be repeated findAgain: function (reverse) { - // this hack is needed to make n/N work with the correct string, if - // we typed /foo after the original search. Since searchString is - // readonly we have to call find() again to update it. - if (getBrowser().fastFind.searchString != lastSearchString) - this.find(lastSearchString, false); - - var up = reverse ? !lastSearchBackwards : lastSearchBackwards; - var result = getBrowser().fastFind.findAgain(up, linksOnly); - - if (result == Components.interfaces.nsITypeAheadFind.FIND_NOTFOUND) - { + if (!rangeFind || !rangeFind.search(null, reverse)) liberator.echoerr("E486: Pattern not found: " + lastSearchPattern); - } - else if (result == Components.interfaces.nsITypeAheadFind.FIND_WRAPPED) + else if (rangeFind.wrapped) { // hack needed, because wrapping causes a "scroll" event which clears // our command line setTimeout(function () { - if (up) + if (rangeFind.backward) commandline.echo("search hit TOP, continuing at BOTTOM", commandline.HL_WARNINGMSG, commandline.APPEND_TO_MESSAGES); else @@ -379,7 +490,7 @@ function Search() //{{{ } else { - liberator.echo((up ? "?" : "/") + lastSearchPattern, null, commandline.FORCE_SINGLELINE); + liberator.echo((rangeFind.backward ? "?" : "/") + lastSearchPattern, null, commandline.FORCE_SINGLELINE); if (options["hlsearch"]) this.highlight(lastSearchString); @@ -390,7 +501,7 @@ function Search() //{{{ searchKeyPressed: function (command) { if (options["incsearch"]) - this.find(command, backwards); + rangeFind.search(command); }, // Called when the enter key is pressed to trigger a search @@ -404,22 +515,19 @@ function Search() //{{{ if (!command) command = lastSearchPattern; - this.clear(); if (!options["incsearch"] || !found) + { + this.clear(); this.find(command, backwards); + } lastSearchBackwards = backwards; //lastSearchPattern = command.replace(backwards ? /\?.*/ : /\/.*/, ""); // XXX lastSearchPattern = command; lastSearchString = searchString; - // TODO: move to find() when reverse incremental searching is kludged in - // need to find again for reverse searching - if (backwards) - setTimeout(function () { search.findAgain(false); }, 0); - if (options["hlsearch"]) - this.highlight(searchString); + this.highlight(rangeFind.searchString); modes.reset(); }, @@ -430,15 +538,15 @@ function Search() //{{{ { this.clear(); // TODO: code to reposition the document to the place before search started + if (rangeFind) + rangeFind.cancel(); + rangeFind = null; }, // FIXME: thunderbird incompatible // this is not dependent on the value of 'hlsearch' highlight: function (text) { - if (config.name == "Muttator") - return; - // already highlighted? if (window.content.document.getElementsByClassName("__liberator-search").length > 0) return; @@ -449,7 +557,8 @@ function Search() //{{{ highlightObj.highlightDoc(window.content, text); // recreate selection since _highlightDoc collapses the selection backwards - getBrowser().fastFind.findAgain(false, linksOnly); + if (rangeFind) + rangeFind.search(null); // TODO: remove highlighting from non-link matches (HTML - A/AREA with href attribute; XML - Xlink [type="simple"]) }, @@ -457,10 +566,9 @@ function Search() //{{{ clear: function () { highlightObj.highlightDoc(window.content); - // need to manually collapse the selection if the document is not - // highlighted - getBrowser().fastFind.collapseSelection(); - } + }, + + rangeFind: RangeFind }; //}}} diff --git a/content/liberator-overlay.js b/content/liberator-overlay.js index 6e80cd50..66903973 100644 --- a/content/liberator-overlay.js +++ b/content/liberator-overlay.js @@ -24,6 +24,7 @@ Components.utils.import("resource://liberator/storage.jsm", modules); ["liberator.js", + "util.js", "config.js", "buffer.js", "commands.js", @@ -37,8 +38,7 @@ "modes.js", "options.js", "template.js", - "ui.js", - "util.js"].forEach(load); + "ui.js"].forEach(load); modules.config.scripts.forEach(load); })()