gecko-dev/toolkit/content/widgets/findbar.xml

1422 строки
52 KiB
XML

<?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[
let { AppConstants } =
Components.utils.import("resource://gre/modules/AppConstants.jsm", {});
if (AppConstants.platform == "macosx") {
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.contains("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="_setEntireWord(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._updateEntireWord();
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._matchesCountTimeoutLength =
prefsvc.getIntPref("accessibility.typeaheadfind.matchesCountTimeout");
this._matchesCountLimit =
prefsvc.getIntPref("accessibility.typeaheadfind.matchesCountLimit");
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._highlightTimeout) {
clearTimeout(this._highlightTimeout);
this._highlightTimeout = null;
}
if (this._findResetTimeout) {
clearTimeout(this._findResetTimeout);
this._findResetTimeout = null;
}
if (this._updateMatchesCountTimeout) {
clearTimeout(this._updateMatchesCountTimeout);
this._updateMatchesCountTimeout = 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._matchesCountLimit == 0 || !this._dispatchFindEvent("matchescount"))
return;
if (this._updateMatchesCountTimeout) {
window.clearTimeout(this._updateMatchesCountTimeout);
}
this._updateMatchesCountTimeout =
window.setTimeout(() => {
this.browser.finder.requestMatchesCount(this._findField.value, this._matchesCountLimit,
this._findMode == this.FIND_LINKS);
}, this._matchesCountTimeoutLength);
]]></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._highlightAll = aHighlight;
if (!this._dispatchFindEvent("highlightallchange")) {
return;
}
this._setHighlightAll(aHighlight, aFromPrefObserver);
let word = this._findField.value;
// Bug 429723. Don't attempt to highlight ""
if (aHighlight && !word)
return;
this.browser.finder.highlight(aHighlight, word);
// 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>
<!--
- 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();
if (this.getElement("highlight").checked)
this._setHighlightTimeout();
this._dispatchFindEvent("casesensitivitychange");
]]></body>
</method>
<!--
- Updates the entire-word mode of the findbar and its UI.
-->
<method name="_updateEntireWord">
<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;
// Update the matches count
this._updateMatchesCount(this.nsITypeAheadFind.FIND_FOUND);
]]></body>
</method>
<!--
- Sets the findbar entire-word mode
- @param aEntireWord (boolean)
- Whether or not entire-word mode should be turned on.
-->
<method name="_setEntireWord">
<parameter name="aEntireWord"/>
<body><![CDATA[
let prefsvc =
Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
// Just set the pref; our observer will change the find bar behavior.
prefsvc.setBoolPref("findbar.entireword", aEntireWord);
if (this.getElement("highlight").checked)
this._setHighlightTimeout();
]]></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);
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;
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("KeyEvents");
event.initKeyEvent(aEvent.type, aEvent.bubbles, aEvent.cancelable,
aEvent.view, aEvent.ctrlKey, aEvent.altKey,
aEvent.shiftKey, aEvent.metaKey, aEvent.keyCode,
aEvent.charCode);
aTarget.dispatchEvent(event);
]]></body>
</method>
<field name="_xulBrowserWindow">null</field>
<method name="_updateStatusUIBar">
<parameter name="aFoundURL"/>
<body><![CDATA[
if (!this._xulBrowserWindow) {
try {
this._xulBrowserWindow =
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIWebNavigation)
.QueryInterface(Components.interfaces.nsIDocShellTreeItem)
.treeOwner
.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._updateEntireWord();
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);
if (this.getElement("highlight").checked)
this._setHighlightTimeout();
this._updateCaseSensitivity(val);
this._updateEntireWord();
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="_setHighlightTimeout">
<body><![CDATA[
if (this._highlightTimeout)
clearTimeout(this._highlightTimeout);
this._highlightTimeout =
setTimeout(function(aSelf) {
aSelf.toggleHighlight(false);
aSelf.toggleHighlight(true);
}, 500, this);
]]></body>
</method>
<method name="_findAgain">
<parameter name="aFindPrevious"/>
<body><![CDATA[
this.browser.finder.findAgain(aFindPrevious,
this._findMode == this.FIND_LINKS,
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;
}
this._updateMatchesCount(res);
]]></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(
this._matchesCountLimit,
this.strBundle.GetStringFromName("FoundMatchesCountLimit")
).replace("#1", this._matchesCountLimit);
} 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;
let { AppConstants } =
Components.utils.import("resource://gre/modules/AppConstants.jsm", {});
if (AppConstants.platform == "macosx" && 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>