diff options
Diffstat (limited to 'toolkit/content/widgets/findbar.xml')
-rw-r--r-- | toolkit/content/widgets/findbar.xml | 1397 |
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> |