Bug 916536 - Backout findbar e10s refactor. r=evilpies, a=lsblakk
authorMike de Boer <mdeboer@mozilla.com>
Fri, 11 Oct 2013 12:43:18 +0200
changeset 160648 50d134c566c4b3070d85e33ad45e4e7235fbca7a
parent 160647 0a7556df975daa76366f157a262902d35e826e9d
child 160649 b3ca57c97999652bf1e3024bb78b892fd1d76c96
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersevilpies, lsblakk
bugs916536
milestone26.0a2
Bug 916536 - Backout findbar e10s refactor. r=evilpies, a=lsblakk
browser/base/content/content.js
browser/base/content/tabbrowser.xml
browser/base/content/test/browser_zbug569342.js
toolkit/components/typeaheadfind/nsTypeAheadFind.cpp
toolkit/content/tests/chrome/findbar_window.xul
toolkit/content/widgets/browser.xml
toolkit/content/widgets/findbar.xml
toolkit/content/widgets/remote-browser.xml
toolkit/modules/Finder.jsm
toolkit/modules/RemoteFinder.jsm
toolkit/modules/moz.build
toolkit/modules/tests/browser/Makefile.in
toolkit/modules/tests/browser/browser_Finder.js
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -168,25 +168,19 @@ let AboutHomeListener = {
       case "settings":
         sendAsyncMessage("AboutHome:Settings");
         break;
     }
   },
 };
 AboutHomeListener.init(this);
 
+
 var global = this;
 
