Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 <?xml version="1.0"?>
2 # -*- Mode: HTML -*-
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/.
7 <!DOCTYPE bindings [
8 <!ENTITY % searchBarDTD SYSTEM "chrome://browser/locale/searchbar.dtd" >
9 %searchBarDTD;
10 <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
11 %browserDTD;
12 ]>
14 <bindings id="SearchBindings"
15 xmlns="http://www.mozilla.org/xbl"
16 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
17 xmlns:xbl="http://www.mozilla.org/xbl">
19 <binding id="searchbar">
20 <resources>
21 <stylesheet src="chrome://browser/content/search/searchbarBindings.css"/>
22 <stylesheet src="chrome://browser/skin/searchbar.css"/>
23 </resources>
24 <content>
25 <xul:stringbundle src="chrome://browser/locale/search.properties"
26 anonid="searchbar-stringbundle"/>
27 <!--
28 There is a dependency between "maxrows" attribute and
29 "SuggestAutoComplete._historyLimit" (nsSearchSuggestions.js). Changing
30 one of them requires changing the other one.
31 -->
32 <xul:textbox class="searchbar-textbox"
33 anonid="searchbar-textbox"
34 type="autocomplete"
35 flex="1"
36 autocompletepopup="PopupAutoComplete"
37 autocompletesearch="search-autocomplete"
38 autocompletesearchparam="searchbar-history"
39 timeout="250"
40 maxrows="10"
41 completeselectedindex="true"
42 showcommentcolumn="true"
43 tabscrolling="true"
44 xbl:inherits="disabled,disableautocomplete,searchengine,src,newlines">
45 <!--
46 Empty <box> to properly position the icon within the autocomplete
47 binding's anonymous children (the autocomplete binding positions <box>
48 children differently)
49 -->
50 <xul:box>
51 <xul:button class="searchbar-engine-button"
52 type="menu"
53 anonid="searchbar-engine-button">
54 <xul:image class="searchbar-engine-image" xbl:inherits="src"/>
55 <xul:image class="searchbar-dropmarker-image"/>
56 <xul:menupopup class="searchbar-popup"
57 anonid="searchbar-popup">
58 <xul:menuseparator/>
59 <xul:menuitem class="open-engine-manager"
60 anonid="open-engine-manager"
61 label="&cmd_engineManager.label;"
62 oncommand="openManager(event);"/>
63 </xul:menupopup>
64 </xul:button>
65 </xul:box>
66 <xul:hbox class="search-go-container">
67 <xul:image class="search-go-button"
68 anonid="search-go-button"
69 onclick="handleSearchCommand(event);"
70 tooltiptext="&searchEndCap.label;"/>
71 </xul:hbox>
72 </xul:textbox>
73 </content>
75 <implementation implements="nsIObserver">
76 <constructor><![CDATA[
77 if (this.parentNode.parentNode.localName == "toolbarpaletteitem")
78 return;
79 // Make sure we rebuild the popup in onpopupshowing
80 this._needToBuildPopup = true;
82 var os =
83 Components.classes["@mozilla.org/observer-service;1"]
84 .getService(Components.interfaces.nsIObserverService);
85 os.addObserver(this, "browser-search-engine-modified", false);
87 this._initialized = true;
89 this.searchService.init((function search_init_cb(aStatus) {
90 // Bail out if the binding's been destroyed
91 if (!this._initialized)
92 return;
94 if (Components.isSuccessCode(aStatus)) {
95 // Refresh the display (updating icon, etc)
96 this.updateDisplay();
97 } else {
98 Components.utils.reportError("Cannot initialize search service, bailing out: " + aStatus);
99 }
100 }).bind(this));
101 ]]></constructor>
103 <destructor><![CDATA[
104 if (this._initialized) {
105 this._initialized = false;
107 var os = Components.classes["@mozilla.org/observer-service;1"]
108 .getService(Components.interfaces.nsIObserverService);
109 os.removeObserver(this, "browser-search-engine-modified");
110 }
112 // Make sure to break the cycle from _textbox to us. Otherwise we leak
113 // the world. But make sure it's actually pointing to us.
114 if (this._textbox.mController.input == this)
115 this._textbox.mController.input = null;
116 ]]></destructor>
118 <field name="_stringBundle">document.getAnonymousElementByAttribute(this,
119 "anonid", "searchbar-stringbundle");</field>
120 <field name="_textbox">document.getAnonymousElementByAttribute(this,
121 "anonid", "searchbar-textbox");</field>
122 <field name="_popup">document.getAnonymousElementByAttribute(this,
123 "anonid", "searchbar-popup");</field>
124 <field name="_ss">null</field>
125 <field name="_engines">null</field>
126 <field name="FormHistory" readonly="true">
127 (Components.utils.import("resource://gre/modules/FormHistory.jsm", {})).FormHistory;
128 </field>
130 <property name="engines" readonly="true">
131 <getter><![CDATA[
132 if (!this._engines)
133 this._engines = this.searchService.getVisibleEngines();
134 return this._engines;
135 ]]></getter>
136 </property>
138 <field name="searchButton">document.getAnonymousElementByAttribute(this,
139 "anonid", "searchbar-engine-button");</field>
141 <property name="currentEngine">
142 <setter><![CDATA[
143 let ss = this.searchService;
144 ss.defaultEngine = ss.currentEngine = val;
145 return val;
146 ]]></setter>
147 <getter><![CDATA[
148 var currentEngine = this.searchService.currentEngine;
149 // Return a dummy engine if there is no currentEngine
150 return currentEngine || {name: "", uri: null};
151 ]]></getter>
152 </property>
154 <!-- textbox is used by sanitize.js to clear the undo history when
155 clearing form information. -->
156 <property name="textbox" readonly="true"
157 onget="return this._textbox;"/>
159 <property name="searchService" readonly="true">
160 <getter><![CDATA[
161 if (!this._ss) {
162 const nsIBSS = Components.interfaces.nsIBrowserSearchService;
163 this._ss =
164 Components.classes["@mozilla.org/browser/search-service;1"]
165 .getService(nsIBSS);
166 }
167 return this._ss;
168 ]]></getter>
169 </property>
171 <property name="value" onget="return this._textbox.value;"
172 onset="return this._textbox.value = val;"/>
174 <method name="focus">
175 <body><![CDATA[
176 this._textbox.focus();
177 ]]></body>
178 </method>
180 <method name="select">
181 <body><![CDATA[
182 this._textbox.select();
183 ]]></body>
184 </method>
186 <method name="observe">
187 <parameter name="aEngine"/>
188 <parameter name="aTopic"/>
189 <parameter name="aVerb"/>
190 <body><![CDATA[
191 if (aTopic == "browser-search-engine-modified") {
192 switch (aVerb) {
193 case "engine-removed":
194 this.offerNewEngine(aEngine);
195 break;
196 case "engine-added":
197 this.hideNewEngine(aEngine);
198 break;
199 case "engine-current":
200 // The current engine was changed. Rebuilding the menu appears to
201 // confuse its idea of whether it should be open when it's just
202 // been clicked, so we force it to close now.
203 this._popup.hidePopup();
204 break;
205 case "engine-changed":
206 // An engine was removed (or hidden) or added, or an icon was
207 // changed. Do nothing special.
208 }
210 // Make sure the engine list is refetched next time it's needed
211 this._engines = null;
213 // Rebuild the popup and update the display after any modification.
214 this.rebuildPopup();
215 this.updateDisplay();
216 }
217 ]]></body>
218 </method>
220 <!-- There are two seaprate lists of search engines, whose uses intersect
221 in this file. The search service (nsIBrowserSearchService and
222 nsSearchService.js) maintains a list of Engine objects which is used to
223 populate the searchbox list of available engines and to perform queries.
224 That list is accessed here via this.SearchService, and it's that sort of
225 Engine that is passed to this binding's observer as aEngine.
227 In addition, browser.js fills two lists of autodetected search engines
228 (browser.engines and browser.hiddenEngines) as properties of
229 mCurrentBrowser. Those lists contain unnamed JS objects of the form
230 { uri:, title:, icon: }, and that's what the searchbar uses to determine
231 whether to show any "Add <EngineName>" menu items in the drop-down.
233 The two types of engines are currently related by their identifying
234 titles (the Engine object's 'name'), although that may change; see bug
235 335102. -->
237 <!-- If the engine that was just removed from the searchbox list was
238 autodetected on this page, move it to each browser's active list so it
239 will be offered to be added again. -->
240 <method name="offerNewEngine">
241 <parameter name="aEngine"/>
242 <body><![CDATA[
243 var allbrowsers = getBrowser().mPanelContainer.childNodes;
244 for (var tab = 0; tab < allbrowsers.length; tab++) {
245 var browser = getBrowser().getBrowserAtIndex(tab);
246 if (browser.hiddenEngines) {
247 // XXX This will need to be changed when engines are identified by
248 // URL rather than title; see bug 335102.
249 var removeTitle = aEngine.wrappedJSObject.name;
250 for (var i = 0; i < browser.hiddenEngines.length; i++) {
251 if (browser.hiddenEngines[i].title == removeTitle) {
252 if (!browser.engines)
253 browser.engines = [];
254 browser.engines.push(browser.hiddenEngines[i]);
255 browser.hiddenEngines.splice(i, 1);
256 break;
257 }
258 }
259 }
260 }
261 ]]></body>
262 </method>
264 <!-- If the engine that was just added to the searchbox list was
265 autodetected on this page, move it to each browser's hidden list so it is
266 no longer offered to be added. -->
267 <method name="hideNewEngine">
268 <parameter name="aEngine"/>
269 <body><![CDATA[
270 var allbrowsers = getBrowser().mPanelContainer.childNodes;
271 for (var tab = 0; tab < allbrowsers.length; tab++) {
272 var browser = getBrowser().getBrowserAtIndex(tab);
273 if (browser.engines) {
274 // XXX This will need to be changed when engines are identified by
275 // URL rather than title; see bug 335102.
276 var removeTitle = aEngine.wrappedJSObject.name;
277 for (var i = 0; i < browser.engines.length; i++) {
278 if (browser.engines[i].title == removeTitle) {
279 if (!browser.hiddenEngines)
280 browser.hiddenEngines = [];
281 browser.hiddenEngines.push(browser.engines[i]);
282 browser.engines.splice(i, 1);
283 break;
284 }
285 }
286 }
287 }
288 ]]></body>
289 </method>
291 <method name="setIcon">
292 <parameter name="element"/>
293 <parameter name="uri"/>
294 <body><![CDATA[
295 if (uri) {
296 let size = Math.round(16 * window.devicePixelRatio);
297 if (!uri.contains("#"))
298 uri += "#-moz-resolution=" + size + "," + size;
299 }
300 element.setAttribute("src", uri);
301 ]]></body>
302 </method>
304 <method name="updateDisplay">
305 <body><![CDATA[
306 var uri = this.currentEngine.iconURI;
307 this.setIcon(this, uri ? uri.spec : "");
309 var name = this.currentEngine.name;
310 var text = this._stringBundle.getFormattedString("searchtip", [name]);
311 this._textbox.placeholder = name;
312 this._textbox.label = text;
313 this._textbox.tooltipText = text;
314 ]]></body>
315 </method>
317 <!-- Rebuilds the dynamic portion of the popup menu (i.e., the menu items
318 for new search engines that can be added to the available list). This
319 is called each time the popup is shown.
320 -->
321 <method name="rebuildPopupDynamic">
322 <body><![CDATA[
323 // We might not have added the main popup items yet, do that first
324 // if needed.
325 if (this._needToBuildPopup)
326 this.rebuildPopup();
328 var popup = this._popup;
329 // Clear any addengine menuitems, including addengine-item entries and
330 // the addengine-separator. Work backward to avoid invalidating the
331 // indexes as items are removed.
332 var items = popup.childNodes;
333 for (var i = items.length - 1; i >= 0; i--) {
334 if (items[i].classList.contains("addengine-item") ||
335 items[i].classList.contains("addengine-separator"))
336 popup.removeChild(items[i]);
337 }
339 var addengines = getBrowser().mCurrentBrowser.engines;
340 if (addengines && addengines.length > 0) {
341 const kXULNS =
342 "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
344 // Find the (first) separator in the remaining menu, or the first item
345 // if no separators are present.
346 var insertLocation = popup.firstChild;
347 while (insertLocation.nextSibling &&
348 insertLocation.localName != "menuseparator") {
349 insertLocation = insertLocation.nextSibling;
350 }
351 if (insertLocation.localName != "menuseparator")
352 insertLocation = popup.firstChild;
354 var separator = document.createElementNS(kXULNS, "menuseparator");
355 separator.setAttribute("class", "addengine-separator");
356 popup.insertBefore(separator, insertLocation);
358 // Insert the "add this engine" items.
359 for (var i = 0; i < addengines.length; i++) {
360 var menuitem = document.createElement("menuitem");
361 var engineInfo = addengines[i];
362 var labelStr =
363 this._stringBundle.getFormattedString("cmd_addFoundEngine",
364 [engineInfo.title]);
365 menuitem = document.createElementNS(kXULNS, "menuitem");
366 menuitem.setAttribute("class", "menuitem-iconic addengine-item");
367 menuitem.setAttribute("label", labelStr);
368 menuitem.setAttribute("tooltiptext", engineInfo.uri);
369 menuitem.setAttribute("uri", engineInfo.uri);
370 if (engineInfo.icon)
371 this.setIcon(menuitem, engineInfo.icon);
372 menuitem.setAttribute("title", engineInfo.title);
373 popup.insertBefore(menuitem, insertLocation);
374 }
375 }
376 ]]></body>
377 </method>
379 <!-- Rebuilds the list of visible search engines in the menu. Does not remove
380 or update any dynamic entries (i.e., "Add this engine" items) nor the
381 Manage Engines item. This is called by the observer when the list of
382 visible engines, or the currently selected engine, has changed.
383 -->
384 <method name="rebuildPopup">
385 <body><![CDATA[
386 var popup = this._popup;
388 // Clear the popup, down to the first separator
389 while (popup.firstChild && popup.firstChild.localName != "menuseparator")
390 popup.removeChild(popup.firstChild);
392 const kXULNS =
393 "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
395 var engines = this.engines;
396 for (var i = engines.length - 1; i >= 0; --i) {
397 var menuitem = document.createElementNS(kXULNS, "menuitem");
398 var name = engines[i].name;
399 menuitem.setAttribute("label", name);
400 menuitem.setAttribute("id", name);
401 menuitem.setAttribute("class", "menuitem-iconic searchbar-engine-menuitem menuitem-with-favicon");
402 // Since this menu is rebuilt by the observer method whenever a new
403 // engine is selected, the "selected" attribute does not need to be
404 // explicitly cleared anywhere.
405 if (engines[i] == this.currentEngine)
406 menuitem.setAttribute("selected", "true");
407 var tooltip = this._stringBundle.getFormattedString("searchtip", [name]);
408 menuitem.setAttribute("tooltiptext", tooltip);
409 if (engines[i].iconURI)
410 this.setIcon(menuitem, engines[i].iconURI.spec);
411 popup.insertBefore(menuitem, popup.firstChild);
412 menuitem.engine = engines[i];
413 }
415 this._needToBuildPopup = false;
416 ]]></body>
417 </method>
419 <method name="openManager">
420 <parameter name="aEvent"/>
421 <body><![CDATA[
422 var wm =
423 Components.classes["@mozilla.org/appshell/window-mediator;1"]
424 .getService(Components.interfaces.nsIWindowMediator);
426 var window = wm.getMostRecentWindow("Browser:SearchManager");
427 if (window)
428 window.focus()
429 else {
430 setTimeout(function () {
431 openDialog("chrome://browser/content/search/engineManager.xul",
432 "_blank", "chrome,dialog,modal,centerscreen,resizable");
433 }, 0);
434 }
435 ]]></body>
436 </method>
438 <method name="selectEngine">
439 <parameter name="aEvent"/>
440 <parameter name="isNextEngine"/>
441 <body><![CDATA[
442 // Find the new index
443 var newIndex = this.engines.indexOf(this.currentEngine);
444 newIndex += isNextEngine ? 1 : -1;
446 if (newIndex >= 0 && newIndex < this.engines.length) {
447 this.currentEngine = this.engines[newIndex];
448 }
450 aEvent.preventDefault();
451 aEvent.stopPropagation();
452 ]]></body>
453 </method>
455 <method name="handleSearchCommand">
456 <parameter name="aEvent"/>
457 <body><![CDATA[
458 var textBox = this._textbox;
459 var textValue = textBox.value;
461 var where = "current";
462 if (aEvent && aEvent.originalTarget.getAttribute("anonid") == "search-go-button") {
463 if (aEvent.button == 2)
464 return;
465 where = whereToOpenLink(aEvent, false, true);
466 }
467 else {
468 var newTabPref = textBox._prefBranch.getBoolPref("browser.search.openintab");
469 if ((aEvent && aEvent.altKey) ^ newTabPref)
470 where = "tab";
471 }
473 this.doSearch(textValue, where);
474 ]]></body>
475 </method>
477 <method name="doSearch">
478 <parameter name="aData"/>
479 <parameter name="aWhere"/>
480 <body><![CDATA[
481 var textBox = this._textbox;
483 // Save the current value in the form history
484 if (aData && !PrivateBrowsingUtils.isWindowPrivate(window)) {
485 this.FormHistory.update(
486 { op : "bump",
487 fieldname : textBox.getAttribute("autocompletesearchparam"),
488 value : aData },
489 { handleError : function(aError) {
490 Components.utils.reportError("Saving search to form history failed: " + aError.message);
491 }});
492 }
494 // null parameter below specifies HTML response for search
495 var submission = this.currentEngine.getSubmission(aData, null, "searchbar");
496 BrowserSearch.recordSearchInHealthReport(this.currentEngine, "searchbar");
497 openUILinkIn(submission.uri.spec, aWhere, null, submission.postData);
498 ]]></body>
499 </method>
500 </implementation>
502 <handlers>
503 <handler event="command"><![CDATA[
504 const target = event.originalTarget;
505 if (target.engine) {
506 this.currentEngine = target.engine;
507 } else if (target.classList.contains("addengine-item")) {
508 var searchService =
509 Components.classes["@mozilla.org/browser/search-service;1"]
510 .getService(Components.interfaces.nsIBrowserSearchService);
511 // We only detect OpenSearch files
512 var type = Components.interfaces.nsISearchEngine.DATA_XML;
513 // Select the installed engine if the installation succeeds
514 var installCallback = {
515 onSuccess: engine => this.currentEngine = engine
516 }
517 searchService.addEngine(target.getAttribute("uri"), type,
518 target.getAttribute("src"), false,
519 installCallback);
520 }
521 else
522 return;
524 this.focus();
525 this.select();
526 ]]></handler>
528 <handler event="popupshowing" action="this.rebuildPopupDynamic();"/>
530 <handler event="DOMMouseScroll"
531 phase="capturing"
532 modifiers="accel"
533 action="this.selectEngine(event, (event.detail > 0));"/>
535 <handler event="focus">
536 <![CDATA[
537 // Speculatively connect to the current engine's search URI (and
538 // suggest URI, if different) to reduce request latency
540 const SUGGEST_TYPE = "application/x-suggestions+json";
541 var engine = this.currentEngine;
542 var connector =
543 Services.io.QueryInterface(Components.interfaces.nsISpeculativeConnect);
544 var searchURI = engine.getSubmission("dummy", null, "searchbar").uri;
545 let callbacks = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
546 .getInterface(Components.interfaces.nsIWebNavigation)
547 .QueryInterface(Components.interfaces.nsILoadContext);
548 connector.speculativeConnect(searchURI, callbacks);
550 if (engine.supportsResponseType(SUGGEST_TYPE)) {
551 var suggestURI = engine.getSubmission("dummy", SUGGEST_TYPE, "searchbar").uri;
552 if (suggestURI.prePath != searchURI.prePath)
553 connector.speculativeConnect(suggestURI, callbacks);
554 }
555 ]]></handler>
556 </handlers>
557 </binding>
559 <binding id="searchbar-textbox"
560 extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
561 <implementation implements="nsIObserver">
562 <constructor><![CDATA[
563 const kXULNS =
564 "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
566 if (document.getBindingParent(this).parentNode.parentNode.localName ==
567 "toolbarpaletteitem")
568 return;
570 // Initialize fields
571 this._stringBundle = document.getBindingParent(this)._stringBundle;
572 this._prefBranch =
573 Components.classes["@mozilla.org/preferences-service;1"]
574 .getService(Components.interfaces.nsIPrefBranch);
575 this._suggestEnabled =
576 this._prefBranch.getBoolPref("browser.search.suggest.enabled");
578 if (this._prefBranch.getBoolPref("browser.urlbar.clickSelectsAll"))
579 this.setAttribute("clickSelectsAll", true);
581 // Add items to context menu and attach controller to handle them
582 var textBox = document.getAnonymousElementByAttribute(this,
583 "anonid", "textbox-input-box");
584 var cxmenu = document.getAnonymousElementByAttribute(textBox,
585 "anonid", "input-box-contextmenu");
586 var pasteAndSearch;
587 cxmenu.addEventListener("popupshowing", function() {
588 if (!pasteAndSearch)
589 return;
590 var controller = document.commandDispatcher.getControllerForCommand("cmd_paste");
591 var enabled = controller.isCommandEnabled("cmd_paste");
592 if (enabled)
593 pasteAndSearch.removeAttribute("disabled");
594 else
595 pasteAndSearch.setAttribute("disabled", "true");
596 }, false);
598 var element, label, akey;
600 element = document.createElementNS(kXULNS, "menuseparator");
601 cxmenu.appendChild(element);
603 var insertLocation = cxmenu.firstChild;
604 while (insertLocation.nextSibling &&
605 insertLocation.getAttribute("cmd") != "cmd_paste")
606 insertLocation = insertLocation.nextSibling;
607 if (insertLocation) {
608 element = document.createElementNS(kXULNS, "menuitem");
609 label = this._stringBundle.getString("cmd_pasteAndSearch");
610 element.setAttribute("label", label);
611 element.setAttribute("anonid", "paste-and-search");
612 element.setAttribute("oncommand",
613 "BrowserSearch.searchBar.select(); goDoCommand('cmd_paste'); BrowserSearch.searchBar.handleSearchCommand();");
614 cxmenu.insertBefore(element, insertLocation.nextSibling);
615 pasteAndSearch = element;
616 }
618 element = document.createElementNS(kXULNS, "menuitem");
619 label = this._stringBundle.getString("cmd_clearHistory");
620 akey = this._stringBundle.getString("cmd_clearHistory_accesskey");
621 element.setAttribute("label", label);
622 element.setAttribute("accesskey", akey);
623 element.setAttribute("cmd", "cmd_clearhistory");
624 cxmenu.appendChild(element);
626 element = document.createElementNS(kXULNS, "menuitem");
627 label = this._stringBundle.getString("cmd_showSuggestions");
628 akey = this._stringBundle.getString("cmd_showSuggestions_accesskey");
629 element.setAttribute("anonid", "toggle-suggest-item");
630 element.setAttribute("label", label);
631 element.setAttribute("accesskey", akey);
632 element.setAttribute("cmd", "cmd_togglesuggest");
633 element.setAttribute("type", "checkbox");
634 element.setAttribute("checked", this._suggestEnabled);
635 element.setAttribute("autocheck", "false");
636 this._suggestMenuItem = element;
637 cxmenu.appendChild(element);
639 this.controllers.appendController(this.searchbarController);
641 // Add observer for suggest preference
642 var prefs = Components.classes["@mozilla.org/preferences-service;1"]
643 .getService(Components.interfaces.nsIPrefBranch);
644 prefs.addObserver("browser.search.suggest.enabled", this, false);
645 ]]></constructor>
647 <destructor><![CDATA[
648 var prefs = Components.classes["@mozilla.org/preferences-service;1"]
649 .getService(Components.interfaces.nsIPrefBranch);
650 prefs.removeObserver("browser.search.suggest.enabled", this);
652 // Because XBL and the customize toolbar code interacts poorly,
653 // there may not be anything to remove here
654 try {
655 this.controllers.removeController(this.searchbarController);
656 } catch (ex) { }
657 ]]></destructor>
659 <field name="_stringBundle"/>
660 <field name="_prefBranch"/>
661 <field name="_suggestMenuItem"/>
662 <field name="_suggestEnabled"/>
664 <!--
665 This overrides the searchParam property in autocomplete.xml. We're
666 hijacking this property as a vehicle for delivering the privacy
667 information about the window into the guts of nsSearchSuggestions.
669 Note that the setter is the same as the parent. We were not sure whether
670 we can override just the getter. If that proves to be the case, the setter
671 can be removed.
672 -->
673 <property name="searchParam"
674 onget="return this.getAttribute('autocompletesearchparam') +
675 (PrivateBrowsingUtils.isWindowPrivate(window) ? '|private' : '');"
676 onset="this.setAttribute('autocompletesearchparam', val); return val;"/>
678 <!--
679 This method overrides the autocomplete binding's openPopup (essentially
680 duplicating the logic from the autocomplete popup binding's
681 openAutocompletePopup method), modifying it so that the popup is aligned with
682 the inner textbox, but sized to not extend beyond the search bar border.
683 -->
684 <method name="openPopup">
685 <body><![CDATA[
686 var popup = this.popup;
687 if (!popup.mPopupOpen) {
688 // Initially the panel used for the searchbar (PopupAutoComplete
689 // in browser.xul) is hidden to avoid impacting startup / new
690 // window performance. The base binding's openPopup would normally
691 // call the overriden openAutocompletePopup in urlbarBindings.xml's
692 // browser-autocomplete-result-popup binding to unhide the popup,
693 // but since we're overriding openPopup we need to unhide the panel
694 // ourselves.
695 popup.hidden = false;
697 popup.mInput = this;
698 popup.view = this.controller.QueryInterface(Components.interfaces.nsITreeView);
699 popup.invalidate();
701 popup.showCommentColumn = this.showCommentColumn;
702 popup.showImageColumn = this.showImageColumn;
704 document.popupNode = null;
706 const isRTL = getComputedStyle(this, "").direction == "rtl";
708 var outerRect = this.getBoundingClientRect();
709 var innerRect = this.inputField.getBoundingClientRect();
710 if (isRTL) {
711 var width = innerRect.right - outerRect.left;
712 } else {
713 var width = outerRect.right - innerRect.left;
714 }
715 popup.setAttribute("width", width > 100 ? width : 100);
717 var yOffset = outerRect.bottom - innerRect.bottom;
718 popup.openPopup(this.inputField, "after_start", 0, yOffset, false, false);
719 }
720 ]]></body>
721 </method>
723 <method name="observe">
724 <parameter name="aSubject"/>
725 <parameter name="aTopic"/>
726 <parameter name="aData"/>
727 <body><![CDATA[
728 if (aTopic == "nsPref:changed") {
729 this._suggestEnabled =
730 this._prefBranch.getBoolPref("browser.search.suggest.enabled");
731 this._suggestMenuItem.setAttribute("checked", this._suggestEnabled);
732 }
733 ]]></body>
734 </method>
736 <method name="openSearch">
737 <body>
738 <![CDATA[
739 // Don't open search popup if history popup is open
740 if (!this.popupOpen) {
741 document.getBindingParent(this).searchButton.open = true;
742 return false;
743 }
744 return true;
745 ]]>
746 </body>
747 </method>
749 <!-- override |onTextEntered| in autocomplete.xml -->
750 <method name="onTextEntered">
751 <parameter name="aEvent"/>
752 <body><![CDATA[
753 var evt = aEvent || this.mEnterEvent;
754 document.getBindingParent(this).handleSearchCommand(evt);
755 this.mEnterEvent = null;
756 ]]></body>
757 </method>
759 <!-- nsIController -->
760 <field name="searchbarController" readonly="true"><![CDATA[({
761 _self: this,
762 supportsCommand: function(aCommand) {
763 return aCommand == "cmd_clearhistory" ||
764 aCommand == "cmd_togglesuggest";
765 },
767 isCommandEnabled: function(aCommand) {
768 return true;
769 },
771 doCommand: function (aCommand) {
772 switch (aCommand) {
773 case "cmd_clearhistory":
774 var param = this._self.getAttribute("autocompletesearchparam");
776 let searchBar = this._self.parentNode;
778 BrowserSearch.searchBar.FormHistory.update({ op : "remove", fieldname : param }, null);
779 this._self.value = "";
780 break;
781 case "cmd_togglesuggest":
782 // The pref observer will update _suggestEnabled and the menu
783 // checkmark.
784 this._self._prefBranch.setBoolPref("browser.search.suggest.enabled",
785 !this._self._suggestEnabled);
786 break;
787 default:
788 // do nothing with unrecognized command
789 }
790 }
791 })]]></field>
792 </implementation>
794 <handlers>
795 <handler event="keypress" keycode="VK_UP" modifiers="accel"
796 phase="capturing"
797 action="document.getBindingParent(this).selectEngine(event, false);"/>
799 <handler event="keypress" keycode="VK_DOWN" modifiers="accel"
800 phase="capturing"
801 action="document.getBindingParent(this).selectEngine(event, true);"/>
803 <handler event="keypress" keycode="VK_DOWN" modifiers="alt"
804 phase="capturing"
805 action="return this.openSearch();"/>
807 <handler event="keypress" keycode="VK_UP" modifiers="alt"
808 phase="capturing"
809 action="return this.openSearch();"/>
811 #ifndef XP_MACOSX
812 <handler event="keypress" keycode="VK_F4"
813 phase="capturing"
814 action="return this.openSearch();"/>
815 #endif
817 <handler event="dragover">
818 <![CDATA[
819 var types = event.dataTransfer.types;
820 if (types.contains("text/plain") || types.contains("text/x-moz-text-internal"))
821 event.preventDefault();
822 ]]>
823 </handler>
825 <handler event="drop">
826 <![CDATA[
827 var dataTransfer = event.dataTransfer;
828 var data = dataTransfer.getData("text/plain");
829 if (!data)
830 data = dataTransfer.getData("text/x-moz-text-internal");
831 if (data) {
832 event.preventDefault();
833 this.value = data;
834 this.onTextEntered(event);
835 }
836 ]]>
837 </handler>
839 </handlers>
840 </binding>
841 </bindings>