Blame toolkit/modules/Finder.jsm

Packit f0b94e
// vim: set ts=2 sw=2 sts=2 tw=80:
Packit f0b94e
// This Source Code Form is subject to the terms of the Mozilla Public
Packit f0b94e
// License, v. 2.0. If a copy of the MPL was not distributed with this
Packit f0b94e
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
Packit f0b94e
Packit f0b94e
var EXPORTED_SYMBOLS = ["Finder", "GetClipboardSearchString"];
Packit f0b94e
Packit f0b94e
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
Packit f0b94e
ChromeUtils.import("resource://gre/modules/Geometry.jsm");
Packit f0b94e
ChromeUtils.import("resource://gre/modules/Services.jsm");
Packit f0b94e
Packit f0b94e
ChromeUtils.defineModuleGetter(this, "BrowserUtils",
Packit f0b94e
  "resource://gre/modules/BrowserUtils.jsm");
Packit f0b94e
Packit f0b94e
XPCOMUtils.defineLazyServiceGetter(this, "Clipboard",
Packit f0b94e
                                         "@mozilla.org/widget/clipboard;1",
Packit f0b94e
                                         "nsIClipboard");
Packit f0b94e
XPCOMUtils.defineLazyServiceGetter(this, "ClipboardHelper",
Packit f0b94e
                                         "@mozilla.org/widget/clipboardhelper;1",
Packit f0b94e
                                         "nsIClipboardHelper");
Packit f0b94e
Packit f0b94e
const kSelectionMaxLen = 150;
Packit f0b94e
const kMatchesCountLimitPref = "accessibility.typeaheadfind.matchesCountLimit";
Packit f0b94e
Packit f0b94e
function Finder(docShell) {
Packit f0b94e
  this._fastFind = Cc["@mozilla.org/typeaheadfind;1"].createInstance(Ci.nsITypeAheadFind);
Packit f0b94e
  this._fastFind.init(docShell);
Packit f0b94e
Packit f0b94e
  this._currentFoundRange = null;
Packit f0b94e
  this._docShell = docShell;
Packit f0b94e
  this._listeners = [];
Packit f0b94e
  this._previousLink = null;
Packit f0b94e
  this._searchString = null;
Packit f0b94e
  this._highlighter = null;
Packit f0b94e
Packit f0b94e
  docShell.QueryInterface(Ci.nsIInterfaceRequestor)
Packit f0b94e
          .getInterface(Ci.nsIWebProgress)
Packit f0b94e
          .addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION);
