<?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>