-// Lazily load the finder code
-addMessageListener("Finder:Initialize", function () {
-  let {RemoteFinderListener} = Cu.import("resource://gre/modules/RemoteFinder.jsm", {});
-  new RemoteFinderListener(global);
-});
-
-
 let ClickEventHandler = {
   init: function init() {
     Cc["@mozilla.org/eventlistenerservice;1"]
       .getService(Ci.nsIEventListenerService)
       .addSystemEventListener(global, "click", this, true);
   },
 
   handleEvent: function(event) {
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -2747,20 +2747,16 @@
       <property name="pageReport"
                 onget="return this.mCurrentBrowser.pageReport;"
                 readonly="true"/>
 
       <property name="currentURI"
                 onget="return this.mCurrentBrowser.currentURI;"
                 readonly="true"/>
 
-      <property name="finder"
-                onget="return this.mCurrentBrowser.finder"
-                readonly="true"/>
-
       <property name="docShell"
                 onget="return this.mCurrentBrowser.docShell"
                 readonly="true"/>
 
       <property name="webNavigation"
                 onget="return this.mCurrentBrowser.webNavigation"
                 readonly="true"/>
 
--- a/browser/base/content/test/browser_zbug569342.js
+++ b/browser/base/content/test/browser_zbug569342.js
@@ -36,21 +36,17 @@ let urls = [
 ];
 
 function nextTest() {
   let url = urls.shift();
   if (url) {
     testFindDisabled(url, nextTest);
   } else {
     // Make sure the find bar is re-enabled after disabled page is closed.
-    testFindEnabled("about:blank", function () {
-      EventUtils.synthesizeKey("VK_ESCAPE", { });
-      ok(gFindBar.hidden, "Find bar should now be hidden");
-      finish();
-    });
+    testFindEnabled("about:blank", finish);
   }
 }
 
 function testFindDisabled(url, cb) {
   load(url, function() {
     ok(gFindBar.hidden, "Find bar should not be visible");
     EventUtils.synthesizeKey("/", {}, gTab.linkedBrowser.contentWindow);
     ok(gFindBar.hidden, "Find bar should not be visible");
--- a/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp
+++ b/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp
@@ -914,27 +914,25 @@ nsTypeAheadFind::FindAgain(bool aFindBac
 
 NS_IMETHODIMP
 nsTypeAheadFind::Find(const nsAString& aSearchString, bool aLinksOnly,
                       uint16_t* aResult)
 {
   *aResult = FIND_NOTFOUND;
 
   nsCOMPtr<nsIPresShell> presShell (GetPresShell());
-  if (!presShell) {
+  if (!presShell) {    
     nsCOMPtr<nsIDocShell> ds (do_QueryReferent(mDocShell));
     NS_ENSURE_TRUE(ds, NS_ERROR_FAILURE);
 
     presShell = ds->GetPresShell();
-    NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
-    mPresShell = do_GetWeakReference(presShell);
-  }
-
+    mPresShell = do_GetWeakReference(presShell);    
+  }  
   nsCOMPtr<nsISelection> selection;
-  nsCOMPtr<nsISelectionController> selectionController =
+  nsCOMPtr<nsISelectionController> selectionController = 
     do_QueryReferent(mSelectionController);
   if (!selectionController) {
     GetSelection(presShell, getter_AddRefs(selectionController),
                  getter_AddRefs(selection)); // cache for reuse
     mSelectionController = do_GetWeakReference(selectionController);
   } else {
     selectionController->GetSelection(
       nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
--- a/toolkit/content/tests/chrome/findbar_window.xul
+++ b/toolkit/content/tests/chrome/findbar_window.xul
@@ -295,18 +295,17 @@
       gBrowser.contentDocument.documentElement.dispatchEvent(event);
 
       ok(!gFindBar.hidden, "testQuickFindLink: failed to open findbar");
       ok(document.commandDispatcher.focusedElement == gFindBar._findField.inputField,
          "testQuickFindLink: find field is not focused");
 
       var searchStr = "Link Test";
       enterStringIntoFindField(searchStr);
-      ok(gBrowser.contentWindow.getSelection() == searchStr,
-          "testQuickFindLink: failed to find sample link");
+      ok(gFindBar._foundLink, "testQuickFindLink: failed to find sample link");
     }
 
     function testQuickFindText() {
       clearFocus();
 
       var event = document.createEvent("KeyEvents");
       event.initKeyEvent("keypress", true, true, null, false, false,
                          false, false, 0, "/".charCodeAt(0));
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -291,56 +291,41 @@
             var tabBrowser = this.parentNode;
             while (tabBrowser && tabBrowser.localName != "tabbrowser")
               tabBrowser = tabBrowser.parentNode;
             return tabBrowser;
           ]]>
         </body>
       </method>
 
-      <field name="_finder">null</field>
-
-      <property name="finder" readonly="true">
-        <getter><![CDATA[
-          if (!this._finder) {
-            if (!this.docShell)
-              return null;
-
-            let Finder = Components.utils.import("resource://gre/modules/Finder.jsm", {}).Finder;
-            this._finder = new Finder(this.docShell);
-          }
-          return this._finder;
-        ]]></getter>
-      </property>
-
       <field name="_fastFind">null</field>
-      <property name="fastFind" readonly="true">
-        <getter><![CDATA[
+      <property name="fastFind"
+                readonly="true">
+        <getter>
+        <![CDATA[
           if (!this._fastFind) {
             if (!("@mozilla.org/typeaheadfind;1" in Components.classes))
               return null;
 
             var tabBrowser = this.getTabBrowser();
             if (tabBrowser && "fastFind" in tabBrowser)
               return this._fastFind = tabBrowser.fastFind;
 
             if (!this.docShell)
               return null;
 
             this._fastFind = Components.classes["@mozilla.org/typeaheadfind;1"]
                                        .createInstance(Components.interfaces.nsITypeAheadFind);
             this._fastFind.init(this.docShell);
           }
           return this._fastFind;
-        ]]></getter>
+        ]]>
+        </getter>
       </property>
 
-      <field name="_lastSearchString">null</field>
-      <field name="_lastSearchHighlight">false</field>
-
       <property name="webProgress"
                 readonly="true"
                 onget="return this.docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIWebProgress);"/>
 
       <field name="_contentWindow">null</field>
 
       <property name="contentWindow"
                 readonly="true"
--- a/toolkit/content/widgets/findbar.xml
+++ b/toolkit/content/widgets/findbar.xml
@@ -26,44 +26,52 @@
                  this._findbar : this._findbar = document.getBindingParent(this);
         </getter>
       </property>
 
       <method name="_handleEnter">
         <parameter name="aEvent"/>
         <body><![CDATA[
           if (this.findbar._findMode == this.findbar.FIND_NORMAL) {
-            let findString = this.findbar._findField;
+            var findString = this.findbar._findField;
             if (!findString.value)
               return;
 #ifdef XP_MACOSX
             if (aEvent.metaKey) {
 #else
             if (aEvent.ctrlKey) {
 #endif
               this.findbar.getElement("highlight").click();
               return;
             }
 
             this.findbar.onFindAgainCommand(aEvent.shiftKey);
-          } else if (this.findbar._findMode == this.findbar.FIND_LINKS) {
-            this.findbar._finishFAYT(aEvent);
+          }
+          else {
+            // We need to keep a reference to _foundLink because
+            // _finishFAYT resets it to null.
+            var tmpLink = this._findbar._foundLink;
+            if (tmpLink && this.findbar._finishFAYT(aEvent))
+              this.findbar._dispatchKeypressEvent(tmpLink, aEvent);
           }
         ]]></body>
       </method>
 
       <method name="_handleTab">
         <parameter name="aEvent"/>
         <body><![CDATA[
-          let shouldHandle = !aEvent.altKey && !aEvent.ctrlKey &&
+          var shouldHandle = !aEvent.altKey && !aEvent.ctrlKey &&
                              !aEvent.metaKey;
           if (shouldHandle &&
-              this.findbar._findMode != this.findbar.FIND_NORMAL) {
-
-            this.findbar._finishFAYT(aEvent);
+              this.findbar._findMode != this.findbar.FIND_NORMAL &&
+              this.findbar._finishFAYT(aEvent)) {
+            if (aEvent.shiftKey)
+              document.commandDispatcher.rewindFocus();
+            else
+              document.commandDispatcher.advanceFocus();
           }
         ]]></body>
       </method>
     </implementation>
 
     <handlers>
       <handler event="input"><![CDATA[
         // We should do nothing during composition.  E.g., composing string
@@ -73,71 +81,92 @@
         // document.
         if (this.findbar._isIMEComposing) {
           return;
         }
         this.findbar._find(this.value);
       ]]></handler>
 
       <handler event="keypress"><![CDATA[
-        let shouldHandle = !event.altKey && !event.ctrlKey &&
+        var win = this.findbar._currentWindow ||
+                  this.findbar.browser.contentWindow;
+        var controller = this.findbar._getSelectionController(win);
+
+        var shouldHandle = !event.altKey && !event.ctrlKey &&
                            !event.metaKey && !event.shiftKey;
 
         switch (event.keyCode) {
           case KeyEvent.DOM_VK_RETURN:
             this._handleEnter(event);
             break;
           case KeyEvent.DOM_VK_TAB:
             this._handleTab(event);
             break;
           case KeyEvent.DOM_VK_PAGE_UP:
+            if (shouldHandle) {
+              controller.scrollPage(false);
+              event.preventDefault();
+            }
+            break;
           case KeyEvent.DOM_VK_PAGE_DOWN:
             if (shouldHandle) {
-              this.findbar.browser.finder.keyPress(event);
+              controller.scrollPage(true);
               event.preventDefault();
             }
             break;
           case KeyEvent.DOM_VK_UP:
+            controller.scrollLine(false);
+            event.preventDefault();
+            break;
           case KeyEvent.DOM_VK_DOWN:
-            this.findbar.browser.finder.keyPress(event);
+            controller.scrollLine(true);
             event.preventDefault();
             break;
         }
       ]]></handler>
 
       <handler event="blur"><![CDATA[
-        let findbar = this.findbar;
-        findbar.browser.finder.removeSelection();
+        var findbar = this.findbar;
+        var fastFind = findbar.browser.fastFind;
+        if (findbar._foundEditable)
+          fastFind.collapseSelection();
+        else {
+          fastFind.setSelectionModeAndRepaint
+            (findbar.nsISelectionController.SELECTION_ON);
+        }
+        findbar._setFoundLink(null);
+        findbar._foundEditable = null;
+        findbar._currentWindow = null;
       ]]></handler>
 
       <handler event="compositionstart"><![CDATA[
         // Don't close the find toolbar while IME is composing.
-        let findbar = this.findbar;
+        var findbar = this.findbar;
         findbar._isIMEComposing = true;
         if (findbar._quickFindTimeout) {
           clearTimeout(findbar._quickFindTimeout);
           findbar._quickFindTimeout = null;
         }
       ]]></handler>
 
       <handler event="compositionend"><![CDATA[
-        let findbar = this.findbar;
+        var findbar = this.findbar;
         findbar._isIMEComposing = false;
         if (findbar._findMode != findbar.FIND_NORMAL &&
             !findbar.hidden)
           findbar._setFindCloseTimeout();
       ]]></handler>
 
       <handler event="dragover"><![CDATA[
         if (event.dataTransfer.types.contains("text/plain"))
           event.preventDefault();
       ]]></handler>
 
       <handler event="drop"><![CDATA[
-        let value = event.dataTransfer.getData("text/plain");
+        var value = event.dataTransfer.getData("text/plain");
         this.value = value;
         this.findbar._find(value);
         event.stopPropagation();
         event.preventDefault();
       ]]></handler>
     </handlers>
   </binding>
 
@@ -198,20 +227,35 @@
     </content>
 
     <implementation implements="nsIDOMEventListener, nsIEditActionListener">
       <field name="FIND_NORMAL">0</field>
       <field name="FIND_TYPEAHEAD">1</field>
       <field name="FIND_LINKS">2</field>
 
       <field name="_findMode">0</field>
+      <field name="_tmpOutline">null</field>
+      <field name="_tmpOutlineOffset">"0"</field>
+      <field name="_drawOutline">false</field>
+      <field name="_editors">null</field>
+      <field name="_stateListeners">null</field>
 
       <field name="_flashFindBar">0</field>
       <field name="_initialFlashFindBarCount">6</field>
 
+      <property name="_foundLink"
+                onget="return this._foundLinkRef.get();"
+                onset="this._foundLinkRef = Components.utils.getWeakReference(val); return val;"/>
+      <property name="_foundEditable"
+                onget="return this._foundEditableRef.get();"
+                onset="this._foundEditableRef = Components.utils.getWeakReference(val); return val;"/>
+      <property name="_currentWindow"
+                onget="return this._currentWindowRef.get();"
+                onset="this._currentWindowRef = Components.utils.getWeakReference(val); return val;"/>
+
       <property name="prefillWithSelection"
                 onget="return this.getAttribute('prefillwithselection') != 'false'"
                 onset="this.setAttribute('prefillwithselection', val); return val;"/>
       <field name="_selectionMaxLen">150</field>
 
       <method name="getElement">
         <parameter name="aAnonymousID"/>
         <body><![CDATA[
@@ -247,29 +291,26 @@
               document.getElementById(this.getAttribute("browserid"));
           }
           return this._browser;
         ]]></getter>
         <setter><![CDATA[
           if (this._browser) {
             this._browser.removeEventListener("keypress", this, false);
             this._browser.removeEventListener("mouseup", this, false);
-            let finder = this._browser.finder;
-            if (finder)
-              finder.removeResultListener(this);
+            this._foundLink = null;
+            this._foundEditable = null;
+            this._currentWindow = null;
           }
 
           this._browser = val;
           if (this._browser) {
             this._browser.addEventListener("keypress", this, false);
             this._browser.addEventListener("mouseup", this, false);
-            this._browser.finder.addResultListener(this);
-
-            this._findField.value = this._browser._lastSearchString;
-            this.toggleHighlight(this.browser._lastSearchHighlight);
+            this._findField.value = this._browser.fastFind.searchString;
           }
           return val;
         ]]></setter>
       </property>
 
       <field name="_observer"><![CDATA[({
         _self: this,
 
@@ -281,17 +322,17 @@
 
           throw Components.results.NS_ERROR_NO_INTERFACE;
         },
 
         observe: function(aSubject, aTopic, aPrefName) {
           if (aTopic != "nsPref:changed")
             return;
 
-          let prefsvc =
+          var prefsvc =
             aSubject.QueryInterface(Components.interfaces.nsIPrefBranch);
 
           switch (aPrefName) {
             case "accessibility.typeaheadfind":
               this._self._useTypeAheadFind = prefsvc.getBoolPref(aPrefName);
               break;
             case "accessibility.typeaheadfind.linksonly":
               this._self._typeAheadLinksOnly = prefsvc.getBoolPref(aPrefName);
@@ -309,19 +350,21 @@
       <field name="_destroyed">false</field>
 
       <constructor><![CDATA[
         // These elements are accessed frequently and are therefore cached
         this._findField = this.getElement("findbar-textbox");
         this._findStatusIcon = this.getElement("find-status-icon");
         this._findStatusDesc = this.getElement("find-status");
 
-        this._foundURL = null;
+        this._foundLink = null;
+        this._foundEditable = null;
+        this._currentWindow = null;
 
-        let prefsvc =
+        var prefsvc =
           Components.classes["@mozilla.org/preferences-service;1"]
                     .getService(Components.interfaces.nsIPrefBranch);
 
         this._quickFindTimeoutLength =
           prefsvc.getIntPref("accessibility.typeaheadfind.timeout");
         this._flashFindBar =
           prefsvc.getIntPref("accessibility.typeaheadfind.flashBar");
 
@@ -365,23 +408,23 @@
             return;
           this._destroyed = true;
 
           // It is possible that the findbar may be destroyed before any
           // documents it is listening to (see nsIEditActionListener code below).
           // Thus, to avoid leaking, if we are listening to any editors, unhook
           // ourselves now, and remove our cached copies
           if (this._editors) {
-            for (let x = this._editors.length - 1; x >= 0; --x)
+            for (var x = this._editors.length - 1; x >= 0; --x)
               this._unhookListenersAtIndex(x);
           }
 
           this.browser = null;
 
-          let prefsvc =
+          var prefsvc =
             Components.classes["@mozilla.org/preferences-service;1"]
                       .getService(Components.interfaces.nsIPrefBranch);
           prefsvc.removeObserver("accessibility.typeaheadfind",
                                  this._observer);
           prefsvc.removeObserver("accessibility.typeaheadfind.linksonly",
                                  this._observer);
           prefsvc.removeObserver("accessibility.typeaheadfind.casesensitive",
                                  this._observer);
@@ -427,77 +470,649 @@
             setTimeout(function(aSelf) {
                          if (aSelf._findMode != aSelf.FIND_NORMAL)
                            aSelf.close();
                        }, this._quickFindTimeoutLength, this);
         ]]></body>
       </method>
 
       <!--
+        - Gets the selection controller for the current browser
+        -->
+      <method name="_getSelectionController">
+        <parameter name="aWindow"/>
+        <body><![CDATA[
+          // display: none iframes don't have a selection controller, see bug 493658
+          if (!aWindow.innerWidth || !aWindow.innerHeight)
+            return null;
+
+          // Yuck. See bug 138068.
+          var Ci = Components.interfaces;
+          var docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                                .getInterface(Ci.nsIWebNavigation)
+                                .QueryInterface(Ci.nsIDocShell);
+
+          var controller = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                                   .getInterface(Ci.nsISelectionDisplay)
+                                   .QueryInterface(Ci.nsISelectionController);
+          return controller;
+        ]]></body>
+      </method>
+
+      <!--
+        - For a given node, walk up it's parent chain, to try and find an
+        - editable node.
+        -
+        - @param aNode the node we want to check
+        - @returns the first node in the parent chain that is editable,
+        -          null if there is no such node
+        -->
+      <method name="_getEditableNode">
+        <parameter name="aNode"/>
+        <body><![CDATA[
+          while (aNode) {
+            if (aNode instanceof Components.interfaces.nsIDOMNSEditableElement) {
+              return aNode.editor ? aNode : null;
+            }
+            aNode = aNode.parentNode;
+          }
+          return null;
+        ]]></body>
+      </method>
+
+      <!--
+        - Helper method to unhook listeners, remove cached editors
+        - and keep the relevant arrays in sync
+        -
+        - @param aIndex the index into the array of editors/state listeners
+        -        we wish to remove
+        -->
+      <method name="_unhookListenersAtIndex">
+        <parameter name="aIndex"/>
+        <body><![CDATA[
+          this._editors[aIndex].removeEditActionListener(this);
+          this._editors[aIndex]
+              .removeDocumentStateListener(this._stateListeners[aIndex]);
+          this._editors.splice(aIndex, 1);
+          this._stateListeners.splice(aIndex, 1);
+          if (this._editors.length == 0) {
+            delete this._editors;
+            delete this._stateListeners;
+          }
+        ]]></body>
+      </method>
+
+      <!--
+        - Remove ourselves as an nsIEditActionListener and
+        - nsIDocumentStateListener from a given cached editor
+        -
+        - @param aEditor the editor we no longer wish to listen to
+        -->
+      <method name="_removeEditorListeners">
+        <parameter name="aEditor"/>
+        <body><![CDATA[
+          // aEditor is an editor that we listen to, so therefore must be
+          // cached. Find the index of this editor
+          var idx = 0;
+          while (this._editors[idx] != aEditor)
+            idx++;
+
+          // Now unhook ourselves, and remove our cached copy
+          this._unhookListenersAtIndex(idx);
+        ]]></body>
+      </method>
+
+      <!--
+        - nsIEditActionListener logic follows
+        -
+        - We implement this interface to allow us to catch the case where
+        - the findbar found a match in a HTML <input> or <textarea>. If the
+        - user adjusts the text in some way, it will no longer match, so we
+        - want to remove the highlight, rather than have it expand/contract
+        - when letters are added or removed.
+        -->
+
+      <!--
+        - Helper method used to check whether a selection intersects with
+        - some highlighting
+        -
+        - @param aSelectionRange the range from the selection to check
+        - @param aFindRange the highlighted range to check against
+        - @returns true if they intersect, false otherwise
+        -->
+      <method name="_checkOverlap">
+        <parameter name="aSelectionRange"/>
+        <parameter name="aFindRange"/>
+        <body><![CDATA[
+          // The ranges overlap if one of the following is true:
+          // 1) At least one of the endpoints of the deleted selection
+          //    is in the find selection
+          // 2) At least one of the endpoints of the find selection
+          //    is in the deleted selection
+          if (aFindRange.isPointInRange(aSelectionRange.startContainer,
+                                        aSelectionRange.startOffset))
+            return true;
+          if (aFindRange.isPointInRange(aSelectionRange.endContainer,
+                                        aSelectionRange.endOffset))
+            return true;
+          if (aSelectionRange.isPointInRange(aFindRange.startContainer,
+                                             aFindRange.startOffset))
+            return true;
+          if (aSelectionRange.isPointInRange(aFindRange.endContainer,
+                                             aFindRange.endOffset))
+            return true;
+
+          return false;
+        ]]></body>
+      </method>
+
+      <!--
+        - Helper method to determine if an edit occurred within a highlight
+        -
+        - @param aSelection the selection we wish to check
+        - @param aNode the node we want to check is contained in aSelection
+        - @param aOffset the offset into aNode that we want to check
+        - @returns the range containing (aNode, aOffset) or null if no ranges
+        -          in the selection contain it
+        -->
+      <method name="_findRange">
+        <parameter name="aSelection"/>
+        <parameter name="aNode"/>
+        <parameter name="aOffset"/>
+        <body><![CDATA[
+          var rangeCount = aSelection.rangeCount;
+          var rangeidx = 0;
+          var foundContainingRange = false;
+          var range = null;
+
+          // Check to see if this node is inside one of the selection's ranges
+          while (!foundContainingRange && rangeidx < rangeCount) {
+            range = aSelection.getRangeAt(rangeidx);
+            if (range.isPointInRange(aNode, aOffset)) {
+              foundContainingRange = true;
+              break;
+            }
+            rangeidx++;
+          }
+
+          if (foundContainingRange)
+            return range;
+
+          return null;
+        ]]></body>
+      </method>
+
+      <!-- Start of nsIEditActionListener implementations -->
+
+      <method name="WillDeleteText">
+        <parameter name="aTextNode"/>
+        <parameter name="aOffset"/>
+        <parameter name="aLength"/>
+        <body><![CDATA[
+          var editor = this._getEditableNode(aTextNode).editor;
+          var controller = editor.selectionController;
+          var fSelection = controller.getSelection(this._findSelection);
+          var range = this._findRange(fSelection, aTextNode, aOffset);
+
+          if (range) {
+            // Don't remove the highlighting if the deleted text is at the
+            // end of the range
+            if (aTextNode != range.endContainer ||
+                aOffset != range.endOffset) {
+              // Text within the highlight is being removed - the text can
+              // no longer be a match, so remove the highlighting
+              fSelection.removeRange(range);
+              if (fSelection.rangeCount == 0)
+                this._removeEditorListeners(editor);
+            }
+          }
+        ]]></body>
+      </method>
+
+      <method name="DidInsertText">
+        <parameter name="aTextNode"/>
+        <parameter name="aOffset"/>
+        <parameter name="aString"/>
+        <body><![CDATA[
+          var editor = this._getEditableNode(aTextNode).editor;
+          var controller = editor.selectionController;
+          var fSelection = controller.getSelection(this._findSelection);
+          var range = this._findRange(fSelection, aTextNode, aOffset);
+
+          if (range) {
+            // If the text was inserted before the highlight
+            // adjust the highlight's bounds accordingly
+            if (aTextNode == range.startContainer &&
+                       aOffset == range.startOffset)
+              range.setStart(range.startContainer,
+                             range.startOffset+aString.length);
+            else if (aTextNode != range.endContainer ||
+                     aOffset != range.endOffset) {
+              // The edit occurred within the highlight - any addition of text
+              // will result in the text no longer being a match,
+              // so remove the highlighting
+              fSelection.removeRange(range);
+              if (fSelection.rangeCount == 0)
+                this._removeEditorListeners(editor);
+            }
+          }
+        ]]></body>
+      </method>
+
+      <method name="WillDeleteSelection">
+        <parameter name="aSelection"/>
+        <body><![CDATA[
+          var editor = this._getEditableNode(aSelection.getRangeAt(0)
+                                                       .startContainer).editor;
+          var controller = editor.selectionController;
+          var fSelection = controller.getSelection(this._findSelection);
+
+          var selectionIndex = 0;
+          var findSelectionIndex = 0;
+          var shouldDelete = {};
+          var numberOfDeletedSelections = 0;
+          var numberOfMatches = fSelection.rangeCount;
+
+          // We need to test if any ranges in the deleted selection (aSelection)
+          // are in any of the ranges of the find selection
+          // Usually both selections will only contain one range, however
+          // either may contain more than one.
+
+          for (var fIndex = 0; fIndex < numberOfMatches; fIndex++) {
+            shouldDelete[fIndex] = false;
+            var fRange = fSelection.getRangeAt(fIndex);
+
+            for (var index = 0; index < aSelection.rangeCount; index++) {
+              if (!shouldDelete[fIndex]) {
+                var selRange = aSelection.getRangeAt(index);
+                var doesOverlap = this._checkOverlap(selRange, fRange);
+                if (doesOverlap) {
+                  shouldDelete[fIndex] = true;
+                  numberOfDeletedSelections++;
+                }
+              }
+            }
+          }
+
+          // OK, so now we know what matches (if any) are in the selection
+          // that is being deleted. Time to remove them.
+          if (numberOfDeletedSelections == 0)
+            return;
+
+          for (var i = numberOfMatches - 1; i >= 0; i--) {
+            if (shouldDelete[i]) {
+              var r = fSelection.getRangeAt(i);
+              fSelection.removeRange(r);
+            }
+          }
+
+          // Remove listeners if no more highlights left
+          if (fSelection.rangeCount == 0)
+            this._removeEditorListeners(editor);
+        ]]></body>
+      </method>
+
+      <method name="WillInsertText">
+        <body><![CDATA[
+          // Unimplemented
+        ]]></body>
+      </method>
+
+      <method name="DidCreateNode">
+        <body><![CDATA[
+          // Unimplemented
+        ]]></body>
+      </method>
+
+      <method name="DidDeleteNode">
+        <body><![CDATA[
+          // Unimplemented
+        ]]></body>
+      </method>
+
+      <method name="DidDeleteSelection">
+        <body><![CDATA[
+          // Unimplemented
+        ]]></body>
+      </method>
+
+      <method name="DidDeleteText">
+        <body><![CDATA[
+          // Unimplemented
+        ]]></body>
+      </method>
+
+      <method name="DidInsertNode">
+        <body><![CDATA[
+          // Unimplemented
+        ]]></body>
+      </method>
+
+      <method name="DidJoinNodes">
+        <body><![CDATA[
+          // Unimplemented
+        ]]></body>
+      </method>
+
+      <method name="DidSplitNode">
+        <body><![CDATA[
+          // Unimplemented
+        ]]></body>
+      </method>
+
+      <method name="WillCreateNode">
+        <body><![CDATA[
+          // Unimplemented
+        ]]></body>
+      </method>
+
+      <method name="WillDeleteNode">
+        <body><![CDATA[
+          // Unimplemented
+        ]]></body>
+      </method>
+
+      <method name="WillInsertNode">
+        <body><![CDATA[
+          // Unimplemented
+        ]]></body>
+      </method>
+
+      <method name="WillJoinNodes">
+        <body><![CDATA[
+          // Unimplemented
+        ]]></body>
+      </method>
+
+      <method name="WillSplitNode">
+        <body><![CDATA[
+          // Unimplemented
+        ]]></body>
+      </method>
+
+      <!-- End of nsIEditActionListener implementations -->
+
+      <!--
+        - nsIDocumentStateListener logic follows
+        -
+        - When attaching nsIEditActionListeners, there are no guarantees
+        - as to whether the findbar or the documents in the browser will get
+        - destructed first. This leads to the potential to either leak, or to
+        - hold on to a reference an editable element's editor for too long,
+        - preventing it from being destructed.
+        -
+        - However, when an editor's owning node is being destroyed, the editor
+        - sends out a DocumentWillBeDestroyed notification. We can use this to
+        - clean up our references to the object, to allow it to be destroyed in a
+        - timely fashion.
+        -->
+
+      <!--
+        - Unhook ourselves when one of our state listeners has been called.
+        - This can happen in 4 cases:
+        -  1) The document the editor belongs to is navigated away from, and
+        -     the document is not being cached
+        -
+        -  2) The document the editor belongs to is expired from the cache
+        -
+        -  3) The tab containing the owning document is closed
+        -
+        -  4) The <input> or <textarea> that owns the editor is explicitly
+        -     removed from the DOM
+        -
+        - @param the listener that was invoked
+        -->
+      <method name="_onEditorDestruction">
+        <parameter name="aListener"/>
+        <body><![CDATA[
+          // First find the index of the editor the given listener listens to.
+          // The listeners and editors arrays must always be in sync.
+          // The listener will be in our array of cached listeners, as this
+          // method could not have been called otherwise.
+          var idx = 0;
+          while (this._stateListeners[idx] != aListener)
+            idx++;
+
+          // Unhook both listeners
+          this._unhookListenersAtIndex(idx);
+        ]]></body>
+      </method>
+
+      <!--
+        - Creates a unique document state listener for an editor.
+        -
+        - It is not possible to simply have the findbar implement the
+        - listener interface itself, as it wouldn't have sufficient information
+        - to work out which editor was being destroyed. Therefore, we create new
+        - listeners on the fly, and cache them in sync with the editors they
+        - listen to.
+        -->
+      <method name="_createStateListener">
+        <body><![CDATA[
+          return ({
+            findbar: this,
+
+            QueryInterface: function(aIID) {
+              if (aIID.equals(Components.interfaces.nsIDocumentStateListener) ||
+                  aIID.equals(Components.interfaces.nsISupports))
+                return this;
+
+              throw Components.results.NS_ERROR_NO_INTERFACE;
+            },
+
+            NotifyDocumentWillBeDestroyed: function() {
+              this.findbar._onEditorDestruction(this);
+            },
+
+            // Unimplemented
+            notifyDocumentCreated: function() {},
+            notifyDocumentStateChanged: function(aDirty) {}
+          });
+        ]]></body>
+      </method>
+
+      <!--
         - Turns highlight on or off.
         - @param aHighlight (boolean)
         -        Whether to turn the highlight on or off
         -->
       <method name="toggleHighlight">
         <parameter name="aHighlight"/>
         <body><![CDATA[
           if (!this._dispatchFindEvent("highlightallchange"))
             return;
 
-          let word = this._findField.value;
+          var word = this._findField.value;
+
           // Bug 429723. Don't attempt to highlight ""
           if (aHighlight && !word)
             return;
 
-          this.browser._lastSearchHighlight = aHighlight;
-          this.browser.finder.highlight(aHighlight, word);
+          // We have to update the status because we might still have the status
+          // of another tab
+          if (this._highlightDoc(aHighlight, word))
+            this._updateStatusUI(this.nsITypeAheadFind.FIND_FOUND);
+          else
+            this._updateStatusUI(this.nsITypeAheadFind.FIND_NOTFOUND);
+        ]]></body>
+      </method>
+
+      <!--
+        - (Un)highlights each instance of the searched word in the passed
+        - window's content.
+        - @param aHighlight (boolean)
+        -        Whether to turn on highlight
+        - @param aWord
+        -        the word to search for
+        - @param aWindow
+        -        the window to search in. Passing undefined will search the
+        -        current content window.
+        - @returns true if aWord was found
+        -->
+      <method name="_highlightDoc">
+        <parameter name="aHighlight"/>
+        <parameter name="aWord"/>
+        <parameter name="aWindow"/>
+        <body><![CDATA[
+          var win = aWindow || this.browser.contentWindow;
+
+          var textFound = false;
+
+          for (var i = 0; win.frames && i < win.frames.length; i++) {
+            if (this._highlightDoc(aHighlight, aWord, win.frames[i]))
+              textFound = true;
+          }
+
+          var controller = this._getSelectionController(win);
+          if (!controller) {
+            // Without the selection controller,
+            // we are unable to (un)highlight any matches
+            return textFound;
+          }
+
+          var doc = win.document;
+          if (!doc || !doc.documentElement)
+           return textFound;
+
+          var body = (doc instanceof HTMLDocument && doc.body) ?
+                     doc.body : doc.documentElement;
+
+          if (aHighlight) {
+            var searchRange = doc.createRange();
+             searchRange.selectNodeContents(body);
+
+            var startPt = searchRange.cloneRange();
+            startPt.collapse(true);
+
+            var endPt = searchRange.cloneRange();
+            endPt.collapse(false);
+
+            var retRange = null;
+            var finder = Components.classes["@mozilla.org/embedcomp/rangefind;1"]
+                                   .createInstance()
+                                   .QueryInterface(Components.interfaces.nsIFind);
+
+            finder.caseSensitive = this._shouldBeCaseSensitive(aWord);
+
+            while ((retRange = finder.Find(aWord, searchRange,
+                                           startPt, endPt))) {
+              this._highlight(retRange, controller);
+              startPt = retRange.cloneRange();
+              startPt.collapse(false);
+
+              textFound = true;
+            }
+          } else {
+            // First, attempt to remove highlighting from main document
+            var sel = controller.getSelection(this._findSelection);
+            sel.removeAllRanges();
+
+            // Next, check our editor cache, for editors belonging to this
+            // document
+            if (this._editors) {
+              for (var x = this._editors.length - 1; x >= 0; --x) {
+                if (this._editors[x].document == doc) {
+                  sel = this._editors[x].selectionController
+                                        .getSelection(this._findSelection);
+                  sel.removeAllRanges();
+                  // We don't need to listen to this editor any more
+                  this._unhookListenersAtIndex(x);
+                }
+              }
+            }
+            return true;
+          }
+
+          return textFound;
+        ]]></body>
+      </method>
+
+     <!--
+       - Highlights the word in the passed range.
+       -
+       - @param aRange
+       -        the range that contains the word to highlight
+       - @param aController
+       -        the current document's selection controller
+       -->
+      <method name="_highlight">
+        <parameter name="aRange"/>
+        <parameter name="aController"/>
+        <body><![CDATA[
+          var node = aRange.startContainer;
+          var controller = aController;
+          var editableNode = this._getEditableNode(node);
+          if (editableNode)
+            controller = editableNode.editor.selectionController;
+
+          var findSelection = controller.getSelection(this._findSelection);
+          findSelection.addRange(aRange);
+
+          if (editableNode) {
+            // Highlighting added, so cache this editor, and hook up listeners
+            // to ensure we deal properly with edits within the highlighting
+            if (!this._editors) {
+              this._editors = [];
+              this._stateListeners = [];
+            }
+
+            var existingIndex = this._editors.indexOf(editableNode.editor);
+            if (existingIndex == -1) {
+              var x = this._editors.length;
+              this._editors[x] = editableNode.editor;
+              this._stateListeners[x] = this._createStateListener();
+              this._editors[x].addEditActionListener(this);
+              this._editors[x].addDocumentStateListener(this._stateListeners[x]);
+            }
+          }
         ]]></body>
       </method>
 
       <!--
         - Updates the case-sensitivity mode of the findbar and its UI.
         - @param [optional] aString
         -        The string for which case sensitivity might be turned on.
         -        This only used when case-sensitivity is in auto mode,
         -        @see _shouldBeCaseSensitive. The default value for this
         -        parameter is the find-field value.
         -->
       <method name="_updateCaseSensitivity">
         <parameter name="aString"/>
         <body><![CDATA[
-          let val = aString || this._findField.value;
+          var val = aString || this._findField.value;
 
-          let caseSensitive = this._shouldBeCaseSensitive(val);
-          let checkbox = this.getElement("find-case-sensitive");
-          let statusLabel = this.getElement("match-case-status");
+          var caseSensitive = this._shouldBeCaseSensitive(val);
+          var checkbox = this.getElement("find-case-sensitive");
+          var statusLabel = this.getElement("match-case-status");
           checkbox.checked = caseSensitive;
 
           statusLabel.value = caseSensitive ? this._caseSensitiveStr : "";
 
           // Show the checkbox on the full Find bar in non-auto mode.
           // Show the label in all other cases.
-          let hideCheckbox = this._findMode != this.FIND_NORMAL ||
+          var hideCheckbox = this._findMode != this.FIND_NORMAL ||
             (this._typeAheadCaseSensitive != 0 &&
              this._typeAheadCaseSensitive != 1);
           checkbox.hidden = hideCheckbox;
           statusLabel.hidden = !hideCheckbox;
 
-          this.browser.finder.caseSensitive = caseSensitive;
+          var fastFind = this.browser.fastFind;
+          fastFind.caseSensitive = caseSensitive;
         ]]></body>
       </method>
 
       <!--
         - Sets the findbar case-sensitivity mode
         - @param aCaseSensitive (boolean)
         -        Whether or not case-sensitivity should be turned on.
         -->
       <method name="_setCaseSensitivity">
         <parameter name="aCaseSensitive"/>
         <body><![CDATA[
-          let prefsvc =
+          var prefsvc =
             Components.classes["@mozilla.org/preferences-service;1"]
                       .getService(Components.interfaces.nsIPrefBranch);
 
           // Just set the pref; our observer will change the find bar behavior
           prefsvc.setIntPref("accessibility.typeaheadfind.casesensitive",
                              aCaseSensitive ? 1 : 0);
 
           this._dispatchFindEvent("casesensitivitychange");
@@ -515,17 +1130,17 @@
         -->
       <method name="open">
         <parameter name="aMode"/>
         <body><![CDATA[
           if (aMode != undefined)
             this._findMode = aMode;
 
           if (!this._notFoundStr) {
-            let stringsBundle =
+            var stringsBundle =
               Components.classes["@mozilla.org/intl/stringbundle;1"]
                         .getService(Components.interfaces.nsIStringBundleService)
                         .createBundle("chrome://global/locale/findbar.properties");
             this._notFoundStr = stringsBundle.GetStringFromName("NotFound");
             this._wrappedToTopStr =
               stringsBundle.GetStringFromName("WrappedToTop");
             this._wrappedToBottomStr =
               stringsBundle.GetStringFromName("WrappedToBottom");
@@ -560,57 +1175,82 @@
       <!--
         - Closes the findbar.
         -->
       <method name="close">
         <body><![CDATA[
           if (this.hidden)
             return;
 
+          var fm =
+            Components.classes["@mozilla.org/focus-manager;1"]
+                      .getService(Components.interfaces.nsIFocusManager);
+          if (window == fm.focusedWindow) {
+            var focusedElement = fm.focusedElement;
+            if (focusedElement) {
+              var bindingParent = document.getBindingParent(focusedElement);
+              if (bindingParent == this || bindingParent == this._findField) {
+                // block scrolling on focus since find already scrolls, further
+                // scrolling is due to user action, so don't override this
+                var element = this._foundLink || this._foundEditable;
+                if (element)
+                  fm.setFocus(element, fm.FLAG_NOSCROLL);
+                else if (this._currentWindow)
+                    this._currentWindow.focus();
+                else
+                  this.browser.contentWindow.focus();
+              }
+            }
+          }
+
           this.hidden = true;
 
-          this.browser.finder.focusContent();
-          this.browser.finder.removeSelection();
-          this._findField.blur();
-
+          var fastFind = this.browser.fastFind;
+          fastFind.setSelectionModeAndRepaint
+            (this.nsISelectionController.SELECTION_ON);
+          this._setFoundLink(null);
           this._cancelTimers();
+          this._foundEditable = null;
+          this._currentWindow = null;
 
           this._findFailedString = null;
         ]]></body>
       </method>
 
       <method name="clear">
         <body><![CDATA[
-          this.browser.finder.removeSelection();
+          this.browser.fastFind.collapseSelection();
           this._findField.reset();
           this.toggleHighlight(false);
           this._updateStatusUI();
           this._enableFindButtons(false);
+          this._setFoundLink(null);
+          this._foundEditable = null;
+          this._currentWindow = null;
         ]]></body>
       </method>
 
       <method name="_dispatchKeypressEvent">
         <parameter name="aTarget"/>
         <parameter name="aEvent"/>
         <body><![CDATA[
           if (!aTarget)
             return;
 
-          let event = document.createEvent("KeyEvents");
+          var event = document.createEvent("KeyEvents");
           event.initKeyEvent(aEvent.type, aEvent.bubbles, aEvent.cancelable,
                              aEvent.view, aEvent.ctrlKey, aEvent.altKey,
                              aEvent.shiftKey, aEvent.metaKey, aEvent.keyCode,
                              aEvent.charCode);
           aTarget.dispatchEvent(event);
         ]]></body>
       </method>
 
       <field name="_xulBrowserWindow">null</field>
       <method name="_updateStatusUIBar">
-        <parameter name="aFoundURL"/>
         <body><![CDATA[
           if (!this._xulBrowserWindow) {
             try {
               this._xulBrowserWindow =
                 window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                       .getInterface(Components.interfaces.nsIWebNavigation)
                       .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
                       .treeOwner
@@ -618,33 +1258,100 @@
                       .getInterface(Components.interfaces.nsIXULWindow)
                       .XULBrowserWindow;
             }
             catch(ex) { }
             if (!this._xulBrowserWindow)
               return false;
           }
 
-          // Call this has the same effect like hovering over link,
-          // the browser shows the URL as a tooltip.
-          this._xulBrowserWindow.setOverLink(aFoundURL || "", null);
+          if (!this._foundLink || !this._foundLink.href ||
+              this._foundLink.href == "") {
+            this._xulBrowserWindow.setOverLink("", null);
+            return true;
+          }
+
+          var docCharset = "";
+          var ownerDoc = this._foundLink.ownerDocument;
+          if (ownerDoc)
+            docCharset = ownerDoc.characterSet;
+
+          if (!this._textToSubURIService) {
+            this._textToSubURIService =
+              Components.classes["@mozilla.org/intl/texttosuburi;1"]
+                        .getService(Components.interfaces.nsITextToSubURI);
+          }
+          var url =
+            this._textToSubURIService.unEscapeURIForUI(docCharset,
+                                                       this._foundLink.href);
+          this._xulBrowserWindow.setOverLink(url, null);
+
           return true;
         ]]></body>
       </method>
 
+      <method name="_setFoundLink">
+        <parameter name="aFoundLink"/>
+        <body><![CDATA[
+          if (this._foundLink == aFoundLink)
+            return;
+
+          if (this._foundLink && this._drawOutline) {
+            // restore original outline
+            this._foundLink.style.outline = this._tmpOutline;
+            this._foundLink.style.outlineOffset = this._tmpOutlineOffset;
+          }
+          this._drawOutline = (aFoundLink && this._findMode != this.FIND_NORMAL);
+          if (this._drawOutline) {
+            // backup original outline
+            this._tmpOutline = aFoundLink.style.outline;
+            this._tmpOutlineOffset = aFoundLink.style.outlineOffset;
+
+            // draw pseudo focus rect
+            // XXX Should we change the following style for FAYT pseudo focus?
+            // XXX Shouldn't we change default design if outline is visible
+            //     already?
+            // Don't set the outline-color, we should always use initial value.
+            aFoundLink.style.outline = "1px dotted";
+            aFoundLink.style.outlineOffset = "0";
+          }
+
+          this._foundLink = aFoundLink;
+
+          // If the mouse cursor is on the document, the status bar text is
+          // changed by a mouse event which is dispatched by a scroll event.
+          // Thus we should change it only after the mouse event is dispatched.
+          if (this._findMode != this.FIND_NORMAL)
+            setTimeout(function(aSelf) { aSelf._updateStatusUIBar(); }, 0, this);
+        ]]></body>
+      </method>
+
       <method name="_finishFAYT">
         <parameter name="aKeypressEvent"/>
         <body><![CDATA[
-          this.browser.finder.focusContent();
+          try {
+            if (this._foundLink)
+              this._foundLink.focus();
+            else if (this._foundEditable) {
+              this._foundEditable.focus();
+              var fastFind = this.browser.fastFind;
+              fastFind.collapseSelection();
+            }
+            else if (this._currentWindow)
+              this._currentWindow.focus();
+            else
+              return false;
+          }
+          catch(e) {
+            return false;
+          }
 
           if (aKeypressEvent)
             aKeypressEvent.preventDefault();
 
-          this.browser.finder.keyPress(aKeypressEvent);
-
           this.close();
           return true;
         ]]></body>
       </method>
 
       <!--
         - Returns true if |aMimeType| is text-based, or false otherwise.
         -
@@ -671,46 +1378,46 @@
         -->
       <method name="_shouldFastFind">
         <parameter name="aEvent"/>
         <body><![CDATA[
           if (aEvent.ctrlKey || aEvent.altKey || aEvent.metaKey ||
               aEvent.defaultPrevented)
             return false;
 
-          let elt = document.commandDispatcher.focusedElement;
+          var elt = document.commandDispatcher.focusedElement;
           if (elt) {
             if (elt instanceof HTMLInputElement && elt.mozIsTextField(false))
               return false;
 
             if (elt instanceof HTMLTextAreaElement ||
                 elt instanceof HTMLSelectElement ||
                 elt instanceof HTMLObjectElement ||
                 elt instanceof HTMLEmbedElement)
               return false;
           }
 
-          let win = document.commandDispatcher.focusedWindow;
+          var win = document.commandDispatcher.focusedWindow;
           if (win && !this._mimeTypeIsTextBased(win.document.contentType))
               return false;
 
           // disable FAYT in about:blank to prevent FAYT opening unexpectedly.
-          let url = this.browser.currentURI;
+          var url = this.browser.currentURI;
           if (url.spec == "about:blank")
             return false;
 
           // disable FAYT in documents that ask for it to be disabled.
           if ((url.schemeIs("about") || url.schemeIs("chrome")) &&
               (win.document.documentElement &&
                win.document.documentElement.getAttribute("disablefastfind") == "true"))
             return false;
 
           if (win) {
             try {
-              let editingSession = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+              var editingSession = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                                    .getInterface(Components.interfaces.nsIWebNavigation)
                                    .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                                    .getInterface(Components.interfaces.nsIEditingSession);
               if (editingSession.windowIsEditable(win))
                 return false;
             }
             catch (e) {
               // If someone built with composer disabled, we can't get an editing session.
@@ -748,22 +1455,22 @@
 
             this._findField.select();
             this._findField.focus();
             this._dispatchKeypressEvent(this._findField.inputField, aEvent);
             aEvent.preventDefault();
             return;
           }
 
-          let key = aEvent.charCode ? String.fromCharCode(aEvent.charCode) : null;
-          let manualstartFAYT = (key == TAF_LINKS_KEY || key == TAF_TEXT_KEY);
-          let autostartFAYT = !manualstartFAYT && this._useTypeAheadFind &&
+          var key = aEvent.charCode ? String.fromCharCode(aEvent.charCode) : null;
+          var manualstartFAYT = (key == TAF_LINKS_KEY || key == TAF_TEXT_KEY);
+          var autostartFAYT = !manualstartFAYT && this._useTypeAheadFind &&
                               key && key != " ";
           if (manualstartFAYT || autostartFAYT) {
-            let mode = (key == TAF_LINKS_KEY ||
+            var mode = (key == TAF_LINKS_KEY ||
                         (autostartFAYT && this._typeAheadLinksOnly)) ?
               this.FIND_LINKS : this.FIND_TYPEAHEAD;
 
             // Clear bar first, so that when openFindBar() calls setCaseSensitivity()
             // it doesn't get confused by a lingering value
             this._findField.value = "";
 
             this.open(mode);
@@ -834,56 +1541,80 @@
             this._findField.placeholder = this._fastFindStr;
           else if (this._findMode == this.FIND_LINKS)
             this._findField.placeholder = this._fastFindLinksStr;
           else
             this._findField.placeholder = this._normalFindStr;
         ]]></body>
       </method>
 
+      <method name="_updateFoundLink">
+        <parameter name="res"/>
+        <body><![CDATA[
+          var val = this._findField.value;
+          if (res == this.nsITypeAheadFind.FIND_NOTFOUND || !val) {
+            this._setFoundLink(null);
+            this._foundEditable = null;
+            this._currentWindow = null;
+          }
+          else {
+            this._setFoundLink(this.browser.fastFind.foundLink);
+            this._foundEditable = this.browser.fastFind.foundEditable;
+            this._currentWindow = this.browser.fastFind.currentWindow;
+          }
+        ]]></body>
+      </method>
+
       <method name="_find">
         <parameter name="aValue"/>
         <body><![CDATA[
           if (!this._dispatchFindEvent(""))
-            return;
-
-          let val = aValue || this._findField.value;
+            return this.nsITypeAheadFind.FIND_PENDING;
 
-          // We have to carry around an explicit version of this,
-          // because finder.searchString doesn't update on failed
-          // searches.
-          this.browser._lastSearchString = val;
+          var val = aValue || this._findField.value;
+          var res = this.nsITypeAheadFind.FIND_NOTFOUND;
 
           // Only search on input if we don't have a last-failed string,
           // or if the current search string doesn't start with it.
           if (this._findFailedString == null ||
-              !val.startsWith(this._findFailedString))
+              val.indexOf(this._findFailedString) != 0)
           {
             this._enableFindButtons(val);
             if (this.getElement("highlight").checked)
               this._setHighlightTimeout();
 
             this._updateCaseSensitivity(val);
 
-            this.browser.finder.fastFind(val, this._findMode == this.FIND_LINKS);
+            var fastFind = this.browser.fastFind;
+            res = fastFind.find(val, this._findMode == this.FIND_LINKS);
+
+            this._updateFoundLink(res);
+            this._updateStatusUI(res, false);
+
+            if (res == this.nsITypeAheadFind.FIND_NOTFOUND)
+              this._findFailedString = val;
+            else
+              this._findFailedString = null;
           }
 
           if (this._findMode != this.FIND_NORMAL)
             this._setFindCloseTimeout();
 
           if (this._findResetTimeout != -1)
             clearTimeout(this._findResetTimeout);
 
           // allow a search to happen on input again after a second has
           // expired since the previous input, to allow for dynamic
           // content and/or page loading
-          this._findResetTimeout = setTimeout(() => {
-            this._findFailedString = null;
-            this._findResetTimeout = -1;
-          }, 1000);
+          this._findResetTimeout = setTimeout(function(self) {
+						  self._findFailedString = null;
+						  self._findResetTimeout = -1; },
+					      1000, this);
+
+          return res;
         ]]></body>
       </method>
 
       <method name="_flash">
         <body><![CDATA[
           if (this._flashFindBarCount === undefined)
             this._flashFindBarCount = this._initialFlashFindBarCount;
 
@@ -910,18 +1641,26 @@
                          aSelf.toggleHighlight(true);
                        }, 500, this);
         ]]></body>
       </method>
 
       <method name="_findAgain">
         <parameter name="aFindPrevious"/>
         <body><![CDATA[
-          this.browser.finder.findAgain(aFindPrevious,
-                                        this._findMode == this.FIND_LINKS);
+          var fastFind = this.browser.fastFind;
+          var res = fastFind.findAgain(aFindPrevious,
+                                       this._findMode == this.FIND_LINKS);
+          this._updateFoundLink(res);
+          this._updateStatusUI(res, aFindPrevious);
+
+          if (this._findMode != this.FIND_NORMAL && !this.hidden)
+            this._setFindCloseTimeout();
+
+          return res;
         ]]></body>
       </method>
 
       <method name="_updateStatusUI">
         <parameter name="res"/>
         <parameter name="aFindPrevious"/>
         <body><![CDATA[
           switch (res) {
@@ -955,43 +1694,44 @@
         <parameter name="aResult"/>
         <parameter name="aFindPrevious"/>
         <body><![CDATA[
           this._updateStatusUI(aResult, aFindPrevious);
           this._enableFindButtons(aResult !== this.nsITypeAheadFind.FIND_NOTFOUND);
         ]]></body>
       </method>
 
+
       <method name="_getInitialSelection">
         <body><![CDATA[
-          let focusedElement = document.commandDispatcher.focusedElement;
-          let selText;
+          var focusedElement = document.commandDispatcher.focusedElement;
+          var selText;
 
           if (focusedElement instanceof Components.interfaces.nsIDOMNSEditableElement &&
               focusedElement.editor &&
               focusedElement.ownerDocument.defaultView.top == this._browser.contentWindow)
           {
             // The user may have a selection in an input or textarea
             selText = focusedElement.editor.selectionController
               .getSelection(Components.interfaces.nsISelectionController.SELECTION_NORMAL)
               .toString();
           }
           else {
             // Look for any selected text on the actual page
-            let focusedWindow = document.commandDispatcher.focusedWindow;
+            var focusedWindow = document.commandDispatcher.focusedWindow;
             if (focusedWindow.top == this._browser.contentWindow)
               selText = focusedWindow.getSelection().toString();
           }
 
           if (!selText)
             return "";
 
           // Process our text to get rid of unwanted characters
           if (selText.length > this._selectionMaxLen) {
-            let pattern = new RegExp("^(?:\\s*.){0," + this._selectionMaxLen + "}");
+            var pattern = new RegExp("^(?:\\s*.){0," + this._selectionMaxLen + "}");
             pattern.test(selText);
             selText = RegExp.lastMatch;
           }
           return selText.replace(/^\s+/, "")
                         .replace(/\s+$/, "")
                         .replace(/\s+/g, " ")
                         .substr(0, this._selectionMaxLen);
         ]]></body>
@@ -1019,33 +1759,34 @@
         - @param aMode
         -        the find mode to be used, which is either FIND_NORMAL,
         -        FIND_TYPEAHEAD or FIND_LINKS. If not passed, the last
         -        find mode if any or FIND_NORMAL.
         -->
       <method name="startFind">
         <parameter name="aMode"/>
         <body><![CDATA[
-          let prefsvc =
+          var prefsvc =
             Components.classes["@mozilla.org/preferences-service;1"]
                       .getService(Components.interfaces.nsIPrefBranch);
-          let userWantsPrefill = true;
+          var userWantsPrefill = true;
           this.open(aMode);
 
           if (this._flashFindBar) {
-            this._flashFindBarTimeout = setInterval(() => this._flash(), 500);
+            this._flashFindBarTimeout =
+              setInterval(function(aSelf) { aSelf._flash(); }, 500, this);
             prefsvc.setIntPref("accessibility.typeaheadfind.flashBar",
                                --this._flashFindBar);
           }
 
           if (this.prefillWithSelection)
             userWantsPrefill =
               prefsvc.getBoolPref("accessibility.typeaheadfind.prefillwithselection");
 
-          let initialString = (this.prefillWithSelection && userWantsPrefill) ?
+          var initialString = (this.prefillWithSelection && userWantsPrefill) ?
                               this._getInitialSelection() : null;
           if (initialString)
             this._findField.value = initialString;
 
           this._enableFindButtons(!!this._findField.value);
 
           this._findField.select();
           this._findField.focus();
@@ -1067,72 +1808,49 @@
       <!--
         - Stub for find-next and find-previous commands
         - @param aFindPrevious
         -        true for find-previous, false otherwise.
         -->
       <method name="onFindAgainCommand">
         <parameter name="aFindPrevious"/>
         <body><![CDATA[
-          let findString = this._browser.finder.searchString || this._findField.value;
+          var findString = this._browser.fastFind.searchString || this._findField.value;
           if (!findString) {
             this.startFind();
             return;
           }
 
           // We dispatch the findAgain event here instead of in _findAgain since
           // if there is a find event handler that prevents the default then
-          // finder.searchString will never get updated which in turn means
+          // fastFind.searchString will never get updated which in turn means
           // there would never be findAgain events because of the logic below.
           if (!this._dispatchFindEvent("again", aFindPrevious))
             return;
 
           // user explicitly requested another search, so do it even if we think it'll fail
           this._findFailedString = null;
 
+          var res;
           // Ensure the stored SearchString is in sync with what we want to find
-          if (this._findField.value != this._browser.finder.searchString)
-            this._find(this._findField.value);
+          if (this._findField.value != this._browser.fastFind.searchString)
+            res = this._find(this._findField.value);
           else
-            this._findAgain(aFindPrevious);
+            res = this._findAgain(aFindPrevious);
 
+          if (res == this.nsITypeAheadFind.FIND_NOTFOUND) {
+            if (this.open()) {
+              if (this._findMode != this.FIND_NORMAL)
+                this._setFindCloseTimeout();
+              this._findField.focus();
+              this._findField.focus();
+              this._updateStatusUI(res, aFindPrevious);
+            }
+          }
         ]]></body>
       </method>
-
-      <!--
-        - This handles all the result changes for both
-        - type-ahead-find and highlighting.
-        - @param aResult
-        -   One of the nsITypeAheadFind.FIND_* constants
-        -   indicating the result of a search operation.
-        - @param aFindBackwards
-        -   If the search was done from the bottom to
-        -   the top. This is used for right error messages
-        -   when reaching "the end of the page".
-        - @param aLinkURL
-        -   When a link matched then its URK. Always null
-        -   when not in FIND_LINKS mode.
-        -->
-      <method name="onFindResult">
-        <parameter name="aResult"/>
-        <parameter name="aFindBackwards"/>
-        <parameter name="aLinkURL" />
-        <body><![CDATA[
-          this._updateStatusUI(aResult, aFindBackwards);
-          this._updateStatusUIBar(aLinkURL);
-
-          if (aResult == this.nsITypeAheadFind.FIND_NOTFOUND)
-            this._findFailedString = this.browser.finder.searchString;
-          else
-            this._findFailedString = null;
-
-          if (this._findMode != this.FIND_NORMAL && !this.hidden)
-            this._setFindCloseTimeout();
-        ]]></body>
-      </method>
-
     </implementation>
 
     <handlers>
       <handler event="keypress" keycode="VK_ESCAPE" phase="capturing" action="this.close();" preventdefault="true"/>
     </handlers>
   </binding>
 </bindings>
--- a/toolkit/content/widgets/remote-browser.xml
+++ b/toolkit/content/widgets/remote-browser.xml
@@ -57,29 +57,16 @@
               this._remoteWebProgress = new RemoteWebProgressManager(this)
                                          .topLevelWebProgress;
             }
             return this._remoteWebProgress;
       	  ]]>
       	</getter>
       </property>
 
-      <field name="_remoteFinder">null</field>
-
-      <property name="finder" readonly="true">
-        <getter><![CDATA[
-          if (!this._remoteFinder) {
-            let jsm = "resource://gre/modules/RemoteFinder.jsm";
-            let RemoteFinder = Cu.import(jsm, {}).RemoteFinder;
-            this._remoteFinder = new RemoteFinder(this);
-          }
-          return this._remoteFinder;
-        ]]></getter>
-      </property>
-
       <field name="_documentURI">null</field>
 
       <property name="documentURI"
                 onget="return this._documentURI;"
                 readonly="true"/>
 
       <field name="_contentTitle">""</field>
 
deleted file mode 100644
--- a/toolkit/modules/Finder.jsm
+++ /dev/null
@@ -1,592 +0,0 @@
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-this.EXPORTED_SYMBOLS = ["Finder"];
-
-const Ci = Components.interfaces;
-const Cc = Components.classes;
-const Cu = Components.utils;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-const Services = Cu.import("resource://gre/modules/Services.jsm").Services;
-
-function Finder(docShell) {
-  this._fastFind = Cc["@mozilla.org/typeaheadfind;1"].createInstance(Ci.nsITypeAheadFind);
-  this._fastFind.init(docShell);
-
-  this._docShell = docShell;
-  this._document = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                           .getInterface(Ci.nsIDOMWindow).document;
-  this._listeners = [];
-
-  this._previousLink = null;
-  this._drewOutline = false;
-
-  this._searchString = null;
-}
-
-Finder.prototype = {
-  addResultListener: function (aListener) {
-    if (this._listeners.indexOf(aListener) === -1)
-      this._listeners.push(aListener);
-  },
-
-  removeResultListener: function (aListener) {
-    this._listeners = this._listeners.filter(l => l != aListener);
-  },
-
-  _notify: function (aResult, aFindBackwards, aLinksOnly) {
-    this._outlineLink(aLinksOnly);
-
-    let foundLink = this._fastFind.foundLink;
-    let linkURL = null;
-    if (aLinksOnly && foundLink) {
-      let docCharset = null;
-      let ownerDoc = foundLink.ownerDocument;
-      if (ownerDoc)
-        docCharset = ownerDoc.characterSet;
-
-      if (!this._textToSubURIService) {
-        this._textToSubURIService = Cc["@mozilla.org/intl/texttosuburi;1"]
-                                      .getService(Ci.nsITextToSubURI);
-      }
-
-      linkURL = this._textToSubURIService.unEscapeURIForUI(docCharset, foundLink.href);
-    }
-
-    for (let l of this._listeners) {
-      l.onFindResult(aResult, aFindBackwards, linkURL);
-    }
-  },
-
-  get searchString() {
-    return this._fastFind.searchString;
-  },
-
-  set caseSensitive(aSensitive) {
-    this._fastFind.caseSensitive = aSensitive;
-  },
-
-  fastFind: function (aSearchString, aLinksOnly) {
-    let result = this._fastFind.find(aSearchString, aLinksOnly);
-    this._notify(result, false, aLinksOnly);
-  },
-
-  findAgain: function (aFindBackwards, aLinksOnly) {
-    let result = this._fastFind.findAgain(aFindBackwards, aLinksOnly);
-    this._notify(result, aFindBackwards, aLinksOnly);
-  },
-
-  highlight: function (aHighlight, aWord) {
-    this._searchString = aWord;
-    let found = this._highlight(aHighlight, aWord, null);
-    if (found)
-      this._notify(Ci.nsITypeAheadFind.FIND_FOUND, false, false);
-    else
-      this._notify(Ci.nsITypeAheadFind.FIND_NOTFOUND, false, false);
-  },
-
-  removeSelection: function() {
-    let fastFind = this._fastFind;
-
-    fastFind.collapseSelection();
-    fastFind.setSelectionModeAndRepaint(Ci.nsISelectionController.SELECTION_ON);
-
-    // We also drew our own outline, remove that as well.
-    if (this._previousLink && this._drewOutline) {
-      this._previousLink.style.outline = this._tmpOutline;
-      this._previousLink.style.outlineOffset = this._tmpOutlineOffset;
-    }
-  },
-
-  focusContent: function() {
-    let fastFind = this._fastFind;
-
-    try {
-      // Try to find the best possible match that should receive focus.
-      if (fastFind.foundLink) {
-        fastFind.foundLink.focus();
-      } else if (fastFind.foundEditable) {
-        fastFind.foundEditable.focus();
-        fastFind.collapseSelection();
-      } else {
-        this._getWindow().focus()
-      }
-    } catch (e) {}
-  },
-
-  keyPress: function (aEvent) {
-    let controller = this._getSelectionController(this._getWindow());
-
-    switch (aEvent.keyCode) {
-      case Ci.nsIDOMKeyEvent.DOM_VK_RETURN:
-        if (this._fastFind.foundLink) // Todo: Handle ctrl click.
-          this._fastFind.foundLink.click();
-        break;
-      case Ci.nsIDOMKeyEvent.DOM_VK_TAB:
-        if (aEvent.shiftKey)
-          this._document.commandDispatcher.rewindFocus();
-        else
-          this._document.commandDispatcher.advanceFocus();
-        break;
-      case Ci.nsIDOMKeyEvent.DOM_VK_PAGE_UP:
-        controller.scrollPage(false);
-        break;
-      case Ci.nsIDOMKeyEvent.DOM_VK_PAGE_DOWN:
-        controller.scrollPage(true);
-        break;
-      case Ci.nsIDOMKeyEvent.DOM_VK_UP:
-        controller.scrollLine(false);
-        break;
-      case Ci.nsIDOMKeyEvent.DOM_VK_DOWN:
-        controller.scrollLine(true);
-        break;
-    }
-  },
-
-  _getWindow: function () {
-    return this._docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
-  },
-
-  _outlineLink: function (aLinksOnly) {
-    let foundLink = this._fastFind.foundLink;
-
-    if (foundLink == this._previousLink)
-      return;
-
-    if (this._previousLink && this._drewOutline) {
-      // restore original outline
-      this._previousLink.style.outline = this._tmpOutline;
-      this._previousLink.style.outlineOffset = this._tmpOutlineOffset;
-    }
-
-    this._drewOutline = (foundLink && aLinksOnly);
-    if (this._drewOutline) {
-      // Backup original outline
-      this._tmpOutline = foundLink.style.outline;
-      this._tmpOutlineOffset = foundLink.style.outlineOffset;
-
-      // Draw pseudo focus rect
-      // XXX Should we change the following style for FAYT pseudo focus?
-      // XXX Shouldn't we change default design if outline is visible
-      //     already?
-      // Don't set the outline-color, we should always use initial value.
-      foundLink.style.outline = "1px dotted";
-      foundLink.style.outlineOffset = "0";
-    }
-
-    this._previousLink = foundLink;
-  },
-
-  _highlight: function (aHighlight, aWord, aWindow) {
-    let win = aWindow || this._getWindow();
-
-    let found = false;
-    for (let i = 0; win.frames && i < win.frames.length; i++) {
-      if (this._highlight(aHighlight, aWord, win.frames[i]))
-        found = true;
-    }
-
-    let controller = this._getSelectionController(win);
-    let doc = win.document;
-    if (!controller || !doc || !doc.documentElement) {
-      // Without the selection controller,
-      // we are unable to (un)highlight any matches
-      return found;
-    }
-
-    let body = (doc instanceof Ci.nsIDOMHTMLDocument && doc.body) ?
-               doc.body : doc.documentElement;
-
-    if (aHighlight) {
-      let searchRange = doc.createRange();
-      searchRange.selectNodeContents(body);
-
-      let startPt = searchRange.cloneRange();
-      startPt.collapse(true);
-
-      let endPt = searchRange.cloneRange();
-      endPt.collapse(false);
-
-      let retRange = null;
-      let finder = Cc["@mozilla.org/embedcomp/rangefind;1"]
-                     .createInstance()
-                     .QueryInterface(Ci.nsIFind);
-
-      finder.caseSensitive = this._fastFind.caseSensitive;
-
-      while ((retRange = finder.Find(aWord, searchRange,
-                                     startPt, endPt))) {
-        this._highlightRange(retRange, controller);
-        startPt = retRange.cloneRange();
-        startPt.collapse(false);
-
-        found = true;
-      }
-    } else {
-      // First, attempt to remove highlighting from main document
-      let sel = controller.getSelection(Ci.nsISelectionController.SELECTION_FIND);
-      sel.removeAllRanges();
-
-      // Next, check our editor cache, for editors belonging to this
-      // document
-      if (this._editors) {
-        for (let x = this._editors.length - 1; x >= 0; --x) {
-          if (this._editors[x].document == doc) {
-            sel = this._editors[x].selectionController
-                                  .getSelection(Ci.nsISelectionController.SELECTION_FIND);
-            sel.removeAllRanges();
-            // We don't need to listen to this editor any more
-            this._unhookListenersAtIndex(x);
-          }
-        }
-      }
-
-      //Removing the highlighting always succeeds, so return true.
-      found = true;
-    }
-
-    return found;
-  },
-
-  _highlightRange: function(aRange, aController) {
-    let node = aRange.startContainer;
-    let controller = aController;
-
-    let editableNode = this._getEditableNode(node);
-    if (editableNode)
-      controller = editableNode.editor.selectionController;
-
-    let findSelection = controller.getSelection(Ci.nsISelectionController.SELECTION_FIND);
-    findSelection.addRange(aRange);
-
-    if (editableNode) {
-      // Highlighting added, so cache this editor, and hook up listeners
-      // to ensure we deal properly with edits within the highlighting
-      if (!this._editors) {
-        this._editors = [];
-        this._stateListeners = [];
-      }
-
-      let existingIndex = this._editors.indexOf(editableNode.editor);
-      if (existingIndex == -1) {
-        let x = this._editors.length;
-        this._editors[x] = editableNode.editor;
-        this._stateListeners[x] = this._createStateListener();
-        this._editors[x].addEditActionListener(this);
-        this._editors[x].addDocumentStateListener(this._stateListeners[x]);
-      }
-    }
-  },
-
-  _getSelectionController: function(aWindow) {
-    // display: none iframes don't have a selection controller, see bug 493658
-    if (!aWindow.innerWidth || !aWindow.innerHeight)
-      return null;
-
-    // Yuck. See bug 138068.
-    let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIWebNavigation)
-                          .QueryInterface(Ci.nsIDocShell);
-
-    let controller = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                             .getInterface(Ci.nsISelectionDisplay)
-                             .QueryInterface(Ci.nsISelectionController);
-    return controller;
-  },
-
-  /*
-   * For a given node, walk up it's parent chain, to try and find an
-   * editable node.
-   *
-   * @param aNode the node we want to check
-   * @returns the first node in the parent chain that is editable,
-   *          null if there is no such node
-   */
-  _getEditableNode: function (aNode) {
-    while (aNode) {
-      if (aNode instanceof Ci.nsIDOMNSEditableElement)
-        return aNode.editor ? aNode : null;
-
-      aNode = aNode.parentNode;
-    }
-    return null;
-  },
-
-  /*
-   * Helper method to unhook listeners, remove cached editors
-   * and keep the relevant arrays in sync
-   *
-   * @param aIndex the index into the array of editors/state listeners
-   *        we wish to remove
-   */
-  _unhookListenersAtIndex: function (aIndex) {
-    this._editors[aIndex].removeEditActionListener(this);
-    this._editors[aIndex]
-        .removeDocumentStateListener(this._stateListeners[aIndex]);
-    this._editors.splice(aIndex, 1);
-    this._stateListeners.splice(aIndex, 1);
-    if (!this._editors.length) {
-      delete this._editors;
-      delete this._stateListeners;
-    }
-  },
-
-  /*
-   * Remove ourselves as an nsIEditActionListener and
-   * nsIDocumentStateListener from a given cached editor
-   *
-   * @param aEditor the editor we no longer wish to listen to
-   */
-  _removeEditorListeners: function (aEditor) {
-    // aEditor is an editor that we listen to, so therefore must be
-    // cached. Find the index of this editor
-    let idx = this._editors.indexOf(aEditor);
-    if (idx == -1)
-      return;
-    // Now unhook ourselves, and remove our cached copy
-    this._unhookListenersAtIndex(idx);
-  },
-
-  /*
-   * nsIEditActionListener logic follows
-   *
-   * We implement this interface to allow us to catch the case where
-   * the findbar found a match in a HTML <input> or <textarea>. If the
-   * user adjusts the text in some way, it will no longer match, so we
-   * want to remove the highlight, rather than have it expand/contract
-   * when letters are added or removed.
-   */
-
-  /*
-   * Helper method used to check whether a selection intersects with
-   * some highlighting
-   *
-   * @param aSelectionRange the range from the selection to check
-   * @param aFindRange the highlighted range to check against
-   * @returns true if they intersect, false otherwise
-   */
-  _checkOverlap: function (aSelectionRange, aFindRange) {
-    // The ranges overlap if one of the following is true:
-    // 1) At least one of the endpoints of the deleted selection
-    //    is in the find selection
-    // 2) At least one of the endpoints of the find selection
-    //    is in the deleted selection
-    if (aFindRange.isPointInRange(aSelectionRange.startContainer,
-                                  aSelectionRange.startOffset))
-      return true;
-    if (aFindRange.isPointInRange(aSelectionRange.endContainer,
-                                  aSelectionRange.endOffset))
-      return true;
-    if (aSelectionRange.isPointInRange(aFindRange.startContainer,
-                                       aFindRange.startOffset))
-      return true;
-    if (aSelectionRange.isPointInRange(aFindRange.endContainer,
-                                       aFindRange.endOffset))
-      return true;
-
-    return false;
-  },
-
-  /*
-   * Helper method to determine if an edit occurred within a highlight
-   *
-   * @param aSelection the selection we wish to check
-   * @param aNode the node we want to check is contained in aSelection
-   * @param aOffset the offset into aNode that we want to check
-   * @returns the range containing (aNode, aOffset) or null if no ranges
-   *          in the selection contain it
-   */
-  _findRange: function (aSelection, aNode, aOffset) {
-    let rangeCount = aSelection.rangeCount;
-    let rangeidx = 0;
-    let foundContainingRange = false;
-    let range = null;
-
-    // Check to see if this node is inside one of the selection's ranges
-    while (!foundContainingRange && rangeidx < rangeCount) {
-      range = aSelection.getRangeAt(rangeidx);
-      if (range.isPointInRange(aNode, aOffset)) {
-        foundContainingRange = true;
-        break;
-      }
-      rangeidx++;
-    }
-
-    if (foundContainingRange)
-      return range;
-
-    return null;
-  },
-
-  // Start of nsIEditActionListener implementations
-
-  WillDeleteText: function (aTextNode, aOffset, aLength) {
-    let editor = this._getEditableNode(aTextNode).editor;
-    let controller = editor.selectionController;
-    let fSelection = controller.getSelection(Ci.nsISelectionController.SELECTION_FIND);
-    let range = this._findRange(fSelection, aTextNode, aOffset);
-
-    if (range) {
-      // Don't remove the highlighting if the deleted text is at the
-      // end of the range
-      if (aTextNode != range.endContainer ||
-          aOffset != range.endOffset) {
-        // Text within the highlight is being removed - the text can
-        // no longer be a match, so remove the highlighting
-        fSelection.removeRange(range);
-        if (fSelection.rangeCount == 0)
-          this._removeEditorListeners(editor);
-      }
-    }
-  },
-
-  DidInsertText: function (aTextNode, aOffset, aString) {
-    let editor = this._getEditableNode(aTextNode).editor;
-    let controller = editor.selectionController;
-    let fSelection = controller.getSelection(Ci.nsISelectionController.SELECTION_FIND);
-    let range = this._findRange(fSelection, aTextNode, aOffset);
-
-    if (range) {
-      // If the text was inserted before the highlight
-      // adjust the highlight's bounds accordingly
-      if (aTextNode == range.startContainer &&
-          aOffset == range.startOffset) {
-        range.setStart(range.startContainer,
-                       range.startOffset+aString.length);
-      } else if (aTextNode != range.endContainer ||
-                 aOffset != range.endOffset) {
-        // The edit occurred within the highlight - any addition of text
-        // will result in the text no longer being a match,
-        // so remove the highlighting
-        fSelection.removeRange(range);
-        if (fSelection.rangeCount == 0)
-          this._removeEditorListeners(editor);
-      }
-    }
-  },
-
-  WillDeleteSelection: function (aSelection) {
-    let editor = this._getEditableNode(aSelection.getRangeAt(0)
-                                                 .startContainer).editor;
-    let controller = editor.selectionController;
-    let fSelection = controller.getSelection(Ci.nsISelectionController.SELECTION_FIND);
-
-    let selectionIndex = 0;
-    let findSelectionIndex = 0;
-    let shouldDelete = {};
-    let numberOfDeletedSelections = 0;
-    let numberOfMatches = fSelection.rangeCount;
-
-    // We need to test if any ranges in the deleted selection (aSelection)
-    // are in any of the ranges of the find selection
-    // Usually both selections will only contain one range, however
-    // either may contain more than one.
-
-    for (let fIndex = 0; fIndex < numberOfMatches; fIndex++) {
-      shouldDelete[fIndex] = false;
-      let fRange = fSelection.getRangeAt(fIndex);
-
-      for (let index = 0; index < aSelection.rangeCount; index++) {
-        if (shouldDelete[fIndex])
-          continue;
-
-        let selRange = aSelection.getRangeAt(index);
-        let doesOverlap = this._checkOverlap(selRange, fRange);
-        if (doesOverlap) {
-          shouldDelete[fIndex] = true;
-          numberOfDeletedSelections++;
-        }
-      }
-    }
-
-    // OK, so now we know what matches (if any) are in the selection
-    // that is being deleted. Time to remove them.
-    if (numberOfDeletedSelections == 0)
-      return;
-
-    for (let i = numberOfMatches - 1; i >= 0; i--) {
-      if (shouldDelete[i])
-        fSelection.removeRange(fSelection.getRangeAt(i));
-    }
-
-    // Remove listeners if no more highlights left
-    if (fSelection.rangeCount == 0)
-      this._removeEditorListeners(editor);
-  },
-
-  /*
-   * nsIDocumentStateListener logic follows
-   *
-   * When attaching nsIEditActionListeners, there are no guarantees
-   * as to whether the findbar or the documents in the browser will get
-   * destructed first. This leads to the potential to either leak, or to
-   * hold on to a reference an editable element's editor for too long,
-   * preventing it from being destructed.
-   *
-   * However, when an editor's owning node is being destroyed, the editor
-   * sends out a DocumentWillBeDestroyed notification. We can use this to
-   * clean up our references to the object, to allow it to be destroyed in a
-   * timely fashion.
-   */
-
-  /*
-   * Unhook ourselves when one of our state listeners has been called.
-   * This can happen in 4 cases:
-   *  1) The document the editor belongs to is navigated away from, and
-   *     the document is not being cached
-   *
-   *  2) The document the editor belongs to is expired from the cache
-   *
-   *  3) The tab containing the owning document is closed
-   *
-   *  4) The <input> or <textarea> that owns the editor is explicitly
-   *     removed from the DOM
-   *
-   * @param the listener that was invoked
-   */
-  _onEditorDestruction: function (aListener) {
-    // First find the index of the editor the given listener listens to.
-    // The listeners and editors arrays must always be in sync.
-    // The listener will be in our array of cached listeners, as this
-    // method could not have been called otherwise.
-    let idx = 0;
-    while (this._stateListeners[idx] != aListener)
-      idx++;
-
-    // Unhook both listeners
-    this._unhookListenersAtIndex(idx);
-  },
-
-  /*
-   * Creates a unique document state listener for an editor.
-   *
-   * It is not possible to simply have the findbar implement the
-   * listener interface itself, as it wouldn't have sufficient information
-   * to work out which editor was being destroyed. Therefore, we create new
-   * listeners on the fly, and cache them in sync with the editors they
-   * listen to.
-   */
-  _createStateListener: function () {
-    return {
-      findbar: this,
-
-      QueryInterface: function(aIID) {
-        if (aIID.equals(Ci.nsIDocumentStateListener) ||
-            aIID.equals(Ci.nsISupports))
-          return this;
-
-        throw Components.results.NS_ERROR_NO_INTERFACE;
-      },
-
-      NotifyDocumentWillBeDestroyed: function() {
-        this.findbar._onEditorDestruction(this);
-      },
-
-      // Unimplemented
-      notifyDocumentCreated: function() {},
-      notifyDocumentStateChanged: function(aDirty) {}
-    };
-  }
-};
deleted file mode 100644
--- a/toolkit/modules/RemoteFinder.jsm
+++ /dev/null
@@ -1,145 +0,0 @@
-// -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-this.EXPORTED_SYMBOLS = ["RemoteFinder", "RemoteFinderListener"];
-
-const Ci = Components.interfaces;
-const Cc = Components.classes;
-const Cu = Components.utils;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-function RemoteFinder(browser) {
-  this._browser = browser;
-  this._listeners = [];
-  this._searchString = null;
-
-  this._browser.messageManager.addMessageListener("Finder:Result", this);
-  this._browser.messageManager.sendAsyncMessage("Finder:Initialize");
-}
-
-RemoteFinder.prototype = {
-  addResultListener: function (aListener) {
-    if (this._listeners.indexOf(aListener) === -1)
-      this._listeners.push(aListener);
-  },
-
-  removeResultListener: function (aListener) {
-    this._listeners = this._listeners.filter(l => l != aListener);
-  },
-
-  receiveMessage: function (aMessage) {
-    this._searchString = aMessage.data.searchString;
-
-    for (let l of this._listeners) {
-      l.onFindResult(aMessage.data.result, aMessage.data.findBackwards,
-                     aMessage.data.linkURL);
-    }
-  },
-
-  get searchString() {
-    return this._searchString;
-  },
-
-  set caseSensitive(aSensitive) {
-    this._browser.messageManager.sendAsyncMessage("Finder:CaseSensitive",
-                                                  { caseSensitive: aSensitive });
-  },
-
-  fastFind: function (aSearchString, aLinksOnly) {
-    this._browser.messageManager.sendAsyncMessage("Finder:FastFind",
-                                                  { searchString: aSearchString,
-                                                    linksOnly: aLinksOnly });
-  },
-
-  findAgain: function (aFindBackwards, aLinksOnly) {
-    this._browser.messageManager.sendAsyncMessage("Finder:FindAgain",
-                                                  { findBackwards: aFindBackwards,
-                                                    linksOnly: aLinksOnly });
-  },
-
-  highlight: function (aHighlight, aWord) {
-    this._browser.messageManager.sendAsyncMessage("Finder:Highlight",
-                                                  { highlight: aHighlight,
-                                                    word: aWord });
-  },
-
-  removeSelection: function () {
-    this._browser.messageManager.sendAsyncMessage("Finder:RemoveSelection");
-  },
-
-  focusContent: function () {
-    this._browser.messageManager.sendAsyncMessage("Finder:FocusContent");
-  },
-
-  keyPress: function (aEvent) {
-    this._browser.messageManager.sendAsyncMessage("Finder:KeyPress",
-                                                  { keyCode: aEvent.keyCode,
-                                                    shiftKey: aEvent.shiftKey });
-  }
-}
-
-function RemoteFinderListener(global) {
-  let {Finder} = Cu.import("resource://gre/modules/Finder.jsm", {});
-  this._finder = new Finder(global.docShell);
-  this._finder.addResultListener(this);
-  this._global = global;
-
-  for (let msg of this.MESSAGES) {
-    global.addMessageListener(msg, this);
-  }
-}
-
-RemoteFinderListener.prototype = {
-  MESSAGES: [
-    "Finder:CaseSensitive",
-    "Finder:FastFind",
-    "Finder:FindAgain",
-    "Finder:Highlight",
-    "Finder:RemoveSelection",
-    "Finder:FocusContent",
-    "Finder:KeyPress"
-  ],
-
-  onFindResult: function (aResult, aFindBackwards, aLinkURL) {
-    let data = { result: aResult, findBackwards: aFindBackwards,
-                 linkURL: aLinkURL, searchString: this._finder.searchString };
-    this._global.sendAsyncMessage("Finder:Result", data);
-  },
-
-  receiveMessage: function (aMessage) {
-    let data = aMessage.data;
-
-    switch (aMessage.name) {
-      case "Finder:CaseSensitive":
-        this._finder.caseSensitive = data.caseSensitive;
-        break;
-
-      case "Finder:FastFind":
-        this._finder.fastFind(data.searchString, data.linksOnly);
-        break;
-
-      case "Finder:FindAgain":
-        this._finder.findAgain(data.findBackwards, data.linksOnly);
-        break;
-
-      case "Finder:Highlight":
-        this._finder.highlight(data.highlight, data.word);
-        break;
-
-      case "Finder:RemoveSelection":
-        this._finder.removeSelection();
-        break;
-
-      case "Finder:FocusContent":
-        this._finder.focusContent();
-        break;
-
-      case "Finder:KeyPress":
-        this._finder.keyPress(data);
-        break;
-    }
-  }
-};
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -7,31 +7,29 @@
 TEST_DIRS += ['tests']
 
 EXTRA_JS_MODULES += [
     'AsyncShutdown.jsm',
     'DeferredTask.jsm',
     'Deprecated.jsm',
     'Dict.jsm',
     'FileUtils.jsm',
-    'Finder.jsm',
     'Geometry.jsm',
     'Http.jsm',
     'InlineSpellChecker.jsm',
     'NewTabUtils.jsm',
     'PageMenu.jsm',
     'PopupNotifications.jsm',
     'Preferences.jsm',
     'PrivateBrowsingUtils.jsm',
     'Promise.jsm',
     'PropertyListUtils.jsm',
     'RemoteAddonsChild.jsm',
     'RemoteAddonsParent.jsm',
     'RemoteController.jsm',
-    'RemoteFinder.jsm',
     'RemoteSecurityUI.jsm',
     'RemoteWebNavigation.jsm',
     'RemoteWebProgress.jsm',
     'SelectContentHelper.jsm',
     'SelectParentHelper.jsm',
     'Sntp.jsm',
     'Sqlite.jsm',
     'Task.jsm',
--- a/toolkit/modules/tests/browser/Makefile.in
+++ b/toolkit/modules/tests/browser/Makefile.in
@@ -1,13 +1,13 @@
 #
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MOCHITEST_BROWSER_FILES = \
   browser_DeferredTask.js \
   browser_Deprecated.js \
-  browser_Finder.js \
   browser_Geometry.js \
   browser_InlineSpellChecker.js \
   browser_Troubleshoot.js \
+  
   $(NULL)
deleted file mode 100644
--- a/toolkit/modules/tests/browser/browser_Finder.js
+++ /dev/null
@@ -1,46 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-const Ci = Components.interfaces;
-
-let tab, browser;
-
-function test () {
-  waitForExplicitFinish();
-
-  tab = gBrowser.addTab("data:text/html,<iframe srcdoc='content'/>");
-  browser = gBrowser.getBrowserForTab(tab);
-  gBrowser.selectedTab = tab;
-
-  browser.addEventListener("load", startTests, true);
-}
-
-function startTests () {
-  browser.removeEventListener("load", startTests, true);
-
-  let finder = browser.finder;
-  let listener = {
-    onFindResult: function () {
-      ok(false, "callback wasn't replaced");
-    }
-  };
-  finder.addResultListener(listener);
-
-  listener.onFindResult = function (result) {
-    ok(result == Ci.nsITypeAheadFind.FIND_FOUND, "should find string");
-
-    listener.onFindResult = function (result) {
-      ok(result == Ci.nsITypeAheadFind.FIND_NOTFOUND, "should not find string");
-
-      cleanup();
-    }
-    finder.highlight(true, "Bla");
-  }
-  finder.highlight(true, "content");
-}
-
-function cleanup() {
-  gBrowser.removeTab(tab);
-  finish();
-}