Packit f0b94e
  BrowserUtils.getRootWindow(this._docShell).addEventListener("unload",
Packit f0b94e
    this.onLocationChange.bind(this, { isTopLevel: true }));
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
Finder.prototype = {
Packit f0b94e
  get iterator() {
Packit f0b94e
    if (this._iterator)
Packit f0b94e
      return this._iterator;
Packit f0b94e
    this._iterator = ChromeUtils.import("resource://gre/modules/FinderIterator.jsm", null).FinderIterator;
Packit f0b94e
    return this._iterator;
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  destroy() {
Packit f0b94e
    if (this._iterator)
Packit f0b94e
      this._iterator.reset();
Packit f0b94e
    let window = this._getWindow();
Packit f0b94e
    if (this._highlighter && window) {
Packit f0b94e
      // if we clear all the references before we hide the highlights (in both
Packit f0b94e
      // highlighting modes), we simply can't use them to find the ranges we
Packit f0b94e
      // need to clear from the selection.
Packit f0b94e
      this._highlighter.hide(window);
Packit f0b94e
      this._highlighter.clear(window);
Packit f0b94e
    }
Packit f0b94e
    this.listeners = [];
Packit f0b94e
    this._docShell.QueryInterface(Ci.nsIInterfaceRequestor)
Packit f0b94e
      .getInterface(Ci.nsIWebProgress)
Packit f0b94e
      .removeProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION);
Packit f0b94e
    this._listeners = [];
Packit f0b94e
    this._currentFoundRange = this._fastFind = this._docShell = this._previousLink =
Packit f0b94e
      this._highlighter = null;
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  addResultListener(aListener) {
Packit f0b94e
    if (!this._listeners.includes(aListener))
Packit f0b94e
      this._listeners.push(aListener);
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  removeResultListener(aListener) {
Packit f0b94e
    this._listeners = this._listeners.filter(l => l != aListener);
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  _notify(options) {
Packit f0b94e
    if (typeof options.storeResult != "boolean")
Packit f0b94e
      options.storeResult = true;
Packit f0b94e
Packit f0b94e
    if (options.storeResult) {
Packit f0b94e
      this._searchString = options.searchString;
Packit f0b94e
      this.clipboardSearchString = options.searchString;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    let foundLink = this._fastFind.foundLink;
Packit f0b94e
    let linkURL = null;
Packit f0b94e
    if (foundLink) {
Packit f0b94e
      let docCharset = null;
Packit f0b94e
      let ownerDoc = foundLink.ownerDocument;
Packit f0b94e
      if (ownerDoc)
Packit f0b94e
        docCharset = ownerDoc.characterSet;
Packit f0b94e
Packit f0b94e
      linkURL = Services.textToSubURI.unEscapeURIForUI(docCharset, foundLink.href);
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    options.linkURL = linkURL;
Packit f0b94e
    options.rect = this._getResultRect();
Packit f0b94e
    options.searchString = this._searchString;
Packit f0b94e
Packit f0b94e
    if (!this.iterator.continueRunning({
Packit f0b94e
      caseSensitive: this._fastFind.caseSensitive,
Packit f0b94e
      entireWord: this._fastFind.entireWord,
Packit f0b94e
      linksOnly: options.linksOnly,
Packit f0b94e
      word: options.searchString
Packit f0b94e
    })) {
Packit f0b94e
      this.iterator.stop();
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    this.highlighter.update(options);
Packit f0b94e
    this.requestMatchesCount(options.searchString, options.linksOnly);
Packit f0b94e
Packit f0b94e
    this._outlineLink(options.drawOutline);
Packit f0b94e
Packit f0b94e
    for (let l of this._listeners) {
Packit f0b94e
      try {
Packit f0b94e
        l.onFindResult(options);
Packit f0b94e
      } catch (ex) {}
Packit f0b94e
    }
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  get searchString() {
Packit f0b94e
    if (!this._searchString && this._fastFind.searchString)
Packit f0b94e
      this._searchString = this._fastFind.searchString;
Packit f0b94e
    return this._searchString;
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  get clipboardSearchString() {
Packit f0b94e
    return GetClipboardSearchString(this._getWindow()
Packit f0b94e
                                        .QueryInterface(Ci.nsIInterfaceRequestor)
Packit f0b94e
                                        .getInterface(Ci.nsIWebNavigation)
Packit f0b94e
                                        .QueryInterface(Ci.nsILoadContext));
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  set clipboardSearchString(aSearchString) {
Packit f0b94e
    if (!aSearchString || !Clipboard.supportsFindClipboard())
Packit f0b94e
      return;
Packit f0b94e
Packit f0b94e
    ClipboardHelper.copyStringToClipboard(aSearchString,
Packit f0b94e
                                          Ci.nsIClipboard.kFindClipboard);
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  set caseSensitive(aSensitive) {
Packit f0b94e
    if (this._fastFind.caseSensitive === aSensitive)
Packit f0b94e
      return;
Packit f0b94e
    this._fastFind.caseSensitive = aSensitive;
Packit f0b94e
    this.iterator.reset();
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  set entireWord(aEntireWord) {
Packit f0b94e
    if (this._fastFind.entireWord === aEntireWord)
Packit f0b94e
      return;
Packit f0b94e
    this._fastFind.entireWord = aEntireWord;
Packit f0b94e
    this.iterator.reset();
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  get highlighter() {
Packit f0b94e
    if (this._highlighter)
Packit f0b94e
      return this._highlighter;
Packit f0b94e
Packit f0b94e
    const {FinderHighlighter} = ChromeUtils.import("resource://gre/modules/FinderHighlighter.jsm", {});
Packit f0b94e
    return this._highlighter = new FinderHighlighter(this);
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  get matchesCountLimit() {
Packit f0b94e
    if (typeof this._matchesCountLimit == "number")
Packit f0b94e
      return this._matchesCountLimit;
Packit f0b94e
Packit f0b94e
    this._matchesCountLimit = Services.prefs.getIntPref(kMatchesCountLimitPref) || 0;
Packit f0b94e
    return this._matchesCountLimit;
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  _lastFindResult: null,
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * Used for normal search operations, highlights the first match.
Packit f0b94e
   *
Packit f0b94e
   * @param aSearchString String to search for.
Packit f0b94e
   * @param aLinksOnly Only consider nodes that are links for the search.
Packit f0b94e
   * @param aDrawOutline Puts an outline around matched links.
Packit f0b94e
   */
Packit f0b94e
  fastFind(aSearchString, aLinksOnly, aDrawOutline) {
Packit f0b94e
    this._lastFindResult = this._fastFind.find(aSearchString, aLinksOnly);
Packit f0b94e
    let searchString = this._fastFind.searchString;
Packit f0b94e
    this._notify({
Packit f0b94e
      searchString,
Packit f0b94e
      result: this._lastFindResult,
Packit f0b94e
      findBackwards: false,
Packit f0b94e
      findAgain: false,
Packit f0b94e
      drawOutline: aDrawOutline,
Packit f0b94e
      linksOnly: aLinksOnly
Packit f0b94e
    });
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * Repeat the previous search. Should only be called after a previous
Packit f0b94e
   * call to Finder.fastFind.
Packit f0b94e
   *
Packit f0b94e
   * @param aFindBackwards Controls the search direction:
Packit f0b94e
   *    true: before current match, false: after current match.
Packit f0b94e
   * @param aLinksOnly Only consider nodes that are links for the search.
Packit f0b94e
   * @param aDrawOutline Puts an outline around matched links.
Packit f0b94e
   */
Packit f0b94e
  findAgain(aFindBackwards, aLinksOnly, aDrawOutline) {
Packit f0b94e
    this._lastFindResult = this._fastFind.findAgain(aFindBackwards, aLinksOnly);
Packit f0b94e
    let searchString = this._fastFind.searchString;
Packit f0b94e
    this._notify({
Packit f0b94e
      searchString,
Packit f0b94e
      result: this._lastFindResult,
Packit f0b94e
      findBackwards: aFindBackwards,
Packit f0b94e
      findAgain: true,
Packit f0b94e
      drawOutline: aDrawOutline,
Packit f0b94e
      linksOnly: aLinksOnly
Packit f0b94e
    });
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * Forcibly set the search string of the find clipboard to the currently
Packit f0b94e
   * selected text in the window, on supported platforms (i.e. OSX).
Packit f0b94e
   */
Packit f0b94e
  setSearchStringToSelection() {
Packit f0b94e
    let searchString = this.getActiveSelectionText();
Packit f0b94e
Packit f0b94e
    // Empty strings are rather useless to search for.
Packit f0b94e
    if (!searchString.length)
Packit f0b94e
      return null;
Packit f0b94e
Packit f0b94e
    this.clipboardSearchString = searchString;
Packit f0b94e
    return searchString;
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  async highlight(aHighlight, aWord, aLinksOnly) {
Packit f0b94e
    await this.highlighter.highlight(aHighlight, aWord, aLinksOnly);
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  getInitialSelection() {
Packit f0b94e
    this._getWindow().setTimeout(() => {
Packit f0b94e
      let initialSelection = this.getActiveSelectionText();
Packit f0b94e
      for (let l of this._listeners) {
Packit f0b94e
        try {
Packit f0b94e
          l.onCurrentSelection(initialSelection, true);
Packit f0b94e
        } catch (ex) {}
Packit f0b94e
      }
Packit f0b94e
    }, 0);
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  getActiveSelectionText() {
Packit f0b94e
    let focusedWindow = {};
Packit f0b94e
    let focusedElement =
Packit f0b94e
      Services.focus.getFocusedElementForWindow(this._getWindow(), true,
Packit f0b94e
                                                focusedWindow);
Packit f0b94e
    focusedWindow = focusedWindow.value;
Packit f0b94e
Packit f0b94e
    let selText;
Packit f0b94e
Packit f0b94e
    if (focusedElement instanceof Ci.nsIDOMNSEditableElement &&
Packit f0b94e
        focusedElement.editor) {
Packit f0b94e
      // The user may have a selection in an input or textarea.
Packit f0b94e
      selText = focusedElement.editor.selectionController
Packit f0b94e
        .getSelection(Ci.nsISelectionController.SELECTION_NORMAL)
Packit f0b94e
        .toString();
Packit f0b94e
    } else {
Packit f0b94e
      // Look for any selected text on the actual page.
Packit f0b94e
      selText = focusedWindow.getSelection().toString();
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    if (!selText)
Packit f0b94e
      return "";
Packit f0b94e
Packit f0b94e
    // Process our text to get rid of unwanted characters.
Packit f0b94e
    selText = selText.trim().replace(/\s+/g, " ");
Packit f0b94e
    let truncLength = kSelectionMaxLen;
Packit f0b94e
    if (selText.length > truncLength) {
Packit f0b94e
      let truncChar = selText.charAt(truncLength).charCodeAt(0);
Packit f0b94e
      if (truncChar >= 0xDC00 && truncChar <= 0xDFFF)
Packit f0b94e
        truncLength++;
Packit f0b94e
      selText = selText.substr(0, truncLength);
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    return selText;
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  enableSelection() {
Packit f0b94e
    this._fastFind.setSelectionModeAndRepaint(Ci.nsISelectionController.SELECTION_ON);
Packit f0b94e
    this._restoreOriginalOutline();
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  removeSelection() {
Packit f0b94e
    this._fastFind.collapseSelection();
Packit f0b94e
    this.enableSelection();
Packit f0b94e
    this.highlighter.clear(this._getWindow());
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  focusContent() {
Packit f0b94e
    // Allow Finder listeners to cancel focusing the content.
Packit f0b94e
    for (let l of this._listeners) {
Packit f0b94e
      try {
Packit f0b94e
        if ("shouldFocusContent" in l &&
Packit f0b94e
            !l.shouldFocusContent())
Packit f0b94e
          return;
Packit f0b94e
      } catch (ex) {
Packit f0b94e
        Cu.reportError(ex);
Packit f0b94e
      }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    let fastFind = this._fastFind;
Packit f0b94e
    try {
Packit f0b94e
      // Try to find the best possible match that should receive focus and
Packit f0b94e
      // block scrolling on focus since find already scrolls. Further
Packit f0b94e
      // scrolling is due to user action, so don't override this.
Packit f0b94e
      if (fastFind.foundLink) {
Packit f0b94e
        Services.focus.setFocus(fastFind.foundLink, Services.focus.FLAG_NOSCROLL);
Packit f0b94e
      } else if (fastFind.foundEditable) {
Packit f0b94e
        Services.focus.setFocus(fastFind.foundEditable, Services.focus.FLAG_NOSCROLL);
Packit f0b94e
        fastFind.collapseSelection();
Packit f0b94e
      } else {
Packit f0b94e
        this._getWindow().focus();
Packit f0b94e
      }
Packit f0b94e
    } catch (e) {}
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  onFindbarClose() {
Packit f0b94e
    this.enableSelection();
Packit f0b94e
    this.highlighter.highlight(false);
Packit f0b94e
    this.iterator.reset();
Packit f0b94e
    BrowserUtils.trackToolbarVisibility(this._docShell, "findbar", false);
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  onFindbarOpen() {
Packit f0b94e
    BrowserUtils.trackToolbarVisibility(this._docShell, "findbar", true);
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  onModalHighlightChange(useModalHighlight) {
Packit f0b94e
    if (this._highlighter)
Packit f0b94e
      this._highlighter.onModalHighlightChange(useModalHighlight);
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  onHighlightAllChange(highlightAll) {
Packit f0b94e
    if (this._highlighter)
Packit f0b94e
      this._highlighter.onHighlightAllChange(highlightAll);
Packit f0b94e
    if (this._iterator)
Packit f0b94e
      this._iterator.reset();
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  keyPress(aEvent) {
Packit f0b94e
    let controller = this._getSelectionController(this._getWindow());
Packit f0b94e
Packit f0b94e
    switch (aEvent.keyCode) {
Packit f0b94e
      case aEvent.DOM_VK_RETURN:
Packit f0b94e
        if (this._fastFind.foundLink) {
Packit f0b94e
          let view = this._fastFind.foundLink.ownerGlobal;
Packit f0b94e
          this._fastFind.foundLink.dispatchEvent(new view.MouseEvent("click", {
Packit f0b94e
            view,
Packit f0b94e
            cancelable: true,
Packit f0b94e
            bubbles: true,
Packit f0b94e
            ctrlKey: aEvent.ctrlKey,
Packit f0b94e
            altKey: aEvent.altKey,
Packit f0b94e
            shiftKey: aEvent.shiftKey,
Packit f0b94e
            metaKey: aEvent.metaKey
Packit f0b94e
          }));
Packit f0b94e
        }
Packit f0b94e
        break;
Packit f0b94e
      case aEvent.DOM_VK_TAB:
Packit f0b94e
        let direction = Services.focus.MOVEFOCUS_FORWARD;
Packit f0b94e
        if (aEvent.shiftKey) {
Packit f0b94e
          direction = Services.focus.MOVEFOCUS_BACKWARD;
Packit f0b94e
        }
Packit f0b94e
        Services.focus.moveFocus(this._getWindow(), null, direction, 0);
Packit f0b94e
        break;
Packit f0b94e
      case aEvent.DOM_VK_PAGE_UP:
Packit f0b94e
        controller.scrollPage(false);
Packit f0b94e
        break;
Packit f0b94e
      case aEvent.DOM_VK_PAGE_DOWN:
Packit f0b94e
        controller.scrollPage(true);
Packit f0b94e
        break;
Packit f0b94e
      case aEvent.DOM_VK_UP:
Packit f0b94e
        controller.scrollLine(false);
Packit f0b94e
        break;
Packit f0b94e
      case aEvent.DOM_VK_DOWN:
Packit f0b94e
        controller.scrollLine(true);
Packit f0b94e
        break;
Packit f0b94e
    }
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  _notifyMatchesCount(result = this._currentMatchesCountResult) {
Packit f0b94e
    // The `_currentFound` property is only used for internal bookkeeping.
Packit f0b94e
    delete result._currentFound;
Packit f0b94e
    result.limit = this.matchesCountLimit;
Packit f0b94e
    if (result.total == result.limit)
Packit f0b94e
      result.total = -1;
Packit f0b94e
Packit f0b94e
    for (let l of this._listeners) {
Packit f0b94e
      try {
Packit f0b94e
        l.onMatchesCountResult(result);
Packit f0b94e
      } catch (ex) {}
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    this._currentMatchesCountResult = null;
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  requestMatchesCount(aWord, aLinksOnly) {
Packit f0b94e
    if (this._lastFindResult == Ci.nsITypeAheadFind.FIND_NOTFOUND ||
Packit f0b94e
        this.searchString == "" || !aWord || !this.matchesCountLimit) {
Packit f0b94e
      this._notifyMatchesCount({
Packit f0b94e
        total: 0,
Packit f0b94e
        current: 0
Packit f0b94e
      });
Packit f0b94e
      return;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    this._currentFoundRange = this._fastFind.getFoundRange();
Packit f0b94e
Packit f0b94e
    let params = {
Packit f0b94e
      caseSensitive: this._fastFind.caseSensitive,
Packit f0b94e
      entireWord: this._fastFind.entireWord,
Packit f0b94e
      linksOnly: aLinksOnly,
Packit f0b94e
      word: aWord
Packit f0b94e
    };
Packit f0b94e
    if (!this.iterator.continueRunning(params))
Packit f0b94e
      this.iterator.stop();
Packit f0b94e
Packit f0b94e
    this.iterator.start(Object.assign(params, {
Packit f0b94e
      finder: this,
Packit f0b94e
      limit: this.matchesCountLimit,
Packit f0b94e
      listener: this,
Packit f0b94e
      useCache: true,
Packit f0b94e
    })).then(() => {
Packit f0b94e
      // Without a valid result, there's nothing to notify about. This happens
Packit f0b94e
      // when the iterator was started before and won the race.
Packit f0b94e
      if (!this._currentMatchesCountResult || !this._currentMatchesCountResult.total)
Packit f0b94e
        return;
Packit f0b94e
      this._notifyMatchesCount();
Packit f0b94e
    });
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  // FinderIterator listener implementation
Packit f0b94e
Packit f0b94e
  onIteratorRangeFound(range) {
Packit f0b94e
    let result = this._currentMatchesCountResult;
Packit f0b94e
    if (!result)
Packit f0b94e
      return;
Packit f0b94e
Packit f0b94e
    ++result.total;
Packit f0b94e
    if (!result._currentFound) {
Packit f0b94e
      ++result.current;
Packit f0b94e
      result._currentFound = (this._currentFoundRange &&
Packit f0b94e
        range.startContainer == this._currentFoundRange.startContainer &&
Packit f0b94e
        range.startOffset == this._currentFoundRange.startOffset &&
Packit f0b94e
        range.endContainer == this._currentFoundRange.endContainer &&
Packit f0b94e
        range.endOffset == this._currentFoundRange.endOffset);
Packit f0b94e
    }
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  onIteratorReset() {},
Packit f0b94e
Packit f0b94e
  onIteratorRestart({ word, linksOnly }) {
Packit f0b94e
    this.requestMatchesCount(word, linksOnly);
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  onIteratorStart() {
Packit f0b94e
    this._currentMatchesCountResult = {
Packit f0b94e
      total: 0,
Packit f0b94e
      current: 0,
Packit f0b94e
      _currentFound: false
Packit f0b94e
    };
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  _getWindow() {
Packit f0b94e
    if (!this._docShell)
Packit f0b94e
      return null;
Packit f0b94e
    return this._docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * Get the bounding selection rect in CSS px relative to the origin of the
Packit f0b94e
   * top-level content document.
Packit f0b94e
   */
Packit f0b94e
  _getResultRect() {
Packit f0b94e
    let topWin = this._getWindow();
Packit f0b94e
    let win = this._fastFind.currentWindow;
Packit f0b94e
    if (!win)
Packit f0b94e
      return null;
Packit f0b94e
Packit f0b94e
    let selection = win.getSelection();
Packit f0b94e
    if (!selection.rangeCount || selection.isCollapsed) {
Packit f0b94e
      // The selection can be into an input or a textarea element.
Packit f0b94e
      let nodes = win.document.querySelectorAll("input, textarea");
Packit f0b94e
      for (let node of nodes) {
Packit f0b94e
        if (node instanceof Ci.nsIDOMNSEditableElement && node.editor) {
Packit f0b94e
          try {
Packit f0b94e
            let sc = node.editor.selectionController;
Packit f0b94e
            selection = sc.getSelection(Ci.nsISelectionController.SELECTION_NORMAL);
Packit f0b94e
            if (selection.rangeCount && !selection.isCollapsed) {
Packit f0b94e
              break;
Packit f0b94e
            }
Packit f0b94e
          } catch (e) {
Packit f0b94e
            // If this textarea is hidden, then its selection controller might
Packit f0b94e
            // not be intialized. Ignore the failure.
Packit f0b94e
          }
Packit f0b94e
        }
Packit f0b94e
      }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    if (!selection.rangeCount || selection.isCollapsed) {
Packit f0b94e
      return null;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    let utils = topWin.QueryInterface(Ci.nsIInterfaceRequestor)
Packit f0b94e
                      .getInterface(Ci.nsIDOMWindowUtils);
Packit f0b94e
Packit f0b94e
    let scrollX = {}, scrollY = {};
Packit f0b94e
    utils.getScrollXY(false, scrollX, scrollY);
Packit f0b94e
Packit f0b94e
    for (let frame = win; frame != topWin; frame = frame.parent) {
Packit f0b94e
      let rect = frame.frameElement.getBoundingClientRect();
Packit f0b94e
      let left = frame.getComputedStyle(frame.frameElement).borderLeftWidth;
Packit f0b94e
      let top = frame.getComputedStyle(frame.frameElement).borderTopWidth;
Packit f0b94e
      scrollX.value += rect.left + parseInt(left, 10);
Packit f0b94e
      scrollY.value += rect.top + parseInt(top, 10);
Packit f0b94e
    }
Packit f0b94e
    let rect = Rect.fromRect(selection.getRangeAt(0).getBoundingClientRect());
Packit f0b94e
    return rect.translate(scrollX.value, scrollY.value);
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  _outlineLink(aDrawOutline) {
Packit f0b94e
    let foundLink = this._fastFind.foundLink;
Packit f0b94e
Packit f0b94e
    // Optimization: We are drawing outlines and we matched
Packit f0b94e
    // the same link before, so don't duplicate work.
Packit f0b94e
    if (foundLink == this._previousLink && aDrawOutline)
Packit f0b94e
      return;
Packit f0b94e
Packit f0b94e
    this._restoreOriginalOutline();
Packit f0b94e
Packit f0b94e
    if (foundLink && aDrawOutline) {
Packit f0b94e
      // Backup original outline
Packit f0b94e
      this._tmpOutline = foundLink.style.outline;
Packit f0b94e
      this._tmpOutlineOffset = foundLink.style.outlineOffset;
Packit f0b94e
Packit f0b94e
      // Draw pseudo focus rect
Packit f0b94e
      // XXX Should we change the following style for FAYT pseudo focus?
Packit f0b94e
      // XXX Shouldn't we change default design if outline is visible
Packit f0b94e
      //     already?
Packit f0b94e
      // Don't set the outline-color, we should always use initial value.
Packit f0b94e
      foundLink.style.outline = "1px dotted";
Packit f0b94e
      foundLink.style.outlineOffset = "0";
Packit f0b94e
Packit f0b94e
      this._previousLink = foundLink;
Packit f0b94e
    }
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  _restoreOriginalOutline() {
Packit f0b94e
    // Removes the outline around the last found link.
Packit f0b94e
    if (this._previousLink) {
Packit f0b94e
      this._previousLink.style.outline = this._tmpOutline;
Packit f0b94e
      this._previousLink.style.outlineOffset = this._tmpOutlineOffset;
Packit f0b94e
      this._previousLink = null;
Packit f0b94e
    }
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  _getSelectionController(aWindow) {
Packit f0b94e
    // display: none iframes don't have a selection controller, see bug 493658
Packit f0b94e
    try {
Packit f0b94e
      if (!aWindow.innerWidth || !aWindow.innerHeight)
Packit f0b94e
        return null;
Packit f0b94e
    } catch (e) {
Packit f0b94e
      // If getting innerWidth or innerHeight throws, we can't get a selection
Packit f0b94e
      // controller.
Packit f0b94e
      return null;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    // Yuck. See bug 138068.
Packit f0b94e
    let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
Packit f0b94e
                          .getInterface(Ci.nsIWebNavigation)
Packit f0b94e
                          .QueryInterface(Ci.nsIDocShell);
Packit f0b94e
Packit f0b94e
    let controller = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
Packit f0b94e
                             .getInterface(Ci.nsISelectionDisplay)
Packit f0b94e
                             .QueryInterface(Ci.nsISelectionController);
Packit f0b94e
    return controller;
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  // Start of nsIWebProgressListener implementation.
Packit f0b94e
Packit f0b94e
  onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
Packit f0b94e
    if (!aWebProgress.isTopLevel)
Packit f0b94e
      return;
Packit f0b94e
    // Ignore events that don't change the document.
Packit f0b94e
    if (aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)
Packit f0b94e
      return;
Packit f0b94e
Packit f0b94e
    // Avoid leaking if we change the page.
Packit f0b94e
    this._lastFindResult = this._previousLink = this._currentFoundRange = null;
Packit f0b94e
    this.highlighter.onLocationChange();
Packit f0b94e
    this.iterator.reset();
Packit f0b94e
  },
Packit f0b94e
Packit f0b94e
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
Packit f0b94e
                                         Ci.nsISupportsWeakReference])
Packit f0b94e
};
Packit f0b94e
Packit f0b94e
function GetClipboardSearchString(aLoadContext) {
Packit f0b94e
  let searchString = "";
Packit f0b94e
  if (!Clipboard.supportsFindClipboard())
Packit f0b94e
    return searchString;
Packit f0b94e
Packit f0b94e
  try {
Packit f0b94e
    let trans = Cc["@mozilla.org/widget/transferable;1"]
Packit f0b94e
                  .createInstance(Ci.nsITransferable);
Packit f0b94e
    trans.init(aLoadContext);
Packit f0b94e
    trans.addDataFlavor("text/unicode");
Packit f0b94e
Packit f0b94e
    Clipboard.getData(trans, Ci.nsIClipboard.kFindClipboard);
Packit f0b94e
Packit f0b94e
    let data = {};
Packit f0b94e
    let dataLen = {};
Packit f0b94e
    trans.getTransferData("text/unicode", data, dataLen);
Packit f0b94e
    if (data.value) {
Packit f0b94e
      data = data.value.QueryInterface(Ci.nsISupportsString);
Packit f0b94e
      searchString = data.toString();
Packit f0b94e
    }
Packit f0b94e
  } catch (ex) {}
Packit f0b94e
Packit f0b94e
  return searchString;
Packit f0b94e
}
Packit f0b94e