summaryrefslogtreecommitdiffstats
path: root/toolkit/content/widgets/findbar.xml
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/content/widgets/findbar.xml')
-rw-r--r--toolkit/content/widgets/findbar.xml1397
1 files changed, 1397 insertions, 0 deletions
diff --git a/toolkit/content/widgets/findbar.xml b/toolkit/content/widgets/findbar.xml
new file mode 100644
index 000000000..f90d41227
--- /dev/null
+++ b/toolkit/content/widgets/findbar.xml
@@ -0,0 +1,1397 @@
+<?xml version="1.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/. -->
+
+<!DOCTYPE bindings [
+<!ENTITY % findBarDTD SYSTEM "chrome://global/locale/findbar.dtd" >
+%findBarDTD;
+]>
+
+<bindings id="findbarBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:xbl="http://www.mozilla.org/xbl">
+
+ <!-- Private binding -->
+ <binding id="findbar-textbox"
+ extends="chrome://global/content/bindings/textbox.xml#textbox">
+ <implementation>
+
+ <field name="_findbar">null</field>
+ <property name="findbar" readonly="true">
+ <getter>
+ return this._findbar ?
+ 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;
+ if (!findString.value)
+ return;
+ if (aEvent.getModifierState("Accel")) {
+ this.findbar.getElement("highlight").click();
+ return;
+ }
+
+ this.findbar.onFindAgainCommand(aEvent.shiftKey);
+ } else {
+ this.findbar._finishFAYT(aEvent);
+ }
+ ]]></body>
+ </method>
+
+ <method name="_handleTab">
+ <parameter name="aEvent"/>
+ <body><![CDATA[
+ let shouldHandle = !aEvent.altKey && !aEvent.ctrlKey &&
+ !aEvent.metaKey;
+ if (shouldHandle &&
+ this.findbar._findMode != this.findbar.FIND_NORMAL) {
+
+ this.findbar._finishFAYT(aEvent);
+ }
+ ]]></body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="input"><![CDATA[
+ // We should do nothing during composition. E.g., composing string
+ // before converting may matches a forward word of expected word.
+ // After that, even if user converts the composition string to the
+ // expected word, it may find second or later searching word in the
+ // document.
+ if (this.findbar._isIMEComposing) {
+ return;
+ }
+
+ if (this._hadValue && !this.value) {
+ this._willfullyDeleted = true;
+ this._hadValue = false;
+ } else if (this.value.trim()) {
+ this._hadValue = true;
+ this._willfullyDeleted = false;
+ }
+ this.findbar._find(this.value);
+ ]]></handler>
+
+ <handler event="keypress"><![CDATA[
+ let 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:
+ case KeyEvent.DOM_VK_PAGE_DOWN:
+ if (shouldHandle) {
+ this.findbar.browser.finder.keyPress(event);
+ event.preventDefault();
+ }
+ break;
+ case KeyEvent.DOM_VK_UP:
+ case KeyEvent.DOM_VK_DOWN:
+ this.findbar.browser.finder.keyPress(event);
+ event.preventDefault();
+ break;
+ }
+ ]]></handler>
+
+ <handler event="blur"><![CDATA[
+ let findbar = this.findbar;
+ // Note: This code used to remove the selection
+ // if it matched an editable.
+ findbar.browser.finder.enableSelection();
+ ]]></handler>
+
+ <handler event="focus"><![CDATA[
+ if (/Mac/.test(navigator.platform)) {
+ let findbar = this.findbar;
+ findbar._onFindFieldFocus();
+ }
+ ]]></handler>
+
+ <handler event="compositionstart"><![CDATA[
+ // Don't close the find toolbar while IME is composing.
+ let findbar = this.findbar;
+ findbar._isIMEComposing = true;
+ if (findbar._quickFindTimeout) {
+ clearTimeout(findbar._quickFindTimeout);
+ findbar._quickFindTimeout = null;
+ }
+ ]]></handler>
+
+ <handler event="compositionend"><![CDATA[
+ let findbar = this.findbar;
+ findbar._isIMEComposing = false;
+ if (findbar._findMode != findbar.FIND_NORMAL)
+ findbar._setFindCloseTimeout();
+ ]]></handler>
+
+ <handler event="dragover"><![CDATA[
+ if (event.dataTransfer.types.includes("text/plain"))
+ event.preventDefault();
+ ]]></handler>
+
+ <handler event="drop"><![CDATA[
+ let value = event.dataTransfer.getData("text/plain");
+ this.value = value;
+ this.findbar._find(value);
+ event.stopPropagation();
+ event.preventDefault();
+ ]]></handler>
+ </handlers>
+ </binding>
+
+ <binding id="findbar"
+ extends="chrome://global/content/bindings/toolbar.xml#toolbar">
+ <resources>
+ <stylesheet src="chrome://global/skin/findBar.css"/>
+ </resources>
+
+ <content hidden="true">
+ <xul:hbox anonid="findbar-container" class="findbar-container" flex="1" align="center">
+ <xul:hbox anonid="findbar-textbox-wrapper" align="stretch">
+ <xul:textbox anonid="findbar-textbox"
+ class="findbar-textbox findbar-find-fast"
+ xbl:inherits="flash"/>
+ <xul:toolbarbutton anonid="find-previous"
+ class="findbar-find-previous tabbable"
+ tooltiptext="&previous.tooltip;"
+ oncommand="onFindAgainCommand(true);"
+ disabled="true"
+ xbl:inherits="accesskey=findpreviousaccesskey"/>
+ <xul:toolbarbutton anonid="find-next"
+ class="findbar-find-next tabbable"
+ tooltiptext="&next.tooltip;"
+ oncommand="onFindAgainCommand(false);"
+ disabled="true"
+ xbl:inherits="accesskey=findnextaccesskey"/>
+ </xul:hbox>
+ <xul:toolbarbutton anonid="highlight"
+ class="findbar-highlight findbar-button tabbable"
+ label="&highlightAll.label;"
+ accesskey="&highlightAll.accesskey;"
+ tooltiptext="&highlightAll.tooltiptext;"
+ oncommand="toggleHighlight(this.checked);"
+ type="checkbox"
+ xbl:inherits="accesskey=highlightaccesskey"/>
+ <xul:toolbarbutton anonid="find-case-sensitive"
+ class="findbar-case-sensitive findbar-button tabbable"
+ label="&caseSensitive.label;"
+ accesskey="&caseSensitive.accesskey;"
+ tooltiptext="&caseSensitive.tooltiptext;"
+ oncommand="_setCaseSensitivity(this.checked ? 1 : 0);"
+ type="checkbox"
+ xbl:inherits="accesskey=matchcaseaccesskey"/>
+ <xul:toolbarbutton anonid="find-entire-word"
+ class="findbar-entire-word findbar-button tabbable"
+ label="&entireWord.label;"
+ accesskey="&entireWord.accesskey;"
+ tooltiptext="&entireWord.tooltiptext;"
+ oncommand="toggleEntireWord(this.checked);"
+ type="checkbox"
+ xbl:inherits="accesskey=entirewordaccesskey"/>
+ <xul:label anonid="match-case-status" class="findbar-find-fast"/>
+ <xul:label anonid="entire-word-status" class="findbar-find-fast"/>
+ <xul:label anonid="found-matches" class="findbar-find-fast found-matches" hidden="true"/>
+ <xul:image anonid="find-status-icon" class="findbar-find-fast find-status-icon"/>
+ <xul:description anonid="find-status"
+ control="findbar-textbox"
+ class="findbar-find-fast findbar-find-status">
+ <!-- Do not use value, first child is used because it provides a11y with text change events -->
+ </xul:description>
+ </xul:hbox>
+ <xul:toolbarbutton anonid="find-closebutton"
+ class="findbar-closebutton close-icon"
+ tooltiptext="&findCloseButton.tooltip;"
+ oncommand="close();"/>
+ </content>
+
+ <implementation implements="nsIMessageListener, nsIEditActionListener">
+ <!-- Please keep in sync with toolkit/content/browser-content.js -->
+ <field name="FIND_NORMAL">0</field>
+ <field name="FIND_TYPEAHEAD">1</field>
+ <field name="FIND_LINKS">2</field>
+
+ <field name="__findMode">0</field>
+ <property name="_findMode" onget="return this.__findMode;"
+ onset="this.__findMode = val; this._updateBrowserWithState(); return val;"/>
+
+ <field name="_flashFindBar">0</field>
+ <field name="_initialFlashFindBarCount">6</field>
+
+ <!--
+ - For tests that need to know when the find bar is finished
+ - initializing, we store a promise to notify on.
+ -->
+ <field name="_startFindDeferred">null</field>
+
+ <property name="prefillWithSelection"
+ onget="return this.getAttribute('prefillwithselection') != 'false'"
+ onset="this.setAttribute('prefillwithselection', val); return val;"/>
+
+ <method name="getElement">
+ <parameter name="aAnonymousID"/>
+ <body><![CDATA[
+ return document.getAnonymousElementByAttribute(this,
+ "anonid",
+ aAnonymousID)
+ ]]></body>
+ </method>
+
+ <property name="findMode"
+ readonly="true"
+ onget="return this._findMode;"/>
+
+ <property name="hasTransactions" readonly="true">
+ <getter><![CDATA[
+ if (this._findField.value)
+ return true;
+
+ // Watch out for lazy editor init
+ if (this._findField.editor) {
+ let tm = this._findField.editor.transactionManager;
+ return !!(tm.numberOfUndoItems || tm.numberOfRedoItems);
+ }
+ return false;
+ ]]></getter>
+ </property>
+
+ <field name="_browser">null</field>
+ <property name="browser">
+ <getter><![CDATA[
+ if (!this._browser) {
+ this._browser =
+ document.getElementById(this.getAttribute("browserid"));
+ }
+ return this._browser;
+ ]]></getter>
+ <setter><![CDATA[
+ if (this._browser) {
+ if (this._browser.messageManager) {
+ this._browser.messageManager.removeMessageListener("Findbar:Keypress", this);
+ this._browser.messageManager.removeMessageListener("Findbar:Mouseup", this);
+ }
+ let finder = this._browser.finder;
+ if (finder)
+ finder.removeResultListener(this);
+ }
+
+ this._browser = val;
+ if (this._browser) {
+ // Need to do this to ensure the correct initial state.
+ this._updateBrowserWithState();
+ this._browser.messageManager.addMessageListener("Findbar:Keypress", this);
+ this._browser.messageManager.addMessageListener("Findbar:Mouseup", this);
+ this._browser.finder.addResultListener(this);
+
+ this._findField.value = this._browser._lastSearchString;
+ }
+ return val;
+ ]]></setter>
+ </property>
+
+ <field name="__prefsvc">null</field>
+ <property name="_prefsvc">
+ <getter><![CDATA[
+ if (!this.__prefsvc) {
+ this.__prefsvc = Components.classes["@mozilla.org/preferences-service;1"]
+ .getService(Components.interfaces.nsIPrefBranch);
+ }
+ return this.__prefsvc;
+ ]]></getter>
+ </property>
+
+ <field name="_observer"><![CDATA[({
+ _self: this,
+
+ QueryInterface: function(aIID) {
+ if (aIID.equals(Components.interfaces.nsIObserver) ||
+ aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
+ aIID.equals(Components.interfaces.nsISupports))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ },
+
+ observe: function(aSubject, aTopic, aPrefName) {
+ if (aTopic != "nsPref:changed")
+ return;
+
+ let prefsvc = this._self._prefsvc;
+
+ switch (aPrefName) {
+ case "accessibility.typeaheadfind":
+ this._self._findAsYouType = prefsvc.getBoolPref(aPrefName);
+ break;
+ case "accessibility.typeaheadfind.linksonly":
+ this._self._typeAheadLinksOnly = prefsvc.getBoolPref(aPrefName);
+ break;
+ case "accessibility.typeaheadfind.casesensitive":
+ this._self._setCaseSensitivity(prefsvc.getIntPref(aPrefName));
+ break;
+ case "findbar.entireword":
+ this._self._entireWord = prefsvc.getBoolPref(aPrefName);
+ this._self.toggleEntireWord(this._self._entireWord, true);
+ break;
+ case "findbar.highlightAll":
+ this._self.toggleHighlight(prefsvc.getBoolPref(aPrefName), true);
+ break;
+ case "findbar.modalHighlight":
+ this._self._useModalHighlight = prefsvc.getBoolPref(aPrefName);
+ if (this._self.browser.finder)
+ this._self.browser.finder.onModalHighlightChange(this._self._useModalHighlight);
+ break;
+ }
+ }
+ })]]></field>
+
+ <field name="_destroyed">false</field>
+
+ <constructor><![CDATA[
+ // These elements are accessed frequently and are therefore cached
+ this._findField = this.getElement("findbar-textbox");
+ this._foundMatches = this.getElement("found-matches");
+ this._findStatusIcon = this.getElement("find-status-icon");
+ this._findStatusDesc = this.getElement("find-status");
+
+ this._foundURL = null;
+
+ let prefsvc = this._prefsvc;
+
+ this._quickFindTimeoutLength =
+ prefsvc.getIntPref("accessibility.typeaheadfind.timeout");
+ this._flashFindBar =
+ prefsvc.getIntPref("accessibility.typeaheadfind.flashBar");
+ this._useModalHighlight = prefsvc.getBoolPref("findbar.modalHighlight");
+
+ prefsvc.addObserver("accessibility.typeaheadfind",
+ this._observer, false);
+ prefsvc.addObserver("accessibility.typeaheadfind.linksonly",
+ this._observer, false);
+ prefsvc.addObserver("accessibility.typeaheadfind.casesensitive",
+ this._observer, false);
+ prefsvc.addObserver("findbar.entireword", this._observer, false);
+ prefsvc.addObserver("findbar.highlightAll", this._observer, false);
+ prefsvc.addObserver("findbar.modalHighlight", this._observer, false);
+
+ this._findAsYouType =
+ prefsvc.getBoolPref("accessibility.typeaheadfind");
+ this._typeAheadLinksOnly =
+ prefsvc.getBoolPref("accessibility.typeaheadfind.linksonly");
+ this._typeAheadCaseSensitive =
+ prefsvc.getIntPref("accessibility.typeaheadfind.casesensitive");
+ this._entireWord = prefsvc.getBoolPref("findbar.entireword");
+ this._highlightAll = prefsvc.getBoolPref("findbar.highlightAll");
+
+ // Convenience
+ this.nsITypeAheadFind = Components.interfaces.nsITypeAheadFind;
+ this.nsISelectionController = Components.interfaces.nsISelectionController;
+ this._findSelection = this.nsISelectionController.SELECTION_FIND;
+
+ this._findResetTimeout = -1;
+
+ // Make sure the FAYT keypress listener is attached by initializing the
+ // browser property
+ if (this.getAttribute("browserid"))
+ setTimeout(function(aSelf) { aSelf.browser = aSelf.browser; }, 0, this);
+ ]]></constructor>
+
+ <destructor><![CDATA[
+ this.destroy();
+ ]]></destructor>
+
+ <!-- This is necessary because the destructor isn't called when
+ we are removed from a document that is not destroyed. This
+ needs to be explicitly called in this case -->
+ <method name="destroy">
+ <body><![CDATA[
+ if (this._destroyed)
+ return;
+ this._destroyed = true;
+
+ if (this.browser.finder)
+ this.browser.finder.destroy();
+
+ this.browser = null;
+
+ let prefsvc = this._prefsvc;
+ prefsvc.removeObserver("accessibility.typeaheadfind",
+ this._observer);
+ prefsvc.removeObserver("accessibility.typeaheadfind.linksonly",
+ this._observer);
+ prefsvc.removeObserver("accessibility.typeaheadfind.casesensitive",
+ this._observer);
+ prefsvc.removeObserver("findbar.entireword", this._observer);
+ prefsvc.removeObserver("findbar.highlightAll", this._observer);
+ prefsvc.removeObserver("findbar.modalHighlight", this._observer);
+
+ // Clear all timers that might still be running.
+ this._cancelTimers();
+ ]]></body>
+ </method>
+
+ <method name="_cancelTimers">
+ <body><![CDATA[
+ if (this._flashFindBarTimeout) {
+ clearInterval(this._flashFindBarTimeout);
+ this._flashFindBarTimeout = null;
+ }
+ if (this._quickFindTimeout) {
+ clearTimeout(this._quickFindTimeout);
+ this._quickFindTimeout = null;
+ }
+ if (this._findResetTimeout) {
+ clearTimeout(this._findResetTimeout);
+ this._findResetTimeout = null;
+ }
+ ]]></body>
+ </method>
+
+ <method name="_setFindCloseTimeout">
+ <body><![CDATA[
+ if (this._quickFindTimeout)
+ clearTimeout(this._quickFindTimeout);
+
+ // Don't close the find toolbar while IME is composing OR when the
+ // findbar is already hidden.
+ if (this._isIMEComposing || this.hidden) {
+ this._quickFindTimeout = null;
+ return;
+ }
+
+ this._quickFindTimeout = setTimeout(() => {
+ if (this._findMode != this.FIND_NORMAL)
+ this.close();
+ this._quickFindTimeout = null;
+ }, this._quickFindTimeoutLength);
+ ]]></body>
+ </method>
+
+ <field name="_pluralForm">null</field>
+ <property name="pluralForm">
+ <getter><![CDATA[
+ if (!this._pluralForm) {
+ this._pluralForm = Components.utils.import(
+ "resource://gre/modules/PluralForm.jsm", {}).PluralForm;
+ }
+ return this._pluralForm;
+ ]]></getter>
+ </property>
+
+ <!--
+ - Updates the search match count after each find operation on a new string.
+ - @param aRes
+ - the result of the find operation
+ -->
+ <method name="_updateMatchesCount">
+ <body><![CDATA[
+ if (!this._dispatchFindEvent("matchescount"))
+ return;
+
+ this.browser.finder.requestMatchesCount(this._findField.value,
+ this._findMode == this.FIND_LINKS);
+ ]]></body>
+ </method>
+
+ <!--
+ - Turns highlight on or off.
+ - @param aHighlight (boolean)
+ - Whether to turn the highlight on or off
+ - @param aFromPrefObserver (boolean)
+ - Whether the callee is the pref observer, which means we should
+ - not set the same pref again.
+ -->
+ <method name="toggleHighlight">
+ <parameter name="aHighlight"/>
+ <parameter name="aFromPrefObserver"/>
+ <body><![CDATA[
+ if (aHighlight === this._highlightAll) {
+ return;
+ }
+
+ this.browser.finder.onHighlightAllChange(aHighlight);
+
+ this._setHighlightAll(aHighlight, aFromPrefObserver);
+
+ if (!this._dispatchFindEvent("highlightallchange")) {
+ return;
+ }
+
+ let word = this._findField.value;
+ // Bug 429723. Don't attempt to highlight ""
+ if (aHighlight && !word)
+ return;
+
+ this.browser.finder.highlight(aHighlight, word,
+ this._findMode == this.FIND_LINKS);
+
+ // Update the matches count
+ this._updateMatchesCount(this.nsITypeAheadFind.FIND_FOUND);
+ ]]></body>
+ </method>
+
+ <!--
+ - Updates the highlight-all mode of the findbar and its UI.
+ - @param aHighlight (boolean)
+ - Whether to turn the highlight on or off.
+ - @param aFromPrefObserver (boolean)
+ - Whether the callee is the pref observer, which means we should
+ - not set the same pref again.
+ -->
+ <method name="_setHighlightAll">
+ <parameter name="aHighlight"/>
+ <parameter name="aFromPrefObserver"/>
+ <body><![CDATA[
+ if (typeof aHighlight != "boolean") {
+ aHighlight = this._highlightAll;
+ }
+ if (aHighlight !== this._highlightAll && !aFromPrefObserver) {
+ this._prefsvc.setBoolPref("findbar.highlightAll", aHighlight);
+ }
+ this._highlightAll = aHighlight;
+ let checkbox = this.getElement("highlight");
+ checkbox.checked = this._highlightAll;
+ ]]></body>
+ </method>
+
+ <method name="_maybeHighlightAll">
+ <body><![CDATA[
+ let word = this._findField.value;
+ // Bug 429723. Don't attempt to highlight ""
+ if (!this._highlightAll || !word)
+ return;
+
+ this.browser.finder.highlight(true, word,
+ this._findMode == this.FIND_LINKS);
+ ]]></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;
+
+ let caseSensitive = this._shouldBeCaseSensitive(val);
+ let checkbox = this.getElement("find-case-sensitive");
+ let 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 ||
+ (this._typeAheadCaseSensitive != 0 &&
+ this._typeAheadCaseSensitive != 1);
+ checkbox.hidden = hideCheckbox;
+ statusLabel.hidden = !hideCheckbox;
+
+ this.browser.finder.caseSensitive = caseSensitive;
+ ]]></body>
+ </method>
+
+ <!--
+ - Sets the findbar case-sensitivity mode
+ - @param aCaseSensitivity (int)
+ - 0 - case insensitive
+ - 1 - case sensitive
+ - 2 - auto = case sensitive iff match string contains upper case letters
+ - @see _shouldBeCaseSensitive
+ -->
+ <method name="_setCaseSensitivity">
+ <parameter name="aCaseSensitivity"/>
+ <body><![CDATA[
+ this._typeAheadCaseSensitive = aCaseSensitivity;
+ this._updateCaseSensitivity();
+ this._findFailedString = null;
+ this._find();
+
+ this._dispatchFindEvent("casesensitivitychange");
+ ]]></body>
+ </method>
+
+ <!--
+ - Updates the entire-word mode of the findbar and its UI.
+ -->
+ <method name="_setEntireWord">
+ <body><![CDATA[
+ let entireWord = this._entireWord;
+ let checkbox = this.getElement("find-entire-word");
+ let statusLabel = this.getElement("entire-word-status");
+ checkbox.checked = entireWord;
+
+ statusLabel.value = entireWord ? this._entireWordStr : "";
+
+ // 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;
+ checkbox.hidden = hideCheckbox;
+ statusLabel.hidden = !hideCheckbox;
+
+ this.browser.finder.entireWord = entireWord;
+ ]]></body>
+ </method>
+
+ <!--
+ - Sets the findbar entire-word mode
+ - @param aEntireWord (boolean)
+ - Whether or not entire-word mode should be turned on.
+ -->
+ <method name="toggleEntireWord">
+ <parameter name="aEntireWord"/>
+ <parameter name="aFromPrefObserver"/>
+ <body><![CDATA[
+ if (!aFromPrefObserver) {
+ // Just set the pref; our observer will change the find bar behavior.
+ this._prefsvc.setBoolPref("findbar.entireword", aEntireWord);
+ return;
+ }
+
+ this._findFailedString = null;
+ this._find();
+ ]]></body>
+ </method>
+
+ <field name="_strBundle">null</field>
+ <property name="strBundle">
+ <getter><![CDATA[
+ if (!this._strBundle) {
+ this._strBundle =
+ Components.classes["@mozilla.org/intl/stringbundle;1"]
+ .getService(Components.interfaces.nsIStringBundleService)
+ .createBundle("chrome://global/locale/findbar.properties");
+ }
+ return this._strBundle;
+ ]]></getter>
+ </property>
+
+ <!--
+ - Opens and displays the find bar.
+ -
+ - @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.
+ - @returns true if the find bar wasn't previously open, false otherwise.
+ -->
+ <method name="open">
+ <parameter name="aMode"/>
+ <body><![CDATA[
+ if (aMode != undefined)
+ this._findMode = aMode;
+
+ if (!this._notFoundStr) {
+ var stringsBundle = this.strBundle;
+ this._notFoundStr = stringsBundle.GetStringFromName("NotFound");
+ this._wrappedToTopStr =
+ stringsBundle.GetStringFromName("WrappedToTop");
+ this._wrappedToBottomStr =
+ stringsBundle.GetStringFromName("WrappedToBottom");
+ this._normalFindStr =
+ stringsBundle.GetStringFromName("NormalFind");
+ this._fastFindStr =
+ stringsBundle.GetStringFromName("FastFind");
+ this._fastFindLinksStr =
+ stringsBundle.GetStringFromName("FastFindLinks");
+ this._caseSensitiveStr =
+ stringsBundle.GetStringFromName("CaseSensitive");
+ this._entireWordStr =
+ stringsBundle.GetStringFromName("EntireWord");
+ }
+
+ this._findFailedString = null;
+
+ this._updateFindUI();
+ if (this.hidden) {
+ this.removeAttribute("noanim");
+ this.hidden = false;
+
+ this._updateStatusUI(this.nsITypeAheadFind.FIND_FOUND);
+
+ let event = document.createEvent("Events");
+ event.initEvent("findbaropen", true, false);
+ this.dispatchEvent(event);
+
+ this.browser.finder.onFindbarOpen();
+
+ return true;
+ }
+ return false;
+ ]]></body>
+ </method>
+
+ <!--
+ - Closes the findbar.
+ -->
+ <method name="close">
+ <parameter name="aNoAnim"/>
+ <body><![CDATA[
+ if (this.hidden)
+ return;
+
+ if (aNoAnim)
+ this.setAttribute("noanim", true);
+ this.hidden = true;
+
+ // 'focusContent()' iterates over all listeners in the chrome
+ // process, so we need to call it from here.
+ this.browser.finder.focusContent();
+ this.browser.finder.onFindbarClose();
+
+ this._cancelTimers();
+
+ this._findFailedString = null;
+ ]]></body>
+ </method>
+
+ <method name="clear">
+ <body><![CDATA[
+ this.browser.finder.removeSelection();
+ this._findField.reset();
+ this.toggleHighlight(false);
+ this._updateStatusUI();
+ this._enableFindButtons(false);
+ ]]></body>
+ </method>
+
+ <method name="_dispatchKeypressEvent">
+ <parameter name="aTarget"/>
+ <parameter name="aEvent"/>
+ <body><![CDATA[
+ if (!aTarget)
+ return;
+
+ let event = document.createEvent("KeyboardEvent");
+ 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
+ .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .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);
+ return true;
+ ]]></body>
+ </method>
+
+ <method name="_finishFAYT">
+ <parameter name="aKeypressEvent"/>
+ <body><![CDATA[
+ this.browser.finder.focusContent();
+
+ if (aKeypressEvent)
+ aKeypressEvent.preventDefault();
+
+ this.browser.finder.keyPress(aKeypressEvent);
+
+ this.close();
+ return true;
+ ]]></body>
+ </method>
+
+ <method name="_shouldBeCaseSensitive">
+ <parameter name="aString"/>
+ <body><![CDATA[
+ if (this._typeAheadCaseSensitive == 0)
+ return false;
+ if (this._typeAheadCaseSensitive == 1)
+ return true;
+
+ return aString != aString.toLowerCase();
+ ]]></body>
+ </method>
+
+ <!-- We get a fake event object through an IPC message which contains the
+ data we need to make a decision. We then return |true| if and only if
+ the page gets to deal with the event itself. Everywhere we return
+ false, the message sender will take care of calling event.preventDefault
+ on the real event. -->
+ <method name="_onBrowserKeypress">
+ <parameter name="aFakeEvent"/>
+ <parameter name="aShouldFastFind"/>
+ <body><![CDATA[
+ const FAYT_LINKS_KEY = "'";
+ const FAYT_TEXT_KEY = "/";
+
+ // Fast keypresses can stack up when the content process is slow or
+ // hangs when in e10s mode. We make sure the findbar isn't 'opened'
+ // several times in a row, because then the find query is selected
+ // each time, losing characters typed initially.
+ let inputField = this._findField.inputField;
+ if (!this.hidden && document.activeElement == inputField) {
+ this._dispatchKeypressEvent(inputField, aFakeEvent);
+ return false;
+ }
+
+ if (this._findMode != this.FIND_NORMAL && this._quickFindTimeout) {
+ if (!aFakeEvent.charCode)
+ return true;
+
+ this._findField.select();
+ this._findField.focus();
+ this._dispatchKeypressEvent(this._findField.inputField, aFakeEvent);
+ return false;
+ }
+
+ if (!aShouldFastFind)
+ return true;
+
+ let key = aFakeEvent.charCode ? String.fromCharCode(aFakeEvent.charCode) : null;
+ let manualstartFAYT = (key == FAYT_LINKS_KEY || key == FAYT_TEXT_KEY);
+ let autostartFAYT = !manualstartFAYT && this._findAsYouType &&
+ key && key != " ";
+ if (manualstartFAYT || autostartFAYT) {
+ let mode = (key == FAYT_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);
+ this._setFindCloseTimeout();
+ this._findField.select();
+ this._findField.focus();
+
+ if (autostartFAYT)
+ this._dispatchKeypressEvent(this._findField.inputField, aFakeEvent);
+ else
+ this._updateStatusUI(this.nsITypeAheadFind.FIND_FOUND);
+
+ return false;
+ }
+ return undefined;
+ ]]></body>
+ </method>
+
+ <!-- See nsIMessageListener -->
+ <method name="receiveMessage">
+ <parameter name="aMessage"/>
+ <body><![CDATA[
+ if (aMessage.target != this._browser) {
+ return undefined;
+ }
+ switch (aMessage.name) {
+ case "Findbar:Mouseup":
+ if (!this.hidden && this._findMode != this.FIND_NORMAL)
+ this.close();
+ break;
+
+ case "Findbar:Keypress":
+ return this._onBrowserKeypress(aMessage.data.fakeEvent,
+ aMessage.data.shouldFastFind);
+ }
+ return undefined;
+ ]]></body>
+ </method>
+
+ <method name="_updateBrowserWithState">
+ <body><![CDATA[
+ if (this._browser && this._browser.messageManager) {
+ this._browser.messageManager.sendAsyncMessage("Findbar:UpdateState", {
+ findMode: this._findMode
+ });
+ }
+ ]]></body>
+ </method>
+
+ <method name="_enableFindButtons">
+ <parameter name="aEnable"/>
+ <body><![CDATA[
+ this.getElement("find-next").disabled =
+ this.getElement("find-previous").disabled = !aEnable;
+ ]]></body>
+ </method>
+
+ <!--
+ - Determines whether minimalist or general-purpose search UI is to be
+ - displayed when the find bar is activated.
+ -->
+ <method name="_updateFindUI">
+ <body><![CDATA[
+ let showMinimalUI = this._findMode != this.FIND_NORMAL;
+
+ let nodes = this.getElement("findbar-container").childNodes;
+ let wrapper = this.getElement("findbar-textbox-wrapper");
+ let foundMatches = this._foundMatches;
+ for (let node of nodes) {
+ if (node == wrapper || node == foundMatches)
+ continue;
+ node.hidden = showMinimalUI;
+ }
+ this.getElement("find-next").hidden =
+ this.getElement("find-previous").hidden = showMinimalUI;
+ foundMatches.hidden = showMinimalUI || !foundMatches.value;
+ this._updateCaseSensitivity();
+ this._setEntireWord();
+ this._setHighlightAll();
+
+ if (showMinimalUI)
+ this._findField.classList.add("minimal");
+ else
+ this._findField.classList.remove("minimal");
+
+ if (this._findMode == this.FIND_TYPEAHEAD)
+ 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="_find">
+ <parameter name="aValue"/>
+ <body><![CDATA[
+ if (!this._dispatchFindEvent(""))
+ return;
+
+ let val = aValue || this._findField.value;
+
+ // We have to carry around an explicit version of this,
+ // because finder.searchString doesn't update on failed
+ // searches.
+ this.browser._lastSearchString = val;
+
+ // Only search on input if we don't have a last-failed string,
+ // or if the current search string doesn't start with it.
+ // In entire-word mode we always attemp a find; since sequential matching
+ // is not guaranteed, the first character typed may not be a word (no
+ // match), but the with the second character it may well be a word,
+ // thus a match.
+ if (!this._findFailedString ||
+ !val.startsWith(this._findFailedString) ||
+ this._entireWord) {
+ // Getting here means the user commanded a find op. Make sure any
+ // initial prefilling is ignored if it hasn't happened yet.
+ if (this._startFindDeferred) {
+ this._startFindDeferred.resolve();
+ this._startFindDeferred = null;
+ }
+
+ this._enableFindButtons(val);
+ this._updateCaseSensitivity(val);
+ this._setEntireWord();
+
+ this.browser.finder.fastFind(val, this._findMode == this.FIND_LINKS,
+ this._findMode != this.FIND_NORMAL);
+ }
+
+ 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);
+ ]]></body>
+ </method>
+
+ <method name="_flash">
+ <body><![CDATA[
+ if (this._flashFindBarCount === undefined)
+ this._flashFindBarCount = this._initialFlashFindBarCount;
+
+ if (this._flashFindBarCount-- == 0) {
+ clearInterval(this._flashFindBarTimeout);
+ this.removeAttribute("flash");
+ this._flashFindBarCount = 6;
+ return;
+ }
+
+ this.setAttribute("flash",
+ (this._flashFindBarCount % 2 == 0) ?
+ "false" : "true");
+ ]]></body>
+ </method>
+
+ <method name="_findAgain">
+ <parameter name="aFindPrevious"/>
+ <body><![CDATA[
+ this.browser.finder.findAgain(aFindPrevious,
+ this._findMode == this.FIND_LINKS,
+ this._findMode != this.FIND_NORMAL);
+ ]]></body>
+ </method>
+
+ <method name="_updateStatusUI">
+ <parameter name="res"/>
+ <parameter name="aFindPrevious"/>
+ <body><![CDATA[
+ switch (res) {
+ case this.nsITypeAheadFind.FIND_WRAPPED:
+ this._findStatusIcon.setAttribute("status", "wrapped");
+ this._findStatusDesc.textContent =
+ aFindPrevious ? this._wrappedToBottomStr : this._wrappedToTopStr;
+ this._findField.removeAttribute("status");
+ break;
+ case this.nsITypeAheadFind.FIND_NOTFOUND:
+ this._findStatusIcon.setAttribute("status", "notfound");
+ this._findStatusDesc.textContent = this._notFoundStr;
+ this._findField.setAttribute("status", "notfound");
+ break;
+ case this.nsITypeAheadFind.FIND_PENDING:
+ this._findStatusIcon.setAttribute("status", "pending");
+ this._findStatusDesc.textContent = "";
+ this._findField.removeAttribute("status");
+ break;
+ case this.nsITypeAheadFind.FIND_FOUND:
+ default:
+ this._findStatusIcon.removeAttribute("status");
+ this._findStatusDesc.textContent = "";
+ this._findField.removeAttribute("status");
+ break;
+ }
+ ]]></body>
+ </method>
+
+ <method name="updateControlState">
+ <parameter name="aResult"/>
+ <parameter name="aFindPrevious"/>
+ <body><![CDATA[
+ this._updateStatusUI(aResult, aFindPrevious);
+ this._enableFindButtons(aResult !== this.nsITypeAheadFind.FIND_NOTFOUND);
+ ]]></body>
+ </method>
+
+ <method name="_dispatchFindEvent">
+ <parameter name="aType"/>
+ <parameter name="aFindPrevious"/>
+ <body><![CDATA[
+ let event = document.createEvent("CustomEvent");
+ event.initCustomEvent("find" + aType, true, true, {
+ query: this._findField.value,
+ caseSensitive: !!this._typeAheadCaseSensitive,
+ entireWord: this._entireWord,
+ highlightAll: this._highlightAll,
+ findPrevious: aFindPrevious
+ });
+ return this.dispatchEvent(event);
+ ]]></body>
+ </method>
+
+
+ <!--
+ - Opens the findbar, focuses the findfield and selects its contents.
+ - Also flashes the findbar the first time it's used.
+ - @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 = this._prefsvc;
+ let userWantsPrefill = true;
+ this.open(aMode);
+
+ if (this._flashFindBar) {
+ this._flashFindBarTimeout = setInterval(() => this._flash(), 500);
+ prefsvc.setIntPref("accessibility.typeaheadfind.flashBar",
+ --this._flashFindBar);
+ }
+
+ let {PromiseUtils} =
+ Components.utils.import("resource://gre/modules/PromiseUtils.jsm", {});
+ this._startFindDeferred = PromiseUtils.defer();
+ let startFindPromise = this._startFindDeferred.promise;
+
+ if (this.prefillWithSelection)
+ userWantsPrefill =
+ prefsvc.getBoolPref("accessibility.typeaheadfind.prefillwithselection");
+
+ if (this.prefillWithSelection && userWantsPrefill) {
+ // NB: We have to focus this._findField here so tests that send
+ // key events can open and close the find bar synchronously.
+ this._findField.focus();
+
+ // (e10s) since we focus lets also select it, otherwise that would
+ // only happen in this.onCurrentSelection and, because it is async,
+ // there's a chance keypresses could come inbetween, leading to
+ // jumbled up queries.
+ this._findField.select();
+
+ this.browser.finder.getInitialSelection();
+ return startFindPromise;
+ }
+
+ // If userWantsPrefill is false but prefillWithSelection is true,
+ // then we might need to check the selection clipboard. Call
+ // onCurrentSelection to do so.
+ // Note: this.onCurrentSelection clears this._startFindDeferred.
+ this.onCurrentSelection("", true);
+ return startFindPromise;
+ ]]></body>
+ </method>
+
+ <!--
+ - Convenient alias to startFind(gFindBar.FIND_NORMAL);
+ -
+ - You should generally map the window's find command to this method.
+ - e.g. <command name="cmd_find" oncommand="gFindBar.onFindCommand();"/>
+ -->
+ <method name="onFindCommand">
+ <body><![CDATA[
+ return this.startFind(this.FIND_NORMAL);
+ ]]></body>
+ </method>
+
+ <!--
+ - 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;
+ if (!findString)
+ return this.startFind();
+
+ // 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
+ // there would never be findAgain events because of the logic below.
+ if (!this._dispatchFindEvent("again", aFindPrevious))
+ return undefined;
+
+ // user explicitly requested another search, so do it even if we think it'll fail
+ this._findFailedString = null;
+
+ // 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);
+ } else {
+ this._findAgain(aFindPrevious);
+ if (this._useModalHighlight) {
+ this.open();
+ this._findField.select();
+ this._findField.focus();
+ }
+ }
+
+ return undefined;
+ ]]></body>
+ </method>
+
+#ifdef XP_MACOSX
+ <!--
+ - Fetches the currently selected text and sets that as the text to search
+ - next. This is a MacOS specific feature.
+ -->
+ <method name="onFindSelectionCommand">
+ <body><![CDATA[
+ let searchString = this.browser.finder.setSearchStringToSelection();
+ if (searchString)
+ this._findField.value = searchString;
+ ]]></body>
+ </method>
+
+ <method name="_onFindFieldFocus">
+ <body><![CDATA[
+ let prefsvc = this._prefsvc;
+ const kPref = "accessibility.typeaheadfind.prefillwithselection";
+ if (this.prefillWithSelection && prefsvc.getBoolPref(kPref))
+ return;
+
+ let clipboardSearchString = this._browser.finder.clipboardSearchString;
+ if (clipboardSearchString && this._findField.value != clipboardSearchString &&
+ !this._findField._willfullyDeleted) {
+ this._findField.value = clipboardSearchString;
+ this._findField._hadValue = true;
+ // Changing the search string makes the previous status invalid, so
+ // we better clear it here.
+ this._updateStatusUI();
+ }
+ ]]></body>
+ </method>
+#endif
+
+ <!--
+ - 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="aData"/>
+ <body><![CDATA[
+ if (aData.result == this.nsITypeAheadFind.FIND_NOTFOUND) {
+ // If an explicit Find Again command fails, re-open the toolbar.
+ if (aData.storeResult && this.open()) {
+ this._findField.select();
+ this._findField.focus();
+ }
+ this._findFailedString = aData.searchString;
+ } else {
+ this._findFailedString = null;
+ }
+
+ this._updateStatusUI(aData.result, aData.findBackwards);
+ this._updateStatusUIBar(aData.linkURL);
+
+ if (this._findMode != this.FIND_NORMAL)
+ this._setFindCloseTimeout();
+ ]]></body>
+ </method>
+
+ <!--
+ - This handles all the result changes for matches counts.
+ - @param aResult
+ - Result Object, containing the total amount of matches and a vector
+ - of the current result.
+ -->
+ <method name="onMatchesCountResult">
+ <parameter name="aResult"/>
+ <body><![CDATA[
+ if (aResult.total !== 0) {
+ if (aResult.total == -1) {
+ this._foundMatches.value = this.pluralForm.get(
+ aResult.limit,
+ this.strBundle.GetStringFromName("FoundMatchesCountLimit")
+ ).replace("#1", aResult.limit);
+ } else {
+ this._foundMatches.value = this.pluralForm.get(
+ aResult.total,
+ this.strBundle.GetStringFromName("FoundMatches")
+ ).replace("#1", aResult.current)
+ .replace("#2", aResult.total);
+ }
+ this._foundMatches.hidden = false;
+ } else {
+ this._foundMatches.hidden = true;
+ this._foundMatches.value = "";
+ }
+ ]]></body>
+ </method>
+
+ <method name="onHighlightFinished">
+ <parameter name="result"/>
+ <body><![CDATA[
+ // Noop.
+ ]]></body>
+ </method>
+
+ <method name="onCurrentSelection">
+ <parameter name="aSelectionString" />
+ <parameter name="aIsInitialSelection" />
+ <body><![CDATA[
+ // Ignore the prefill if the user has already typed in the findbar,
+ // it would have been overwritten anyway. See bug 1198465.
+ if (aIsInitialSelection && !this._startFindDeferred)
+ return;
+
+ if (/Mac/.test(navigator.platform) && aIsInitialSelection && !aSelectionString) {
+ let clipboardSearchString = this.browser.finder.clipboardSearchString;
+ if (clipboardSearchString)
+ aSelectionString = clipboardSearchString;
+ }
+
+ if (aSelectionString)
+ this._findField.value = aSelectionString;
+
+ if (aIsInitialSelection) {
+ this._enableFindButtons(!!this._findField.value);
+ this._findField.select();
+ this._findField.focus();
+
+ this._startFindDeferred.resolve();
+ this._startFindDeferred = null;
+ }
+ ]]></body>
+ </method>
+
+ <!--
+ - This handler may cancel a request to focus content by returning |false|
+ - explicitly.
+ -->
+ <method name="shouldFocusContent">
+ <body><![CDATA[
+ const fm = Components.classes["@mozilla.org/focus-manager;1"]
+ .getService(Components.interfaces.nsIFocusManager);
+ if (fm.focusedWindow != window)
+ return false;
+
+ let focusedElement = fm.focusedElement;
+ if (!focusedElement)
+ return false;
+
+ let bindingParent = document.getBindingParent(focusedElement);
+ if (bindingParent != this && bindingParent != this._findField)
+ return false;
+
+ return true;
+ ]]></body>
+ </method>
+
+ </implementation>
+
+ <handlers>
+ <!--
+ - We have to guard against `this.close` being |null| due to an unknown
+ - issue, which is tracked in bug 957999.
+ -->
+ <handler event="keypress" keycode="VK_ESCAPE" phase="capturing"
+ action="if (this.close) this.close();" preventdefault="true"/>
+ </handlers>
+ </binding>
+</bindings>