gecko-dev/toolkit/content/widgets/findbar.xml
2012-10-22 12:42:53 +01:00

1837 lines
66 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) {
var findString = this.findbar._findField;
if (!findString.value)
return;
#ifdef XP_MACOSX
if (aEvent.metaKey) {
#else
if (aEvent.ctrlKey) {
#endif
this.findbar.getElement("highlight").click();
return;
}
this.findbar.onFindAgainCommand(aEvent.shiftKey);
}
else {
// We need to keep a reference to _foundLink because
// _finishFAYT resets it to null.
var tmpLink = this._findbar._foundLink;
if (tmpLink && this.findbar._finishFAYT(aEvent))
this.findbar._dispatchKeypressEvent(tmpLink, aEvent);
}
]]></body>
</method>
<method name="_handleTab">
<parameter name="aEvent"/>
<body><![CDATA[
var shouldHandle = !aEvent.altKey && !aEvent.ctrlKey &&
!aEvent.metaKey;
if (shouldHandle &&
this.findbar._findMode != this.findbar.FIND_NORMAL &&
this.findbar._finishFAYT(aEvent)) {
if (aEvent.shiftKey)
document.commandDispatcher.rewindFocus();
else
document.commandDispatcher.advanceFocus();
}
]]></body>
</method>
</implementation>
<handlers>
<handler event="input"><![CDATA[
// We should do nothing during composition. E.g., composing string
// 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;
}
this.findbar._find(this.value);
]]></handler>
<handler event="keypress"><![CDATA[
var win = this.findbar._currentWindow ||
this.findbar.browser.contentWindow;
var controller = this.findbar._getSelectionController(win);
var shouldHandle = !event.altKey && !event.ctrlKey &&
!event.metaKey && !event.shiftKey;
switch (event.keyCode) {
case KeyEvent.DOM_VK_RETURN:
this._handleEnter(event);
break;
case KeyEvent.DOM_VK_TAB:
this._handleTab(event);
break;
case KeyEvent.DOM_VK_PAGE_UP:
if (shouldHandle) {
controller.scrollPage(false);
event.preventDefault();
}
break;
case KeyEvent.DOM_VK_PAGE_DOWN:
if (shouldHandle) {
controller.scrollPage(true);
event.preventDefault();
}
break;
case KeyEvent.DOM_VK_UP:
controller.scrollLine(false);
event.preventDefault();
break;
case KeyEvent.DOM_VK_DOWN:
controller.scrollLine(true);
event.preventDefault();
break;
}
]]></handler>
<handler event="blur"><![CDATA[
var findbar = this.findbar;
var fastFind = findbar.browser.fastFind;
if (findbar._foundEditable)
fastFind.collapseSelection();
else {
fastFind.setSelectionModeAndRepaint
(findbar.nsISelectionController.SELECTION_ON);
}
findbar._setFoundLink(null);
findbar._foundEditable = null;
findbar._currentWindow = null;
]]></handler>
<handler event="compositionstart"><![CDATA[
// Don't close the find toolbar while IME is composing.
var findbar = this.findbar;
findbar._isIMEComposing = true;
if (findbar._quickFindTimeout) {
clearTimeout(findbar._quickFindTimeout);
findbar._quickFindTimeout = null;
}
]]></handler>
<handler event="compositionend"><![CDATA[
var findbar = this.findbar;
findbar._isIMEComposing = false;
if (findbar._findMode != findbar.FIND_NORMAL &&
!findbar.hidden)
findbar._setFindCloseTimeout();
]]></handler>
<handler event="dragover"><![CDATA[
if (event.dataTransfer.types.contains("text/plain"))
event.preventDefault();
]]></handler>
<handler event="drop"><![CDATA[
var 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:toolbarbutton anonid="find-closebutton"
class="findbar-closebutton"
tooltiptext="&findCloseButton.tooltip;"
oncommand="close();"/>
<xul:label anonid="find-label" class="findbar-find-fast" control="findbar-textbox"/>
<xul:textbox class="findbar-textbox findbar-find-fast" anonid="findbar-textbox"
xbl:inherits="flash"/>
#ifdef MOZ_WIDGET_GTK
<xul:toolbarbutton anonid="find-previous"
class="findbar-find-previous tabbable"
label="&previous.label;"
accesskey="&previous.accesskey;"
tooltiptext="&previous.tooltip;"
oncommand="onFindAgainCommand(true);"
disabled="true"
xbl:inherits="accesskey=findpreviousaccesskey"/>
#endif
<xul:toolbarbutton anonid="find-next"
class="findbar-find-next tabbable"
label="&next.label;"
accesskey="&next.accesskey;"
tooltiptext="&next.tooltip;"
oncommand="onFindAgainCommand(false);"
disabled="true"
xbl:inherits="accesskey=findnextaccesskey"/>
#ifndef MOZ_WIDGET_GTK
<xul:toolbarbutton anonid="find-previous"
class="findbar-find-previous tabbable"
label="&previous.label;"
accesskey="&previous.accesskey;"
tooltiptext="&previous.tooltip;"
oncommand="onFindAgainCommand(true);"
disabled="true"
xbl:inherits="accesskey=findpreviousaccesskey"/>
#endif
<xul:toolbarbutton anonid="highlight"
class="findbar-highlight tabbable"
label="&highlight.label;"
accesskey="&highlight.accesskey;"
tooltiptext="&highlight.tooltiptext;"
oncommand="toggleHighlight(this.checked);"
type="checkbox"
disabled="true"
xbl:inherits="accesskey=highlightaccesskey"/>
<xul:checkbox anonid="find-case-sensitive"
oncommand="_setCaseSensitivity(this.checked);"
label="&caseSensitiveCheckbox.label;"
accesskey="&caseSensitiveCheckbox.accesskey;"
xbl:inherits="accesskey=matchcaseaccesskey"/>
<xul:label anonid="match-case-status" class="findbar-find-fast"/>
<xul:image anonid="find-status-icon" class="findbar-find-fast find-status-icon"/>
<xul:description anonid="find-status" class="findbar-find-fast findbar-find-status"
control="findbar-textbox">
<!-- Do not use value, first child is used because it provides a11y with text change events -->
</xul:description>
</xul:hbox>
</content>
<implementation implements="nsIDOMEventListener, nsIEditActionListener">
<field name="FIND_NORMAL">0</field>
<field name="FIND_TYPEAHEAD">1</field>
<field name="FIND_LINKS">2</field>
<field name="_findMode">0</field>
<field name="_tmpOutline">null</field>
<field name="_tmpOutlineOffset">"0"</field>
<field name="_drawOutline">false</field>
<field name="_editors">null</field>
<field name="_stateListeners">null</field>
<field name="_flashFindBar">0</field>
<field name="_initialFlashFindBarCount">6</field>
<property name="_foundLink"
onget="return this._foundLinkRef.get();"
onset="this._foundLinkRef = Components.utils.getWeakReference(val); return val;"/>
<property name="_foundEditable"
onget="return this._foundEditableRef.get();"
onset="this._foundEditableRef = Components.utils.getWeakReference(val); return val;"/>
<property name="_currentWindow"
onget="return this._currentWindowRef.get();"
onset="this._currentWindowRef = Components.utils.getWeakReference(val); return val;"/>
<property name="prefillWithSelection"
onget="return this.getAttribute('prefillwithselection') != 'false'"
onset="this.setAttribute('prefillwithselection', val); return val;"/>
<field name="_selectionMaxLen">150</field>
<method name="getElement">
<parameter name="aAnonymousID"/>
<body><![CDATA[
return document.getAnonymousElementByAttribute(this,
"anonid",
aAnonymousID)
]]></body>
</method>
<property name="findMode"
readonly="true"
onget="return this._findMode;"/>
<property name="canClear" 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) {
this._browser.removeEventListener("keypress", this, false);
this._browser.removeEventListener("mouseup", this, false);
this._foundLink = null;
this._foundEditable = null;
this._currentWindow = null;
}
this._browser = val;
if (this._browser) {
this._browser.addEventListener("keypress", this, false);
this._browser.addEventListener("mouseup", this, false);
this._findField.value = this._browser.fastFind.searchString;
}
return val;
]]></setter>
</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;
var prefsvc =
aSubject.QueryInterface(Components.interfaces.nsIPrefBranch);
switch (aPrefName) {
case "accessibility.typeaheadfind":
this._self._useTypeAheadFind = prefsvc.getBoolPref(aPrefName);
break;
case "accessibility.typeaheadfind.linksonly":
this._self._typeAheadLinksOnly = prefsvc.getBoolPref(aPrefName);
break;
case "accessibility.typeaheadfind.casesensitive":
this._self._typeAheadCaseSensitive = prefsvc.getIntPref(aPrefName);
this._self._updateCaseSensitivity();
if (this._self.getElement("highlight").checked)
this._self._setHighlightTimeout();
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._findStatusIcon = this.getElement("find-status-icon");
this._findStatusDesc = this.getElement("find-status");
this._foundLink = null;
this._foundEditable = null;
this._currentWindow = null;
var prefsvc =
Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
this._quickFindTimeoutLength =
prefsvc.getIntPref("accessibility.typeaheadfind.timeout");
this._flashFindBar =
prefsvc.getIntPref("accessibility.typeaheadfind.flashBar");
prefsvc.addObserver("accessibility.typeaheadfind",
this._observer, false);
prefsvc.addObserver("accessibility.typeaheadfind.linksonly",
this._observer, false);
prefsvc.addObserver("accessibility.typeaheadfind.casesensitive",
this._observer, false);
this._useTypeAheadFind =
prefsvc.getBoolPref("accessibility.typeaheadfind");
this._typeAheadLinksOnly =
prefsvc.getBoolPref("accessibility.typeaheadfind.linksonly");
this._typeAheadCaseSensitive =
prefsvc.getIntPref("accessibility.typeaheadfind.casesensitive");
// 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;
// It is possible that the findbar may be destroyed before any
// documents it is listening to (see nsIEditActionListener code below).
// Thus, to avoid leaking, if we are listening to any editors, unhook
// ourselves now, and remove our cached copies
if (this._editors) {
for (var x = this._editors.length - 1; x >= 0; --x)
this._unhookListenersAtIndex(x);
}
this.browser = null;
var prefsvc =
Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
prefsvc.removeObserver("accessibility.typeaheadfind",
this._observer);
prefsvc.removeObserver("accessibility.typeaheadfind.linksonly",
this._observer);
prefsvc.removeObserver("accessibility.typeaheadfind.casesensitive",
this._observer);
]]></body>
</method>
<method name="_setFindCloseTimeout">
<body><![CDATA[
if (this._quickFindTimeout)
clearTimeout(this._quickFindTimeout);
// Don't close the find toolbar while IME is composing.
if (this._isIMEComposing) {
this._quickFindTimeout = null;
return;
}
this._quickFindTimeout =
setTimeout(function(aSelf) {
if (aSelf._findMode != aSelf.FIND_NORMAL)
aSelf.close();
}, this._quickFindTimeoutLength, this);
]]></body>
</method>
<!--
- Gets the selection controller for the current browser
-->
<method name="_getSelectionController">
<parameter name="aWindow"/>
<body><![CDATA[
// display: none iframes don't have a selection controller, see bug 493658
if (!aWindow.innerWidth || !aWindow.innerHeight)
return null;
// Yuck. See bug 138068.
var Ci = Components.interfaces;
var docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
var controller = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsISelectionDisplay)
.QueryInterface(Ci.nsISelectionController);
return controller;
]]></body>
</method>
<!--
- For a given node, walk up it's parent chain, to try and find an
- editable node.
-
- @param aNode the node we want to check
- @returns the first node in the parent chain that is editable,
- null if there is no such node
-->
<method name="_getEditableNode">
<parameter name="aNode"/>
<body><![CDATA[
while (aNode) {
if (aNode instanceof Components.interfaces.nsIDOMNSEditableElement) {
return aNode.editor ? aNode : null;
}
aNode = aNode.parentNode;
}
return null;
]]></body>
</method>
<!--
- Helper method to unhook listeners, remove cached editors
- and keep the relevant arrays in sync
-
- @param aIndex the index into the array of editors/state listeners
- we wish to remove
-->
<method name="_unhookListenersAtIndex">
<parameter name="aIndex"/>
<body><![CDATA[
this._editors[aIndex].removeEditActionListener(this);
this._editors[aIndex]
.removeDocumentStateListener(this._stateListeners[aIndex]);
this._editors.splice(aIndex, 1);
this._stateListeners.splice(aIndex, 1);
if (this._editors.length == 0) {
delete this._editors;
delete this._stateListeners;
}
]]></body>
</method>
<!--
- Remove ourselves as an nsIEditActionListener and
- nsIDocumentStateListener from a given cached editor
-
- @param aEditor the editor we no longer wish to listen to
-->
<method name="_removeEditorListeners">
<parameter name="aEditor"/>
<body><![CDATA[
// aEditor is an editor that we listen to, so therefore must be
// cached. Find the index of this editor
var idx = 0;
while (this._editors[idx] != aEditor)
idx++;
// Now unhook ourselves, and remove our cached copy
this._unhookListenersAtIndex(idx);
]]></body>
</method>
<!--
- nsIEditActionListener logic follows
-
- We implement this interface to allow us to catch the case where
- the findbar found a match in a HTML <input> or <textarea>. If the
- user adjusts the text in some way, it will no longer match, so we
- want to remove the highlight, rather than have it expand/contract
- when letters are added or removed.
-->
<!--
- Helper method used to check whether a selection intersects with
- some highlighting
-
- @param aSelectionRange the range from the selection to check
- @param aFindRange the highlighted range to check against
- @returns true if they intersect, false otherwise
-->
<method name="_checkOverlap">
<parameter name="aSelectionRange"/>
<parameter name="aFindRange"/>
<body><![CDATA[
// The ranges overlap if one of the following is true:
// 1) At least one of the endpoints of the deleted selection
// is in the find selection
// 2) At least one of the endpoints of the find selection
// is in the deleted selection
if (aFindRange.isPointInRange(aSelectionRange.startContainer,
aSelectionRange.startOffset))
return true;
if (aFindRange.isPointInRange(aSelectionRange.endContainer,
aSelectionRange.endOffset))
return true;
if (aSelectionRange.isPointInRange(aFindRange.startContainer,
aFindRange.startOffset))
return true;
if (aSelectionRange.isPointInRange(aFindRange.endContainer,
aFindRange.endOffset))
return true;
return false;
]]></body>
</method>
<!--
- Helper method to determine if an edit occurred within a highlight
-
- @param aSelection the selection we wish to check
- @param aNode the node we want to check is contained in aSelection
- @param aOffset the offset into aNode that we want to check
- @returns the range containing (aNode, aOffset) or null if no ranges
- in the selection contain it
-->
<method name="_findRange">
<parameter name="aSelection"/>
<parameter name="aNode"/>
<parameter name="aOffset"/>
<body><![CDATA[
var rangeCount = aSelection.rangeCount;
var rangeidx = 0;
var foundContainingRange = false;
var range = null;
// Check to see if this node is inside one of the selection's ranges
while (!foundContainingRange && rangeidx < rangeCount) {
range = aSelection.getRangeAt(rangeidx);
if (range.isPointInRange(aNode, aOffset)) {
foundContainingRange = true;
break;
}
rangeidx++;
}
if (foundContainingRange)
return range;
return null;
]]></body>
</method>
<!-- Start of nsIEditActionListener implementations -->
<method name="WillDeleteText">
<parameter name="aTextNode"/>
<parameter name="aOffset"/>
<parameter name="aLength"/>
<body><![CDATA[
var editor = this._getEditableNode(aTextNode).editor;
var controller = editor.selectionController;
var fSelection = controller.getSelection(this._findSelection);
var range = this._findRange(fSelection, aTextNode, aOffset);
if (range) {
// Don't remove the highlighting if the deleted text is at the
// end of the range
if (aTextNode != range.endContainer ||
aOffset != range.endOffset) {
// Text within the highlight is being removed - the text can
// no longer be a match, so remove the highlighting
fSelection.removeRange(range);
if (fSelection.rangeCount == 0)
this._removeEditorListeners(editor);
}
}
]]></body>
</method>
<method name="DidInsertText">
<parameter name="aTextNode"/>
<parameter name="aOffset"/>
<parameter name="aString"/>
<body><![CDATA[
var editor = this._getEditableNode(aTextNode).editor;
var controller = editor.selectionController;
var fSelection = controller.getSelection(this._findSelection);
var range = this._findRange(fSelection, aTextNode, aOffset);
if (range) {
// If the text was inserted before the highlight
// adjust the highlight's bounds accordingly
if (aTextNode == range.startContainer &&
aOffset == range.startOffset)
range.setStart(range.startContainer,
range.startOffset+aString.length);
else if (aTextNode != range.endContainer ||
aOffset != range.endOffset) {
// The edit occurred within the highlight - any addition of text
// will result in the text no longer being a match,
// so remove the highlighting
fSelection.removeRange(range);
if (fSelection.rangeCount == 0)
this._removeEditorListeners(editor);
}
}
]]></body>
</method>
<method name="WillDeleteSelection">
<parameter name="aSelection"/>
<body><![CDATA[
var editor = this._getEditableNode(aSelection.getRangeAt(0)
.startContainer).editor;
var controller = editor.selectionController;
var fSelection = controller.getSelection(this._findSelection);
var selectionIndex = 0;
var findSelectionIndex = 0;
var shouldDelete = {};
var numberOfDeletedSelections = 0;
var numberOfMatches = fSelection.rangeCount;
// We need to test if any ranges in the deleted selection (aSelection)
// are in any of the ranges of the find selection
// Usually both selections will only contain one range, however
// either may contain more than one.
for (var fIndex = 0; fIndex < numberOfMatches; fIndex++) {
shouldDelete[fIndex] = false;
var fRange = fSelection.getRangeAt(fIndex);
for (var index = 0; index < aSelection.rangeCount; index++) {
if (!shouldDelete[fIndex]) {
var selRange = aSelection.getRangeAt(index);
var doesOverlap = this._checkOverlap(selRange, fRange);
if (doesOverlap) {
shouldDelete[fIndex] = true;
numberOfDeletedSelections++;
}
}
}
}
// OK, so now we know what matches (if any) are in the selection
// that is being deleted. Time to remove them.
if (numberOfDeletedSelections == 0)
return;
for (var i = numberOfMatches - 1; i >= 0; i--) {
if (shouldDelete[i]) {
var r = fSelection.getRangeAt(i);
fSelection.removeRange(r);
}
}
// Remove listeners if no more highlights left
if (fSelection.rangeCount == 0)
this._removeEditorListeners(editor);
]]></body>
</method>
<method name="WillInsertText">
<body><![CDATA[
// Unimplemented
]]></body>
</method>
<method name="DidCreateNode">
<body><![CDATA[
// Unimplemented
]]></body>
</method>
<method name="DidDeleteNode">
<body><![CDATA[
// Unimplemented
]]></body>
</method>
<method name="DidDeleteSelection">
<body><![CDATA[
// Unimplemented
]]></body>
</method>
<method name="DidDeleteText">
<body><![CDATA[
// Unimplemented
]]></body>
</method>
<method name="DidInsertNode">
<body><![CDATA[
// Unimplemented
]]></body>
</method>
<method name="DidJoinNodes">
<body><![CDATA[
// Unimplemented
]]></body>
</method>
<method name="DidSplitNode">
<body><![CDATA[
// Unimplemented
]]></body>
</method>
<method name="WillCreateNode">
<body><![CDATA[
// Unimplemented
]]></body>
</method>
<method name="WillDeleteNode">
<body><![CDATA[
// Unimplemented
]]></body>
</method>
<method name="WillInsertNode">
<body><![CDATA[
// Unimplemented
]]></body>
</method>
<method name="WillJoinNodes">
<body><![CDATA[
// Unimplemented
]]></body>
</method>
<method name="WillSplitNode">
<body><![CDATA[
// Unimplemented
]]></body>
</method>
<!-- End of nsIEditActionListener implementations -->
<!--
- nsIDocumentStateListener logic follows
-
- When attaching nsIEditActionListeners, there are no guarantees
- as to whether the findbar or the documents in the browser will get
- destructed first. This leads to the potential to either leak, or to
- hold on to a reference an editable element's editor for too long,
- preventing it from being destructed.
-
- However, when an editor's owning node is being destroyed, the editor
- sends out a DocumentWillBeDestroyed notification. We can use this to
- clean up our references to the object, to allow it to be destroyed in a
- timely fashion.
-->
<!--
- Unhook ourselves when one of our state listeners has been called.
- This can happen in 4 cases:
- 1) The document the editor belongs to is navigated away from, and
- the document is not being cached
-
- 2) The document the editor belongs to is expired from the cache
-
- 3) The tab containing the owning document is closed
-
- 4) The <input> or <textarea> that owns the editor is explicitly
- removed from the DOM
-
- @param the listener that was invoked
-->
<method name="_onEditorDestruction">
<parameter name="aListener"/>
<body><![CDATA[
// First find the index of the editor the given listener listens to.
// The listeners and editors arrays must always be in sync.
// The listener will be in our array of cached listeners, as this
// method could not have been called otherwise.
var idx = 0;
while (this._stateListeners[idx] != aListener)
idx++;
// Unhook both listeners
this._unhookListenersAtIndex(idx);
]]></body>
</method>
<!--
- Creates a unique document state listener for an editor.
-
- It is not possible to simply have the findbar implement the
- listener interface itself, as it wouldn't have sufficient information
- to work out which editor was being destroyed. Therefore, we create new
- listeners on the fly, and cache them in sync with the editors they
- listen to.
-->
<method name="_createStateListener">
<body><![CDATA[
return ({
findbar: this,
QueryInterface: function(aIID) {
if (aIID.equals(Components.interfaces.nsIDocumentStateListener) ||
aIID.equals(Components.interfaces.nsISupports))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
},
NotifyDocumentWillBeDestroyed: function() {
this.findbar._onEditorDestruction(this);
},
// Unimplemented
notifyDocumentCreated: function() {},
notifyDocumentStateChanged: function(aDirty) {}
});
]]></body>
</method>
<!--
- Turns highlight on or off.
- @param aHighlight (boolean)
- Whether to turn the highlight on or off
-->
<method name="toggleHighlight">
<parameter name="aHighlight"/>
<body><![CDATA[
if (!this._dispatchFindEvent("highlightallchange"))
return;
var word = this._findField.value;
// Bug 429723. Don't attempt to highlight ""
if (aHighlight && !word)
return;
// We have to update the status because we might still have the status
// of another tab
if (this._highlightDoc(aHighlight, word))
this._updateStatusUI(this.nsITypeAheadFind.FIND_FOUND);
else
this._updateStatusUI(this.nsITypeAheadFind.FIND_NOTFOUND);
]]></body>
</method>
<!--
- (Un)highlights each instance of the searched word in the passed
- window's content.
- @param aHighlight (boolean)
- Whether to turn on highlight
- @param aWord
- the word to search for
- @param aWindow
- the window to search in. Passing undefined will search the
- current content window.
- @returns true if aWord was found
-->
<method name="_highlightDoc">
<parameter name="aHighlight"/>
<parameter name="aWord"/>
<parameter name="aWindow"/>
<body><![CDATA[
var win = aWindow || this.browser.contentWindow;
var textFound = false;
for (var i = 0; win.frames && i < win.frames.length; i++) {
if (this._highlightDoc(aHighlight, aWord, win.frames[i]))
textFound = true;
}
var controller = this._getSelectionController(win);
if (!controller) {
// Without the selection controller,
// we are unable to (un)highlight any matches
return textFound;
}
var doc = win.document;
if (!doc || !(doc instanceof HTMLDocument))
return textFound;
if (aHighlight) {
var searchRange = doc.createRange();
searchRange.selectNodeContents(doc.body);
var startPt = searchRange.cloneRange();
startPt.collapse(true);
var endPt = searchRange.cloneRange();
endPt.collapse(false);
var retRange = null;
var finder = Components.classes["@mozilla.org/embedcomp/rangefind;1"]
.createInstance()
.QueryInterface(Components.interfaces.nsIFind);
finder.caseSensitive = this._shouldBeCaseSensitive(aWord);
while ((retRange = finder.Find(aWord, searchRange,
startPt, endPt))) {
this._highlight(retRange, controller);
startPt = retRange.cloneRange();
startPt.collapse(false);
textFound = true;
}
} else {
// First, attempt to remove highlighting from main document
var sel = controller.getSelection(this._findSelection);
sel.removeAllRanges();
// Next, check our editor cache, for editors belonging to this
// document
if (this._editors) {
for (var x = this._editors.length - 1; x >= 0; --x) {
if (this._editors[x].document == doc) {
sel = this._editors[x].selectionController
.getSelection(this._findSelection);
sel.removeAllRanges();
// We don't need to listen to this editor any more
this._unhookListenersAtIndex(x);
}
}
}
return true;
}
return textFound;
]]></body>
</method>
<!--
- Highlights the word in the passed range.
-
- @param aRange
- the range that contains the word to highlight
- @param aController
- the current document's selection controller
-->
<method name="_highlight">
<parameter name="aRange"/>
<parameter name="aController"/>
<body><![CDATA[
var node = aRange.startContainer;
var controller = aController;
var editableNode = this._getEditableNode(node);
if (editableNode)
controller = editableNode.editor.selectionController;
var findSelection = controller.getSelection(this._findSelection);
findSelection.addRange(aRange);
if (editableNode) {
// Highlighting added, so cache this editor, and hook up listeners
// to ensure we deal properly with edits within the highlighting
if (!this._editors) {
this._editors = [];
this._stateListeners = [];
}
var existingIndex = this._editors.indexOf(editableNode.editor);
if (existingIndex == -1) {
var x = this._editors.length;
this._editors[x] = editableNode.editor;
this._stateListeners[x] = this._createStateListener();
this._editors[x].addEditActionListener(this);
this._editors[x].addDocumentStateListener(this._stateListeners[x]);
}
}
]]></body>
</method>
<!--
- Updates the case-sensitivity mode of the findbar and its UI.
- @param [optional] aString
- The string for which case sensitivity might be turned on.
- This only used when case-sensitivity is in auto mode,
- @see _shouldBeCaseSensitive. The default value for this
- parameter is the find-field value.
-->
<method name="_updateCaseSensitivity">
<parameter name="aString"/>
<body><![CDATA[
var val = aString || this._findField.value;
var caseSensitive = this._shouldBeCaseSensitive(val);
var checkbox = this.getElement("find-case-sensitive");
var statusLabel = this.getElement("match-case-status");
checkbox.checked = caseSensitive;
statusLabel.value = caseSensitive ? this._caseSensitiveStr : "";
// Show the checkbox on the full Find bar in non-auto mode.
// Show the label in all other cases.
var hideCheckbox = this._findMode != this.FIND_NORMAL ||
(this._typeAheadCaseSensitive != 0 &&
this._typeAheadCaseSensitive != 1);
checkbox.hidden = hideCheckbox;
statusLabel.hidden = !hideCheckbox;
var fastFind = this.browser.fastFind;
fastFind.caseSensitive = caseSensitive;
]]></body>
</method>
<!--
- Sets the findbar case-sensitivity mode
- @param aCaseSensitive (boolean)
- Whether or not case-sensitivity should be turned on.
-->
<method name="_setCaseSensitivity">
<parameter name="aCaseSensitive"/>
<body><![CDATA[
var prefsvc =
Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
// Just set the pref; our observer will change the find bar behavior
prefsvc.setIntPref("accessibility.typeaheadfind.casesensitive",
aCaseSensitive ? 1 : 0);
this._dispatchFindEvent("casesensitivitychange");
]]></body>
</method>
<!--
- 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 =
Components.classes["@mozilla.org/intl/stringbundle;1"]
.getService(Components.interfaces.nsIStringBundleService)
.createBundle("chrome://global/locale/findbar.properties");
this._notFoundStr = stringsBundle.GetStringFromName("NotFound");
this._wrappedToTopStr =
stringsBundle.GetStringFromName("WrappedToTop");
this._wrappedToBottomStr =
stringsBundle.GetStringFromName("WrappedToBottom");
this._normalFindStr =
stringsBundle.GetStringFromName("NormalFindLabel");
this._fastFindStr =
stringsBundle.GetStringFromName("FastFindLabel");
this._fastFindLinksStr =
stringsBundle.GetStringFromName("FastFindLinksLabel");
this._caseSensitiveStr =
stringsBundle.GetStringFromName("CaseSensitive");
}
this._findFailedString = null;
this._updateFindUI();
if (this.hidden) {
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">
<body><![CDATA[
if (this.hidden)
return;
var fm =
Components.classes["@mozilla.org/focus-manager;1"]
.getService(Components.interfaces.nsIFocusManager);
if (window == fm.focusedWindow) {
var focusedElement = fm.focusedElement;
if (focusedElement) {
var bindingParent = document.getBindingParent(focusedElement);
if (bindingParent == this || bindingParent == this._findField) {
// block scrolling on focus since find already scrolls, further
// scrolling is due to user action, so don't override this
var element = this._foundLink || this._foundEditable;
if (element)
fm.setFocus(element, fm.FLAG_NOSCROLL);
else if (this._currentWindow)
this._currentWindow.focus();
else
this.browser.contentWindow.focus();
}
}
}
this.hidden = true;
var fastFind = this.browser.fastFind;
fastFind.setSelectionModeAndRepaint
(this.nsISelectionController.SELECTION_ON);
this._setFoundLink(null);
this._foundEditable = null;
this._currentWindow = null;
if (this._quickFindTimeout) {
clearTimeout(this._quickFindTimeout);
this._quickFindTimeout = null;
}
this._findFailedString = null;
]]></body>
</method>
<method name="clear">
<body><![CDATA[
this.browser.fastFind.collapseSelection();
this._findField.reset();
this.toggleHighlight(false);
this._updateStatusUI();
this._enableFindButtons(false);
this._setFoundLink(null);
this._foundEditable = null;
this._currentWindow = null;
]]></body>
</method>
<method name="_dispatchKeypressEvent">
<parameter name="aTarget"/>
<parameter name="aEvent"/>
<body><![CDATA[
if (!aTarget)
return;
var event = document.createEvent("KeyEvents");
event.initKeyEvent(aEvent.type, aEvent.bubbles, aEvent.cancelable,
aEvent.view, aEvent.ctrlKey, aEvent.altKey,
aEvent.shiftKey, aEvent.metaKey, aEvent.keyCode,
aEvent.charCode);
aTarget.dispatchEvent(event);
]]></body>
</method>
<field name="_xulBrowserWindow">null</field>
<method name="_updateStatusUIBar">
<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;
}
if (!this._foundLink || !this._foundLink.href ||
this._foundLink.href == "") {
this._xulBrowserWindow.setOverLink("", null);
return true;
}
var docCharset = "";
var ownerDoc = this._foundLink.ownerDocument;
if (ownerDoc)
docCharset = ownerDoc.characterSet;
if (!this._textToSubURIService) {
this._textToSubURIService =
Components.classes["@mozilla.org/intl/texttosuburi;1"]
.getService(Components.interfaces.nsITextToSubURI);
}
var url =
this._textToSubURIService.unEscapeURIForUI(docCharset,
this._foundLink.href);
this._xulBrowserWindow.setOverLink(url, null);
return true;
]]></body>
</method>
<method name="_setFoundLink">
<parameter name="aFoundLink"/>
<body><![CDATA[
if (this._foundLink == aFoundLink)
return;
if (this._foundLink && this._drawOutline) {
// restore original outline
this._foundLink.style.outline = this._tmpOutline;
this._foundLink.style.outlineOffset = this._tmpOutlineOffset;
}
this._drawOutline = (aFoundLink && this._findMode != this.FIND_NORMAL);
if (this._drawOutline) {
// backup original outline
this._tmpOutline = aFoundLink.style.outline;
this._tmpOutlineOffset = aFoundLink.style.outlineOffset;
// draw pseudo focus rect
// XXX Should we change the following style for FAYT pseudo focus?
// XXX Shouldn't we change default design if outline is visible
// already?
// Don't set the outline-color, we should always use initial value.
aFoundLink.style.outline = "1px dotted";
aFoundLink.style.outlineOffset = "0";
}
this._foundLink = aFoundLink;
// If the mouse cursor is on the document, the status bar text is
// changed by a mouse event which is dispatched by a scroll event.
// Thus we should change it only after the mouse event is dispatched.
if (this._findMode != this.FIND_NORMAL)
setTimeout(function(aSelf) { aSelf._updateStatusUIBar(); }, 0, this);
]]></body>
</method>
<method name="_finishFAYT">
<parameter name="aKeypressEvent"/>
<body><![CDATA[
try {
if (this._foundLink)
this._foundLink.focus();
else if (this._foundEditable) {
this._foundEditable.focus();
var fastFind = this.browser.fastFind;
fastFind.collapseSelection();
}
else if (this._currentWindow)
this._currentWindow.focus();
else
return false;
}
catch(e) {
return false;
}
if (aKeypressEvent)
aKeypressEvent.preventDefault();
this.close();
return true;
]]></body>
</method>
<!--
- Returns true if |aMimeType| is text-based, or false otherwise.
-
- @param aMimeType
- The MIME type to check.
-
- if adding types to this function, please see the similar function
- in browser/base/content/browser.js
-->
<method name="_mimeTypeIsTextBased">
<parameter name="aMimeType"/>
<body><![CDATA[
return /^text\/|\+xml$/.test(aMimeType) ||
aMimeType == "application/x-javascript" ||
aMimeType == "application/javascript" ||
aMimeType == "application/xml";
]]></body>
</method>
<!--
- Returns whether FAYT can be used for the given event in
- the current content state.
-->
<method name="_shouldFastFind">
<parameter name="aEvent"/>
<body><![CDATA[
if (aEvent.ctrlKey || aEvent.altKey || aEvent.metaKey ||
aEvent.defaultPrevented)
return false;
var elt = document.commandDispatcher.focusedElement;
if (elt) {
if (elt instanceof HTMLInputElement && elt.mozIsTextField(false))
return false;
if (elt instanceof HTMLTextAreaElement ||
elt instanceof HTMLSelectElement ||
elt instanceof HTMLObjectElement ||
elt instanceof HTMLEmbedElement)
return false;
}
var win = document.commandDispatcher.focusedWindow;
if (win && !this._mimeTypeIsTextBased(win.document.contentType))
return false;
// disable FAYT in about:blank to prevent FAYT opening unexpectedly.
var url = this.browser.currentURI;
if (url.spec == "about:blank")
return false;
// disable FAYT in documents that ask for it to be disabled.
if ((url.schemeIs("about") || url.schemeIs("chrome")) &&
(win.document.documentElement &&
win.document.documentElement.getAttribute("disablefastfind") == "true"))
return false;
if (win) {
try {
var editingSession = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIWebNavigation)
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIEditingSession);
if (editingSession.windowIsEditable(win))
return false;
}
catch (e) {
// If someone built with composer disabled, we can't get an editing session.
}
}
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>
<method name="_onBrowserKeypress">
<parameter name="aEvent"/>
<body><![CDATA[
const TAF_LINKS_KEY = "'";
const TAF_TEXT_KEY = "/";
if (!this._shouldFastFind(aEvent))
return;
if (this._findMode != this.FIND_NORMAL && this._quickFindTimeout) {
if (!aEvent.charCode)
return;
this._findField.select();
this._findField.focus();
this._dispatchKeypressEvent(this._findField.inputField, aEvent);
aEvent.preventDefault();
return;
}
var key = aEvent.charCode ? String.fromCharCode(aEvent.charCode) : null;
var manualstartFAYT = (key == TAF_LINKS_KEY || key == TAF_TEXT_KEY);
var autostartFAYT = !manualstartFAYT && this._useTypeAheadFind &&
key && key != " ";
if (manualstartFAYT || autostartFAYT) {
var mode = (key == TAF_LINKS_KEY ||
(autostartFAYT && this._typeAheadLinksOnly)) ?
this.FIND_LINKS : this.FIND_TYPEAHEAD;
// Clear bar first, so that when openFindBar() calls setCaseSensitivity()
// it doesn't get confused by a lingering value
this._findField.value = "";
this.open(mode);
this._setFindCloseTimeout();
this._findField.select();
this._findField.focus();
if (autostartFAYT)
this._dispatchKeypressEvent(this._findField.inputField, aEvent);
else
this._updateStatusUI(this.nsITypeAheadFind.FIND_FOUND);
aEvent.preventDefault();
}
]]></body>
</method>
<!-- See nsIDOMEventListener -->
<method name="handleEvent">
<parameter name="aEvent"/>
<body><![CDATA[
switch (aEvent.type) {
case "mouseup":
if (!this.hidden && this._findMode != this.FIND_NORMAL)
this.close();
break;
case "keypress":
this._onBrowserKeypress(aEvent);
break;
}
]]></body>
</method>
<method name="_enableFindButtons">
<parameter name="aEnable"/>
<body><![CDATA[
this.getElement("find-next").disabled =
this.getElement("find-previous").disabled =
this.getElement("highlight").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[
var showMinimalUI = this._findMode != this.FIND_NORMAL;
var nodes = this.getElement("findbar-container").childNodes;
for (var i = 0; i < nodes.length; i++) {
if (nodes[i].className.indexOf("findbar-find-fast") != -1)
continue;
nodes[i].hidden = showMinimalUI;
}
this._updateCaseSensitivity();
if (this._findMode == this.FIND_TYPEAHEAD)
this.getElement("find-label").value = this._fastFindStr;
else if (this._findMode == this.FIND_LINKS)
this.getElement("find-label").value = this._fastFindLinksStr;
else
this.getElement("find-label").value = this._normalFindStr;
]]></body>
</method>
<method name="_updateFoundLink">
<parameter name="res"/>
<body><![CDATA[
var val = this._findField.value;
if (res == this.nsITypeAheadFind.FIND_NOTFOUND || !val) {
this._setFoundLink(null);
this._foundEditable = null;
this._currentWindow = null;
}
else {
this._setFoundLink(this.browser.fastFind.foundLink);
this._foundEditable = this.browser.fastFind.foundEditable;
this._currentWindow = this.browser.fastFind.currentWindow;
}
]]></body>
</method>
<method name="_find">
<parameter name="aValue"/>
<body><![CDATA[
if (!this._dispatchFindEvent(""))
return this.nsITypeAheadFind.FIND_PENDING;
var val = aValue || this._findField.value;
var res = this.nsITypeAheadFind.FIND_NOTFOUND;
// Only search on input if we don't have a last-failed string,
// or if the current search string doesn't start with it.
if (this._findFailedString == null ||
val.indexOf(this._findFailedString) != 0)
{
this._enableFindButtons(val);
if (this.getElement("highlight").checked)
this._setHighlightTimeout();
this._updateCaseSensitivity(val);
var fastFind = this.browser.fastFind;
res = fastFind.find(val, this._findMode == this.FIND_LINKS);
this._updateFoundLink(res);
this._updateStatusUI(res, false);
if (res == this.nsITypeAheadFind.FIND_NOTFOUND)
this._findFailedString = val;
else
this._findFailedString = null;
}
if (this._findMode != this.FIND_NORMAL)
this._setFindCloseTimeout();
if (this._findResetTimeout != -1)
clearTimeout(this._findResetTimeout);
// allow a search to happen on input again after a second has
// expired since the previous input, to allow for dynamic
// content and/or page loading
this._findResetTimeout = setTimeout(function(self) {
self._findFailedString = null;
self._findResetTimeout = -1; },
1000, this);
return res;
]]></body>
</method>
<method name="_flash">
<body><![CDATA[
if (this._flashFindBarCount === undefined)
this._flashFindBarCount = this._initialFlashFindBarCount;
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[
var fastFind = this.browser.fastFind;
var res = fastFind.findAgain(aFindPrevious,
this._findMode == this.FIND_LINKS);
this._updateFoundLink(res);
this._updateStatusUI(res, aFindPrevious);
if (this._findMode != this.FIND_NORMAL && !this.hidden)
this._setFindCloseTimeout();
return res;
]]></body>
</method>
<method name="_updateStatusUI">
<parameter name="res"/>
<parameter name="aFindPrevious"/>
<body><![CDATA[
switch (res) {
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="_getInitialSelection">
<body><![CDATA[
var focusedElement = document.commandDispatcher.focusedElement;
var selText;
if (focusedElement instanceof Components.interfaces.nsIDOMNSEditableElement &&
focusedElement.editor &&
focusedElement.ownerDocument.defaultView.top == this._browser.contentWindow)
{
// The user may have a selection in an input or textarea
selText = focusedElement.editor.selectionController
.getSelection(Components.interfaces.nsISelectionController.SELECTION_NORMAL)
.toString();
}
else {
// Look for any selected text on the actual page
var focusedWindow = document.commandDispatcher.focusedWindow;
if (focusedWindow.top == this._browser.contentWindow)
selText = focusedWindow.getSelection().toString();
}
if (!selText)
return "";
// Process our text to get rid of unwanted characters
if (selText.length > this._selectionMaxLen) {
var pattern = new RegExp("^(?:\\s*.){0," + this._selectionMaxLen + "}");
pattern.test(selText);
selText = RegExp.lastMatch;
}
return selText.replace(/^\s+/, "")
.replace(/\s+$/, "")
.replace(/\s+/g, " ")
.substr(0, this._selectionMaxLen);
]]></body>
</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,
highlightAll: this.getElement("highlight").checked,
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[
var prefsvc =
Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
var userWantsPrefill = true;
this.open(aMode);
if (this._flashFindBar) {
this._flashFindBarTimeout =
setInterval(function(aSelf) { aSelf._flash(); }, 500, this);
prefsvc.setIntPref("accessibility.typeaheadfind.flashBar",
--this._flashFindBar);
}
if (this.prefillWithSelection)
userWantsPrefill =
prefsvc.getBoolPref("accessibility.typeaheadfind.prefillwithselection");
var initialString = (this.prefillWithSelection && userWantsPrefill) ?
this._getInitialSelection() : null;
if (initialString) {
this._findField.value = initialString;
this._enableFindButtons(true);
}
else if (!this._findField.value)
this._enableFindButtons(false);
this._findField.select();
this._findField.focus();
]]></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[
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[
var findString = this._browser.fastFind.searchString || this._findField.value;
if (!findString) {
this.startFind();
return;
}
// We dispatch the findAgain event here instead of in _findAgain since
// if there is a find event handler that prevents the default then
// fastFind.searchString will never get updated which in turn means
// there would never be findAgain events because of the logic below.
if (!this._dispatchFindEvent("again", aFindPrevious))
return;
// user explicitly requested another search, so do it even if we think it'll fail
this._findFailedString = null;
var res;
// Ensure the stored SearchString is in sync with what we want to find
if (this._findField.value != this._browser.fastFind.searchString)
res = this._find(this._findField.value);
else
res = this._findAgain(aFindPrevious);
if (res == this.nsITypeAheadFind.FIND_NOTFOUND) {
if (this.open()) {
if (this._findMode != this.FIND_NORMAL)
this._setFindCloseTimeout();
this._findField.focus();
this._findField.focus();
this._updateStatusUI(res, aFindPrevious);
}
}
]]></body>
</method>
</implementation>
<handlers>
<handler event="keypress" keycode="VK_ESCAPE" phase="capturing" action="this.close();" preventdefault="true"/>
</handlers>
</binding>
</bindings>