toolkit/content/widgets/findbar.xml

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:2652269db539
1 <?xml version="1.0"?>
2
3 <!-- This Source Code Form is subject to the terms of the Mozilla Public
4 - License, v. 2.0. If a copy of the MPL was not distributed with this
5 - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
6
7 <!DOCTYPE bindings [
8 <!ENTITY % findBarDTD SYSTEM "chrome://global/locale/findbar.dtd" >
9 %findBarDTD;
10 ]>
11
12 <bindings id="findbarBindings"
13 xmlns="http://www.mozilla.org/xbl"
14 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
15 xmlns:xbl="http://www.mozilla.org/xbl">
16
17 <!-- Private binding -->
18 <binding id="findbar-textbox"
19 extends="chrome://global/content/bindings/textbox.xml#textbox">
20 <implementation>
21
22 <field name="_findbar">null</field>
23 <property name="findbar" readonly="true">
24 <getter>
25 return this._findbar ?
26 this._findbar : this._findbar = document.getBindingParent(this);
27 </getter>
28 </property>
29
30 <method name="_handleEnter">
31 <parameter name="aEvent"/>
32 <body><![CDATA[
33 if (this.findbar._findMode == this.findbar.FIND_NORMAL) {
34 let findString = this.findbar._findField;
35 if (!findString.value)
36 return;
37 #ifdef XP_MACOSX
38 if (aEvent.metaKey) {
39 #else
40 if (aEvent.ctrlKey) {
41 #endif
42 this.findbar.getElement("highlight").click();
43 return;
44 }
45
46 this.findbar.onFindAgainCommand(aEvent.shiftKey);
47 } else {
48 this.findbar._finishFAYT(aEvent);
49 }
50 ]]></body>
51 </method>
52
53 <method name="_handleTab">
54 <parameter name="aEvent"/>
55 <body><![CDATA[
56 let shouldHandle = !aEvent.altKey && !aEvent.ctrlKey &&
57 !aEvent.metaKey;
58 if (shouldHandle &&
59 this.findbar._findMode != this.findbar.FIND_NORMAL) {
60
61 this.findbar._finishFAYT(aEvent);
62 }
63 ]]></body>
64 </method>
65 </implementation>
66
67 <handlers>
68 <handler event="input"><![CDATA[
69 // We should do nothing during composition. E.g., composing string
70 // before converting may matches a forward word of expected word.
71 // After that, even if user converts the composition string to the
72 // expected word, it may find second or later searching word in the
73 // document.
74 if (this.findbar._isIMEComposing) {
75 return;
76 }
77 this.findbar._find(this.value);
78 ]]></handler>
79
80 <handler event="keypress"><![CDATA[
81 let shouldHandle = !event.altKey && !event.ctrlKey &&
82 !event.metaKey && !event.shiftKey;
83
84 switch (event.keyCode) {
85 case KeyEvent.DOM_VK_RETURN:
86 this._handleEnter(event);
87 break;
88 case KeyEvent.DOM_VK_TAB:
89 this._handleTab(event);
90 break;
91 case KeyEvent.DOM_VK_PAGE_UP:
92 case KeyEvent.DOM_VK_PAGE_DOWN:
93 if (shouldHandle) {
94 this.findbar.browser.finder.keyPress(event);
95 event.preventDefault();
96 }
97 break;
98 case KeyEvent.DOM_VK_UP:
99 case KeyEvent.DOM_VK_DOWN:
100 this.findbar.browser.finder.keyPress(event);
101 event.preventDefault();
102 break;
103 }
104 ]]></handler>
105
106 <handler event="blur"><![CDATA[
107 let findbar = this.findbar;
108 // Note: This code used to remove the selection
109 // if it matched an editable.
110 findbar.browser.finder.enableSelection();
111 ]]></handler>
112
113 #ifdef XP_MACOSX
114 <handler event="focus"><![CDATA[
115 let findbar = this.findbar;
116 findbar._onFindFieldFocus();
117 ]]></handler>
118 #endif
119
120 <handler event="compositionstart"><![CDATA[
121 // Don't close the find toolbar while IME is composing.
122 let findbar = this.findbar;
123 findbar._isIMEComposing = true;
124 if (findbar._quickFindTimeout) {
125 clearTimeout(findbar._quickFindTimeout);
126 findbar._quickFindTimeout = null;
127 }
128 ]]></handler>
129
130 <handler event="compositionend"><![CDATA[
131 let findbar = this.findbar;
132 findbar._isIMEComposing = false;
133 if (findbar._findMode != findbar.FIND_NORMAL)
134 findbar._setFindCloseTimeout();
135 ]]></handler>
136
137 <handler event="dragover"><![CDATA[
138 if (event.dataTransfer.types.contains("text/plain"))
139 event.preventDefault();
140 ]]></handler>
141
142 <handler event="drop"><![CDATA[
143 let value = event.dataTransfer.getData("text/plain");
144 this.value = value;
145 this.findbar._find(value);
146 event.stopPropagation();
147 event.preventDefault();
148 ]]></handler>
149 </handlers>
150 </binding>
151
152 <binding id="findbar"
153 extends="chrome://global/content/bindings/toolbar.xml#toolbar">
154 <resources>
155 <stylesheet src="chrome://global/skin/findBar.css"/>
156 </resources>
157
158 <content hidden="true">
159 <xul:hbox anonid="findbar-container" class="findbar-container" flex="1" align="center">
160 <xul:hbox anonid="findbar-textbox-wrapper" align="stretch">
161 <xul:textbox anonid="findbar-textbox"
162 class="findbar-textbox findbar-find-fast"
163 xbl:inherits="flash"/>
164 <xul:toolbarbutton anonid="find-previous"
165 class="findbar-find-previous tabbable"
166 tooltiptext="&previous.tooltip;"
167 oncommand="onFindAgainCommand(true);"
168 disabled="true"
169 xbl:inherits="accesskey=findpreviousaccesskey"/>
170 <xul:toolbarbutton anonid="find-next"
171 class="findbar-find-next tabbable"
172 tooltiptext="&next.tooltip;"
173 oncommand="onFindAgainCommand(false);"
174 disabled="true"
175 xbl:inherits="accesskey=findnextaccesskey"/>
176 </xul:hbox>
177 <xul:toolbarbutton anonid="highlight"
178 class="findbar-highlight tabbable"
179 label="&highlightAll.label;"
180 accesskey="&highlightAll.accesskey;"
181 tooltiptext="&highlightAll.tooltiptext;"
182 oncommand="toggleHighlight(this.checked);"
183 type="checkbox"
184 xbl:inherits="accesskey=highlightaccesskey"/>
185 <xul:toolbarbutton anonid="find-case-sensitive"
186 class="findbar-case-sensitive tabbable"
187 label="&caseSensitive.label;"
188 accesskey="&caseSensitive.accesskey;"
189 tooltiptext="&caseSensitive.tooltiptext;"
190 oncommand="_setCaseSensitivity(this.checked);"
191 type="checkbox"
192 xbl:inherits="accesskey=matchcaseaccesskey"/>
193 <xul:label anonid="match-case-status" class="findbar-find-fast"/>
194 <xul:image anonid="find-status-icon" class="findbar-find-fast find-status-icon"/>
195 <xul:description anonid="find-status"
196 control="findbar-textbox"
197 class="findbar-find-fast findbar-find-status">
198 <!-- Do not use value, first child is used because it provides a11y with text change events -->
199 </xul:description>
200 </xul:hbox>
201 <xul:toolbarbutton anonid="find-closebutton"
202 class="findbar-closebutton close-icon"
203 tooltiptext="&findCloseButton.tooltip;"
204 oncommand="close();"/>
205 </content>
206
207 <implementation implements="nsIDOMEventListener, nsIEditActionListener">
208 <field name="FIND_NORMAL">0</field>
209 <field name="FIND_TYPEAHEAD">1</field>
210 <field name="FIND_LINKS">2</field>
211
212 <field name="_findMode">0</field>
213
214 <field name="_flashFindBar">0</field>
215 <field name="_initialFlashFindBarCount">6</field>
216
217 <property name="prefillWithSelection"
218 onget="return this.getAttribute('prefillwithselection') != 'false'"
219 onset="this.setAttribute('prefillwithselection', val); return val;"/>
220 <field name="_selectionMaxLen">150</field>
221
222 <method name="getElement">
223 <parameter name="aAnonymousID"/>
224 <body><![CDATA[
225 return document.getAnonymousElementByAttribute(this,
226 "anonid",
227 aAnonymousID)
228 ]]></body>
229 </method>
230
231 <property name="findMode"
232 readonly="true"
233 onget="return this._findMode;"/>
234
235 <property name="canClear" readonly="true">
236 <getter><![CDATA[
237 if (this._findField.value)
238 return true;
239
240 // Watch out for lazy editor init
241 if (this._findField.editor) {
242 let tm = this._findField.editor.transactionManager;
243 return !!(tm.numberOfUndoItems || tm.numberOfRedoItems);
244 }
245 return false;
246 ]]></getter>
247 </property>
248
249 <field name="_browser">null</field>
250 <property name="browser">
251 <getter><![CDATA[
252 if (!this._browser) {
253 this._browser =
254 document.getElementById(this.getAttribute("browserid"));
255 }
256 return this._browser;
257 ]]></getter>
258 <setter><![CDATA[
259 if (this._browser) {
260 this._browser.removeEventListener("keypress", this, false);
261 this._browser.removeEventListener("mouseup", this, false);
262 let finder = this._browser.finder;
263 if (finder)
264 finder.removeResultListener(this);
265 }
266
267 this._browser = val;
268 if (this._browser) {
269 this._browser.addEventListener("keypress", this, false);
270 this._browser.addEventListener("mouseup", this, false);
271 this._browser.finder.addResultListener(this);
272
273 this._findField.value = this._browser._lastSearchString;
274 this.toggleHighlight(this.browser._lastSearchHighlight);
275 }
276 return val;
277 ]]></setter>
278 </property>
279
280 <field name="_observer"><![CDATA[({
281 _self: this,
282
283 QueryInterface: function(aIID) {
284 if (aIID.equals(Components.interfaces.nsIObserver) ||
285 aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
286 aIID.equals(Components.interfaces.nsISupports))
287 return this;
288
289 throw Components.results.NS_ERROR_NO_INTERFACE;
290 },
291
292 observe: function(aSubject, aTopic, aPrefName) {
293 if (aTopic != "nsPref:changed")
294 return;
295
296 let prefsvc =
297 aSubject.QueryInterface(Components.interfaces.nsIPrefBranch);
298
299 switch (aPrefName) {
300 case "accessibility.typeaheadfind":
301 this._self._useTypeAheadFind = prefsvc.getBoolPref(aPrefName);
302 break;
303 case "accessibility.typeaheadfind.linksonly":
304 this._self._typeAheadLinksOnly = prefsvc.getBoolPref(aPrefName);
305 break;
306 case "accessibility.typeaheadfind.casesensitive":
307 this._self._typeAheadCaseSensitive = prefsvc.getIntPref(aPrefName);
308 this._self._updateCaseSensitivity();
309 if (this._self.getElement("highlight").checked)
310 this._self._setHighlightTimeout();
311 break;
312 }
313 }
314 })]]></field>
315
316 <field name="_destroyed">false</field>
317
318 <constructor><![CDATA[
319 // These elements are accessed frequently and are therefore cached
320 this._findField = this.getElement("findbar-textbox");
321 this._findStatusIcon = this.getElement("find-status-icon");
322 this._findStatusDesc = this.getElement("find-status");
323
324 this._foundURL = null;
325
326 let prefsvc =
327 Components.classes["@mozilla.org/preferences-service;1"]
328 .getService(Components.interfaces.nsIPrefBranch);
329
330 this._quickFindTimeoutLength =
331 prefsvc.getIntPref("accessibility.typeaheadfind.timeout");
332 this._flashFindBar =
333 prefsvc.getIntPref("accessibility.typeaheadfind.flashBar");
334
335 prefsvc.addObserver("accessibility.typeaheadfind",
336 this._observer, false);
337 prefsvc.addObserver("accessibility.typeaheadfind.linksonly",
338 this._observer, false);
339 prefsvc.addObserver("accessibility.typeaheadfind.casesensitive",
340 this._observer, false);
341
342 this._useTypeAheadFind =
343 prefsvc.getBoolPref("accessibility.typeaheadfind");
344 this._typeAheadLinksOnly =
345 prefsvc.getBoolPref("accessibility.typeaheadfind.linksonly");
346 this._typeAheadCaseSensitive =
347 prefsvc.getIntPref("accessibility.typeaheadfind.casesensitive");
348
349 // Convenience
350 this.nsITypeAheadFind = Components.interfaces.nsITypeAheadFind;
351 this.nsISelectionController = Components.interfaces.nsISelectionController;
352 this._findSelection = this.nsISelectionController.SELECTION_FIND;
353
354 this._findResetTimeout = -1;
355
356 // Make sure the FAYT keypress listener is attached by initializing the
357 // browser property
358 if (this.getAttribute("browserid"))
359 setTimeout(function(aSelf) { aSelf.browser = aSelf.browser; }, 0, this);
360 ]]></constructor>
361
362 <destructor><![CDATA[
363 this.destroy();
364 ]]></destructor>
365
366 <!-- This is necessary because the destructor isn't called when
367 we are removed from a document that is not destroyed. This
368 needs to be explicitly called in this case -->
369 <method name="destroy">
370 <body><![CDATA[
371 if (this._destroyed)
372 return;
373 this._destroyed = true;
374
375 this.browser = null;
376
377 let prefsvc =
378 Components.classes["@mozilla.org/preferences-service;1"]
379 .getService(Components.interfaces.nsIPrefBranch);
380 prefsvc.removeObserver("accessibility.typeaheadfind",
381 this._observer);
382 prefsvc.removeObserver("accessibility.typeaheadfind.linksonly",
383 this._observer);
384 prefsvc.removeObserver("accessibility.typeaheadfind.casesensitive",
385 this._observer);
386
387 // Clear all timers that might still be running.
388 this._cancelTimers();
389 ]]></body>
390 </method>
391
392 <method name="_cancelTimers">
393 <body><![CDATA[
394 if (this._flashFindBarTimeout) {
395 clearInterval(this._flashFindBarTimeout);
396 this._flashFindBarTimeout = null;
397 }
398 if (this._quickFindTimeout) {
399 clearTimeout(this._quickFindTimeout);
400 this._quickFindTimeout = null;
401 }
402 if (this._highlightTimeout) {
403 clearTimeout(this._highlightTimeout);
404 this._highlightTimeout = null;
405 }
406 if (this._findResetTimeout) {
407 clearTimeout(this._findResetTimeout);
408 this._findResetTimeout = null;
409 }
410 ]]></body>
411 </method>
412
413 <method name="_setFindCloseTimeout">
414 <body><![CDATA[
415 if (this._quickFindTimeout)
416 clearTimeout(this._quickFindTimeout);
417
418 // Don't close the find toolbar while IME is composing OR when the
419 // findbar is already hidden.
420 if (this._isIMEComposing || this.hidden) {
421 this._quickFindTimeout = null;
422 return;
423 }
424
425 this._quickFindTimeout = setTimeout(() => {
426 if (this._findMode != this.FIND_NORMAL)
427 this.close();
428 this._quickFindTimeout = null;
429 }, this._quickFindTimeoutLength);
430 ]]></body>
431 </method>
432
433 <!--
434 - Turns highlight on or off.
435 - @param aHighlight (boolean)
436 - Whether to turn the highlight on or off
437 -->
438 <method name="toggleHighlight">
439 <parameter name="aHighlight"/>
440 <body><![CDATA[
441 if (!this._dispatchFindEvent("highlightallchange"))
442 return;
443
444 let word = this._findField.value;
445 // Bug 429723. Don't attempt to highlight ""
446 if (aHighlight && !word)
447 return;
448
449 this.browser._lastSearchHighlight = aHighlight;
450 this.browser.finder.highlight(aHighlight, word);
451 ]]></body>
452 </method>
453
454 <!--
455 - Updates the case-sensitivity mode of the findbar and its UI.
456 - @param [optional] aString
457 - The string for which case sensitivity might be turned on.
458 - This only used when case-sensitivity is in auto mode,
459 - @see _shouldBeCaseSensitive. The default value for this
460 - parameter is the find-field value.
461 -->
462 <method name="_updateCaseSensitivity">
463 <parameter name="aString"/>
464 <body><![CDATA[
465 let val = aString || this._findField.value;
466
467 let caseSensitive = this._shouldBeCaseSensitive(val);
468 let checkbox = this.getElement("find-case-sensitive");
469 let statusLabel = this.getElement("match-case-status");
470 checkbox.checked = caseSensitive;
471
472 statusLabel.value = caseSensitive ? this._caseSensitiveStr : "";
473
474 // Show the checkbox on the full Find bar in non-auto mode.
475 // Show the label in all other cases.
476 let hideCheckbox = this._findMode != this.FIND_NORMAL ||
477 (this._typeAheadCaseSensitive != 0 &&
478 this._typeAheadCaseSensitive != 1);
479 checkbox.hidden = hideCheckbox;
480 statusLabel.hidden = !hideCheckbox;
481
482 this.browser.finder.caseSensitive = caseSensitive;
483 ]]></body>
484 </method>
485
486 <!--
487 - Sets the findbar case-sensitivity mode
488 - @param aCaseSensitive (boolean)
489 - Whether or not case-sensitivity should be turned on.
490 -->
491 <method name="_setCaseSensitivity">
492 <parameter name="aCaseSensitive"/>
493 <body><![CDATA[
494 let prefsvc =
495 Components.classes["@mozilla.org/preferences-service;1"]
496 .getService(Components.interfaces.nsIPrefBranch);
497
498 // Just set the pref; our observer will change the find bar behavior
499 prefsvc.setIntPref("accessibility.typeaheadfind.casesensitive",
500 aCaseSensitive ? 1 : 0);
501
502 this._dispatchFindEvent("casesensitivitychange");
503 ]]></body>
504 </method>
505
506 <!--
507 - Opens and displays the find bar.
508 -
509 - @param aMode
510 - the find mode to be used, which is either FIND_NORMAL,
511 - FIND_TYPEAHEAD or FIND_LINKS. If not passed, the last
512 - find mode if any or FIND_NORMAL.
513 - @returns true if the find bar wasn't previously open, false otherwise.
514 -->
515 <method name="open">
516 <parameter name="aMode"/>
517 <body><![CDATA[
518 if (aMode != undefined)
519 this._findMode = aMode;
520
521 if (!this._notFoundStr) {
522 let stringsBundle =
523 Components.classes["@mozilla.org/intl/stringbundle;1"]
524 .getService(Components.interfaces.nsIStringBundleService)
525 .createBundle("chrome://global/locale/findbar.properties");
526 this._notFoundStr = stringsBundle.GetStringFromName("NotFound");
527 this._wrappedToTopStr =
528 stringsBundle.GetStringFromName("WrappedToTop");
529 this._wrappedToBottomStr =
530 stringsBundle.GetStringFromName("WrappedToBottom");
531 this._normalFindStr =
532 stringsBundle.GetStringFromName("NormalFind");
533 this._fastFindStr =
534 stringsBundle.GetStringFromName("FastFind");
535 this._fastFindLinksStr =
536 stringsBundle.GetStringFromName("FastFindLinks");
537 this._caseSensitiveStr =
538 stringsBundle.GetStringFromName("CaseSensitive");
539 }
540
541 this._findFailedString = null;
542
543 this._updateFindUI();
544 if (this.hidden) {
545 this.hidden = false;
546
547 this._updateStatusUI(this.nsITypeAheadFind.FIND_FOUND);
548
549 let event = document.createEvent("Events");
550 event.initEvent("findbaropen", true, false);
551 this.dispatchEvent(event);
552
553 return true;
554 }
555 return false;
556 ]]></body>
557 </method>
558
559 <!--
560 - Closes the findbar.
561 -->
562 <method name="close">
563 <body><![CDATA[
564 if (this.hidden)
565 return;
566
567 this.hidden = true;
568
569 this.browser.finder.focusContent();
570 this.browser.finder.enableSelection();
571 this._findField.blur();
572
573 this._cancelTimers();
574
575 this._findFailedString = null;
576 ]]></body>
577 </method>
578
579 <method name="clear">
580 <body><![CDATA[
581 this.browser.finder.removeSelection();
582 this._findField.reset();
583 this.toggleHighlight(false);
584 this._updateStatusUI();
585 this._enableFindButtons(false);
586 ]]></body>
587 </method>
588
589 <method name="_dispatchKeypressEvent">
590 <parameter name="aTarget"/>
591 <parameter name="aEvent"/>
592 <body><![CDATA[
593 if (!aTarget)
594 return;
595
596 let event = document.createEvent("KeyEvents");
597 event.initKeyEvent(aEvent.type, aEvent.bubbles, aEvent.cancelable,
598 aEvent.view, aEvent.ctrlKey, aEvent.altKey,
599 aEvent.shiftKey, aEvent.metaKey, aEvent.keyCode,
600 aEvent.charCode);
601 aTarget.dispatchEvent(event);
602 ]]></body>
603 </method>
604
605 <field name="_xulBrowserWindow">null</field>
606 <method name="_updateStatusUIBar">
607 <parameter name="aFoundURL"/>
608 <body><![CDATA[
609 if (!this._xulBrowserWindow) {
610 try {
611 this._xulBrowserWindow =
612 window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
613 .getInterface(Components.interfaces.nsIWebNavigation)
614 .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
615 .treeOwner
616 .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
617 .getInterface(Components.interfaces.nsIXULWindow)
618 .XULBrowserWindow;
619 }
620 catch(ex) { }
621 if (!this._xulBrowserWindow)
622 return false;
623 }
624
625 // Call this has the same effect like hovering over link,
626 // the browser shows the URL as a tooltip.
627 this._xulBrowserWindow.setOverLink(aFoundURL || "", null);
628 return true;
629 ]]></body>
630 </method>
631
632 <method name="_finishFAYT">
633 <parameter name="aKeypressEvent"/>
634 <body><![CDATA[
635 this.browser.finder.focusContent();
636
637 if (aKeypressEvent)
638 aKeypressEvent.preventDefault();
639
640 this.browser.finder.keyPress(aKeypressEvent);
641
642 this.close();
643 return true;
644 ]]></body>
645 </method>
646
647 <!--
648 - Returns true if |aMimeType| is text-based, or false otherwise.
649 -
650 - @param aMimeType
651 - The MIME type to check.
652 -
653 - if adding types to this function, please see the similar function
654 - in browser/base/content/browser.js
655 -->
656 <method name="_mimeTypeIsTextBased">
657 <parameter name="aMimeType"/>
658 <body><![CDATA[
659 return /^text\/|\+xml$/.test(aMimeType) ||
660 aMimeType == "application/x-javascript" ||
661 aMimeType == "application/javascript" ||
662 aMimeType == "application/json" ||
663 aMimeType == "application/xml";
664 ]]></body>
665 </method>
666
667 <!--
668 - Returns whether FAYT can be used for the given event in
669 - the current content state.
670 -->
671 <method name="_shouldFastFind">
672 <parameter name="aEvent"/>
673 <body><![CDATA[
674 if (aEvent.ctrlKey || aEvent.altKey || aEvent.metaKey ||
675 aEvent.defaultPrevented)
676 return false;
677
678 let {BrowserUtils} = Components.utils.import("resource://gre/modules/BrowserUtils.jsm", {});
679 let [elt, win] = BrowserUtils.getFocusSync(document);
680
681 if (elt) {
682 if (elt instanceof HTMLInputElement && elt.mozIsTextField(false))
683 return false;
684
685 if (elt.isContentEditable)
686 return false;
687
688 if (elt instanceof HTMLTextAreaElement ||
689 elt instanceof HTMLSelectElement ||
690 elt instanceof HTMLObjectElement ||
691 elt instanceof HTMLEmbedElement)
692 return false;
693 }
694
695 if (win && !this._mimeTypeIsTextBased(win.document.contentType))
696 return false;
697
698 // disable FAYT in about:blank to prevent FAYT opening unexpectedly.
699 let url = this.browser.currentURI;
700 if (url.spec == "about:blank")
701 return false;
702
703 // disable FAYT in documents that ask for it to be disabled.
704 if ((url.schemeIs("about") || url.schemeIs("chrome")) &&
705 (win.document.documentElement &&
706 win.document.documentElement.getAttribute("disablefastfind") == "true"))
707 return false;
708
709 if (win) {
710 try {
711 let editingSession = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
712 .getInterface(Components.interfaces.nsIWebNavigation)
713 .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
714 .getInterface(Components.interfaces.nsIEditingSession);
715 if (editingSession.windowIsEditable(win))
716 return false;
717 }
718 catch (e) {
719 // If someone built with composer disabled, we can't get an editing session.
720 }
721 }
722
723 return true;
724 ]]></body>
725 </method>
726
727 <method name="_shouldBeCaseSensitive">
728 <parameter name="aString"/>
729 <body><![CDATA[
730 if (this._typeAheadCaseSensitive == 0)
731 return false;
732 if (this._typeAheadCaseSensitive == 1)
733 return true;
734
735 return aString != aString.toLowerCase();
736 ]]></body>
737 </method>
738
739 <method name="_onBrowserKeypress">
740 <parameter name="aEvent"/>
741 <body><![CDATA[
742 const TAF_LINKS_KEY = "'";
743 const TAF_TEXT_KEY = "/";
744
745 if (!this._shouldFastFind(aEvent))
746 return;
747
748 if (this._findMode != this.FIND_NORMAL && this._quickFindTimeout) {
749 if (!aEvent.charCode)
750 return;
751
752 this._findField.select();
753 this._findField.focus();
754 this._dispatchKeypressEvent(this._findField.inputField, aEvent);
755 aEvent.preventDefault();
756 return;
757 }
758
759 let key = aEvent.charCode ? String.fromCharCode(aEvent.charCode) : null;
760 let manualstartFAYT = (key == TAF_LINKS_KEY || key == TAF_TEXT_KEY);
761 let autostartFAYT = !manualstartFAYT && this._useTypeAheadFind &&
762 key && key != " ";
763 if (manualstartFAYT || autostartFAYT) {
764 let mode = (key == TAF_LINKS_KEY ||
765 (autostartFAYT && this._typeAheadLinksOnly)) ?
766 this.FIND_LINKS : this.FIND_TYPEAHEAD;
767
768 // Clear bar first, so that when openFindBar() calls setCaseSensitivity()
769 // it doesn't get confused by a lingering value
770 this._findField.value = "";
771
772 this.open(mode);
773 this._setFindCloseTimeout();
774 this._findField.select();
775 this._findField.focus();
776
777 if (autostartFAYT)
778 this._dispatchKeypressEvent(this._findField.inputField, aEvent);
779 else
780 this._updateStatusUI(this.nsITypeAheadFind.FIND_FOUND);
781
782 aEvent.preventDefault();
783 }
784 ]]></body>
785 </method>
786
787 <!-- See nsIDOMEventListener -->
788 <method name="handleEvent">
789 <parameter name="aEvent"/>
790 <body><![CDATA[
791 switch (aEvent.type) {
792 case "mouseup":
793 if (!this.hidden && this._findMode != this.FIND_NORMAL)
794 this.close();
795
796 break;
797 case "keypress":
798 this._onBrowserKeypress(aEvent);
799 break;
800 }
801 ]]></body>
802 </method>
803
804 <method name="_enableFindButtons">
805 <parameter name="aEnable"/>
806 <body><![CDATA[
807 this.getElement("find-next").disabled =
808 this.getElement("find-previous").disabled = !aEnable;
809 ]]></body>
810 </method>
811
812 <!--
813 - Determines whether minimalist or general-purpose search UI is to be
814 - displayed when the find bar is activated.
815 -->
816 <method name="_updateFindUI">
817 <body><![CDATA[
818 let showMinimalUI = this._findMode != this.FIND_NORMAL;
819
820 let nodes = this.getElement("findbar-container").childNodes;
821 let wrapper = this.getElement("findbar-textbox-wrapper");
822 for (let node of nodes) {
823 if (node == wrapper)
824 continue;
825 node.hidden = showMinimalUI;
826 }
827 this.getElement("find-next").hidden =
828 this.getElement("find-previous").hidden = showMinimalUI;
829 this._updateCaseSensitivity();
830
831 if (showMinimalUI)
832 this._findField.classList.add("minimal");
833 else
834 this._findField.classList.remove("minimal");
835
836 if (this._findMode == this.FIND_TYPEAHEAD)
837 this._findField.placeholder = this._fastFindStr;
838 else if (this._findMode == this.FIND_LINKS)
839 this._findField.placeholder = this._fastFindLinksStr;
840 else
841 this._findField.placeholder = this._normalFindStr;
842 ]]></body>
843 </method>
844
845 <method name="_find">
846 <parameter name="aValue"/>
847 <body><![CDATA[
848 if (!this._dispatchFindEvent(""))
849 return;
850
851 let val = aValue || this._findField.value;
852
853 // We have to carry around an explicit version of this,
854 // because finder.searchString doesn't update on failed
855 // searches.
856 this.browser._lastSearchString = val;
857
858 // Only search on input if we don't have a last-failed string,
859 // or if the current search string doesn't start with it.
860 if (!this._findFailedString ||
861 !val.startsWith(this._findFailedString))
862 {
863 this._enableFindButtons(val);
864 if (this.getElement("highlight").checked)
865 this._setHighlightTimeout();
866
867 this._updateCaseSensitivity(val);
868
869 this.browser.finder.fastFind(val, this._findMode == this.FIND_LINKS,
870 this._findMode != this.FIND_NORMAL);
871 }
872
873 if (this._findMode != this.FIND_NORMAL)
874 this._setFindCloseTimeout();
875
876 if (this._findResetTimeout != -1)
877 clearTimeout(this._findResetTimeout);
878
879 // allow a search to happen on input again after a second has
880 // expired since the previous input, to allow for dynamic
881 // content and/or page loading
882 this._findResetTimeout = setTimeout(() => {
883 this._findFailedString = null;
884 this._findResetTimeout = -1;
885 }, 1000);
886 ]]></body>
887 </method>
888
889 <method name="_flash">
890 <body><![CDATA[
891 if (this._flashFindBarCount === undefined)
892 this._flashFindBarCount = this._initialFlashFindBarCount;
893
894 if (this._flashFindBarCount-- == 0) {
895 clearInterval(this._flashFindBarTimeout);
896 this.removeAttribute("flash");
897 this._flashFindBarCount = 6;
898 return;
899 }
900
901 this.setAttribute("flash",
902 (this._flashFindBarCount % 2 == 0) ?
903 "false" : "true");
904 ]]></body>
905 </method>
906
907 <method name="_setHighlightTimeout">
908 <body><![CDATA[
909 if (this._highlightTimeout)
910 clearTimeout(this._highlightTimeout);
911 this._highlightTimeout =
912 setTimeout(function(aSelf) {
913 aSelf.toggleHighlight(false);
914 aSelf.toggleHighlight(true);
915 }, 500, this);
916 ]]></body>
917 </method>
918
919 <method name="_findAgain">
920 <parameter name="aFindPrevious"/>
921 <body><![CDATA[
922 this.browser.finder.findAgain(aFindPrevious,
923 this._findMode == this.FIND_LINKS,
924 this._findMode != this.FIND_NORMAL);
925 ]]></body>
926 </method>
927
928 <method name="_updateStatusUI">
929 <parameter name="res"/>
930 <parameter name="aFindPrevious"/>
931 <body><![CDATA[
932 switch (res) {
933 case this.nsITypeAheadFind.FIND_WRAPPED:
934 this._findStatusIcon.setAttribute("status", "wrapped");
935 this._findStatusDesc.textContent =
936 aFindPrevious ? this._wrappedToBottomStr : this._wrappedToTopStr;
937 this._findField.removeAttribute("status");
938 break;
939 case this.nsITypeAheadFind.FIND_NOTFOUND:
940 this._findStatusIcon.setAttribute("status", "notfound");
941 this._findStatusDesc.textContent = this._notFoundStr;
942 this._findField.setAttribute("status", "notfound");
943 break;
944 case this.nsITypeAheadFind.FIND_PENDING:
945 this._findStatusIcon.setAttribute("status", "pending");
946 this._findStatusDesc.textContent = "";
947 this._findField.removeAttribute("status");
948 break;
949 case this.nsITypeAheadFind.FIND_FOUND:
950 default:
951 this._findStatusIcon.removeAttribute("status");
952 this._findStatusDesc.textContent = "";
953 this._findField.removeAttribute("status");
954 break;
955 }
956 ]]></body>
957 </method>
958
959 <method name="updateControlState">
960 <parameter name="aResult"/>
961 <parameter name="aFindPrevious"/>
962 <body><![CDATA[
963 this._updateStatusUI(aResult, aFindPrevious);
964 this._enableFindButtons(aResult !== this.nsITypeAheadFind.FIND_NOTFOUND);
965 ]]></body>
966 </method>
967
968 <method name="_getInitialSelection">
969 <body><![CDATA[
970 let focusedElement = document.commandDispatcher.focusedElement;
971 let selText;
972
973 if (focusedElement instanceof Components.interfaces.nsIDOMNSEditableElement &&
974 focusedElement.editor &&
975 focusedElement.ownerDocument.defaultView.top == this._browser.contentWindow)
976 {
977 // The user may have a selection in an input or textarea
978 selText = focusedElement.editor.selectionController
979 .getSelection(Components.interfaces.nsISelectionController.SELECTION_NORMAL)
980 .toString();
981 }
982 else {
983 // Look for any selected text on the actual page
984 let focusedWindow = document.commandDispatcher.focusedWindow;
985 if (focusedWindow.top == this._browser.contentWindow)
986 selText = focusedWindow.getSelection().toString();
987 }
988
989 if (!selText)
990 return "";
991
992 // Process our text to get rid of unwanted characters
993 if (selText.length > this._selectionMaxLen) {
994 let pattern = new RegExp("^(?:\\s*.){0," + this._selectionMaxLen + "}");
995 pattern.test(selText);
996 selText = RegExp.lastMatch;
997 }
998 return selText.replace(/^\s+/, "")
999 .replace(/\s+$/, "")
1000 .replace(/\s+/g, " ")
1001 .substr(0, this._selectionMaxLen);
1002 ]]></body>
1003 </method>
1004
1005 <method name="_dispatchFindEvent">
1006 <parameter name="aType"/>
1007 <parameter name="aFindPrevious"/>
1008 <body><![CDATA[
1009 let event = document.createEvent("CustomEvent");
1010 event.initCustomEvent("find" + aType, true, true, {
1011 query: this._findField.value,
1012 caseSensitive: !!this._typeAheadCaseSensitive,
1013 highlightAll: this.getElement("highlight").checked,
1014 findPrevious: aFindPrevious
1015 });
1016 return this.dispatchEvent(event);
1017 ]]></body>
1018 </method>
1019
1020
1021 <!--
1022 - Opens the findbar, focuses the findfield and selects its contents.
1023 - Also flashes the findbar the first time it's used.
1024 - @param aMode
1025 - the find mode to be used, which is either FIND_NORMAL,
1026 - FIND_TYPEAHEAD or FIND_LINKS. If not passed, the last
1027 - find mode if any or FIND_NORMAL.
1028 -->
1029 <method name="startFind">
1030 <parameter name="aMode"/>
1031 <body><![CDATA[
1032 let prefsvc =
1033 Components.classes["@mozilla.org/preferences-service;1"]
1034 .getService(Components.interfaces.nsIPrefBranch);
1035 let userWantsPrefill = true;
1036 this.open(aMode);
1037
1038 if (this._flashFindBar) {
1039 this._flashFindBarTimeout = setInterval(() => this._flash(), 500);
1040 prefsvc.setIntPref("accessibility.typeaheadfind.flashBar",
1041 --this._flashFindBar);
1042 }
1043
1044 if (this.prefillWithSelection)
1045 userWantsPrefill =
1046 prefsvc.getBoolPref("accessibility.typeaheadfind.prefillwithselection");
1047
1048 let initialString = null;
1049 if (this.prefillWithSelection && userWantsPrefill)
1050 initialString = this._getInitialSelection();
1051 #ifdef XP_MACOSX
1052 if (!initialString) {
1053 let clipboardSearchString = this.browser.finder.clipboardSearchString;
1054 if (clipboardSearchString)
1055 initialString = clipboardSearchString;
1056 }
1057 #endif
1058
1059 if (initialString)
1060 this._findField.value = initialString;
1061
1062 this._enableFindButtons(!!this._findField.value);
1063
1064 this._findField.select();
1065 this._findField.focus();
1066 ]]></body>
1067 </method>
1068
1069 <!--
1070 - Convenient alias to startFind(gFindBar.FIND_NORMAL);
1071 -
1072 - You should generally map the window's find command to this method.
1073 - e.g. <command name="cmd_find" oncommand="gFindBar.onFindCommand();"/>
1074 -->
1075 <method name="onFindCommand">
1076 <body><![CDATA[
1077 this.startFind(this.FIND_NORMAL);
1078 ]]></body>
1079 </method>
1080
1081 <!--
1082 - Stub for find-next and find-previous commands
1083 - @param aFindPrevious
1084 - true for find-previous, false otherwise.
1085 -->
1086 <method name="onFindAgainCommand">
1087 <parameter name="aFindPrevious"/>
1088 <body><![CDATA[
1089 let findString = this._browser.finder.searchString || this._findField.value;
1090 if (!findString) {
1091 this.startFind();
1092 return;
1093 }
1094
1095 // We dispatch the findAgain event here instead of in _findAgain since
1096 // if there is a find event handler that prevents the default then
1097 // finder.searchString will never get updated which in turn means
1098 // there would never be findAgain events because of the logic below.
1099 if (!this._dispatchFindEvent("again", aFindPrevious))
1100 return;
1101
1102 // user explicitly requested another search, so do it even if we think it'll fail
1103 this._findFailedString = null;
1104
1105 // Ensure the stored SearchString is in sync with what we want to find
1106 if (this._findField.value != this._browser.finder.searchString)
1107 this._find(this._findField.value);
1108 else
1109 this._findAgain(aFindPrevious);
1110
1111 ]]></body>
1112 </method>
1113
1114 #ifdef XP_MACOSX
1115 <!--
1116 - Fetches the currently selected text and sets that as the text to search
1117 - next. This is a MacOS specific feature.
1118 -->
1119 <method name="onFindSelectionCommand">
1120 <body><![CDATA[
1121 let searchString = this.browser.finder.setSearchStringToSelection();
1122 if (searchString)
1123 this._findField.value = searchString;
1124 ]]></body>
1125 </method>
1126
1127 <method name="_onFindFieldFocus">
1128 <body><![CDATA[
1129 let prefsvc =
1130 Components.classes["@mozilla.org/preferences-service;1"]
1131 .getService(Components.interfaces.nsIPrefBranch);
1132 const kPref = "accessibility.typeaheadfind.prefillwithselection";
1133 if (this.prefillWithSelection && prefsvc.getBoolPref(kPref))
1134 return;
1135
1136 let clipboardSearchString = this._browser.finder.clipboardSearchString;
1137 if (clipboardSearchString && this._findField.value != clipboardSearchString) {
1138 this._findField.value = clipboardSearchString;
1139 // Changing the search string makes the previous status invalid, so
1140 // we better clear it here.
1141 this._updateStatusUI();
1142 }
1143 ]]></body>
1144 </method>
1145 #endif
1146
1147 <!--
1148 - This handles all the result changes for both
1149 - type-ahead-find and highlighting.
1150 - @param aResult
1151 - One of the nsITypeAheadFind.FIND_* constants
1152 - indicating the result of a search operation.
1153 - @param aFindBackwards
1154 - If the search was done from the bottom to
1155 - the top. This is used for right error messages
1156 - when reaching "the end of the page".
1157 - @param aLinkURL
1158 - When a link matched then its URK. Always null
1159 - when not in FIND_LINKS mode.
1160 -->
1161 <method name="onFindResult">
1162 <parameter name="aData"/>
1163 <body><![CDATA[
1164 if (aData.storeResult && this._findField.value != this.browser.finder.searchString)
1165 this._findField.value = this.browser.finder.searchString;
1166 this._updateStatusUI(aData.result, aData.findBackwards);
1167 this._updateStatusUIBar(aData.linkURL);
1168
1169 if (aData.result == this.nsITypeAheadFind.FIND_NOTFOUND)
1170 this._findFailedString = aData.searchString;
1171 else
1172 this._findFailedString = null;
1173
1174 if (this._findMode != this.FIND_NORMAL)
1175 this._setFindCloseTimeout();
1176 ]]></body>
1177 </method>
1178
1179 <!--
1180 - This handler may cancel a request to focus content by returning |false|
1181 - explicitly.
1182 -->
1183 <method name="shouldFocusContent">
1184 <body><![CDATA[
1185 const fm = Components.classes["@mozilla.org/focus-manager;1"]
1186 .getService(Components.interfaces.nsIFocusManager);
1187 if (fm.focusedWindow != window)
1188 return false;
1189
1190 let focusedElement = fm.focusedElement;
1191 if (!focusedElement)
1192 return false;
1193
1194 let bindingParent = document.getBindingParent(focusedElement);
1195 if (bindingParent != this && bindingParent != this._findField)
1196 return false;
1197
1198 return true;
1199 ]]></body>
1200 </method>
1201
1202 </implementation>
1203
1204 <handlers>
1205 <!--
1206 - We have to guard against `this.close` being |null| due to an unknown
1207 - issue, which is tracked in bug 957999.
1208 -->
1209 <handler event="keypress" keycode="VK_ESCAPE" phase="capturing"
1210 action="if (this.close) this.close();" preventdefault="true"/>
1211 </handlers>
1212 </binding>
1213 </bindings>

mercurial