browser/components/search/content/search.xml

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:7d743aa88d45
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/.
6
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 ]>
13
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">
18
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>
74
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;
81
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);
86
87 this._initialized = true;
88
89 this.searchService.init((function search_init_cb(aStatus) {
90 // Bail out if the binding's been destroyed
91 if (!this._initialized)
92 return;
93
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>
102
103 <destructor><![CDATA[
104 if (this._initialized) {
105 this._initialized = false;
106
107 var os = Components.classes["@mozilla.org/observer-service;1"]
108 .getService(Components.interfaces.nsIObserverService);
109 os.removeObserver(this, "browser-search-engine-modified");
110 }
111
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>
117
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>
129
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>
137
138 <field name="searchButton">document.getAnonymousElementByAttribute(this,
139 "anonid", "searchbar-engine-button");</field>
140
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>
153
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;"/>
158
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>
170
171 <property name="value" onget="return this._textbox.value;"
172 onset="return this._textbox.value = val;"/>
173
174 <method name="focus">
175 <body><![CDATA[
176 this._textbox.focus();
177 ]]></body>
178 </method>
179
180 <method name="select">
181 <body><![CDATA[
182 this._textbox.select();
183 ]]></body>
184 </method>
185
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 }
209
210 // Make sure the engine list is refetched next time it's needed
211 this._engines = null;
212
213 // Rebuild the popup and update the display after any modification.
214 this.rebuildPopup();
215 this.updateDisplay();
216 }
217 ]]></body>
218 </method>
219
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.
226
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.
232
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. -->
236
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>
263
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>
290
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>
303
304 <method name="updateDisplay">
305 <body><![CDATA[
306 var uri = this.currentEngine.iconURI;
307 this.setIcon(this, uri ? uri.spec : "");
308
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>
316
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();
327
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 }
338
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";
343
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;
353
354 var separator = document.createElementNS(kXULNS, "menuseparator");
355 separator.setAttribute("class", "addengine-separator");
356 popup.insertBefore(separator, insertLocation);
357
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>
378
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;
387
388 // Clear the popup, down to the first separator
389 while (popup.firstChild && popup.firstChild.localName != "menuseparator")
390 popup.removeChild(popup.firstChild);
391
392 const kXULNS =
393 "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
394
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 }
414
415 this._needToBuildPopup = false;
416 ]]></body>
417 </method>
418
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);
425
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>
437
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;
445
446 if (newIndex >= 0 && newIndex < this.engines.length) {
447 this.currentEngine = this.engines[newIndex];
448 }
449
450 aEvent.preventDefault();
451 aEvent.stopPropagation();
452 ]]></body>
453 </method>
454
455 <method name="handleSearchCommand">
456 <parameter name="aEvent"/>
457 <body><![CDATA[
458 var textBox = this._textbox;
459 var textValue = textBox.value;
460
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 }
472
473 this.doSearch(textValue, where);
474 ]]></body>
475 </method>
476
477 <method name="doSearch">
478 <parameter name="aData"/>
479 <parameter name="aWhere"/>
480 <body><![CDATA[
481 var textBox = this._textbox;
482
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 }
493
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>
501
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;
523
524 this.focus();
525 this.select();
526 ]]></handler>
527
528 <handler event="popupshowing" action="this.rebuildPopupDynamic();"/>
529
530 <handler event="DOMMouseScroll"
531 phase="capturing"
532 modifiers="accel"
533 action="this.selectEngine(event, (event.detail > 0));"/>
534
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
539
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);
549
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>
558
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";
565
566 if (document.getBindingParent(this).parentNode.parentNode.localName ==
567 "toolbarpaletteitem")
568 return;
569
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");
577
578 if (this._prefBranch.getBoolPref("browser.urlbar.clickSelectsAll"))
579 this.setAttribute("clickSelectsAll", true);
580
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);
597
598 var element, label, akey;
599
600 element = document.createElementNS(kXULNS, "menuseparator");
601 cxmenu.appendChild(element);
602
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 }
617
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);
625
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);
638
639 this.controllers.appendController(this.searchbarController);
640
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>
646
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);
651
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>
658
659 <field name="_stringBundle"/>
660 <field name="_prefBranch"/>
661 <field name="_suggestMenuItem"/>
662 <field name="_suggestEnabled"/>
663
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.
668
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;"/>
677
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;
696
697 popup.mInput = this;
698 popup.view = this.controller.QueryInterface(Components.interfaces.nsITreeView);
699 popup.invalidate();
700
701 popup.showCommentColumn = this.showCommentColumn;
702 popup.showImageColumn = this.showImageColumn;
703
704 document.popupNode = null;
705
706 const isRTL = getComputedStyle(this, "").direction == "rtl";
707
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);
716
717 var yOffset = outerRect.bottom - innerRect.bottom;
718 popup.openPopup(this.inputField, "after_start", 0, yOffset, false, false);
719 }
720 ]]></body>
721 </method>
722
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>
735
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>
748
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>
758
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 },
766
767 isCommandEnabled: function(aCommand) {
768 return true;
769 },
770
771 doCommand: function (aCommand) {
772 switch (aCommand) {
773 case "cmd_clearhistory":
774 var param = this._self.getAttribute("autocompletesearchparam");
775
776 let searchBar = this._self.parentNode;
777
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>
793
794 <handlers>
795 <handler event="keypress" keycode="VK_UP" modifiers="accel"
796 phase="capturing"
797 action="document.getBindingParent(this).selectEngine(event, false);"/>
798
799 <handler event="keypress" keycode="VK_DOWN" modifiers="accel"
800 phase="capturing"
801 action="document.getBindingParent(this).selectEngine(event, true);"/>
802
803 <handler event="keypress" keycode="VK_DOWN" modifiers="alt"
804 phase="capturing"
805 action="return this.openSearch();"/>
806
807 <handler event="keypress" keycode="VK_UP" modifiers="alt"
808 phase="capturing"
809 action="return this.openSearch();"/>
810
811 #ifndef XP_MACOSX
812 <handler event="keypress" keycode="VK_F4"
813 phase="capturing"
814 action="return this.openSearch();"/>
815 #endif
816
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>
824
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>
838
839 </handlers>
840 </binding>
841 </bindings>

mercurial