browser/components/search/content/search.xml

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial